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