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
usage()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
display_status(msg,maj_stat,min_stat)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
display_status_1(m,code,type)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
freedomnames(char ** npp)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
getdomnames(krb5_context ctx,char * realm,char *** dnames)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
set_svc_domnames(char * svcname,char ** dnames,u_int program,u_int version)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
main(int argc,char * argv[])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 *) ¶ms, 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, ¶ms,
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, ¶ms,
579 ¶ms))) {
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(7) 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, ¶ms, 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
kadm_svc_run(void)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 */
setup_signal_handlers(iprop_role iproprole)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
request_exit(int signum)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 */
sig_pipe(int unused)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