#
# This patch provides support in kadmin and kpropd to work in a
# multi-master environment.
#
# Note that we have discussed support for this with MIT in the past but
# they have been reticent to add such support. It is possible that
# support for this may be introduced at a later time at which point we
# should look at modifying/deleting this patch.
# Patch source: in-house
#
@@ -255,7 +255,7 @@ kadmin_startup(int argc, char *argv[], char **request_out, char ***args_out)
char **db_args = NULL;
int db_args_size = 0;
char *db_name = NULL;
- char *svcname, *realm;
+ char **svcnames = NULL, *realm;
memset(¶ms, 0, sizeof(params));
@@ -370,11 +370,6 @@ kadmin_startup(int argc, char *argv[], char **request_out, char ***args_out)
params.mask |= KADM5_CONFIG_REALM;
params.realm = def_realm;
- if (params.mask & KADM5_CONFIG_OLD_AUTH_GSSAPI)
- svcname = KADM5_ADMIN_SERVICE;
- else
- svcname = NULL;
-
/*
* Set cc to an open credentials cache, either specified by the -c
* argument or the default.
@@ -503,13 +498,14 @@ kadmin_startup(int argc, char *argv[], char **request_out, char ***args_out)
if (ccache_name) {
info(_("Authenticating as principal %s with existing "
"credentials.\n"), princstr);
- retval = kadm5_init_with_creds(context, princstr, cc, svcname, ¶ms,
+ retval = kadm5_init_with_creds_mm(context, princstr, cc, svcnames,
+ ¶ms,
KADM5_STRUCT_VERSION,
KADM5_API_VERSION_4, db_args, &handle);
} else if (use_anonymous) {
info(_("Authenticating as principal %s with password; "
"anonymous requested.\n"), princstr);
- retval = kadm5_init_anonymous(context, princstr, svcname, ¶ms,
+ retval = kadm5_init_anonymous_mm(context, princstr, svcnames, ¶ms,
KADM5_STRUCT_VERSION,
KADM5_API_VERSION_4, db_args, &handle);
} else if (use_keytab) {
@@ -520,17 +516,20 @@ kadmin_startup(int argc, char *argv[], char **request_out, char ***args_out)
info(_("Authenticating as principal %s with default keytab.\n"),
princstr);
}
- retval = kadm5_init_with_skey(context, princstr, keytab_name, svcname,
+ retval = kadm5_init_with_skey_mm(context, princstr, keytab_name,
+ svcnames,
¶ms, KADM5_STRUCT_VERSION,
KADM5_API_VERSION_4, db_args, &handle);
} else {
info(_("Authenticating as principal %s with password.\n"),
princstr);
- retval = kadm5_init_with_password(context, princstr, password, svcname,
+ retval = kadm5_init_with_password_mm(context, princstr, password,
+ svcnames,
¶ms, KADM5_STRUCT_VERSION,
KADM5_API_VERSION_4, db_args,
&handle);
}
+ free_srv_names(svcnames);
if (retval) {
com_err(whoami, retval, _("while initializing %s interface"), whoami);
if (retval == KADM5_BAD_CLIENT_PARAMS ||
@@ -345,6 +345,51 @@ kadm5_ret_t kadm5_init_with_creds(krb5_context context,
krb5_ui_4 api_version,
char **db_args,
void **server_handle);
+kadm5_ret_t kadm5_init_with_creds_mm(krb5_context context,
+ char *client_name,
+ krb5_ccache ccache,
+ char **svcnames,
+ kadm5_config_params *params,
+ krb5_ui_4 struct_version,
+ krb5_ui_4 api_version,
+ char **db_args,
+ void **server_handle);
+kadm5_ret_t kadm5_init_with_password_mm(krb5_context context,
+ char *client_name,
+ char *pass,
+ char **svcnames,
+ kadm5_config_params *params,
+ krb5_ui_4 struct_version,
+ krb5_ui_4 api_version,
+ char **db_args,
+ void **server_handle);
+kadm5_ret_t kadm5_init_anonymous_mm(krb5_context context,
+ char *client_name,
+ char **svcnames,
+ kadm5_config_params *params,
+ krb5_ui_4 struct_version,
+ krb5_ui_4 api_version,
+ char **db_args,
+ void **server_handle);
+kadm5_ret_t kadm5_init_mm(krb5_context context,
+ char *client_name,
+ char *pass,
+ char **svcnames,
+ kadm5_config_params *params,
+ krb5_ui_4 struct_version,
+ krb5_ui_4 api_version,
+ char **db_args,
+ void **server_handle);
+kadm5_ret_t kadm5_init_with_skey_mm(krb5_context context,
+ char *client_name,
+ char *keytab,
+ char **svcnames,
+ kadm5_config_params *params,
+ krb5_ui_4 struct_version,
+ krb5_ui_4 api_version,
+ char **db_args,
+ void **server_handle);
+
kadm5_ret_t kadm5_lock(void *server_handle);
kadm5_ret_t kadm5_unlock(void *server_handle);
kadm5_ret_t kadm5_flush(void *server_handle);
@@ -55,7 +55,7 @@ enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS, INIT_ANONYMOUS };
static kadm5_ret_t
init_any(krb5_context context, char *client_name, enum init_type init_type,
- char *pass, krb5_ccache ccache_in, char *service_name,
+ char *pass, krb5_ccache ccache_in, char **service_names,
kadm5_config_params *params, krb5_ui_4 struct_version,
krb5_ui_4 api_version, char **db_args, void **server_handle);
@@ -87,8 +87,25 @@ kadm5_init_with_creds(krb5_context context, char *client_name,
krb5_ui_4 api_version, char **db_args,
void **server_handle)
{
+ char *svcnames[2];
+
+ svcnames[0] = service_name;
+ svcnames[1] = NULL;
+
return init_any(context, client_name, INIT_CREDS, NULL, ccache,
- service_name, params, struct_version, api_version, db_args,
+ svcnames, params, struct_version, api_version, db_args,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_creds_mm(krb5_context context, char *client_name,
+ krb5_ccache ccache, char **svcnames,
+ kadm5_config_params *params, krb5_ui_4 struct_version,
+ krb5_ui_4 api_version, char **db_args,
+ void **server_handle)
+{
+ return init_any(context, client_name, INIT_CREDS, NULL, ccache,
+ svcnames, params, struct_version, api_version, db_args,
server_handle);
}
@@ -99,7 +116,24 @@ kadm5_init_with_password(krb5_context context, char *client_name,
krb5_ui_4 api_version, char **db_args,
void **server_handle)
{
- return init_any(context, client_name, INIT_PASS, pass, NULL, service_name,
+ char *svcnames[2];
+
+ svcnames[0] = service_name;
+ svcnames[1] = NULL;
+
+ return init_any(context, client_name, INIT_PASS, pass, NULL, svcnames,
+ params, struct_version, api_version, db_args,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_password_mm(krb5_context context, char *client_name,
+ char *pass, char **svcnames,
+ kadm5_config_params *params, krb5_ui_4 struct_version,
+ krb5_ui_4 api_version, char **db_args,
+ void **server_handle)
+{
+ return init_any(context, client_name, INIT_PASS, pass, NULL, svcnames,
params, struct_version, api_version, db_args,
server_handle);
}
@@ -110,8 +144,24 @@ kadm5_init_anonymous(krb5_context context, char *client_name,
krb5_ui_4 struct_version, krb5_ui_4 api_version,
char **db_args, void **server_handle)
{
+ char *svcnames[2];
+
+ svcnames[0] = service_name;
+ svcnames[1] = NULL;
+
return init_any(context, client_name, INIT_ANONYMOUS, NULL, NULL,
- service_name, params, struct_version, api_version,
+ svcnames, params, struct_version, api_version,
+ db_args, server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_anonymous_mm(krb5_context context, char *client_name,
+ char **svcnames, kadm5_config_params *params,
+ krb5_ui_4 struct_version, krb5_ui_4 api_version,
+ char **db_args, void **server_handle)
+{
+ return init_any(context, client_name, INIT_ANONYMOUS, NULL, NULL,
+ svcnames, params, struct_version, api_version,
db_args, server_handle);
}
@@ -121,7 +171,23 @@ kadm5_init(krb5_context context, char *client_name, char *pass,
krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args,
void **server_handle)
{
- return init_any(context, client_name, INIT_PASS, pass, NULL, service_name,
+ char *svcnames[2];
+
+ svcnames[0] = service_name;
+ svcnames[1] = NULL;
+
+ return init_any(context, client_name, INIT_PASS, pass, NULL, svcnames,
+ params, struct_version, api_version, db_args,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_mm(krb5_context context, char *client_name, char *pass,
+ char **svcnames, kadm5_config_params *params,
+ krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args,
+ void **server_handle)
+{
+ return init_any(context, client_name, INIT_PASS, pass, NULL, svcnames,
params, struct_version, api_version, db_args,
server_handle);
}
@@ -133,8 +199,25 @@ kadm5_init_with_skey(krb5_context context, char *client_name,
krb5_ui_4 api_version, char **db_args,
void **server_handle)
{
+ char *svcnames[2];
+
+ svcnames[0] = service_name;
+ svcnames[1] = NULL;
+
+ return init_any(context, client_name, INIT_SKEY, keytab, NULL,
+ svcnames, params, struct_version, api_version, db_args,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_skey_mm(krb5_context context, char *client_name,
+ char *keytab, char **svcnames,
+ kadm5_config_params *params, krb5_ui_4 struct_version,
+ krb5_ui_4 api_version, char **db_args,
+ void **server_handle)
+{
return init_any(context, client_name, INIT_SKEY, keytab, NULL,
- service_name, params, struct_version, api_version, db_args,
+ svcnames, params, struct_version, api_version, db_args,
server_handle);
}
@@ -339,7 +422,7 @@ _kadm5_initialize_rpcsec_gss_handle(kadm5_server_handle_t handle,
}
/*
- * iprop fallback logic:
+ * iprop fallback logic:
* - if iprop_port is configured, connect to iprop_port
* - if not, query remote rpc/bind
* - if that fails, try consuming iprop service on kadmin port
@@ -512,9 +595,35 @@ cleanup:
return (code);
}
+/* utility function used below */
+static void
+clean_up(kadm5_server_handle_t handle,
+ krb5_principal *server,
+ krb5_ccache *ccache)
+{
+ if (handle->destroy_cache && handle->cache_name) {
+ if (krb5_cc_resolve(handle->context,
+ handle->cache_name, ccache) == 0)
+ (void) krb5_cc_destroy (handle->context, *ccache);
+ }
+
+ free(handle->cache_name);
+ handle->cache_name = NULL;
+
+ if (handle->clnt && handle->clnt->cl_auth)
+ AUTH_DESTROY(handle->clnt->cl_auth);
+ if (handle->clnt) {
+ clnt_destroy(handle->clnt);
+ handle->clnt = NULL;
+ }
+
+ krb5_free_principal(handle->context, *server);
+ *server = NULL;
+}
+
static kadm5_ret_t
init_any(krb5_context context, char *client_name, enum init_type init_type,
- char *pass, krb5_ccache ccache_in, char *svcname,
+ char *pass, krb5_ccache ccache_in, char **svcnames_in,
kadm5_config_params *params_in, krb5_ui_4 struct_version,
krb5_ui_4 api_version, char **db_args, void **server_handle)
{
@@ -532,6 +641,10 @@ init_any(krb5_context context, char *client_name, enum init_type init_type,
int code = 0;
generic_ret *r;
+ char **kadmin_srv_names = NULL;
+ char *tmp_srv_names[2] = {NULL, NULL};
+ char **svcname_ptr;
+ int i;
initialize_ovk_error_table();
/* initialize_adb_error_table(); */
@@ -599,34 +712,56 @@ init_any(krb5_context context, char *client_name, enum init_type init_type,
if (code)
goto error;
- /* NULL svcname means use host-based. */
- if (svcname == NULL) {
- char **kadmin_srv_names;
+ if (svcnames_in == NULL || svcnames_in[0] == NULL) {
+ if (params_in && params_in->mask & KADM5_CONFIG_ADMIN_SERVER &&
+ params_in->admin_server != NULL) {
+ /* User passed in a admin server host name so use that */
+ if (asprintf(&tmp_srv_names[0], "%s@%s", KADM5_ADMIN_HOST_SERVICE,
+ params_in->admin_server) == -1) {
+ code = ENOMEM;
+ goto error;
+ }
+ svcname_ptr = tmp_srv_names;
+ } else {
+ /* Otherwise punt and get the admin server names. */
+ code = kadm5_get_adm_host_srv_names(context, handle->params.realm,
+ &kadmin_srv_names);
+ if (code)
+ goto error;
+ svcname_ptr = kadmin_srv_names;
+ }
+ } else {
+ svcname_ptr = svcnames_in;
+ }
- code = kadm5_get_adm_host_srv_names(context, handle->params.realm,
- &kadmin_srv_names);
- if (code)
- goto error;
- svcname = strdup(kadmin_srv_names[0]);
- free_srv_names(kadmin_srv_names);
- if (svcname == NULL) {
- code = ENOMEM;
- goto error;
- }
+ for (i = 0; svcname_ptr[i]; i++) {
+ /* Get credentials. */
+ code = get_init_creds(handle, client, init_type, pass, ccache_in,
+ svcname_ptr[i], handle->params.realm, &server);
+ if (code) {
+ if (code == KADM5_SECURE_PRINC_MISSING ||
+ code == KRB5_ERR_BAD_HOSTNAME) {
+ /* clean up for another go around */
+ clean_up(handle, &server, &ccache);
+ continue;
+ } else
+ goto error;
+ }
+
+ code = _kadm5_initialize_rpcsec_gss_handle(handle, client_name,
+ svcname_ptr[i]);
+ if (code) {
+ /* clean up for another go around */
+ clean_up(handle, &server, &ccache);
+ } else {
+ /* inited the rpcsec_gss handle, can stop looping now */
+ break;
+ }
}
- /* Get credentials. */
- code = get_init_creds(handle, client, init_type, pass, ccache_in,
- svcname, handle->params.realm, &server);
if (code)
goto error;
- code = _kadm5_initialize_rpcsec_gss_handle(handle, client_name,
- svcname);
- if (code != 0) {
- goto error;
- }
-
*server_handle = (void *) handle;
goto cleanup;
@@ -659,6 +794,8 @@ cleanup:
krb5_free_principal(handle->context, server);
if (code)
free(handle);
+ free_srv_names(kadmin_srv_names);
+ free(tmp_srv_names[0]);
return code;
}
@@ -671,46 +808,43 @@ get_init_creds(kadm5_server_handle_t handle, krb5_principal client,
{
kadm5_ret_t code;
krb5_ccache ccache = NULL;
- krb5_principal cprinc;
char svcbuf[BUFSIZ], *service, *host, *svcname, *save;
+ char strtok_buf[BUFSIZ];
*server_out = NULL;
- /*
- * Convert the RPCSEC_GSS version to the host based version as this
- * is what gic expects.
- */
- if ((svcname = strdup(svcname_in)) == NULL) {
- code = ENOMEM;
- goto error;
- }
- if ((service = strtok_r(svcname, "@", &save)) != NULL) {
- if ((host = strtok_r(NULL, "@", &save)) != NULL) {
- char *str = NULL;
- /*
- * We want the canonical name here, via sname_to_principal.
- */
- code = krb5_sname_to_principal(handle->context, host, service,
- KRB5_NT_SRV_HST, &cprinc);
- if (code) {
- free(svcname);
- goto error;
- }
- code = krb5_unparse_name_flags(handle->context, cprinc,
- KRB5_PRINCIPAL_UNPARSE_NO_REALM, &str);
- krb5_free_principal(handle->context, cprinc);
- if (code) {
- free(svcname);
- goto error;
- }
- (void) strncpy(svcbuf, str, sizeof(svcbuf));
- krb5_free_unparsed_name(handle->context, str);
- }
+ if (strpbrk(svcname_in, "@") == NULL) {
+ svcname = svcname_in;
} else {
- strncpy(svcbuf, svcname, sizeof(svcbuf));
- svcbuf[sizeof(svcname)-1] = '\0';
+ /*
+ * Convert the RPCSEC_GSS version to the host based version as this
+ * is what gic expects.
+ */
+ (void) strlcpy(strtok_buf, svcname_in, sizeof(strtok_buf));
+ service = strtok_r(strtok_buf, "@", &save);
+ if ((host = strtok_r(NULL, "@", &save)) != NULL) {
+ char *str = NULL;
+ krb5_principal cprinc;
+ /*
+ * We want the canonical name here, via sname_to_principal.
+ */
+ code = krb5_sname_to_principal(handle->context, host, service,
+ KRB5_NT_SRV_HST, &cprinc);
+ if (code)
+ goto error;
+
+ code = krb5_unparse_name_flags(handle->context, cprinc,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+ &str);
+ krb5_free_principal(handle->context, cprinc);
+ if (code)
+ goto error;
+
+ (void) strlcpy(svcbuf, str, sizeof(svcbuf));
+ krb5_free_unparsed_name(handle->context, str);
+ svcname = svcbuf;
+ }
}
- free(svcname);
/*
* Acquire a service ticket for svcname@realm for client, using password
@@ -747,7 +881,7 @@ get_init_creds(kadm5_server_handle_t handle, krb5_principal client,
}
handle->lhandle->cache_name = handle->cache_name;
- code = gic_iter(handle, init_type, ccache, client, pass, svcbuf, realm,
+ code = gic_iter(handle, init_type, ccache, client, pass, svcname, realm,
server_out);
/* Improved error messages */
if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD;
@@ -31,6 +31,11 @@ kadm5_init_krb5_context
kadm5_init_with_creds
kadm5_init_with_password
kadm5_init_with_skey
+kadm5_init_mm
+kadm5_init_anonymous_mm
+kadm5_init_with_creds_mm
+kadm5_init_with_password_mm
+kadm5_init_with_skey_mm
kadm5_lock
kadm5_modify_policy
kadm5_modify_principal
@@ -97,6 +97,29 @@ kadm5_ret_t kadm5_init_with_password(krb5_context context, char *client_name,
server_handle);
}
+/*
+ * The following *_mm() functions are also defined here because kadmin.local
+ * shares the same source as kadmin. One difference however is that
+ * these functions must also be defined here. Given kadmin.local can only
+ * operate on one server, the *_mm() functions just pass in the first server in
+ * the list of service_names.
+ */
+kadm5_ret_t kadm5_init_with_password_mm(krb5_context context,
+ char *client_name,
+ char *pass, char **service_names,
+ kadm5_config_params *params,
+ krb5_ui_4 struct_version,
+ krb5_ui_4 api_version,
+ char **db_args,
+ void **server_handle)
+{
+ return kadm5_init_with_password(context, client_name, pass,
+ service_names ? service_names[0] : NULL,
+ params, struct_version, api_version,
+ db_args, server_handle);
+}
+
kadm5_ret_t kadm5_init_anonymous(krb5_context context, char *client_name,
char *service_name,
kadm5_config_params *params,
@@ -110,6 +133,20 @@ kadm5_ret_t kadm5_init_anonymous(krb5_context context, char *client_name,
server_handle);
}
+kadm5_ret_t kadm5_init_anonymous_mm(krb5_context context, char *client_name,
+ char **service_names,
+ kadm5_config_params *params,
+ krb5_ui_4 struct_version,
+ krb5_ui_4 api_version,
+ char **db_args,
+ void **server_handle)
+{
+ return kadm5_init_anonymous(context, client_name,
+ service_names ? service_names[0] : NULL,
+ params, struct_version, api_version,
+ db_args, server_handle);
+}
+
kadm5_ret_t kadm5_init_with_creds(krb5_context context,
char *client_name,
krb5_ccache ccache,
@@ -133,6 +170,22 @@ kadm5_ret_t kadm5_init_with_creds(krb5_context context,
server_handle);
}
+kadm5_ret_t kadm5_init_with_creds_mm(krb5_context context,
+ char *client_name,
+ krb5_ccache ccache,
+ char **service_names,
+ kadm5_config_params *params,
+ krb5_ui_4 struct_version,
+ krb5_ui_4 api_version,
+ char **db_args,
+ void **server_handle)
+{
+ return kadm5_init_with_creds(context, client_name, ccache,
+ service_names ? service_names[0] : NULL,
+ params, struct_version, api_version,
+ db_args, server_handle);
+}
+
kadm5_ret_t kadm5_init_with_skey(krb5_context context, char *client_name,
char *keytab, char *service_name,
@@ -155,6 +208,20 @@ kadm5_ret_t kadm5_init_with_skey(krb5_context context, char *client_name,
server_handle);
}
+kadm5_ret_t kadm5_init_with_skey_mm(krb5_context context, char *client_name,
+ char *keytab, char **service_names,
+ kadm5_config_params *params,
+ krb5_ui_4 struct_version,
+ krb5_ui_4 api_version,
+ char **db_args,
+ void **server_handle)
+{
+ return kadm5_init_with_skey(context, client_name, keytab,
+ service_names ? service_names[0] : NULL,
+ params, struct_version, api_version, db_args,
+ server_handle);
+}
+
kadm5_ret_t kadm5_init(krb5_context context, char *client_name, char *pass,
char *service_name,
kadm5_config_params *params_in,
@@ -613,7 +613,7 @@ do_iprop()
kadm5_ret_t retval;
krb5_principal iprop_svc_principal;
void *server_handle = NULL;
- char *iprop_svc_princstr = NULL, *master_svc_princstr = NULL;
+ char *iprop_svc_princstr = NULL, **master_svc_princstrs = NULL;
unsigned int pollin, backoff_time;
int backoff_cnt = 0, reinit_cnt = 0;
struct timeval iprop_start, iprop_end;
@@ -643,22 +643,9 @@ do_iprop()
params.mask |= KADM5_CONFIG_REALM;
params.realm = def_realm;
- if (master_svc_princstr == NULL) {
-#if 0
- retval = kadm5_get_kiprop_host_srv_name(kpropd_context, def_realm,
- &master_svc_princstr);
- if (retval) {
- com_err(progname, retval,
- _("%s: unable to get kiprop host based "
- "service name for realm %s\n"),
- progname, def_realm);
- return retval;
- }
-#endif
- char **kiprop_srv_names;
-
+ if (master_svc_princstrs == NULL) {
retval = kadm5_get_kiprop_host_srv_names(kpropd_context, def_realm,
- &kiprop_srv_names);
+ &master_svc_princstrs);
if (retval) {
com_err(progname, retval,
_("%s: unable to get kiprop host based "
@@ -666,15 +653,6 @@ do_iprop()
progname, def_realm);
return retval;
}
- master_svc_princstr = strdup(kiprop_srv_names[0]);
- free_srv_names(kiprop_srv_names);
- if (master_svc_princstr == NULL) {
- com_err(progname, retval,
- _("%s: unable to allocate memory for kiprop host based "
- "service name for realm %s\n"),
- progname, def_realm);
- return ENOMEM;
- }
}
retval = krb5_sname_to_principal(kpropd_context, NULL, KIPROP_SVC_NAME,
@@ -718,9 +696,9 @@ reinit:
fprintf(stderr, _("Initializing kadm5 as client %s\n"),
iprop_svc_princstr);
}
- retval = kadm5_init_with_skey(kpropd_context, iprop_svc_princstr,
+ retval = kadm5_init_with_skey_mm(kpropd_context, iprop_svc_princstr,
srvtab,
- master_svc_princstr,
+ master_svc_princstrs,
¶ms,
KADM5_STRUCT_VERSION,
KADM5_API_VERSION_4,
@@ -1018,7 +996,7 @@ error:
syslog(LOG_ERR, _("ERROR returned by master KDC, bailing.\n"));
done:
free(iprop_svc_princstr);
- free(master_svc_princstr);
+ free_srv_names(master_svc_princstrs);
krb5_free_default_realm(kpropd_context, def_realm);
kadm5_destroy(server_handle);
krb5_free_context(kpropd_context);