xref: /titanic_51/usr/src/lib/krb5/kadm5/clnt/client_init.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  *
5  * $Header: /cvs/krbdev/krb5/src/lib/kadm5/clnt/client_init.c,v 1.13.2.2 2000/05/09 13:17:14 raeburn Exp $
6  */
7 
8 #pragma ident	"%Z%%M%	%I%	%E% SMI"
9 
10 /*
11  * Copyright (C) 1998 by the FundsXpress, INC.
12  *
13  * All rights reserved.
14  *
15  * Export of this software from the United States of America may require
16  * a specific license from the United States Government.  It is the
17  * responsibility of any person or organization contemplating export to
18  * obtain such a license before exporting.
19  *
20  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
21  * distribute this software and its documentation for any purpose and
22  * without fee is hereby granted, provided that the above copyright
23  * notice appear in all copies and that both that copyright notice and
24  * this permission notice appear in supporting documentation, and that
25  * the name of FundsXpress. not be used in advertising or publicity pertaining
26  * to distribution of the software without specific, written prior
27  * permission.  FundsXpress makes no representations about the suitability of
28  * this software for any purpose.  It is provided "as is" without express
29  * or implied warranty.
30  *
31  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
32  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
33  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
34  */
35 
36 
37 /*
38  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
39  *
40  * $Header: /afs/athena.mit.edu/astaff/project/krbdev/.cvsroot/src/lib/kadm5/clnt/client_init.c,v 1.6 1996/11/07 17:13:44 tytso Exp $
41  */
42 
43 #include <stdio.h>
44 #include <netdb.h>
45 #include <memory.h>
46 #include <string.h>
47 #include <com_err.h>
48 #include <sys/types.h>
49 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #include <krb5.h>
52 #include <k5-int.h> /* for KRB5_ADM_DEFAULT_PORT */
53 #ifdef __STDC__
54 #include <stdlib.h>
55 #endif
56 #include <libintl.h>
57 
58 #include <syslog.h>
59 #include <gssapi/gssapi.h>
60 #include <gssapi_krb5.h>
61 #include <gssapiP_krb5.h>
62 #include <kadm5/kadm_rpc.h>
63 #include <rpc/clnt.h>
64 #include <kadm5/admin.h>
65 #include "client_internal.h"
66 #include <iprop_hdr.h>
67 #include "iprop.h"
68 
69 #define	ADM_CCACHE  "/tmp/ovsec_adm.XXXXXX"
70 
71 /* connection timeout to kadmind in seconds */
72 #define		KADMIND_CONNECT_TIMEOUT	25
73 
74 int _kadm5_check_handle();
75 
76 enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS };
77 
78 static kadm5_ret_t _kadm5_init_any(char *client_name,
79 				   enum init_type init_type,
80 				   char *pass,
81 				   krb5_ccache ccache_in,
82 				   char *service_name,
83 				   kadm5_config_params *params,
84 				   krb5_ui_4 struct_version,
85 				   krb5_ui_4 api_version,
86 				   void **server_handle);
87 
88 kadm5_ret_t kadm5_init_with_creds(char *client_name,
89 				  krb5_ccache ccache,
90 				  char *service_name,
91 				  kadm5_config_params *params,
92 				  krb5_ui_4 struct_version,
93 				  krb5_ui_4 api_version,
94 				  void **server_handle)
95 {
96 	return _kadm5_init_any(client_name, INIT_CREDS, NULL, ccache,
97 			    service_name, params,
98 			    struct_version, api_version,
99 			    server_handle);
100 }
101 
102 
103 kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass,
104 				     char *service_name,
105 				     kadm5_config_params *params,
106 				     krb5_ui_4 struct_version,
107 				     krb5_ui_4 api_version,
108 				     void **server_handle)
109 {
110 	return _kadm5_init_any(client_name, INIT_PASS, pass, NULL,
111 			    service_name, params, struct_version,
112 			    api_version, server_handle);
113 }
114 
115 kadm5_ret_t kadm5_init(char *client_name, char *pass,
116 			 char *service_name,
117 			 kadm5_config_params *params,
118 			 krb5_ui_4 struct_version,
119 			 krb5_ui_4 api_version,
120 			 void **server_handle)
121 {
122 	return _kadm5_init_any(client_name, INIT_PASS, pass, NULL,
123 			    service_name, params, struct_version,
124 			    api_version, server_handle);
125 }
126 
127 kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab,
128 				 char *service_name,
129 				 kadm5_config_params *params,
130 				 krb5_ui_4 struct_version,
131 				 krb5_ui_4 api_version,
132 				 void **server_handle)
133 {
134 	return _kadm5_init_any(client_name, INIT_SKEY, keytab, NULL,
135 			    service_name, params, struct_version,
136 			    api_version, server_handle);
137 }
138 
139 krb5_error_code  kadm5_free_config_params();
140 
141 static void
142 display_status_1(m, code, type, mech)
143 char *m;
144 OM_uint32 code;
145 int type;
146 const gss_OID mech;
147 {
148 	OM_uint32 maj_stat, min_stat;
149 	gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
150 	OM_uint32 msg_ctx;
151 
152 	msg_ctx = 0;
153 	ADMIN_LOG(LOG_ERR, "%s\n", m);
154 	/* LINTED */
155 	while (1) {
156 		maj_stat = gss_display_status(&min_stat, code,
157 					    type, mech,
158 					    &msg_ctx, &msg);
159 		if (maj_stat != GSS_S_COMPLETE) {
160 			syslog(LOG_ERR,
161 			    dgettext(TEXT_DOMAIN,
162 				    "error in gss_display_status"
163 				    " called from <%s>\n"), m);
164 			break;
165 		} else
166 			syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
167 						"GSS-API error : %s\n"),
168 			    m);
169 		syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
170 					"GSS-API error : %s\n"),
171 		    (char *)msg.value);
172 		if (msg.length != 0)
173 			(void) gss_release_buffer(&min_stat, &msg);
174 
175 		if (!msg_ctx)
176 			break;
177 	}
178 }
179 
180 /*
181  * Function: display_status
182  *
183  * Purpose: displays GSS-API messages
184  *
185  * Arguments:
186  *
187  * 	msg		a string to be displayed with the message
188  * 	maj_stat	the GSS-API major status code
189  * 	min_stat	the GSS-API minor status code
190  *	mech		kerberos mech
191  * Effects:
192  *
193  * The GSS-API messages associated with maj_stat and min_stat are
194  * displayed on stderr, each preceeded by "GSS-API error <msg>: " and
195  * followed by a newline.
196  */
197 void
198 display_status(msg, maj_stat, min_stat, mech)
199 char *msg;
200 OM_uint32 maj_stat;
201 OM_uint32 min_stat;
202 char *mech;
203 {
204 	gss_OID mech_oid;
205 
206 	if (!rpc_gss_mech_to_oid(mech, (rpc_gss_OID *)&mech_oid)) {
207 		ADMIN_LOG(LOG_ERR,
208 			dgettext(TEXT_DOMAIN,
209 				"Invalid mechanism oid <%s>"), mech);
210 		return;
211 	}
212 
213 	display_status_1(msg, maj_stat, GSS_C_GSS_CODE, mech_oid);
214 	display_status_1(msg, min_stat, GSS_C_MECH_CODE, mech_oid);
215 }
216 
217 /*
218  * Open an fd for the given address and connect asynchronously. Wait
219  * KADMIND_CONNECT_TIMEOUT seconds or till it succeeds. If it succeeds
220  * change fd to blocking and return it, else return -1.
221  */
222 static int
223 get_connection(struct netconfig *nconf, struct netbuf netaddr)
224 {
225 	struct t_info tinfo;
226 	struct t_call sndcall;
227 	struct t_call *rcvcall = NULL;
228 	int connect_time;
229 	int flags;
230 	int fd;
231 
232 	(void) memset(&tinfo, 0, sizeof (tinfo));
233 
234 	/* we'l open with O_NONBLOCK and avoid an fcntl */
235 	fd = t_open(nconf->nc_device, O_RDWR | O_NONBLOCK, &tinfo);
236 	if (fd == -1) {
237 		return (-1);
238 	}
239 
240 	if (t_bind(fd, (struct t_bind *)NULL, (struct t_bind *)NULL) == -1) {
241 		(void) close(fd);
242 		return (-1);
243 	}
244 
245 	/* we can't connect unless fd is in IDLE state */
246 	if (t_getstate(fd) != T_IDLE) {
247 		(void) close(fd);
248 		return (-1);
249 	}
250 
251 	/* setup connect parameters */
252 	netaddr.len = netaddr.maxlen = __rpc_get_a_size(tinfo.addr);
253 	sndcall.addr = netaddr;
254 	sndcall.opt.len = sndcall.udata.len = 0;
255 
256 	/* we wait for KADMIND_CONNECT_TIMEOUT seconds from now */
257 	connect_time = time(NULL) + KADMIND_CONNECT_TIMEOUT;
258 	if (t_connect(fd, &sndcall, rcvcall) != 0) {
259 		if (t_errno != TNODATA) {
260 			(void) close(fd);
261 			return (-1);
262 		}
263 	}
264 
265 	/* loop till success or timeout */
266 	for (;;) {
267 		if (t_rcvconnect(fd, rcvcall) == 0)
268 			break;
269 
270 		if (t_errno != TNODATA || time(NULL) > connect_time) {
271 			/* we have either timed out or caught an error */
272 			(void) close(fd);
273 			if (rcvcall != NULL)
274 				t_free((char *)rcvcall, T_CALL);
275 			return (-1);
276 		}
277 		sleep(1);
278 	}
279 
280 	/* make the fd blocking (synchronous) */
281 	flags = fcntl(fd, F_GETFL, 0);
282 	(void) fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
283 	if (rcvcall != NULL)
284 		t_free((char *)rcvcall, T_CALL);
285 	return (fd);
286 }
287 
288 /*
289  * Open an RPCSEC_GSS connection and
290  * get a client handle to use for future RPCSEC calls.
291  *
292  * This function is only used when changing passwords and
293  * the kpasswd_protocol is RPCSEC_GSS
294  */
295 static int
296 _kadm5_initialize_rpcsec_gss_handle(kadm5_server_handle_t handle,
297 				    char *client_name,
298 				    char *service_name)
299 {
300 	struct netbuf netaddr;
301 	struct hostent *hp;
302 	int fd;
303 	struct sockaddr_in addr;
304 	struct sockaddr_in *sin;
305 	struct netconfig *nconf;
306 	int code = 0;
307 	generic_ret *r;
308 	char *ccname_orig;
309 	char *iprop_svc;
310 	boolean_t iprop_enable = B_FALSE;
311 	char mech[] = "kerberos_v5";
312 	gss_OID mech_oid;
313 	gss_OID_set_desc oid_set;
314 	gss_name_t gss_client;
315 	gss_buffer_desc input_name;
316 	gss_cred_id_t gss_client_creds = GSS_C_NO_CREDENTIAL;
317 	rpc_gss_options_req_t   options_req;
318 	rpc_gss_options_ret_t   options_ret;
319 	rpc_gss_service_t service = rpc_gss_svc_privacy;
320 	OM_uint32 gssstat, minor_stat;
321 	void *handlep;
322 	enum clnt_stat rpc_err_code;
323 
324 	hp = gethostbyname(handle->params.admin_server);
325 	if (hp == (struct hostent *)NULL) {
326 		code = KADM5_BAD_SERVER_NAME;
327 		ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
328 					    "bad server name\n"));
329 		goto cleanup;
330 	}
331 
332 	memset(&addr, 0, sizeof (addr));
333 	addr.sin_family = hp->h_addrtype;
334 	(void) memcpy((char *)&addr.sin_addr, (char *)hp->h_addr,
335 		    sizeof (addr.sin_addr));
336 	addr.sin_port = htons((ushort_t)handle->params.kadmind_port);
337 	sin = &addr;
338 #ifdef DEBUG
339 	printf("kadmin_port %d\n", handle->params.kadmind_port);
340 	printf("addr: sin_port: %d, sin_family: %d, sin_zero %s\n",
341 	    addr.sin_port, addr.sin_family, addr.sin_zero);
342 	printf("sin_addr %d:%d\n", addr.sin_addr.S_un.S_un_w.s_w1,
343 	    addr.sin_addr.S_un.S_un_w.s_w2);
344 #endif
345 	if ((handlep = setnetconfig()) == (void *) NULL) {
346 		(void) syslog(LOG_ERR,
347 			    dgettext(TEXT_DOMAIN,
348 				    "cannot get any transport information"));
349 		goto error;
350 	}
351 
352 	while (nconf = getnetconfig(handlep)) {
353 		if ((nconf->nc_semantics == NC_TPI_COTS_ORD) &&
354 		    (strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
355 		    (strcmp(nconf->nc_proto, NC_TCP) == 0))
356 			break;
357 	}
358 
359 	if (nconf == (struct netconfig *)NULL)
360 		goto error;
361 
362 	/* Transform addr to netbuf */
363 	(void) memset(&netaddr, 0, sizeof (netaddr));
364 	netaddr.buf = (char *)sin;
365 
366 	/* get an fd connected to the given address */
367 	fd =  get_connection(nconf, netaddr);
368 	if (fd == -1) {
369 		syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
370 			"unable to open connection to ADMIN server "
371 			"(t_error %i)"), t_errno);
372 		code = KADM5_RPC_ERROR;
373 		goto error;
374 	}
375 
376 #ifdef DEBUG
377 	printf("fd: %d, KADM: %d, KADMVERS %d\n", fd, KADM, KADMVERS);
378 	printf("nconf: nc_netid: %s, nc_semantics: %d, nc_flag: %d, "
379 	    "nc_protofmly: %s\n",
380 	    nconf->nc_netid, nconf->nc_semantics, nconf->nc_flag,
381 	    nconf->nc_protofmly);
382 	printf("nc_proto: %s, nc_device: %s, nc_nlookups: %d, nc_used: %d\n",
383 	    nconf->nc_proto, nconf->nc_device, nconf->nc_nlookups,
384 	    nconf->nc_unused);
385 	printf("netaddr: maxlen %d, buf: %s, len: %d\n", netaddr.maxlen,
386 	    netaddr.buf, netaddr.len);
387 #endif
388  	/*
389 	 * Tell clnt_tli_create that given fd is already connected
390 	 *
391 	 * If the service_name and client_name are iprop-centric,
392 	 * we need to clnt_tli_create to the appropriate RPC prog
393 	 */
394 	iprop_svc = strdup(KIPROP_SVC_NAME);
395 	if (iprop_svc == NULL)
396 		return (ENOMEM);
397 
398 	if ((strstr(service_name, iprop_svc) != NULL) &&
399 	    (strstr(client_name, iprop_svc) != NULL)) {
400 		iprop_enable = B_TRUE;
401 		handle->clnt = clnt_tli_create(fd, nconf, NULL,
402 				    KRB5_IPROP_PROG, KRB5_IPROP_VERS, 0, 0);
403 	}
404 	else
405 		handle->clnt = clnt_tli_create(fd, nconf, NULL,
406 				    KADM, KADMVERS, 0, 0);
407 
408 	if (iprop_svc)
409 		free(iprop_svc);
410 
411 	if (handle->clnt == NULL) {
412 		syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
413 					"clnt_tli_create failed\n"));
414 		code = KADM5_RPC_ERROR;
415 		(void) close(fd);
416 		goto error;
417 	}
418 	/*
419 	 * The rpc-handle was created on an fd opened and connected
420 	 * by us, so we have to explicitly tell rpc to close it.
421 	 */
422 	if (clnt_control(handle->clnt, CLSET_FD_CLOSE, NULL) != TRUE) {
423 		clnt_pcreateerror("ERROR:");
424 		syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
425 			"clnt_control failed to set CLSET_FD_CLOSE"));
426 		code = KADM5_RPC_ERROR;
427 		(void) close(fd);
428 		goto error;
429 	}
430 
431 	handle->lhandle->clnt = handle->clnt;
432 
433 	/* now that handle->clnt is set, we can check the handle */
434 	if (code = _kadm5_check_handle((void *) handle))
435 		goto error;
436 
437 	/*
438 	 * The RPC connection is open; establish the GSS-API
439 	 * authentication context.
440 	 */
441 	ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
442 				    "have an rpc connection open\n"));
443 	/* use the kadm5 cache */
444 	ccname_orig = getenv("KRB5CCNAME");
445 	if (ccname_orig)
446 		ccname_orig = strdup(ccname_orig);
447 
448 	(void) krb5_setenv("KRB5CCNAME", handle->cache_name, 1);
449 
450 	ADMIN_LOG(LOG_ERR,
451 		dgettext(TEXT_DOMAIN,
452 			"current credential cache: %s"), handle->cache_name);
453 	input_name.value = client_name;
454 	input_name.length = strlen((char *)input_name.value) + 1;
455 	gssstat = gss_import_name(&minor_stat, &input_name,
456 				(gss_OID)gss_nt_krb5_name, &gss_client);
457 	if (gssstat != GSS_S_COMPLETE) {
458 		code = KADM5_GSS_ERROR;
459 		ADMIN_LOGO(LOG_ERR,
460 			dgettext(TEXT_DOMAIN,
461 				"gss_import_name failed for client name\n"));
462 		goto error;
463 	}
464 
465 	if (!rpc_gss_mech_to_oid(mech, (rpc_gss_OID *)&mech_oid)) {
466 		ADMIN_LOG(LOG_ERR,
467 			dgettext(TEXT_DOMAIN,
468 				"Invalid mechanism oid <%s>"), mech);
469 		goto error;
470 	}
471 
472 	oid_set.count = 1;
473 	oid_set.elements = mech_oid;
474 
475 	gssstat = gss_acquire_cred(&minor_stat, gss_client, 0,
476 				&oid_set, GSS_C_INITIATE,
477 				&gss_client_creds, NULL, NULL);
478 	(void) gss_release_name(&minor_stat, &gss_client);
479 	if (gssstat != GSS_S_COMPLETE) {
480 		code = KADM5_GSS_ERROR;
481 		ADMIN_LOG(LOG_ERR,
482 			dgettext(TEXT_DOMAIN,
483 				"could not acquire credentials, "
484 				"major error code: %d\n"), gssstat);
485 		goto error;
486 	}
487 	handle->my_cred = gss_client_creds;
488 	options_req.my_cred = gss_client_creds;
489 	options_req.req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
490 	options_req.time_req = 0;
491 	options_req.input_channel_bindings = NULL;
492 #ifndef INIT_TEST
493 	handle->clnt->cl_auth = rpc_gss_seccreate(handle->clnt,
494 						service_name,
495 						mech,
496 						service,
497 						NULL,
498 						&options_req,
499 						&options_ret);
500 #endif /* ! INIT_TEST */
501 
502 	if (ccname_orig) {
503 		(void) krb5_setenv("KRB5CCNAME", ccname_orig, 1);
504 		free(ccname_orig);
505 	} else
506 		(void) krb5_unsetenv("KRB5CCNAME");
507 
508 	if (handle->clnt->cl_auth == NULL) {
509 		code = KADM5_GSS_ERROR;
510 		display_status(dgettext(TEXT_DOMAIN,
511 					"rpc_gss_seccreate failed\n"),
512 			    options_ret.major_status,
513 			    options_ret.minor_status,
514 			    mech);
515 		goto error;
516 	}
517 
518 	/*
519 	 * Bypass the remainder of the code and return straightaway
520 	 * if the gss service requested is kiprop
521 	 */
522 	if (iprop_enable == B_TRUE) {
523 		code = 0;
524 		goto cleanup;
525 	}
526 
527 	r = init_1(&handle->api_version, handle->clnt, &rpc_err_code);
528 	if (r == NULL) {
529 		ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
530 			"error during admin api initialization\n"));
531 
532 		if (rpc_err_code == RPC_CANTENCODEARGS) {
533 			ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
534 				"encryption needed to encode RPC data may not be "
535 				"installed/configured on this system"));
536 			code = KADM5_RPC_ERROR_CANTENCODEARGS;
537 		} else if (rpc_err_code == RPC_CANTDECODEARGS) {
538 			ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
539 				"encryption needed to decode RPC data may not be "
540 				"installed/configured on the server"));
541 			code = KADM5_RPC_ERROR_CANTDECODEARGS;
542 		} else
543 			code = KADM5_RPC_ERROR;
544 
545 		goto error;
546 
547 	}
548 	if (r->code) {
549 		code = r->code;
550 		ADMIN_LOG(LOG_ERR,
551 			dgettext(TEXT_DOMAIN,
552 				"error during admin api initialization: %d\n"),
553 			r->code);
554 		goto error;
555 	}
556 error:
557 cleanup:
558 
559 	if (handlep != (void *) NULL)
560 		(void) endnetconfig(handlep);
561 	/*
562 	 * gss_client_creds is freed only when there is an error condition,
563 	 * given that rpc_gss_seccreate() will assign the cred pointer to the
564 	 * my_cred member in the auth handle's private data structure.
565 	 */
566 	if (code && (gss_client_creds != GSS_C_NO_CREDENTIAL))
567 		(void) gss_release_cred(&minor_stat, &gss_client_creds);
568 
569 	return (code);
570 }
571 
572 static kadm5_ret_t _kadm5_init_any(char *client_name,
573 				   enum init_type init_type,
574 				   char *pass,
575 				   krb5_ccache ccache_in,
576 				   char *service_name,
577 				   kadm5_config_params *params_in,
578 				   krb5_ui_4 struct_version,
579 				   krb5_ui_4 api_version,
580 				   void **server_handle)
581 {
582 	int i;
583 	krb5_creds	creds;
584 	krb5_ccache ccache = NULL;
585 	krb5_timestamp  now;
586 	OM_uint32 gssstat, minor_stat;
587 	kadm5_server_handle_t handle;
588 	kadm5_config_params params_local;
589 	int code = 0;
590 	krb5_get_init_creds_opt opt;
591 	gss_buffer_desc input_name;
592 	krb5_error_code kret;
593 	krb5_int32 starttime;
594 	char *server = NULL;
595 	krb5_principal serverp = NULL, clientp = NULL;
596 
597 	ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
598 		"entering kadm5_init_any\n"));
599 	if (! server_handle) {
600 		return (EINVAL);
601 	}
602 
603 	if (! (handle = malloc(sizeof(*handle)))) {
604 		return (ENOMEM);
605 	}
606 	if (! (handle->lhandle = malloc(sizeof(*handle)))) {
607 		free(handle);
608 		return (ENOMEM);
609 	}
610 
611 	handle->magic_number = KADM5_SERVER_HANDLE_MAGIC;
612 	handle->struct_version = struct_version;
613 	handle->api_version = api_version;
614 	handle->clnt = 0;
615 	handle->cache_name = 0;
616 	handle->destroy_cache = 0;
617 	*handle->lhandle = *handle;
618 	handle->lhandle->api_version = KADM5_API_VERSION_2;
619 	handle->lhandle->struct_version = KADM5_STRUCT_VERSION;
620 	handle->lhandle->lhandle = handle->lhandle;
621 
622 	kret = krb5_init_context(&handle->context);
623 	if (kret) {
624 		free(handle->lhandle);
625 		free(handle);
626 		return (kret);
627 	}
628 
629 	if(service_name == NULL || client_name == NULL) {
630 		krb5_free_context(handle->context);
631 		free(handle->lhandle);
632 		free(handle);
633 		return (EINVAL);
634 	}
635 	memset((char *) &creds, 0, sizeof(creds));
636 
637 	/*
638 	 * Verify the version numbers before proceeding; we can't use
639 	 * CHECK_HANDLE because not all fields are set yet.
640 	 */
641 	GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION,
642 			  KADM5_NEW_LIB_API_VERSION);
643 
644 	/*
645 	 * Acquire relevant profile entries.  In version 2, merge values
646 	 * in params_in with values from profile, based on
647 	 * params_in->mask.
648 	 *
649 	 * In version 1, we've given a realm (which may be NULL) instead
650 	 * of params_in.  So use that realm, make params_in contain an
651 	 * empty mask, and behave like version 2.
652 	 */
653 	memset((char *) &params_local, 0, sizeof(params_local));
654 	if (api_version == KADM5_API_VERSION_1) {
655 		if (params_in)
656 			params_local.mask = KADM5_CONFIG_REALM;
657 		params_in = &params_local;
658 	}
659 
660 #define ILLEGAL_PARAMS ( \
661 	KADM5_CONFIG_ACL_FILE	| KADM5_CONFIG_ADB_LOCKFILE | \
662 	KADM5_CONFIG_DBNAME	| KADM5_CONFIG_ADBNAME | \
663 	KADM5_CONFIG_DICT_FILE	| KADM5_CONFIG_ADMIN_KEYTAB | \
664 	KADM5_CONFIG_STASH_FILE | KADM5_CONFIG_MKEY_NAME | \
665 	KADM5_CONFIG_ENCTYPE	| KADM5_CONFIG_MAX_LIFE	| \
666 	KADM5_CONFIG_MAX_RLIFE	| KADM5_CONFIG_EXPIRATION | \
667 	KADM5_CONFIG_FLAGS	| KADM5_CONFIG_ENCTYPES	| \
668 	KADM5_CONFIG_MKEY_FROM_KBD)
669 
670 	if (params_in && params_in->mask & ILLEGAL_PARAMS) {
671 		krb5_free_context(handle->context);
672 		free(handle->lhandle);
673 		free(handle);
674 		ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN,
675 			"bad client parameters, returning %d"),
676 			KADM5_BAD_CLIENT_PARAMS);
677 		return (KADM5_BAD_CLIENT_PARAMS);
678 	}
679 
680 	if ((code = kadm5_get_config_params(handle->context,
681 					DEFAULT_PROFILE_PATH,
682 					"KRB5_CONFIG",
683 					params_in,
684 					&handle->params))) {
685 		krb5_free_context(handle->context);
686 		free(handle->lhandle);
687 		free(handle);
688 		ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN,
689 			"failed to get config_params, return: %d\n"), code);
690 		return(code);
691 	}
692 
693 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \
694 			 KADM5_CONFIG_ADMIN_SERVER | \
695 			 KADM5_CONFIG_KADMIND_PORT)
696 
697 	if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
698 		(void) kadm5_free_config_params(handle->context,
699 						&handle->params);
700 		krb5_free_context(handle->context);
701 		free(handle->lhandle);
702 		free(handle);
703 		ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
704 			"missing config parameters\n"));
705 		return (KADM5_MISSING_CONF_PARAMS);
706 	}
707 
708 	/*
709 	 * Acquire a service ticket for service_name@realm in the name of
710 	 * client_name, using password pass (which could be NULL), and
711 	 * create a ccache to store them in.  If INIT_CREDS, use the
712 	 * ccache we were provided instead.
713 	 */
714 	if ((code = krb5_parse_name(handle->context, client_name,
715 			    &creds.client))) {
716 		ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
717 			    "could not parse client name\n"));
718 		goto error;
719 	}
720 	clientp = creds.client;
721 
722 	if (init_type == INIT_PASS &&
723 	    handle->params.kpasswd_protocol == KRB5_CHGPWD_CHANGEPW_V2) {
724 		/*
725 		 * The 'service_name' is constructed by the caller
726 		 * but its done before the parameter which determines
727 		 * the kpasswd_protocol is found.  The servers that
728 		 * support the SET/CHANGE password protocol expect
729 		 * a slightly different service principal than
730 		 * the normal SEAM kadmind so construct the correct
731 		 * name here and then forget it.
732 		 */
733 		char *newsvcname = NULL;
734 		newsvcname = malloc(strlen(KADM5_CHANGEPW_SERVICE) +
735 				    strlen(handle->params.realm) + 2);
736 		if (newsvcname == NULL) {
737 			return (ENOMEM);
738 		}
739 		sprintf(newsvcname, "%s@%s", KADM5_CHANGEPW_SERVICE,
740 			handle->params.realm);
741 
742 		if ((code = krb5_parse_name(handle->context, newsvcname,
743 					    &creds.server))) {
744 			ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
745 					    "could not parse server "
746 					    "name\n"));
747 			free(newsvcname);
748 			goto error;
749 		}
750 		free(newsvcname);
751 	} else {
752 		input_name.value = service_name;
753 		input_name.length = strlen((char *)input_name.value) + 1;
754 		gssstat = krb5_gss_import_name(handle->context,
755 				    &minor_stat,
756 				    &input_name,
757 				    (gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
758 				    (gss_name_t *)&creds.server);
759 
760 		if (gssstat != GSS_S_COMPLETE) {
761 			code = KADM5_GSS_ERROR;
762 			ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
763 				"gss_import_name failed for client name\n"));
764 			goto error;
765 		}
766 	}
767 	serverp = creds.server;
768 
769 	/* XXX temporarily fix a bug in krb5_cc_get_type */
770 #undef krb5_cc_get_type
771 #define krb5_cc_get_type(context, cache) ((cache)->ops->prefix)
772 
773 	if (init_type == INIT_CREDS) {
774 		ccache = ccache_in;
775 		handle->cache_name = (char *)
776 			malloc(strlen(krb5_cc_get_type(handle->context, ccache)) +
777 		    	strlen(krb5_cc_get_name(handle->context, ccache)) + 2);
778 
779 		if (handle->cache_name == NULL) {
780 			code = ENOMEM;
781 			goto error;
782 		}
783 		sprintf(handle->cache_name, "%s:%s",
784 			krb5_cc_get_type(handle->context, ccache),
785 			krb5_cc_get_name(handle->context, ccache));
786 	} else {
787 		handle->cache_name =
788 			(char *) malloc(strlen(ADM_CCACHE)+strlen("FILE:")+1);
789 		if (handle->cache_name == NULL) {
790 			code = ENOMEM;
791 			goto error;
792 		}
793 		sprintf(handle->cache_name, "FILE:%s", ADM_CCACHE);
794 		mktemp(handle->cache_name + strlen("FILE:"));
795 
796 		if ((code = krb5_cc_resolve(handle->context,
797 			handle->cache_name, &ccache)))
798 			goto error;
799 
800 		if ((code = krb5_cc_initialize (handle->context, ccache,
801 					  creds.client)))
802 			goto error;
803 
804 		handle->destroy_cache = 1;
805 	}
806 	handle->lhandle->cache_name = handle->cache_name;
807 	ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN,
808 		"cache created: %s\n"), handle->cache_name);
809 
810 	if ((code = krb5_timeofday(handle->context, &now)))
811 		goto error;
812 
813 	/*
814 	 * Get a ticket, use the method specified in init_type.
815 	 */
816 	creds.times.starttime = 0; /* start timer at KDC */
817 	creds.times.endtime = 0; /* endtime will be limited by service */
818 
819 	memset(&opt, 0, sizeof (opt));
820 	krb5_get_init_creds_opt_init(&opt);
821 
822 	if (creds.times.endtime) {
823 		if (creds.times.starttime)
824 			starttime = creds.times.starttime;
825 		else
826 			starttime = now;
827 
828 		krb5_get_init_creds_opt_set_tkt_life(&opt,
829 			creds.times.endtime - starttime);
830 	}
831 	code = krb5_unparse_name(handle->context, creds.server, &server);
832 	if (code)
833 		goto error;
834 
835 	if (init_type == INIT_PASS) {
836 		code = krb5_get_init_creds_password(handle->context,
837 			&creds, creds.client, pass, NULL,
838 			NULL, creds.times.starttime,
839 			server, &opt);
840 	} else if (init_type == INIT_SKEY) {
841 		krb5_keytab kt = NULL;
842 
843 		if (!(pass && (code = krb5_kt_resolve(handle->context,
844 					pass, &kt)))) {
845 			code = krb5_get_init_creds_keytab(
846 					handle->context,
847 					&creds, creds.client, kt,
848 					creds.times.starttime,
849 					server, &opt);
850 
851 			if (pass)
852 				krb5_kt_close(handle->context, kt);
853 		}
854 	}
855 
856 	/* Improved error messages */
857 	if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY)
858 		code = KADM5_BAD_PASSWORD;
859 
860 	if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
861 		code = KADM5_SECURE_PRINC_MISSING;
862 
863 	if (code != 0) {
864 		ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
865 			"failed to obtain credentials cache\n"));
866 		goto error;
867 	}
868 
869 	/*
870 	 * If we got this far, save the creds in the cache.
871 	 */
872 	if (ccache) {
873 		code = krb5_cc_store_cred(handle->context, ccache, &creds);
874 	}
875 
876 	ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "obtained credentials cache\n"));
877 
878 #ifdef ZEROPASSWD
879 	if (pass != NULL)
880 		memset(pass, 0, strlen(pass));
881 #endif
882 
883 	if (init_type != INIT_PASS ||
884 	    handle->params.kpasswd_protocol == KRB5_CHGPWD_RPCSEC) {
885 		code = _kadm5_initialize_rpcsec_gss_handle(handle,
886 					client_name, service_name);
887 		if (code != 0)
888 			goto error;
889 	}
890 
891 	*server_handle = (void *) handle;
892 
893 	if (init_type != INIT_CREDS)
894 		krb5_cc_close(handle->context, ccache);
895 
896 	goto cleanup;
897 
898 error:
899 	/*
900 	* Note that it is illegal for this code to execute if "handle"
901 	* has not been allocated and initialized.  I.e., don't use "goto
902 	* error" before the block of code at the top of the function
903 	* that allocates and initializes "handle".
904 	*/
905 	if (handle->cache_name)
906 	 free(handle->cache_name);
907 	if (handle->destroy_cache && ccache)
908 	 krb5_cc_destroy(handle->context, ccache);
909 	if(handle->clnt && handle->clnt->cl_auth)
910 	  AUTH_DESTROY(handle->clnt->cl_auth);
911 	if(handle->clnt)
912 	  clnt_destroy(handle->clnt);
913 	(void) kadm5_free_config_params(handle->context, &handle->params);
914 
915 cleanup:
916 	if (server)
917 		free(server);
918 
919 	/*
920 	 * Creds server and client members may actually be different
921 	 * from when initially passed into the get_init_* functions.
922 	 * So we must free the members we allocated above since get_init
923 	 * zeros the members out, without freeing first.
924 	 */
925 	if (clientp != NULL) {
926 		krb5_free_principal(handle->context, clientp);
927 		clientp = NULL;
928 	}
929 	if (serverp != NULL) {
930 		krb5_free_principal(handle->context, serverp);
931 		serverp = NULL;
932 	}
933 
934 	krb5_free_cred_contents(handle->context, &creds);
935 
936 	/*
937 	 * Dont clean up the handle if the code is OK (code==0)
938 	 * because it is returned to the caller in the 'server_handle'
939 	 * ptr.
940 	 */
941 	if (code) {
942 		krb5_free_context(handle->context);
943 		free(handle->lhandle);
944 	  free(handle);
945 	}
946 
947 	return (code);
948 }
949 
950 kadm5_ret_t
951 kadm5_destroy(void *server_handle)
952 {
953 	krb5_ccache	    ccache = NULL;
954 	int		    code = KADM5_OK;
955 	kadm5_server_handle_t	handle =
956 	  (kadm5_server_handle_t) server_handle;
957 	OM_uint32 min_stat;
958 
959 	CHECK_HANDLE(server_handle);
960 
961 	if (handle->destroy_cache && handle->cache_name) {
962 	 if ((code = krb5_cc_resolve(handle->context,
963 				     handle->cache_name, &ccache)) == 0)
964 	     code = krb5_cc_destroy (handle->context, ccache);
965 	}
966 	if (handle->cache_name)
967 	 free(handle->cache_name);
968 
969 	if (handle->clnt && handle->clnt->cl_auth) {
970 		/*
971 		 * Since kadm5 doesn't use the default credentials we
972 		 * must clean this up manually.
973 		 */
974 		if (handle->my_cred != GSS_C_NO_CREDENTIAL)
975 			(void) gss_release_cred(&min_stat, &handle->my_cred);
976 		AUTH_DESTROY(handle->clnt->cl_auth);
977 	}
978 	if (handle->clnt)
979 	  clnt_destroy(handle->clnt);
980 	if (handle->lhandle)
981 	    free (handle->lhandle);
982 
983 	kadm5_free_config_params(handle->context, &handle->params);
984 	krb5_free_context(handle->context);
985 
986 	handle->magic_number = 0;
987 	free(handle);
988 
989 	return (code);
990 }
991 
992 /*ARGSUSED*/
993 kadm5_ret_t
994 kadm5_flush(void *server_handle)
995 {
996 	return (KADM5_OK);
997 }
998 
999 int
1000 _kadm5_check_handle(void *handle)
1001 {
1002 	CHECK_HANDLE(handle);
1003 	return (0);
1004 }
1005 
1006 /*
1007  * Stub function for kadmin.  It was created to eliminate the dependency on
1008  * libkdb's ulog functions.  The srv equivalent makes the actual calls.
1009  */
1010 krb5_error_code
1011 kadm5_init_iprop(void *handle)
1012 {
1013 	return (0);
1014 }
1015