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
97int	signal_pure_report = 0;
98int	signal_pure_clear = 0;
99void	request_pure_report(int);
100void	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)
113extern int daemon(int, int);
114#endif
115
116static int	signal_request_exit = 0;
117kadm5_config_params chgpw_params;
118void    setup_signal_handlers(iprop_role iproprole);
119void	request_exit(int);
120void	sig_pipe(int);
121void	kadm_svc_run(void);
122
123#ifdef POSIX_SIGNALS
124static struct sigaction s_action;
125#endif /* POSIX_SIGNALS */
126
127
128#define	TIMEOUT	15
129
130typedef struct _auth_gssapi_name {
131	char *name;
132	gss_OID type;
133} auth_gssapi_name;
134
135gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL;
136void *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
147extern void krb5_iprop_prog_1();
148extern kadm5_ret_t kiprop_get_adm_host_srv_name(
149	krb5_context,
150	const char *,
151	char **);
152
153static int schpw;
154
155
156in_port_t l_port = 0;	/* global local port num, for BSM audits */
157
158int 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
172static 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 */
196static void display_status_1(char *, OM_uint32, int);
197
198static 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
207static 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
236extern struct hostent   *res_getipnodebyaddr(const void *, size_t, int, int *);
237extern void             res_freehostent(struct hostent *);
238
239static void
240freedomnames(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 */
260static int
261getdomnames(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
347err:
348	if (npp) {
349		freedomnames(npp);
350		npp = NULL;
351	}
352
353	if (hp) {
354		res_freehostent(hp);
355		hp = NULL;
356	}
357
358out:
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 */
377static void
378set_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 */
403static krb5_context context;
404
405static krb5_context hctx;
406
407int 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     }
929kterr:
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
1238void 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 */
1302void 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
1354void 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 */
1370void 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