krb5 commit: Modify k5buf interfaces for easier use

Greg Hudson ghudson at MIT.EDU
Wed Jul 30 13:39:16 EDT 2014


https://github.com/krb5/krb5/commit/651f3af251d172361a954f55f2d87561ae42c2d0
commit 651f3af251d172361a954f55f2d87561ae42c2d0
Author: Greg Hudson <ghudson at mit.edu>
Date:   Wed Jul 2 12:03:54 2014 -0400

    Modify k5buf interfaces for easier use
    
    Make struct k5buf less opaque and get rid of k5buf-int.h.  Make it
    easy to initialize a k5buf in an error state so that it can be freed
    in a cleanup handler.  Add a function k5_buf_status which returns 0 or
    ENOMEM.  Remove k5_buf_data and k5_buf_len.  Rename k5_free_buf to
    k5_buf_free.  Adjust all callers to match.

 src/clients/ksu/authorization.c                    |    4 +-
 src/include/k5-buf.h                               |   87 ++++--------
 src/lib/crypto/crypto_tests/t_hmac.c               |    2 +-
 src/lib/crypto/krb/cf2.c                           |   11 +-
 src/lib/gssapi/generic/gssapiP_generic.h           |   20 ++--
 src/lib/gssapi/generic/oid_ops.c                   |    4 -
 src/lib/gssapi/mechglue/g_export_cred.c            |    6 +-
 src/lib/kadm5/alt_prof.c                           |   11 +-
 src/lib/kadm5/str_conv.c                           |    4 +-
 src/lib/kdb/kdb5.c                                 |   20 ++--
 src/lib/krb5/ccache/cc_file.c                      |   22 +--
 src/lib/krb5/ccache/ccmarshal.c                    |   12 +-
 src/lib/krb5/krb/chpw.c                            |   12 +-
 src/lib/krb5/krb/srv_rcache.c                      |   12 +-
 src/lib/krb5/os/dnsglue.c                          |    5 +-
 src/lib/krb5/os/dnssrv.c                           |    7 +-
 src/lib/krb5/os/expand_path.c                      |   17 +--
 src/lib/krb5/os/localauth_rule.c                   |   15 +-
 src/lib/krb5/os/sendto_kdc.c                       |    6 +-
 src/lib/krb5/os/trace.c                            |    2 +-
 src/lib/krb5/rcache/rc_dfl.c                       |   15 +--
 src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c  |    4 +-
 src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.c      |    2 +-
 src/plugins/preauth/pkinit/pkinit_crypto_nss.c     |   12 +-
 src/plugins/preauth/pkinit/pkinit_crypto_openssl.c |    6 +-
 src/util/gss-kernel-lib/t_kgss_user.c              |    6 +-
 src/util/support/deps                              |    4 +-
 src/util/support/json.c                            |    8 +-
 src/util/support/k5buf-int.h                       |   48 -------
 src/util/support/k5buf.c                           |  111 ++++++++-------
 src/util/support/libkrb5support-fixed.exports      |    5 +-
 src/util/support/t_k5buf.c                         |  149 ++++++++------------
 32 files changed, 258 insertions(+), 391 deletions(-)

diff --git a/src/clients/ksu/authorization.c b/src/clients/ksu/authorization.c
index 7f393b8..90aafbd 100644
--- a/src/clients/ksu/authorization.c
+++ b/src/clients/ksu/authorization.c
@@ -518,11 +518,11 @@ krb5_boolean find_first_cmd_that_exists(fcmd_arr, cmd_out, err_out)
         for(j= 0; j < i; j ++)
             k5_buf_add_fmt(&buf, " %s ", fcmd_arr[j]);
         k5_buf_add(&buf, "\n");
-        *err_out = k5_buf_data(&buf);
-        if (*err_out == NULL) {
+        if (k5_buf_status(&buf) != 0) {
             perror(prog_name);
             exit(1);
         }
+        *err_out = buf.data;
     }
 
 
diff --git a/src/include/k5-buf.h b/src/include/k5-buf.h
index da10fd9..f3207bd 100644
--- a/src/include/k5-buf.h
+++ b/src/include/k5-buf.h
@@ -27,62 +27,47 @@
 #ifndef K5_BUF_H
 #define K5_BUF_H
 
-#if defined(_MSDOS) || defined(_WIN32)
-#include <win-mac.h>
-#endif
-#ifndef KRB5_CALLCONV
-#define KRB5_CALLCONV
-#define KRB5_CALLCONV_C
-#endif
-
 #include <stdarg.h>
 #include <string.h>
-#ifndef _WIN32
-#include <unistd.h>
-#endif
 
 /*
  * The k5buf module is intended to allow multi-step string construction in a
  * fixed or dynamic buffer without the need to check for a failure at each step
  * (and without aborting on malloc failure).  If an allocation failure occurs
- * or if the fixed buffer runs out of room, the error will be discovered when
- * the caller retrieves the C string value or checks the length of the
- * resulting buffer.
+ * or the fixed buffer runs out of room, the buffer will be set to an error
+ * state which can be detected with k5_buf_status.  Data in a buffer is
+ * terminated with a zero byte so that it can be used as a C string.
  *
- * k5buf structures are stack-allocated, but are intended to be opaque, so do
- * not access the fields directly.  This is a tool, not a way of life, so do
- * not put k5buf structure pointers into the public API or into significant
- * internal APIs.
+ * k5buf structures are usually stack-allocated.  Do not put k5buf structure
+ * pointers into public APIs.  It is okay to reference the data and len fields
+ * of a buffer (they will be NULL/0 if the buffer is in an error state), but do
+ * not change them.
  */
 
-/*
- * We must define the k5buf structure here to allow stack allocation.  The
- * structure is intended to be opaque, so the fields have funny names.
- */
+/* Buffer type values */
+enum k5buftype { K5BUF_ERROR, K5BUF_FIXED, K5BUF_DYNAMIC };
+
 struct k5buf {
-    int xx_buftype;
-    char *xx_data;
-    size_t xx_space;
-    size_t xx_len;
+    enum k5buftype buftype;
+    void *data;
+    size_t space;
+    size_t len;
 };
 
+#define EMPTY_K5BUF { K5BUF_ERROR }
+
 /* Initialize a k5buf using a fixed-sized, existing buffer.  SPACE must be
  * more than zero, or an assertion failure will result. */
 void k5_buf_init_fixed(struct k5buf *buf, char *data, size_t space);
 
-/* Initialize a k5buf using an internally allocated dynamic buffer.  The
- * buffer contents must be freed with k5_free_buf. */
+/* Initialize a k5buf using an internally allocated dynamic buffer. */
 void k5_buf_init_dynamic(struct k5buf *buf);
 
 /* Add a C string to BUF. */
 void k5_buf_add(struct k5buf *buf, const char *data);
 
-/*
- * Add a counted set of bytes to BUF.  It is okay for DATA[0..LEN-1]
- * to contain null bytes if you are prepared to deal with that in the
- * output (use k5_buf_len to retrieve the length of the output).
- */
-void k5_buf_add_len(struct k5buf *buf, const char *data, size_t len);
+/* Add a counted series of bytes to BUF. */
+void k5_buf_add_len(struct k5buf *buf, const void *data, size_t len);
 
 /* Add sprintf-style formatted data to BUF. */
 void k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
@@ -99,36 +84,16 @@ void *k5_buf_get_space(struct k5buf *buf, size_t len);
  * length, or an assertion failure will result. */
 void k5_buf_truncate(struct k5buf *buf, size_t len);
 
-/*
- * Retrieve the byte array value of BUF, or NULL if there has been an
- * allocation failure or the fixed buffer ran out of room.
- *
- * The byte array will be a C string unless binary data was added with
- * k5_buf_add_len; it will be null-terminated regardless.  Modifying the byte
- * array does not invalidate the buffer, as long as its length is not changed.
- *
- * For a fixed buffer, the return value will always be equal to the passed-in
- * value of DATA at initialization time if it is not NULL.
- *
- * For a dynamic buffer, any buffer modification operation except
- * k5_buf_truncate may invalidate the byte array address.
- */
-char *k5_buf_data(struct k5buf *buf);
-
-/*
- * Retrieve the length of BUF, or -1 if there has been an allocation failure or
- * the fixed buffer ran out of room.  The length is equal to
- * strlen(k5_buf_data(buf)) unless binary data was added with k5_buf_add_len.
- */
-ssize_t k5_buf_len(struct k5buf *buf);
+/* Return ENOMEM if buf is in an error state, 0 otherwise. */
+int k5_buf_status(struct k5buf *buf);
 
 /*
  * Free the storage used in the dynamic buffer BUF.  The caller may choose to
- * take responsibility for freeing the return value of k5_buf_data instead of
- * using this function.  If BUF is a fixed buffer, an assertion failure will
- * result.  It is unnecessary (though harmless) to free a buffer after an error
- * is detected; the storage will already have been freed in that case.
+ * take responsibility for freeing the data pointer instead of using this
+ * function.  If BUF is a fixed buffer, an assertion failure will result.
+ * Freeing a buffer in the error state, a buffer initialized with EMPTY_K5BUF,
+ * or a zeroed k5buf structure is a no-op.
  */
-void k5_free_buf(struct k5buf *buf);
+void k5_buf_free(struct k5buf *buf);
 
 #endif /* K5_BUF_H */
diff --git a/src/lib/crypto/crypto_tests/t_hmac.c b/src/lib/crypto/crypto_tests/t_hmac.c
index 65efa60..8961380 100644
--- a/src/lib/crypto/crypto_tests/t_hmac.c
+++ b/src/lib/crypto/crypto_tests/t_hmac.c
@@ -250,7 +250,7 @@ static void test_hmac()
         k5_buf_add(&buf, "0x");
         for (j = 0; j < out.length; j++)
             k5_buf_add_fmt(&buf, "%02x", 0xff & outbuf[j]);
-        if (k5_buf_data(&buf) == NULL)
+        if (k5_buf_status(&buf) != 0)
             abort();
         if (strcmp(stroutbuf, md5tests[i].hexdigest)) {
             printf("*** CHECK FAILED!\n"
diff --git a/src/lib/crypto/krb/cf2.c b/src/lib/crypto/krb/cf2.c
index 5eec154..a0654b6 100644
--- a/src/lib/crypto/krb/cf2.c
+++ b/src/lib/crypto/krb/cf2.c
@@ -59,12 +59,11 @@ prf_plus(krb5_context context, krb5_keyblock *k, const char *pepper,
     buffer = k5calloc(iterations, prflen, &retval);
     if (retval)
         goto cleanup;
-    if (k5_buf_len(&prf_inbuf) == -1) {
-        retval = ENOMEM;
+    retval = k5_buf_status(&prf_inbuf);
+    if (retval)
         goto cleanup;
-    }
-    in_data.length = (krb5_int32)k5_buf_len(&prf_inbuf);
-    in_data.data = k5_buf_data(&prf_inbuf);
+    in_data.length = prf_inbuf.len;
+    in_data.data = prf_inbuf.data;
     out_data.length = prflen;
     out_data.data = buffer;
 
@@ -82,7 +81,7 @@ prf_plus(krb5_context context, krb5_keyblock *k, const char *pepper,
 
 cleanup:
     free(buffer);
-    k5_free_buf(&prf_inbuf);
+    k5_buf_free(&prf_inbuf);
     return retval;
 }
 
diff --git a/src/lib/gssapi/generic/gssapiP_generic.h b/src/lib/gssapi/generic/gssapiP_generic.h
index 5e32186..686a217 100644
--- a/src/lib/gssapi/generic/gssapiP_generic.h
+++ b/src/lib/gssapi/generic/gssapiP_generic.h
@@ -273,13 +273,18 @@ k5buf_to_gss(OM_uint32 *minor,
              gss_buffer_t output_buffer)
 {
     OM_uint32 status = GSS_S_COMPLETE;
-    char *bp = k5_buf_data(input_k5buf);
-    output_buffer->length = k5_buf_len(input_k5buf);
+
+    if (k5_buf_status(input_k5buf) != 0) {
+        *minor = ENOMEM;
+        return GSS_S_FAILURE;
+    }
+    output_buffer->length = input_k5buf->len;
 #if defined(_WIN32) || defined(DEBUG_GSSALLOC)
     if (output_buffer->length > 0) {
         output_buffer->value = gssalloc_malloc(output_buffer->length);
         if (output_buffer->value) {
-            memcpy(output_buffer->value, bp, output_buffer->length);
+            memcpy(output_buffer->value, input_k5buf->data,
+                   output_buffer->length);
         } else {
             status = GSS_S_FAILURE;
             *minor = ENOMEM;
@@ -287,13 +292,10 @@ k5buf_to_gss(OM_uint32 *minor,
     } else {
         output_buffer->value = NULL;
     }
-    k5_free_buf(input_k5buf);
+    k5_buf_free(input_k5buf);
 #else
-    output_buffer->value = bp;
-    /*
-     * it would be nice to invalidate input_k5buf here
-     * but there is no api for that currently...
-     */
+    output_buffer->value = input_k5buf->data;
+    memset(input_k5buf, 0, sizeof(*input_k5buf));
 #endif
     return status;
 }
diff --git a/src/lib/gssapi/generic/oid_ops.c b/src/lib/gssapi/generic/oid_ops.c
index 1229f38..5ad02fc 100644
--- a/src/lib/gssapi/generic/oid_ops.c
+++ b/src/lib/gssapi/generic/oid_ops.c
@@ -276,10 +276,6 @@ generic_gss_oid_to_str(OM_uint32 *minor_status,
         }
     }
     k5_buf_add_len(&buf, "}\0", 2);
-    if (k5_buf_data(&buf) == NULL) {
-        *minor_status = ENOMEM;
-        return(GSS_S_FAILURE);
-    }
     return k5buf_to_gss(minor_status, &buf, oid_str);
 }
 
diff --git a/src/lib/gssapi/mechglue/g_export_cred.c b/src/lib/gssapi/mechglue/g_export_cred.c
index 16d1ebe..8f5fe4a 100644
--- a/src/lib/gssapi/mechglue/g_export_cred.c
+++ b/src/lib/gssapi/mechglue/g_export_cred.c
@@ -106,13 +106,9 @@ gss_export_cred(OM_uint32 * minor_status, gss_cred_id_t cred_handle,
         gss_release_buffer(&tmpmin, &mech_token);
     }
 
-    if (k5_buf_data(&buf) == NULL) {
-        *minor_status = ENOMEM;
-        return GSS_S_FAILURE;
-    }
     return k5buf_to_gss(minor_status, &buf, token);
 
 error:
-    k5_free_buf(&buf);
+    k5_buf_free(&buf);
     return status;
 }
diff --git a/src/lib/kadm5/alt_prof.c b/src/lib/kadm5/alt_prof.c
index 205333b..db45d13 100644
--- a/src/lib/kadm5/alt_prof.c
+++ b/src/lib/kadm5/alt_prof.c
@@ -66,7 +66,7 @@ krb5_aprof_init(char *fname, char *envname, krb5_pointer *acontextp)
     krb5_error_code ret;
     profile_t profile;
     const char *kdc_config;
-    char *profile_path, **filenames;
+    char **filenames;
     int i;
     struct k5buf buf;
 
@@ -79,17 +79,16 @@ krb5_aprof_init(char *fname, char *envname, krb5_pointer *acontextp)
     if (kdc_config)
         k5_buf_add(&buf, kdc_config);
     for (i = 0; filenames[i] != NULL; i++) {
-        if (k5_buf_len(&buf) > 0)
+        if (buf.len > 0)
             k5_buf_add(&buf, ":");
         k5_buf_add(&buf, filenames[i]);
     }
     krb5_free_config_files(filenames);
-    profile_path = k5_buf_data(&buf);
-    if (profile_path == NULL)
+    if (k5_buf_status(&buf) != 0)
         return ENOMEM;
     profile = (profile_t) NULL;
-    ret = profile_init_path(profile_path, &profile);
-    free(profile_path);
+    ret = profile_init_path(buf.data, &profile);
+    k5_buf_free(&buf);
     if (ret)
         return ret;
     *acontextp = profile;
diff --git a/src/lib/kadm5/str_conv.c b/src/lib/kadm5/str_conv.c
index e8de915..216b580 100644
--- a/src/lib/kadm5/str_conv.c
+++ b/src/lib/kadm5/str_conv.c
@@ -187,14 +187,14 @@ krb5_flags_to_string(flags, sep, buffer, buflen)
     /* Blast through the table matching all we can */
     for (i=0; i<flags_table_nents; i++) {
         if (flags & flags_table[i].fl_flags) {
-            if (k5_buf_len(&buf) > 0)
+            if (buf.len > 0)
                 k5_buf_add(&buf, sepstring);
             k5_buf_add(&buf, _(flags_table[i].fl_output));
             /* Keep track of what we matched */
             pflags |= flags_table[i].fl_flags;
         }
     }
-    if (k5_buf_data(&buf) == NULL)
+    if (k5_buf_status(&buf) != 0)
         return(ENOMEM);
 
     /* See if there's any leftovers */
diff --git a/src/lib/kdb/kdb5.c b/src/lib/kdb/kdb5.c
index 7541b1e..7c82399 100644
--- a/src/lib/kdb/kdb5.c
+++ b/src/lib/kdb/kdb5.c
@@ -2034,10 +2034,9 @@ krb5_dbe_set_string(krb5_context context, krb5_db_entry *entry,
 {
     krb5_error_code code;
     const char *pos, *end, *mapkey, *mapval;
-    struct k5buf buf;
+    struct k5buf buf = EMPTY_K5BUF;
     krb5_boolean found = FALSE;
     krb5_tl_data tl_data;
-    ssize_t len;
 
     /* Copy the current mapping to buf, updating key with value if found. */
     code = begin_attrs(context, entry, &pos, &end);
@@ -2063,17 +2062,20 @@ krb5_dbe_set_string(krb5_context context, krb5_db_entry *entry,
         k5_buf_add_len(&buf, value, strlen(value) + 1);
     }
 
-    len = k5_buf_len(&buf);
-    if (len == -1)
+    if (k5_buf_status(&buf) != 0)
         return ENOMEM;
-    if (len > 65535)
-        return KRB5_KDB_STRINGS_TOOLONG;
+    if (buf.len > 65535) {
+        code = KRB5_KDB_STRINGS_TOOLONG;
+        goto cleanup;
+    }
     tl_data.tl_data_type = KRB5_TL_STRING_ATTRS;
-    tl_data.tl_data_contents = (krb5_octet *)k5_buf_data(&buf);
-    tl_data.tl_data_length = len;
+    tl_data.tl_data_contents = buf.data;
+    tl_data.tl_data_length = buf.len;
 
     code = krb5_dbe_update_tl_data(context, entry, &tl_data);
-    k5_free_buf(&buf);
+
+cleanup:
+    k5_buf_free(&buf);
     return code;
 }
 
diff --git a/src/lib/krb5/ccache/cc_file.c b/src/lib/krb5/ccache/cc_file.c
index 3f6443f..2407851 100644
--- a/src/lib/krb5/ccache/cc_file.c
+++ b/src/lib/krb5/ccache/cc_file.c
@@ -402,7 +402,6 @@ read_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
     krb5_error_code ret;
     struct k5buf buf;
     size_t maxsize;
-    unsigned char *bytes;
 
     *princ = NULL;
     k5_cc_mutex_assert_locked(context, &((fcc_data *)id->data)->lock);
@@ -415,17 +414,15 @@ read_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
     ret = load_principal(context, id, maxsize, &buf);
     if (ret)
         goto cleanup;
-    bytes = (unsigned char *)k5_buf_data(&buf);
-    if (bytes == NULL) {
-        ret = ENOMEM;
+    ret = k5_buf_status(&buf);
+    if (ret)
         goto cleanup;
-    }
 
     /* Unmarshal it from buf into princ. */
-    ret = k5_unmarshal_princ(bytes, k5_buf_len(&buf), version(id), princ);
+    ret = k5_unmarshal_princ(buf.data, buf.len, version(id), princ);
 
 cleanup:
-    k5_free_buf(&buf);
+    k5_buf_free(&buf);
     return ret;
 }
 
@@ -1092,7 +1089,6 @@ fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
     fcc_data *data = id->data;
     struct k5buf buf;
     size_t maxsize;
-    unsigned char *bytes;
 
     memset(creds, 0, sizeof(*creds));
     k5_cc_mutex_lock(context, &data->lock);
@@ -1111,18 +1107,16 @@ fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
     ret = load_cred(context, id, maxsize, &buf);
     if (ret)
         goto cleanup;
-    bytes = (unsigned char *)k5_buf_data(&buf);
-    if (bytes == NULL) {
-        ret = ENOMEM;
+    ret = k5_buf_status(&buf);
+    if (ret)
         goto cleanup;
-    }
 
     /* Unmarshal it from buf into creds. */
     fcursor->pos = fcc_lseek(data, 0, SEEK_CUR);
-    ret = k5_unmarshal_cred(bytes, k5_buf_len(&buf), version(id), creds);
+    ret = k5_unmarshal_cred(buf.data, buf.len, version(id), creds);
 
 cleanup:
-    k5_free_buf(&buf);
+    k5_buf_free(&buf);
     MAYBE_CLOSE(context, id, ret);
     k5_cc_mutex_unlock(context, &data->lock);
     return ret;
diff --git a/src/lib/krb5/ccache/ccmarshal.c b/src/lib/krb5/ccache/ccmarshal.c
index c27b43f..d190577 100644
--- a/src/lib/krb5/ccache/ccmarshal.c
+++ b/src/lib/krb5/ccache/ccmarshal.c
@@ -449,10 +449,10 @@ k5_marshal_cred(krb5_creds *creds, int version, unsigned char **bytes_out,
     marshal_authdata(&buf, version, creds->authdata);
     put_data(&buf, version, &creds->ticket);
     put_data(&buf, version, &creds->second_ticket);
-    if (k5_buf_data(&buf) == NULL)
+    if (k5_buf_status(&buf) != 0)
         return ENOMEM;
-    *bytes_out = (unsigned char *)k5_buf_data(&buf);
-    *len_out = k5_buf_len(&buf);
+    *bytes_out = buf.data;
+    *len_out = buf.len;
     return 0;
 }
 
@@ -468,9 +468,9 @@ k5_marshal_princ(krb5_principal princ, int version, unsigned char **bytes_out,
     *len_out = 0;
     k5_buf_init_dynamic(&buf);
     marshal_princ(&buf, version, princ);
-    if (k5_buf_data(&buf) == NULL)
+    if (k5_buf_status(&buf) != 0)
         return ENOMEM;
-    *bytes_out = (unsigned char *)k5_buf_data(&buf);
-    *len_out = k5_buf_len(&buf);
+    *bytes_out = buf.data;
+    *len_out = buf.len;
     return 0;
 }
diff --git a/src/lib/krb5/krb/chpw.c b/src/lib/krb5/krb/chpw.c
index 463ce64..b8010b3 100644
--- a/src/lib/krb5/krb/chpw.c
+++ b/src/lib/krb5/krb/chpw.c
@@ -384,7 +384,7 @@ struct ad_policy_info {
 static void
 add_spaces(struct k5buf *buf)
 {
-    if (k5_buf_len(buf) > 0)
+    if (buf->len > 0)
         k5_buf_add(buf, "  ");
 }
 
@@ -394,7 +394,6 @@ decode_ad_policy_info(const krb5_data *data, char **msg_out)
     struct ad_policy_info policy;
     uint64_t password_days;
     const char *p;
-    char *msg;
     struct k5buf buf;
 
     *msg_out = NULL;
@@ -465,14 +464,13 @@ decode_ad_policy_info(const krb5_data *data, char **msg_out)
                        (int)password_days);
     }
 
-    msg = k5_buf_data(&buf);
-    if (msg == NULL)
+    if (k5_buf_status(&buf) != 0)
         return ENOMEM;
 
-    if (*msg != '\0')
-        *msg_out = msg;
+    if (buf.len > 0)
+        *msg_out = buf.data;
     else
-        free(msg);
+        k5_buf_free(&buf);
     return 0;
 }
 
diff --git a/src/lib/krb5/krb/srv_rcache.c b/src/lib/krb5/krb/srv_rcache.c
index 1b0a91a..692c853 100644
--- a/src/lib/krb5/krb/srv_rcache.c
+++ b/src/lib/krb5/krb/srv_rcache.c
@@ -35,10 +35,10 @@ krb5_get_server_rcache(krb5_context context, const krb5_data *piece,
                        krb5_rcache *rcptr)
 {
     krb5_rcache rcache = 0;
-    char *cachename = 0, *cachetype;
+    char *cachetype;
     krb5_error_code retval;
     unsigned int i;
-    struct k5buf buf;
+    struct k5buf buf = EMPTY_K5BUF;
 #ifdef HAVE_GETEUID
     unsigned long uid = geteuid();
 #endif
@@ -63,11 +63,10 @@ krb5_get_server_rcache(krb5_context context, const krb5_data *piece,
     k5_buf_add_fmt(&buf, "_%lu", uid);
 #endif
 
-    cachename = k5_buf_data(&buf);
-    if (cachename == NULL)
+    if (k5_buf_status(&buf) != 0)
         return ENOMEM;
 
-    retval = krb5_rc_resolve_full(context, &rcache, cachename);
+    retval = krb5_rc_resolve_full(context, &rcache, buf.data);
     if (retval)
         goto cleanup;
 
@@ -83,7 +82,6 @@ krb5_get_server_rcache(krb5_context context, const krb5_data *piece,
 cleanup:
     if (rcache)
         krb5_rc_close(context, rcache);
-    if (cachename)
-        free(cachename);
+    k5_buf_free(&buf);
     return retval;
 }
diff --git a/src/lib/krb5/os/dnsglue.c b/src/lib/krb5/os/dnsglue.c
index fcb99ff..7d25fee 100644
--- a/src/lib/krb5/os/dnsglue.c
+++ b/src/lib/krb5/os/dnsglue.c
@@ -389,11 +389,10 @@ k5_try_realm_txt_rr(krb5_context context, const char *prefix, const char *name,
            the local domain or domain search lists to be expanded.
         */
 
-        len = k5_buf_len(&buf);
-        if (len > 0 && host[len - 1] != '.')
+        if (buf.len > 0 && host[buf.len - 1] != '.')
             k5_buf_add(&buf, ".");
     }
-    if (k5_buf_data(&buf) == NULL)
+    if (k5_buf_status(&buf) != 0)
         return KRB5_ERR_HOST_REALM_UNKNOWN;
     ret = krb5int_dns_init(&ds, host, C_IN, T_TXT);
     if (ret < 0) {
diff --git a/src/lib/krb5/os/dnssrv.c b/src/lib/krb5/os/dnssrv.c
index 33ee0e7..c41b0d8 100644
--- a/src/lib/krb5/os/dnssrv.c
+++ b/src/lib/krb5/os/dnssrv.c
@@ -59,7 +59,7 @@ krb5int_make_srv_query_realm(const krb5_data *realm,
 {
     const unsigned char *p = NULL, *base = NULL;
     char host[MAXDNAME];
-    int size, ret, rdlen, nlen, len;
+    int size, ret, rdlen, nlen;
     unsigned short priority, weight, port;
     struct krb5int_dns_state *ds = NULL;
     struct k5buf buf;
@@ -93,11 +93,10 @@ krb5int_make_srv_query_realm(const krb5_data *realm,
        a search on the prefix alone then the intention is to allow
        the local domain or domain search lists to be expanded.  */
 
-    len = k5_buf_len(&buf);
-    if (len > 0 && host[len - 1] != '.')
+    if (buf.len > 0 && host[buf.len - 1] != '.')
         k5_buf_add(&buf, ".");
 
-    if (k5_buf_data(&buf) == NULL)
+    if (k5_buf_status(&buf) != 0)
         return 0;
 
 #ifdef TEST
diff --git a/src/lib/krb5/os/expand_path.c b/src/lib/krb5/os/expand_path.c
index 4646c54..6142b3b 100644
--- a/src/lib/krb5/os/expand_path.c
+++ b/src/lib/krb5/os/expand_path.c
@@ -454,7 +454,7 @@ k5_expand_path_tokens_extra(krb5_context context, const char *path_in,
 {
     krb5_error_code ret;
     struct k5buf buf;
-    char *tok_begin, *tok_end, *tok_val, *path, **extra_tokens = NULL;
+    char *tok_begin, *tok_end, *tok_val, **extra_tokens = NULL;
     const char *path_left;
     size_t nargs = 0, i;
     va_list ap;
@@ -517,26 +517,25 @@ k5_expand_path_tokens_extra(krb5_context context, const char *path_in,
         path_left = tok_end + 1;
     }
 
-    path = k5_buf_data(&buf);
-    if (path == NULL) {
-        ret = ENOMEM;
+    ret = k5_buf_status(&buf);
+    if (ret)
         goto cleanup;
-    }
+
 #ifdef _WIN32
     /* Also deal with slashes. */
     {
         char *p;
-        for (p = path; *p != '\0'; p++) {
+        for (p = buf.data; *p != '\0'; p++) {
             if (*p == '/')
                 *p = '\\';
         }
     }
 #endif
-    *path_out = path;
+    *path_out = buf.data;
+    memset(&buf, 0, sizeof(buf));
 
 cleanup:
-    if (*path_out == NULL)
-        k5_free_buf(&buf);
+    k5_buf_free(&buf);
     free_extra_tokens(extra_tokens);
     return 0;
 }
diff --git a/src/lib/krb5/os/localauth_rule.c b/src/lib/krb5/os/localauth_rule.c
index 584dcba..8522108 100644
--- a/src/lib/krb5/os/localauth_rule.c
+++ b/src/lib/krb5/os/localauth_rule.c
@@ -130,8 +130,10 @@ do_replacement(const char *regstr, const char *repl, krb5_boolean doall,
     }
     regfree(&re);
     k5_buf_add(&buf, instr);
-    *outstr = k5_buf_data(&buf);
-    return *outstr == NULL ? ENOMEM : 0;
+    if (k5_buf_status(&buf) != 0)
+        return ENOMEM;
+    *outstr = buf.data;
+    return 0;
 }
 
 /*
@@ -207,7 +209,7 @@ aname_get_selstring(krb5_context context, krb5_const_principal aname,
                     const char **contextp, char **selstring_out)
 {
     const char *current;
-    char *end, *str;
+    char *end;
     long num_comps, ind;
     const krb5_data *datap;
     struct k5buf selstring;
@@ -257,16 +259,15 @@ aname_get_selstring(krb5_context context, krb5_const_principal aname,
 
     /* Check that we hit a ']' and not the end of the string. */
     if (*current != ']') {
-        k5_free_buf(&selstring);
+        k5_buf_free(&selstring);
         return KRB5_CONFIG_BADFORMAT;
     }
 
-    str = k5_buf_data(&selstring);
-    if (str == NULL)
+    if (k5_buf_status(&selstring) != 0)
         return ENOMEM;
 
     *contextp = current + 1;
-    *selstring_out = str;
+    *selstring_out = selstring.data;
     return 0;
 }
 
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 2242240..3b3b438 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -558,13 +558,13 @@ make_proxy_request(struct conn_state *state, const krb5_data *realm,
     k5_buf_add(&buf, "Content-type: application/kerberos\r\n");
     k5_buf_add_fmt(&buf, "Content-Length: %d\r\n\r\n", encoded_pm->length);
     k5_buf_add_len(&buf, encoded_pm->data, encoded_pm->length);
-    if (k5_buf_data(&buf) == NULL) {
+    if (k5_buf_status(&buf) != 0) {
         ret = ENOMEM;
         goto cleanup;
     }
 
-    *req_out = k5_buf_data(&buf);
-    *len_out = k5_buf_len(&buf);
+    *req_out = buf.data;
+    *len_out = buf.len;
 
 cleanup:
     krb5_free_data_contents(NULL, &pm.kerb_message);
diff --git a/src/lib/krb5/os/trace.c b/src/lib/krb5/os/trace.c
index 5fbe573..83c8d4d 100644
--- a/src/lib/krb5/os/trace.c
+++ b/src/lib/krb5/os/trace.c
@@ -305,7 +305,7 @@ trace_format(krb5_context context, const char *fmt, va_list ap)
                    creds->client, creds->server);
         }
     }
-    return k5_buf_data(&buf);
+    return buf.data;
 }
 
 /* Allows trace_format formatters to be represented in terms of other
diff --git a/src/lib/krb5/rcache/rc_dfl.c b/src/lib/krb5/rcache/rc_dfl.c
index 8f517d0..2fb6aa0 100644
--- a/src/lib/krb5/rcache/rc_dfl.c
+++ b/src/lib/krb5/rcache/rc_dfl.c
@@ -642,11 +642,10 @@ krb5_rc_io_store(krb5_context context, struct dfl_data *t,
                  krb5_donot_replay *rep)
 {
     size_t clientlen, serverlen;
-    ssize_t buflen;
     unsigned int len;
     krb5_error_code ret;
     struct k5buf buf, extbuf;
-    char *bufptr, *extstr;
+    char *extstr;
 
     clientlen = strlen(rep->client);
     serverlen = strlen(rep->server);
@@ -663,9 +662,9 @@ krb5_rc_io_store(krb5_context context, struct dfl_data *t,
         k5_buf_add_fmt(&extbuf, "HASH:%s %lu:%s %lu:%s", rep->msghash,
                        (unsigned long)clientlen, rep->client,
                        (unsigned long)serverlen, rep->server);
-        extstr = k5_buf_data(&extbuf);
-        if (!extstr)
+        if (k5_buf_status(&extbuf) != 0)
             return KRB5_RC_MALLOC;
+        extstr = extbuf.data;
 
         /*
          * Put the extension value into the server field of a
@@ -693,13 +692,11 @@ krb5_rc_io_store(krb5_context context, struct dfl_data *t,
     k5_buf_add_len(&buf, (char *)&rep->cusec, sizeof(rep->cusec));
     k5_buf_add_len(&buf, (char *)&rep->ctime, sizeof(rep->ctime));
 
-    bufptr = k5_buf_data(&buf);
-    buflen = k5_buf_len(&buf);
-    if (bufptr == NULL || buflen < 0)
+    if (k5_buf_status(&buf) != 0)
         return KRB5_RC_MALLOC;
 
-    ret = krb5_rc_io_write(context, &t->d, bufptr, buflen);
-    k5_free_buf(&buf);
+    ret = krb5_rc_io_write(context, &t->d, buf.data, buf.len);
+    k5_buf_free(&buf);
     return ret;
 }
 
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c
index 3f3efdc..af0eaf1 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c
@@ -422,9 +422,9 @@ krb5_ldap_parse_principal_name(char *i_princ_name, char **o_princ_name)
             k5_buf_add_len(&buf, p, 1);
         }
         k5_buf_add(&buf, at_rlm_name);
-        *o_princ_name = k5_buf_data(&buf);
-        if (!*o_princ_name)
+        if (k5_buf_status(&buf) != 0)
             return ENOMEM;
+        *o_princ_name = buf.data;
     }
     return 0;
 }
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.c
index 7858a55..e9fb3fa 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.c
@@ -87,7 +87,7 @@ ldap_filter_correct (char *in)
             break;
         k5_buf_add_fmt(&buf, "\\%2x", (unsigned char)*in++);
     }
-    return k5_buf_data(&buf);
+    return buf.data;
 }
 
 static int
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c
index 0226163..c849f87 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c
@@ -2173,11 +2173,11 @@ reassemble_pkcs11_name(PLArenaPool *pool, pkinit_identity_opts *idopts)
         k5_buf_add_fmt(&buf, "%sslotid=%ld", n++ ? ":" : "",
                        (long)idopts->slotid);
     }
-    if (k5_buf_len(&buf) >= 0)
-        ret = PORT_ArenaStrdup(pool, k5_buf_data(&buf));
+    if (k5_buf_status(&buf) == 0)
+        ret = PORT_ArenaStrdup(pool, buf.data);
     else
         ret = NULL;
-    k5_free_buf(&buf);
+    k5_buf_free(&buf);
     return ret;
 }
 
@@ -2209,11 +2209,11 @@ reassemble_pkcs11_identity(PLArenaPool *pool, pkinit_identity_opts *idopts,
     if (tokenname != NULL)
         k5_buf_add_fmt(&buf, "%stoken=%s", n++ ? ":" : "", tokenname);
 
-    if (k5_buf_len(&buf) >= 0)
-        ret = PORT_ArenaStrdup(pool, k5_buf_data(&buf));
+    if (k5_buf_status(&buf) == 0)
+        ret = PORT_ArenaStrdup(pool, buf.data);
     else
         ret = NULL;
-    k5_free_buf(&buf);
+    k5_buf_free(&buf);
 
     return ret;
 }
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index 246bc1b..4d9b5e5 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -4450,11 +4450,11 @@ reassemble_pkcs11_name(pkinit_identity_opts *idopts)
         k5_buf_add_fmt(&buf, "%sslotid=%ld", n++ ? ":" : "",
                        (long)idopts->slotid);
     }
-    if (k5_buf_len(&buf) >= 0)
-        ret = strdup(k5_buf_data(&buf));
+    if (k5_buf_status(&buf) == 0)
+        ret = strdup(buf.data);
     else
         ret = NULL;
-    k5_free_buf(&buf);
+    k5_buf_free(&buf);
     return ret;
 }
 
diff --git a/src/util/gss-kernel-lib/t_kgss_user.c b/src/util/gss-kernel-lib/t_kgss_user.c
index 0af9aa6..8c67b5d 100644
--- a/src/util/gss-kernel-lib/t_kgss_user.c
+++ b/src/util/gss-kernel-lib/t_kgss_user.c
@@ -175,9 +175,9 @@ marshal_lucid_context(const gss_krb5_lucid_context_v1_t *lctx,
             add_lucid_key(&buf, &lctx->cfx_kd.acceptor_subkey);
     } else
         abort();
-    assert(k5_buf_data(&buf) != NULL);
-    *data_out = (unsigned char *)k5_buf_data(&buf);
-    *len_out = k5_buf_len(&buf);
+    assert(k5_buf_status(&buf) == 0);
+    *data_out = buf.data;
+    *len_out = buf.len;
 }
 
 /* Export ctx as a lucid context, marshal it, and write it to fd. */
diff --git a/src/util/support/deps b/src/util/support/deps
index 76bdefd..82a2b4e 100644
--- a/src/util/support/deps
+++ b/src/util/support/deps
@@ -20,7 +20,7 @@ errors.so errors.po $(OUTPRE)errors.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/k5-thread.h errors.c supp-int.h
 k5buf.so k5buf.po $(OUTPRE)k5buf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-thread.h k5buf-int.h k5buf.c
+  $(top_srcdir)/include/k5-thread.h k5buf.c
 gmt_mktime.so gmt_mktime.po $(OUTPRE)gmt_mktime.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(top_srcdir)/include/k5-gmt_mktime.h \
   gmt_mktime.c
@@ -53,7 +53,7 @@ mkstemp.so mkstemp.po $(OUTPRE)mkstemp.$(OBJEXT): $(BUILDTOP)/include/autoconf.h
   mkstemp.c
 t_k5buf.so t_k5buf.po $(OUTPRE)t_k5buf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-thread.h k5buf-int.h t_k5buf.c
+  $(top_srcdir)/include/k5-thread.h t_k5buf.c
 t_unal.so t_unal.po $(OUTPRE)t_unal.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
   t_unal.c
diff --git a/src/util/support/json.c b/src/util/support/json.c
index d23267c..98f6568 100644
--- a/src/util/support/json.c
+++ b/src/util/support/json.c
@@ -696,11 +696,13 @@ k5_json_encode(k5_json_value val, char **json_out)
     k5_buf_init_dynamic(&buf);
     ret = encode_value(&buf, val);
     if (ret) {
-        k5_free_buf(&buf);
+        k5_buf_free(&buf);
         return ret;
     }
-    *json_out = k5_buf_data(&buf);
-    return (*json_out == NULL) ? ENOMEM : 0;
+    if (k5_buf_status(&buf) != 0)
+        return ENOMEM;
+    *json_out = buf.data;
+    return 0;
 }
 
 /*** JSON decoding ***/
diff --git a/src/util/support/k5buf-int.h b/src/util/support/k5buf-int.h
deleted file mode 100644
index 6f2ec19..0000000
--- a/src/util/support/k5buf-int.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/* util/support/k5buf-int.h - Internal declarations for string buffers */
-/*
- * Copyright 2008 Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * Export of this software from the United States of America may
- *   require a specific license from the United States Government.
- *   It is the responsibility of any person or organization contemplating
- *   export to obtain such a license before exporting.
- *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission.  Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose.  It is provided "as is" without express
- * or implied warranty.
- */
-
-#ifndef K5BUF_INT_H
-#define K5BUF_INT_H
-
-#include "k5-platform.h"
-#include "k5-buf.h"
-
-/*
- * The k5buf structure has funny field names to discourage callers from
- * violating the abstraction barrier.  Define less funny names for them here.
- */
-#define buftype xx_buftype
-#define data xx_data
-#define space xx_space
-#define len xx_len
-
-#define DYNAMIC_INITIAL_SIZE 128
-#define SPACE_MAX (SIZE_MAX / 2) /* rounds down, since SIZE_MAX is odd */
-
-/* Buffer type values. */
-enum { BUFTYPE_FIXED, BUFTYPE_DYNAMIC, BUFTYPE_ERROR };
-
-#endif /* K5BUF_INT_H */
diff --git a/src/util/support/k5buf.c b/src/util/support/k5buf.c
index c3c81b0..f619f6a 100644
--- a/src/util/support/k5buf.c
+++ b/src/util/support/k5buf.c
@@ -30,20 +30,36 @@
  * needs to be generated with error tables, after util/et, which builds after
  * this directory.
  */
-#include "k5buf-int.h"
+#include "k5-platform.h"
+#include "k5-buf.h"
 #include <assert.h>
 
 /*
  * Structure invariants:
  *
- * buftype is BUFTYPE_FIXED, BUFTYPE_DYNAMIC, or BUFTYPE_ERROR
- * if buftype is not BUFTYPE_ERROR:
+ * buftype is K5BUF_FIXED, K5BUF_DYNAMIC, or K5BUF_ERROR
+ * if buftype is K5BUF_ERROR, the other fields are NULL or 0
+ * if buftype is not K5BUF_ERROR:
  *   space > 0
- *   space <= floor(SIZE_MAX / 2) (to fit within ssize_t)
  *   len < space
  *   data[len] = '\0'
  */
 
+/* Return a character pointer to the current end of buf. */
+static inline char *
+endptr(struct k5buf *buf)
+{
+    return (char *)buf->data + buf->len;
+}
+
+static inline void
+set_error(struct k5buf *buf)
+{
+    buf->buftype = K5BUF_ERROR;
+    buf->data = NULL;
+    buf->space = buf->len = 0;
+}
+
 /*
  * Make sure there is room for LEN more characters in BUF, in addition to the
  * null terminator and what's already in there.  Return true on success.  On
@@ -55,18 +71,19 @@ ensure_space(struct k5buf *buf, size_t len)
     size_t new_space;
     char *new_data;
 
-    if (buf->buftype == BUFTYPE_ERROR)
+    if (buf->buftype == K5BUF_ERROR)
         return 0;
     if (buf->space - 1 - buf->len >= len) /* Enough room already. */
         return 1;
-    if (buf->buftype == BUFTYPE_FIXED) /* Can't resize a fixed buffer. */
+    if (buf->buftype == K5BUF_FIXED) /* Can't resize a fixed buffer. */
         goto error_exit;
-    assert(buf->buftype == BUFTYPE_DYNAMIC);
+    assert(buf->buftype == K5BUF_DYNAMIC);
     new_space = buf->space * 2;
-    while (new_space <= SPACE_MAX && new_space - buf->len - 1 < len)
+    while (new_space - buf->len - 1 < len) {
+        if (new_space > SIZE_MAX / 2)
+            goto error_exit;
         new_space *= 2;
-    if (new_space > SPACE_MAX)
-        goto error_exit;
+    }
     new_data = realloc(buf->data, new_space);
     if (new_data == NULL)
         goto error_exit;
@@ -75,11 +92,9 @@ ensure_space(struct k5buf *buf, size_t len)
     return 1;
 
 error_exit:
-    if (buf->buftype == BUFTYPE_DYNAMIC) {
+    if (buf->buftype == K5BUF_DYNAMIC)
         free(buf->data);
-        buf->data = NULL;
-    }
-    buf->buftype = BUFTYPE_ERROR;
+    set_error(buf);
     return 0;
 }
 
@@ -87,25 +102,25 @@ void
 k5_buf_init_fixed(struct k5buf *buf, char *data, size_t space)
 {
     assert(space > 0);
-    buf->buftype = BUFTYPE_FIXED;
+    buf->buftype = K5BUF_FIXED;
     buf->data = data;
     buf->space = space;
     buf->len = 0;
-    buf->data[0] = '\0';
+    *endptr(buf) = '\0';
 }
 
 void
 k5_buf_init_dynamic(struct k5buf *buf)
 {
-    buf->buftype = BUFTYPE_DYNAMIC;
-    buf->space = DYNAMIC_INITIAL_SIZE;
+    buf->buftype = K5BUF_DYNAMIC;
+    buf->space = 128;
     buf->data = malloc(buf->space);
     if (buf->data == NULL) {
-        buf->buftype = BUFTYPE_ERROR;
+        set_error(buf);
         return;
     }
     buf->len = 0;
-    buf->data[0] = '\0';
+    *endptr(buf) = '\0';
 }
 
 void
@@ -115,14 +130,14 @@ k5_buf_add(struct k5buf *buf, const char *data)
 }
 
 void
-k5_buf_add_len(struct k5buf *buf, const char *data, size_t len)
+k5_buf_add_len(struct k5buf *buf, const void *data, size_t len)
 {
     if (!ensure_space(buf, len))
         return;
     if (len > 0)
-        memcpy(buf->data + buf->len, data, len);
+        memcpy(endptr(buf), data, len);
     buf->len += len;
-    buf->data[buf->len] = '\0';
+    *endptr(buf) = '\0';
 }
 
 void
@@ -133,26 +148,26 @@ k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
     size_t remaining;
     char *tmp;
 
-    if (buf->buftype == BUFTYPE_ERROR)
+    if (buf->buftype == K5BUF_ERROR)
         return;
     remaining = buf->space - buf->len;
 
-    if (buf->buftype == BUFTYPE_FIXED) {
+    if (buf->buftype == K5BUF_FIXED) {
         /* Format the data directly into the fixed buffer. */
         va_start(ap, fmt);
-        r = vsnprintf(buf->data + buf->len, remaining, fmt, ap);
+        r = vsnprintf(endptr(buf), remaining, fmt, ap);
         va_end(ap);
         if (SNPRINTF_OVERFLOW(r, remaining))
-            buf->buftype = BUFTYPE_ERROR;
+            set_error(buf);
         else
             buf->len += (unsigned int) r;
         return;
     }
 
     /* Optimistically format the data directly into the dynamic buffer. */
-    assert(buf->buftype == BUFTYPE_DYNAMIC);
+    assert(buf->buftype == K5BUF_DYNAMIC);
     va_start(ap, fmt);
-    r = vsnprintf(buf->data + buf->len, remaining, fmt, ap);
+    r = vsnprintf(endptr(buf), remaining, fmt, ap);
     va_end(ap);
     if (!SNPRINTF_OVERFLOW(r, remaining)) {
         buf->len += (unsigned int) r;
@@ -165,10 +180,10 @@ k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
             return;
         remaining = buf->space - buf->len;
         va_start(ap, fmt);
-        r = vsnprintf(buf->data + buf->len, remaining, fmt, ap);
+        r = vsnprintf(endptr(buf), remaining, fmt, ap);
         va_end(ap);
         if (SNPRINTF_OVERFLOW(r, remaining))  /* Shouldn't ever happen. */
-            buf->buftype = BUFTYPE_ERROR;
+            k5_buf_free(buf);
         else
             buf->len += (unsigned int) r;
         return;
@@ -180,12 +195,12 @@ k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
     r = vasprintf(&tmp, fmt, ap);
     va_end(ap);
     if (r < 0) {
-        buf->buftype = BUFTYPE_ERROR;
+        k5_buf_free(buf);
         return;
     }
     if (ensure_space(buf, r)) {
         /* Copy the temporary string into buf, including terminator. */
-        memcpy(buf->data + buf->len, tmp, r + 1);
+        memcpy(endptr(buf), tmp, r + 1);
         buf->len += r;
     }
     free(tmp);
@@ -197,40 +212,32 @@ k5_buf_get_space(struct k5buf *buf, size_t len)
     if (!ensure_space(buf, len))
         return NULL;
     buf->len += len;
-    buf->data[buf->len] = '\0';
-    return &buf->data[buf->len - len];
+    *endptr(buf) = '\0';
+    return endptr(buf) - len;
 }
 
 void
 k5_buf_truncate(struct k5buf *buf, size_t len)
 {
-    if (buf->buftype == BUFTYPE_ERROR)
+    if (buf->buftype == K5BUF_ERROR)
         return;
     assert(len <= buf->len);
     buf->len = len;
-    buf->data[buf->len] = '\0';
+    *endptr(buf) = '\0';
 }
 
-
-char *
-k5_buf_data(struct k5buf *buf)
+int
+k5_buf_status(struct k5buf *buf)
 {
-    return (buf->buftype == BUFTYPE_ERROR) ? NULL : buf->data;
-}
-
-ssize_t
-k5_buf_len(struct k5buf *buf)
-{
-    return (buf->buftype == BUFTYPE_ERROR) ? -1 : (ssize_t) buf->len;
+    return (buf->buftype == K5BUF_ERROR) ? ENOMEM : 0;
 }
 
 void
-k5_free_buf(struct k5buf *buf)
+k5_buf_free(struct k5buf *buf)
 {
-    if (buf->buftype == BUFTYPE_ERROR)
+    if (buf->buftype == K5BUF_ERROR)
         return;
-    assert(buf->buftype == BUFTYPE_DYNAMIC);
+    assert(buf->buftype == K5BUF_DYNAMIC);
     free(buf->data);
-    buf->data = NULL;
-    buf->buftype = BUFTYPE_ERROR;
+    set_error(buf);
 }
diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports
index b5ca0b2..1aa2428 100644
--- a/src/util/support/libkrb5support-fixed.exports
+++ b/src/util/support/libkrb5support-fixed.exports
@@ -8,9 +8,8 @@ k5_buf_add_len
 k5_buf_add_fmt
 k5_buf_get_space
 k5_buf_truncate
-k5_buf_data
-k5_buf_len
-k5_free_buf
+k5_buf_status
+k5_buf_free
 k5_set_error
 k5_vset_error
 k5_get_error
diff --git a/src/util/support/t_k5buf.c b/src/util/support/t_k5buf.c
index 5e660d4..ba86851 100644
--- a/src/util/support/t_k5buf.c
+++ b/src/util/support/t_k5buf.c
@@ -24,7 +24,8 @@
  * or implied warranty.
  */
 
-#include "k5buf-int.h"
+#include "k5-platform.h"
+#include "k5-buf.h"
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -41,47 +42,45 @@ fail_if(int condition, const char *name)
 static void
 check_buf(struct k5buf *buf, const char *name)
 {
-    fail_if(buf->buftype != BUFTYPE_FIXED && buf->buftype != BUFTYPE_DYNAMIC
-            && buf->buftype != BUFTYPE_ERROR, name);
-    if (buf->buftype == BUFTYPE_ERROR)
-        return;
-    fail_if(buf->space == 0, name);
-    fail_if(buf->space > SPACE_MAX, name);
-    fail_if(buf->len >= buf->space, name);
-    fail_if(buf->data[buf->len] != 0, name);
+    fail_if(buf->buftype != K5BUF_FIXED && buf->buftype != K5BUF_DYNAMIC &&
+            buf->buftype != K5BUF_ERROR, name);
+    if (buf->buftype == K5BUF_ERROR) {
+        fail_if(buf->data != NULL, name);
+        fail_if(buf->space != 0 || buf->len != 0, name);
+    } else {
+        fail_if(buf->space == 0, name);
+        fail_if(buf->len >= buf->space, name);
+        fail_if(((char *)buf->data)[buf->len] != 0, name);
+    }
 }
 
 static void
 test_basic()
 {
     struct k5buf buf;
-    char storage[1024], *s;
-    ssize_t len;
+    char storage[1024];
 
     k5_buf_init_fixed(&buf, storage, sizeof(storage));
     k5_buf_add(&buf, "Hello ");
     k5_buf_add_len(&buf, "world", 5);
     check_buf(&buf, "basic fixed");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(!s || strcmp(s, "Hello world") != 0 || len != 11, "basic fixed");
+    fail_if(buf.data == NULL || buf.len != 11, "basic fixed");
+    fail_if(strcmp(buf.data, "Hello world") != 0, "basic fixed");
 
     k5_buf_init_dynamic(&buf);
     k5_buf_add_len(&buf, "Hello", 5);
     k5_buf_add(&buf, " world");
     check_buf(&buf, "basic dynamic");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(!s || strcmp(s, "Hello world") != 0 || len != 11, "basic dynamic");
-    k5_free_buf(&buf);
+    fail_if(buf.data == NULL || buf.len != 11, "basic dynamic");
+    fail_if(strcmp(buf.data, "Hello world") != 0, "basic dynamic");
+    k5_buf_free(&buf);
 }
 
 static void
 test_realloc()
 {
     struct k5buf buf;
-    char data[1024], *s;
-    ssize_t len;
+    char data[1024];
     size_t i;
 
     for (i = 0; i < sizeof(data); i++)
@@ -93,18 +92,16 @@ test_realloc()
     k5_buf_add_len(&buf, data, 128);
     fail_if(buf.space != 256, "realloc 1");
     check_buf(&buf, "realloc 1");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(!s || len != 138 || memcmp(s, data, len) != 0, "realloc 1");
+    fail_if(buf.data == NULL || buf.len != 138, "realloc 1");
+    fail_if(memcmp(buf.data, data, buf.len) != 0, "realloc 1");
 
     /* Cause the same buffer to double in size to 512 bytes. */
     k5_buf_add_len(&buf, data, 128);
     fail_if(buf.space != 512, "realloc 2");
     check_buf(&buf, "realloc 2");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(!s || len != 266 || memcmp(s, data, len) != 0, "realloc 2");
-    k5_free_buf(&buf);
+    fail_if(buf.data == NULL || buf.len != 266, "realloc 2");
+    fail_if(memcmp(buf.data, data, buf.len) != 0, "realloc 2");
+    k5_buf_free(&buf);
 
     /* Cause a buffer to increase from 128 to 512 bytes directly. */
     k5_buf_init_dynamic(&buf);
@@ -112,10 +109,9 @@ test_realloc()
     k5_buf_add_len(&buf, data, 256);
     fail_if(buf.space != 512, "realloc 3");
     check_buf(&buf, "realloc 3");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(!s || len != 266 || memcmp(s, data, len) != 0, "realloc 3");
-    k5_free_buf(&buf);
+    fail_if(buf.data == NULL || buf.len != 266, "realloc 3");
+    fail_if(memcmp(buf.data, data, buf.len) != 0, "realloc 3");
+    k5_buf_free(&buf);
 
     /* Cause a buffer to increase from 128 to 1024 bytes directly. */
     k5_buf_init_dynamic(&buf);
@@ -123,60 +119,38 @@ test_realloc()
     k5_buf_add_len(&buf, data, 512);
     fail_if(buf.space != 1024, "realloc 4");
     check_buf(&buf, "realloc 4");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(!s || len != 522 || memcmp(s, data, len) != 0, "realloc 4");
-    k5_free_buf(&buf);
-
-    /* Cause a reallocation to fail by exceeding SPACE_MAX. */
-    k5_buf_init_dynamic(&buf);
-    k5_buf_add_len(&buf, data, 10);
-    k5_buf_add_len(&buf, NULL, SPACE_MAX);
-    check_buf(&buf, "realloc 5");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(buf.buftype != BUFTYPE_ERROR || s != NULL || len != -1,
-            "realloc 5");
-    k5_free_buf(&buf);
+    fail_if(buf.data == NULL || buf.len != 522, "realloc 4");
+    fail_if(memcmp(buf.data, data, buf.len) != 0, "realloc 4");
+    k5_buf_free(&buf);
 
     /* Cause a reallocation to fail by integer overflow. */
     k5_buf_init_dynamic(&buf);
     k5_buf_add_len(&buf, data, 100);
-    k5_buf_add_len(&buf, NULL, SPACE_MAX * 2);
-    check_buf(&buf, "realloc 6");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(buf.buftype != BUFTYPE_ERROR || s != NULL || len != -1,
-            "realloc 6");
-    k5_free_buf(&buf);
+    k5_buf_add_len(&buf, NULL, SIZE_MAX);
+    check_buf(&buf, "realloc 5");
+    fail_if(buf.buftype != K5BUF_ERROR, "realloc 5");
+    k5_buf_free(&buf);
 }
 
 static void
 test_overflow()
 {
     struct k5buf buf;
-    char storage[10], *s;
-    ssize_t len;
+    char storage[10];
 
     /* Cause a fixed-sized buffer overflow. */
     k5_buf_init_fixed(&buf, storage, sizeof(storage));
     k5_buf_add(&buf, "12345");
     k5_buf_add(&buf, "12345");
     check_buf(&buf, "overflow 1");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(buf.buftype != BUFTYPE_ERROR || s != NULL || len != -1,
-            "overflow 1");
+    fail_if(buf.buftype != K5BUF_ERROR, "overflow 1");
 
     /* Cause a fixed-sized buffer overflow with integer overflow. */
     k5_buf_init_fixed(&buf, storage, sizeof(storage));
     k5_buf_add(&buf, "12345");
-    k5_buf_add_len(&buf, NULL, SPACE_MAX * 2);
+    k5_buf_add_len(&buf, NULL, SIZE_MAX);
     check_buf(&buf, "overflow 2");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(buf.buftype != BUFTYPE_ERROR || s != NULL || len != -1,
-            "overflow 2");
+    fail_if(buf.buftype != K5BUF_ERROR, "overflow 2");
 }
 
 static void
@@ -188,7 +162,7 @@ test_error()
     /* Cause an overflow and then perform actions afterwards. */
     k5_buf_init_fixed(&buf, storage, sizeof(storage));
     k5_buf_add(&buf, "1");
-    fail_if(buf.buftype != BUFTYPE_ERROR, "error");
+    fail_if(buf.buftype != K5BUF_ERROR, "error");
     check_buf(&buf, "error");
     k5_buf_add(&buf, "test");
     check_buf(&buf, "error");
@@ -196,52 +170,46 @@ test_error()
     check_buf(&buf, "error");
     k5_buf_truncate(&buf, 3);
     check_buf(&buf, "error");
-    fail_if(buf.buftype != BUFTYPE_ERROR, "error");
+    fail_if(buf.buftype != K5BUF_ERROR, "error");
 }
 
 static void
 test_truncate()
 {
     struct k5buf buf;
-    char *s;
-    ssize_t len;
 
     k5_buf_init_dynamic(&buf);
     k5_buf_add(&buf, "abcde");
     k5_buf_add(&buf, "fghij");
     k5_buf_truncate(&buf, 7);
     check_buf(&buf, "truncate");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(!s || len != 7 || strcmp(s, "abcdefg") != 0, "truncate");
-    k5_free_buf(&buf);
+    fail_if(buf.data == NULL || buf.len != 7, "truncate");
+    fail_if(strcmp(buf.data, "abcdefg") != 0, "truncate");
+    k5_buf_free(&buf);
 }
 
 static void
 test_binary()
 {
     struct k5buf buf;
-    char *s, data[] = { 'a', 0, 'b' };
-    ssize_t len;
+    char data[] = { 'a', 0, 'b' }, *s;
 
     k5_buf_init_dynamic(&buf);
     k5_buf_add_len(&buf, data, 3);
     k5_buf_add_len(&buf, data, 3);
     check_buf(&buf, "binary");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(!s || len != 6, "binary");
+    fail_if(buf.data == NULL || buf.len != 6, "binary");
+    s = buf.data;
     fail_if(s[0] != 'a' || s[1] != 0 || s[2] != 'b', "binary");
     fail_if(s[3] != 'a' || s[4] != 0 || s[5] != 'b', "binary");
-    k5_free_buf(&buf);
+    k5_buf_free(&buf);
 }
 
 static void
 test_fmt()
 {
     struct k5buf buf;
-    char *s, storage[10], data[1024];
-    ssize_t len;
+    char storage[10], data[1024];
     size_t i;
 
     for (i = 0; i < sizeof(data) - 1; i++)
@@ -253,34 +221,29 @@ test_fmt()
     k5_buf_add(&buf, "foo");
     k5_buf_add_fmt(&buf, " %d ", 3);
     check_buf(&buf, "fmt 1");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(!s || len != 6 || strcmp(s, "foo 3 ") != 0, "fmt 1");
+    fail_if(buf.data == NULL || buf.len != 6, "fmt 1");
+    fail_if(strcmp(buf.data, "foo 3 ") != 0, "fmt 1");
 
     /* Overflow the same buffer with formatted text. */
     k5_buf_add_fmt(&buf, "%d%d%d%d", 1, 2, 3, 4);
     check_buf(&buf, "fmt 2");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(buf.buftype != BUFTYPE_ERROR || s != NULL || len != -1, "fmt 2");
+    fail_if(buf.buftype != K5BUF_ERROR, "fmt 2");
 
     /* Format some text into a non-empty dynamic buffer. */
     k5_buf_init_dynamic(&buf);
     k5_buf_add(&buf, "foo");
     k5_buf_add_fmt(&buf, " %d ", 3);
     check_buf(&buf, "fmt 3");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(!s || len != 6 || strcmp(s, "foo 3 ") != 0, "fmt 3");
+    fail_if(buf.data == NULL || buf.len != 6, "fmt 3");
+    fail_if(strcmp(buf.data, "foo 3 ") != 0, "fmt 3");
 
     /* Format more text into the same buffer, causing a big resize. */
     k5_buf_add_fmt(&buf, "%s", data);
     check_buf(&buf, "fmt 4");
     fail_if(buf.space != 2048, "fmt 4");
-    s = k5_buf_data(&buf);
-    len = k5_buf_len(&buf);
-    fail_if(!s || len != 1029 || strcmp(s + 6, data) != 0, "fmt 4");
-    k5_free_buf(&buf);
+    fail_if(buf.data == NULL || buf.len != 1029, "fmt 4");
+    fail_if(strcmp((char *)buf.data + 6, data) != 0, "fmt 4");
+    k5_buf_free(&buf);
 }
 
 int


More information about the cvs-krb5 mailing list