Home | History | Annotate | Download | only in clnt
      1 /*
      2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 
      7 
      8 /*
      9  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
     10  */
     11 
     12 /*
     13  * Copyright (C) 1998 by the FundsXpress, INC.
     14  *
     15  * All rights reserved.
     16  *
     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.
     21  *
     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.
     32  *
     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.
     36  */
     37 
     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>
     55 
     56 #include <kadm5/admin.h>
     57 #include <kadm5/kadm_rpc.h>
     58 #include "client_internal.h"
     59 
     60 #include <syslog.h>
     61 #include <gssapi/gssapi.h>
     62 #include <gssapi_krb5.h>
     63 #include <gssapiP_krb5.h>
     64 #include <rpc/clnt.h>
     65 
     66 #include <iprop_hdr.h>
     67 #include "iprop.h"
     68 
     69 #define	ADM_CCACHE  "/tmp/ovsec_adm.XXXXXX"
     70 
     71 static int old_auth_gssapi = 0;
     72 /* connection timeout to kadmind in seconds */
     73 #define		KADMIND_CONNECT_TIMEOUT	25
     74 
     75 int _kadm5_check_handle();
     76 
     77 enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS };
     78 
     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);
     89 
     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)
     98 {
     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);
    103 }
    104 
    105 
    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)
    113 {
    114      return _kadm5_init_any(client_name, INIT_PASS, pass, NULL,
    115 			    service_name, params, struct_version,
    116 			    api_version, db_args, server_handle);
    117 }
    118 
    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)
    126 {
    127      return _kadm5_init_any(client_name, INIT_PASS, pass, NULL,
    128 			    service_name, params, struct_version,
    129 			    api_version, db_args, 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 				 char **db_args,
    138 				 void **server_handle)
    139 {
    140      return _kadm5_init_any(client_name, INIT_SKEY, keytab, NULL,
    141 			    service_name, params, struct_version,
    142 			    api_version, db_args, server_handle);
    143 }
    144 
    145 krb5_error_code  kadm5_free_config_params();
    146 
    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;
    153 {
    154 	OM_uint32 maj_stat, min_stat;
    155 	gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
    156 	OM_uint32 msg_ctx;
    157 
    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"),
    174 			    m);
    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);
    180 
    181 		if (!msg_ctx)
    182 			break;
    183 	}
    184 }
    185 
    186 /*
    187  * Function: display_status
    188  *
    189  * Purpose: displays GSS-API messages
    190  *
    191  * Arguments:
    192  *
    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:
    198  *
    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.
    202  */
    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;
    209 {
    210 	gss_OID mech_oid;
    211 
    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;
    217 	}
    218 
    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);
    221 }
    222 
    223 /*
    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.
    227  */
    228 static int
    229 get_connection(struct netconfig *nconf, struct netbuf netaddr)
    230 {
    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;
    237 
    238 	(void) memset(&tinfo, 0, sizeof (tinfo));
    239 
    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);
    244 	}
    245 
    246 	if (t_bind(fd, (struct t_bind *)NULL, (struct t_bind *)NULL) == -1) {
    247 		(void) close(fd);
    248 		return (-1);
    249 	}
    250 
    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);
    255 	}
    256 
    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;
    261 
    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);
    268 		}
    269 	}
    270 
    271 	/* loop till success or timeout */
    272 	for (;;) {
    273 		if (t_rcvconnect(fd, rcvcall) == 0)
    274 			break;
    275 
    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);
    282 		}
    283 		sleep(1);
    284 	}
    285 
    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);
    292 }
    293 
    294 /*
    295  * Open an RPCSEC_GSS connection and
    296  * get a client handle to use for future RPCSEC calls.
    297  *
    298  * This function is only used when changing passwords and
    299  * the kpasswd_protocol is RPCSEC_GSS
    300  */
    301 static int
    302 _kadm5_initialize_rpcsec_gss_handle(kadm5_server_handle_t handle,
    303 				    char *client_name,
    304 				    char *service_name)
    305 {
    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;
    330 
    331 	/*
    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.
    334 	 */
    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;
    339 	}
    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;
    346 	}
    347 
    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;
    366 	}
    367 
    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;
    373 	}
    374 
    375 	if (nconf == (struct netconfig *)NULL)
    376 		goto error;
    377 
    378 	/* Transform addr to netbuf */
    379 	(void) memset(&netaddr, 0, sizeof (netaddr));
    380 	netaddr.buf = (char *)sin;
    381 
    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;
    390 	}
    391 
    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
    404  	/*
    405 	 * Tell clnt_tli_create that given fd is already connected
    406 	 *
    407 	 * If the service_name and client_name are iprop-centric,
    408 	 * we need to clnt_tli_create to the appropriate RPC prog
    409 	 */
    410 	iprop_svc = strdup(KIPROP_SVC_NAME);
    411 	if (iprop_svc == NULL)
    412 		return (ENOMEM);
    413 
    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);
    419 	}
    420 	else
    421 		handle->clnt = clnt_tli_create(fd, nconf, NULL,
    422 				    KADM, KADMVERS, 0, 0);
    423 
    424 	if (iprop_svc)
    425 		free(iprop_svc);
    426 
    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;
    433 	}
    434 	/*
    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.
    437 	 */
    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;
    445 	}
    446 
    447 	handle->lhandle->clnt = handle->clnt;
    448 
    449 	/* now that handle->clnt is set, we can check the handle */
    450 	if (code = _kadm5_check_handle((void *) handle))
    451 		goto error;
    452 
    453 	/*
    454 	 * The RPC connection is open; establish the GSS-API
    455 	 * authentication context.
    456 	 */
    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);
    463 
    464 	(void) krb5_setenv("KRB5CCNAME", handle->cache_name, 1);
    465 
    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;
    479 	}
    480 
    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;
    486 	}
    487 
    488 	oid_set.count = 1;
    489 	oid_set.elements = mech_oid;
    490 
    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;
    502 	}
    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 */
    517 
    518 	if (ccname_orig) {
    519 		(void) krb5_setenv("KRB5CCNAME", ccname_orig, 1);
    520 		free(ccname_orig);
    521 	} else
    522 		(void) krb5_unsetenv("KRB5CCNAME");
    523 
    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;
    532 	}
    533 
    534 	/*
    535 	 * Bypass the remainder of the code and return straightaway
    536 	 * if the gss service requested is kiprop
    537 	 */
    538 	if (iprop_enable == B_TRUE) {
    539 		code = 0;
    540 		goto cleanup;
    541 	}
    542 
    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;
    550 	}
    551 
    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;
    559 	}
    560 error:
    561 cleanup:
    562 
    563 	if (handlep != (void *) NULL)
    564 		(void) endnetconfig(handlep);
    565 	/*
    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.
    569 	 */
    570 	if (code && (gss_client_creds != GSS_C_NO_CREDENTIAL))
    571 		(void) gss_release_cred(&minor_stat, &gss_client_creds);
    572 
    573 	return (code);
    574 }
    575 
    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)
    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      krb5_principal saved_server = NULL;
    602      bool_t cpw = FALSE;
    603 
    604 	ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN,
    605 		"entering kadm5_init_any\n"));
    606      if (! server_handle) {
    607 	 return EINVAL;
    608      }
    609 
    610      if (! (handle = malloc(sizeof(*handle)))) {
    611 	  return ENOMEM;
    612      }
    613      if (! (handle->lhandle = malloc(sizeof(*handle)))) {
    614 	  free(handle);
    615 	  return ENOMEM;
    616      }
    617 
    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;
    628 
    629     kret = krb5_init_context(&handle->context);
    630 	if (kret) {
    631 		free(handle->lhandle);
    632 		free(handle);
    633 		return (kret);
    634 	}
    635 
    636      if(service_name == NULL || client_name == NULL) {
    637 	krb5_free_context(handle->context);
    638 	free(handle->lhandle);
    639 	free(handle);
    640 	return EINVAL;
    641      }
    642      memset((char *) &creds, 0, sizeof(creds));
    643 
    644      /*
    645       * Verify the version numbers before proceeding; we can't use
    646       * CHECK_HANDLE because not all fields are set yet.
    647       */
    648      GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION,
    649 			  KADM5_NEW_LIB_API_VERSION);
    650 
    651      /*
    652       * Acquire relevant profile entries.  In version 2, merge values
    653       * in params_in with values from profile, based on
    654       * params_in->mask.
    655       *
    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.
    659       */
    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;
    665 	}
    666 
    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)
    676 
    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;
    685      }
    686 
    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);
    695      }
    696 
    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)
    703 
    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;
    714      }
    715 
    716 	/*
    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.
    721 	 */
    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;
    727 	}
    728 	clientp = creds.client;
    729 
    730 	if (strncmp(service_name, KADM5_CHANGEPW_HOST_SERVICE,
    731 	    strlen(KADM5_CHANGEPW_HOST_SERVICE)) == 0)
    732 		cpw = TRUE;
    733 
    734 	if (init_type == INIT_PASS &&
    735 	    handle->params.kpasswd_protocol == KRB5_CHGPWD_CHANGEPW_V2 &&
    736 	    cpw == TRUE) {
    737 		/*
    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.
    745 		 */
    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;
    754 		}
    755 		sprintf(newsvcname, "%s@%s", KADM5_CHANGEPW_SERVICE,
    756 			handle->params.realm);
    757 
    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;
    765 		}
    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);
    774 
    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;
    780 		}
    781 	}
    782 	serverp = creds.server;
    783 
    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)
    787 
    788 
    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;
    797 	  }
    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;
    808 	  }
    809 	  sprintf(handle->cache_name, "FILE:%s", ADM_CCACHE);
    810 	  mktemp(handle->cache_name + strlen("FILE:"));
    811 #endif
    812 	  {
    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++);
    817 	  }
    818 
    819 	  if ((code = krb5_cc_resolve(handle->context, handle->cache_name,
    820 				      &ccache)))
    821 	       goto error;
    822 
    823 	  if ((code = krb5_cc_initialize (handle->context, ccache,
    824 					  creds.client)))
    825 	       goto error;
    826 
    827 	  handle->destroy_cache = 1;
    828      }
    829      handle->lhandle->cache_name = handle->cache_name;
    830 	ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN,
    831 		"cache created: %s\n"), handle->cache_name);
    832 
    833      if ((code = krb5_timeofday(handle->context, &now)))
    834 	  goto error;
    835 
    836      /*
    837       * Get a ticket, use the method specified in init_type.
    838       */
    839 
    840      creds.times.starttime = 0; /* start timer at KDC */
    841      creds.times.endtime = 0; /* endtime will be limited by service */
    842 
    843 	memset(&opt, 0, sizeof (opt));
    844 	krb5_get_init_creds_opt_init(&opt);
    845 
    846 	if (creds.times.endtime) {
    847 		if (creds.times.starttime)
    848 			starttime = creds.times.starttime;
    849 		else
    850 			starttime = now;
    851 
    852 		krb5_get_init_creds_opt_set_tkt_life(&opt,
    853 			creds.times.endtime - starttime);
    854 	}
    855 	code = krb5_unparse_name(handle->context, creds.server, &server);
    856 	if (code)
    857 		goto error;
    858 
    859 	/*
    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.
    863 	 */
    864 	code = krb5_copy_principal(handle->context, creds.server, &saved_server);
    865 	if (code)
    866 		goto error;
    867 
    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;
    875 
    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);
    883 
    884 	       if (pass) krb5_kt_close(handle->context, kt);
    885 	  }
    886      }
    887 
    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;
    892 
    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;
    898 	}
    899 
    900 	/*
    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.
    907 	 */
    908 	if (init_type != INIT_CREDS) {
    909 		krb5_free_principal(handle->context, creds.server);
    910 	}
    911 	creds.server = saved_server;
    912 
    913 	/*
    914 	 * If we got this far, save the creds in the cache.
    915 	 */
    916 	if (ccache) {
    917 		code = krb5_cc_store_cred(handle->context, ccache, &creds);
    918 	}
    919 
    920 	ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "obtained credentials cache\n"));
    921 
    922 #ifdef ZEROPASSWD
    923      if (pass != NULL)
    924 	  memset(pass, 0, strlen(pass));
    925 #endif
    926 
    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);
    932 
    933 		/*
    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.
    940 		 */
    941 		if (code != 0) {
    942 			if (init_type != INIT_CREDS) {
    943 				krb5_cc_close(handle->context, ccache);
    944 				ccache = NULL;
    945 			}
    946 			goto error;
    947 		}
    948 	}
    949 
    950 	*server_handle = (void *) handle;
    951 
    952 	if (init_type != INIT_CREDS)
    953 		krb5_cc_close(handle->context, ccache);
    954 
    955 	goto cleanup;
    956 
    957 error:
    958      /*
    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".
    963       */
    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);
    973 
    974 cleanup:
    975 	if (server)
    976 		free(server);
    977 
    978 	/*
    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.
    983 	 */
    984 	if (clientp && clientp != creds.client)
    985 		krb5_free_principal(handle->context, clientp);
    986 
    987 	if (serverp && serverp != creds.server)
    988 		krb5_free_principal(handle->context, serverp);
    989 
    990      krb5_free_cred_contents(handle->context, &creds);
    991 
    992 	/*
    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.
    996 	 */
    997      if (code) {
    998 		krb5_free_context(handle->context);
    999 		free(handle->lhandle);
   1000 	  free(handle);
   1001 	}
   1002 
   1003      return code;
   1004 }
   1005 
   1006 kadm5_ret_t
   1007 kadm5_destroy(void *server_handle)
   1008 {
   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;
   1014 
   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.
   1022  */
   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);
   1028      }
   1029 #endif
   1030      if (handle->cache_name)
   1031 	 free(handle->cache_name);
   1032      if (handle->clnt && handle->clnt->cl_auth) {
   1033 		/*
   1034 		 * Since kadm5 doesn't use the default credentials we
   1035 		 * must clean this up manually.
   1036 		 */
   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);
   1040 	}
   1041      if (handle->clnt)
   1042 	  clnt_destroy(handle->clnt);
   1043      if (handle->lhandle)
   1044           free (handle->lhandle);
   1045 
   1046      kadm5_free_config_params(handle->context, &handle->params);
   1047      krb5_free_context(handle->context);
   1048 
   1049      handle->magic_number = 0;
   1050      free(handle);
   1051 
   1052      return code;
   1053 }
   1054 /* not supported on client */
   1055 kadm5_ret_t kadm5_lock(void *server_handle)
   1056 {
   1057     return EINVAL;
   1058 }
   1059 
   1060 /* not supported on client */
   1061 kadm5_ret_t kadm5_unlock(void *server_handle)
   1062 {
   1063     return EINVAL;
   1064 }
   1065 
   1066 kadm5_ret_t kadm5_flush(void *server_handle)
   1067 {
   1068      return KADM5_OK;
   1069 }
   1070 
   1071 int _kadm5_check_handle(void *handle)
   1072 {
   1073      CHECK_HANDLE(handle);
   1074      return 0;
   1075 }
   1076 
   1077 krb5_error_code kadm5_init_krb5_context (krb5_context *ctx)
   1078 {
   1079     return krb5_init_context(ctx);
   1080 }
   1081 
   1082 /*
   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.
   1085  */
   1086 krb5_error_code
   1087 kadm5_init_iprop(void *handle)
   1088 {
   1089 	return (0);
   1090 }
   1091