summaryrefslogtreecommitdiffstats
path: root/src/util
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2012-08-09 18:05:50 -0400
committerGreg Hudson <ghudson@mit.edu>2012-09-11 01:14:31 -0400
commit382a87cf344b002bf5660ed3f27799ed18c54948 (patch)
tree55eb5a5e5c574775f6e55716cb735f50f3cc0c65 /src/util
parent3b55b8aba850fa54a4d0c5017a91641538c16835 (diff)
downloadkrb5-382a87cf344b002bf5660ed3f27799ed18c54948.tar.gz
krb5-382a87cf344b002bf5660ed3f27799ed18c54948.tar.xz
krb5-382a87cf344b002bf5660ed3f27799ed18c54948.zip
Add internal JSON encoding and decoding support
Add JSON support based loosely on Heimdal's heimbase code.
Diffstat (limited to 'src/util')
-rw-r--r--src/util/support/Makefile.in15
-rw-r--r--src/util/support/json.c903
-rw-r--r--src/util/support/libkrb5support-fixed.exports24
-rw-r--r--src/util/support/t_json.c297
4 files changed, 1237 insertions, 2 deletions
diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in
index ca04ad7134..690a2c373c 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 0000000000..e6d7eea269
--- /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 496259c992..13b1b575e9 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 0000000000..7b30007afd
--- /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;
+}