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); }
/** heim_db_register * @brief Registers a DB type for use with heim_db_create(). * * @param dbtype Name of DB type * @param data Private data argument to the dbtype's openf method * @param plugin Structure with DB type methods (function pointers) * * Backends that provide begin/commit/rollback methods must provide ACID * semantics. * * The registered DB type will have ACID semantics for backends that do * not provide begin/commit/rollback methods but do provide lock/unlock * and rdjournal/wrjournal methods (using a replay log journalling * scheme). * * If the registered DB type does not natively provide read vs. write * transaction isolation but does provide a lock method then the DB will * provide read/write transaction isolation. * * @return ENOMEM on failure, else 0. * * @addtogroup heimbase */ int heim_db_register(const char *dbtype, void *data, struct heim_db_type *plugin) { heim_dict_t plugins; heim_string_t s; db_plugin plug, plug2; int ret = 0; if ((plugin->beginf != NULL && plugin->commitf == NULL) || (plugin->beginf != NULL && plugin->rollbackf == NULL) || (plugin->lockf != NULL && plugin->unlockf == NULL) || plugin->copyf == NULL) heim_abort("Invalid DB plugin; make sure methods are paired"); /* Initialize */ plugins = heim_dict_create(11); if (plugins == NULL) return ENOMEM; heim_base_once_f(&db_plugin_init_once, plugins, db_init_plugins_once); heim_release(plugins); heim_assert(db_plugins != NULL, "heim_db plugin table initialized"); s = heim_string_create(dbtype); if (s == NULL) return ENOMEM; plug = heim_alloc(sizeof (*plug), "db_plug", plugin_dealloc); if (plug == NULL) { heim_release(s); return ENOMEM; } plug->name = heim_retain(s); plug->openf = plugin->openf; plug->clonef = plugin->clonef; plug->closef = plugin->closef; plug->lockf = plugin->lockf; plug->unlockf = plugin->unlockf; plug->syncf = plugin->syncf; plug->beginf = plugin->beginf; plug->commitf = plugin->commitf; plug->rollbackf = plugin->rollbackf; plug->copyf = plugin->copyf; plug->setf = plugin->setf; plug->delf = plugin->delf; plug->iterf = plugin->iterf; plug->data = data; HEIMDAL_MUTEX_lock(&db_type_mutex); plug2 = heim_dict_get_value(db_plugins, s); if (plug2 == NULL) ret = heim_dict_set_value(db_plugins, s, plug); HEIMDAL_MUTEX_unlock(&db_type_mutex); heim_release(plug); heim_release(s); return ret; }
static void search_modules(heim_object_t key, heim_object_t value, void *ctx) { struct iter_ctx *s = ctx; struct plugin2 *p = value; struct plug *pl = heim_dict_get_value(p->names, s->n); struct common_plugin_method *cpm; if (pl == NULL) { if (p->dsohandle == NULL) return; pl = heim_alloc(sizeof(*pl), "struct-plug", plug_free); cpm = pl->dataptr = dlsym(p->dsohandle, s->name); if (cpm) { int ret; ret = cpm->init(s->context, &pl->ctx); if (ret) cpm = pl->dataptr = NULL; } heim_dict_set_value(p->names, s->n, pl); heim_release(pl); } else { cpm = pl->dataptr; } if (cpm && cpm->version >= s->min_version) heim_array_append_value(s->result, pl); }
static void eval_object(heim_object_t o) { heim_tid_t t = heim_get_tid(o); if (t == heim_array_get_type_id()) { heim_array_iterate_f(o, NULL, eval_array_element); } else if (t == heim_dict_get_type_id()) { const char *op = heim_dict_get_value(o, HSTR("op")); heim_assert(op != NULL, "op missing"); if (strcmp(op, "repeat") == 0) { eval_repeat(o); } else if (strcmp(op, "kinit") == 0) { eval_kinit(o); } else if (strcmp(op, "kgetcred") == 0) { eval_kgetcred(o); } else if (strcmp(op, "kdestroy") == 0) { eval_kdestroy(o); } else { errx(1, "unsupported ops %s", op); } } else errx(1, "unsupported"); }
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 void eval_repeat(heim_dict_t o) { heim_object_t or = heim_dict_get_value(o, HSTR("value")); heim_number_t n = heim_dict_get_value(o, HSTR("num")); int i, num; struct perf perf; perf_start(&perf); heim_assert(or != NULL, "value missing"); heim_assert(n != NULL, "num missing"); num = heim_number_get_int(n); heim_assert(num >= 0, "num >= 0"); for (i = 0; i < num; i++) eval_object(or); perf_stop(&perf); }
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 void json_db_iter(void *db, heim_string_t table, void *iter_data, heim_db_iterator_f_t iter_f, heim_error_t *error) { json_db_t jsondb = db; struct json_db_iter_ctx ctx; heim_dict_t table_dict; if (error) *error = NULL; if (table == NULL) table = HSTR(""); table_dict = heim_dict_get_value(jsondb->dict, table); if (table_dict == NULL) return; ctx.iter_ctx = iter_data; ctx.iter_f = iter_f; heim_dict_iterate_f(table_dict, &ctx, json_db_iter_f); }
heim_string_t __heim_string_constant(const char *_str) { static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER; static heim_base_once_t once; static heim_dict_t dict = NULL; heim_string_t s, s2; heim_base_once_f(&once, &dict, init_string); s = heim_string_create(_str); HEIMDAL_MUTEX_lock(&mutex); s2 = heim_dict_get_value(dict, s); if (s2) { heim_release(s); s = s2; } else { _heim_make_permanent(s); heim_dict_set_value(dict, s, s); } HEIMDAL_MUTEX_unlock(&mutex); return s; }
/** * 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))); }
/** * Open a database of the given dbtype. * * Database type names can be composed of one or more pseudo-DB types * and one concrete DB type joined with a '+' between each. For * example: "transaction+bdb" might be a Berkeley DB with a layer above * that provides transactions. * * Options may be provided via a dict (an associative array). Existing * options include: * * - "create", with any value (create if DB doesn't exist) * - "exclusive", with any value (exclusive create) * - "truncate", with any value (truncate the DB) * - "read-only", with any value (disallow writes) * - "sync", with any value (make transactions durable) * - "journal-name", with a string value naming a journal file name * * @param dbtype Name of DB type * @param dbname Name of DB (likely a file path) * @param options Options dict * @param db Output open DB handle * @param error Output error object * * @return a DB handle * * @addtogroup heimbase */ heim_db_t heim_db_create(const char *dbtype, const char *dbname, heim_dict_t options, heim_error_t *error) { heim_string_t s; char *p; db_plugin plug; heim_db_t db; int ret = 0; if (options == NULL) { options = heim_dict_create(11); if (options == NULL) { if (error) *error = heim_error_create_enomem(); return NULL; } } else { (void) heim_retain(options); } if (db_plugins == NULL) { heim_release(options); return NULL; } if (dbtype == NULL || *dbtype == '\0') { struct dbtype_iter iter_ctx = { NULL, dbname, options, error}; /* Try all dbtypes */ heim_dict_iterate_f(db_plugins, &iter_ctx, dbtype_iter2create_f); heim_release(options); return iter_ctx.db; } else if (strstr(dbtype, "json")) { (void) heim_db_register(dbtype, NULL, &json_dbt); } /* * Allow for dbtypes that are composed from pseudo-dbtypes chained * to a real DB type with '+'. For example a pseudo-dbtype might * add locking, transactions, transcoding of values, ... */ p = strchr(dbtype, '+'); if (p != NULL) s = heim_string_create_with_bytes(dbtype, p - dbtype); else s = heim_string_create(dbtype); if (s == NULL) { heim_release(options); return NULL; } HEIMDAL_MUTEX_lock(&db_type_mutex); plug = heim_dict_get_value(db_plugins, s); HEIMDAL_MUTEX_unlock(&db_type_mutex); heim_release(s); if (plug == NULL) { if (error) *error = heim_error_create(ENOENT, N_("Heimdal DB plugin not found: %s", ""), dbtype); heim_release(options); return NULL; } db = _heim_alloc_object(&db_object, sizeof(*db)); if (db == NULL) { heim_release(options); return NULL; } db->in_transaction = 0; db->ro_tx = 0; db->set_keys = NULL; db->del_keys = NULL; db->plug = plug; db->options = options; ret = plug->openf(plug->data, dbtype, dbname, options, &db->db_data, error); if (ret) { heim_release(db); if (error && *error == NULL) *error = heim_error_create(ENOENT, N_("Heimdal DB could not be opened: %s", ""), dbname); return NULL; } ret = db_replay_log(db, error); if (ret) { heim_release(db); return NULL; } if (plug->clonef == NULL) { db->dbtype = heim_string_create(dbtype); db->dbname = heim_string_create(dbname); if (!db->dbtype || ! db->dbname) { heim_release(db); if (error) *error = heim_error_create_enomem(); return NULL; } } return db; }
static int json_db_open(void *plug, const char *dbtype, const char *dbname, heim_dict_t options, void **db, heim_error_t *error) { json_db_t jsondb; heim_dict_t contents = NULL; heim_string_t dbname_s = NULL; heim_string_t bkpname_s = NULL; if (error) *error = NULL; if (dbtype && *dbtype && strcmp(dbtype, "json")) return HEIM_ERROR(error, EINVAL, (EINVAL, N_("Wrong DB type", ""))); if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0) { char *ext = strrchr(dbname, '.'); char *bkpname; size_t len; int ret; if (ext == NULL || strcmp(ext, ".json") != 0) return HEIM_ERROR(error, EINVAL, (EINVAL, N_("JSON DB files must end in .json", ""))); if (options) { heim_object_t vc, ve, vt; vc = heim_dict_get_value(options, HSTR("create")); ve = heim_dict_get_value(options, HSTR("exclusive")); vt = heim_dict_get_value(options, HSTR("truncate")); if (vc && vt) { ret = open_file(dbname, 1, ve ? 1 : 0, NULL, error); if (ret) return ret; } else if (vc || ve || vt) { return HEIM_ERROR(error, EINVAL, (EINVAL, N_("Invalid JSON DB open options", ""))); } /* * We don't want cloned handles to truncate the DB, eh? * * We should really just create a copy of the options dict * rather than modify the caller's! But for that it'd be * nicer to have copy utilities in heimbase, something like * this: * * heim_object_t heim_copy(heim_object_t src, int depth, * heim_error_t *error); * * so that options = heim_copy(options, 1); means copy the * dict but nothing else (whereas depth == 0 would mean * heim_retain(), and depth > 1 would be copy that many * levels). */ heim_dict_delete_key(options, HSTR("create")); heim_dict_delete_key(options, HSTR("exclusive")); heim_dict_delete_key(options, HSTR("truncate")); } dbname_s = heim_string_create(dbname); if (dbname_s == NULL) return HEIM_ENOMEM(error); len = snprintf(NULL, 0, "%s~", dbname); bkpname = malloc(len + 2); if (bkpname == NULL) { heim_release(dbname_s); return HEIM_ENOMEM(error); } (void) snprintf(bkpname, len + 1, "%s~", dbname); bkpname_s = heim_string_create(bkpname); free(bkpname); if (bkpname_s == NULL) { heim_release(dbname_s); return HEIM_ENOMEM(error); } ret = read_json(dbname, (heim_object_t *)&contents, error); if (ret) return ret; if (contents != NULL && heim_get_tid(contents) != HEIM_TID_DICT) return HEIM_ERROR(error, EINVAL, (EINVAL, N_("JSON DB contents not valid JSON", ""))); } jsondb = heim_alloc(sizeof (*jsondb), "json_db", NULL); if (jsondb == NULL) { heim_release(contents); heim_release(dbname_s); return ENOMEM; } jsondb->last_read_time = time(NULL); jsondb->fd = -1; jsondb->dbname = dbname_s; jsondb->bkpname = bkpname_s; jsondb->read_only = 0; if (contents != NULL) jsondb->dict = contents; else { jsondb->dict = heim_dict_create(29); if (jsondb->dict == NULL) { heim_release(jsondb); return ENOMEM; } } *db = jsondb; return 0; }
/** * Run plugins for the given @module (e.g., "krb5") and @name (e.g., * "kuserok"). Specifically, the @func is invoked once per-plugin with * four arguments: the @context, the plugin symbol value (a pointer to a * struct whose first three fields are the same as struct common_plugin_method), * a context value produced by the plugin's init method, and @userctx. * * @func should unpack arguments for a plugin function and invoke it * with arguments taken from @userctx. @func should save plugin * outputs, if any, in @userctx. * * All loaded and registered plugins are invoked via @func until @func * returns something other than KRB5_PLUGIN_NO_HANDLE. Plugins that * have nothing to do for the given arguments should return * KRB5_PLUGIN_NO_HANDLE. * * Inputs: * * @context A krb5_context * @module Name of module (typically "krb5") * @name Name of pluggable interface (e.g., "kuserok") * @min_version Lowest acceptable plugin minor version number * @flags Flags (none defined at this time) * @userctx Callback data for the callback function @func * @func A callback function, invoked once per-plugin * * Outputs: None, other than the return value and such outputs as are * gathered by @func. */ krb5_error_code _krb5_plugin_run_f(krb5_context context, const char *module, const char *name, int min_version, int flags, void *userctx, krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *)) { heim_string_t m = heim_string_create(module); heim_dict_t dict; void *plug_ctx; struct common_plugin_method *cpm; struct iter_ctx s; struct krb5_plugin *registered_plugins = NULL; struct krb5_plugin *p; /* Get registered plugins */ (void) _krb5_plugin_find(context, SYMBOL, name, ®istered_plugins); HEIMDAL_MUTEX_lock(&plugin_mutex); s.context = context; s.name = name; s.n = heim_string_create(name); s.min_version = min_version; s.result = heim_array_create(); s.func = func; s.userctx = userctx; s.ret = KRB5_PLUGIN_NO_HANDLE; /* Get loaded plugins */ dict = heim_dict_get_value(modules, m); heim_release(m); /* Add loaded plugins to s.result array */ if (dict) heim_dict_iterate_f(dict, &s, search_modules); /* We don't need to hold plugin_mutex during plugin invocation */ HEIMDAL_MUTEX_unlock(&plugin_mutex); /* Invoke registered plugins (old system) */ for (p = registered_plugins; p; p = p->next) { /* * XXX This is the wrong way to handle registered plugins, as we * call init/fini on each invocation! We do this because we * have nowhere in the struct plugin registered list to store * the context allocated by the plugin's init function. (But at * least we do call init/fini!) * * What we should do is adapt the old plugin system to the new * one and change how we register plugins so that we use the new * struct plug to keep track of their context structures, that * way we can init once, invoke many times, then fini. */ cpm = (struct common_plugin_method *)p->symbol; s.ret = cpm->init(context, &plug_ctx); if (s.ret) continue; s.ret = s.func(s.context, p->symbol, plug_ctx, s.userctx); cpm->fini(plug_ctx); if (s.ret != KRB5_PLUGIN_NO_HANDLE) break; } _krb5_plugin_free(registered_plugins); /* Invoke loaded plugins (new system) */ if (s.ret != KRB5_PLUGIN_NO_HANDLE) heim_array_iterate_f(s.result, &s, eval_results); heim_release(s.result); heim_release(s.n); return s.ret; }
/** * Load plugins (new system) for the given module @name (typicall * "krb5") from the given directory @paths. * * Inputs: * * @context A krb5_context * @name Name of plugin module (typically "krb5") * @paths Array of directory paths where to look */ void _krb5_load_plugins(krb5_context context, const char *name, const char **paths) { #ifdef HAVE_DLOPEN heim_string_t s = heim_string_create(name); heim_dict_t module; struct dirent *entry; krb5_error_code ret; const char **di; DIR *d; HEIMDAL_MUTEX_lock(&plugin_mutex); if (modules == NULL) { modules = heim_dict_create(11); if (modules == NULL) { HEIMDAL_MUTEX_unlock(&plugin_mutex); return; } } module = heim_dict_get_value(modules, s); if (module == NULL) { module = heim_dict_create(11); if (module == NULL) { HEIMDAL_MUTEX_unlock(&plugin_mutex); heim_release(s); return; } heim_dict_set_value(modules, s, module); heim_release(module); } heim_release(s); for (di = paths; *di != NULL; di++) { d = opendir(*di); if (d == NULL) continue; rk_cloexec_dir(d); while ((entry = readdir(d)) != NULL) { char *n = entry->d_name; char *path = NULL; heim_string_t spath; struct plugin2 *p; /* skip . and .. */ if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0'))) continue; ret = 0; #ifdef __APPLE__ { /* support loading bundles on MacOS */ size_t len = strlen(n); if (len > 7 && strcmp(&n[len - 7], ".bundle") == 0) ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", *di, n, (int)(len - 7), n); } #endif if (ret < 0 || path == NULL) ret = asprintf(&path, "%s/%s", *di, n); if (ret < 0 || path == NULL) continue; spath = heim_string_create(n); if (spath == NULL) { free(path); continue; } /* check if already cached */ p = heim_dict_get_value(module, spath); if (p == NULL) { p = heim_alloc(sizeof(*p), "krb5-plugin", plug_dealloc); if (p) p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY); if (p->dsohandle) { p->path = heim_retain(spath); p->names = heim_dict_create(11); heim_dict_set_value(module, spath, p); } heim_release(p); } heim_release(spath); free(path); } closedir(d); } HEIMDAL_MUTEX_unlock(&plugin_mutex); #endif /* HAVE_DLOPEN */ }
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); }