diff --git a/kadmin/kadmin_locl.h b/kadmin/kadmin_locl.h index 368901807..ef9420f16 100644 --- a/kadmin/kadmin_locl.h +++ b/kadmin/kadmin_locl.h @@ -149,12 +149,12 @@ void start_server(krb5_context, const char*); /* server.c */ krb5_error_code -kadmind_loop (krb5_context, krb5_keytab, int); +kadmind_loop (krb5_context, krb5_keytab, int, int); /* rpc.c */ int -handle_mit(krb5_context, void *, size_t, int); +handle_mit(krb5_context, void *, size_t, int, int); /* mod.c */ diff --git a/kadmin/kadmind.c b/kadmin/kadmind.c index f88aec45c..10bbea840 100644 --- a/kadmin/kadmind.c +++ b/kadmin/kadmind.c @@ -42,6 +42,7 @@ static char *keytab_str = sHDB; static int help_flag; static int version_flag; static int debug_flag; +static int readonly_flag; static char *port_str; char *realm; @@ -81,6 +82,8 @@ static struct getargs args[] = { }, { "ports", 'p', arg_string, &port_str, "ports to listen to", "port" }, + { "read-only", 0, arg_flag, &readonly_flag, + "read-only operations", NULL }, { "help", 'h', arg_flag, &help_flag, NULL, NULL }, { "version", 'v', arg_flag, &version_flag, NULL, NULL } }; @@ -211,7 +214,7 @@ main(int argc, char **argv) if(realm) krb5_set_default_realm(context, realm); /* XXX */ - kadmind_loop(context, keytab, sfd); + kadmind_loop(context, keytab, sfd, readonly_flag); return 0; } diff --git a/kadmin/rpc.c b/kadmin/rpc.c index 374695cc8..6ddb9dfa8 100644 --- a/kadmin/rpc.c +++ b/kadmin/rpc.c @@ -689,7 +689,7 @@ proc_init(kadm5_server_context *contextp, struct krb5_proc { const char *name; void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *); -} procs[] = { +} rwprocs[] = { { "NULL", NULL }, { "create principal", proc_create_principal }, { "delete principal", proc_delete_principal }, @@ -712,6 +712,29 @@ struct krb5_proc { { "chpass principal v3", NULL }, { "chrand principal v3", NULL }, { "setkey principal v3", NULL } +}, roprocs[] = { + { "NULL", NULL }, + { "create principal", NULL }, + { "delete principal", NULL }, + { "modify principal", NULL }, + { "rename principal", NULL }, + { "get principal", proc_get_principal }, + { "chpass principal", NULL }, + { "chrand principal", NULL }, + { "create policy", NULL }, + { "delete policy", NULL }, + { "modify policy", NULL }, + { "get policy", NULL }, + { "get privs", NULL }, + { "init", NULL }, + { "get principals", NULL }, + { "get polices", NULL }, + { "setkey principal", NULL }, + { "setkey principal v4", NULL }, + { "create principal v3", NULL }, + { "chpass principal v3", NULL }, + { "chrand principal v3", NULL }, + { "setkey principal v3", NULL } }; static krb5_error_code @@ -742,8 +765,10 @@ struct gctx { static int process_stream(krb5_context contextp, - unsigned char *buf, size_t ilen, - krb5_storage *sp) + unsigned char *buf, + size_t ilen, + krb5_storage *sp, + int readonly) { krb5_error_code ret; krb5_storage *msg, *reply, *dreply; @@ -884,6 +909,7 @@ process_stream(krb5_context contextp, int conf_state; uint32_t seq; krb5_storage *sp1; + struct krb5_proc *procs = readonly ? roprocs : rwprocs; INSIST(gcred.service == rpg_privacy); @@ -921,11 +947,16 @@ process_stream(krb5_context contextp, */ CHECK(krb5_store_uint32(dreply, gctx.seq_num)); - if (chdr.proc >= sizeof(procs)/sizeof(procs[0])) { + if (chdr.proc >= sizeof(rwprocs)/sizeof(rwprocs[0])) { krb5_warnx(contextp, "proc number out of array"); } else if (procs[chdr.proc].func == NULL) { - krb5_warnx(contextp, "proc '%s' never implemented", - procs[chdr.proc].name); + if (readonly && rwprocs[chdr.proc].func) + krb5_warnx(contextp, + "proc '%s' not allowed (readonly mode)", + procs[chdr.proc].name); + else + krb5_warnx(contextp, "proc '%s' never implemented", + procs[chdr.proc].name); } else { krb5_warnx(contextp, "proc %s", procs[chdr.proc].name); INSIST(server_handle != NULL); @@ -1101,7 +1132,11 @@ process_stream(krb5_context contextp, int -handle_mit(krb5_context contextp, void *buf, size_t len, krb5_socket_t sock) +handle_mit(krb5_context contextp, + void *buf, + size_t len, + krb5_socket_t sock, + int readonly) { krb5_storage *sp; @@ -1110,7 +1145,7 @@ handle_mit(krb5_context contextp, void *buf, size_t len, krb5_socket_t sock) sp = krb5_storage_from_socket(sock); INSIST(sp != NULL); - process_stream(contextp, buf, len, sp); + process_stream(contextp, buf, len, sp, readonly); return 0; } diff --git a/kadmin/server.c b/kadmin/server.c index cc82644c6..cbe16948c 100644 --- a/kadmin/server.c +++ b/kadmin/server.c @@ -40,7 +40,7 @@ static kadm5_ret_t check_aliases(kadm5_server_context *, static kadm5_ret_t kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, - krb5_data *in, krb5_data *out) + krb5_data *in, krb5_data *out, int readonly) { kadm5_ret_t ret; int32_t cmd, mask, kvno, tmp; @@ -139,8 +139,12 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, } case kadm_delete:{ op = "DELETE"; + if (readonly) { + ret = KADM5_READ_ONLY; + goto fail; + } ret = krb5_ret_principal(sp, &princ); - if(ret) + if (ret) goto fail; krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); krb5_warnx(contextp->context, "%s: %s %s", client, op, name); @@ -165,6 +169,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, } case kadm_create:{ op = "CREATE"; + if (readonly) { + ret = KADM5_READ_ONLY; + goto fail; + } ret = kadm5_ret_principal_ent(sp, &ent); if(ret) goto fail; @@ -212,6 +220,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, } case kadm_modify:{ op = "MODIFY"; + if (readonly) { + ret = KADM5_READ_ONLY; + goto fail; + } ret = kadm5_ret_principal_ent(sp, &ent); if(ret) goto fail; @@ -261,6 +273,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, } case kadm_prune:{ op = "PRUNE"; + if (readonly) { + ret = KADM5_READ_ONLY; + goto fail; + } ret = krb5_ret_principal(sp, &princ); if (ret) goto fail; @@ -288,6 +304,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, } case kadm_rename:{ op = "RENAME"; + if (readonly) { + ret = KADM5_READ_ONLY; + goto fail; + } ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; @@ -335,6 +355,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, krb5_boolean is_self_cpw, allow_self_cpw; op = "CHPASS"; + if (readonly) { + ret = KADM5_READ_ONLY; + goto fail; + } ret = krb5_ret_principal(sp, &princ); if (ret) goto fail; @@ -382,6 +406,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, int n_key_data; op = "CHPASS_WITH_KEY"; + if (readonly) { + ret = KADM5_READ_ONLY; + goto fail; + } ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; @@ -453,6 +481,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, size_t i; op = "RANDKEY"; + if (readonly) { + ret = KADM5_READ_ONLY; + goto fail; + } ret = krb5_ret_principal(sp, &princ); if (ret) goto fail; @@ -758,7 +790,8 @@ v5_loop (krb5_context contextp, krb5_auth_context ac, krb5_boolean initial, void *kadm_handlep, - krb5_socket_t fd) + krb5_socket_t fd, + int readonly) { krb5_error_code ret; krb5_data in, out; @@ -773,7 +806,7 @@ v5_loop (krb5_context contextp, if(ret) krb5_err(contextp, 1, ret, "krb5_read_priv_message"); doing_useful_work = 1; - ret = kadmind_dispatch(kadm_handlep, initial, &in, &out); + ret = kadmind_dispatch(kadm_handlep, initial, &in, &out, readonly); if (ret) krb5_err(contextp, 1, ret, "kadmind_dispatch"); krb5_data_free(&in); @@ -798,7 +831,8 @@ match_appl_version(const void *data, const char *appl_version) static void handle_v5(krb5_context contextp, krb5_keytab keytab, - krb5_socket_t fd) + krb5_socket_t fd, + int readonly) { krb5_error_code ret; krb5_ticket *ticket; @@ -853,13 +887,14 @@ handle_v5(krb5_context contextp, &kadm_handlep); if(ret) krb5_err (contextp, 1, ret, "kadm5_init_with_password_ctx"); - v5_loop (contextp, ac, initial, kadm_handlep, fd); + v5_loop (contextp, ac, initial, kadm_handlep, fd, readonly); } krb5_error_code kadmind_loop(krb5_context contextp, krb5_keytab keytab, - krb5_socket_t sock) + krb5_socket_t sock, + int readonly) { u_char buf[sizeof(KRB5_SENDAUTH_VERSION) + 4]; ssize_t n; @@ -881,14 +916,14 @@ kadmind_loop(krb5_context contextp, krb5_errx (contextp, 1, "EOF reading sendauth version"); if(memcmp(buf + 4, KRB5_SENDAUTH_VERSION, len) == 0) { - handle_v5(contextp, keytab, sock); + handle_v5(contextp, keytab, sock, readonly); return 0; } len += 4; } else len = 4; - handle_mit(contextp, buf, len, sock); + handle_mit(contextp, buf, len, sock, readonly); return 0; } diff --git a/lib/kadm5/admin.h b/lib/kadm5/admin.h index e881ea83b..3aa4ed863 100644 --- a/lib/kadm5/admin.h +++ b/lib/kadm5/admin.h @@ -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; diff --git a/lib/kadm5/chpass_c.c b/lib/kadm5/chpass_c.c index 9494359ba..103691874 100644 --- a/lib/kadm5/chpass_c.c +++ b/lib/kadm5/chpass_c.c @@ -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; diff --git a/lib/kadm5/create_c.c b/lib/kadm5/create_c.c index f3e757e9e..c239beb22 100644 --- a/lib/kadm5/create_c.c +++ b/lib/kadm5/create_c.c @@ -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; diff --git a/lib/kadm5/delete_c.c b/lib/kadm5/delete_c.c index e8fdf21d1..cf06b6f28 100644 --- a/lib/kadm5/delete_c.c +++ b/lib/kadm5/delete_c.c @@ -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; diff --git a/lib/kadm5/destroy_c.c b/lib/kadm5/destroy_c.c index 9a4ab83be..15e2b5e2a 100644 --- a/lib/kadm5/destroy_c.c +++ b/lib/kadm5/destroy_c.c @@ -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) diff --git a/lib/kadm5/get_c.c b/lib/kadm5/get_c.c index fbed89ddd..5b2bcca76 100644 --- a/lib/kadm5/get_c.c +++ b/lib/kadm5/get_c.c @@ -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; diff --git a/lib/kadm5/get_princs_c.c b/lib/kadm5/get_princs_c.c index 2a90c19e2..4998223d2 100644 --- a/lib/kadm5/get_princs_c.c +++ b/lib/kadm5/get_princs_c.c @@ -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; diff --git a/lib/kadm5/init_c.c b/lib/kadm5/init_c.c index 91e03ada1..99b0547bd 100644 --- a/lib/kadm5/init_c.c +++ b/lib/kadm5/init_c.c @@ -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; } diff --git a/lib/kadm5/kadm5_err.et b/lib/kadm5/kadm5_err.et index 42f4069e7..63d7b3227 100644 --- a/lib/kadm5/kadm5_err.et +++ b/lib/kadm5/kadm5_err.et @@ -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" diff --git a/lib/kadm5/modify_c.c b/lib/kadm5/modify_c.c index 69888704b..a38cb33d5 100644 --- a/lib/kadm5/modify_c.c +++ b/lib/kadm5/modify_c.c @@ -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; diff --git a/lib/kadm5/private.h b/lib/kadm5/private.h index 719abff44..88c2f2823 100644 --- a/lib/kadm5/private.h +++ b/lib/kadm5/private.h @@ -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 { diff --git a/lib/kadm5/privs_c.c b/lib/kadm5/privs_c.c index 6646d8128..b6879cf0b 100644 --- a/lib/kadm5/privs_c.c +++ b/lib/kadm5/privs_c.c @@ -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; diff --git a/lib/kadm5/prune_c.c b/lib/kadm5/prune_c.c index 13b68644f..70a1dcbda 100644 --- a/lib/kadm5/prune_c.c +++ b/lib/kadm5/prune_c.c @@ -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) diff --git a/lib/kadm5/randkey_c.c b/lib/kadm5/randkey_c.c index f116ec043..ace068761 100644 --- a/lib/kadm5/randkey_c.c +++ b/lib/kadm5/randkey_c.c @@ -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; diff --git a/lib/kadm5/rename_c.c b/lib/kadm5/rename_c.c index 2e82cde9c..fb2cdc517 100644 --- a/lib/kadm5/rename_c.c +++ b/lib/kadm5/rename_c.c @@ -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; diff --git a/lib/krb5/krb5.h b/lib/krb5/krb5.h index c372bef74..0c0926415 100644 --- a/lib/krb5/krb5.h +++ b/lib/krb5/krb5.h @@ -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, diff --git a/lib/krb5/krbhst.c b/lib/krb5/krbhst.c index 2e3535bdc..a704ab6fd 100644 --- a/lib/krb5/krbhst.c +++ b/lib/krb5/krbhst.c @@ -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' */ diff --git a/lib/krb5/libkrb5-exports.def.in b/lib/krb5/libkrb5-exports.def.in index 9b29d0e02..8d9963e44 100644 --- a/lib/krb5/libkrb5-exports.def.in +++ b/lib/krb5/libkrb5-exports.def.in @@ -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 diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map index 3af6a205b..ef2441384 100644 --- a/lib/krb5/version-script.map +++ b/lib/krb5/version-script.map @@ -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;