krb5 commit: Add internal JSON encoding and decoding support

Greg Hudson ghudson at MIT.EDU
Tue Sep 11 01:18:58 EDT 2012


https://github.com/krb5/krb5/commit/382a87cf344b002bf5660ed3f27799ed18c54948
commit 382a87cf344b002bf5660ed3f27799ed18c54948
Author: Greg Hudson <ghudson at mit.edu>
Date:   Thu Aug 9 18:05:50 2012 -0400

    Add internal JSON encoding and decoding support
    
    Add JSON support based loosely on Heimdal's heimbase code.

 .gitignore                                    |    1 +
 src/include/k5-json.h                         |  192 ++++++
 src/include/k5-platform.h                     |    6 +
 src/util/support/Makefile.in                  |   15 +-
 src/util/support/json.c                       |  903 +++++++++++++++++++++++++
 src/util/support/libkrb5support-fixed.exports |   24 +
 src/util/support/t_json.c                     |  297 ++++++++
 7 files changed, 1436 insertions(+), 2 deletions(-)

diff --git a/.gitignore b/.gitignore
index 77d4d26..7a12334 100644
--- a/.gitignore
+++ b/.gitignore
@@ -341,6 +341,7 @@ testlog
 
 /src/util/support/libkrb5support.exports
 /src/util/support/t_base64
+/src/util/support/t_json
 /src/util/support/t_k5buf
 /src/util/support/t_path
 /src/util/support/t_path_win
diff --git a/src/include/k5-json.h b/src/include/k5-json.h
new file mode 100644
index 0000000..fb9a3af
--- /dev/null
+++ b/src/include/k5-json.h
@@ -0,0 +1,192 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/k5-json.h - JSON declarations */
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef K5_JSON_H
+#define K5_JSON_H
+
+#include <stddef.h>
+
+#define K5_JSON_TID_NUMBER 0
+#define K5_JSON_TID_NULL 1
+#define K5_JSON_TID_BOOL 2
+#define K5_JSON_TID_MEMORY 128
+#define K5_JSON_TID_ARRAY 129
+#define K5_JSON_TID_OBJECT 130
+#define K5_JSON_TID_STRING 131
+
+/*
+ * The k5_json_value C type can represent any kind of JSON value.  It has no
+ * static type safety since it is represented using a void pointer, so be
+ * careful with it.  Its type can be checked dynamically with k5_json_get_tid()
+ * and the above constants.
+ */
+typedef void *k5_json_value;
+typedef unsigned int k5_json_tid;
+
+k5_json_tid k5_json_get_tid(k5_json_value val);
+
+/*
+ * k5_json_value objects are reference-counted.  These functions increment and
+ * 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);
+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.
+ */
+
+/*
+ * Null
+ */
+
+typedef struct k5_json_null_st *k5_json_null;
+
+k5_json_null k5_json_null_create(void);
+
+/*
+ * Boolean
+ */
+
+typedef struct k5_json_bool_st *k5_json_bool;
+
+k5_json_bool k5_json_bool_create(int truth);
+int k5_json_bool_value(k5_json_bool bval);
+
+/*
+ * Array
+ */
+
+typedef struct k5_json_array_st *k5_json_array;
+
+k5_json_array k5_json_array_create(void);
+size_t k5_json_array_length(k5_json_array array);
+
+/* Both of these functions increment the reference count on val. */
+int k5_json_array_add(k5_json_array array, k5_json_value val);
+void k5_json_array_set(k5_json_array array, size_t idx, k5_json_value val);
+
+/* Get an alias to the idx-th element of array, without incrementing the
+ * reference count.  The caller must check idx against the array length. */
+k5_json_value k5_json_array_get(k5_json_array array, size_t idx);
+
+/*
+ * Object
+ */
+
+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);
+void k5_json_object_iterate(k5_json_object obj,
+                            k5_json_object_iterator_fn func, void *arg);
+
+
+/* Store val into object at key, incrementing val's reference count. */
+int k5_json_object_set(k5_json_object obj, const char *key, k5_json_value val);
+
+/* Get an alias to the object's value for key, without incrementing the
+ * reference count.  Returns NULL if there is no value for key. */
+k5_json_value k5_json_object_get(k5_json_object obj, const char *key);
+
+/*
+ * String
+ */
+
+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);
+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);
+
+/* 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);
+
+/*
+ * Number
+ */
+
+typedef struct k5_json_number_st *k5_json_number;
+
+k5_json_number k5_json_number_create(long long number);
+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);
+
+#endif /* K5_JSON_H */
diff --git a/src/include/k5-platform.h b/src/include/k5-platform.h
index c927291..e4c3392 100644
--- a/src/include/k5-platform.h
+++ b/src/include/k5-platform.h
@@ -421,12 +421,18 @@ typedef struct { int error; unsigned char did_run; } k5_init_t;
 # endif
 # define INT64_TYPE int64_t
 # define UINT64_TYPE uint64_t
+# define INT64_FMT PRId64
+# define UINT64_FMT PRIu64
 #elif defined(_WIN32)
 # define INT64_TYPE signed __int64
 # define UINT64_TYPE unsigned __int64
+# define INT64_FMT "I64d"
+# define UINT64_FMT "I64u"
 #else /* not Windows, and neither stdint.h nor inttypes.h */
 # define INT64_TYPE signed long long
 # define UINT64_TYPE unsigned long long
+# define INT64_FMT "lld"
+# define UINT64_FMT "llu"
 #endif
 
 #ifndef SIZE_MAX
diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in
index ca04ad7..690a2c3 100644
--- a/src/util/support/Makefile.in
+++ b/src/util/support/Makefile.in
@@ -75,6 +75,7 @@ STLIBOBJS= \
 	zap.o \
 	path.o \
 	base64.o \
+	json.o \
 	$(GETTIMEOFDAY_ST_OBJ) \
 	$(IPC_ST_OBJ) \
 	$(STRLCPY_ST_OBJ) \
@@ -95,6 +96,7 @@ LIBOBJS= \
 	$(OUTPRE)zap.$(OBJEXT) \
 	$(OUTPRE)path.$(OBJEXT) \
 	$(OUTPRE)base64.$(OBJECT) \
+	$(OUTPRE)json.$(OBJEXT) \
 	$(GETTIMEOFDAY_OBJ) \
 	$(IPC_OBJ) \
 	$(STRLCPY_OBJ) \
@@ -124,9 +126,12 @@ SRCS=\
 	$(srcdir)/mkstemp.c \
 	$(srcdir)/t_k5buf.c \
 	$(srcdir)/t_unal.c \
+	$(srcdir)/t_path.c \
+	$(srcdir)/t_json.c \
 	$(srcdir)/zap.c \
 	$(srcdir)/path.c \
-	$(srcdir)/base64.c
+	$(srcdir)/base64.c \
+	$(srcdir)/json.c
 
 SHLIB_EXPDEPS =
 # Add -lm if dumping thread stats, for sqrt.
@@ -192,16 +197,22 @@ path_win.o: $(srcdir)/path.c
 t_base64: t_base64.o base64.o
 	$(CC_LINK) -o $@ t_base64.o base64.o
 
+T_JSON_OBJS= t_json.o json.o base64.o k5buf.o $(PRINTF_ST_OBJ)
+
+t_json: $(T_JSON_OBJS)
+	$(CC_LINK) -o $@ $(T_JSON_OBJS)
+
 t_unal: t_unal.o
 	$(CC_LINK) -o t_unal t_unal.o
 
-TEST_PROGS= t_k5buf t_path t_path_win t_base64 t_unal
+TEST_PROGS= t_k5buf t_path t_path_win t_base64 t_json t_unal
 
 check-unix:: $(TEST_PROGS)
 	./t_k5buf
 	./t_path
 	./t_path_win
 	./t_base64
+	./t_json
 	./t_unal
 
 clean::
diff --git a/src/util/support/json.c b/src/util/support/json.c
new file mode 100644
index 0000000..e6d7eea
--- /dev/null
+++ b/src/util/support/json.c
@@ -0,0 +1,903 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/json.c - JSON parser and unparser */
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file implements a minimal dynamic type system for JSON values and a
+ * JSON encoder and decoder.  It is loosely based on the heimbase code from
+ * Heimdal.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <k5-base64.h>
+#include <k5-json.h>
+#include <k5-buf.h>
+
+#define MAX_DECODE_DEPTH 64
+
+typedef void (*type_dealloc_fn)(void *val);
+
+typedef struct json_type_st {
+    k5_json_tid tid;
+    const char *name;
+    type_dealloc_fn dealloc;
+} *json_type;
+
+struct value_base {
+    json_type isa;
+    unsigned int ref_cnt;
+};
+
+#define PTR2BASE(ptr) (((struct value_base *)ptr) - 1)
+#define BASE2PTR(ptr) ((void *)(((struct value_base *)ptr) + 1))
+
+void *
+k5_json_retain(k5_json_value val)
+{
+    struct value_base *p;
+
+    if (val == NULL)
+        return val;
+    p = PTR2BASE(val);
+    assert(p->ref_cnt != 0);
+    p->ref_cnt++;
+    return val;
+}
+
+void
+k5_json_release(k5_json_value val)
+{
+    struct value_base *p;
+
+    if (val == NULL)
+        return;
+    p = PTR2BASE(val);
+    assert(p->ref_cnt != 0);
+    p->ref_cnt--;
+    if (p->ref_cnt == 0) {
+        if (p->isa->dealloc != NULL)
+            p->isa->dealloc(val);
+        free(p);
+    }
+}
+
+/* Get the type description of a k5_json_value. */
+static json_type
+get_isa(k5_json_value val)
+{
+    struct value_base *p = PTR2BASE(val);
+
+    return p->isa;
+}
+
+k5_json_tid
+k5_json_get_tid(k5_json_value val)
+{
+    json_type isa = get_isa(val);
+
+    return isa->tid;
+}
+
+static k5_json_value
+alloc_value(json_type type, size_t size)
+{
+    struct value_base *p = calloc(1, size + sizeof(*p));
+
+    if (p == NULL)
+        return NULL;
+    p->isa = type;
+    p->ref_cnt = 1;
+
+    return BASE2PTR(p);
+}
+
+/*** Null type ***/
+
+static struct json_type_st null_type = { K5_JSON_TID_NULL, "null", NULL };
+
+k5_json_null
+k5_json_null_create(void)
+{
+    return alloc_value(&null_type, 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)
+{
+    k5_json_bool b;
+
+    b = alloc_value(&bool_type, 1);
+    *(unsigned char *)b = !!truth;
+    return b;
+}
+
+int
+k5_json_bool_value(k5_json_bool bval)
+{
+    return *(unsigned char *)bval;
+}
+
+/*** Array type ***/
+
+struct k5_json_array_st {
+    k5_json_value *values;
+    size_t len;
+    size_t allocated;
+};
+
+static void
+array_dealloc(void *ptr)
+{
+    k5_json_array array = ptr;
+    size_t i;
+
+    for (i = 0; i < array->len; i++)
+        k5_json_release(array->values[i]);
+    free(array->values);
+}
+
+static struct json_type_st array_type = {
+    K5_JSON_TID_ARRAY, "array", array_dealloc
+};
+
+k5_json_array
+k5_json_array_create(void)
+{
+    return alloc_value(&array_type, sizeof(struct k5_json_array_st));
+}
+
+int
+k5_json_array_add(k5_json_array array, k5_json_value val)
+{
+    k5_json_value *ptr;
+    size_t new_alloc;
+
+    if (array->len >= array->allocated) {
+        /* Increase the number of slots by 50% (16 slots minimum). */
+        new_alloc = array->len + 1 + (array->len >> 1);
+        if (new_alloc < 16)
+            new_alloc = 16;
+        ptr = realloc(array->values, new_alloc * sizeof(*array->values));
+        if (ptr == NULL)
+            return ENOMEM;
+        array->values = ptr;
+        array->allocated = new_alloc;
+    }
+    array->values[array->len++] = k5_json_retain(val);
+    return 0;
+}
+
+size_t
+k5_json_array_length(k5_json_array array)
+{
+    return array->len;
+}
+
+k5_json_value
+k5_json_array_get(k5_json_array array, size_t idx)
+{
+    if (idx >= array->len)
+        abort();
+    return array->values[idx];
+}
+
+void
+k5_json_array_set(k5_json_array array, size_t idx, k5_json_value val)
+{
+    if (idx >= array->len)
+        abort();
+    k5_json_release(array->values[idx]);
+    array->values[idx] = k5_json_retain(val);
+}
+
+/*** Object type (string:value mapping) ***/
+
+struct entry {
+    char *key;
+    k5_json_value value;
+};
+
+struct k5_json_object_st {
+    struct entry *entries;
+    size_t len;
+    size_t allocated;
+};
+
+static void
+object_dealloc(void *ptr)
+{
+    k5_json_object obj = ptr;
+    size_t i;
+
+    for (i = 0; i < obj->len; i++) {
+        free(obj->entries[i].key);
+        k5_json_release(obj->entries[i].value);
+    }
+    free(obj->entries);
+}
+
+static struct json_type_st object_type = {
+    K5_JSON_TID_OBJECT, "object", object_dealloc
+};
+
+k5_json_object
+k5_json_object_create(void)
+{
+    return alloc_value(&object_type, sizeof(struct k5_json_object_st));
+}
+
+/* Return the entry for key within obj, or NULL if none exists. */
+static struct entry *
+object_search(k5_json_object obj, const char *key)
+{
+    size_t i;
+
+    for (i = 0; i < obj->len; i++) {
+        if (strcmp(key, obj->entries[i].key) == 0)
+            return &obj->entries[i];
+    }
+    return NULL;
+}
+
+k5_json_value
+k5_json_object_get(k5_json_object obj, const char *key)
+{
+    struct entry *ent;
+
+    ent = object_search(obj, key);
+    if (ent == NULL)
+        return NULL;
+    return ent->value;
+}
+
+int
+k5_json_object_set(k5_json_object obj, const char *key, k5_json_value val)
+{
+    struct entry *ent, *ptr;
+    size_t new_alloc;
+
+    ent = object_search(obj, key);
+    if (ent) {
+        k5_json_release(ent->value);
+        ent->value = k5_json_retain(val);
+        return 0;
+    }
+
+    if (obj->len >= obj->allocated) {
+        /* Increase the number of slots by 50% (16 slots minimum). */
+        new_alloc = obj->len + 1 + (obj->len >> 1);
+        if (new_alloc < 16)
+            new_alloc = 16;
+        ptr = realloc(obj->entries, new_alloc * sizeof(*obj->entries));
+        if (ptr == NULL)
+            return ENOMEM;
+        obj->entries = ptr;
+        obj->allocated = new_alloc;
+    }
+    obj->entries[obj->len].key = strdup(key);
+    if (obj->entries[obj->len].key == NULL)
+        return ENOMEM;
+    obj->entries[obj->len].value = k5_json_retain(val);
+    obj->len++;
+    return 0;
+}
+
+void
+k5_json_object_iterate(k5_json_object obj, k5_json_object_iterator_fn func,
+                       void *arg)
+{
+    size_t i;
+
+    for (i = 0; i < obj->len; i++)
+        func(arg, obj->entries[i].key, obj->entries[i].value);
+}
+
+/*** String type ***/
+
+static struct json_type_st string_type = {
+    K5_JSON_TID_STRING, "string", NULL
+};
+
+k5_json_string
+k5_json_string_create(const char *string)
+{
+    return k5_json_string_create_len(string, strlen(string));
+}
+
+k5_json_string
+k5_json_string_create_len(const void *data, size_t len)
+{
+    char *s;
+
+    s = alloc_value(&string_type, len + 1);
+    if (s == NULL)
+        return NULL;
+    memcpy(s, data, len);
+    s[len] = '\0';
+    return (k5_json_string)s;
+}
+
+k5_json_string
+k5_json_string_create_base64(const void *data, size_t len)
+{
+    char *base64;
+    k5_json_string s;
+
+    base64 = k5_base64_encode(data, len);
+    if (base64 == NULL)
+        return NULL;
+    s = k5_json_string_create(base64);
+    free(base64);
+    return s;
+}
+
+const char *
+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)
+{
+    return k5_base64_decode((const char *)string, len_out);
+}
+
+/*** Number type ***/
+
+static struct json_type_st number_type = {
+    K5_JSON_TID_NUMBER, "number", NULL
+};
+
+k5_json_number
+k5_json_number_create(long long number)
+{
+    k5_json_number n;
+
+    n = alloc_value(&number_type, sizeof(long long));
+    if (n)
+        *((long long *)n) = number;
+    return n;
+}
+
+long long
+k5_json_number_value(k5_json_number number)
+{
+    return *(long long *)number;
+}
+
+/*** JSON encoding ***/
+
+static const char quotemap_json[] = "\"\\/bfnrt";
+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 void
+encode_string(struct encode_ctx *j, const char *str)
+{
+    size_t n;
+    const char *p;
+
+    krb5int_buf_add(&j->buf, "\"");
+    while (*str != '\0') {
+        n = strcspn(str, needs_quote);
+        krb5int_buf_add_len(&j->buf, str, n);
+        str += n;
+        if (*str == '\0')
+            break;
+        krb5int_buf_add(&j->buf, "\\");
+        p = strchr(quotemap_c, *str);
+        if (p != NULL)
+            krb5int_buf_add_len(&j->buf, quotemap_json + (p - quotemap_c), 1);
+        else
+            krb5int_buf_add_fmt(&j->buf, "u00%02X", (unsigned int)*str);
+        str++;
+    }
+    krb5int_buf_add(&j->buf, "\"");
+}
+
+static void
+encode_dict_entry(void *ctx, const char *key, k5_json_value value)
+{
+    struct encode_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;
+}
+
+static int
+encode_value(struct encode_ctx *j, k5_json_value val)
+{
+    k5_json_tid type;
+    int first = 0, ret;
+    size_t i, len;
+
+    if (val == NULL)
+        return EINVAL;
+
+    type = k5_json_get_tid(val);
+    switch (type) {
+    case K5_JSON_TID_ARRAY:
+        krb5int_buf_add(&j->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));
+            if (ret)
+                return ret;
+        }
+        krb5int_buf_add(&j->buf, "]");
+        break;
+
+    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;
+
+    case K5_JSON_TID_STRING:
+        encode_string(j, k5_json_string_utf8(val));
+        break;
+
+    case K5_JSON_TID_NUMBER:
+        krb5int_buf_add_fmt(&j->buf, "%lld", k5_json_number_value(val));
+        break;
+
+    case K5_JSON_TID_NULL:
+        krb5int_buf_add(&j->buf, "null");
+        break;
+
+    case K5_JSON_TID_BOOL:
+        krb5int_buf_add(&j->buf, k5_json_bool_value(val) ? "true" : "false");
+        break;
+
+    default:
+        return 1;
+    }
+    return 0;
+}
+
+char *
+k5_json_encode(k5_json_value val)
+{
+    struct encode_ctx j;
+
+    j.ret = 0;
+    j.first = 1;
+    krb5int_buf_init_dynamic(&j.buf);
+    if (encode_value(&j, val)) {
+        krb5int_free_buf(&j.buf);
+        return NULL;
+    }
+    return krb5int_buf_data(&j.buf);
+}
+
+/*** JSON decoding ***/
+
+struct decode_ctx {
+    const unsigned char *p;
+    size_t depth;
+};
+
+static k5_json_value
+parse_value(struct decode_ctx *ctx);
+
+/* Consume whitespace.  Return 0 if there is anything left to parse after the
+ * whitespace, -1 if not. */
+static int
+white_spaces(struct decode_ctx *ctx)
+{
+    unsigned char c;
+
+    for (; *ctx->p != '\0'; ctx->p++) {
+        c = *ctx->p;
+        if (c != ' ' && c != '\t' && c != '\r' && c != '\n')
+            return 0;
+    }
+    return -1;
+}
+
+/* Return true if c is a decimal digit. */
+static inline int
+is_digit(unsigned char c)
+{
+    return ('0' <= c && c <= '9');
+}
+
+/* Return true if c is a hexadecimal digit (per RFC 5234 HEXDIG). */
+static inline int
+is_hex_digit(unsigned char c)
+{
+    return is_digit(c) || ('A' <= c && c <= 'F');
+}
+
+/* Return the numeric value of a hex digit; aborts if c is not a hex digit. */
+static inline unsigned int
+hexval(unsigned char c)
+{
+    if (is_digit(c))
+        return c - '0';
+    else if ('A' <= c && c <= 'F')
+        return c - 'A' + 10;
+    abort();
+}
+
+/* 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)
+{
+    const unsigned long long umax = ~0ULL, smax = (1ULL << 63) - 1;
+    unsigned long long number = 0;
+    int neg = 1;
+
+    if (*ctx->p == '-') {
+        neg = -1;
+        ctx->p++;
+    }
+
+    if (!is_digit(*ctx->p))
+        return NULL;
+
+    /* 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;
+        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 k5_json_number_create(number * neg);
+}
+
+/* Parse a JSON string (which must not quote Unicode code points above 256). */
+static char *
+parse_string(struct decode_ctx *ctx)
+{
+    const unsigned char *p, *start, *end = NULL;
+    const char *q;
+    char *buf, *pos;
+    unsigned int code;
+
+    /* Find the start and end of the string. */
+    if (*ctx->p != '"')
+        return NULL;
+    start = ++ctx->p;
+    for (; *ctx->p != '\0'; ctx->p++) {
+        if (*ctx->p == '\\') {
+            ctx->p++;
+            if (*ctx->p == '\0')
+                return NULL;
+        } else if (*ctx->p == '"') {
+            end = ctx->p++;
+            break;
+        }
+    }
+    if (end == NULL)
+        return NULL;
+
+    pos = buf = malloc(end - start + 1);
+    if (buf == NULL)
+        return NULL;
+    for (p = start; p < end;) {
+        if (*p == '\\') {
+            p++;
+            if (*p == 'u' && is_hex_digit(p[1]) && is_hex_digit(p[2]) &&
+                is_hex_digit(p[3]) && is_hex_digit(p[4])) {
+                code = (hexval(p[1]) << 12) | (hexval(p[2]) << 8) |
+                    (hexval(p[3]) << 4) | hexval(p[4]);
+                if (code <= 0xff) {
+                    *pos++ = code;
+                } else {
+                    /* Code points above 0xff don't need to be quoted, so we
+                     * don't implement translating those into UTF-8. */
+                    free(buf);
+                    return NULL;
+                }
+                p += 5;
+            } else {
+                q = strchr(quotemap_json, *p);
+                if (q != NULL) {
+                    *pos++ = quotemap_c[q - quotemap_json];
+                } else {
+                    free(buf);
+                    return NULL;
+                }
+                p++;
+            }
+        } else {
+            *pos++ = *p++;
+        }
+    }
+    *pos = '\0';
+    return buf;
+}
+
+/*
+ * 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.
+ */
+static int
+parse_pair(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;
+    }
+
+    /* Parse the key and value. */
+    key = parse_string(ctx);
+    if (key == NULL)
+        goto err;
+    if (white_spaces(ctx))
+        goto err;
+    if (*ctx->p != ':')
+        goto err;
+    ctx->p++;
+    if (white_spaces(ctx))
+        goto err;
+    value = parse_value(ctx);
+    if (value == NULL) {
+        free(key);
+        return -1;
+    }
+
+    /* Add the key and value to the object. */
+    k5_json_object_set(obj, key, value);
+    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;
+
+    return 1;
+
+err:
+    free(key);
+    return -1;
+}
+
+/* Parse a JSON object. */
+static k5_json_object
+parse_object(struct decode_ctx *ctx)
+{
+    k5_json_object obj;
+    int ret;
+
+    obj = k5_json_object_create();
+    if (obj == NULL)
+        return NULL;
+
+    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;
+
+    if (*ctx->p == ']') {
+        ctx->p++;
+        return 0;
+    }
+
+    value = parse_value(ctx);
+    if (value == NULL)
+        return -1;
+
+    k5_json_array_add(array, value);
+    k5_json_release(value);
+
+    if (white_spaces(ctx))
+        return -1;
+
+    if (*ctx->p == ',')
+        ctx->p++;
+    else if (*ctx->p != ']')
+        return -1;
+    return 1;
+}
+
+/* Parse a JSON array. */
+static k5_json_array
+parse_array(struct decode_ctx *ctx)
+{
+    k5_json_array array = k5_json_array_create();
+    int ret;
+
+    assert(*ctx->p == '[');
+    ctx->p += 1;
+
+    while ((ret = parse_item(array, ctx)) > 0)
+        ;
+    if (ret < 0) {
+        k5_json_release(array);
+        return NULL;
+    }
+    return array;
+}
+
+/* Parse a JSON value of any type. */
+static k5_json_value
+parse_value(struct decode_ctx *ctx)
+{
+    k5_json_value v;
+    char *str;
+
+    if (white_spaces(ctx))
+        return NULL;
+
+    if (*ctx->p == '"') {
+        str = parse_string(ctx);
+        if (str == NULL)
+            return NULL;
+        v = k5_json_string_create(str);
+        free(str);
+        return v;
+    } else if (*ctx->p == '{') {
+        if (ctx->depth-- == 1)
+            return NULL;
+        v = parse_object(ctx);
+        ctx->depth++;
+        return v;
+    } else if (*ctx->p == '[') {
+        if (ctx->depth-- == 1)
+            return NULL;
+        v = parse_array(ctx);
+        ctx->depth++;
+        return v;
+    } else if (is_digit(*ctx->p) || *ctx->p == '-') {
+        return parse_number(ctx);
+    }
+
+    if (strncmp((char *)ctx->p, "null", 4) == 0) {
+        ctx->p += 4;
+        return k5_json_null_create();
+    } else if (strncmp((char *)ctx->p, "true", 4) == 0) {
+        ctx->p += 4;
+        return k5_json_bool_create(1);
+    } else if (strncmp((char *)ctx->p, "false", 5) == 0) {
+        ctx->p += 5;
+        return k5_json_bool_create(0);
+    }
+
+    return NULL;
+}
+
+k5_json_value
+k5_json_decode(const char *string)
+{
+    struct decode_ctx ctx;
+    k5_json_value v;
+
+    ctx.p = (unsigned char *)string;
+    ctx.depth = MAX_DECODE_DEPTH;
+    v = parse_value(&ctx);
+    if (white_spaces(&ctx) == 0) {
+        k5_json_release(v);
+        return NULL;
+    }
+    return v;
+}
diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports
index 496259c..13b1b57 100644
--- a/src/util/support/libkrb5support-fixed.exports
+++ b/src/util/support/libkrb5support-fixed.exports
@@ -1,3 +1,27 @@
+k5_json_array_add
+k5_json_array_create
+k5_json_array_get
+k5_json_array_length
+k5_json_array_set
+k5_json_bool_create
+k5_json_bool_value
+k5_json_decode
+k5_json_encode
+k5_json_get_tid
+k5_json_null_create
+k5_json_number_create
+k5_json_number_value
+k5_json_object_create
+k5_json_object_get
+k5_json_object_iterate
+k5_json_object_set
+k5_json_release
+k5_json_retain
+k5_json_string_create
+k5_json_string_create_base64
+k5_json_string_create_len
+k5_json_string_unbase64
+k5_json_string_utf8
 k5_path_isabs
 k5_path_join
 k5_path_split
diff --git a/src/util/support/t_json.c b/src/util/support/t_json.c
new file mode 100644
index 0000000..7b30007
--- /dev/null
+++ b/src/util/support/t_json.c
@@ -0,0 +1,297 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/t_json.c - JSON test program */
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <k5-json.h>
+
+static void
+err(const char *str)
+{
+    fprintf(stderr, "%s\n", str);
+    exit(1);
+}
+
+static void
+check(int pred, const char *str)
+{
+    if (!pred)
+        err(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_value v;
+
+    k5_json_array_add(a, v1);
+    k5_json_array_add(a, v2);
+    k5_json_array_add(a, v3);
+
+    check(k5_json_array_length(a) == 3, "array length");
+    v = k5_json_array_get(a, 2);
+    check(k5_json_get_tid(v) == K5_JSON_TID_NULL, "array[2] tid");
+    v = k5_json_array_get(a, 1);
+    check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "array[1] tid");
+    check(k5_json_number_value(v) == 2, "array[1] value");
+    v = k5_json_array_get(a, 0);
+    check(k5_json_get_tid(v) == K5_JSON_TID_STRING, "array[0] tid");
+    check(strcmp(k5_json_string_utf8(v), "abc") == 0, "array[0] value");
+
+    k5_json_release(v1);
+    k5_json_release(v2);
+    k5_json_release(v3);
+    k5_json_release(a);
+}
+
+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");
+
+    object = k5_json_object_create();
+    k5_json_object_set(object, "key1", v1);
+    k5_json_object_set(object, "key2", v2);
+
+    n = k5_json_object_get(object, "key1");
+    if (k5_json_number_value(n) != 1)
+        err("Retrieving key1 from object failed");
+
+    s = k5_json_object_get(object, "key2");
+    if (strcmp(k5_json_string_utf8(s), "hejsan") != 0)
+        err("Retrieving key2 from object failed");
+
+    k5_json_release(v1);
+    k5_json_release(v2);
+    k5_json_release(object);
+}
+
+static void
+test_string(void)
+{
+    k5_json_string s1, s2, s3;
+    void *data;
+    size_t len;
+
+    s1 = k5_json_string_create("hejsan");
+    s2 = k5_json_string_create("hejsan");
+    s3 = k5_json_string_create_base64("55555", 5);
+
+    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)
+        err("base64 string doesn't decode to correct value");
+
+    k5_json_release(s1);
+    k5_json_release(s2);
+    k5_json_release(s3);
+}
+
+static void
+test_json(void)
+{
+    static char *tests[] = {
+        "{\"k1\":\"s1\",\"k2\":\"s2\"}",
+        "{\"k1\":[\"s1\",\"s2\",\"s3\"],\"k2\":\"s3\"}",
+        "{\"k1\":{\"k2\":\"s1\",\"k3\":\"s2\",\"k4\":\"s3\"},\"k5\":\"s4\"}",
+        "[\"v1\",\"v2\",[\"v3\",\"v4\",[\"v 5\",\" v 7 \"]],-123456789,"
+        "null,true,false,123456789,\"\"]",
+        "-1",
+        "\"\\\\abc\\\"\\nde\\b\\r/\\ff\\tghi\\u0001\\u001F\"",
+        "9223372036854775807",
+        "-9223372036854775808",
+        NULL
+    };
+    char **tptr, *s, *enc, *p, orig;
+    int i;
+    k5_json_value v, v2;
+
+    v = k5_json_decode("\"string\"");
+    check(v != NULL, "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_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_get_tid(v) == K5_JSON_TID_OBJECT, "object1 tid");
+    v2 = k5_json_object_get(v, "key");
+    check(v2 != NULL, "object[key]");
+    check(k5_json_get_tid(v2) == K5_JSON_TID_STRING, "object1[key] tid");
+    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");
+    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");
+    v2 = k5_json_object_get(v2, "k3");
+    check(v2 != NULL, "object2[k1][k3]");
+    check(k5_json_get_tid(v2) == K5_JSON_TID_STRING, "object2[k1][k3] tid");
+    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_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_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_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_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);
+    check(v2 != NULL, "array1[0]");
+    check(k5_json_get_tid(v2) == K5_JSON_TID_NUMBER, "array1[0] tid");
+    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_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);
+    check(v2 != NULL, "array2[0]");
+    check(k5_json_get_tid(v2) == K5_JSON_TID_NUMBER, "array2[0] tid");
+    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");
+
+    for (tptr = tests; *tptr != NULL; tptr++) {
+        s = strdup(*tptr);
+        v = k5_json_decode(s);
+        if (v == NULL)
+            err(s);
+        enc = k5_json_encode(v);
+        if (enc == NULL || strcmp(enc, s) != 0)
+            err(s);
+        free(enc);
+        k5_json_release(v);
+
+        /* Fuzz bytes.  Parsing may succeed or fail; we're just looking for
+         * memory access bugs. */
+        for (p = s; *p != '\0'; p++) {
+            orig = *p;
+            for (i = 0; i <= 255; i++) {
+                *p = i;
+                k5_json_release(k5_json_decode(s));
+            }
+            *p = orig;
+        }
+        free(s);
+    }
+}
+
+int
+main(int argc, char **argv)
+{
+    test_array();
+    test_object();
+    test_string();
+    test_json();
+    return 0;
+}


More information about the cvs-krb5 mailing list