1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
10  *
11  *	Openvision retains the copyright to derivative works of
12  *	this source code.	Do *NOT* create a derivative of this
13  *	source code before consulting with your legal department.
14  *	Do *NOT* integrate *ANY* of this source code into another
15  *	product before consulting with your legal department.
16  *
17  *	For further information, read the top-level Openvision
18  *	copyright which is contained in the top-level MIT Kerberos
19  *	copyright.
20  *
21  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
22  *
23  */
24 
25 /*
26  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
27  */
28 
29 /*
30  * Copyright (C) 1998 by the FundsXpress, INC.
31  *
32  * All rights reserved.
33  *
34  * Export of this software from the United States of America may require
35  * a specific license from the United States Government.  It is the
36  * responsibility of any person or organization contemplating export to
37  * obtain such a license before exporting.
38  *
39  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
40  * distribute this software and its documentation for any purpose and
41  * without fee is hereby granted, provided that the above copyright
42  * notice appear in all copies and that both that copyright notice and
43  * this permission notice appear in supporting documentation, and that
44  * the name of FundsXpress. not be used in advertising or publicity pertaining
45  * to distribution of the software without specific, written prior
46  * permission.  FundsXpress makes no representations about the suitability of
47  * this software for any purpose.  It is provided "as is" without express
48  * or implied warranty.
49  *
50  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
51  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
52  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
53  */
54 
55 
56 /*
57  * SUNWresync121 XXX
58  * Beware future resyncers, this file is much diff from MIT (1.0...)
59  */
60 
61 #include    <stdio.h>
62 #include    <stdio_ext.h>
63 #include    <signal.h>
64 #include    <syslog.h>
65 #include    <sys/types.h>
66 #ifdef _AIX
67 #include    <sys/select.h>
68 #endif
69 #include    <sys/time.h>
70 #include    <sys/socket.h>
71 #include    <unistd.h>
72 #include    <netinet/in.h>
73 #include    <arpa/inet.h>  /* inet_ntoa */
74 #include    <gssapi/gssapi.h>
75 #include    <rpc/rpc.h>
76 #include    <kadm5/admin.h>
77 #include    <kadm5/kadm_rpc.h>
78 #include    <server_acl.h>
79 #include    <krb5/adm_proto.h>
80 #include    <string.h>
81 #include    <kadm5/server_internal.h>
82 #include    <gssapi_krb5.h>
83 #include    <libintl.h>
84 #include    <locale.h>
85 #include    <sys/resource.h>
86 #include    <kdb/kdb_log.h>
87 #include    <kdb_kt.h>
88 
89 #include <rpc/rpcsec_gss.h>
90 #include    "misc.h"
91 
92 #ifndef	FD_SETSIZE
93 #define	FD_SETSIZE	256
94 #endif
95 
96 #ifndef MAX
97 #define	MAX(a, b)	(((a) > (b)) ? (a) : (b))
98 #endif
99 
100 #if defined(NEED_DAEMON_PROTO)
101 extern int daemon(int, int);
102 #endif
103 
104 
105 
106 static int signal_request_exit = 0;
107 static int schpw;
108 kadm5_config_params chgpw_params;
109 void kadm_svc_run(void);
110 void setup_signal_handlers(iprop_role iproprole);
111 void sig_exit(int);
112 void sig_pipe(int);
113 krb5_error_code log_kt_error(char*, char*);
114 
115 #ifdef POSIX_SIGNALS
116 static struct sigaction s_action;
117 #endif /* POSIX_SIGNALS */
118 
119 
120 #define	TIMEOUT	15
121 
122 typedef struct _auth_gssapi_name {
123 	char *name;
124 	gss_OID type;
125 } auth_gssapi_name;
126 
127 gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL;
128 void *global_server_handle;
129 
130 /*
131  * This is a kludge, but the server needs these constants to be
132  * compatible with old clients.  They are defined in <kadm5/admin.h>,
133  * but only if USE_KADM5_API_VERSION == 1.
134  */
135 #define	OVSEC_KADM_ADMIN_SERVICE_P	"ovsec_adm@admin"
136 #define	OVSEC_KADM_CHANGEPW_SERVICE_P	"ovsec_adm@changepw"
137 
138 /*
139  * This enables us to set the keytab that gss_acquire_cred uses, but
140  * it also restricts us to linking against the Kv5 GSS-API library.
141  * Since this is *k*admind, that shouldn't be a problem.
142  */
143 extern char *krb5_overridekeyname;
144 
145 extern void krb5_iprop_prog_1();
146 extern kadm5_ret_t kiprop_get_adm_host_srv_name(
147 	krb5_context,
148 	const char *,
149 	char **);
150 
151 static krb5_context context;  /* XXX yuck.  the signal handlers need this */
152 
153 static krb5_context hctx;
154 
155 in_port_t l_port = 0;	/* global local port num, for BSM audits */
156 
157 int nofork = 0; /* global; don't fork (debug mode) */
158 
159 
160 /*
161  * Function: usage
162  *
163  * Purpose: print out the server usage message
164  *
165  * Arguments:
166  * Requires:
167  * Effects:
168  * Modifies:
169  */
170 
171 static void usage()
172 {
173 	fprintf(stderr, gettext("Usage: kadmind [-x db_args]* [-r realm] [-m] [-d] "
174 	    "[-p port-number]\n"));
175 	exit(1);
176 }
177 
178 /*
179  * Function: display_status
180  *
181  * Purpose: displays GSS-API messages
182  *
183  * Arguments:
184  *
185  * 	msg		a string to be displayed with the message
186  * 	maj_stat	the GSS-API major status code
187  * 	min_stat	the GSS-API minor status code
188  *
189  * Effects:
190  *
191  * The GSS-API messages associated with maj_stat and min_stat are
192  * displayed on stderr, each preceeded by "GSS-API error <msg>: " and
193  * followed by a newline.
194  */
195 static void display_status_1(char *, OM_uint32, int);
196 
197 static void display_status(msg, maj_stat, min_stat)
198      char *msg;
199      OM_uint32 maj_stat;
200      OM_uint32 min_stat;
201 {
202      display_status_1(msg, maj_stat, GSS_C_GSS_CODE);
203      display_status_1(msg, min_stat, GSS_C_MECH_CODE);
204 }
205 
206 static void display_status_1(m, code, type)
207      char *m;
208      OM_uint32 code;
209      int type;
210 {
211 	OM_uint32 maj_stat, min_stat;
212 	gss_buffer_desc msg;
213 	OM_uint32 msg_ctx;
214 
215 	msg_ctx = 0;
216 	while (1) {
217 		maj_stat = gss_display_status(&min_stat, code,
218 					      type, GSS_C_NULL_OID,
219 					      &msg_ctx, &msg);
220 		fprintf(stderr, "GSS-API error %s: %s\n", m,
221 			(char *)msg.value);
222 		(void) gss_release_buffer(&min_stat, &msg);
223 
224 		if (!msg_ctx)
225 			break;
226 	}
227 }
228 
229 
230 /*
231  * Solaris Kerberos: the following prototypes are needed because these are
232  * private interfaces that do not have prototypes in any .h
233  */
234 
235 extern struct hostent   *res_getipnodebyaddr(const void *, size_t, int, int *);
236 extern void             res_freehostent(struct hostent *);
237 
238 static void
239 freedomnames(char **npp)
240 {
241 	char **tpp;
242 
243 	if (npp) {
244 		tpp = npp;
245 		while (*tpp++) {
246 			free(*(tpp-1));
247 		}
248 		free(npp);
249 	}
250 }
251 
252 /*
253  * Construct a list of uniq FQDNs of all the net interfaces (except
254  * krb5.conf master dups) and return it in arg 'dnames'.
255  *
256  * On successful return (0), caller must call freedomnames()
257  * to free memory.
258  */
259 static int
260 getdomnames(krb5_context ctx, char *realm, char ***dnames)
261 {
262 	krb5_address **addresses = NULL;
263 	krb5_address *a = NULL;
264 	struct hostent *hp = NULL;
265 	int ret, i, result=0, error;
266 	char **npp = NULL, **tpp=NULL;
267 	int dup=0, n = 0;
268 	char *cfhost = NULL; /* krb5 conf file master hostname */
269 
270 	if (ret = kadm5_get_master(ctx, realm, &cfhost)) {
271 		return (ret);
272 	}
273 
274 	ret = krb5_os_localaddr(ctx, &addresses);
275 	if (ret != 0) {
276 		if (nofork)
277 			(void) fprintf(stderr,
278 				    "kadmind: get localaddrs failed: %s",
279 				    error_message(ret));
280 		result = ret;
281 		goto err;
282 	}
283 
284 
285 	for (i=0; addresses[i]; i++) {
286 		a = addresses[i];
287 		hp = res_getipnodebyaddr(a->contents, a->length,
288 					a->addrtype == ADDRTYPE_INET
289 					? AF_INET : AF_INET6,
290 					&error);
291 		if (hp != NULL) {
292 
293 			/* skip master host in krb5.conf */
294 			if (strcasecmp(cfhost, hp->h_name) == 0) {
295 				res_freehostent(hp);
296 				hp = NULL;
297 				continue;
298 			}
299 
300 			dup = 0;
301 			tpp = npp;
302 			/* skip if hostname already exists in list */
303 			while (tpp && *tpp++) {
304 				if (strcasecmp(*(tpp-1), hp->h_name) == 0) {
305 					dup++;
306 					break;
307 				}
308 			}
309 
310 			if (dup) {
311 				res_freehostent(hp);
312 				hp = NULL;
313 				continue;
314 			}
315 
316 			npp = realloc(npp, sizeof(char *) * (n + 2));
317 			if (!npp) {
318 				result = ENOMEM;
319 				goto err;
320 			}
321 			npp[n] = strdup(hp->h_name);
322 			if (!npp[n]) {
323 				result = ENOMEM;
324 				goto err;
325 			}
326 			npp[n+1] = NULL;
327 			n++;
328 
329 			res_freehostent(hp);
330 			hp = NULL;
331 			result = 0;
332 		}
333 
334 	}
335 
336 #ifdef DEBUG
337 	printf("getdomnames: n=%d, i=%d, npp=%p\n", n, i, npp);
338 	tpp = npp;
339 	while (tpp && *tpp++) {
340 		printf("tpp=%s\n", *(tpp-1));
341 	}
342 #endif
343 
344 	goto out;
345 
346 err:
347 	if (npp) {
348 		freedomnames(npp);
349 		npp = NULL;
350 	}
351 
352 	if (hp) {
353 		res_freehostent(hp);
354 		hp = NULL;
355 	}
356 
357 out:
358 	if (cfhost) {
359 		free (cfhost);
360 		cfhost = NULL;
361 	}
362 	if (addresses) {
363 		krb5_free_addresses(ctx, addresses);
364 		addresses = NULL;
365 	}
366 
367 	if (result == 0)
368 		*dnames = npp;
369 
370 	return (result);
371 }
372 
373 /*
374  * Set the rpcsec_gss svc names for all net interfaces.
375  */
376 static void
377 set_svc_domnames(char *svcname, char **dnames,
378 		u_int program, u_int version)
379 {
380 	bool_t ret;
381 	char **tpp = dnames;
382 
383 	if (!tpp)
384 		return;
385 
386 	while (*tpp++) {
387 		/* MAX_NAME_LEN from rpc/rpcsec_gss.h */
388 		char name[MAXHOSTNAMELEN+MAX_NAME_LEN+2] = {0};
389 		(void) snprintf(name, sizeof(name), "%s@%s",
390 				svcname, *(tpp-1));
391 		ret = rpc_gss_set_svc_name(name,
392 					"kerberos_v5", 0,
393 					program, version);
394 		if (nofork && ret)
395 			(void) fprintf(stderr,
396 				    "rpc_gss_set_svc_name success: %s\n",
397 				    name);
398 	}
399 }
400 
401 
402 
403 
404 int
405 main(int argc, char *argv[])
406 {
407 	SVCXPRT *transp;
408 	extern char *optarg;
409 	extern int optind, opterr;
410 	int ret, rlen, oldnames = 0;
411 	OM_uint32 OMret, major_status, minor_status;
412 	char *whoami;
413 	FILE *acl_file;
414 	gss_buffer_desc in_buf;
415 	struct servent *srv;
416 	struct sockaddr_in addr;
417 	struct sockaddr_in *sin;
418 	int s;
419 	int optchar;
420 	struct netconfig *nconf;
421 	void *handlep;
422 	int fd;
423 	struct t_info tinfo;
424 	struct t_bind tbindstr, *tres;
425 
426 	struct t_optmgmt req, resp;
427 	struct opthdr *opt;
428 	char reqbuf[128];
429 	struct rlimit rl;
430 
431 	char *kiprop_name = NULL; /* IProp svc name */
432 	kdb_log_context *log_ctx;
433 	kadm5_server_handle_t handle;
434 	krb5_context ctx;
435 
436 	kadm5_config_params params;
437 	char **db_args      = NULL;
438 	int    db_args_size = 0;
439 	auth_gssapi_name names[6];
440      	gss_buffer_desc gssbuf;
441      	gss_OID nt_krb5_name_oid;
442 
443 	char **dnames = NULL;
444 	int retdn;
445 	int iprop_supported;
446 
447 	/* Solaris Kerberos: Stores additional error messages */
448 	char *emsg = NULL;
449 
450 	/* Solaris Kerberos: Indicates whether loalhost is master or not */
451 	krb5_boolean is_master;
452 
453 	/* Solaris Kerberos: Used for checking acl file */
454 	gss_name_t name;
455 
456 	/* This is OID value the Krb5_Name NameType */
457      	gssbuf.value = "{1 2 840 113554 1 2 2 1}";
458      	gssbuf.length = strlen(gssbuf.value);
459      	major_status = gss_str_to_oid(&minor_status, &gssbuf,
460 				    &nt_krb5_name_oid);
461      	if (major_status != GSS_S_COMPLETE) {
462 		fprintf(stderr,
463 			gettext("Couldn't create KRB5 Name NameType OID\n"));
464 		display_status("str_to_oid", major_status, minor_status);
465 		exit(1);
466      	}
467 
468 	names[0].name = names[1].name = names[2].name =
469 		names[3].name = names[4].name  = names[5].name =NULL;
470 	names[0].type = names[1].type = names[2].type =
471 		names[3].type = names[4].type = names[5].type =
472 		(gss_OID) nt_krb5_name_oid;
473 
474 	whoami = (strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]);
475 
476 	(void) setlocale(LC_ALL, "");
477 
478 #if !defined(TEXT_DOMAIN)  /* Should be defined by cc -D */
479 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
480 #endif
481 
482 	(void) textdomain(TEXT_DOMAIN);
483 
484 	nofork = 0;
485 
486 	memset((char *) &params, 0, sizeof (params));
487 
488 	while ((optchar = getopt(argc, argv, "r:mdp:x:")) != EOF) {
489 		switch (optchar) {
490 		case 'r':
491 			if (!optarg)
492 				usage();
493 			params.realm = optarg;
494 			params.mask |= KADM5_CONFIG_REALM;
495 			break;
496 		case 'm':
497 			params.mkey_from_kbd = 1;
498 			params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
499 			break;
500 		case 'd':
501 			nofork = 1;
502 			break;
503 		case 'p':
504 			if (!optarg)
505 				usage();
506 			params.kadmind_port = atoi(optarg);
507 			params.mask |= KADM5_CONFIG_KADMIND_PORT;
508 			break;
509 		case 'x':
510 			if (!optarg)
511 				usage();
512 			db_args_size++;
513 			{
514 			    char **temp = realloc( db_args,
515 				sizeof(char*) * (db_args_size+1)); /* one for NULL */
516 			    if( temp == NULL )
517 			    {
518 				fprintf(stderr, gettext("%s: cannot initialize. Not enough memory\n"),
519 				    whoami);
520 				exit(1);
521 			    }
522 			    db_args = temp;
523 			}
524 			db_args[db_args_size-1] = optarg;
525 			db_args[db_args_size]   = NULL;
526 			break;
527 		case '?':
528 		default:
529 			usage();
530 		}
531 	}
532 
533 
534 	if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
535 		rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, FD_SETSIZE);
536 		(void) setrlimit(RLIMIT_NOFILE, &rl);
537 		(void) enable_extended_FILE_stdio(-1, -1);
538 	}
539 
540 	if (ret = krb5_init_context(&context)) {
541 		fprintf(stderr,
542 		    gettext("%s: %s while initializing context, aborting\n"),
543 		    whoami, error_message(ret));
544 		exit(1);
545 	}
546 
547 	krb5_klog_init(context, "admin_server", whoami, 1);
548     /* SUNW14resync */
549 #if 0
550     krb5_klog_syslog(LOG_INFO, "Seeding random number generator");
551           ret = krb5_c_random_os_entropy(context, 1, NULL);
552 	if(ret) {
553 	krb5_klog_syslog(LOG_ERR, "Error getting random seed: %s, aborting",
554 			     error_message(ret));
555 	exit(1);
556 	}
557 #endif
558 
559 	/*
560 	 * When using the Horowitz/IETF protocol for
561 	 * password changing, the default port is 464
562 	 * (officially recognized by IANA)
563 	 *
564 	 * DEFAULT_KPASSWD_PORT -> 464
565 	 */
566 	chgpw_params.kpasswd_port = DEFAULT_KPASSWD_PORT;
567 	chgpw_params.mask |= KADM5_CONFIG_KPASSWD_PORT;
568 	chgpw_params.kpasswd_protocol = KRB5_CHGPWD_CHANGEPW_V2;
569 	chgpw_params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL;
570 
571 	if (ret = kadm5_get_config_params(context, NULL, NULL, &chgpw_params,
572 		&chgpw_params)) {
573 		/* Solaris Kerberos: Remove double "whoami" */
574 		krb5_klog_syslog(LOG_ERR, gettext("%s while initializing,"
575 		    " aborting"), error_message(ret));
576 		fprintf(stderr,
577 		    gettext("%s: %s while initializing, aborting\n"),
578 		    whoami, error_message(ret));
579 		krb5_klog_close(context);
580 		exit(1);
581 	}
582 
583 	/*
584 	 * We now setup the socket and bind() to port 464, so that
585 	 * kadmind can now listen to and process change-pwd requests
586 	 * from non-Solaris Kerberos V5 clients such as Microsoft,
587 	 * MIT, AIX, HP etc
588 	 */
589 	if ((schpw = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
590 		krb5_klog_syslog(LOG_ERR, gettext( "cannot create simple "
591 				"chpw socket: %s"), error_message(errno));
592 		fprintf(stderr, gettext("Cannot create simple chpw "
593 					"socket: %s"), error_message(errno));
594 		krb5_klog_close(context);
595 		exit(1);
596 	}
597 
598 	memset(&addr, 0, sizeof(addr));
599 	addr.sin_family = AF_INET;
600 	addr.sin_addr.s_addr = INADDR_ANY;
601 	addr.sin_port = htons(chgpw_params.kpasswd_port);
602 
603 	if (bind(schpw, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
604 		char portbuf[32];
605 		int oerrno = errno;
606 		fprintf(stderr, gettext("%s: Cannot bind socket.\n"), whoami);
607 		fprintf(stderr, gettext("bind: %s\n"), error_message(oerrno));
608 		errno = oerrno;
609 		(void) snprintf(portbuf, sizeof (portbuf), "%d",
610 				ntohs(addr.sin_port));
611 		krb5_klog_syslog(LOG_ERR, gettext("cannot bind simple "
612 				"chpw socket: %s"), error_message(oerrno));
613 		if(oerrno == EADDRINUSE) {
614 			char *w = strrchr(whoami, '/');
615 			if (w) {
616 				w++;
617 			}
618 			else {
619 				w = whoami;
620 			}
621 			fprintf(stderr, gettext(
622 				"This probably means that another %s process\n"
623 				"is already running, or that another program\n"
624 				"is using the server port (number %d).\n"
625 				"If another %s is already running, you should\n"
626 				"kill it before restarting the server.\n"),
627 				w, ntohs(addr.sin_port), w);
628 		}
629 		krb5_klog_close(context);
630 		exit(1);
631 	}
632 
633 	if (ret = kadm5_get_config_params(context, NULL, NULL, &params,
634 		&params)) {
635 		/* Solaris Kerberos: Remove double "whoami" */
636 		krb5_klog_syslog(LOG_ERR, gettext("%s while initializing,"
637 		    " aborting"), error_message(ret));
638 		fprintf(stderr,
639 		    gettext("%s: %s while initializing, aborting\n"),
640 		    whoami, error_message(ret));
641 		krb5_klog_close(context);
642 		exit(1);
643 	}
644 #define	REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE)
645 
646 	if ((params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
647 		/* Solaris Kerberos: Keep error messages consistent */
648 		krb5_klog_syslog(LOG_ERR,
649 		    gettext("Missing required configuration values "
650 			"(%lx) while initializing, aborting"),
651 		    (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS);
652 		fprintf(stderr,
653 		    gettext("%s: Missing required configuration values "
654 			"(%lx) while initializing, aborting\n"), whoami,
655 		    (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS);
656 		krb5_klog_close(context);
657 		exit(1);
658 	}
659 
660 	/* Solaris Kerberos: Ensure that kadmind is only run on a master kdc */
661 	if (ret = kadm5_is_master(context, params.realm, &is_master)){
662 		krb5_klog_syslog(LOG_ERR,
663 		    gettext("Failed to determine whether host is master "
664 		    "KDC for realm %s: %s"), params.realm,
665 		    error_message(ret));
666 		fprintf(stderr,
667 		    gettext("%s: Failed to determine whether host is master "
668 		    "KDC for realm %s: %s\n"), whoami, params.realm,
669 		    error_message(ret));
670 		krb5_klog_close(context);
671 		exit(1);
672 	}
673 
674 	if (is_master == FALSE) {
675 		char *master = NULL;
676 		kadm5_get_master(context, params.realm, &master);
677 
678 		krb5_klog_syslog(LOG_ERR,
679 		    gettext("%s can only be run on the master KDC, %s, for "
680 		    "realm %s"), whoami, master ? master : "unknown",
681 		    params.realm);
682 		fprintf(stderr,
683 		    gettext("%s: %s can only be run on the master KDC, %s, for "
684 		    "realm %s\n"), whoami, whoami, master ? master: "unknown",
685 		    params.realm);
686 		krb5_klog_close(context);
687 		exit(1);
688 	}
689 
690 	memset((char *) &addr, 0, sizeof (struct sockaddr_in));
691 	addr.sin_family = AF_INET;
692 	addr.sin_addr.s_addr = INADDR_ANY;
693 	l_port = addr.sin_port = htons(params.kadmind_port);
694 	sin = &addr;
695 
696 	if ((handlep = setnetconfig()) == (void *) NULL) {
697 		(void) krb5_klog_syslog(LOG_ERR,
698 		    gettext("cannot get any transport information"));
699 		krb5_klog_close(context);
700 		exit(1);
701 	}
702 	while (nconf = getnetconfig(handlep)) {
703 		if ((nconf->nc_semantics == NC_TPI_COTS_ORD) &&
704 		    (strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
705 		    (strcmp(nconf->nc_proto, NC_TCP) == 0))
706 			break;
707 	}
708 
709 	if (nconf == (struct netconfig *) NULL) {
710 		(void) endnetconfig(handlep);
711 		krb5_klog_close(context);
712 		exit(1);
713 	}
714 	fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
715 	if (fd == -1) {
716 		krb5_klog_syslog(LOG_ERR,
717 		    gettext("unable to open connection for ADMIN server"));
718 		krb5_klog_close(context);
719 		exit(1);
720 	}
721 	/* LINTED */
722 	opt = (struct opthdr *) reqbuf;
723 	opt->level = SOL_SOCKET;
724 	opt->name = SO_REUSEADDR;
725 	opt->len = sizeof (int);
726 
727 	/*
728 	 * The option value is "1".  This will allow the server to restart
729 	 * whilst the previous process is cleaning up after itself in a
730 	 * FIN_WAIT_2 or TIME_WAIT state.  If another process is started
731 	 * outside of smf(5) then bind will fail anyway, which is what we want.
732 	 */
733 	reqbuf[sizeof (struct opthdr)] = 1;
734 
735 	req.flags = T_NEGOTIATE;
736 	req.opt.len = sizeof (struct opthdr) + opt->len;
737 	req.opt.buf = (char *) opt;
738 
739 	resp.flags = 0;
740 	resp.opt.buf = reqbuf;
741 	resp.opt.maxlen = sizeof (reqbuf);
742 
743 	if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
744 		t_error("t_optmgmt");
745 		exit(1);
746 	}
747 	/* Transform addr to netbuf */
748 
749 	tres = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR);
750 	if (tres == NULL) {
751 		(void) t_close(fd);
752 		(void) krb5_klog_syslog(LOG_ERR,
753 					gettext("cannot allocate netbuf"));
754 		krb5_klog_close(context);
755 		exit(1);
756 	}
757 	tbindstr.qlen = 8;
758 	tbindstr.addr.buf = (char *) sin;
759 	tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr);
760 	sin = (struct sockaddr_in *) tbindstr.addr.buf;
761 	/* SUNWresync121 XXX (void) memset(&addr, 0, sizeof(addr)); */
762 
763 	if (t_bind(fd, &tbindstr, tres) < 0) {
764 		int oerrno = errno;
765 		fprintf(stderr, gettext("%s: Cannot bind socket.\n"), whoami);
766 		fprintf(stderr, gettext("bind: %s\n"), error_message(oerrno));
767 		errno = oerrno;
768 		krb5_klog_syslog(LOG_ERR, gettext("Cannot bind socket: %s"),
769 		    error_message(errno));
770 		if (oerrno == EADDRINUSE) {
771 			char *w = strrchr(whoami, '/');
772 
773 			if (w) {
774 				w++;
775 			} else {
776 				w = whoami;
777 			}
778 			fprintf(stderr, gettext(
779 				"This probably means that another %s "
780 				"process is already\n"
781 				"running, or that another program is using "
782 				"the server port (number %d)\n"
783 				"after being assigned it by the RPC "
784 				"portmap deamon.	If another\n"
785 				"%s is already running, you should kill "
786 				"it before\n"
787 				"restarting the server. If, on the other hand, "
788 				"another program is\n"
789 				"using the server port, you should kill it "
790 				"before running\n"
791 				"%s, and ensure that the conflict does "
792 				"not occur in the\n"
793 				"future by making sure that %s is started "
794 				"on reboot\n"
795 				"before portmap.\n"),
796 			    w, ntohs(addr.sin_port), w, w, w);
797 			krb5_klog_syslog(LOG_ERR,
798 			    gettext("Check for already-running %s or for "
799 				"another process using port %d"), w,
800 			    htons(addr.sin_port));
801 		}
802 		krb5_klog_close(context);
803 		exit(1);
804 	}
805 	transp = svc_tli_create(fd, nconf, NULL, 0, 0);
806 	(void) t_free((char *) tres, T_BIND);
807 	if (transp == NULL) {
808 		fprintf(stderr, gettext("%s: Cannot create RPC service.\n"),
809 			whoami);
810 		krb5_klog_syslog(LOG_ERR, gettext("Cannot create RPC service: %m"));
811 		krb5_klog_close(context);
812 		exit(1);
813 	}
814 	if (!svc_register(transp, KADM, KADMVERS, kadm_1, 0)) {
815 		fprintf(stderr,
816 		    gettext("%s: Cannot register RPC service.\n"), whoami);
817 		krb5_klog_syslog(LOG_ERR,
818 		    gettext("Cannot register RPC service, failing."));
819 		krb5_klog_close(context);
820 		exit(1);
821 	}
822 
823 	/*
824 	 * XXX krb5_defkeyname is an internal library global and should go
825 	 * away
826 	 */
827 	krb5_overridekeyname = params.admin_keytab;
828 
829 	/* Solaris Kerberos:
830 	 * The only service principals which matter here are
831 	 *  -> names[0].name (kadmin/<fqdn>)
832 	 *  -> names[1].name (changepw/<fqdn>)
833 	 * KADM5_ADMIN_SERVICE_P, KADM5_CHANGEPW_SERVICE_P,
834 	 * OVSEC_KADM_ADMIN_SERVICE_P, OVSEC_KADM_CHANGEPW_SERVICE_P
835 	 * are all legacy service princs and calls to rpc_gss_set_svc_name()
836 	 * using these principals will always fail as they are not host
837 	 * based principals.
838 	 */
839 
840 	if (ret = kadm5_get_adm_host_srv_name(context, params.realm,
841 	    &names[0].name)) {
842 		krb5_klog_syslog(LOG_ERR,
843 		    gettext("Cannot get host based service name for admin "
844 		    "principal in realm %s: %s"), params.realm,
845 		    error_message(ret));
846 		fprintf(stderr,
847 		    gettext("%s: Cannot get host based service name for admin "
848 		    "principal in realm %s: %s\n"), whoami, params.realm,
849 		    error_message(ret));
850 		krb5_klog_close(context);
851 		exit(1);
852 	}
853 
854 	if (ret = kadm5_get_cpw_host_srv_name(context, params.realm,
855 	    &names[1].name)) {
856 		krb5_klog_syslog(LOG_ERR,
857 		    gettext("Cannot get host based service name for changepw "
858 		    "principal in realm %s: %s"), params.realm,
859 		    error_message(ret));
860 		fprintf(stderr,
861 		    gettext("%s: Cannot get host based service name for "
862 		    "changepw principal in realm %s: %s\n"), whoami, params.realm,
863 		    error_message(ret));
864 		krb5_klog_close(context);
865 		exit(1);
866 	}
867 
868 	names[2].name = KADM5_ADMIN_SERVICE_P;
869 	names[3].name = KADM5_CHANGEPW_SERVICE_P;
870 	names[4].name = OVSEC_KADM_ADMIN_SERVICE_P;
871 	names[5].name = OVSEC_KADM_CHANGEPW_SERVICE_P;
872 
873 	/*
874 	 * Try to acquire creds for the old OV services as well as the new
875 	 * names, but if that fails just fall back on the new names.
876 	 */
877 
878 	if (rpc_gss_set_svc_name(names[5].name,
879 				"kerberos_v5", 0, KADM, KADMVERS) &&
880 	    rpc_gss_set_svc_name(names[4].name,
881 				"kerberos_v5", 0, KADM, KADMVERS))
882 		oldnames++;
883 	if (rpc_gss_set_svc_name(names[3].name,
884 				"kerberos_v5", 0, KADM, KADMVERS))
885 		oldnames++;
886 	if (rpc_gss_set_svc_name(names[2].name,
887 				"kerberos_v5", 0, KADM, KADMVERS))
888 		oldnames++;
889 
890     /* If rpc_gss_set_svc_name() fails for either kadmin/<fqdn> or
891      * for changepw/<fqdn> then try to determine if this is caused
892      * by a missing keytab file or entry. If so, log it and continue.
893      */
894 	if (rpc_gss_set_svc_name(names[0].name,
895 				"kerberos_v5", 0, KADM, KADMVERS))
896 		oldnames++;
897 	else
898 		log_kt_error(names[0].name, whoami);
899 	if (rpc_gss_set_svc_name(names[1].name,
900 				"kerberos_v5", 0, KADM, KADMVERS))
901 		oldnames++;
902 	else
903 		log_kt_error(names[1].name, whoami);
904 
905 	retdn = getdomnames(context, params.realm, &dnames);
906 	if (retdn == 0 && dnames) {
907 		/*
908 		 * Multi-homed KDCs sometimes may need to set svc names
909 		 * for multiple net interfaces so we set them for
910 		 * all interfaces just in case.
911 		 */
912 		set_svc_domnames(KADM5_ADMIN_HOST_SERVICE,
913 				dnames, KADM, KADMVERS);
914 		set_svc_domnames(KADM5_CHANGEPW_HOST_SERVICE,
915 				dnames, KADM, KADMVERS);
916 	}
917 
918 	/* if set_names succeeded, this will too */
919 	in_buf.value = names[1].name;
920 	in_buf.length = strlen(names[1].name) + 1;
921 	(void) gss_import_name(&OMret, &in_buf, (gss_OID) nt_krb5_name_oid,
922 	    &gss_changepw_name);
923 	if (oldnames) {
924 		in_buf.value = names[3].name;
925 		in_buf.length = strlen(names[3].name) + 1;
926 		(void) gss_import_name(&OMret, &in_buf,
927 					(gss_OID) nt_krb5_name_oid,
928 					&gss_oldchangepw_name);
929 	}
930 	if (ret = kadm5int_acl_init(context, 0, params.acl_file)) {
931 		krb5_klog_syslog(LOG_ERR, gettext("Cannot initialize acl file: %s"),
932 		    error_message(ret));
933 		fprintf(stderr, gettext("%s: Cannot initialize acl file: %s\n"),
934 		    whoami, error_message(ret));
935 		krb5_klog_close(context);
936 		exit(1);
937 	}
938 
939 	/*
940 	 * Solaris Kerberos:
941 	 * Warn if the acl file doesn't contain an entry for a principal in the
942 	 * default realm.
943 	 */
944 	gssbuf.length = strlen("joe/admin@") + strlen(params.realm) + 1;
945 	gssbuf.value = malloc(gssbuf.length);
946 	if (gssbuf.value != NULL) {
947 	/* Use any value as the first component - joe in this case */
948 		(void) snprintf(gssbuf.value, gssbuf.length, "joe/admin@%s",
949 		    params.realm);
950 
951 		if (gss_import_name(&minor_status, &gssbuf, GSS_C_NT_USER_NAME,
952 		    &name) == GSS_S_COMPLETE) {
953 
954 			if (kadm5int_acl_check(context, name, ACL_MODIFY, NULL,
955 			    NULL) == 0) {
956 				krb5_klog_syslog(LOG_WARNING,
957 				    gettext("acls may not be properly "
958 				    "configured: failed to find an acl "
959 				    "matching the default realm \"%s\" in %s"),
960 				    params.realm, params.acl_file);
961 				(void) fprintf(stderr, gettext("%s: Warning: "
962 				    "acls may not be properly configured: "
963 				    "failed to find an acl matching the "
964 				    "default realm \"%s\" in %s\n"),
965 				    whoami, params.realm,
966 				    params.acl_file);
967 			}
968 			(void) gss_release_name(&minor_status, &name);
969 		}
970 		free(gssbuf.value);
971 		gssbuf.value = NULL;
972 	}
973 	gssbuf.length = 0;
974 
975 	/*
976 	 * Solaris Kerberos:
977 	 * Call a private version of kadm5_init which potentially returns an
978 	 * additional error string in case of failure.
979 	 */
980 	if ((ret = kadm5_init2("kadmind", NULL,
981 		    NULL, &params,
982 		    KADM5_STRUCT_VERSION,
983 		    KADM5_API_VERSION_2,
984 		    db_args,
985 		    &global_server_handle,
986 		    &emsg)) != KADM5_OK) {
987 
988 		krb5_klog_syslog(LOG_ERR,
989 		    gettext("%s while initializing, aborting"),
990 		    (emsg ? emsg : error_message(ret)));
991 		fprintf(stderr,
992 		    gettext("%s: %s while initializing, aborting\n"),
993 		    whoami, (emsg ? emsg : error_message(ret)));
994 		if (emsg)
995 		    free(emsg);
996 
997 		krb5_klog_close(context);
998 		kadm5_destroy(context);
999 		exit(1);
1000 	}
1001 
1002 	/*
1003 	 * Solaris Kerberos:
1004 	 * List the logs (FILE, STDERR, etc) which are currently being
1005 	 * logged to and print to stderr. Useful when trying to
1006 	 * track down a failure via SMF.
1007 	 */
1008 	if (ret = krb5_klog_list_logs(whoami)) {
1009 		fprintf(stderr, gettext("%s: %s while listing logs\n"),
1010 		    whoami, error_message(ret));
1011 		krb5_klog_syslog(LOG_ERR, gettext("%s while listing logs"),
1012 		    error_message(ret));
1013 	}
1014 
1015 	if (!nofork && (ret = daemon(0, 0))) {
1016 		ret = errno;
1017 		krb5_klog_syslog(LOG_ERR,
1018 		    gettext("Cannot detach from tty: %s"),
1019 		    error_message(ret));
1020 		fprintf(stderr, gettext("%s: Cannot detach from tty: %s\n"),
1021 		    whoami, error_message(ret));
1022 		krb5_klog_close(context);
1023 		exit(1);
1024 	}
1025 
1026 	if( db_args )
1027 	{
1028 	    free(db_args), db_args=NULL;
1029 	}
1030 
1031 	handle = global_server_handle;
1032 	ctx = handle->context;
1033 	if (params.iprop_enabled == TRUE) {
1034 		if (ret = krb5_db_supports_iprop(ctx, &iprop_supported)) {
1035 			fprintf(stderr,
1036 				gettext("%s: %s while trying to determine if KDB "
1037 				"plugin supports iprop\n"), whoami,
1038 				error_message(ret));
1039 			krb5_klog_syslog(LOG_ERR,
1040 				gettext("%s while trying to determine if KDB "
1041 				"plugin supports iprop"), error_message(ret));
1042 			krb5_klog_close(ctx);
1043 			exit(1);
1044 		}
1045 
1046 		if (!iprop_supported) {
1047 			fprintf(stderr,
1048 				gettext("%s: Warning, current KDB "
1049 				"plugin does not support iprop, continuing "
1050 				"with iprop disabled\n"), whoami);
1051 			krb5_klog_syslog(LOG_WARNING,
1052 				gettext("Warning, current KDB "
1053 				"plugin does not support iprop, continuing "
1054 				"with iprop disabled"));
1055 
1056 			ulog_set_role(ctx, IPROP_NULL);
1057 		} else
1058 			ulog_set_role(ctx, IPROP_MASTER);
1059 	} else
1060 		ulog_set_role(ctx, IPROP_NULL);
1061 
1062 	log_ctx = ctx->kdblog_context;
1063 
1064 	if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1065 		/*
1066 		 * IProp is enabled, so let's map in the update log
1067 		 * and setup the service.
1068 		 */
1069 		if (ret = ulog_map(ctx, &params, FKADMIND)) {
1070 			fprintf(stderr,
1071 				gettext("%s: %s while mapping update log "
1072 				"(`%s.ulog')\n"), whoami, error_message(ret),
1073 				params.dbname);
1074 			krb5_klog_syslog(LOG_ERR,
1075 				gettext("%s while mapping update log "
1076 				"(`%s.ulog')"), error_message(ret),
1077 				params.dbname);
1078 			krb5_klog_close(ctx);
1079 			exit(1);
1080 		}
1081 
1082 
1083 		if (nofork)
1084 			fprintf(stderr,
1085 				"%s: create IPROP svc (PROG=%d, VERS=%d)\n",
1086 				whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1087 
1088 		if (!svc_create(krb5_iprop_prog_1,
1089 				KRB5_IPROP_PROG, KRB5_IPROP_VERS,
1090 				"circuit_v")) {
1091 			fprintf(stderr,
1092     gettext("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"),
1093 				whoami,
1094 				KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1095 			krb5_klog_syslog(LOG_ERR,
1096     gettext("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."),
1097 					KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1098 			krb5_klog_close(ctx);
1099 			exit(1);
1100 		}
1101 
1102 		if (ret = kiprop_get_adm_host_srv_name(ctx,
1103 							params.realm,
1104 							&kiprop_name)) {
1105 			krb5_klog_syslog(LOG_ERR,
1106 			gettext("%s while getting IProp svc name, failing"),
1107 					error_message(ret));
1108 			fprintf(stderr,
1109 		gettext("%s: %s while getting IProp svc name, failing\n"),
1110 				whoami, error_message(ret));
1111 			krb5_klog_close(ctx);
1112 			exit(1);
1113 		}
1114 
1115 		if (!rpc_gss_set_svc_name(kiprop_name, "kerberos_v5", 0,
1116 					KRB5_IPROP_PROG, KRB5_IPROP_VERS)) {
1117 			rpc_gss_error_t err;
1118 			(void) rpc_gss_get_error(&err);
1119 
1120 		/* Try to determine if the error was caused by a missing keytab or
1121 		 * missing keytab entries (and log it).
1122 		 */
1123 			log_kt_error(kiprop_name, whoami);
1124 			krb5_klog_syslog(LOG_ERR,
1125     gettext("Unable to set RPCSEC_GSS service name (`%s'), failing."),
1126 					kiprop_name ? kiprop_name : "<null>");
1127 			fprintf(stderr,
1128     gettext("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"),
1129 				whoami,
1130 				kiprop_name ? kiprop_name : "<null>");
1131 
1132 			if (nofork) {
1133 				fprintf(stderr,
1134 			"%s: set svc name (rpcsec err=%d, sys err=%d)\n",
1135 					whoami,
1136 					err.rpc_gss_error,
1137 					err.system_error);
1138 			}
1139 
1140 			exit(1);
1141 		}
1142 		free(kiprop_name);
1143 
1144 		if (retdn == 0 && dnames) {
1145 			set_svc_domnames(KADM5_KIPROP_HOST_SERVICE,
1146 					dnames,
1147 					KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1148 		}
1149 
1150 	} else {
1151 		if (!oldnames) {
1152 		/* rpc_gss_set_svc_name failed for both kadmin/<fqdn> and
1153 		 * changepw/<fqdn>.
1154 		 */
1155 			krb5_klog_syslog(LOG_ERR,
1156 					gettext("Unable to set RPCSEC_GSS service names "
1157 						"('%s, %s')"),
1158 					names[0].name, names[1].name);
1159 			fprintf(stderr,
1160 					gettext("%s: Unable to set RPCSEC_GSS service names "
1161 						"('%s, %s')\n"),
1162 					whoami,
1163 					names[0].name, names[1].name);
1164 			krb5_klog_close(context);
1165 			exit(1);
1166 		}
1167 	}
1168 
1169 	if (dnames)
1170 		freedomnames(dnames);
1171 
1172 	setup_signal_handlers(log_ctx->iproprole);
1173 	krb5_klog_syslog(LOG_INFO, gettext("starting"));
1174 	if (nofork)
1175 		fprintf(stderr, "%s: starting...\n", whoami);
1176 
1177 
1178 	/*
1179 	 * We now call our own customized async event processing
1180 	 * function kadm_svc_run(), as opposed to svc_run() earlier,
1181 	 * since this enables kadmind to also listen-to/process
1182 	 * non-RPCSEC_GSS based change-pwd requests apart from the
1183 	 * regular, RPCSEC_GSS kpasswd requests from Solaris Krb5 clients.
1184 	 */
1185 	kadm_svc_run();
1186 
1187 	krb5_klog_syslog(LOG_INFO, gettext("finished, exiting"));
1188 	kadm5_destroy(global_server_handle);
1189 	t_close(fd);
1190 	krb5_klog_close(context);
1191 	exit(0);
1192 }
1193 
1194 
1195 /*
1196  * Function: kadm_svc_run
1197  *
1198  * Purpose: modified version of sunrpc svc_run.
1199  *	    which closes the database every TIMEOUT seconds.
1200  *
1201  * Arguments:
1202  * Requires:
1203  * Effects:
1204  * Modifies:
1205  */
1206 void
1207 kadm_svc_run(void)
1208 {
1209 	struct pollfd	*rfd = 0;
1210 	struct	timeval	    timeout;
1211 	int pollret;
1212 	int nfds = 0;
1213 	int i;
1214 
1215 	while(signal_request_exit == 0) {
1216 		timeout.tv_sec = TIMEOUT;
1217 		timeout.tv_usec = 0;
1218 
1219 		if (nfds != svc_max_pollfd) {
1220 			rfd = realloc(rfd, sizeof (pollfd_t) * svc_max_pollfd);
1221 			nfds = svc_max_pollfd;
1222 		}
1223 
1224 		(void) memcpy(rfd, svc_pollfd,
1225 			sizeof (pollfd_t) * svc_max_pollfd);
1226 
1227 		for (i = 0; i < nfds; i++) {
1228 			if (rfd[i].fd == -1) {
1229 				rfd[i].fd = schpw;
1230 				rfd[i].events = POLLIN;
1231 				break;
1232 			}
1233 		}
1234 
1235 		switch(pollret = poll(rfd, nfds,
1236 				__rpc_timeval_to_msec(&timeout))) {
1237 		case -1:
1238 			if(errno == EINTR)
1239 				continue;
1240 			perror("poll");
1241 			return;
1242 		case 0:
1243 			continue;
1244 		default:
1245 			for (i = 0; i < nfds; i++) {
1246 				if (rfd[i].revents & POLLIN) {
1247 					if (rfd[i].fd == schpw)
1248 						handle_chpw(context, schpw,
1249 							global_server_handle,
1250 							&chgpw_params);
1251 					else
1252 						svc_getreq_poll(rfd, pollret);
1253 					break;
1254 				} else {
1255 					if (i == (nfds - 1))
1256 						perror("poll");
1257 				}
1258 			}
1259 			break;
1260 		}
1261 	}
1262 }
1263 
1264 
1265 /*
1266  * Function: setup_signal_handlers
1267  *
1268  * Purpose: Setup signal handling functions with System V's signal().
1269  */
1270 void setup_signal_handlers(iprop_role iproprole) {
1271 	signal(SIGINT, sig_exit);
1272 	signal(SIGTERM, sig_exit);
1273 	signal(SIGQUIT, sig_exit);
1274 	signal(SIGPIPE, sig_pipe);
1275 
1276 	/*
1277 	 * IProp will fork for a full-resync, we don't want to
1278 	 * wait on it and we don't want the living dead procs either.
1279 	 */
1280 	if (iproprole == IPROP_MASTER)
1281 		(void) signal(SIGCHLD, SIG_IGN);
1282 
1283 	return;
1284 }
1285 
1286 
1287 /*
1288  * Function: sig_exit
1289  *
1290  * Purpose: sets flags saying the server got a signal and that it
1291  *          should exit when convenient.
1292  *
1293  * Effects:
1294  *      Modifies signal_request_exit which ideally makes the server exit
1295  *      at some point.
1296  *
1297  * Modifies:
1298  *      Signal_request_exit
1299  */
1300 void sig_exit(int signum)
1301 {
1302 	krb5_klog_syslog(LOG_NOTICE, gettext("Got signal to request exit"));
1303 	signal_request_exit = 1;
1304 	return;
1305 }
1306 
1307 
1308 /*
1309  * Function: sig_pipe
1310  *
1311  * Purpose: SIGPIPE handler
1312  *
1313  * Effects: krb5_klog_syslog a message that a SIGPIPE occurred and returns,
1314  * thus causing the read() or write() to fail and, presumable, the RPC
1315  * to recover.	Otherwise, the process aborts.
1316  */
1317 void
1318 sig_pipe(int unused)
1319 {
1320 	krb5_klog_syslog(LOG_NOTICE, gettext("Warning: Received a SIGPIPE; "
1321 		"probably a client aborted.  Continuing."));
1322 }
1323 
1324 
1325 /*
1326  * Given a service name (s_name) determine if the keytab file exists
1327  * and if the keytab entry is present. Log missing keytab
1328  * at LOG_ERR and log missing keytab entries at LOG_WARNING.
1329  * If any of krb5_* (or strdup) fail it will return the failure.
1330  */
1331 krb5_error_code log_kt_error(char *s_name, char *whoami) {
1332 	krb5_keytab kt;
1333 	krb5_principal princ;
1334 	krb5_keytab_entry entry;
1335 	krb5_error_code	code = 0;
1336 	char kt_name[MAX_KEYTAB_NAME_LEN];
1337 	char *service;
1338 	char *host;
1339 
1340 	service = strdup(s_name);
1341 	if(!service)
1342 		return ENOMEM;
1343 
1344 	host = strchr(service, '@');
1345 	*host++ = '\0';
1346 	if (code = krb5_sname_to_principal(context, host,
1347 				service, KRB5_NT_SRV_HST, &princ)) {
1348 		krb5_klog_syslog(LOG_ERR,
1349 				gettext("krb5_sname_to_principal failed: %s"),
1350 				error_message(code));
1351 		fprintf(stderr, gettext("%s: krb5_sname_to_principal failed: %s\n"),
1352 				whoami, error_message(code));
1353 		free(service);
1354 		return code;
1355 	}
1356 
1357 	if (code = krb5_kt_default_name(context, kt_name, sizeof (kt_name))) {
1358 		krb5_klog_syslog(LOG_ERR,
1359 				gettext("krb5_kt_default_name failed: %s"),
1360 				error_message(code));
1361 		fprintf(stderr, gettext("%s: krb5_kt_default_name failed: %s\n"),
1362 				whoami, error_message(code));
1363 		krb5_free_principal(context, princ);
1364 		free(service);
1365 		return code;
1366 	}
1367 
1368 	if (code = krb5_kt_default(context, &kt)) {
1369 		krb5_klog_syslog(LOG_ERR,
1370 				gettext("krb5_kt_default failed: %s"),
1371 				error_message(code));
1372 		fprintf(stderr, gettext("%s: krb5_kt_default failed: %s\n"),
1373 				whoami, error_message(code));
1374 		krb5_free_principal(context, princ);
1375 		free(service);
1376 		return code;
1377 	}
1378 
1379 	code = krb5_kt_get_entry(context, kt, princ, 0, 0, &entry);
1380 
1381 	switch (code) {
1382 		case 0:
1383 			krb5_kt_free_entry(context, &entry);
1384 			break;
1385 		case KRB5_KT_NOTFOUND:
1386 			krb5_klog_syslog(LOG_WARNING,
1387 					gettext("Keytab entry \"%s/%s\" is missing from \"%s\""),
1388 					service, host,
1389 					kt_name);
1390 			fprintf(stderr, gettext("%s: Keytab entry \"%s/%s\" is missing from \"%s\".\n"),
1391 					whoami,
1392 					service, host,
1393 					kt_name);
1394 			break;
1395 		case ENOENT:
1396 			krb5_klog_syslog(LOG_ERR,
1397 					gettext("Keytab file \"%s\" does not exist"),
1398 					kt_name);
1399 			fprintf(stderr, gettext("%s: Keytab file \"%s\" does not exist.\n"),
1400 					whoami,
1401 					kt_name);
1402 			break;
1403 	}
1404 	krb5_kt_close(context,kt);
1405 	krb5_free_principal(context, princ);
1406 	free(service);
1407 	return code;
1408 }
1409 
1410