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