8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / krb5 / kadm5 / clnt / client_init.c
blob15fdbebea8739efbd5e047eccaf265709f2833ff
1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
8 /*
9 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
13 * Copyright (C) 1998 by the FundsXpress, INC.
15 * All rights reserved.
17 * Export of this software from the United States of America may require
18 * a specific license from the United States Government. It is the
19 * responsibility of any person or organization contemplating export to
20 * obtain such a license before exporting.
22 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
23 * distribute this software and its documentation for any purpose and
24 * without fee is hereby granted, provided that the above copyright
25 * notice appear in all copies and that both that copyright notice and
26 * this permission notice appear in supporting documentation, and that
27 * the name of FundsXpress. not be used in advertising or publicity pertaining
28 * to distribution of the software without specific, written prior
29 * permission. FundsXpress makes no representations about the suitability of
30 * this software for any purpose. It is provided "as is" without express
31 * or implied warranty.
33 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
34 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
35 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
38 #include <stdio.h>
39 #include <netdb.h>
40 #include "autoconf.h"
41 #ifdef HAVE_MEMORY_H
42 #include <memory.h>
43 #endif
44 #include <string.h>
45 #include <com_err.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <k5-int.h> /* for KRB5_ADM_DEFAULT_PORT */
50 #include <krb5.h>
51 #ifdef __STDC__
52 #include <stdlib.h>
53 #endif
54 #include <libintl.h>
56 #include <kadm5/admin.h>
57 #include <kadm5/kadm_rpc.h>
58 #include "client_internal.h"
60 #include <syslog.h>
61 #include <gssapi/gssapi.h>
62 #include <gssapi_krb5.h>
63 #include <gssapiP_krb5.h>
64 #include <rpc/clnt.h>
66 #include <iprop_hdr.h>
67 #include "iprop.h"
69 #define ADM_CCACHE "/tmp/ovsec_adm.XXXXXX"
71 static int old_auth_gssapi = 0;
72 /* connection timeout to kadmind in seconds */
73 #define KADMIND_CONNECT_TIMEOUT 25
75 int _kadm5_check_handle();
77 enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS };
79 static kadm5_ret_t _kadm5_init_any(char *client_name,
80 enum init_type init_type,
81 char *pass,
82 krb5_ccache ccache_in,
83 char *service_name,
84 kadm5_config_params *params,
85 krb5_ui_4 struct_version,
86 krb5_ui_4 api_version,
87 char **db_args,
88 void **server_handle);
90 kadm5_ret_t kadm5_init_with_creds(char *client_name,
91 krb5_ccache ccache,
92 char *service_name,
93 kadm5_config_params *params,
94 krb5_ui_4 struct_version,
95 krb5_ui_4 api_version,
96 char **db_args,
97 void **server_handle)
99 return _kadm5_init_any(client_name, INIT_CREDS, NULL, ccache,
100 service_name, params,
101 struct_version, api_version, db_args,
102 server_handle);
106 kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass,
107 char *service_name,
108 kadm5_config_params *params,
109 krb5_ui_4 struct_version,
110 krb5_ui_4 api_version,
111 char **db_args,
112 void **server_handle)
114 return _kadm5_init_any(client_name, INIT_PASS, pass, NULL,
115 service_name, params, struct_version,
116 api_version, db_args, server_handle);
119 kadm5_ret_t kadm5_init(char *client_name, char *pass,
120 char *service_name,
121 kadm5_config_params *params,
122 krb5_ui_4 struct_version,
123 krb5_ui_4 api_version,
124 char **db_args,
125 void **server_handle)
127 return _kadm5_init_any(client_name, INIT_PASS, pass, NULL,
128 service_name, params, struct_version,
129 api_version, db_args, server_handle);
132 kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab,
133 char *service_name,
134 kadm5_config_params *params,
135 krb5_ui_4 struct_version,
136 krb5_ui_4 api_version,
137 char **db_args,
138 void **server_handle)
140 return _kadm5_init_any(client_name, INIT_SKEY, keytab, NULL,
141 service_name, params, struct_version,
142 api_version, db_args, server_handle);
145 krb5_error_code kadm5_free_config_params();
147 static void
148 display_status_1(m, code, type, mech)
149 char *m;
150 OM_uint32 code;
151 int type;
152 const gss_OID mech;
154 OM_uint32 maj_stat, min_stat;
155 gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
156 OM_uint32 msg_ctx;
158 msg_ctx = 0;
159 ADMIN_LOG(LOG_ERR, "%s\n", m);
160 /* LINTED */
161 while (1) {
162 maj_stat = gss_display_status(&min_stat, code,
163 type, mech,
164 &msg_ctx, &msg);
165 if (maj_stat != GSS_S_COMPLETE) {
166 syslog(LOG_ERR,
167 dgettext(TEXT_DOMAIN,
168 "error in gss_display_status"
169 " called from <%s>\n"), m);
170 break;
171 } else
172 syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
173 "GSS-API error : %s\n"),
175 syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
176 "GSS-API error : %s\n"),
177 (char *)msg.value);
178 if (msg.length != 0)
179 (void) gss_release_buffer(&min_stat, &msg);
181 if (!msg_ctx)
182 break;
187 * Function: display_status
189 * Purpose: displays GSS-API messages
191 * Arguments:
193 * msg a string to be displayed with the message
194 * maj_stat the GSS-API major status code
195 * min_stat the GSS-API minor status code
196 * mech kerberos mech
197 * Effects:
199 * The GSS-API messages associated with maj_stat and min_stat are
200 * displayed on stderr, each preceeded by "GSS-API error <msg>: " and
201 * followed by a newline.
203 void
204 display_status(msg, maj_stat, min_stat, mech)
205 char *msg;
206 OM_uint32 maj_stat;
207 OM_uint32 min_stat;
208 char *mech;
210 gss_OID mech_oid;
212 if (!rpc_gss_mech_to_oid(mech, (rpc_gss_OID *)&mech_oid)) {
213 ADMIN_LOG(LOG_ERR,
214 dgettext(TEXT_DOMAIN,
215 "Invalid mechanism oid <%s>"), mech);
216 return;
219 display_status_1(msg, maj_stat, GSS_C_GSS_CODE, mech_oid);
220 display_status_1(msg, min_stat, GSS_C_MECH_CODE, mech_oid);
224 * Open an fd for the given address and connect asynchronously. Wait
225 * KADMIND_CONNECT_TIMEOUT seconds or till it succeeds. If it succeeds
226 * change fd to blocking and return it, else return -1.
228 static int
229 get_connection(struct netconfig *nconf, struct netbuf netaddr)
231 struct t_info tinfo;
232 struct t_call sndcall;
233 struct t_call *rcvcall = NULL;
234 int connect_time;
235 int flags;
236 int fd;
238 (void) memset(&tinfo, 0, sizeof (tinfo));
240 /* we'l open with O_NONBLOCK and avoid an fcntl */
241 fd = t_open(nconf->nc_device, O_RDWR | O_NONBLOCK, &tinfo);
242 if (fd == -1) {
243 return (-1);
246 if (t_bind(fd, (struct t_bind *)NULL, (struct t_bind *)NULL) == -1) {
247 (void) close(fd);
248 return (-1);
251 /* we can't connect unless fd is in IDLE state */
252 if (t_getstate(fd) != T_IDLE) {
253 (void) close(fd);
254 return (-1);
257 /* setup connect parameters */
258 netaddr.len = netaddr.maxlen = __rpc_get_a_size(tinfo.addr);
259 sndcall.addr = netaddr;
260 sndcall.opt.len = sndcall.udata.len = 0;
262 /* we wait for KADMIND_CONNECT_TIMEOUT seconds from now */
263 connect_time = time(NULL) + KADMIND_CONNECT_TIMEOUT;
264 if (t_connect(fd, &sndcall, rcvcall) != 0) {
265 if (t_errno != TNODATA) {
266 (void) close(fd);
267 return (-1);
271 /* loop till success or timeout */
272 for (;;) {
273 if (t_rcvconnect(fd, rcvcall) == 0)
274 break;
276 if (t_errno != TNODATA || time(NULL) > connect_time) {
277 /* we have either timed out or caught an error */
278 (void) close(fd);
279 if (rcvcall != NULL)
280 t_free((char *)rcvcall, T_CALL);
281 return (-1);
283 sleep(1);
286 /* make the fd blocking (synchronous) */
287 flags = fcntl(fd, F_GETFL, 0);
288 (void) fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
289 if (rcvcall != NULL)
290 t_free((char *)rcvcall, T_CALL);
291 return (fd);
295 * Open an RPCSEC_GSS connection and
296 * get a client handle to use for future RPCSEC calls.
298 * This function is only used when changing passwords and
299 * the kpasswd_protocol is RPCSEC_GSS
301 static int
302 _kadm5_initialize_rpcsec_gss_handle(kadm5_server_handle_t handle,
303 char *client_name,
304 char *service_name)
306 struct netbuf netaddr;
307 struct hostent *hp;
308 int fd;
309 struct sockaddr_in addr;
310 struct sockaddr_in *sin;
311 struct netconfig *nconf;
312 int code = 0;
313 generic_ret *r;
314 char *ccname_orig;
315 char *iprop_svc;
316 boolean_t iprop_enable = B_FALSE;
317 char mech[] = "kerberos_v5";
318 gss_OID mech_oid;
319 gss_OID_set_desc oid_set;
320 gss_name_t gss_client;
321 gss_buffer_desc input_name;
322 gss_cred_id_t gss_client_creds = GSS_C_NO_CREDENTIAL;
323 rpc_gss_options_req_t options_req;
324 rpc_gss_options_ret_t options_ret;
325 rpc_gss_service_t service = rpc_gss_svc_privacy;
326 OM_uint32 gssstat, minor_stat;
327 void *handlep;
328 enum clnt_stat rpc_err_code;
329 char *server = handle->params.admin_server;
332 * Try to find the kpasswd_server first if this is for the changepw
333 * service. If defined then it should be resolvable else return error.
335 if (strncmp(service_name, KADM5_CHANGEPW_HOST_SERVICE,
336 strlen(KADM5_CHANGEPW_HOST_SERVICE)) == 0) {
337 if (handle->params.kpasswd_server != NULL)
338 server = handle->params.kpasswd_server;
340 hp = gethostbyname(server);
341 if (hp == (struct hostent *)NULL) {
342 code = KADM5_BAD_SERVER_NAME;
343 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
344 "bad server name\n"));
345 goto cleanup;
348 memset(&addr, 0, sizeof (addr));
349 addr.sin_family = hp->h_addrtype;
350 (void) memcpy((char *)&addr.sin_addr, (char *)hp->h_addr,
351 sizeof (addr.sin_addr));
352 addr.sin_port = htons((ushort_t)handle->params.kadmind_port);
353 sin = &addr;
354 #ifdef DEBUG
355 printf("kadmin_port %d\n", handle->params.kadmind_port);
356 printf("addr: sin_port: %d, sin_family: %d, sin_zero %s\n",
357 addr.sin_port, addr.sin_family, addr.sin_zero);
358 printf("sin_addr %d:%d\n", addr.sin_addr.S_un.S_un_w.s_w1,
359 addr.sin_addr.S_un.S_un_w.s_w2);
360 #endif
361 if ((handlep = setnetconfig()) == (void *) NULL) {
362 (void) syslog(LOG_ERR,
363 dgettext(TEXT_DOMAIN,
364 "cannot get any transport information"));
365 goto error;
368 while (nconf = getnetconfig(handlep)) {
369 if ((nconf->nc_semantics == NC_TPI_COTS_ORD) &&
370 (strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
371 (strcmp(nconf->nc_proto, NC_TCP) == 0))
372 break;
375 if (nconf == (struct netconfig *)NULL)
376 goto error;
378 /* Transform addr to netbuf */
379 (void) memset(&netaddr, 0, sizeof (netaddr));
380 netaddr.buf = (char *)sin;
382 /* get an fd connected to the given address */
383 fd = get_connection(nconf, netaddr);
384 if (fd == -1) {
385 syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
386 "unable to open connection to ADMIN server "
387 "(t_error %i)"), t_errno);
388 code = KADM5_RPC_ERROR;
389 goto error;
392 #ifdef DEBUG
393 printf("fd: %d, KADM: %d, KADMVERS %d\n", fd, KADM, KADMVERS);
394 printf("nconf: nc_netid: %s, nc_semantics: %d, nc_flag: %d, "
395 "nc_protofmly: %s\n",
396 nconf->nc_netid, nconf->nc_semantics, nconf->nc_flag,
397 nconf->nc_protofmly);
398 printf("nc_proto: %s, nc_device: %s, nc_nlookups: %d, nc_used: %d\n",
399 nconf->nc_proto, nconf->nc_device, nconf->nc_nlookups,
400 nconf->nc_unused);
401 printf("netaddr: maxlen %d, buf: %s, len: %d\n", netaddr.maxlen,
402 netaddr.buf, netaddr.len);
403 #endif
405 * Tell clnt_tli_create that given fd is already connected
407 * If the service_name and client_name are iprop-centric,
408 * we need to clnt_tli_create to the appropriate RPC prog
410 iprop_svc = strdup(KIPROP_SVC_NAME);
411 if (iprop_svc == NULL)
412 return (ENOMEM);
414 if ((strstr(service_name, iprop_svc) != NULL) &&
415 (strstr(client_name, iprop_svc) != NULL)) {
416 iprop_enable = B_TRUE;
417 handle->clnt = clnt_tli_create(fd, nconf, NULL,
418 KRB5_IPROP_PROG, KRB5_IPROP_VERS, 0, 0);
420 else
421 handle->clnt = clnt_tli_create(fd, nconf, NULL,
422 KADM, KADMVERS, 0, 0);
424 if (iprop_svc)
425 free(iprop_svc);
427 if (handle->clnt == NULL) {
428 syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
429 "clnt_tli_create failed\n"));
430 code = KADM5_RPC_ERROR;
431 (void) close(fd);
432 goto error;
435 * The rpc-handle was created on an fd opened and connected
436 * by us, so we have to explicitly tell rpc to close it.
438 if (clnt_control(handle->clnt, CLSET_FD_CLOSE, NULL) != TRUE) {
439 clnt_pcreateerror("ERROR:");
440 syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
441 "clnt_control failed to set CLSET_FD_CLOSE"));
442 code = KADM5_RPC_ERROR;
443 (void) close(fd);
444 goto error;
447 handle->lhandle->clnt = handle->clnt;
449 /* now that handle->clnt is set, we can check the handle */
450 if (code = _kadm5_check_handle((void *) handle))
451 goto error;
454 * The RPC connection is open; establish the GSS-API
455 * authentication context.
457 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
458 "have an rpc connection open\n"));
459 /* use the kadm5 cache */
460 ccname_orig = getenv("KRB5CCNAME");
461 if (ccname_orig)
462 ccname_orig = strdup(ccname_orig);
464 (void) krb5_setenv("KRB5CCNAME", handle->cache_name, 1);
466 ADMIN_LOG(LOG_ERR,
467 dgettext(TEXT_DOMAIN,
468 "current credential cache: %s"), handle->cache_name);
469 input_name.value = client_name;
470 input_name.length = strlen((char *)input_name.value) + 1;
471 gssstat = gss_import_name(&minor_stat, &input_name,
472 (gss_OID)gss_nt_krb5_name, &gss_client);
473 if (gssstat != GSS_S_COMPLETE) {
474 code = KADM5_GSS_ERROR;
475 ADMIN_LOGO(LOG_ERR,
476 dgettext(TEXT_DOMAIN,
477 "gss_import_name failed for client name\n"));
478 goto error;
481 if (!rpc_gss_mech_to_oid(mech, (rpc_gss_OID *)&mech_oid)) {
482 ADMIN_LOG(LOG_ERR,
483 dgettext(TEXT_DOMAIN,
484 "Invalid mechanism oid <%s>"), mech);
485 goto error;
488 oid_set.count = 1;
489 oid_set.elements = mech_oid;
491 gssstat = gss_acquire_cred(&minor_stat, gss_client, 0,
492 &oid_set, GSS_C_INITIATE,
493 &gss_client_creds, NULL, NULL);
494 (void) gss_release_name(&minor_stat, &gss_client);
495 if (gssstat != GSS_S_COMPLETE) {
496 code = KADM5_GSS_ERROR;
497 ADMIN_LOG(LOG_ERR,
498 dgettext(TEXT_DOMAIN,
499 "could not acquire credentials, "
500 "major error code: %d\n"), gssstat);
501 goto error;
503 handle->my_cred = gss_client_creds;
504 options_req.my_cred = gss_client_creds;
505 options_req.req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
506 options_req.time_req = 0;
507 options_req.input_channel_bindings = NULL;
508 #ifndef INIT_TEST
509 handle->clnt->cl_auth = rpc_gss_seccreate(handle->clnt,
510 service_name,
511 mech,
512 service,
513 NULL,
514 &options_req,
515 &options_ret);
516 #endif /* ! INIT_TEST */
518 if (ccname_orig) {
519 (void) krb5_setenv("KRB5CCNAME", ccname_orig, 1);
520 free(ccname_orig);
521 } else
522 (void) krb5_unsetenv("KRB5CCNAME");
524 if (handle->clnt->cl_auth == NULL) {
525 code = KADM5_GSS_ERROR;
526 display_status(dgettext(TEXT_DOMAIN,
527 "rpc_gss_seccreate failed\n"),
528 options_ret.major_status,
529 options_ret.minor_status,
530 mech);
531 goto error;
535 * Bypass the remainder of the code and return straightaway
536 * if the gss service requested is kiprop
538 if (iprop_enable == B_TRUE) {
539 code = 0;
540 goto cleanup;
543 r = init_2(&handle->api_version, handle->clnt);
544 /* Solaris Kerberos: 163 resync */
545 if (r == NULL) {
546 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
547 "error during admin api initialization\n"));
548 code = KADM5_RPC_ERROR;
549 goto error;
552 if (r->code) {
553 code = r->code;
554 ADMIN_LOG(LOG_ERR,
555 dgettext(TEXT_DOMAIN,
556 "error during admin api initialization: %d\n"),
557 r->code);
558 goto error;
560 error:
561 cleanup:
563 if (handlep != (void *) NULL)
564 (void) endnetconfig(handlep);
566 * gss_client_creds is freed only when there is an error condition,
567 * given that rpc_gss_seccreate() will assign the cred pointer to the
568 * my_cred member in the auth handle's private data structure.
570 if (code && (gss_client_creds != GSS_C_NO_CREDENTIAL))
571 (void) gss_release_cred(&minor_stat, &gss_client_creds);
573 return (code);
576 static kadm5_ret_t _kadm5_init_any(char *client_name,
577 enum init_type init_type,
578 char *pass,
579 krb5_ccache ccache_in,
580 char *service_name,
581 kadm5_config_params *params_in,
582 krb5_ui_4 struct_version,
583 krb5_ui_4 api_version,
584 char **db_args,
585 void **server_handle)
587 int i;
588 krb5_creds creds;
589 krb5_ccache ccache = NULL;
590 krb5_timestamp now;
591 OM_uint32 gssstat, minor_stat;
592 kadm5_server_handle_t handle;
593 kadm5_config_params params_local;
594 int code = 0;
595 krb5_get_init_creds_opt opt;
596 gss_buffer_desc input_name;
597 krb5_error_code kret;
598 krb5_int32 starttime;
599 char *server = NULL;
600 krb5_principal serverp = NULL, clientp = NULL;
601 krb5_principal saved_server = NULL;
602 bool_t cpw = FALSE;
604 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
605 "entering kadm5_init_any\n"));
606 if (! server_handle) {
607 return EINVAL;
610 if (! (handle = malloc(sizeof(*handle)))) {
611 return ENOMEM;
613 if (! (handle->lhandle = malloc(sizeof(*handle)))) {
614 free(handle);
615 return ENOMEM;
618 handle->magic_number = KADM5_SERVER_HANDLE_MAGIC;
619 handle->struct_version = struct_version;
620 handle->api_version = api_version;
621 handle->clnt = 0;
622 handle->cache_name = 0;
623 handle->destroy_cache = 0;
624 *handle->lhandle = *handle;
625 handle->lhandle->api_version = KADM5_API_VERSION_2;
626 handle->lhandle->struct_version = KADM5_STRUCT_VERSION;
627 handle->lhandle->lhandle = handle->lhandle;
629 kret = krb5_init_context(&handle->context);
630 if (kret) {
631 free(handle->lhandle);
632 free(handle);
633 return (kret);
636 if(service_name == NULL || client_name == NULL) {
637 krb5_free_context(handle->context);
638 free(handle->lhandle);
639 free(handle);
640 return EINVAL;
642 memset((char *) &creds, 0, sizeof(creds));
645 * Verify the version numbers before proceeding; we can't use
646 * CHECK_HANDLE because not all fields are set yet.
648 GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION,
649 KADM5_NEW_LIB_API_VERSION);
652 * Acquire relevant profile entries. In version 2, merge values
653 * in params_in with values from profile, based on
654 * params_in->mask.
656 * In version 1, we've given a realm (which may be NULL) instead
657 * of params_in. So use that realm, make params_in contain an
658 * empty mask, and behave like version 2.
660 memset((char *) &params_local, 0, sizeof(params_local));
661 if (api_version == KADM5_API_VERSION_1) {
662 if (params_in)
663 params_local.mask = KADM5_CONFIG_REALM;
664 params_in = &params_local;
667 #define ILLEGAL_PARAMS ( \
668 KADM5_CONFIG_ACL_FILE | KADM5_CONFIG_ADB_LOCKFILE | \
669 KADM5_CONFIG_DBNAME | KADM5_CONFIG_ADBNAME | \
670 KADM5_CONFIG_DICT_FILE | KADM5_CONFIG_ADMIN_KEYTAB | \
671 KADM5_CONFIG_STASH_FILE | KADM5_CONFIG_MKEY_NAME | \
672 KADM5_CONFIG_ENCTYPE | KADM5_CONFIG_MAX_LIFE | \
673 KADM5_CONFIG_MAX_RLIFE | KADM5_CONFIG_EXPIRATION | \
674 KADM5_CONFIG_FLAGS | KADM5_CONFIG_ENCTYPES | \
675 KADM5_CONFIG_MKEY_FROM_KBD)
677 if (params_in && params_in->mask & ILLEGAL_PARAMS) {
678 krb5_free_context(handle->context);
679 free(handle->lhandle);
680 free(handle);
681 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN,
682 "bad client parameters, returning %d"),
683 KADM5_BAD_CLIENT_PARAMS);
684 return KADM5_BAD_CLIENT_PARAMS;
687 if ((code = kadm5_get_config_params(handle->context, 0,
688 params_in, &handle->params))) {
689 krb5_free_context(handle->context);
690 free(handle->lhandle);
691 free(handle);
692 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN,
693 "failed to get config_params, return: %d\n"), code);
694 return(code);
697 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \
698 KADM5_CONFIG_ADMIN_SERVER | \
699 KADM5_CONFIG_KADMIND_PORT)
700 #define KPW_REQUIRED_PARAMS (KADM5_CONFIG_REALM | \
701 KADM5_CONFIG_KPASSWD_SERVER | \
702 KADM5_CONFIG_KPASSWD_PORT)
704 if (((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) &&
705 ((handle->params.mask & KPW_REQUIRED_PARAMS) != KPW_REQUIRED_PARAMS)) {
706 (void) kadm5_free_config_params(handle->context,
707 &handle->params);
708 krb5_free_context(handle->context);
709 free(handle->lhandle);
710 free(handle);
711 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
712 "missing config parameters\n"));
713 return KADM5_MISSING_KRB5_CONF_PARAMS;
717 * Acquire a service ticket for service_name@realm in the name of
718 * client_name, using password pass (which could be NULL), and
719 * create a ccache to store them in. If INIT_CREDS, use the
720 * ccache we were provided instead.
722 if ((code = krb5_parse_name(handle->context, client_name,
723 &creds.client))) {
724 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
725 "could not parse client name\n"));
726 goto error;
728 clientp = creds.client;
730 if (strncmp(service_name, KADM5_CHANGEPW_HOST_SERVICE,
731 strlen(KADM5_CHANGEPW_HOST_SERVICE)) == 0)
732 cpw = TRUE;
734 if (init_type == INIT_PASS &&
735 handle->params.kpasswd_protocol == KRB5_CHGPWD_CHANGEPW_V2 &&
736 cpw == TRUE) {
738 * The 'service_name' is constructed by the caller
739 * but its done before the parameter which determines
740 * the kpasswd_protocol is found. The servers that
741 * support the SET/CHANGE password protocol expect
742 * a slightly different service principal than
743 * the normal SEAM kadmind so construct the correct
744 * name here and then forget it.
746 char *newsvcname = NULL;
747 newsvcname = malloc(strlen(KADM5_CHANGEPW_SERVICE) +
748 strlen(handle->params.realm) + 2);
749 if (newsvcname == NULL) {
750 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
751 "could not malloc\n"));
752 code = ENOMEM;
753 goto error;
755 sprintf(newsvcname, "%s@%s", KADM5_CHANGEPW_SERVICE,
756 handle->params.realm);
758 if ((code = krb5_parse_name(handle->context, newsvcname,
759 &creds.server))) {
760 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
761 "could not parse server "
762 "name\n"));
763 free(newsvcname);
764 goto error;
766 free(newsvcname);
767 } else {
768 input_name.value = service_name;
769 input_name.length = strlen((char *)input_name.value) + 1;
770 gssstat = krb5_gss_import_name(&minor_stat,
771 &input_name,
772 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
773 (gss_name_t *)&creds.server);
775 if (gssstat != GSS_S_COMPLETE) {
776 code = KADM5_GSS_ERROR;
777 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
778 "gss_import_name failed for client name\n"));
779 goto error;
782 serverp = creds.server;
784 /* XXX temporarily fix a bug in krb5_cc_get_type */
785 #undef krb5_cc_get_type
786 #define krb5_cc_get_type(context, cache) ((cache)->ops->prefix)
789 if (init_type == INIT_CREDS) {
790 ccache = ccache_in;
791 handle->cache_name = (char *)
792 malloc(strlen(krb5_cc_get_type(handle->context, ccache)) +
793 strlen(krb5_cc_get_name(handle->context, ccache)) + 2);
794 if (handle->cache_name == NULL) {
795 code = ENOMEM;
796 goto error;
798 sprintf(handle->cache_name, "%s:%s",
799 krb5_cc_get_type(handle->context, ccache),
800 krb5_cc_get_name(handle->context, ccache));
801 } else {
802 #if 0
803 handle->cache_name =
804 (char *) malloc(strlen(ADM_CCACHE)+strlen("FILE:")+1);
805 if (handle->cache_name == NULL) {
806 code = ENOMEM;
807 goto error;
809 sprintf(handle->cache_name, "FILE:%s", ADM_CCACHE);
810 mktemp(handle->cache_name + strlen("FILE:"));
811 #endif
813 static int counter = 0;
814 handle->cache_name = malloc(sizeof("MEMORY:kadm5_")
815 + 3*sizeof(counter));
816 sprintf(handle->cache_name, "MEMORY:kadm5_%u", counter++);
819 if ((code = krb5_cc_resolve(handle->context, handle->cache_name,
820 &ccache)))
821 goto error;
823 if ((code = krb5_cc_initialize (handle->context, ccache,
824 creds.client)))
825 goto error;
827 handle->destroy_cache = 1;
829 handle->lhandle->cache_name = handle->cache_name;
830 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN,
831 "cache created: %s\n"), handle->cache_name);
833 if ((code = krb5_timeofday(handle->context, &now)))
834 goto error;
837 * Get a ticket, use the method specified in init_type.
840 creds.times.starttime = 0; /* start timer at KDC */
841 creds.times.endtime = 0; /* endtime will be limited by service */
843 memset(&opt, 0, sizeof (opt));
844 krb5_get_init_creds_opt_init(&opt);
846 if (creds.times.endtime) {
847 if (creds.times.starttime)
848 starttime = creds.times.starttime;
849 else
850 starttime = now;
852 krb5_get_init_creds_opt_set_tkt_life(&opt,
853 creds.times.endtime - starttime);
855 code = krb5_unparse_name(handle->context, creds.server, &server);
856 if (code)
857 goto error;
860 * Solaris Kerberos:
861 * Save the original creds.server as krb5_get_init_creds*() always
862 * sets the realm of the server to the client realm.
864 code = krb5_copy_principal(handle->context, creds.server, &saved_server);
865 if (code)
866 goto error;
868 if (init_type == INIT_PASS) {
869 code = krb5_get_init_creds_password(handle->context,
870 &creds, creds.client, pass, NULL,
871 NULL, creds.times.starttime,
872 server, &opt);
873 } else if (init_type == INIT_SKEY) {
874 krb5_keytab kt = NULL;
876 if (!(pass && (code = krb5_kt_resolve(handle->context,
877 pass, &kt)))) {
878 code = krb5_get_init_creds_keytab(
879 handle->context,
880 &creds, creds.client, kt,
881 creds.times.starttime,
882 server, &opt);
884 if (pass) krb5_kt_close(handle->context, kt);
888 /* Improved error messages */
889 if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD;
890 if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
891 code = KADM5_SECURE_PRINC_MISSING;
893 if (code != 0) {
894 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
895 "failed to obtain credentials cache\n"));
896 krb5_free_principal(handle->context, saved_server);
897 goto error;
901 * Solaris Kerberos:
902 * If the server principal had an empty realm then store that in
903 * the cred cache and not the server realm as returned by
904 * krb5_get_init_creds_{keytab|password}(). This ensures that rpcsec_gss
905 * will find the credential in the cred cache even if a "fallback"
906 * method is being used to determine the realm.
908 if (init_type != INIT_CREDS) {
909 krb5_free_principal(handle->context, creds.server);
911 creds.server = saved_server;
914 * If we got this far, save the creds in the cache.
916 if (ccache) {
917 code = krb5_cc_store_cred(handle->context, ccache, &creds);
920 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "obtained credentials cache\n"));
922 #ifdef ZEROPASSWD
923 if (pass != NULL)
924 memset(pass, 0, strlen(pass));
925 #endif
927 if (init_type != INIT_PASS ||
928 handle->params.kpasswd_protocol == KRB5_CHGPWD_RPCSEC ||
929 cpw == FALSE) {
930 code = _kadm5_initialize_rpcsec_gss_handle(handle,
931 client_name, service_name);
934 * Solaris Kerberos:
935 * If _kadm5_initialize_rpcsec_gss_handle() fails it will have
936 * called krb5_gss_release_cred(). If the credential cache is a
937 * MEMORY cred cache krb5_gss_release_cred() destroys the
938 * cred cache data. Make sure that the cred-cache is closed
939 * to prevent a double free in the "error" code.
941 if (code != 0) {
942 if (init_type != INIT_CREDS) {
943 krb5_cc_close(handle->context, ccache);
944 ccache = NULL;
946 goto error;
950 *server_handle = (void *) handle;
952 if (init_type != INIT_CREDS)
953 krb5_cc_close(handle->context, ccache);
955 goto cleanup;
957 error:
959 * Note that it is illegal for this code to execute if "handle"
960 * has not been allocated and initialized. I.e., don't use "goto
961 * error" before the block of code at the top of the function
962 * that allocates and initializes "handle".
964 if (handle->cache_name)
965 free(handle->cache_name);
966 if (handle->destroy_cache && ccache)
967 krb5_cc_destroy(handle->context, ccache);
968 if(handle->clnt && handle->clnt->cl_auth)
969 AUTH_DESTROY(handle->clnt->cl_auth);
970 if(handle->clnt)
971 clnt_destroy(handle->clnt);
972 (void) kadm5_free_config_params(handle->context, &handle->params);
974 cleanup:
975 if (server)
976 free(server);
979 * cred's server and client pointers could have been overwritten
980 * by the krb5_get_init_* functions. If the addresses are different
981 * before and after the calls then we must free the memory that
982 * was allocated before the call.
984 if (clientp && clientp != creds.client)
985 krb5_free_principal(handle->context, clientp);
987 if (serverp && serverp != creds.server)
988 krb5_free_principal(handle->context, serverp);
990 krb5_free_cred_contents(handle->context, &creds);
993 * Dont clean up the handle if the code is OK (code==0)
994 * because it is returned to the caller in the 'server_handle'
995 * ptr.
997 if (code) {
998 krb5_free_context(handle->context);
999 free(handle->lhandle);
1000 free(handle);
1003 return code;
1006 kadm5_ret_t
1007 kadm5_destroy(void *server_handle)
1009 krb5_ccache ccache = NULL;
1010 int code = KADM5_OK;
1011 kadm5_server_handle_t handle =
1012 (kadm5_server_handle_t) server_handle;
1013 OM_uint32 min_stat;
1015 CHECK_HANDLE(server_handle);
1016 /* SUNW14resync:
1017 * krb5_cc_resolve() will resolve a ccache with the same data that
1018 * handle->my_cred points to. If the ccache is a MEMORY ccache then
1019 * gss_release_cred() will free that data (it doesn't do this when ccache
1020 * is a FILE ccache).
1021 * if'ed out to avoid the double free.
1023 #if 0
1024 if (handle->destroy_cache && handle->cache_name) {
1025 if ((code = krb5_cc_resolve(handle->context,
1026 handle->cache_name, &ccache)) == 0)
1027 code = krb5_cc_destroy (handle->context, ccache);
1029 #endif
1030 if (handle->cache_name)
1031 free(handle->cache_name);
1032 if (handle->clnt && handle->clnt->cl_auth) {
1034 * Since kadm5 doesn't use the default credentials we
1035 * must clean this up manually.
1037 if (handle->my_cred != GSS_C_NO_CREDENTIAL)
1038 (void) gss_release_cred(&min_stat, &handle->my_cred);
1039 AUTH_DESTROY(handle->clnt->cl_auth);
1041 if (handle->clnt)
1042 clnt_destroy(handle->clnt);
1043 if (handle->lhandle)
1044 free (handle->lhandle);
1046 kadm5_free_config_params(handle->context, &handle->params);
1047 krb5_free_context(handle->context);
1049 handle->magic_number = 0;
1050 free(handle);
1052 return code;
1054 /* not supported on client */
1055 kadm5_ret_t kadm5_lock(void *server_handle)
1057 return EINVAL;
1060 /* not supported on client */
1061 kadm5_ret_t kadm5_unlock(void *server_handle)
1063 return EINVAL;
1066 kadm5_ret_t kadm5_flush(void *server_handle)
1068 return KADM5_OK;
1071 int _kadm5_check_handle(void *handle)
1073 CHECK_HANDLE(handle);
1074 return 0;
1077 krb5_error_code kadm5_init_krb5_context (krb5_context *ctx)
1079 return krb5_init_context(ctx);
1083 * Stub function for kadmin. It was created to eliminate the dependency on
1084 * libkdb's ulog functions. The srv equivalent makes the actual calls.
1086 krb5_error_code
1087 kadm5_init_iprop(void *handle)
1089 return (0);