krb5 commit: Make internal JSON functions return error codes

Greg Hudson ghudson at MIT.EDU
Wed Feb 13 15:53:43 EST 2013


https://github.com/krb5/krb5/commit/61116eb28a7520dda1e5febba95ac6ba1e70e6ac
commit 61116eb28a7520dda1e5febba95ac6ba1e70e6ac
Author: Greg Hudson <ghudson at mit.edu>
Date:   Tue Oct 30 17:17:45 2012 -0400

    Make internal JSON functions return error codes
    
    Return error codes (0, ENOMEM, or EINVAL) from JSON support functions
    instead of returning results directly.  This makes error handling
    simpler for functions which assemble JSON objects and then return a
    krb5_error_code values.  Adjust all callers.  Use shims in
    export_cred.c to minimize changes there; it will be redesigned
    internally in a subsequent commit.

 src/include/k5-json.h             |   36 ++--
 src/lib/gssapi/krb5/export_cred.c |  151 ++++++-----
 src/lib/gssapi/krb5/import_cred.c |   21 +-
 src/lib/krb5/krb/get_in_tkt.c     |   32 +--
 src/lib/krb5/krb/init_creds_ctx.h |    4 +-
 src/lib/krb5/krb/preauth2.c       |   19 +-
 src/lib/krb5/krb/preauth_otp.c    |  128 ++++-----
 src/util/support/json.c           |  536 +++++++++++++++++++++----------------
 src/util/support/t_json.c         |   83 +++---
 9 files changed, 546 insertions(+), 464 deletions(-)

diff --git a/src/include/k5-json.h b/src/include/k5-json.h
index 42dcfa3..4b9b8fe 100644
--- a/src/include/k5-json.h
+++ b/src/include/k5-json.h
@@ -93,13 +93,12 @@ k5_json_tid k5_json_get_tid(k5_json_value val);
  * decrement the refcount, possibly freeing the value.  k5_json_retain returns
  * its argument and always succeeds.  Both functions gracefully accept NULL.
  */
-void *k5_json_retain(k5_json_value val);
+k5_json_value k5_json_retain(k5_json_value val);
 void k5_json_release(k5_json_value val);
 
 /*
- * Unless otherwise specified, the following functions return NULL on error
- * (generally only if out of memory) if they return a pointer type, or 0 on
- * success and -1 on failure if they return int.
+ * If a k5_json_* function can fail, it returns 0 on success and an errno value
+ * on failure.
  */
 
 /*
@@ -108,7 +107,7 @@ void k5_json_release(k5_json_value val);
 
 typedef struct k5_json_null_st *k5_json_null;
 
-k5_json_null k5_json_null_create(void);
+int k5_json_null_create(k5_json_null *null_out);
 
 /*
  * Boolean
@@ -116,7 +115,7 @@ k5_json_null k5_json_null_create(void);
 
 typedef struct k5_json_bool_st *k5_json_bool;
 
-k5_json_bool k5_json_bool_create(int truth);
+int k5_json_bool_create(int truth, k5_json_bool *val_out);
 int k5_json_bool_value(k5_json_bool bval);
 
 /*
@@ -125,7 +124,7 @@ int k5_json_bool_value(k5_json_bool bval);
 
 typedef struct k5_json_array_st *k5_json_array;
 
-k5_json_array k5_json_array_create(void);
+int k5_json_array_create(k5_json_array *val_out);
 size_t k5_json_array_length(k5_json_array array);
 
 /* Both of these functions increment the reference count on val. */
@@ -144,7 +143,7 @@ typedef struct k5_json_object_st *k5_json_object;
 typedef void (*k5_json_object_iterator_fn)(void *arg, const char *key,
                                            k5_json_value val);
 
-k5_json_object k5_json_object_create(void);
+int k5_json_object_create(k5_json_object *val_out);
 void k5_json_object_iterate(k5_json_object obj,
                             k5_json_object_iterator_fn func, void *arg);
 
@@ -164,16 +163,19 @@ k5_json_value k5_json_object_get(k5_json_object obj, const char *key);
 
 typedef struct k5_json_string_st *k5_json_string;
 
-k5_json_string k5_json_string_create(const char *string);
-k5_json_string k5_json_string_create_len(const void *data, size_t len);
+int k5_json_string_create(const char *cstring, k5_json_string *val_out);
+int k5_json_string_create_len(const void *data, size_t len,
+                              k5_json_string *val_out);
 const char *k5_json_string_utf8(k5_json_string string);
 
+
 /* Create a base64 string value from binary data. */
-k5_json_string k5_json_string_create_base64(const void *data, size_t len);
+int k5_json_string_create_base64(const void *data, size_t len,
+                                 k5_json_string *val_out);
 
-/* Decode a base64 string.  Returns NULL and *len_out == 0 if out of memory,
- * NULL and *len == SIZE_MAX if string's contents aren't valid base64. */
-void *k5_json_string_unbase64(k5_json_string string, size_t *len_out);
+/* Decode the base64 contents of string. */
+int k5_json_string_unbase64(k5_json_string string, unsigned char **data_out,
+                            size_t *len_out);
 
 /*
  * Number
@@ -181,14 +183,14 @@ void *k5_json_string_unbase64(k5_json_string string, size_t *len_out);
 
 typedef struct k5_json_number_st *k5_json_number;
 
-k5_json_number k5_json_number_create(long long number);
+int k5_json_number_create(long long number, k5_json_number *val_out);
 long long k5_json_number_value(k5_json_number number);
 
 /*
  * JSON encoding and decoding
  */
 
-char *k5_json_encode(k5_json_value val);
-k5_json_value k5_json_decode(const char *str);
+int k5_json_encode(k5_json_value val, char **json_out);
+int k5_json_decode(const char *str, k5_json_value *val_out);
 
 #endif /* K5_JSON_H */
diff --git a/src/lib/gssapi/krb5/export_cred.c b/src/lib/gssapi/krb5/export_cred.c
index 1625479..e5ffaf5 100644
--- a/src/lib/gssapi/krb5/export_cred.c
+++ b/src/lib/gssapi/krb5/export_cred.c
@@ -44,12 +44,51 @@ add(k5_json_array array, k5_json_value v)
     return 0;
 }
 
+static inline k5_json_number
+number(long long nval)
+{
+    k5_json_number num;
+
+    return k5_json_number_create(nval, &num) ? NULL : num;
+}
+
+static inline k5_json_string
+string(const char *cstring)
+{
+    k5_json_string str;
+
+    return k5_json_string_create(cstring, &str) ? NULL : str;
+}
+
+static inline k5_json_string
+base64string(const void *data, size_t len)
+{
+    k5_json_string str;
+
+    return k5_json_string_create_base64(data, len, &str) ? NULL : str;
+}
+
+static inline k5_json_null
+null(void)
+{
+    k5_json_null n;
+
+    return k5_json_null_create(&n) ? NULL : n;
+}
+
+static inline k5_json_bool
+bool(int truth)
+{
+    k5_json_bool b;
+
+    return k5_json_bool_create(truth, &b) ? NULL : b;
+}
+
 /* Return a JSON null or string value representing str. */
 static k5_json_value
 json_optional_string(const char *str)
 {
-    return (str == NULL) ? (k5_json_value)k5_json_null_create() :
-        (k5_json_value)k5_json_string_create(str);
+    return (str == NULL) ? (k5_json_value)null() : string(str);
 }
 
 /* Return a JSON null or array value representing princ. */
@@ -60,10 +99,10 @@ json_principal(krb5_context context, krb5_principal princ)
     k5_json_string str;
 
     if (princ == NULL)
-        return k5_json_null_create();
+        return null();
     if (krb5_unparse_name(context, princ, &princname))
         return NULL;
-    str = k5_json_string_create(princname);
+    str = string(princname);
     krb5_free_unparsed_name(context, princname);
     return str;
 }
@@ -75,12 +114,11 @@ json_etypes(krb5_enctype *etypes)
     k5_json_array array;
 
     if (etypes == NULL)
-        return k5_json_null_create();
-    array = k5_json_array_create();
-    if (array == NULL)
+        return null();
+    if (k5_json_array_create(&array))
         return NULL;
     for (; *etypes != 0; etypes++) {
-        if (add(array, k5_json_number_create(*etypes)))
+        if (add(array, number(*etypes)))
             goto oom;
     }
     return array;
@@ -96,9 +134,8 @@ json_kgname(krb5_context context, krb5_gss_name_t name)
     k5_json_array array;
 
     if (name == NULL)
-        return k5_json_null_create();
-    array = k5_json_array_create();
-    if (array == NULL)
+        return null();
+    if (k5_json_array_create(&array))
         return NULL;
     if (add(array, json_principal(context, name->princ)))
         goto oom;
@@ -119,10 +156,10 @@ json_keytab(krb5_context context, krb5_keytab keytab)
     char name[1024];
 
     if (keytab == NULL)
-        return k5_json_null_create();
+        return null();
     if (krb5_kt_get_name(context, keytab, name, sizeof(name)))
         return NULL;
-    return k5_json_string_create(name);
+    return string(name);
 }
 
 /* Return a JSON null or string value representing rcache. */
@@ -133,11 +170,11 @@ json_rcache(krb5_context context, krb5_rcache rcache)
     k5_json_string str;
 
     if (rcache == NULL)
-        return k5_json_null_create();
+        return null();
     if (asprintf(&name, "%s:%s", krb5_rc_get_type(context, rcache),
                  krb5_rc_get_name(context, rcache)) < 0)
         return NULL;
-    str = k5_json_string_create(name);
+    str = string(name);
     free(name);
     return str;
 }
@@ -148,13 +185,11 @@ json_keyblock(krb5_keyblock *keyblock)
 {
     k5_json_array array;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
-    if (add(array, k5_json_number_create(keyblock->enctype)))
+    if (add(array, number(keyblock->enctype)))
         goto oom;
-    if (add(array, k5_json_string_create_base64(keyblock->contents,
-                                                keyblock->length)))
+    if (add(array, base64string(keyblock->contents, keyblock->length)))
         goto oom;
     return array;
 oom:
@@ -168,12 +203,11 @@ json_address(krb5_address *addr)
 {
     k5_json_array array;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
-    if (add(array, k5_json_number_create(addr->addrtype)))
+    if (add(array, number(addr->addrtype)))
         goto oom;
-    if (add(array, k5_json_string_create_base64(addr->contents, addr->length)))
+    if (add(array, base64string(addr->contents, addr->length)))
         goto oom;
     return array;
 oom:
@@ -188,9 +222,8 @@ json_addresses(krb5_address **addrs)
     k5_json_array array;
 
     if (addrs == NULL)
-        return k5_json_null_create();
-    array = k5_json_array_create();
-    if (array == NULL)
+        return null();
+    if (k5_json_array_create(&array))
         return NULL;
     for (; *addrs != NULL; addrs++) {
         if (add(array, json_address(*addrs))) {
@@ -207,12 +240,11 @@ json_authdata_element(krb5_authdata *ad)
 {
     k5_json_array array;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
-    if (add(array, k5_json_number_create(ad->ad_type)))
+    if (add(array, number(ad->ad_type)))
         goto oom;
-    if (add(array, k5_json_string_create_base64(ad->contents, ad->length)))
+    if (add(array, base64string(ad->contents, ad->length)))
         goto oom;
     return array;
 oom:
@@ -227,9 +259,8 @@ json_authdata(krb5_authdata **authdata)
     k5_json_array array;
 
     if (authdata == NULL)
-        return k5_json_null_create();
-    array = k5_json_array_create();
-    if (array == NULL)
+        return null();
+    if (k5_json_array_create(&array))
         return NULL;
     for (; *authdata != NULL; authdata++) {
         if (add(array, json_authdata_element(*authdata))) {
@@ -246,8 +277,7 @@ json_creds(krb5_context context, krb5_creds *creds)
 {
     k5_json_array array;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
     if (add(array, json_principal(context, creds->client)))
         goto eom;
@@ -255,25 +285,24 @@ json_creds(krb5_context context, krb5_creds *creds)
         goto eom;
     if (add(array, json_keyblock(&creds->keyblock)))
         goto eom;
-    if (add(array, k5_json_number_create(creds->times.authtime)))
+    if (add(array, number(creds->times.authtime)))
         goto eom;
-    if (add(array, k5_json_number_create(creds->times.starttime)))
+    if (add(array, number(creds->times.starttime)))
         goto eom;
-    if (add(array, k5_json_number_create(creds->times.endtime)))
+    if (add(array, number(creds->times.endtime)))
         goto eom;
-    if (add(array, k5_json_number_create(creds->times.renew_till)))
+    if (add(array, number(creds->times.renew_till)))
         goto eom;
-    if (add(array, k5_json_bool_create(creds->is_skey)))
+    if (add(array, bool(creds->is_skey)))
         goto eom;
-    if (add(array, k5_json_number_create(creds->ticket_flags)))
+    if (add(array, number(creds->ticket_flags)))
         goto eom;
     if (add(array, json_addresses(creds->addresses)))
         goto eom;
-    if (add(array, k5_json_string_create_base64(creds->ticket.data,
-                                                creds->ticket.length)))
+    if (add(array, base64string(creds->ticket.data, creds->ticket.length)))
         goto eom;
-    if (add(array, k5_json_string_create_base64(creds->second_ticket.data,
-                                                creds->second_ticket.length)))
+    if (add(array, base64string(creds->second_ticket.data,
+                                creds->second_ticket.length)))
         goto eom;
     if (add(array, json_authdata(creds->authdata)))
         goto eom;
@@ -294,8 +323,7 @@ json_ccache_contents(krb5_context context, krb5_ccache ccache)
     k5_json_array array;
     int st;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
 
     /* Put the principal in the first array entry. */
@@ -334,13 +362,13 @@ json_ccache(krb5_context context, krb5_ccache ccache)
     k5_json_string str;
 
     if (ccache == NULL)
-        return k5_json_null_create();
+        return null();
     if (strcmp(krb5_cc_get_type(context, ccache), "MEMORY") == 0) {
         return json_ccache_contents(context, ccache);
     } else {
         if (krb5_cc_get_full_name(context, ccache, &name))
             return NULL;
-        str = k5_json_string_create(name);
+        str = string(name);
         free(name);
         return str;
     }
@@ -352,18 +380,17 @@ json_kgcred(krb5_context context, krb5_gss_cred_id_t cred)
 {
     k5_json_array array;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
-    if (add(array, k5_json_number_create(cred->usage)))
+    if (add(array, number(cred->usage)))
         goto oom;
     if (add(array, json_kgname(context, cred->name)))
         goto oom;
     if (add(array, json_principal(context, cred->impersonator)))
         goto oom;
-    if (add(array, k5_json_bool_create(cred->default_identity)))
+    if (add(array, bool(cred->default_identity)))
         goto oom;
-    if (add(array, k5_json_bool_create(cred->iakerb_mech)))
+    if (add(array, bool(cred->iakerb_mech)))
         goto oom;
     /* Don't marshal cred->destroy_ccache. */
     if (add(array, json_keytab(context, cred->keytab)))
@@ -374,11 +401,11 @@ json_kgcred(krb5_context context, krb5_gss_cred_id_t cred)
         goto oom;
     if (add(array, json_keytab(context, cred->client_keytab)))
         goto oom;
-    if (add(array, k5_json_bool_create(cred->have_tgt)))
+    if (add(array, bool(cred->have_tgt)))
         goto oom;
-    if (add(array, k5_json_number_create(cred->expire)))
+    if (add(array, number(cred->expire)))
         goto oom;
-    if (add(array, k5_json_number_create(cred->refresh_time)))
+    if (add(array, number(cred->refresh_time)))
         goto oom;
     if (add(array, json_etypes(cred->req_enctypes)))
         goto oom;
@@ -414,16 +441,14 @@ krb5_gss_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
         return status;
     cred = (krb5_gss_cred_id_t)cred_handle;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         goto oom;
-    if (add(array, k5_json_string_create(CRED_EXPORT_MAGIC)))
+    if (add(array, string(CRED_EXPORT_MAGIC)))
         goto oom;
     if (add(array, json_kgcred(context, cred)))
         goto oom;
 
-    str = k5_json_encode(array);
-    if (str == NULL)
+    if (k5_json_encode(array, &str))
         goto oom;
     d = string2data(str);
     if (data_to_gss(&d, token))
diff --git a/src/lib/gssapi/krb5/import_cred.c b/src/lib/gssapi/krb5/import_cred.c
index ad9a111..973b9d0 100644
--- a/src/lib/gssapi/krb5/import_cred.c
+++ b/src/lib/gssapi/krb5/import_cred.c
@@ -205,8 +205,7 @@ json_to_keyblock(k5_json_value v, krb5_keyblock *keyblock)
     s = check_element(array, 1, K5_JSON_TID_STRING);
     if (s == NULL)
         return -1;
-    keyblock->contents = k5_json_string_unbase64(s, &len);
-    if (keyblock->contents == NULL)
+    if (k5_json_string_unbase64(s, &keyblock->contents, &len))
         return -1;
     keyblock->length = len;
     keyblock->magic = KV5M_KEYBLOCK;
@@ -241,8 +240,7 @@ json_to_address(k5_json_value v, krb5_address **addr_out)
     if (addr == NULL)
         return -1;
     addr->addrtype = k5_json_number_value(n);
-    addr->contents = k5_json_string_unbase64(s, &len);
-    if (addr->contents == NULL) {
+    if (k5_json_string_unbase64(s, &addr->contents, &len)) {
         free(addr);
         return -1;
     }
@@ -311,8 +309,7 @@ json_to_authdata_element(k5_json_value v, krb5_authdata **ad_out)
     if (ad == NULL)
         return -1;
     ad->ad_type = k5_json_number_value(n);
-    ad->contents = k5_json_string_unbase64(s, &len);
-    if (ad->contents == NULL) {
+    if (k5_json_string_unbase64(s, &ad->contents, &len)) {
         free(ad);
         return -1;
     }
@@ -361,6 +358,7 @@ json_to_creds(krb5_context context, k5_json_value v, krb5_creds *creds)
     k5_json_number n;
     k5_json_bool b;
     k5_json_string s;
+    unsigned char *data;
     size_t len;
 
     memset(creds, 0, sizeof(*creds));
@@ -418,17 +416,17 @@ json_to_creds(krb5_context context, k5_json_value v, krb5_creds *creds)
     s = check_element(array, 10, K5_JSON_TID_STRING);
     if (s == NULL)
         goto invalid;
-    creds->ticket.data = k5_json_string_unbase64(s, &len);
-    if (creds->ticket.data == NULL)
+    if (k5_json_string_unbase64(s, &data, &len))
         goto invalid;
+    creds->ticket.data = (char *)data;
     creds->ticket.length = len;
 
     s = check_element(array, 11, K5_JSON_TID_STRING);
     if (s == NULL)
         goto invalid;
-    creds->second_ticket.data = k5_json_string_unbase64(s, &len);
-    if (creds->second_ticket.data == NULL)
+    if (k5_json_string_unbase64(s, &data, &len))
         goto invalid;
+    creds->second_ticket.data = (char *)data;
     creds->second_ticket.length = len;
 
     if (json_to_authdata(context, k5_json_array_get(array, 12),
@@ -620,8 +618,7 @@ krb5_gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token,
         *minor_status = ret;
         goto cleanup;
     }
-    v = k5_json_decode(copy);
-    if (v == NULL)
+    if (k5_json_decode(copy, &v))
         goto invalid;
 
     /* Decode the CRED_EXPORT_MAGIC array wrapper. */
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index c88b67a..dfec991 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -1124,24 +1124,21 @@ save_selected_preauth_type(krb5_context context, krb5_ccache ccache,
 static krb5_error_code
 clear_cc_config_out_data(krb5_context context, krb5_init_creds_context ctx)
 {
-    if (ctx->cc_config_out != NULL)
-        k5_json_release(ctx->cc_config_out);
-    ctx->cc_config_out = k5_json_object_create();
-    if (ctx->cc_config_out == NULL)
-        return ENOMEM;
-    return 0;
+    k5_json_release(ctx->cc_config_out);
+    ctx->cc_config_out = NULL;
+    return k5_json_object_create(&ctx->cc_config_out);
 }
 
 static krb5_error_code
 read_cc_config_in_data(krb5_context context, krb5_init_creds_context ctx)
 {
+    k5_json_value val;
     krb5_data config;
     char *encoded;
     krb5_error_code code;
     int i;
 
-    if (ctx->cc_config_in != NULL)
-        k5_json_release(ctx->cc_config_in);
+    k5_json_release(ctx->cc_config_in);
     ctx->cc_config_in = NULL;
 
     if (ctx->opte->opt_private->in_ccache == NULL)
@@ -1159,16 +1156,15 @@ read_cc_config_in_data(krb5_context context, krb5_init_creds_context ctx)
     if (i < 0)
         return ENOMEM;
 
-    ctx->cc_config_in = k5_json_decode(encoded);
+    code = k5_json_decode(encoded, &val);
     free(encoded);
-    if (ctx->cc_config_in == NULL)
-        return ENOMEM;
-    if (k5_json_get_tid(ctx->cc_config_in) != K5_JSON_TID_OBJECT) {
-        k5_json_release(ctx->cc_config_in);
-        ctx->cc_config_in = NULL;
+    if (code)
+        return code;
+    if (k5_json_get_tid(val) != K5_JSON_TID_OBJECT) {
+        k5_json_release(val);
         return EINVAL;
     }
-
+    ctx->cc_config_in = val;
     return 0;
 }
 
@@ -1183,9 +1179,9 @@ save_cc_config_out_data(krb5_context context, krb5_ccache ccache,
     if (ctx->cc_config_out == NULL ||
         k5_json_object_count(ctx->cc_config_out) == 0)
         return 0;
-    encoded = k5_json_encode(ctx->cc_config_out);
-    if (encoded == NULL)
-        return ENOMEM;
+    code = k5_json_encode(ctx->cc_config_out, &encoded);
+    if (code)
+        return code;
     config = string2data(encoded);
     code = krb5_cc_set_config(context, ccache, ctx->cred.server,
                               KRB5_CC_CONF_PA_CONFIG_DATA, &config);
diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h
index 5d81391..1bc90a5 100644
--- a/src/lib/krb5/krb/init_creds_ctx.h
+++ b/src/lib/krb5/krb/init_creds_ctx.h
@@ -50,8 +50,8 @@ struct _krb5_init_creds_context {
     struct krb5_responder_context_st rctx;
     krb5_preauthtype selected_preauth_type;
     krb5_preauthtype allowed_preauth_type;
-    void *cc_config_in;
-    void *cc_config_out;
+    k5_json_object cc_config_in;
+    k5_json_object cc_config_out;
     /* Discovered offset of server time during preauth */
     krb5_timestamp pa_offset;
     krb5_int32 pa_offset_usec;
diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c
index 060f98a..74a4f27 100644
--- a/src/lib/krb5/krb/preauth2.c
+++ b/src/lib/krb5/krb/preauth2.c
@@ -469,22 +469,19 @@ set_cc_config(krb5_context context, krb5_clpreauth_rock rock,
               const char *key, const char *data)
 {
     krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
-    k5_json_value value;
-    int i;
+    krb5_error_code ret;
+    k5_json_string str;
 
     if (ctx->cc_config_out == NULL)
         return ENOENT;
 
-    value = k5_json_string_create(data);
-    if (value == NULL)
-        return ENOMEM;
-
-    i = k5_json_object_set(ctx->cc_config_out, key, value);
-    k5_json_release(value);
-    if (i < 0)
-        return ENOMEM;
+    ret = k5_json_string_create(data, &str);
+    if (ret)
+        return ret;
 
-    return 0;
+    ret = k5_json_object_set(ctx->cc_config_out, key, str);
+    k5_json_release(str);
+    return ret;
 }
 
 static struct krb5_clpreauth_callbacks_st callbacks = {
diff --git a/src/lib/krb5/krb/preauth_otp.c b/src/lib/krb5/krb/preauth_otp.c
index e610d47..d343683 100644
--- a/src/lib/krb5/krb/preauth_otp.c
+++ b/src/lib/krb5/krb/preauth_otp.c
@@ -99,13 +99,13 @@ codec_data_to_value(krb5_data *data, k5_json_object obj, const char *key)
     if (data->data == NULL)
         return 0;
 
-    str = k5_json_string_create_len(data->data, data->length);
-    if (str == NULL)
-        return ENOMEM;
+    retval = k5_json_string_create_len(data->data, data->length, &str);
+    if (retval)
+        return retval;
 
     retval = k5_json_object_set(obj, key, str);
     k5_json_release(str);
-    return retval == 0 ? 0 : ENOMEM;
+    return retval;
 }
 
 /* Converts a property of a json object into a krb5_int32. */
@@ -135,25 +135,25 @@ codec_int32_to_value(krb5_int32 int32, k5_json_object obj, const char *key)
     if (int32 == -1)
         return 0;
 
-    num = k5_json_number_create(int32);
-    if (num == NULL)
-        return ENOMEM;
+    retval = k5_json_number_create(int32, &num);
+    if (retval)
+        return retval;
 
     retval = k5_json_object_set(obj, key, num);
     k5_json_release(num);
-    return retval == 0 ? 0 : ENOMEM;
+    return retval;
 }
 
 /* Converts a krb5_otp_tokeninfo into a JSON object. */
 static krb5_error_code
 codec_encode_tokeninfo(krb5_otp_tokeninfo *ti, k5_json_object *out)
 {
-    krb5_error_code retval = 0;
+    krb5_error_code retval;
     k5_json_object obj;
     krb5_flags flags;
 
-    obj = k5_json_object_create();
-    if (obj == NULL)
+    retval = k5_json_object_create(&obj);
+    if (retval != 0)
         goto error;
 
     flags = KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN;
@@ -212,60 +212,51 @@ codec_encode_challenge(krb5_context ctx, krb5_pa_otp_challenge *chl,
     k5_json_object obj = NULL, tmp = NULL;
     k5_json_string str = NULL;
     k5_json_array arr = NULL;
-    krb5_error_code retval = 0;
+    krb5_error_code retval;
     int i;
 
-    obj = k5_json_object_create();
-    if (obj == NULL)
-        goto error;
+    retval = k5_json_object_create(&obj);
+    if (retval != 0)
+        goto cleanup;
 
     if (chl->service.data) {
-        str = k5_json_string_create_len(chl->service.data,
-                                        chl->service.length);
-        if (str == NULL)
-            goto error;
+        retval = k5_json_string_create_len(chl->service.data,
+                                           chl->service.length, &str);
+        if (retval != 0)
+            goto cleanup;
         retval = k5_json_object_set(obj, "service", str);
         k5_json_release(str);
-        if (retval != 0) {
-            retval = ENOMEM;
-            goto error;
-        }
+        if (retval != 0)
+            goto cleanup;
     }
 
-    arr = k5_json_array_create();
-    if (arr == NULL)
-        goto error;
+    retval = k5_json_array_create(&arr);
+    if (retval != 0)
+        goto cleanup;
 
     for (i = 0; chl->tokeninfo[i] != NULL ; i++) {
         retval = codec_encode_tokeninfo(chl->tokeninfo[i], &tmp);
         if (retval != 0)
-            goto error;
+            goto cleanup;
 
         retval = k5_json_array_add(arr, tmp);
         k5_json_release(tmp);
-        if (retval != 0) {
-            retval = ENOMEM;
-            goto error;
-        }
-    }
-
-    if (k5_json_object_set(obj, "tokenInfo", arr) != 0) {
-        retval = ENOMEM;
-        goto error;
+        if (retval != 0)
+            goto cleanup;
     }
 
-    *json = k5_json_encode(obj);
-    if (*json == NULL)
-        goto error;
+    retval = k5_json_object_set(obj, "tokenInfo", arr);
+    if (retval != 0)
+        goto cleanup;
 
-    k5_json_release(arr);
-    k5_json_release(obj);
-    return 0;
+    retval = k5_json_encode(obj, json);
+    if (retval)
+        goto cleanup;
 
-error:
+cleanup:
     k5_json_release(arr);
     k5_json_release(obj);
-    return retval == 0 ? ENOMEM : retval;
+    return retval;
 }
 
 /* Converts a JSON object into a krb5_responder_otp_tokeninfo. */
@@ -327,8 +318,8 @@ codec_decode_challenge(krb5_context ctx, const char *json)
     krb5_error_code retval;
     size_t i;
 
-    obj = k5_json_decode(json);
-    if (obj == NULL)
+    retval = k5_json_decode(json, &obj);
+    if (retval != 0)
         goto error;
 
     if (k5_json_get_tid(obj) != K5_JSON_TID_OBJECT)
@@ -384,7 +375,7 @@ codec_decode_answer(krb5_context context, const char *answer,
                     krb5_otp_tokeninfo **tis, krb5_otp_tokeninfo **ti,
                     krb5_data *value, krb5_data *pin)
 {
-    krb5_error_code retval = EBADMSG;
+    krb5_error_code retval;
     k5_json_value val = NULL;
     krb5_int32 indx, i;
     krb5_data tmp;
@@ -392,8 +383,8 @@ codec_decode_answer(krb5_context context, const char *answer,
     if (answer == NULL)
         return EBADMSG;
 
-    val = k5_json_decode(answer);
-    if (val == NULL)
+    retval = k5_json_decode(answer, &val);
+    if (retval != 0)
         goto cleanup;
 
     if (k5_json_get_tid(val) != K5_JSON_TID_OBJECT)
@@ -1196,48 +1187,49 @@ krb5_responder_otp_set_answer(krb5_context ctx, krb5_responder_context rctx,
 {
     krb5_error_code retval;
     k5_json_object obj = NULL;
-    k5_json_value val = NULL;
+    k5_json_number num;
+    k5_json_string str;
     char *tmp;
 
-    obj = k5_json_object_create();
-    if (obj == NULL)
+    retval = k5_json_object_create(&obj);
+    if (retval != 0)
         goto error;
 
-    val = k5_json_number_create(ti);
-    if (val == NULL)
+    retval = k5_json_number_create(ti, &num);
+    if (retval != 0)
         goto error;
 
-    retval = k5_json_object_set(obj, "tokeninfo", val);
-    k5_json_release(val);
+    retval = k5_json_object_set(obj, "tokeninfo", num);
+    k5_json_release(num);
     if (retval != 0)
         goto error;
 
     if (value != NULL) {
-        val = k5_json_string_create(value);
-        if (val == NULL)
+        retval = k5_json_string_create(value, &str);
+        if (retval != 0)
             goto error;
 
-        retval = k5_json_object_set(obj, "value", val);
-        k5_json_release(val);
+        retval = k5_json_object_set(obj, "value", str);
+        k5_json_release(str);
         if (retval != 0)
             goto error;
     }
 
     if (pin != NULL) {
-        val = k5_json_string_create(pin);
-        if (val == NULL)
+        retval = k5_json_string_create(pin, &str);
+        if (retval != 0)
             goto error;
 
-        retval = k5_json_object_set(obj, "pin", val);
-        k5_json_release(val);
+        retval = k5_json_object_set(obj, "pin", str);
+        k5_json_release(str);
         if (retval != 0)
             goto error;
     }
 
-    tmp = k5_json_encode(obj);
-    k5_json_release(obj);
-    if (tmp == NULL)
+    retval = k5_json_encode(obj, &tmp);
+    if (retval != 0)
         goto error;
+    k5_json_release(obj);
 
     retval = krb5_responder_set_answer(ctx, rctx, KRB5_RESPONDER_QUESTION_OTP,
                                        tmp);
@@ -1246,7 +1238,7 @@ krb5_responder_otp_set_answer(krb5_context ctx, krb5_responder_context rctx,
 
 error:
     k5_json_release(obj);
-    return ENOMEM;
+    return retval;
 }
 
 void KRB5_CALLCONV
diff --git a/src/util/support/json.c b/src/util/support/json.c
index f02fe26..5151b84 100644
--- a/src/util/support/json.c
+++ b/src/util/support/json.c
@@ -96,7 +96,7 @@ struct value_base {
 #define PTR2BASE(ptr) (((struct value_base *)ptr) - 1)
 #define BASE2PTR(ptr) ((void *)(((struct value_base *)ptr) + 1))
 
-void *
+k5_json_value
 k5_json_retain(k5_json_value val)
 {
     struct value_base *p;
@@ -160,24 +160,29 @@ alloc_value(json_type type, size_t size)
 
 static struct json_type_st null_type = { K5_JSON_TID_NULL, "null", NULL };
 
-k5_json_null
-k5_json_null_create(void)
+int
+k5_json_null_create(k5_json_null *val_out)
 {
-    return alloc_value(&null_type, 0);
+    *val_out = alloc_value(&null_type, 0);
+    return (*val_out == NULL) ? ENOMEM : 0;
 }
 
 /*** Boolean type ***/
 
 static struct json_type_st bool_type = { K5_JSON_TID_BOOL, "bool", NULL };
 
-k5_json_bool
-k5_json_bool_create(int truth)
+int
+k5_json_bool_create(int truth, k5_json_bool *val_out)
 {
     k5_json_bool b;
 
+    *val_out = NULL;
     b = alloc_value(&bool_type, 1);
+    if (b == NULL)
+        return ENOMEM;
     *(unsigned char *)b = !!truth;
-    return b;
+    *val_out = b;
+    return 0;
 }
 
 int
@@ -209,10 +214,11 @@ static struct json_type_st array_type = {
     K5_JSON_TID_ARRAY, "array", array_dealloc
 };
 
-k5_json_array
-k5_json_array_create(void)
+int
+k5_json_array_create(k5_json_array *val_out)
 {
-    return alloc_value(&array_type, sizeof(struct k5_json_array_st));
+    *val_out = alloc_value(&array_type, sizeof(struct k5_json_array_st));
+    return (*val_out == NULL) ? ENOMEM : 0;
 }
 
 int
@@ -289,10 +295,11 @@ static struct json_type_st object_type = {
     K5_JSON_TID_OBJECT, "object", object_dealloc
 };
 
-k5_json_object
-k5_json_object_create(void)
+int
+k5_json_object_create(k5_json_object *val_out)
 {
-    return alloc_value(&object_type, sizeof(struct k5_json_object_st));
+    *val_out = alloc_value(&object_type, sizeof(struct k5_json_object_st));
+    return (*val_out == NULL) ? ENOMEM : 0;
 }
 
 size_t
@@ -373,37 +380,42 @@ static struct json_type_st string_type = {
     K5_JSON_TID_STRING, "string", NULL
 };
 
-k5_json_string
-k5_json_string_create(const char *string)
+int
+k5_json_string_create(const char *cstring, k5_json_string *val_out)
 {
-    return k5_json_string_create_len(string, strlen(string));
+    return k5_json_string_create_len(cstring, strlen(cstring), val_out);
 }
 
-k5_json_string
-k5_json_string_create_len(const void *data, size_t len)
+int
+k5_json_string_create_len(const void *data, size_t len,
+                          k5_json_string *val_out)
 {
     char *s;
 
+    *val_out = NULL;
     s = alloc_value(&string_type, len + 1);
     if (s == NULL)
-        return NULL;
+        return ENOMEM;
     memcpy(s, data, len);
     s[len] = '\0';
-    return (k5_json_string)s;
+    *val_out = (k5_json_string)s;
+    return 0;
 }
 
-k5_json_string
-k5_json_string_create_base64(const void *data, size_t len)
+int
+k5_json_string_create_base64(const void *data, size_t len,
+                             k5_json_string *val_out)
 {
     char *base64;
-    k5_json_string s;
+    int ret;
 
+    *val_out = NULL;
     base64 = k5_base64_encode(data, len);
     if (base64 == NULL)
-        return NULL;
-    s = k5_json_string_create(base64);
+        return ENOMEM;
+    ret = k5_json_string_create(base64, val_out);
     free(base64);
-    return s;
+    return ret;
 }
 
 const char *
@@ -412,10 +424,21 @@ k5_json_string_utf8(k5_json_string string)
     return (const char *)string;
 }
 
-void *
-k5_json_string_unbase64(k5_json_string string, size_t *len_out)
+int
+k5_json_string_unbase64(k5_json_string string, unsigned char **data_out,
+                        size_t *len_out)
 {
-    return k5_base64_decode((const char *)string, len_out);
+    unsigned char *data;
+    size_t len;
+
+    *data_out = NULL;
+    *len_out = 0;
+    data = k5_base64_decode((const char *)string, &len);
+    if (data == NULL)
+        return (len == 0) ? ENOMEM : EINVAL;
+    *data_out = data;
+    *len_out = len;
+    return 0;
 }
 
 /*** Number type ***/
@@ -424,15 +447,18 @@ static struct json_type_st number_type = {
     K5_JSON_TID_NUMBER, "number", NULL
 };
 
-k5_json_number
-k5_json_number_create(long long number)
+int
+k5_json_number_create(long long number, k5_json_number *val_out)
 {
     k5_json_number n;
 
+    *val_out = NULL;
     n = alloc_value(&number_type, sizeof(long long));
-    if (n)
-        *((long long *)n) = number;
-    return n;
+    if (n == NULL)
+        return ENOMEM;
+    *((long long *)n) = number;
+    *val_out = n;
+    return 0;
 }
 
 long long
@@ -448,62 +474,61 @@ static const char quotemap_c[] = "\"\\/\b\f\n\r\t";
 static const char needs_quote[] = "\\\"\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"
     "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37";
 
-struct encode_ctx {
-    struct k5buf buf;
-    int ret;
-    int first;
-};
-
-static int encode_value(struct encode_ctx *j, k5_json_value val);
+static int encode_value(struct k5buf *buf, k5_json_value val);
 
 static void
-encode_string(struct encode_ctx *j, const char *str)
+encode_string(struct k5buf *buf, const char *str)
 {
     size_t n;
     const char *p;
 
-    krb5int_buf_add(&j->buf, "\"");
+    krb5int_buf_add(buf, "\"");
     while (*str != '\0') {
         n = strcspn(str, needs_quote);
-        krb5int_buf_add_len(&j->buf, str, n);
+        krb5int_buf_add_len(buf, str, n);
         str += n;
         if (*str == '\0')
             break;
-        krb5int_buf_add(&j->buf, "\\");
+        krb5int_buf_add(buf, "\\");
         p = strchr(quotemap_c, *str);
         if (p != NULL)
-            krb5int_buf_add_len(&j->buf, quotemap_json + (p - quotemap_c), 1);
+            krb5int_buf_add_len(buf, quotemap_json + (p - quotemap_c), 1);
         else
-            krb5int_buf_add_fmt(&j->buf, "u00%02X", (unsigned int)*str);
+            krb5int_buf_add_fmt(buf, "u00%02X", (unsigned int)*str);
         str++;
     }
-    krb5int_buf_add(&j->buf, "\"");
+    krb5int_buf_add(buf, "\"");
 }
 
+struct obj_ctx {
+    struct k5buf *buf;
+    int ret;
+    int first;
+};
+
 static void
-encode_dict_entry(void *ctx, const char *key, k5_json_value value)
+encode_obj_entry(void *ctx, const char *key, k5_json_value value)
 {
-    struct encode_ctx *j = ctx;
+    struct obj_ctx *j = ctx;
 
     if (j->ret)
         return;
     if (j->first)
         j->first = 0;
     else
-        krb5int_buf_add(&j->buf, ",");
-    encode_string(j, key);
-    krb5int_buf_add(&j->buf, ":");
-    j->ret = encode_value(j, value);
-    if (j->ret)
-        return;
+        krb5int_buf_add(j->buf, ",");
+    encode_string(j->buf, key);
+    krb5int_buf_add(j->buf, ":");
+    j->ret = encode_value(j->buf, value);
 }
 
 static int
-encode_value(struct encode_ctx *j, k5_json_value val)
+encode_value(struct k5buf *buf, k5_json_value val)
 {
     k5_json_tid type;
-    int first = 0, ret;
+    int ret;
     size_t i, len;
+    struct obj_ctx ctx;
 
     if (val == NULL)
         return EINVAL;
@@ -511,62 +536,63 @@ encode_value(struct encode_ctx *j, k5_json_value val)
     type = k5_json_get_tid(val);
     switch (type) {
     case K5_JSON_TID_ARRAY:
-        krb5int_buf_add(&j->buf, "[");
+        krb5int_buf_add(buf, "[");
         len = k5_json_array_length(val);
         for (i = 0; i < len; i++) {
             if (i != 0)
-                krb5int_buf_add(&j->buf, ",");
-            ret = encode_value(j, k5_json_array_get(val, i));
+                krb5int_buf_add(buf, ",");
+            ret = encode_value(buf, k5_json_array_get(val, i));
             if (ret)
                 return ret;
         }
-        krb5int_buf_add(&j->buf, "]");
-        break;
+        krb5int_buf_add(buf, "]");
+        return 0;
 
     case K5_JSON_TID_OBJECT:
-        krb5int_buf_add(&j->buf, "{");
-        first = j->first;
-        j->first = 1;
-        k5_json_object_iterate(val, encode_dict_entry, j);
-        krb5int_buf_add(&j->buf, "}");
-        j->first = first;
-        break;
+        krb5int_buf_add(buf, "{");
+        ctx.buf = buf;
+        ctx.ret = 0;
+        ctx.first = 1;
+        k5_json_object_iterate(val, encode_obj_entry, &ctx);
+        krb5int_buf_add(buf, "}");
+        return ctx.ret;
 
     case K5_JSON_TID_STRING:
-        encode_string(j, k5_json_string_utf8(val));
-        break;
+        encode_string(buf, k5_json_string_utf8(val));
+        return 0;
 
     case K5_JSON_TID_NUMBER:
-        krb5int_buf_add_fmt(&j->buf, "%lld", k5_json_number_value(val));
-        break;
+        krb5int_buf_add_fmt(buf, "%lld", k5_json_number_value(val));
+        return 0;
 
     case K5_JSON_TID_NULL:
-        krb5int_buf_add(&j->buf, "null");
-        break;
+        krb5int_buf_add(buf, "null");
+        return 0;
 
     case K5_JSON_TID_BOOL:
-        krb5int_buf_add(&j->buf, k5_json_bool_value(val) ? "true" : "false");
-        break;
+        krb5int_buf_add(buf, k5_json_bool_value(val) ? "true" : "false");
+        return 0;
 
     default:
-        return 1;
+        return EINVAL;
     }
-    return 0;
 }
 
-char *
-k5_json_encode(k5_json_value val)
+int
+k5_json_encode(k5_json_value val, char **json_out)
 {
-    struct encode_ctx j;
+    struct k5buf buf;
+    int ret;
 
-    j.ret = 0;
-    j.first = 1;
-    krb5int_buf_init_dynamic(&j.buf);
-    if (encode_value(&j, val)) {
-        krb5int_free_buf(&j.buf);
-        return NULL;
+    *json_out = NULL;
+    krb5int_buf_init_dynamic(&buf);
+    ret = encode_value(&buf, val);
+    if (ret) {
+        krb5int_free_buf(&buf);
+        return ret;
     }
-    return krb5int_buf_data(&j.buf);
+    *json_out = krb5int_buf_data(&buf);
+    return (*json_out == NULL) ? ENOMEM : 0;
 }
 
 /*** JSON decoding ***/
@@ -576,8 +602,7 @@ struct decode_ctx {
     size_t depth;
 };
 
-static k5_json_value
-parse_value(struct decode_ctx *ctx);
+static int parse_value(struct decode_ctx *ctx, k5_json_value *val_out);
 
 /* Consume whitespace.  Return 0 if there is anything left to parse after the
  * whitespace, -1 if not. */
@@ -621,66 +646,70 @@ hexval(unsigned char c)
 
 /* Parse a JSON number (which must be an integer in the signed 64-bit range; we
  * do not allow floating-point numbers). */
-static k5_json_number
-parse_number(struct decode_ctx *ctx)
+static int
+parse_number(struct decode_ctx *ctx, k5_json_number *val_out)
 {
     const unsigned long long umax = ~0ULL, smax = (1ULL << 63) - 1;
     unsigned long long number = 0;
     int neg = 1;
 
+    *val_out = NULL;
+
     if (*ctx->p == '-') {
         neg = -1;
         ctx->p++;
     }
 
     if (!is_digit(*ctx->p))
-        return NULL;
+        return EINVAL;
 
     /* Read the number into an unsigned 64-bit container, ensuring that we
      * don't overflow it. */
     while (is_digit(*ctx->p)) {
         if (number + 1 > umax / 10)
-            return NULL;
+            return EOVERFLOW;
         number = (number * 10) + (*ctx->p - '0');
         ctx->p++;
     }
 
     /* Make sure the unsigned 64-bit value fits in the signed 64-bit range. */
     if (number > smax + 1 || (number > smax && neg == 1))
-        return NULL;
+        return EOVERFLOW;
 
-    return k5_json_number_create(number * neg);
+    return k5_json_number_create(number * neg, val_out);
 }
 
 /* Parse a JSON string (which must not quote Unicode code points above 256). */
-static char *
-parse_string(struct decode_ctx *ctx)
+static int
+parse_string(struct decode_ctx *ctx, char **str_out)
 {
     const unsigned char *p, *start, *end = NULL;
     const char *q;
     char *buf, *pos;
     unsigned int code;
 
+    *str_out = NULL;
+
     /* Find the start and end of the string. */
     if (*ctx->p != '"')
-        return NULL;
+        return EINVAL;
     start = ++ctx->p;
     for (; *ctx->p != '\0'; ctx->p++) {
         if (*ctx->p == '\\') {
             ctx->p++;
             if (*ctx->p == '\0')
-                return NULL;
+                return EINVAL;
         } else if (*ctx->p == '"') {
             end = ctx->p++;
             break;
         }
     }
     if (end == NULL)
-        return NULL;
+        return EINVAL;
 
     pos = buf = malloc(end - start + 1);
     if (buf == NULL)
-        return NULL;
+        return ENOMEM;
     for (p = start; p < end;) {
         if (*p == '\\') {
             p++;
@@ -694,7 +723,7 @@ parse_string(struct decode_ctx *ctx)
                     /* Code points above 0xff don't need to be quoted, so we
                      * don't implement translating those into UTF-8. */
                     free(buf);
-                    return NULL;
+                    return EINVAL;
                 }
                 p += 5;
             } else {
@@ -703,7 +732,7 @@ parse_string(struct decode_ctx *ctx)
                     *pos++ = quotemap_c[q - quotemap_json];
                 } else {
                     free(buf);
-                    return NULL;
+                    return EINVAL;
                 }
                 p++;
             }
@@ -712,198 +741,245 @@ parse_string(struct decode_ctx *ctx)
         }
     }
     *pos = '\0';
-    return buf;
+    *str_out = buf;
+    return 0;
 }
 
-/*
- * Parse an object association and the following comma.  Return 1 if an
- * association was parsed, 0 if the end of the object was reached, and -1 on
- * error.
- */
+/* Parse an object association and place it into obj. */
 static int
-parse_pair(k5_json_object obj, struct decode_ctx *ctx)
+parse_object_association(k5_json_object obj, struct decode_ctx *ctx)
 {
     char *key = NULL;
-    k5_json_value value;
-
-    if (white_spaces(ctx))
-        goto err;
-
-    /* Check for the end of the object. */
-    if (*ctx->p == '}') {
-        ctx->p++;
-        return 0;
-    }
+    k5_json_value val;
+    int ret;
 
     /* Parse the key and value. */
-    key = parse_string(ctx);
-    if (key == NULL)
-        goto err;
+    ret = parse_string(ctx, &key);
+    if (ret)
+        return ret;
     if (white_spaces(ctx))
-        goto err;
+        goto invalid;
     if (*ctx->p != ':')
-        goto err;
+        goto invalid;
     ctx->p++;
     if (white_spaces(ctx))
-        goto err;
-    value = parse_value(ctx);
-    if (value == NULL) {
+        goto invalid;
+    ret = parse_value(ctx, &val);
+    if (ret) {
         free(key);
-        return -1;
+        return ret;
     }
 
-    /* Add the key and value to the object. */
-    k5_json_object_set(obj, key, value);
+    /* Add the key and value to obj. */
+    ret = k5_json_object_set(obj, key, val);
     free(key);
-    key = NULL;
-    k5_json_release(value);
-
-    /* Consume the following comma if this isn't the last item. */
-    if (white_spaces(ctx))
-        goto err;
-    if (*ctx->p == ',')
-        ctx->p++;
-    else if (*ctx->p != '}')
-        goto err;
+    k5_json_release(val);
+    return ret;
 
-    return 1;
-
-err:
+invalid:
     free(key);
-    return -1;
+    return EINVAL;
 }
 
 /* Parse a JSON object. */
-static k5_json_object
-parse_object(struct decode_ctx *ctx)
+static int
+parse_object(struct decode_ctx *ctx, k5_json_object *val_out)
 {
-    k5_json_object obj;
+    k5_json_object obj = NULL;
     int ret;
 
-    obj = k5_json_object_create();
-    if (obj == NULL)
-        return NULL;
+    *val_out = NULL;
 
+    /* Parse past the opening brace. */
+    if (*ctx->p != '{')
+        return EINVAL;
     ctx->p++;
-    while ((ret = parse_pair(obj, ctx)) > 0)
-        ;
-    if (ret < 0) {
-        k5_json_release(obj);
-        return NULL;
-    }
-    return obj;
-}
-
-/* Parse a JSON array item and the following comma.  Return 1 if an item was
- * parsed, 0 if the end of the array was reached, and -1 on error. */
-static int
-parse_item(k5_json_array array, struct decode_ctx *ctx)
-{
-    k5_json_value value;
-
     if (white_spaces(ctx))
-        return -1;
+        return EINVAL;
 
-    if (*ctx->p == ']') {
-        ctx->p++;
-        return 0;
-    }
+    ret = k5_json_object_create(&obj);
+    if (ret)
+        return ret;
 
-    value = parse_value(ctx);
-    if (value == NULL)
-        return -1;
+    /* Pairs associations until we reach the terminating brace. */
+    if (*ctx->p != '}') {
+        while (1) {
+            ret = parse_object_association(obj, ctx);
+            if (ret) {
+                k5_json_release(obj);
+                return ret;
+            }
+            if (white_spaces(ctx))
+                goto invalid;
+            if (*ctx->p == '}')
+                break;
+            if (*ctx->p != ',')
+                goto invalid;
+            ctx->p++;
+            if (white_spaces(ctx))
+                goto invalid;
+        }
+    }
+    ctx->p++;
+    *val_out = obj;
+    return 0;
 
-    k5_json_array_add(array, value);
-    k5_json_release(value);
+invalid:
+    k5_json_release(obj);
+    return EINVAL;
+}
 
-    if (white_spaces(ctx))
-        return -1;
+/* Parse an value and place it into array. */
+static int
+parse_array_item(k5_json_array array, struct decode_ctx *ctx)
+{
+    k5_json_value val;
+    int ret;
 
-    if (*ctx->p == ',')
-        ctx->p++;
-    else if (*ctx->p != ']')
-        return -1;
-    return 1;
+    ret = parse_value(ctx, &val);
+    if (ret)
+        return ret;
+    ret = k5_json_array_add(array, val);
+    k5_json_release(val);
+    return ret;
 }
 
 /* Parse a JSON array. */
-static k5_json_array
-parse_array(struct decode_ctx *ctx)
+static int
+parse_array(struct decode_ctx *ctx, k5_json_array *val_out)
 {
-    k5_json_array array = k5_json_array_create();
+    k5_json_array array = NULL;
     int ret;
 
-    assert(*ctx->p == '[');
-    ctx->p += 1;
+    *val_out = NULL;
 
-    while ((ret = parse_item(array, ctx)) > 0)
-        ;
-    if (ret < 0) {
-        k5_json_release(array);
-        return NULL;
+    /* Parse past the opening bracket. */
+    if (*ctx->p != '[')
+        return EINVAL;
+    ctx->p++;
+    if (white_spaces(ctx))
+        return EINVAL;
+
+    ret = k5_json_array_create(&array);
+    if (ret)
+        return ret;
+
+    /* Pairs values until we reach the terminating bracket. */
+    if (*ctx->p != ']') {
+        while (1) {
+            ret = parse_array_item(array, ctx);
+            if (ret) {
+                k5_json_release(array);
+                return ret;
+            }
+            if (white_spaces(ctx))
+                goto invalid;
+            if (*ctx->p == ']')
+                break;
+            if (*ctx->p != ',')
+                goto invalid;
+            ctx->p++;
+            if (white_spaces(ctx))
+                goto invalid;
+        }
     }
-    return array;
+    ctx->p++;
+    *val_out = array;
+    return 0;
+
+invalid:
+    k5_json_release(array);
+    return EINVAL;
 }
 
 /* Parse a JSON value of any type. */
-static k5_json_value
-parse_value(struct decode_ctx *ctx)
+static int
+parse_value(struct decode_ctx *ctx, k5_json_value *val_out)
 {
-    k5_json_value v;
-    char *str;
+    k5_json_null null;
+    k5_json_bool bval;
+    k5_json_number num;
+    k5_json_string str;
+    k5_json_object obj;
+    k5_json_array array;
+    char *cstring;
+    int ret;
+
+    *val_out = NULL;
 
     if (white_spaces(ctx))
-        return NULL;
+        return EINVAL;
 
     if (*ctx->p == '"') {
-        str = parse_string(ctx);
-        if (str == NULL)
-            return NULL;
-        v = k5_json_string_create(str);
-        free(str);
-        return v;
+        ret = parse_string(ctx, &cstring);
+        if (ret)
+            return ret;
+        ret = k5_json_string_create(cstring, &str);
+        free(cstring);
+        if (ret)
+            return ret;
+        *val_out = str;
     } else if (*ctx->p == '{') {
         if (ctx->depth-- == 1)
-            return NULL;
-        v = parse_object(ctx);
+            return EINVAL;
+        ret = parse_object(ctx, &obj);
+        if (ret)
+            return ret;
         ctx->depth++;
-        return v;
+        *val_out = obj;
     } else if (*ctx->p == '[') {
         if (ctx->depth-- == 1)
-            return NULL;
-        v = parse_array(ctx);
+            return EINVAL;
+        ret = parse_array(ctx, &array);
         ctx->depth++;
-        return v;
+        *val_out = array;
     } else if (is_digit(*ctx->p) || *ctx->p == '-') {
-        return parse_number(ctx);
-    }
-
-    if (strncmp((char *)ctx->p, "null", 4) == 0) {
+        ret = parse_number(ctx, &num);
+        if (ret)
+            return ret;
+        *val_out = num;
+    } else if (strncmp((char *)ctx->p, "null", 4) == 0) {
         ctx->p += 4;
-        return k5_json_null_create();
+        ret = k5_json_null_create(&null);
+        if (ret)
+            return ret;
+        *val_out = null;
     } else if (strncmp((char *)ctx->p, "true", 4) == 0) {
         ctx->p += 4;
-        return k5_json_bool_create(1);
+        ret = k5_json_bool_create(1, &bval);
+        if (ret)
+            return ret;
+        *val_out = bval;
     } else if (strncmp((char *)ctx->p, "false", 5) == 0) {
         ctx->p += 5;
-        return k5_json_bool_create(0);
+        ret = k5_json_bool_create(0, &bval);
+        if (ret)
+            return ret;
+        *val_out = bval;
+    } else {
+        return EINVAL;
     }
 
-    return NULL;
+    return 0;
 }
 
-k5_json_value
-k5_json_decode(const char *string)
+int
+k5_json_decode(const char *string, k5_json_value *val_out)
 {
     struct decode_ctx ctx;
-    k5_json_value v;
+    k5_json_value val;
+    int ret;
 
+    *val_out = NULL;
     ctx.p = (unsigned char *)string;
     ctx.depth = MAX_DECODE_DEPTH;
-    v = parse_value(&ctx);
+    ret = parse_value(&ctx, &val);
+    if (ret)
+        return ret;
     if (white_spaces(&ctx) == 0) {
-        k5_json_release(v);
-        return NULL;
+        k5_json_release(val);
+        return EINVAL;
     }
-    return v;
+    *val_out = val;
+    return 0;
 }
diff --git a/src/util/support/t_json.c b/src/util/support/t_json.c
index 7b30007..afb02ee 100644
--- a/src/util/support/t_json.c
+++ b/src/util/support/t_json.c
@@ -88,14 +88,18 @@ check(int pred, const char *str)
 static void
 test_array()
 {
-    k5_json_string v1 = k5_json_string_create("abc");
-    k5_json_number v2 = k5_json_number_create(2);
-    k5_json_null v3 = k5_json_null_create();
-    k5_json_array a = k5_json_array_create();
+    k5_json_string v1;
+    k5_json_number v2;
+    k5_json_null v3;
+    k5_json_array a;
     k5_json_value v;
 
+    k5_json_array_create(&a);
+    k5_json_string_create("abc", &v1);
     k5_json_array_add(a, v1);
+    k5_json_number_create(2, &v2);
     k5_json_array_add(a, v2);
+    k5_json_null_create(&v3);
     k5_json_array_add(a, v3);
 
     check(k5_json_array_length(a) == 3, "array length");
@@ -118,11 +122,13 @@ static void
 test_object(void)
 {
     k5_json_object object;
-    k5_json_number n, v1 = k5_json_number_create(1);
-    k5_json_string s, v2 = k5_json_string_create("hejsan");
+    k5_json_number n, v1;
+    k5_json_string s, v2;
 
-    object = k5_json_object_create();
+    k5_json_object_create(&object);
+    k5_json_number_create(1, &v1);
     k5_json_object_set(object, "key1", v1);
+    k5_json_string_create("hejsan", &v2);
     k5_json_object_set(object, "key2", v2);
 
     n = k5_json_object_get(object, "key1");
@@ -142,20 +148,21 @@ static void
 test_string(void)
 {
     k5_json_string s1, s2, s3;
-    void *data;
+    unsigned char *data;
     size_t len;
 
-    s1 = k5_json_string_create("hejsan");
-    s2 = k5_json_string_create("hejsan");
-    s3 = k5_json_string_create_base64("55555", 5);
+    k5_json_string_create("hejsan", &s1);
+    k5_json_string_create("hejsan", &s2);
+    k5_json_string_create_base64("55555", 5, &s3);
 
     if (strcmp(k5_json_string_utf8(s1), k5_json_string_utf8(s2)) != 0)
         err("Identical strings are not identical");
     if (strcmp(k5_json_string_utf8(s3), "NTU1NTU=") != 0)
         err("base64 string has incorrect value");
-    data = k5_json_string_unbase64(s3, &len);
-    if (data == NULL || len != 5 || memcmp(data, "55555", 5) != 0)
+    k5_json_string_unbase64(s3, &data, &len);
+    if (len != 5 || memcmp(data, "55555", 5) != 0)
         err("base64 string doesn't decode to correct value");
+    free(data);
 
     k5_json_release(s1);
     k5_json_release(s2);
@@ -181,20 +188,17 @@ test_json(void)
     int i;
     k5_json_value v, v2;
 
-    v = k5_json_decode("\"string\"");
-    check(v != NULL, "string1");
+    check(k5_json_decode("\"string\"", &v) == 0, "string1");
     check(k5_json_get_tid(v) == K5_JSON_TID_STRING, "string1 tid");
     check(strcmp(k5_json_string_utf8(v), "string") == 0, "string1 utf8");
     k5_json_release(v);
 
-    v = k5_json_decode("\t \"foo\\\"bar\" ");
-    check(v != NULL, "string2");
+    check(k5_json_decode("\t \"foo\\\"bar\" ", &v) == 0, "string2");
     check(k5_json_get_tid(v) == K5_JSON_TID_STRING, "string2 tid");
     check(strcmp(k5_json_string_utf8(v), "foo\"bar") == 0, "string2 utf8");
     k5_json_release(v);
 
-    v = k5_json_decode(" { \"key\" : \"value\" }");
-    check(v != NULL, "object1");
+    check(k5_json_decode(" { \"key\" : \"value\" }", &v) == 0, "object1");
     check(k5_json_get_tid(v) == K5_JSON_TID_OBJECT, "object1 tid");
     v2 = k5_json_object_get(v, "key");
     check(v2 != NULL, "object[key]");
@@ -202,9 +206,8 @@ test_json(void)
     check(strcmp(k5_json_string_utf8(v2), "value") == 0, "object1[key] utf8");
     k5_json_release(v);
 
-    v = k5_json_decode("{ \"k1\" : { \"k2\" : \"s2\", \"k3\" : \"s3\" }, "
-                       "\"k4\" : \"s4\" }");
-    check(v != NULL, "object2");
+    check(k5_json_decode("{ \"k1\" : { \"k2\" : \"s2\", \"k3\" : \"s3\" }, "
+                         "\"k4\" : \"s4\" }", &v) == 0, "object2");
     v2 = k5_json_object_get(v, "k1");
     check(v2 != NULL, "object2[k1]");
     check(k5_json_get_tid(v2) == K5_JSON_TID_OBJECT, "object2[k1] tid");
@@ -214,28 +217,24 @@ test_json(void)
     check(strcmp(k5_json_string_utf8(v2), "s3") == 0, "object2[k1][k3] utf8");
     k5_json_release(v);
 
-    v = k5_json_decode("{ \"k1\" : 1 }");
-    check(v != NULL, "object3");
+    check(k5_json_decode("{ \"k1\" : 1 }", &v) == 0, "object3");
     check(k5_json_get_tid(v) == K5_JSON_TID_OBJECT, "object3 id");
     v2 = k5_json_object_get(v, "k1");
     check(k5_json_get_tid(v2) == K5_JSON_TID_NUMBER, "object3[k1] tid");
     check(k5_json_number_value(v2) == 1, "object3[k1] value");
     k5_json_release(v);
 
-    v = k5_json_decode("-10");
-    check(v != NULL, "number1");
+    check(k5_json_decode("-10", &v) == 0, "number1");
     check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "number1 tid");
     check(k5_json_number_value(v) == -10, "number1 value");
     k5_json_release(v);
 
-    v = k5_json_decode("99");
-    check(v != NULL, "number2");
+    check(k5_json_decode("99", &v) == 0, "number2");
     check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "number2 tid");
     check(k5_json_number_value(v) == 99, "number2 value");
     k5_json_release(v);
 
-    v = k5_json_decode(" [ 1 ]");
-    check(v != NULL, "array1");
+    check(k5_json_decode(" [ 1 ]", &v) == 0, "array1");
     check(k5_json_get_tid(v) == K5_JSON_TID_ARRAY, "array1 tid");
     check(k5_json_array_length(v) == 1, "array1 len");
     v2 = k5_json_array_get(v, 0);
@@ -244,8 +243,7 @@ test_json(void)
     check(k5_json_number_value(v2) == 1, "array1[0] value");
     k5_json_release(v);
 
-    v = k5_json_decode(" [ -1 ]");
-    check(v != NULL, "array2");
+    check(k5_json_decode(" [ -1 ]", &v) == 0, "array2");
     check(k5_json_get_tid(v) == K5_JSON_TID_ARRAY, "array2 tid");
     check(k5_json_array_length(v) == 1, "array2 len");
     v2 = k5_json_array_get(v, 0);
@@ -254,20 +252,18 @@ test_json(void)
     check(k5_json_number_value(v2) == -1, "array2[0] value");
     k5_json_release(v);
 
-    v = k5_json_decode("18446744073709551616");
-    check(v == NULL, "unsigned 64-bit overflow");
-    v = k5_json_decode("9223372036854775808");
-    check(v == NULL, "signed 64-bit positive overflow");
-    v = k5_json_decode("-9223372036854775809");
-    check(v == NULL, "signed 64-bit negative overflow");
+    check(k5_json_decode("18446744073709551616", &v) == EOVERFLOW,
+          "unsigned 64-bit overflow");
+    check(k5_json_decode("9223372036854775808", &v) == EOVERFLOW,
+          "signed 64-bit positive overflow");
+    check(k5_json_decode("-9223372036854775809", &v) == EOVERFLOW,
+          "signed 64-bit negative overflow");
 
     for (tptr = tests; *tptr != NULL; tptr++) {
         s = strdup(*tptr);
-        v = k5_json_decode(s);
-        if (v == NULL)
+        if (k5_json_decode(s, &v))
             err(s);
-        enc = k5_json_encode(v);
-        if (enc == NULL || strcmp(enc, s) != 0)
+        if (k5_json_encode(v, &enc) || strcmp(enc, s) != 0)
             err(s);
         free(enc);
         k5_json_release(v);
@@ -278,7 +274,8 @@ test_json(void)
             orig = *p;
             for (i = 0; i <= 255; i++) {
                 *p = i;
-                k5_json_release(k5_json_decode(s));
+                k5_json_decode(s, &v);
+                k5_json_release(v);
             }
             *p = orig;
         }


More information about the cvs-krb5 mailing list