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 *) ¶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 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