xref: /illumos-gate/usr/src/cmd/krb5/kadmin/server/ovsec_kadmd.c (revision 2aeafac3612e19716bf8164f89c3c9196342979c)
1 /*
2  * Copyright 2009 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 min_stat;
213 	gss_buffer_desc msg;
214 	OM_uint32 msg_ctx;
215 
216 	msg_ctx = 0;
217 	while (1) {
218 		(void) 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 contains an entry for a principal matching the
1006 	 * default (unconfigured) acl rule.
1007 	 */
1008 	gssbuf.length = strlen("x/admin@___default_realm___");
1009 	gssbuf.value = "x/admin@___default_realm___";
1010 	/* Use any value as the first component - 'x' in this case */
1011 	if (gss_import_name(&minor_status, &gssbuf, GSS_C_NT_USER_NAME, &name)
1012 	    == GSS_S_COMPLETE) {
1013 		if (kadm5int_acl_check(context, name, ACL_MODIFY, NULL, NULL)) {
1014 			krb5_klog_syslog(LOG_WARNING,
1015 			    gettext("acls may not be properly configured: "
1016 			    "found an acl matching \"___default_realm___\" in "
1017 			    " %s"), params.acl_file);
1018 			(void) fprintf(stderr, gettext("%s: Warning: "
1019 			    "acls may not be properly configured: found an acl "
1020 			    "matching \"___default_realm___\" in %s\n"),
1021 			    whoami, params.acl_file);
1022 		}
1023 		(void) gss_release_name(&minor_status, &name);
1024 	}
1025 	gssbuf.value = NULL;
1026 	gssbuf.length = 0;
1027 
1028 	/*
1029 	 * Solaris Kerberos:
1030 	 * List the logs (FILE, STDERR, etc) which are currently being
1031 	 * logged to and print to stderr. Useful when trying to
1032 	 * track down a failure via SMF.
1033 	 */
1034 	if (ret = krb5_klog_list_logs(whoami)) {
1035 		fprintf(stderr, gettext("%s: %s while listing logs\n"),
1036 		    whoami, error_message(ret));
1037 		krb5_klog_syslog(LOG_ERR, gettext("%s while listing logs"),
1038 		    error_message(ret));
1039 	}
1040 
1041      if (!nofork && (ret = daemon(0, 0))) {
1042 	  ret = errno;
1043 	  errmsg = krb5_get_error_message (context, ret);
1044 	  krb5_klog_syslog(LOG_ERR,
1045 		    gettext("Cannot detach from tty: %s"), errmsg);
1046 	  fprintf(stderr, gettext("%s: Cannot detach from tty: %s\n"),
1047 		  whoami, errmsg);
1048 	  kadm5_destroy(global_server_handle);
1049 	  krb5_klog_close(context);
1050 	  exit(1);
1051      }
1052 
1053     /* SUNW14resync */
1054 #if 0
1055      krb5_klog_syslog(LOG_INFO, "Seeding random number generator");
1056      ret = krb5_c_random_os_entropy(context, 1, NULL);
1057      if (ret) {
1058 	  krb5_klog_syslog(LOG_ERR, "Error getting random seed: %s, aborting",
1059 			   krb5_get_error_message(context, ret));
1060 	  kadm5_destroy(global_server_handle);
1061 	  krb5_klog_close(context);
1062 	  exit(1);
1063      }
1064 #endif
1065 
1066 
1067 	handle = global_server_handle;
1068 	ctx = handle->context;
1069 	if (params.iprop_enabled == TRUE) {
1070 		if (ret = krb5_db_supports_iprop(ctx, &iprop_supported)) {
1071 			fprintf(stderr,
1072 				gettext("%s: %s while trying to determine if KDB "
1073 				"plugin supports iprop\n"), whoami,
1074 				error_message(ret));
1075 			krb5_klog_syslog(LOG_ERR,
1076 				gettext("%s while trying to determine if KDB "
1077 				"plugin supports iprop"), error_message(ret));
1078 			krb5_klog_close(ctx);
1079 			exit(1);
1080 		}
1081 
1082 		if (!iprop_supported) {
1083 			fprintf(stderr,
1084 				gettext("%s: Warning, current KDB "
1085 				"plugin does not support iprop, continuing "
1086 				"with iprop disabled\n"), whoami);
1087 			krb5_klog_syslog(LOG_WARNING,
1088 				gettext("Warning, current KDB "
1089 				"plugin does not support iprop, continuing "
1090 				"with iprop disabled"));
1091 
1092 			ulog_set_role(ctx, IPROP_NULL);
1093 		} else
1094 			ulog_set_role(ctx, IPROP_MASTER);
1095 	} else
1096 		ulog_set_role(ctx, IPROP_NULL);
1097 
1098 	log_ctx = ctx->kdblog_context;
1099 
1100 	if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1101 		/*
1102 		 * IProp is enabled, so let's map in the update log
1103 		 * and setup the service.
1104 		 */
1105 		if (ret = ulog_map(ctx, &params, FKADMIND)) {
1106 			fprintf(stderr,
1107 				gettext("%s: %s while mapping update log "
1108 				"(`%s.ulog')\n"), whoami, error_message(ret),
1109 				params.dbname);
1110 			krb5_klog_syslog(LOG_ERR,
1111 				gettext("%s while mapping update log "
1112 				"(`%s.ulog')"), error_message(ret),
1113 				params.dbname);
1114 			krb5_klog_close(ctx);
1115 			exit(1);
1116 		}
1117 
1118 
1119 		if (nofork)
1120 			fprintf(stderr,
1121 				"%s: create IPROP svc (PROG=%d, VERS=%d)\n",
1122 				whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1123 
1124 		if (!svc_create(krb5_iprop_prog_1,
1125 				KRB5_IPROP_PROG, KRB5_IPROP_VERS,
1126 				"circuit_v")) {
1127 			fprintf(stderr,
1128     gettext("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"),
1129 				whoami,
1130 				KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1131 			krb5_klog_syslog(LOG_ERR,
1132     gettext("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."),
1133 					KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1134 			krb5_klog_close(ctx);
1135 			exit(1);
1136 		}
1137 
1138 		if (ret = kiprop_get_adm_host_srv_name(ctx,
1139 							params.realm,
1140 							&kiprop_name)) {
1141 			krb5_klog_syslog(LOG_ERR,
1142 			gettext("%s while getting IProp svc name, failing"),
1143 					error_message(ret));
1144 			fprintf(stderr,
1145 		gettext("%s: %s while getting IProp svc name, failing\n"),
1146 				whoami, error_message(ret));
1147 			krb5_klog_close(ctx);
1148 			exit(1);
1149 		}
1150 
1151 		if (!rpc_gss_set_svc_name(kiprop_name, "kerberos_v5", 0,
1152 					KRB5_IPROP_PROG, KRB5_IPROP_VERS)) {
1153 			rpc_gss_error_t err;
1154 			(void) rpc_gss_get_error(&err);
1155 
1156 			krb5_klog_syslog(LOG_ERR,
1157     gettext("Unable to set RPCSEC_GSS service name (`%s'), failing."),
1158 					kiprop_name ? kiprop_name : "<null>");
1159 			fprintf(stderr,
1160     gettext("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"),
1161 				whoami,
1162 				kiprop_name ? kiprop_name : "<null>");
1163 
1164 			if (nofork) {
1165 				fprintf(stderr,
1166 			"%s: set svc name (rpcsec err=%d, sys err=%d)\n",
1167 					whoami,
1168 					err.rpc_gss_error,
1169 					err.system_error);
1170 			}
1171 
1172 			exit(1);
1173 		}
1174 		free(kiprop_name);
1175 
1176 		if (retdn == 0 && dnames) {
1177 			set_svc_domnames(KADM5_KIPROP_HOST_SERVICE,
1178 					dnames,
1179 					KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1180 		}
1181 
1182 	} else {
1183 		if (!oldnames) {
1184 		/* rpc_gss_set_svc_name failed for both kadmin/<fqdn> and
1185 		 * changepw/<fqdn>.
1186 		 */
1187 			krb5_klog_syslog(LOG_ERR,
1188 					gettext("Unable to set RPCSEC_GSS service names "
1189 						"('%s, %s')"),
1190 					names[0].name, names[1].name);
1191 			fprintf(stderr,
1192 					gettext("%s: Unable to set RPCSEC_GSS service names "
1193 						"('%s, %s')\n"),
1194 					whoami,
1195 					names[0].name, names[1].name);
1196 			krb5_klog_close(context);
1197 			exit(1);
1198 		}
1199 	}
1200 
1201 	if (dnames)
1202 		freedomnames(dnames);
1203 
1204 	setup_signal_handlers(log_ctx->iproprole);
1205 	krb5_klog_syslog(LOG_INFO, gettext("starting"));
1206 	if (nofork)
1207 		fprintf(stderr, "%s: starting...\n", whoami);
1208 
1209 
1210 	/*
1211 	 * We now call our own customized async event processing
1212 	 * function kadm_svc_run(), as opposed to svc_run() earlier,
1213 	 * since this enables kadmind to also listen-to/process
1214 	 * non-RPCSEC_GSS based change-pwd requests apart from the
1215 	 * regular, RPCSEC_GSS kpasswd requests from Solaris Krb5 clients.
1216 	 */
1217 	kadm_svc_run();
1218 
1219 	krb5_klog_syslog(LOG_INFO, gettext("finished, exiting"));
1220 	kadm5_destroy(global_server_handle);
1221 	t_close(fd);
1222 	krb5_klog_close(context);
1223 	exit(0);
1224 }
1225 
1226 /*
1227  * Function: kadm_svc_run
1228  *
1229  * Purpose: modified version of sunrpc svc_run.
1230  *	    which closes the database every TIMEOUT seconds.
1231  *
1232  * Arguments:
1233  * Requires:
1234  * Effects:
1235  * Modifies:
1236  */
1237 
1238 void kadm_svc_run(void)
1239 {
1240      struct pollfd	*rfd = 0;
1241      struct	timeval	    timeout;
1242      int pollret;
1243      int nfds = 0;
1244      int i;
1245 
1246      while(signal_request_exit == 0) {
1247 		timeout.tv_sec = TIMEOUT;
1248 		timeout.tv_usec = 0;
1249 
1250 		if (nfds != svc_max_pollfd) {
1251 			rfd = realloc(rfd, sizeof (pollfd_t) * svc_max_pollfd);
1252 			nfds = svc_max_pollfd;
1253 		}
1254 
1255 		(void) memcpy(rfd, svc_pollfd,
1256 			sizeof (pollfd_t) * svc_max_pollfd);
1257 
1258 		for (i = 0; i < nfds; i++) {
1259 			if (rfd[i].fd == -1) {
1260 				rfd[i].fd = schpw;
1261 				rfd[i].events = POLLIN;
1262 				break;
1263 			}
1264 		}
1265 
1266 		switch(pollret = poll(rfd, nfds,
1267 				__rpc_timeval_to_msec(&timeout))) {
1268 		case -1:
1269 			if(errno == EINTR)
1270 				continue;
1271 			perror("poll");
1272 			return;
1273 		case 0:
1274 			continue;
1275 		default:
1276 			for (i = 0; i < nfds; i++) {
1277 				if (rfd[i].revents & POLLIN) {
1278 					if (rfd[i].fd == schpw)
1279 						handle_chpw(context, schpw,
1280 							global_server_handle,
1281 							&chgpw_params);
1282 					else
1283 						svc_getreq_poll(rfd, pollret);
1284 					break;
1285 				} else {
1286 					if (i == (nfds - 1))
1287 						perror("poll");
1288 				}
1289 			}
1290 			break;
1291 		}
1292 	}
1293 }
1294 
1295 
1296 /*
1297  * Function: setup_signal_handlers
1298  *
1299  * Purpose: Setup signal handling functions with either
1300  * System V's signal() or POSIX_SIGNALS.
1301  */
1302 void setup_signal_handlers(iprop_role iproprole) {
1303 #ifdef POSIX_SIGNALS
1304 	(void) sigemptyset(&s_action.sa_mask);
1305 	s_action.sa_handler = request_exit;
1306 	(void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL);
1307 	(void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
1308 	(void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL);
1309 	s_action.sa_handler = sig_pipe;
1310 	(void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL);
1311 
1312 	/*
1313 	 * IProp will fork for a full-resync, we don't want to
1314 	 * wait on it and we don't want the living dead procs either.
1315 	 */
1316 	if (iproprole == IPROP_MASTER) {
1317 		s_action.sa_handler = SIG_IGN;
1318 		(void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL);
1319 	}
1320 #else
1321      signal(SIGINT, request_exit);
1322      signal(SIGTERM, request_exit);
1323      signal(SIGQUIT, request_exit);
1324      signal(SIGPIPE, sig_pipe);
1325 
1326 	/*
1327 	 * IProp will fork for a full-resync, we don't want to
1328 	 * wait on it and we don't want the living dead procs either.
1329 	 */
1330 	if (iproprole == IPROP_MASTER)
1331 		(void) signal(SIGCHLD, SIG_IGN);
1332 
1333 #endif /* POSIX_SIGNALS */
1334 	return;
1335 }
1336 
1337 
1338 /*
1339  * Function: request_exit
1340  *
1341  * Purpose: sets flags saying the server got a signal and that it
1342  *	    should exit when convient.
1343  *
1344  * Arguments:
1345  * Requires:
1346  * Effects:
1347  *	modifies signal_request_exit which ideally makes the server exit
1348  *	at some point.
1349  *
1350  * Modifies:
1351  *	signal_request_exit
1352  */
1353 
1354 void request_exit(int signum)
1355 {
1356      krb5_klog_syslog(LOG_NOTICE, gettext("Got signal to request exit"));
1357      signal_request_exit = 1;
1358      return;
1359 }
1360 
1361 /*
1362  * Function: sig_pipe
1363  *
1364  * Purpose: SIGPIPE handler
1365  *
1366  * Effects: krb5_klog_syslogs a message that a SIGPIPE occurred and returns,
1367  * thus causing the read() or write() to fail and, presumable, the RPC
1368  * to recover.  Otherwise, the process aborts.
1369  */
1370 void sig_pipe(int unused)
1371 {
1372 #ifndef POSIX_SIGNALS
1373      signal(SIGPIPE, sig_pipe);
1374 #endif /* POSIX_SIGNALS */
1375      krb5_klog_syslog(LOG_NOTICE, gettext("Warning: Received a SIGPIPE; "
1376 	    "probably a client aborted.  Continuing."));
1377      return;
1378 }
1379 
1380