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