kadm5/kadmin: Add read-only mode

Now we can have read-only kadmind instances.
This commit is contained in:
Nicolas Williams
2020-08-19 23:49:03 -05:00
parent 5447b81fb1
commit ea83f068e9
23 changed files with 274 additions and 68 deletions
+10
View File
@@ -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;
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1
View File
@@ -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)
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+91 -13
View File
@@ -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;
}
+1
View File
@@ -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"
+1 -1
View File
@@ -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;
+4
View File
@@ -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 {
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+6 -5
View File
@@ -833,11 +833,12 @@ typedef struct krb5_verify_opt {
struct krb5_krbhst_data;
typedef struct krb5_krbhst_data *krb5_krbhst_handle;
#define KRB5_KRBHST_KDC 1
#define KRB5_KRBHST_ADMIN 2
#define KRB5_KRBHST_CHANGEPW 3
#define KRB5_KRBHST_KRB524 4
#define KRB5_KRBHST_KCA 5
#define KRB5_KRBHST_KDC 1
#define KRB5_KRBHST_ADMIN 2
#define KRB5_KRBHST_CHANGEPW 3
#define KRB5_KRBHST_KRB524 4
#define KRB5_KRBHST_KCA 5
#define KRB5_KRBHST_READONLY_ADMIN 6
typedef struct krb5_krbhst_info {
enum { KRB5_KRBHST_UDP,
+55 -19
View File
@@ -163,6 +163,8 @@ srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
struct krb5_krbhst_data {
const char *config_param;
const char *srv_label;
char *realm;
unsigned int flags;
int def_port;
@@ -739,7 +741,7 @@ kdc_get_next(krb5_context context,
}
if((kd->flags & KD_CONFIG) == 0) {
config_get_hosts(context, kd, "kdc");
config_get_hosts(context, kd, kd->config_param);
kd->flags |= KD_CONFIG;
if(get_next(kd, host))
return 0;
@@ -754,20 +756,20 @@ kdc_get_next(krb5_context context,
if(context->srv_lookup) {
if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) {
srv_get_hosts(context, kd, "udp", "kerberos");
srv_get_hosts(context, kd, "udp", kd->srv_label);
kd->flags |= KD_SRV_UDP;
if(get_next(kd, host))
return 0;
}
if((kd->flags & KD_SRV_TCP) == 0) {
srv_get_hosts(context, kd, "tcp", "kerberos");
srv_get_hosts(context, kd, "tcp", kd->srv_label);
kd->flags |= KD_SRV_TCP;
if(get_next(kd, host))
return 0;
}
if((kd->flags & KD_SRV_HTTP) == 0) {
srv_get_hosts(context, kd, "http", "kerberos");
srv_get_hosts(context, kd, "http", kd->srv_label);
kd->flags |= KD_SRV_HTTP;
if(get_next(kd, host))
return 0;
@@ -804,7 +806,7 @@ admin_get_next(krb5_context context,
}
if((kd->flags & KD_CONFIG) == 0) {
config_get_hosts(context, kd, "admin_server");
config_get_hosts(context, kd, kd->config_param);
kd->flags |= KD_CONFIG;
if(get_next(kd, host))
return 0;
@@ -819,7 +821,7 @@ admin_get_next(krb5_context context,
if(context->srv_lookup) {
if((kd->flags & KD_SRV_TCP) == 0) {
srv_get_hosts(context, kd, "tcp", "kerberos-adm");
srv_get_hosts(context, kd, "tcp", kd->srv_label);
kd->flags |= KD_SRV_TCP;
if(get_next(kd, host))
return 0;
@@ -858,7 +860,7 @@ kpasswd_get_next(krb5_context context,
}
if((kd->flags & KD_CONFIG) == 0) {
config_get_hosts(context, kd, "kpasswd_server");
config_get_hosts(context, kd, kd->config_param);
kd->flags |= KD_CONFIG;
if(get_next(kd, host))
return 0;
@@ -873,13 +875,13 @@ kpasswd_get_next(krb5_context context,
if(context->srv_lookup) {
if((kd->flags & KD_SRV_UDP) == 0) {
srv_get_hosts(context, kd, "udp", "kpasswd");
srv_get_hosts(context, kd, "udp", kd->srv_label);
kd->flags |= KD_SRV_UDP;
if(get_next(kd, host))
return 0;
}
if((kd->flags & KD_SRV_TCP) == 0) {
srv_get_hosts(context, kd, "tcp", "kpasswd");
srv_get_hosts(context, kd, "tcp", kd->srv_label);
kd->flags |= KD_SRV_TCP;
if(get_next(kd, host))
return 0;
@@ -921,6 +923,8 @@ krbhost_dealloc(void *ptr)
static struct krb5_krbhst_data*
common_init(krb5_context context,
const char *config_param,
const char *srv_label,
const char *service,
const char *realm,
int flags)
@@ -935,6 +939,9 @@ common_init(krb5_context context,
return NULL;
}
kd->config_param = config_param;
kd->srv_label = srv_label;
_krb5_debug(context, 2, "Trying to find service %s for realm %s flags %x",
service, realm, flags);
@@ -972,6 +979,8 @@ krb5_krbhst_init_flags(krb5_context context,
krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *,
krb5_krbhst_info **);
int def_port;
const char *config_param;
const char *srv_label;
const char *service;
*handle = NULL;
@@ -979,19 +988,33 @@ krb5_krbhst_init_flags(krb5_context context,
switch(type) {
case KRB5_KRBHST_KDC:
next = kdc_get_next;
def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88));
def_port = ntohs(krb5_getportbyname(context, "kerberos", "udp", 88));
config_param = "kdc";
srv_label = "kerberos";
service = "kdc";
break;
case KRB5_KRBHST_ADMIN:
next = admin_get_next;
def_port = ntohs(krb5_getportbyname (context, "kerberos-adm",
"tcp", 749));
def_port = ntohs(krb5_getportbyname(context, "kerberos-adm",
"tcp", 749));
config_param = "admin_server";
srv_label = "kerberos-adm";
service = "admin";
break;
case KRB5_KRBHST_READONLY_ADMIN:
next = admin_get_next;
def_port = ntohs(krb5_getportbyname(context, "kerberos-adm",
"tcp", 749));
config_param = "readonly_admin_server";
srv_label = "kerberos-adm-readonly";
service = "admin";
break;
case KRB5_KRBHST_CHANGEPW:
next = kpasswd_get_next;
def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp",
KPASSWD_PORT));
def_port = ntohs(krb5_getportbyname(context, "kpasswd", "udp",
KPASSWD_PORT));
config_param = "kpasswd_server";
srv_label = "kpasswd";
service = "change_password";
break;
default:
@@ -999,7 +1022,8 @@ krb5_krbhst_init_flags(krb5_context context,
N_("unknown krbhst type (%u)", ""), type);
return ENOTTY;
}
if((kd = common_init(context, service, realm, flags)) == NULL)
if((kd = common_init(context, config_param, srv_label, service, realm,
flags)) == NULL)
return ENOMEM;
kd->get_next = next;
kd->def_port = def_port;
@@ -1118,17 +1142,29 @@ gethostlist(krb5_context context, const char *realm,
}
/*
* return an malloced list of kadmin-hosts for `realm' in `hostlist'
* Return a malloced list of kadmin-hosts for `realm' in `hostlist'
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_krb_admin_hst (krb5_context context,
const krb5_realm *realm,
char ***hostlist)
krb5_get_krb_admin_hst(krb5_context context,
const krb5_realm *realm,
char ***hostlist)
{
return gethostlist(context, *realm, KRB5_KRBHST_ADMIN, hostlist);
}
/*
* Return a malloced list of writable kadmin-hosts for `realm' in `hostlist'
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_krb_readonly_admin_hst(krb5_context context,
const krb5_realm *realm,
char ***hostlist)
{
return gethostlist(context, *realm, KRB5_KRBHST_READONLY_ADMIN, hostlist);
}
/*
* return an malloced list of changepw-hosts for `realm' in `hostlist'
*/
+1
View File
@@ -378,6 +378,7 @@ EXPORTS
krb5_get_krb524hst
krb5_get_krb_admin_hst
krb5_get_krb_changepw_hst
krb5_get_krb_readonly_admin_hst
krb5_get_krbhst
krb5_get_max_time_skew
krb5_get_pw_salt
+1
View File
@@ -374,6 +374,7 @@ HEIMDAL_KRB5_2.0 {
krb5_get_krb524hst;
krb5_get_krb_admin_hst;
krb5_get_krb_changepw_hst;
krb5_get_krb_readonly_admin_hst;
krb5_get_krbhst;
krb5_get_max_time_skew;
krb5_get_pw_salt;