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