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