xref: /titanic_52/usr/src/cmd/krb5/kadmin/server/ovsec_kadmd.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
1 /*
2  * Copyright 2005 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 	if (!oldnames) {
775 		krb5_klog_syslog(LOG_ERR,
776 		    gettext("Cannot initialize GSS-API authentication, "
777 			"failing."));
778 		fprintf(stderr,
779 		    gettext("%s:Cannot initialize GSS-API authentication.\n"),
780 		    whoami);
781 		krb5_klog_close(context);
782 		exit(1);
783 	}
784 
785 	retdn = getdomnames(context, params.realm, &dnames);
786 	if (retdn == 0 && dnames) {
787 		/*
788 		 * Multi-homed KDCs sometimes may need to set svc names
789 		 * for multiple net interfaces so we set them for
790 		 * all interfaces just in case.
791 		 */
792 		set_svc_domnames(KADM5_ADMIN_HOST_SERVICE,
793 				dnames, KADM, KADMVERS);
794 		set_svc_domnames(KADM5_CHANGEPW_HOST_SERVICE,
795 				dnames, KADM, KADMVERS);
796 	}
797 
798 	/* if set_names succeeded, this will too */
799 	in_buf.value = names[1].name;
800 	in_buf.length = strlen(names[1].name) + 1;
801 	(void) gss_import_name(&OMret, &in_buf, (gss_OID) nt_krb5_name_oid,
802 	    &gss_changepw_name);
803 	if (oldnames) {
804 		in_buf.value = names[3].name;
805 		in_buf.length = strlen(names[3].name) + 1;
806 		(void) gss_import_name(&OMret, &in_buf,
807 					(gss_OID) nt_krb5_name_oid,
808 					&gss_oldchangepw_name);
809 	}
810 	if (ret = acl_init(context, 0, params.acl_file)) {
811 		krb5_klog_syslog(LOG_ERR, gettext("Cannot initialize acl file: %s"),
812 		    error_message(ret));
813 		fprintf(stderr, gettext("%s: Cannot initialize acl file: %s\n"),
814 		    whoami, error_message(ret));
815 		krb5_klog_close(context);
816 		exit(1);
817 	}
818 	if ((ret = kadm5_init("kadmind", NULL,
819 		    NULL, &params,
820 		    KADM5_STRUCT_VERSION,
821 		    KADM5_API_VERSION_2,
822 		    &global_server_handle)) != KADM5_OK) {
823 		krb5_klog_syslog(LOG_ERR,
824 		    gettext("%s while initializing, aborting"),
825 		    error_message(ret));
826 		fprintf(stderr,
827 		    gettext("%s: %s while initializing, aborting\n"),
828 		    whoami, error_message(ret));
829 		krb5_klog_close(context);
830 		exit(1);
831 	}
832 
833 	handle = global_server_handle;
834 	ctx = handle->context;
835 	if (params.iprop_enabled == TRUE)
836 		ulog_set_role(ctx, IPROP_MASTER);
837 	else
838 		ulog_set_role(ctx, IPROP_NULL);
839 
840 	log_ctx = ctx->kdblog_context;
841 
842 	if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
843 		/*
844 		 * IProp is enabled, so let's map in the update log
845 		 * and setup the service.
846 		 */
847 		if (ret = ulog_map(ctx, &params, FKADMIND)) {
848 			fprintf(stderr,
849 				gettext("%s: %s while mapping update log "
850 				"(`%s.ulog')\n"), whoami, error_message(ret),
851 				params.dbname);
852 			krb5_klog_syslog(LOG_ERR,
853 				gettext("%s while mapping update log "
854 				"(`%s.ulog')"), error_message(ret),
855 				params.dbname);
856 			krb5_klog_close(ctx);
857 			exit(1);
858 		}
859 
860 
861 		if (nofork)
862 			fprintf(stderr,
863 				"%s: create IPROP svc (PROG=%d, VERS=%d)\n",
864 				whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS);
865 
866 		if (!svc_create(krb5_iprop_prog_1,
867 				KRB5_IPROP_PROG, KRB5_IPROP_VERS,
868 				"circuit_v")) {
869 			fprintf(stderr,
870     gettext("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"),
871 				whoami,
872 				KRB5_IPROP_PROG, KRB5_IPROP_VERS);
873 			krb5_klog_syslog(LOG_ERR,
874     gettext("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."),
875 					KRB5_IPROP_PROG, KRB5_IPROP_VERS);
876 			krb5_klog_close(ctx);
877 			exit(1);
878 		}
879 
880 		if (ret = kiprop_get_adm_host_srv_name(ctx,
881 							params.realm,
882 							&kiprop_name)) {
883 			krb5_klog_syslog(LOG_ERR,
884 			gettext("%s while getting IProp svc name, failing"),
885 					error_message(ret));
886 			fprintf(stderr,
887 		gettext("%s: %s while getting IProp svc name, failing\n"),
888 				whoami, error_message(ret));
889 			krb5_klog_close(ctx);
890 			exit(1);
891 		}
892 
893 		if (!rpc_gss_set_svc_name(kiprop_name, "kerberos_v5", 0,
894 					KRB5_IPROP_PROG, KRB5_IPROP_VERS)) {
895 			rpc_gss_error_t err;
896 			(void) rpc_gss_get_error(&err);
897 
898 			krb5_klog_syslog(LOG_ERR,
899     gettext("Unable to set RPCSEC_GSS service name (`%s'), failing."),
900 					kiprop_name ? kiprop_name : "<null>");
901 
902 			fprintf(stderr,
903     gettext("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"),
904 				whoami,
905 				kiprop_name ? kiprop_name : "<null>");
906 
907 			if (nofork) {
908 				fprintf(stderr,
909 			"%s: set svc name (rpcsec err=%d, sys err=%d)\n",
910 					whoami,
911 					err.rpc_gss_error,
912 					err.system_error);
913 			}
914 
915 			exit(1);
916 		}
917 		free(kiprop_name);
918 
919 		if (retdn == 0 && dnames) {
920 			set_svc_domnames(KADM5_KIPROP_HOST_SERVICE,
921 					dnames,
922 					KRB5_IPROP_PROG, KRB5_IPROP_VERS);
923 		}
924 
925 	}
926 
927 	if (dnames)
928 		freedomnames(dnames);
929 
930 	setup_signal_handlers(log_ctx->iproprole);
931 	krb5_klog_syslog(LOG_INFO, gettext("starting"));
932 	if (nofork)
933 		fprintf(stderr, "%s: starting...\n", whoami);
934 
935 
936 	/*
937 	 * We now call our own customized async event processing
938 	 * function kadm_svc_run(), as opposed to svc_run() earlier,
939 	 * since this enables kadmind to also listen-to/process
940 	 * non-RPCSEC_GSS based change-pwd requests apart from the
941 	 * regular, RPCSEC_GSS kpasswd requests from Solaris Krb5 clients.
942 	 */
943 	kadm_svc_run();
944 
945 	krb5_klog_syslog(LOG_INFO, gettext("finished, exiting"));
946 	kadm5_destroy(global_server_handle);
947 	t_close(fd);
948 	krb5_klog_close(context);
949 	exit(0);
950 }
951 
952 
953 /*
954  * Function: kadm_svc_run
955  *
956  * Purpose: modified version of sunrpc svc_run.
957  *	    which closes the database every TIMEOUT seconds.
958  *
959  * Arguments:
960  * Requires:
961  * Effects:
962  * Modifies:
963  */
964 void
965 kadm_svc_run(void)
966 {
967 	struct pollfd	*rfd = 0;
968 	struct	timeval	    timeout;
969 	int pollret;
970 	int nfds = 0;
971 	int i;
972 
973 	while(signal_request_exit == 0) {
974 		timeout.tv_sec = TIMEOUT;
975 		timeout.tv_usec = 0;
976 
977 		if (nfds != svc_max_pollfd) {
978 			rfd = realloc(rfd, sizeof (pollfd_t) * svc_max_pollfd);
979 			nfds = svc_max_pollfd;
980 		}
981 
982 		(void) memcpy(rfd, svc_pollfd,
983 			sizeof (pollfd_t) * svc_max_pollfd);
984 
985 		for (i = 0; i < nfds; i++) {
986 			if (rfd[i].fd == -1) {
987 				rfd[i].fd = schpw;
988 				rfd[i].events = POLLIN;
989 				break;
990 			}
991 		}
992 
993 		switch(pollret = poll(rfd, nfds,
994 				__rpc_timeval_to_msec(&timeout))) {
995 		case -1:
996 			if(errno == EINTR)
997 				continue;
998 			perror("poll");
999 			return;
1000 		case 0:
1001 			continue;
1002 		default:
1003 			for (i = 0; i < nfds; i++) {
1004 				if (rfd[i].revents & POLLIN) {
1005 					if (rfd[i].fd == schpw)
1006 						handle_chpw(context, schpw,
1007 							global_server_handle,
1008 							&chgpw_params);
1009 					else
1010 						svc_getreq_poll(rfd, pollret);
1011 					break;
1012 				} else {
1013 					if (i == (nfds - 1))
1014 						perror("poll");
1015 				}
1016 			}
1017 			break;
1018 		}
1019 	}
1020 }
1021 
1022 
1023 /*
1024  * Function: setup_signal_handlers
1025  *
1026  * Purpose: Setup signal handling functions with System V's signal().
1027  */
1028 void setup_signal_handlers(iprop_role iproprole) {
1029 	signal(SIGINT, sig_exit);
1030 	signal(SIGTERM, sig_exit);
1031 	signal(SIGQUIT, sig_exit);
1032 	signal(SIGPIPE, sig_pipe);
1033 
1034 	/*
1035 	 * IProp will fork for a full-resync, we don't want to
1036 	 * wait on it and we don't want the living dead procs either.
1037 	 */
1038 	if (iproprole == IPROP_MASTER)
1039 		(void) signal(SIGCHLD, SIG_IGN);
1040 
1041 	return;
1042 }
1043 
1044 
1045 /*
1046  * Function: sig_exit
1047  *
1048  * Purpose: sets flags saying the server got a signal and that it
1049  *          should exit when convenient.
1050  *
1051  * Effects:
1052  *      Modifies signal_request_exit which ideally makes the server exit
1053  *      at some point.
1054  *
1055  * Modifies:
1056  *      Signal_request_exit
1057  */
1058 void sig_exit(int signum)
1059 {
1060 	krb5_klog_syslog(LOG_NOTICE, gettext("Got signal to request exit"));
1061 	signal_request_exit = 1;
1062 	return;
1063 }
1064 
1065 
1066 /*
1067  * Function: sig_pipe
1068  *
1069  * Purpose: SIGPIPE handler
1070  *
1071  * Effects: krb5_klog_syslog a message that a SIGPIPE occurred and returns,
1072  * thus causing the read() or write() to fail and, presumable, the RPC
1073  * to recover.	Otherwise, the process aborts.
1074  */
1075 void
1076 sig_pipe(int unused)
1077 {
1078 	krb5_klog_syslog(LOG_NOTICE, gettext("Warning: Received a SIGPIPE; "
1079 		"probably a client aborted.  Continuing."));
1080 }
1081