diff --git a/lib/kadm5/ad.c b/lib/kadm5/ad.c index 58ccf32ea..d606cd89f 100644 --- a/lib/kadm5/ad.c +++ b/lib/kadm5/ad.c @@ -1065,6 +1065,33 @@ kadm5_ad_get_principals(void *server_handle, #endif } +static kadm5_ret_t +kadm5_ad_iter_principals(void *server_handle, + const char *expression, + int (*cb)(void *, const char *), + void *cbdata) +{ + kadm5_ad_context *context = server_handle; + +#ifdef OPENLDAP + kadm5_ret_t ret; + + ret = ad_get_cred(context, NULL); + if (ret) + return ret; + + ret = _kadm5_ad_connect(server_handle); + if (ret) + return ret; + + krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); + return KADM5_RPC_ERROR; +#else + krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); + return KADM5_RPC_ERROR; +#endif +} + static kadm5_ret_t kadm5_ad_get_privs(void *server_handle, uint32_t*privs) { @@ -1388,6 +1415,9 @@ set_funcs(kadm5_ad_context *c) SET(c, lock); SET(c, unlock); SETNOTIMP(c, setkey_principal_3); + SETNOTIMP(c, prune_principal); + SET(c, iter_principals); + SET(c, dup_context); } kadm5_ret_t @@ -1455,6 +1485,12 @@ kadm5_ad_init_with_password_ctx(krb5_context context, return 0; } +kadm5_ret_t +kadm5_ad_dup_context(void *in, void **out) +{ + return ENOTSUP; +} + kadm5_ret_t kadm5_ad_init_with_password(const char *client_name, const char *password, diff --git a/lib/kadm5/common_glue.c b/lib/kadm5/common_glue.c index 9993b0a34..210bf93b9 100644 --- a/lib/kadm5/common_glue.c +++ b/lib/kadm5/common_glue.c @@ -38,6 +38,12 @@ RCSID("$Id$"); #define __CALL(F, P) (*((kadm5_common_context*)server_handle)->funcs.F)P #define __CALLABLE(F) (((kadm5_common_context*)server_handle)->funcs.F != 0) +kadm5_ret_t +kadm5_dup_context(void *server_handle, void **dup_server_handle) +{ + return __CALL(dup_context, (server_handle, dup_server_handle)); +} + kadm5_ret_t kadm5_chpass_principal(void *server_handle, krb5_principal princ, @@ -222,6 +228,15 @@ kadm5_get_principals(void *server_handle, return __CALL(get_principals, (server_handle, expression, princs, count)); } +kadm5_ret_t +kadm5_iter_principals(void *server_handle, + const char *expression, + int (*cb)(void *, const char *), + void *cbdata) +{ + return __CALL(iter_principals, (server_handle, expression, cb, cbdata)); +} + kadm5_ret_t kadm5_get_privs(void *server_handle, uint32_t *privs) diff --git a/lib/kadm5/context_s.c b/lib/kadm5/context_s.c index 0c154ecfe..5c9b3e31c 100644 --- a/lib/kadm5/context_s.c +++ b/lib/kadm5/context_s.c @@ -112,6 +112,8 @@ set_funcs(kadm5_server_context *c) SET(c, lock); SET(c, unlock); SET(c, setkey_principal_3); + SET(c, iter_principals); + SET(c, dup_context); } #ifndef NO_UNIX_SOCKETS @@ -250,6 +252,8 @@ _kadm5_s_init_context(kadm5_server_context **ctx, krb5_add_et_list (context, initialize_kadm5_error_table_r); #define is_set(M) (params && params->mask & KADM5_CONFIG_ ## M) + if (params) + (*ctx)->config.mask = params->mask; if (is_set(REALM)) { (*ctx)->config.realm = strdup(params->realm); if ((*ctx)->config.realm == NULL) @@ -275,9 +279,9 @@ _kadm5_s_init_context(kadm5_server_context **ctx, return krb5_enomem(context); } - find_db_spec(*ctx); - - ret = _kadm5_s_init_hooks(*ctx); + ret = find_db_spec(*ctx); + if (ret == 0) + ret = _kadm5_s_init_hooks(*ctx); if (ret != 0) { kadm5_s_destroy(*ctx); *ctx = NULL; diff --git a/lib/kadm5/destroy_s.c b/lib/kadm5/destroy_s.c index fc86e5960..0558b45c5 100644 --- a/lib/kadm5/destroy_s.c +++ b/lib/kadm5/destroy_s.c @@ -42,10 +42,12 @@ RCSID("$Id$"); static void destroy_config (kadm5_config_params *c) { - free (c->realm); - free (c->dbname); - free (c->acl_file); - free (c->stash_file); + if (!c) + return; + free(c->realm); + free(c->dbname); + free(c->acl_file); + free(c->stash_file); } /* @@ -55,6 +57,8 @@ destroy_config (kadm5_config_params *c) static void destroy_kadm5_log_context (kadm5_log_context *c) { + if (!c) + return; free(c->log_file); if (c->socket_fd != rk_INVALID_SOCKET) rk_closesocket(c->socket_fd); diff --git a/lib/kadm5/get_princs_c.c b/lib/kadm5/get_princs_c.c index 4998223d2..82994a8e3 100644 --- a/lib/kadm5/get_princs_c.c +++ b/lib/kadm5/get_princs_c.c @@ -66,7 +66,7 @@ kadm5_c_get_principals(void *server_handle, ret = krb5_store_int32(sp, kadm_get_princs); if (ret) goto out; - ret = krb5_store_int32(sp, expression != NULL); + ret = krb5_store_int32(sp, expression != NULL ? 1 : 0); if (ret) goto out; if (expression) { @@ -117,3 +117,179 @@ kadm5_c_get_principals(void *server_handle, krb5_data_free(&reply); return ret; } + +kadm5_ret_t +kadm5_c_iter_principals(void *server_handle, + const char *expression, + int (*cb)(void *, const char *), + void *cbdata) +{ + kadm5_client_context *context = server_handle; + kadm5_ret_t ret; + krb5_storage *sp; + unsigned char buf[1024]; + int32_t tmp; + krb5_data reply; + size_t i; + + ret = _kadm5_connect(server_handle, 0 /* want_write */); + if (ret) + return ret; + + krb5_data_zero(&reply); + + sp = krb5_storage_from_mem(buf, sizeof(buf)); + if (sp == NULL) { + ret = krb5_enomem(context->context); + goto out_keep_error; + } + ret = krb5_store_int32(sp, kadm_get_princs); + if (ret) + goto out; + + /* + * Our protocol has an int boolean for this operation to indicate whether + * there's an expression. What we'll do here is that instead of sending + * just false or trueish, for online iteration we'll send a number other + * than 0 or 1 -- a magic value > 0 and < INT_MAX. + * + * In response we'll expect multiple replies, each with up to some small + * number of principal names. See kadmin/server.c. + */ + ret = krb5_store_int32(sp, 0x55555555); + if (ret) + goto out; + ret = krb5_store_string(sp, expression ? expression : ""); + if (ret) + goto out; + ret = _kadm5_client_send(context, sp); + if (ret) + goto out_keep_error; + ret = _kadm5_client_recv(context, &reply); + if (ret) + goto out_keep_error; + krb5_storage_free(sp); + sp = krb5_storage_from_data (&reply); + if (sp == NULL) { + ret = krb5_enomem(context->context); + goto out_keep_error; + } + ret = krb5_ret_int32(sp, &tmp); + if (ret == 0) + ret = tmp; + + if (ret) + goto out; + + ret = krb5_ret_int32(sp, &tmp); + if (ret) + goto out; + + if (tmp < 0) { + size_t n = -tmp; + int more = 1; + int stop = 0; + + /* The server supports online iteration, hooray! */ + + while (more) { + /* + * We expect any number of chunks, each having `n' names, except + * the last one would have fewer than `n' (possibly zero, even). + * + * After that we expect one more reply with just a final return + * code. + */ + krb5_data_free(&reply); + krb5_storage_free(sp); + sp = NULL; + ret = _kadm5_client_recv(context, &reply); + if (ret == 0 && (sp = krb5_storage_from_data(&reply)) == NULL) + ret = krb5_enomem(context->context); + if (ret) + goto out; + + /* Every chunk begins with a status code */ + ret = krb5_ret_int32(sp, &tmp); + if (ret == 0) + ret = tmp; + if (ret) + goto out; + + /* We expect up to -tmp principals per reply */ + for (i = 0; i < n; i++) { + char *princ = NULL; + + ret = krb5_ret_string(sp, &princ); + if (ret == HEIM_ERR_EOF) { + /* This was the last reply */ + more = 0; + ret = 0; + break; + } + if (ret) + goto out; + if (!stop) { + stop = cb(cbdata, princ); + if (stop) { + /* Tell the server to stop */ + krb5_storage_free(sp); + if ((sp = krb5_storage_emem()) && + krb5_store_int32(sp, kadm_nop) == 0) + (void) _kadm5_client_send(context, sp); + } + } + free(princ); + } + + if (!more) { + if (ret == 0) + ret = stop; + break; + } + } + /* Get the final result code */ + krb5_data_free(&reply); + krb5_storage_free(sp); + sp = NULL; + ret = _kadm5_client_recv(context, &reply); + if (ret == 0 && (sp = krb5_storage_from_data(&reply)) == NULL) + ret = krb5_enomem(context->context); + if (ret) + goto out; + ret = krb5_ret_int32(sp, &tmp); + if (ret == 0) + ret = tmp; + if (!stop) { + /* + * Send our "interrupt" after the last chunk if we hand't + * interrupted already. + */ + krb5_storage_free(sp); + if ((sp = krb5_storage_emem()) && + krb5_store_int32(sp, kadm_nop) == 0) + (void) _kadm5_client_send(context, sp); + } + } else { + size_t n = tmp; + + /* Old server -- listing not online */ + for (i = 0; i < n; i++) { + char *princ = NULL; + + ret = krb5_ret_string(sp, &princ); + if (ret) + goto out; + cb(cbdata, princ); + free(princ); + } + } + + out: + krb5_clear_error_message(context->context); + + out_keep_error: + krb5_storage_free(sp); + krb5_data_free(&reply); + return ret; +} diff --git a/lib/kadm5/get_princs_s.c b/lib/kadm5/get_princs_s.c index 9fd64f200..14d3907d7 100644 --- a/lib/kadm5/get_princs_s.c +++ b/lib/kadm5/get_princs_s.c @@ -94,7 +94,7 @@ kadm5_s_get_principals(void *server_handle, { struct foreach_data d; kadm5_server_context *context = server_handle; - kadm5_ret_t ret; + kadm5_ret_t ret = 0; if (!context->keep_open) { ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0); @@ -104,24 +104,25 @@ kadm5_s_get_principals(void *server_handle, } } d.exp = expression; - { + d.exp2 = NULL; + if (expression) { krb5_realm r; int aret; ret = krb5_get_default_realm(context->context, &r); - if (ret) - goto out; - aret = asprintf(&d.exp2, "%s@%s", expression, r); - free(r); - if (aret == -1 || d.exp2 == NULL) { - ret = krb5_enomem(context->context); - goto out; - } + if (ret == 0) { + aret = asprintf(&d.exp2, "%s@%s", expression, r); + free(r); + if (aret == -1 || d.exp2 == NULL) + ret = krb5_enomem(context->context); + } } d.princs = NULL; d.nalloced = 0; d.count = 0; - ret = hdb_foreach(context->context, context->db, HDB_F_ADMIN_DATA, foreach, &d); + if (ret == 0) + ret = hdb_foreach(context->context, context->db, HDB_F_ADMIN_DATA, + foreach, &d); if (ret == 0) ret = add_princ(context->context, &d, NULL); @@ -134,7 +135,72 @@ kadm5_s_get_principals(void *server_handle, else kadm5_free_name_list(context, d.princs, count); free(d.exp2); - out: + if (!context->keep_open) + context->db->hdb_close(context->context, context->db); + return _kadm5_error_code(ret); +} + +struct foreach_online_data { + const char *exp; + char *exp2; + int (*cb)(void *, const char *); + void *cbdata; +}; + +static krb5_error_code +foreach_online(krb5_context context, HDB *db, hdb_entry *ent, void *data) +{ + struct foreach_online_data *d = data; + krb5_error_code ret; + char *princ = NULL; + + ret = krb5_unparse_name(context, ent->principal, &princ); + if (ret == 0) { + if (!d->exp || + fnmatch(d->exp, princ, 0) == 0 || fnmatch(d->exp2, princ, 0) == 0) + ret = d->cb(d->cbdata, princ); + free(princ); + } + return ret; +} + +kadm5_ret_t +kadm5_s_iter_principals(void *server_handle, + const char *expression, + int (*cb)(void *, const char *), + void *cbdata) +{ + struct foreach_online_data d; + kadm5_server_context *context = server_handle; + kadm5_ret_t ret = 0; + + if (!context->keep_open) { + ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0); + if (ret) { + krb5_warn(context->context, ret, "opening database"); + return ret; + } + } + d.exp = expression; + d.exp2 = NULL; + d.cb = cb; + d.cbdata = cbdata; + if (expression) { + krb5_realm r; + int aret; + + ret = krb5_get_default_realm(context->context, &r); + if (ret == 0) { + aret = asprintf(&d.exp2, "%s@%s", expression, r); + free(r); + if (aret == -1 || d.exp2 == NULL) + ret = krb5_enomem(context->context); + } + } + if (ret == 0) + ret = hdb_foreach(context->context, context->db, HDB_F_ADMIN_DATA, + foreach_online, &d); + free(d.exp2); if (!context->keep_open) context->db->hdb_close(context->context, context->db); return _kadm5_error_code(ret); diff --git a/lib/kadm5/init_c.c b/lib/kadm5/init_c.c index 5d585d1a2..841e39dc1 100644 --- a/lib/kadm5/init_c.c +++ b/lib/kadm5/init_c.c @@ -78,6 +78,8 @@ set_funcs(kadm5_client_context *c) SET(c, lock); SET(c, unlock); SETNOTIMP(c, setkey_principal_3); + SET(c, iter_principals); + SET(c, dup_context); } kadm5_ret_t @@ -192,6 +194,53 @@ _kadm5_c_init_context(kadm5_client_context **ctx, return 0; } +kadm5_ret_t +kadm5_c_dup_context(void *vin, void **out) +{ + krb5_error_code ret; + kadm5_client_context *in = vin; + krb5_context context = in->context; + kadm5_client_context *ctx; + + *out = NULL; + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) + return krb5_enomem(in->context); + + + memset(ctx, 0, sizeof(*ctx)); + set_funcs(ctx); + ctx->readonly_kadmind_port = in->readonly_kadmind_port; + ctx->kadmind_port = in->kadmind_port; + + ret = krb5_copy_context(context, &(ctx->context)); + if (ret == 0) + ret = krb5_add_et_list(ctx->context, initialize_kadm5_error_table_r); + if (ret == 0 && (ctx->realm = strdup(in->realm)) == NULL) + ret = krb5_enomem(context); + if (ret == 0 && + (ctx->admin_server = strdup(in->admin_server)) == NULL) + ret = krb5_enomem(context); + if (in->readonly_admin_server && + (ctx->readonly_admin_server = strdup(in->readonly_admin_server)) == NULL) + ret = krb5_enomem(context); + if (in->keytab && (ctx->keytab = strdup(in->keytab)) == NULL) + ret = krb5_enomem(context); + if (in->ccache) { + char *fullname = NULL; + + ret = krb5_cc_get_full_name(context, in->ccache, &fullname); + if (ret == 0) + ret = krb5_cc_resolve(context, fullname, &ctx->ccache); + free(fullname); + } + if (ret == 0) + *out = ctx; + else + kadm5_c_destroy(ctx); + return ret; +} + static krb5_error_code get_kadm_ticket(krb5_context context, krb5_ccache id, diff --git a/lib/kadm5/init_s.c b/lib/kadm5/init_s.c index 1b1d7f2ff..35402d88a 100644 --- a/lib/kadm5/init_s.c +++ b/lib/kadm5/init_s.c @@ -108,6 +108,21 @@ kadm5_s_init_with_context(krb5_context context, return ret; } +kadm5_ret_t +kadm5_s_dup_context(void *vin, void **out) +{ + kadm5_server_context *in = vin; + kadm5_ret_t ret; + char *p = NULL; + + ret = krb5_unparse_name(in->context, in->caller, &p); + if (ret == 0) + ret = kadm5_s_init_with_context(in->context, p, NULL, + &in->config, 0, 0, out); + free(p); + return ret; +} + kadm5_ret_t kadm5_s_init_with_password_ctx(krb5_context context, const char *client_name, diff --git a/lib/kadm5/libkadm5srv-exports.def b/lib/kadm5/libkadm5srv-exports.def index 72ba4174c..56366ae20 100644 --- a/lib/kadm5/libkadm5srv-exports.def +++ b/lib/kadm5/libkadm5srv-exports.def @@ -15,6 +15,7 @@ EXPORTS kadm5_delete_policy kadm5_delete_principal kadm5_destroy + kadm5_dup_context kadm5_flush kadm5_free_key_data kadm5_free_name_list @@ -32,6 +33,7 @@ EXPORTS kadm5_init_with_password_ctx kadm5_init_with_skey kadm5_init_with_skey_ctx + kadm5_iter_principals kadm5_lock kadm5_modify_policy kadm5_modify_principal diff --git a/lib/kadm5/private.h b/lib/kadm5/private.h index 88c2f2823..1cb8e3918 100644 --- a/lib/kadm5/private.h +++ b/lib/kadm5/private.h @@ -68,6 +68,8 @@ struct kadm_func { int, krb5_key_salt_tuple *, krb5_keyblock *, int); kadm5_ret_t (*prune_principal) (void *, krb5_principal, int); + kadm5_ret_t (*iter_principals) (void*, const char*, int (*)(void *, const char *), void *); + kadm5_ret_t (*dup_context) (void*, void **); }; typedef struct kadm5_hook_context { diff --git a/lib/kadm5/version-script-client.map b/lib/kadm5/version-script-client.map index 4d89fabf3..baae30b11 100644 --- a/lib/kadm5/version-script-client.map +++ b/lib/kadm5/version-script-client.map @@ -11,6 +11,7 @@ HEIMDAL_KADM5_CLIENT_1.0 { kadm5_c_create_principal; kadm5_c_delete_principal; kadm5_c_destroy; + kadm5_c_dup_context; kadm5_c_flush; kadm5_c_get_principal; kadm5_c_get_principals; @@ -21,6 +22,8 @@ HEIMDAL_KADM5_CLIENT_1.0 { kadm5_c_init_with_password_ctx; kadm5_c_init_with_skey; kadm5_c_init_with_skey_ctx; + kadm5_c_iter_principals; + kadm5_c_get_privs; kadm5_c_modify_principal; kadm5_c_prune_principal; kadm5_c_randkey_principal; @@ -30,6 +33,7 @@ HEIMDAL_KADM5_CLIENT_1.0 { kadm5_create_principal; kadm5_delete_principal; kadm5_destroy; + kadm5_dup_context; kadm5_flush; kadm5_free_key_data; kadm5_free_name_list; @@ -43,6 +47,7 @@ HEIMDAL_KADM5_CLIENT_1.0 { kadm5_init_with_password_ctx; kadm5_init_with_skey; kadm5_init_with_skey_ctx; + kadm5_iter_principals; kadm5_modify_principal; kadm5_randkey_principal; kadm5_randkey_principal_3; diff --git a/lib/kadm5/version-script.map b/lib/kadm5/version-script.map index e4b4100f3..6de88fc22 100644 --- a/lib/kadm5/version-script.map +++ b/lib/kadm5/version-script.map @@ -16,6 +16,7 @@ HEIMDAL_KAMD5_SERVER_1.0 { kadm5_create_principal_3; kadm5_delete_principal; kadm5_destroy; + kadm5_dup_context; kadm5_decrypt_key; kadm5_delete_policy; kadm5_flush; @@ -35,6 +36,7 @@ HEIMDAL_KAMD5_SERVER_1.0 { kadm5_init_with_password_ctx; kadm5_init_with_skey; kadm5_init_with_skey_ctx; + kadm5_iter_principals; kadm5_lock; kadm5_modify_principal; kadm5_modify_policy;