diff options
Diffstat (limited to 'lib/kadm5/init_c.c')
| -rw-r--r-- | lib/kadm5/init_c.c | 310 |
1 files changed, 211 insertions, 99 deletions
diff --git a/lib/kadm5/init_c.c b/lib/kadm5/init_c.c index 0436fb127cd5..ffbb639581cf 100644 --- a/lib/kadm5/init_c.c +++ b/lib/kadm5/init_c.c @@ -72,11 +72,14 @@ set_funcs(kadm5_client_context *c) SET(c, get_principals); SET(c, get_privs); SET(c, modify_principal); + SET(c, prune_principal); SET(c, randkey_principal); SET(c, rename_principal); SET(c, lock); SET(c, unlock); SETNOTIMP(c, setkey_principal_3); + SET(c, iter_principals); + SET(c, dup_context); } kadm5_ret_t @@ -88,62 +91,159 @@ _kadm5_c_init_context(kadm5_client_context **ctx, char *colon; *ctx = malloc(sizeof(**ctx)); - if(*ctx == NULL) - return ENOMEM; + if (*ctx == NULL) + return krb5_enomem(context); memset(*ctx, 0, sizeof(**ctx)); - krb5_add_et_list (context, initialize_kadm5_error_table_r); + krb5_add_et_list(context, initialize_kadm5_error_table_r); set_funcs(*ctx); (*ctx)->context = context; - if(params->mask & KADM5_CONFIG_REALM) { + if (params->mask & KADM5_CONFIG_REALM) { ret = 0; (*ctx)->realm = strdup(params->realm); if ((*ctx)->realm == NULL) - ret = ENOMEM; + ret = krb5_enomem(context); } else ret = krb5_get_default_realm((*ctx)->context, &(*ctx)->realm); if (ret) { free(*ctx); return ret; } - if(params->mask & KADM5_CONFIG_ADMIN_SERVER) + + /* + * FIXME: If we have a hostlist, we should use the hostlist so that if we + * can't reach one server we try another. + */ + if (params->mask & KADM5_CONFIG_ADMIN_SERVER) (*ctx)->admin_server = strdup(params->admin_server); else { char **hostlist; - ret = krb5_get_krb_admin_hst (context, &(*ctx)->realm, &hostlist); + ret = krb5_get_krb_admin_hst(context, &(*ctx)->realm, &hostlist); if (ret) { free((*ctx)->realm); free(*ctx); return ret; } (*ctx)->admin_server = strdup(*hostlist); - krb5_free_krbhst (context, hostlist); + krb5_free_krbhst(context, hostlist); } if ((*ctx)->admin_server == NULL) { free((*ctx)->realm); free(*ctx); - return ENOMEM; + return krb5_enomem(context); } - colon = strchr ((*ctx)->admin_server, ':'); + colon = strchr((*ctx)->admin_server, ':'); if (colon != NULL) *colon++ = '\0'; (*ctx)->kadmind_port = 0; - if(params->mask & KADM5_CONFIG_KADMIND_PORT) + if (params->mask & KADM5_CONFIG_KADMIND_PORT) (*ctx)->kadmind_port = params->kadmind_port; else if (colon != NULL) { char *end; - (*ctx)->kadmind_port = htons(strtol (colon, &end, 0)); + (*ctx)->kadmind_port = htons(strtol(colon, &end, 0)); } if ((*ctx)->kadmind_port == 0) - (*ctx)->kadmind_port = krb5_getportbyname (context, "kerberos-adm", - "tcp", 749); + (*ctx)->kadmind_port = krb5_getportbyname(context, "kerberos-adm", + "tcp", 749); + + if (params->mask & KADM5_CONFIG_READONLY_ADMIN_SERVER) { + (*ctx)->readonly_admin_server = strdup(params->readonly_admin_server); + if ((*ctx)->readonly_admin_server == NULL) { + free((*ctx)->realm); + free(*ctx); + return krb5_enomem(context); + } + } else { + char **hostlist; + + ret = krb5_get_krb_readonly_admin_hst(context, &(*ctx)->realm, + &hostlist); + if (ret == 0) { + (*ctx)->readonly_admin_server = strdup(*hostlist); + krb5_free_krbhst(context, hostlist); + if ((*ctx)->readonly_admin_server == NULL) { + free((*ctx)->realm); + free(*ctx); + return krb5_enomem(context); + } + } + } + if ((*ctx)->readonly_admin_server) { + colon = strchr((*ctx)->readonly_admin_server, ':'); + if (colon != NULL) + *colon++ = '\0'; + + } else { + colon = NULL; + } + + (*ctx)->readonly_kadmind_port = 0; + if (params->mask & KADM5_CONFIG_READONLY_KADMIN_PORT) + (*ctx)->readonly_kadmind_port = params->readonly_kadmind_port; + else if (colon != NULL) { + char *end; + + (*ctx)->readonly_kadmind_port = htons(strtol(colon, &end, 0)); + } + if ((*ctx)->readonly_kadmind_port == 0) + (*ctx)->readonly_kadmind_port = (*ctx)->kadmind_port; 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) { + ctx->my_context = TRUE; + 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); + } + ctx->sock = -1; + 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, @@ -368,18 +468,26 @@ _kadm5_c_get_cred_cache(krb5_context context, * No client was specified by the caller and we cannot * determine the client from a credentials cache. */ - const char *user; - - user = get_default_username (); + char userbuf[128]; + const char *user = NULL; - if(user == NULL) { +#ifndef WIN32 + if (geteuid() == 0) + user = roken_get_loginname(userbuf, sizeof(userbuf)); +#endif + if (user == NULL) + user = roken_get_username(userbuf, sizeof(userbuf)); + if (user == NULL) { krb5_set_error_message(context, KADM5_FAILURE, "Unable to find local user name"); + krb5_free_principal(context, client); return KADM5_FAILURE; } ret = krb5_make_principal(context, &default_client, NULL, user, "admin", NULL); - if(ret) + if (ret) { + krb5_free_principal(context, client); return ret; + } } } @@ -422,63 +530,78 @@ static kadm5_ret_t kadm_connect(kadm5_client_context *ctx) { kadm5_ret_t ret; - krb5_principal server; - krb5_ccache cc; + krb5_principal server = NULL; + krb5_ccache cc = NULL; rk_socket_t s = rk_INVALID_SOCKET; struct addrinfo *ai, *a; struct addrinfo hints; + int free_ai = 0; int error; + int kadmin_port = 0; + const char *admin_server = NULL; char portstr[NI_MAXSERV]; - char *hostname, *slash; - char *service_name; + const char *hostname, *slash; + char *service_name = NULL; krb5_context context = ctx->context; + int writable = 0; - memset (&hints, 0, sizeof(hints)); + if (ctx->ac) + krb5_auth_con_free(context, ctx->ac); + ctx->ac = NULL; + + if (!ctx->want_write) { + admin_server = ctx->readonly_admin_server; + kadmin_port = ctx->readonly_kadmind_port; + } + if (admin_server == NULL) { + admin_server = ctx->admin_server; + writable = 1; + } + if (kadmin_port < 1) + kadmin_port = ctx->kadmind_port; + + memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; - snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port)); + snprintf(portstr, sizeof(portstr), "%u", ntohs(kadmin_port)); - hostname = ctx->admin_server; - slash = strchr (hostname, '/'); + hostname = admin_server; + slash = strchr(hostname, '/'); if (slash != NULL) hostname = slash + 1; - error = getaddrinfo (hostname, portstr, &hints, &ai); + error = getaddrinfo(hostname, portstr, &hints, &ai); if (error) { - krb5_clear_error_message(context); - return KADM5_BAD_SERVER_NAME; + ret = KADM5_BAD_SERVER_NAME; + goto out; } + free_ai = 1; for (a = ai; a != NULL; a = a->ai_next) { - s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); + s = socket(a->ai_family, a->ai_socktype, a->ai_protocol); if (s < 0) continue; - if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { - krb5_clear_error_message(context); - krb5_warn (context, errno, "connect(%s)", hostname); - rk_closesocket (s); + if (connect(s, a->ai_addr, a->ai_addrlen) < 0) { + krb5_warn(context, errno, "connect(%s)", hostname); + rk_closesocket(s); + s = rk_INVALID_SOCKET; continue; } break; } if (a == NULL) { - freeaddrinfo (ai); - krb5_clear_error_message(context); - krb5_warnx (context, "failed to contact %s", hostname); - return KADM5_FAILURE; + krb5_set_error_message(context, ret = KADM5_FAILURE, + "failed to contact %s", hostname); + goto out; } ret = _kadm5_c_get_cred_cache(context, ctx->client_name, ctx->service_name, NULL, ctx->prompter, ctx->keytab, ctx->ccache, &cc); - - if(ret) { - freeaddrinfo (ai); - rk_closesocket(s); - return ret; - } + if (ret) + goto out; if (ctx->realm) error = asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, @@ -487,28 +610,19 @@ kadm_connect(kadm5_client_context *ctx) error = asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE); if (error == -1 || service_name == NULL) { - freeaddrinfo (ai); - rk_closesocket(s); - krb5_clear_error_message(context); - return ENOMEM; + ret = krb5_enomem(context); + goto out; } ret = krb5_parse_name(context, service_name, &server); - free(service_name); - if(ret) { - freeaddrinfo (ai); - if(ctx->ccache == NULL) - krb5_cc_close(context, cc); - rk_closesocket(s); - return ret; - } - ctx->ac = NULL; + if (ret) + goto out; ret = krb5_sendauth(context, &ctx->ac, &s, KADMIN_APPL_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, cc, NULL, NULL, NULL); - if(ret == 0) { + if (ret == 0) { krb5_data params; kadm5_config_params p; memset(&p, 0, sizeof(p)); @@ -517,55 +631,53 @@ kadm_connect(kadm5_client_context *ctx) p.realm = ctx->realm; } ret = _kadm5_marshal_params(context, &p, ¶ms); - - ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms); - krb5_data_free(¶ms); - if(ret) { - freeaddrinfo (ai); - rk_closesocket(s); - if(ctx->ccache == NULL) - krb5_cc_close(context, cc); - return ret; - } - } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) { - rk_closesocket(s); - - s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); - if (s < 0) { - freeaddrinfo (ai); - krb5_clear_error_message(context); - return errno; - } - if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { - rk_closesocket (s); - freeaddrinfo (ai); - krb5_clear_error_message(context); - return errno; - } - ret = krb5_sendauth(context, &ctx->ac, &s, - KADMIN_OLD_APPL_VERSION, NULL, - server, AP_OPTS_MUTUAL_REQUIRED, - NULL, NULL, cc, NULL, NULL, NULL); + if (ret == 0) { + ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms); + krb5_data_free(¶ms); + } } - freeaddrinfo (ai); - if(ret) { - rk_closesocket(s); - return ret; + + if (ret == 0) { + ctx->sock = s; + ctx->connected_to_writable = !!writable; } +out: + free(service_name); + krb5_cc_close(context, cc); krb5_free_principal(context, server); - if(ctx->ccache == NULL) - krb5_cc_close(context, cc); - ctx->sock = s; - - return 0; + if (free_ai) + freeaddrinfo(ai); + if (ret) { + if (s != rk_INVALID_SOCKET) + rk_closesocket(s); + krb5_auth_con_free(context, ctx->ac); + ctx->ac = NULL; + } + return ret; } kadm5_ret_t -_kadm5_connect(void *handle) +_kadm5_connect(void *handle, int want_write) { kadm5_client_context *ctx = handle; - if(ctx->sock == -1) + + /* + * Reconnect? Note that we don't reconnect to read-only kadmin servers if + * we're already connected to a writable kadmin server because we sometimes + * get a principal record after writing it. We really need the application + * upstairs to tell us when to stop hogging writable kadmin servers. + * + * FIXME: Add an API for marking a kadm5_client_context as not needing to + * connect to writable kadmin servers. + */ + ctx->want_write = !!want_write; + if (ctx->sock != rk_INVALID_SOCKET && want_write && + !ctx->connected_to_writable) { + rk_closesocket(ctx->sock); + ctx->sock = rk_INVALID_SOCKET; + } + if (ctx->sock == rk_INVALID_SOCKET) return kadm_connect(ctx); return 0; } |
