kadm5/kadmin: Add read-only mode
Now we can have read-only kadmind instances.
This commit is contained in:
@@ -204,6 +204,8 @@ typedef struct _kadm5_policy_ent_t {
|
||||
#define KADM5_CONFIG_EXPIRATION (1 << 16)
|
||||
#define KADM5_CONFIG_FLAGS (1 << 17)
|
||||
#define KADM5_CONFIG_ENCTYPES (1 << 18)
|
||||
#define KADM5_CONFIG_READONLY_ADMIN_SERVER (1 << 19)
|
||||
#define KADM5_CONFIG_READONLY_KADMIN_PORT (1 << 20)
|
||||
|
||||
#define KADM5_PRIV_GET (1 << 0)
|
||||
#define KADM5_PRIV_ADD (1 << 1)
|
||||
@@ -218,6 +220,10 @@ typedef struct _kadm5_policy_ent_t {
|
||||
|
||||
#define KADM5_BOGUS_KEY_DATA "\xe5\xe5\xe5\xe5"
|
||||
|
||||
/*
|
||||
* ABI NOTE: We can add fields at the end of this provided that we define new
|
||||
* mask bits that must be set in the mask field when setting the new fields.
|
||||
*/
|
||||
typedef struct _kadm5_config_params {
|
||||
uint32_t mask;
|
||||
|
||||
@@ -234,6 +240,10 @@ typedef struct _kadm5_config_params {
|
||||
|
||||
/* server library (database) fields */
|
||||
char *stash_file;
|
||||
|
||||
/* read-only kadmin server */
|
||||
char *readonly_admin_server;
|
||||
int readonly_kadmind_port;
|
||||
} kadm5_config_params;
|
||||
|
||||
typedef krb5_error_code kadm5_ret_t;
|
||||
|
@@ -58,7 +58,7 @@ kadm5_c_chpass_principal(void *server_handle,
|
||||
if (n_ks_tuple > 0)
|
||||
return KADM5_KS_TUPLE_NOSUPP;
|
||||
|
||||
ret = _kadm5_connect(server_handle);
|
||||
ret = _kadm5_connect(server_handle, 1 /* want_write */);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -121,7 +121,7 @@ kadm5_c_chpass_principal_with_key(void *server_handle,
|
||||
krb5_data reply;
|
||||
int i;
|
||||
|
||||
ret = _kadm5_connect(server_handle);
|
||||
ret = _kadm5_connect(server_handle, 1 /* want_write */);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@@ -64,7 +64,7 @@ kadm5_c_create_principal(void *server_handle,
|
||||
if (n_ks_tuple > 0)
|
||||
return KADM5_KS_TUPLE_NOSUPP;
|
||||
|
||||
ret = _kadm5_connect(server_handle);
|
||||
ret = _kadm5_connect(server_handle, 1 /* want_write */);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@@ -45,7 +45,7 @@ kadm5_c_delete_principal(void *server_handle, krb5_principal princ)
|
||||
int32_t tmp;
|
||||
krb5_data reply;
|
||||
|
||||
ret = _kadm5_connect(server_handle);
|
||||
ret = _kadm5_connect(server_handle, 1 /* want_write */);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@@ -41,6 +41,7 @@ kadm5_c_destroy(void *server_handle)
|
||||
kadm5_client_context *context = server_handle;
|
||||
|
||||
free(context->realm);
|
||||
free(context->readonly_admin_server);
|
||||
free(context->admin_server);
|
||||
rk_closesocket(context->sock);
|
||||
if (context->client_name)
|
||||
|
@@ -48,7 +48,7 @@ kadm5_c_get_principal(void *server_handle,
|
||||
int32_t tmp;
|
||||
krb5_data reply;
|
||||
|
||||
ret = _kadm5_connect(server_handle);
|
||||
ret = _kadm5_connect(server_handle, 0 /* want_write */);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@@ -52,7 +52,7 @@ kadm5_c_get_principals(void *server_handle,
|
||||
*count = 0;
|
||||
*princs = NULL;
|
||||
|
||||
ret = _kadm5_connect(server_handle);
|
||||
ret = _kadm5_connect(server_handle, 0 /* want_write */);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@@ -89,13 +89,13 @@ _kadm5_c_init_context(kadm5_client_context **ctx,
|
||||
char *colon;
|
||||
|
||||
*ctx = malloc(sizeof(**ctx));
|
||||
if(*ctx == NULL)
|
||||
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)
|
||||
@@ -106,19 +106,24 @@ _kadm5_c_init_context(kadm5_client_context **ctx,
|
||||
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) {
|
||||
@@ -126,22 +131,64 @@ _kadm5_c_init_context(kadm5_client_context **ctx,
|
||||
free(*ctx);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -434,10 +481,24 @@ kadm_connect(kadm5_client_context *ctx)
|
||||
struct addrinfo *ai, *a;
|
||||
struct addrinfo hints;
|
||||
int error;
|
||||
int kadmin_port = 0;
|
||||
const char *admin_server = NULL;
|
||||
char portstr[NI_MAXSERV];
|
||||
char *hostname, *slash;
|
||||
char *service_name;
|
||||
krb5_context context = ctx->context;
|
||||
int writable = 0;
|
||||
|
||||
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;
|
||||
@@ -563,14 +624,31 @@ kadm_connect(kadm5_client_context *ctx)
|
||||
krb5_cc_close(context, cc);
|
||||
ctx->sock = s;
|
||||
|
||||
ctx->connected_to_writable = !!writable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@@ -71,3 +71,4 @@ error_code BAD_SERVER_HOOK, "Bad KADM5 server hook"
|
||||
error_code SERVER_HOOK_NOT_FOUND, "Cannot find KADM5 server hook"
|
||||
error_code OLD_SERVER_HOOK_VERSION, "KADM5 server hook is too old for this version of Heimdal"
|
||||
error_code NEW_SERVER_HOOK_VERSION, "KADM5 server hook is too new for this version of Heimdal"
|
||||
error_code READ_ONLY, "Database is read-only; try primary server"
|
||||
|
@@ -47,7 +47,7 @@ kadm5_c_modify_principal(void *server_handle,
|
||||
int32_t tmp;
|
||||
krb5_data reply;
|
||||
|
||||
ret = _kadm5_connect(server_handle);
|
||||
ret = _kadm5_connect(server_handle, 1 /* want_write */);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@@ -137,6 +137,10 @@ typedef struct kadm5_client_context {
|
||||
const char *keytab;
|
||||
krb5_ccache ccache;
|
||||
kadm5_config_params *realm_params;
|
||||
char *readonly_admin_server;
|
||||
int readonly_kadmind_port;
|
||||
unsigned int want_write:1;
|
||||
unsigned int connected_to_writable:1;
|
||||
} kadm5_client_context;
|
||||
|
||||
typedef struct kadm5_ad_context {
|
||||
|
@@ -47,7 +47,7 @@ kadm5_c_get_privs(void *server_handle, uint32_t *privs)
|
||||
|
||||
*privs = 0;
|
||||
|
||||
ret = _kadm5_connect(server_handle);
|
||||
ret = _kadm5_connect(server_handle, 0 /* want_write */);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@@ -44,7 +44,7 @@ kadm5_c_prune_principal(void *server_handle, krb5_principal princ, int kvno)
|
||||
krb5_data reply;
|
||||
|
||||
krb5_data_zero(&reply);
|
||||
ret = _kadm5_connect(server_handle);
|
||||
ret = _kadm5_connect(server_handle, 1 /* want_write */);
|
||||
if (ret == 0 && (sp = krb5_storage_from_mem(buf, sizeof(buf))) == NULL)
|
||||
ret = krb5_enomem(context->context);
|
||||
if (ret == 0)
|
||||
|
@@ -53,7 +53,7 @@ kadm5_c_randkey_principal(void *server_handle,
|
||||
krb5_data reply;
|
||||
krb5_keyblock *k;
|
||||
|
||||
ret = _kadm5_connect(server_handle);
|
||||
ret = _kadm5_connect(server_handle, 1 /* want_write */);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@@ -47,7 +47,7 @@ kadm5_c_rename_principal(void *server_handle,
|
||||
int32_t tmp;
|
||||
krb5_data reply;
|
||||
|
||||
ret = _kadm5_connect(server_handle);
|
||||
ret = _kadm5_connect(server_handle, 1 /* want_write */);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
Reference in New Issue
Block a user