2 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
6 * Copyright (C) 1998 by the FundsXpress, INC.
10 * Export of this software from the United States of America may require
11 * a specific license from the United States Government. It is the
12 * responsibility of any person or organization contemplating export to
13 * obtain such a license before exporting.
15 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
16 * distribute this software and its documentation for any purpose and
17 * without fee is hereby granted, provided that the above copyright
18 * notice appear in all copies and that both that copyright notice and
19 * this permission notice appear in supporting documentation, and that
20 * the name of FundsXpress. not be used in advertising or publicity pertaining
21 * to distribution of the software without specific, written prior
22 * permission. FundsXpress makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
26 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
27 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
28 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
42 #include <k5-int.h> /* for KRB5_ADM_DEFAULT_PORT */
47 #include <kadm5/admin.h>
48 #include <kadm5/kadm_rpc.h>
49 #include "client_internal.h"
51 #include <gssrpc/rpc.h>
52 #include <gssapi/gssapi.h>
53 #include <gssapi/gssapi_krb5.h>
54 #include <gssrpc/auth_gssapi.h>
56 #define ADM_CCACHE "/tmp/ovsec_adm.XXXXXX"
58 enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS };
60 static kadm5_ret_t _kadm5_init_any(char *client_name,
61 enum init_type init_type,
63 krb5_ccache ccache_in,
65 kadm5_config_params *params,
66 krb5_ui_4 struct_version,
67 krb5_ui_4 api_version,
69 void **server_handle);
72 kadm5_get_init_creds(kadm5_server_handle_t handle,
73 char *client_name, enum init_type init_type,
74 char *pass, krb5_ccache ccache_in,
75 char *svcname_in, char *realm,
76 char *full_svcname, unsigned int full_svcname_len);
79 kadm5_gic_iter(kadm5_server_handle_t handle,
80 enum init_type init_type,
82 krb5_principal client, char *pass,
83 char *svcname, char *realm,
84 char *full_svcname, unsigned int full_svcname_len);
87 kadm5_setup_gss(kadm5_server_handle_t handle,
88 kadm5_config_params *params_in,
89 char *client_name, char *full_svcname);
92 kadm5_rpc_auth(kadm5_server_handle_t handle,
93 kadm5_config_params *params_in,
94 gss_cred_id_t gss_client_creds,
95 gss_name_t gss_target);
97 kadm5_ret_t kadm5_init_with_creds(char *client_name,
100 kadm5_config_params *params,
101 krb5_ui_4 struct_version,
102 krb5_ui_4 api_version,
104 void **server_handle)
106 return _kadm5_init_any(client_name, INIT_CREDS, NULL, ccache,
107 service_name, params,
108 struct_version, api_version, db_args,
113 kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass,
115 kadm5_config_params *params,
116 krb5_ui_4 struct_version,
117 krb5_ui_4 api_version,
119 void **server_handle)
121 return _kadm5_init_any(client_name, INIT_PASS, pass, NULL,
122 service_name, params, struct_version,
123 api_version, db_args, server_handle);
126 kadm5_ret_t kadm5_init(char *client_name, char *pass,
128 kadm5_config_params *params,
129 krb5_ui_4 struct_version,
130 krb5_ui_4 api_version,
132 void **server_handle)
134 return _kadm5_init_any(client_name, INIT_PASS, pass, NULL,
135 service_name, params, struct_version,
136 api_version, db_args, server_handle);
139 kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab,
141 kadm5_config_params *params,
142 krb5_ui_4 struct_version,
143 krb5_ui_4 api_version,
145 void **server_handle)
147 return _kadm5_init_any(client_name, INIT_SKEY, keytab, NULL,
148 service_name, params, struct_version,
149 api_version, db_args, server_handle);
152 static kadm5_ret_t _kadm5_init_any(char *client_name,
153 enum init_type init_type,
155 krb5_ccache ccache_in,
157 kadm5_config_params *params_in,
158 krb5_ui_4 struct_version,
159 krb5_ui_4 api_version,
161 void **server_handle)
163 struct sockaddr_in addr;
167 char full_svcname[BUFSIZ];
170 kadm5_server_handle_t handle;
171 kadm5_config_params params_local;
176 initialize_ovk_error_table();
177 /* initialize_adb_error_table(); */
178 initialize_ovku_error_table();
180 if (! server_handle) {
184 if (! (handle = malloc(sizeof(*handle)))) {
187 if (! (handle->lhandle = malloc(sizeof(*handle)))) {
192 handle->magic_number = KADM5_SERVER_HANDLE_MAGIC;
193 handle->struct_version = struct_version;
194 handle->api_version = api_version;
196 handle->cache_name = 0;
197 handle->destroy_cache = 0;
198 *handle->lhandle = *handle;
199 handle->lhandle->api_version = KADM5_API_VERSION_2;
200 handle->lhandle->struct_version = KADM5_STRUCT_VERSION;
201 handle->lhandle->lhandle = handle->lhandle;
203 krb5_init_context(&handle->context);
205 if(client_name == NULL) {
211 * Verify the version numbers before proceeding; we can't use
212 * CHECK_HANDLE because not all fields are set yet.
214 GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION,
215 KADM5_NEW_LIB_API_VERSION);
218 * Acquire relevant profile entries. In version 2, merge values
219 * in params_in with values from profile, based on
222 * In version 1, we've given a realm (which may be NULL) instead
223 * of params_in. So use that realm, make params_in contain an
224 * empty mask, and behave like version 2.
226 memset((char *) ¶ms_local, 0, sizeof(params_local));
227 if (api_version == KADM5_API_VERSION_1) {
228 realm = params_local.realm = (char *) params_in;
230 params_local.mask = KADM5_CONFIG_REALM;
232 /* Use old AUTH_GSSAPI for version 1 protocol. */
233 params_local.mask |= KADM5_CONFIG_OLD_AUTH_GSSAPI;
234 params_in = ¶ms_local;
236 if (params_in && (params_in->mask & KADM5_CONFIG_REALM))
237 realm = params_in->realm;
242 #define ILLEGAL_PARAMS (KADM5_CONFIG_DBNAME | KADM5_CONFIG_ADBNAME | \
243 KADM5_CONFIG_ADB_LOCKFILE | \
244 KADM5_CONFIG_ACL_FILE | KADM5_CONFIG_DICT_FILE \
245 | KADM5_CONFIG_ADMIN_KEYTAB | \
246 KADM5_CONFIG_STASH_FILE | \
247 KADM5_CONFIG_MKEY_NAME | KADM5_CONFIG_ENCTYPE \
248 | KADM5_CONFIG_MAX_LIFE | \
249 KADM5_CONFIG_MAX_RLIFE | \
250 KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_FLAGS | \
251 KADM5_CONFIG_ENCTYPES | KADM5_CONFIG_MKEY_FROM_KBD)
253 if (params_in && params_in->mask & ILLEGAL_PARAMS) {
255 return KADM5_BAD_CLIENT_PARAMS;
258 if ((code = kadm5_get_config_params(handle->context,
259 DEFAULT_PROFILE_PATH,
263 krb5_free_context(handle->context);
268 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \
269 KADM5_CONFIG_ADMIN_SERVER | \
270 KADM5_CONFIG_KADMIND_PORT)
272 if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
273 krb5_free_context(handle->context);
275 return KADM5_MISSING_KRB5_CONF_PARAMS;
279 * Get credentials. Also does some fallbacks in case kadmin/fqdn
280 * principal doesn't exist.
282 code = kadm5_get_init_creds(handle, client_name, init_type, pass,
283 ccache_in, service_name, realm,
284 full_svcname, sizeof(full_svcname));
288 * We have ticket; open the RPC connection.
291 hp = gethostbyname(handle->params.admin_server);
292 if (hp == (struct hostent *) NULL) {
293 code = KADM5_BAD_SERVER_NAME;
297 memset(&addr, 0, sizeof(addr));
298 addr.sin_family = hp->h_addrtype;
299 (void) memcpy((char *) &addr.sin_addr, (char *) hp->h_addr,
300 sizeof(addr.sin_addr));
301 addr.sin_port = htons((u_short) handle->params.kadmind_port);
305 handle->clnt = clnttcp_create(&addr, KADM, KADMVERS, &fd, 0, 0);
306 if (handle->clnt == NULL) {
307 code = KADM5_RPC_ERROR;
309 clnt_pcreateerror("clnttcp_create");
313 handle->lhandle->clnt = handle->clnt;
315 /* now that handle->clnt is set, we can check the handle */
316 if ((code = _kadm5_check_handle((void *) handle)))
320 * The RPC connection is open; establish the GSS-API
321 * authentication context.
323 code = kadm5_setup_gss(handle, params_in, client_name, full_svcname);
327 r = init_1(&handle->api_version, handle->clnt);
329 code = KADM5_RPC_ERROR;
331 clnt_perror(handle->clnt, "init_1 null resp");
340 *server_handle = (void *) handle;
346 * Note that it is illegal for this code to execute if "handle"
347 * has not been allocated and initialized. I.e., don't use "goto
348 * error" before the block of code at the top of the function
349 * that allocates and initializes "handle".
351 if (handle->cache_name)
352 free(handle->cache_name);
353 if(handle->clnt && handle->clnt->cl_auth)
354 AUTH_DESTROY(handle->clnt->cl_auth);
356 clnt_destroy(handle->clnt);
366 * kadm5_get_init_creds
368 * Get initial credentials for authenticating to server. Perform
369 * fallback from kadmin/fqdn to kadmin/admin if svcname_in is NULL.
372 kadm5_get_init_creds(kadm5_server_handle_t handle,
373 char *client_name, enum init_type init_type,
374 char *pass, krb5_ccache ccache_in,
375 char *svcname_in, char *realm,
376 char *full_svcname, unsigned int full_svcname_len)
379 krb5_principal client;
381 char svcname[BUFSIZ];
385 /* NULL svcname means use host-based. */
386 if (svcname_in == NULL) {
387 code = kadm5_get_admin_service_name(handle->context,
388 handle->params.realm,
389 svcname, sizeof(svcname));
391 code = KADM5_MISSING_KRB5_CONF_PARAMS;
395 strncpy(svcname, svcname_in, sizeof(svcname));
396 svcname[sizeof(svcname)-1] = '\0';
399 * Acquire a service ticket for svcname@realm in the name of
400 * client_name, using password pass (which could be NULL), and
401 * create a ccache to store them in. If INIT_CREDS, use the
402 * ccache we were provided instead.
404 code = krb5_parse_name(handle->context, client_name, &client);
408 if (init_type == INIT_CREDS) {
410 handle->cache_name = (char *)
411 malloc(strlen(krb5_cc_get_type(handle->context, ccache)) +
412 strlen(krb5_cc_get_name(handle->context, ccache)) + 2);
413 if (handle->cache_name == NULL) {
417 sprintf(handle->cache_name, "%s:%s",
418 krb5_cc_get_type(handle->context, ccache),
419 krb5_cc_get_name(handle->context, ccache));
421 static int counter = 0;
423 handle->cache_name = malloc(sizeof("MEMORY:kadm5_")
424 + 3*sizeof(counter));
425 sprintf(handle->cache_name, "MEMORY:kadm5_%u", counter++);
427 code = krb5_cc_resolve(handle->context, handle->cache_name,
432 code = krb5_cc_initialize (handle->context, ccache, client);
436 handle->destroy_cache = 1;
438 handle->lhandle->cache_name = handle->cache_name;
440 code = kadm5_gic_iter(handle, init_type, ccache,
441 client, pass, svcname, realm,
442 full_svcname, full_svcname_len);
443 if ((code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
444 || code == KRB5_CC_NOTFOUND) && svcname_in == NULL) {
445 /* Retry with old host-independent service princpal. */
446 code = kadm5_gic_iter(handle, init_type, ccache,
448 KADM5_ADMIN_SERVICE, realm,
449 full_svcname, full_svcname_len);
451 /* Improved error messages */
452 if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD;
453 if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
454 code = KADM5_SECURE_PRINC_MISSING;
457 if (ccache != NULL && init_type != INIT_CREDS)
458 krb5_cc_close(handle->context, ccache);
465 * Perform one iteration of attempting to get credentials. This
466 * includes searching existing ccache for requested service if
470 kadm5_gic_iter(kadm5_server_handle_t handle,
471 enum init_type init_type,
473 krb5_principal client, char *pass,
474 char *svcname, char *realm,
475 char *full_svcname, unsigned int full_svcname_len)
480 krb5_get_init_creds_opt opt;
481 krb5_creds mcreds, outcreds;
483 ctx = handle->context;
485 memset(full_svcname, 0, full_svcname_len);
486 memset(&opt, 0, sizeof(opt));
487 memset(&mcreds, 0, sizeof(mcreds));
488 memset(&outcreds, 0, sizeof(outcreds));
492 if ((strlen(svcname) + strlen(realm) + 1) >= full_svcname_len)
494 sprintf(full_svcname, "%s@%s", svcname, realm);
496 /* krb5_princ_realm(client) is not null terminated */
497 if ((strlen(svcname) + krb5_princ_realm(ctx, client)->length + 1)
501 strcpy(full_svcname, svcname);
502 strcat(full_svcname, "@");
503 strncat(full_svcname,
504 krb5_princ_realm(ctx, client)->data,
505 krb5_princ_realm(ctx, client)->length);
508 if (init_type != INIT_CREDS)
509 krb5_get_init_creds_opt_init(&opt);
511 if (init_type == INIT_PASS) {
512 code = krb5_get_init_creds_password(ctx, &outcreds, client, pass,
518 } else if (init_type == INIT_SKEY) {
520 code = krb5_kt_resolve(ctx, pass, &kt);
524 code = krb5_get_init_creds_keytab(ctx, &outcreds, client, kt,
525 0, full_svcname, &opt);
527 krb5_kt_close(ctx, kt);
530 } else if (init_type == INIT_CREDS) {
531 mcreds.client = client;
532 code = krb5_parse_name(ctx, full_svcname, &mcreds.server);
535 code = krb5_cc_retrieve_cred(ctx, ccache, 0,
537 krb5_free_principal(ctx, mcreds.server);
541 if (init_type != INIT_CREDS) {
542 /* Caller has initialized ccache. */
543 code = krb5_cc_store_cred(ctx, ccache, &outcreds);
548 krb5_free_cred_contents(ctx, &outcreds);
555 * Acquire GSSAPI credentials and set up RPC auth flavor.
558 kadm5_setup_gss(kadm5_server_handle_t handle,
559 kadm5_config_params *params_in,
560 char *client_name, char *full_svcname)
563 OM_uint32 gssstat, minor_stat;
565 gss_name_t gss_client;
566 gss_name_t gss_target;
567 gss_cred_id_t gss_client_creds;
568 const char *c_ccname_orig;
571 code = KADM5_GSS_ERROR;
572 gss_client_creds = GSS_C_NO_CREDENTIAL;
575 /* Temporarily use the kadm5 cache. */
576 gssstat = gss_krb5_ccache_name(&minor_stat, handle->cache_name,
578 if (gssstat != GSS_S_COMPLETE) {
579 code = KADM5_GSS_ERROR;
583 ccname_orig = strdup(c_ccname_orig);
587 buf.value = full_svcname;
588 buf.length = strlen((char *)buf.value) + 1;
589 gssstat = gss_import_name(&minor_stat, &buf,
590 (gss_OID) gss_nt_krb5_name, &gss_target);
591 if (gssstat != GSS_S_COMPLETE) {
592 code = KADM5_GSS_ERROR;
596 buf.value = client_name;
597 buf.length = strlen((char *)buf.value) + 1;
598 gssstat = gss_import_name(&minor_stat, &buf,
599 (gss_OID) gss_nt_krb5_name, &gss_client);
600 if (gssstat != GSS_S_COMPLETE) {
601 code = KADM5_GSS_ERROR;
605 gssstat = gss_acquire_cred(&minor_stat, gss_client, 0,
606 GSS_C_NULL_OID_SET, GSS_C_INITIATE,
607 &gss_client_creds, NULL, NULL);
608 if (gssstat != GSS_S_COMPLETE) {
609 code = KADM5_GSS_ERROR;
614 * Do actual creation of RPC auth handle. Implements auth flavor
617 kadm5_rpc_auth(handle, params_in, gss_client_creds, gss_target);
620 if (gss_client_creds != GSS_C_NO_CREDENTIAL)
621 (void) gss_release_cred(&minor_stat, &gss_client_creds);
624 gss_release_name(&minor_stat, &gss_client);
626 gss_release_name(&minor_stat, &gss_target);
628 /* Revert to prior gss_krb5 ccache. */
630 gssstat = gss_krb5_ccache_name(&minor_stat, ccname_orig, NULL);
632 return KADM5_GSS_ERROR;
636 gssstat = gss_krb5_ccache_name(&minor_stat, NULL, NULL);
638 return KADM5_GSS_ERROR;
642 if (handle->clnt->cl_auth == NULL) {
643 return KADM5_GSS_ERROR;
651 * Create RPC auth handle. Do auth flavor fallback if needed.
654 kadm5_rpc_auth(kadm5_server_handle_t handle,
655 kadm5_config_params *params_in,
656 gss_cred_id_t gss_client_creds,
657 gss_name_t gss_target)
659 OM_uint32 gssstat, minor_stat;
660 struct rpc_gss_sec sec;
662 /* Allow unauthenticated option for testing. */
663 if (params_in != NULL && (params_in->mask & KADM5_CONFIG_NO_AUTH))
666 /* Use RPCSEC_GSS by default. */
667 if (params_in == NULL ||
668 !(params_in->mask & KADM5_CONFIG_OLD_AUTH_GSSAPI)) {
669 sec.mech = gss_mech_krb5;
670 sec.qop = GSS_C_QOP_DEFAULT;
671 sec.svc = RPCSEC_GSS_SVC_PRIVACY;
672 sec.cred = gss_client_creds;
673 sec.req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
675 handle->clnt->cl_auth = authgss_create(handle->clnt,
677 if (handle->clnt->cl_auth != NULL)
681 if (params_in != NULL && (params_in->mask & KADM5_CONFIG_AUTH_NOFALLBACK))
684 /* Fall back to old AUTH_GSSAPI. */
685 handle->clnt->cl_auth = auth_gssapi_create(handle->clnt,
690 (gss_OID) gss_mech_krb5,
693 0, NULL, NULL, NULL);
697 kadm5_destroy(void *server_handle)
699 krb5_ccache ccache = NULL;
701 kadm5_server_handle_t handle =
702 (kadm5_server_handle_t) server_handle;
704 CHECK_HANDLE(server_handle);
706 if (handle->destroy_cache && handle->cache_name) {
707 if ((code = krb5_cc_resolve(handle->context,
708 handle->cache_name, &ccache)) == 0)
709 code = krb5_cc_destroy (handle->context, ccache);
711 if (handle->cache_name)
712 free(handle->cache_name);
713 if (handle->clnt && handle->clnt->cl_auth)
714 AUTH_DESTROY(handle->clnt->cl_auth);
716 clnt_destroy(handle->clnt);
718 free (handle->lhandle);
720 kadm5_free_config_params(handle->context, &handle->params);
721 krb5_free_context(handle->context);
723 handle->magic_number = 0;
728 /* not supported on client */
729 kadm5_ret_t kadm5_lock(void *server_handle)
734 /* not supported on client */
735 kadm5_ret_t kadm5_unlock(void *server_handle)
740 kadm5_ret_t kadm5_flush(void *server_handle)
745 int _kadm5_check_handle(void *handle)
747 CHECK_HANDLE(handle);