static int db_replay_log(heim_db_t db, heim_error_t *error) { int ret; heim_string_t journal_fname = NULL; heim_object_t journal; size_t len; heim_assert(!db->in_transaction, "DB transaction not open"); heim_assert(db->set_keys == NULL && db->set_keys == NULL, "DB transaction not open"); if (error) *error = NULL; if (db->options == NULL) return 0; journal_fname = heim_dict_get_value(db->options, HSTR("journal-filename")); if (journal_fname == NULL) return 0; ret = read_json(heim_string_get_utf8(journal_fname), &journal, error); if (ret == ENOENT) return 0; if (ret == 0 && journal == NULL) return 0; if (ret != 0) return ret; if (heim_get_tid(journal) != HEIM_TID_ARRAY) return HEIM_ERROR(error, EINVAL, (ret, N_("Invalid journal contents; delete journal", ""))); len = heim_array_get_length(journal); if (len > 0) db->set_keys = heim_array_get_value(journal, 0); if (len > 1) db->del_keys = heim_array_get_value(journal, 1); ret = db_do_log_actions(db, error); if (ret) return ret; /* Truncate replay log and we're done */ ret = open_file(heim_string_get_utf8(journal_fname), 1, 0, NULL, error); if (ret) return ret; heim_release(db->set_keys); heim_release(db->del_keys); db->set_keys = NULL; db->del_keys = NULL; return 0; }
static heim_data_t json_db_copy_value(void *db, heim_string_t table, heim_data_t key, heim_error_t *error) { json_db_t jsondb = db; heim_string_t key_string; const heim_octet_string *key_data = heim_data_get_data(key); struct stat st; heim_data_t result; if (error) *error = NULL; if (strnlen(key_data->data, key_data->length) != key_data->length) { HEIM_ERROR(error, EINVAL, (EINVAL, N_("JSON DB requires keys that are actually " "strings", ""))); return NULL; } if (stat(heim_string_get_utf8(jsondb->dbname), &st) == -1) { HEIM_ERROR(error, errno, (errno, N_("Could not stat JSON DB file", ""))); return NULL; } if (st.st_mtime > jsondb->last_read_time || st.st_ctime > jsondb->last_read_time) { heim_dict_t contents = NULL; int ret; /* Ignore file is gone (ENOENT) */ ret = read_json(heim_string_get_utf8(jsondb->dbname), (heim_object_t *)&contents, error); if (ret) return NULL; if (contents == NULL) contents = heim_dict_create(29); heim_release(jsondb->dict); jsondb->dict = contents; jsondb->last_read_time = time(NULL); } key_string = heim_string_create_with_bytes(key_data->data, key_data->length); if (key_string == NULL) { (void) HEIM_ENOMEM(error); return NULL; } result = heim_path_copy(jsondb->dict, error, table, key_string, NULL); heim_release(key_string); return result; }
static heim_dict_t parse_dict(struct parse_ctx *ctx) { heim_dict_t dict; size_t count = 0; int ret; heim_assert(*ctx->p == '{', "string doesn't start with {"); dict = heim_dict_create(11); if (dict == NULL) { ctx->error = heim_error_create_enomem(); return NULL; } ctx->p += 1; /* safe because parse_pair() calls white_spaces() first */ while ((ret = parse_pair(dict, ctx)) > 0) count++; if (ret < 0) { heim_release(dict); return NULL; } if (count == 1 && !(ctx->flags & HEIM_JSON_F_NO_DATA_DICT)) { heim_object_t v = heim_dict_copy_value(dict, heim_tid_data_uuid_key); /* * Binary data encoded as a dict with a single magic key with * base64-encoded value? Decode as heim_data_t. */ if (v != NULL && heim_get_tid(v) == HEIM_TID_STRING) { void *buf; size_t len; buf = malloc(strlen(heim_string_get_utf8(v))); if (buf == NULL) { heim_release(dict); heim_release(v); ctx->error = heim_error_create_enomem(); return NULL; } len = base64_decode(heim_string_get_utf8(v), buf); heim_release(v); if (len == -1) { free(buf); return dict; /* assume aliasing accident */ } heim_release(dict); return (heim_dict_t)heim_data_ref_create(buf, len, free); } } return dict; }
static void eval_kgetcred(heim_dict_t o) { heim_string_t server, ccache; krb5_get_creds_opt opt; heim_bool_t nostore; krb5_error_code ret; krb5_ccache cc = NULL; krb5_principal s; krb5_creds *out = NULL; if (ptop) ptop->tgs_req++; server = heim_dict_get_value(o, HSTR("server")); if (server == NULL) krb5_errx(kdc_context, 1, "no server"); ccache = heim_dict_get_value(o, HSTR("ccache")); if (ccache == NULL) krb5_errx(kdc_context, 1, "no ccache"); nostore = heim_dict_get_value(o, HSTR("nostore")); if (nostore == NULL) nostore = heim_bool_create(1); ret = krb5_cc_resolve(kdc_context, heim_string_get_utf8(ccache), &cc); if (ret) krb5_err(kdc_context, 1, ret, "krb5_cc_resolve"); ret = krb5_parse_name(kdc_context, heim_string_get_utf8(server), &s); if (ret) krb5_err(kdc_context, 1, ret, "krb5_parse_name"); ret = krb5_get_creds_opt_alloc(kdc_context, &opt); if (ret) krb5_err(kdc_context, 1, ret, "krb5_get_creds_opt_alloc"); if (heim_bool_val(nostore)) krb5_get_creds_opt_add_options(kdc_context, opt, KRB5_GC_NO_STORE); ret = krb5_get_creds(kdc_context, opt, cc, s, &out); if (ret) krb5_err(kdc_context, 1, ret, "krb5_get_creds"); krb5_free_creds(kdc_context, out); krb5_free_principal(kdc_context, s); krb5_get_creds_opt_free(kdc_context, opt); krb5_cc_close(kdc_context, cc); }
char * hx509_get_error_string(hx509_context context, int error_code) { heim_error_t msg = context->error; heim_string_t s; char *str = NULL; if (msg == NULL || heim_error_get_code(msg) != error_code) { const char *cstr; cstr = com_right(context->et_list, error_code); if (cstr) return strdup(cstr); cstr = strerror(error_code); if (cstr) return strdup(cstr); if (asprintf(&str, "<unknown error: %d>", error_code) == -1) return NULL; return str; } s = heim_error_copy_string(msg); if (s) { const char *cstr = heim_string_get_utf8(s); if (cstr) str = strdup(cstr); heim_release(s); } return str; }
static krb5_error_code an2ln_plugin(krb5_context context, const char *rule, krb5_const_principal aname, size_t lnsize, char *lname) { krb5_error_code ret; struct plctx ctx; ctx.rule = rule; ctx.aname = aname; ctx.luser = NULL; /* * Order of plugin invocation is non-deterministic, but there should * really be no more than one plugin that can handle any given kind * rule, so the effect should be deterministic anyways. */ ret = _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_AN2LN, KRB5_PLUGIN_AN2LN_VERSION_0, 0, &ctx, plcallback); if (ret != 0) { heim_release(ctx.luser); return ret; } if (ctx.luser == NULL) return KRB5_PLUGIN_NO_HANDLE; if (strlcpy(lname, heim_string_get_utf8(ctx.luser), lnsize) >= lnsize) ret = KRB5_CONFIG_NOTENUFSPACE; heim_release(ctx.luser); return ret; }
static int base2json(heim_object_t obj, struct twojson *j) { heim_tid_t type; if (obj == NULL) { indent(j); j->out(j->ctx, "<NULL>\n"); } type = heim_get_tid(obj); switch (type) { case HEIM_TID_ARRAY: indent(j); j->out(j->ctx, "[\n"); j->indent++; heim_array_iterate_f(obj, j, array2json); j->indent--; indent(j); j->out(j->ctx, "]\n"); break; case HEIM_TID_DICT: indent(j); j->out(j->ctx, "{\n"); j->indent++; heim_dict_iterate_f(obj, j, dict2json); j->indent--; indent(j); j->out(j->ctx, "}\n"); break; case HEIM_TID_STRING: indent(j); j->out(j->ctx, "\""); j->out(j->ctx, heim_string_get_utf8(obj)); j->out(j->ctx, "\""); break; case HEIM_TID_NUMBER: { char num[16]; indent(j); snprintf(num, sizeof(num), "%d", heim_number_get_int(obj)); j->out(j->ctx, num); break; } case HEIM_TID_NULL: indent(j); j->out(j->ctx, "null"); break; case HEIM_TID_BOOL: indent(j); j->out(j->ctx, heim_bool_val(obj) ? "true" : "false"); break; default: return 1; } return 0; }
/* * Helper to create a DB handle with the first registered DB type that * can open the given DB. This is useful when the app doesn't know the * DB type a priori. This assumes that DB types can "taste" DBs, either * from the filename extension or from the actual file contents. */ static void dbtype_iter2create_f(heim_object_t dbtype, heim_object_t junk, void *arg) { struct dbtype_iter *iter_ctx = arg; if (iter_ctx->db != NULL) return; iter_ctx->db = heim_db_create(heim_string_get_utf8(dbtype), iter_ctx->dbname, iter_ctx->options, iter_ctx->error); }
static void json_db_iter_f(heim_object_t key, heim_object_t value, void *arg) { struct json_db_iter_ctx *ctx = arg; const char *key_string; heim_data_t key_data; key_string = heim_string_get_utf8((heim_string_t)key); key_data = heim_data_ref_create(key_string, strlen(key_string), NULL); ctx->iter_f(key_data, (heim_object_t)value, ctx->iter_ctx); heim_release(key_data); }
static heim_data_t from_base64(heim_string_t s, heim_error_t *error) { void *buf; size_t len; heim_data_t d; buf = malloc(strlen(heim_string_get_utf8(s))); if (buf == NULL) goto enomem; len = rk_base64_decode(heim_string_get_utf8(s), buf); d = heim_data_ref_create(buf, len, free); if (d == NULL) goto enomem; return d; enomem: free(buf); if (error) *error = heim_error_create_enomem(); return NULL; }
/** * Clone (duplicate) an open DB handle. * * This is useful for multi-threaded applications. Applications must * synchronize access to any given DB handle. * * Returns EBUSY if there is an open transaction for the input db. * * @param db Open DB handle * @param error Output error object * * @return a DB handle * * @addtogroup heimbase */ heim_db_t heim_db_clone(heim_db_t db, heim_error_t *error) { heim_db_t result; int ret; if (heim_get_tid(db) != HEIM_TID_DB) heim_abort("Expected a database"); if (db->in_transaction) heim_abort("DB handle is busy"); if (db->plug->clonef == NULL) { return heim_db_create(heim_string_get_utf8(db->dbtype), heim_string_get_utf8(db->dbname), db->options, error); } result = _heim_alloc_object(&db_object, sizeof(*result)); if (result == NULL) { if (error) *error = heim_error_create_enomem(); return NULL; } result->set_keys = NULL; result->del_keys = NULL; ret = db->plug->clonef(db->db_data, &result->db_data, error); if (ret) { heim_release(result); if (error && !*error) *error = heim_error_create(ENOENT, N_("Could not re-open DB while cloning", "")); return NULL; } db->db_data = NULL; return result; }
static int json_db_unlock(void *db, heim_error_t *error) { json_db_t jsondb = db; int ret = 0; heim_assert(jsondb->locked, "DB not locked when unlock attempted"); if (jsondb->fd > -1) ret = close(jsondb->fd); jsondb->fd = -1; jsondb->read_only = 0; jsondb->locked = 0; if (jsondb->locked_needs_unlink) unlink(heim_string_get_utf8(jsondb->bkpname)); jsondb->locked_needs_unlink = 0; return ret; }
static void eval_kdestroy(heim_dict_t o) { heim_string_t ccache = heim_dict_get_value(o, HSTR("ccache"));; krb5_error_code ret; const char *name; krb5_ccache cc; heim_assert(ccache != NULL, "ccache_missing"); name = heim_string_get_utf8(ccache); ret = krb5_cc_resolve(kdc_context, name, &cc); if (ret) krb5_err(kdc_context, 1, ret, "krb5_cc_resolve"); krb5_cc_destroy(kdc_context, cc); }
static int json_db_lock(void *db, int read_only, heim_error_t *error) { json_db_t jsondb = db; int ret; heim_assert(jsondb->fd == -1 || (jsondb->read_only && !read_only), "DB locks are not recursive"); jsondb->read_only = read_only ? 1 : 0; if (jsondb->fd > -1) return 0; ret = open_file(heim_string_get_utf8(jsondb->bkpname), 1, 1, &jsondb->fd, error); if (ret == 0) { jsondb->locked_needs_unlink = 1; jsondb->locked = 1; } return ret; }
/** * Commit an open transaction on the given db. * * @param db Open DB handle * @param error Output error object * * @return 0 on success, system error otherwise * * @addtogroup heimbase */ int heim_db_commit(heim_db_t db, heim_error_t *error) { int ret, ret2; heim_string_t journal_fname = NULL; if (heim_get_tid(db) != HEIM_TID_DB) return EINVAL; if (!db->in_transaction) return 0; if (db->plug->commitf == NULL && db->plug->lockf == NULL) return EINVAL; if (db->plug->commitf != NULL) { ret = db->plug->commitf(db->db_data, error); if (ret) (void) db->plug->rollbackf(db->db_data, error); db->in_transaction = 0; db->ro_tx = 0; return ret; } if (db->ro_tx) { ret = 0; goto done; } if (db->options == NULL) journal_fname = heim_dict_get_value(db->options, HSTR("journal-filename")); if (journal_fname != NULL) { heim_array_t a; heim_string_t journal_contents; size_t len, bytes; int save_errno; /* Create contents for replay log */ ret = ENOMEM; a = heim_array_create(); if (a == NULL) goto err; ret = heim_array_append_value(a, db->set_keys); if (ret) { heim_release(a); goto err; } ret = heim_array_append_value(a, db->del_keys); if (ret) { heim_release(a); goto err; } journal_contents = heim_json_copy_serialize(a, 0, error); heim_release(a); /* Write replay log */ if (journal_fname != NULL) { int fd; ret = open_file(heim_string_get_utf8(journal_fname), 1, 0, &fd, error); if (ret) { heim_release(journal_contents); goto err; } len = strlen(heim_string_get_utf8(journal_contents)); bytes = write(fd, heim_string_get_utf8(journal_contents), len); save_errno = errno; heim_release(journal_contents); ret = close(fd); if (bytes != len) { /* Truncate replay log */ (void) open_file(heim_string_get_utf8(journal_fname), 1, 0, NULL, error); ret = save_errno; goto err; } if (ret) goto err; } } /* Apply logged actions */ ret = db_do_log_actions(db, error); if (ret) return ret; if (db->plug->syncf != NULL) { /* fsync() or whatever */ ret = db->plug->syncf(db->db_data, error); if (ret) return ret; } /* Truncate replay log and we're done */ if (journal_fname != NULL) { int fd; ret2 = open_file(heim_string_get_utf8(journal_fname), 1, 0, &fd, error); if (ret2 == 0) (void) close(fd); } /* * Clean up; if we failed to remore the replay log that's OK, we'll * handle that again in heim_db_commit() */ done: heim_release(db->set_keys); heim_release(db->del_keys); db->set_keys = NULL; db->del_keys = NULL; db->in_transaction = 0; db->ro_tx = 0; ret2 = db->plug->unlockf(db->db_data, error); if (ret == 0) ret = ret2; return ret; err: return HEIM_ERROR(error, ret, (ret, N_("Error while committing transaction: %s", ""), strerror(ret))); }
static int json_db_sync(void *db, heim_error_t *error) { json_db_t jsondb = db; size_t len, bytes; heim_error_t e; heim_string_t json; const char *json_text = NULL; int ret = 0; int fd = -1; #ifdef WIN32 int tries = 3; #endif heim_assert(jsondb->fd > -1, "DB not locked when sync attempted"); json = heim_json_copy_serialize(jsondb->dict, 0, &e); if (json == NULL) { if (error) *error = e; else heim_release(e); return heim_error_get_code(e); } json_text = heim_string_get_utf8(json); len = strlen(json_text); errno = 0; #ifdef WIN32 while (tries--) { ret = open_file(heim_string_get_utf8(jsondb->dbname), 1, 0, &fd, error); if (ret == 0) break; sleep(1); } if (ret) { heim_release(json); return ret; } #else fd = jsondb->fd; #endif /* WIN32 */ bytes = write(fd, json_text, len); heim_release(json); if (bytes != len) return errno ? errno : EIO; ret = fsync(fd); if (ret) return ret; #ifdef WIN32 ret = close(fd); if (ret) return GetLastError(); #else ret = rename(heim_string_get_utf8(jsondb->bkpname), heim_string_get_utf8(jsondb->dbname)); if (ret == 0) { jsondb->locked_needs_unlink = 0; return 0; } #endif /* WIN32 */ return errno; }
static void eval_kinit(heim_dict_t o) { heim_string_t user, password, keytab, fast_armor_cc, pk_user_id, ccache; krb5_get_init_creds_opt *opt; krb5_init_creds_context ctx; krb5_principal client; krb5_keytab ktmem = NULL; krb5_ccache fast_cc = NULL; krb5_error_code ret; if (ptop) ptop->as_req++; user = heim_dict_get_value(o, HSTR("client")); if (user == NULL) krb5_errx(kdc_context, 1, "no client"); password = heim_dict_get_value(o, HSTR("password")); keytab = heim_dict_get_value(o, HSTR("keytab")); pk_user_id = heim_dict_get_value(o, HSTR("pkinit-user-cert-id")); if (password == NULL && keytab == NULL && pk_user_id == NULL) krb5_errx(kdc_context, 1, "password, keytab, nor PKINIT user cert ID"); ccache = heim_dict_get_value(o, HSTR("ccache")); ret = krb5_parse_name(kdc_context, heim_string_get_utf8(user), &client); if (ret) krb5_err(kdc_context, 1, ret, "krb5_unparse_name"); /* PKINIT parts */ ret = krb5_get_init_creds_opt_alloc (kdc_context, &opt); if (ret) krb5_err(kdc_context, 1, ret, "krb5_get_init_creds_opt_alloc"); if (pk_user_id) { heim_bool_t rsaobj = heim_dict_get_value(o, HSTR("pkinit-use-rsa")); int use_rsa = rsaobj ? heim_bool_val(rsaobj) : 0; ret = krb5_get_init_creds_opt_set_pkinit(kdc_context, opt, client, heim_string_get_utf8(pk_user_id), NULL, NULL, NULL, use_rsa ? 2 : 0, NULL, NULL, NULL); if (ret) krb5_err(kdc_context, 1, ret, "krb5_get_init_creds_opt_set_pkinit"); } ret = krb5_init_creds_init(kdc_context, client, NULL, NULL, 0, opt, &ctx); if (ret) krb5_err(kdc_context, 1, ret, "krb5_init_creds_init"); fast_armor_cc = heim_dict_get_value(o, HSTR("fast-armor-cc")); if (fast_armor_cc) { ret = krb5_cc_resolve(kdc_context, heim_string_get_utf8(fast_armor_cc), &fast_cc); if (ret) krb5_err(kdc_context, 1, ret, "krb5_cc_resolve"); ret = krb5_init_creds_set_fast_ccache(kdc_context, ctx, fast_cc); if (ret) krb5_err(kdc_context, 1, ret, "krb5_init_creds_set_fast_ccache"); } if (password) { ret = krb5_init_creds_set_password(kdc_context, ctx, heim_string_get_utf8(password)); if (ret) krb5_err(kdc_context, 1, ret, "krb5_init_creds_set_password"); } if (keytab) { krb5_keytab kt = NULL; ret = krb5_kt_resolve(kdc_context, heim_string_get_utf8(keytab), &kt); if (ret) krb5_err(kdc_context, 1, ret, "krb5_kt_resolve"); ret = krb5_kt_resolve(kdc_context, "MEMORY:keytab", &ktmem); if (ret) krb5_err(kdc_context, 1, ret, "krb5_kt_resolve(MEMORY)"); ret = copy_keytab(kdc_context, kt, ktmem); if (ret) krb5_err(kdc_context, 1, ret, "copy_keytab"); krb5_kt_close(kdc_context, kt); ret = krb5_init_creds_set_keytab(kdc_context, ctx, ktmem); if (ret) krb5_err(kdc_context, 1, ret, "krb5_init_creds_set_keytab"); } ret = krb5_init_creds_get(kdc_context, ctx); if (ret) krb5_err(kdc_context, 1, ret, "krb5_init_creds_get"); if (ccache) { const char *name = heim_string_get_utf8(ccache); krb5_creds cred; krb5_ccache cc; ret = krb5_init_creds_get_creds(kdc_context, ctx, &cred); if (ret) krb5_err(kdc_context, 1, ret, "krb5_init_creds_get_creds"); ret = krb5_cc_resolve(kdc_context, name, &cc); if (ret) krb5_err(kdc_context, 1, ret, "krb5_cc_resolve"); krb5_init_creds_store(kdc_context, ctx, cc); ret = krb5_cc_close(kdc_context, cc); if (ret) krb5_err(kdc_context, 1, ret, "krb5_cc_close"); krb5_free_cred_contents(kdc_context, &cred); } krb5_init_creds_free(kdc_context, ctx); if (ktmem) krb5_kt_close(kdc_context, ktmem); if (fast_cc) krb5_cc_close(kdc_context, fast_cc); }
static heim_string_t parse_string(struct parse_ctx *ctx) { const uint8_t *start; int quote = 0; if (ctx->flags & HEIM_JSON_F_STRICT_STRINGS) { ctx->error = heim_error_create(EINVAL, "Strict JSON string encoding " "not yet supported"); return NULL; } if (*ctx->p != '"') { ctx->error = heim_error_create(EINVAL, "Expected a JSON string but " "found something else at line %lu", ctx->lineno); return NULL; } start = ++ctx->p; while (ctx->p < ctx->pend) { if (*ctx->p == '\n') { ctx->lineno++; } else if (*ctx->p == '\\') { if (ctx->p + 1 == ctx->pend) goto out; ctx->p++; quote = 1; } else if (*ctx->p == '"') { heim_object_t o; if (quote) { char *p0, *p; p = p0 = malloc(ctx->p - start); if (p == NULL) goto out; while (start < ctx->p) { if (*start == '\\') { start++; /* XXX validate quoted char */ } *p++ = *start++; } o = heim_string_create_with_bytes(p0, p - p0); free(p0); } else { o = heim_string_create_with_bytes(start, ctx->p - start); if (o == NULL) { ctx->error = heim_error_create_enomem(); return NULL; } /* If we can decode as base64, then let's */ if (ctx->flags & HEIM_JSON_F_TRY_DECODE_DATA) { void *buf; size_t len; const char *s; s = heim_string_get_utf8(o); len = strlen(s); if (len >= 4 && strspn(s, base64_chars) >= len - 2) { buf = malloc(len); if (buf == NULL) { heim_release(o); ctx->error = heim_error_create_enomem(); return NULL; } len = base64_decode(s, buf); if (len == -1) { free(buf); return o; } heim_release(o); o = heim_data_ref_create(buf, len, free); } } } ctx->p += 1; return o; } ctx->p += 1; } out: ctx->error = heim_error_create(EINVAL, "ran out of string"); return NULL; }
static int base2json(heim_object_t obj, struct twojson *j) { heim_tid_t type; int first = 0; if (obj == NULL) { if (j->flags & HEIM_JSON_F_CNULL2JSNULL) { obj = heim_null_create(); } else if (j->flags & HEIM_JSON_F_NO_C_NULL) { return EINVAL; } else { indent(j); j->out(j->ctx, "<NULL>\n"); /* This is NOT valid JSON! */ return 0; } } type = heim_get_tid(obj); switch (type) { case HEIM_TID_ARRAY: indent(j); j->out(j->ctx, "[\n"); j->indent++; first = j->first; j->first = 1; heim_array_iterate_f(obj, j, array2json); j->indent--; if (!j->first) j->out(j->ctx, "\n"); indent(j); j->out(j->ctx, "]\n"); j->first = first; break; case HEIM_TID_DICT: indent(j); j->out(j->ctx, "{\n"); j->indent++; first = j->first; j->first = 1; heim_dict_iterate_f(obj, j, dict2json); j->indent--; if (!j->first) j->out(j->ctx, "\n"); indent(j); j->out(j->ctx, "}\n"); j->first = first; break; case HEIM_TID_STRING: indent(j); j->out(j->ctx, "\""); j->out(j->ctx, heim_string_get_utf8(obj)); j->out(j->ctx, "\""); break; case HEIM_TID_DATA: { heim_dict_t d; heim_string_t v; const heim_octet_string *data; char *b64 = NULL; int ret; if (j->flags & HEIM_JSON_F_NO_DATA) return EINVAL; /* JSON doesn't do binary */ data = heim_data_get_data(obj); ret = base64_encode(data->data, data->length, &b64); if (ret < 0 || b64 == NULL) return ENOMEM; if (j->flags & HEIM_JSON_F_NO_DATA_DICT) { indent(j); j->out(j->ctx, "\""); j->out(j->ctx, b64); /* base64-encode; hope there's no aliasing */ j->out(j->ctx, "\""); free(b64); } else { /* * JSON has no way to represent binary data, therefore the * following is a Heimdal-specific convention. * * We encode binary data as a dict with a single very magic * key with a base64-encoded value. The magic key includes * a uuid, so we're not likely to alias accidentally. */ d = heim_dict_create(2); if (d == NULL) { free(b64); return ENOMEM; } v = heim_string_ref_create(b64, free); if (v == NULL) { free(b64); heim_release(d); return ENOMEM; } ret = heim_dict_set_value(d, heim_tid_data_uuid_key, v); heim_release(v); if (ret) { heim_release(d); return ENOMEM; } ret = base2json(d, j); heim_release(d); if (ret) return ret; } break; } case HEIM_TID_NUMBER: { char num[32]; indent(j); snprintf(num, sizeof (num), "%d", heim_number_get_int(obj)); j->out(j->ctx, num); break; } case HEIM_TID_NULL: indent(j); j->out(j->ctx, "null"); break; case HEIM_TID_BOOL: indent(j); j->out(j->ctx, heim_bool_val(obj) ? "true" : "false"); break; default: return 1; } return 0; }