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