1 /* 2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 10 * 11 * Openvision retains the copyright to derivative works of 12 * this source code. Do *NOT* create a derivative of this 13 * source code before consulting with your legal department. 14 * Do *NOT* integrate *ANY* of this source code into another 15 * product before consulting with your legal department. 16 * 17 * For further information, read the top-level Openvision 18 * copyright which is contained in the top-level MIT Kerberos 19 * copyright. 20 * 21 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 22 * 23 */ 24 25 26 /* 27 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved 28 */ 29 30 /* 31 * SUNWresync121 XXX 32 * Beware future resyncers, this file is much diff from MIT (1.0...) 33 */ 34 35 #include <stdio.h> 36 #include <signal.h> 37 #include <syslog.h> 38 #include <sys/types.h> 39 #include <sys/time.h> 40 #include <sys/socket.h> 41 #include <unistd.h> 42 #include <netinet/in.h> 43 #include <arpa/inet.h> /* inet_ntoa */ 44 #include <netdb.h> 45 #include <gssapi/gssapi.h> 46 #include <rpc/rpc.h> 47 #include <kadm5/admin.h> 48 #include <kadm5/kadm_rpc.h> 49 #include <kadm5/server_internal.h> 50 #include <server_acl.h> 51 #include <krb5/adm_proto.h> 52 #include <string.h> 53 #include <gssapi_krb5.h> 54 #include <libintl.h> 55 #include <locale.h> 56 #include <sys/resource.h> 57 #include <kdb/kdb_log.h> 58 59 #include <rpc/rpcsec_gss.h> 60 61 #ifndef FD_SETSIZE 62 #define FD_SETSIZE 256 63 #endif 64 65 #ifndef MAX 66 #define MAX(a, b) (((a) > (b)) ? (a) : (b)) 67 #endif 68 69 static int signal_request_exit = 0; 70 static int schpw; 71 kadm5_config_params chgpw_params; 72 void kadm_svc_run(void); 73 void setup_signal_handlers(iprop_role iproprole); 74 void sig_exit(int); 75 void sig_pipe(int); 76 77 #ifdef POSIX_SIGNALS 78 static struct sigaction s_action; 79 #endif /* POSIX_SIGNALS */ 80 81 #define TIMEOUT 15 82 83 typedef struct _auth_gssapi_name { 84 char *name; 85 gss_OID type; 86 } auth_gssapi_name; 87 88 gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL; 89 void *global_server_handle; 90 91 /* 92 * This is a kludge, but the server needs these constants to be 93 * compatible with old clients. They are defined in <kadm5/admin.h>, 94 * but only if USE_KADM5_API_VERSION == 1. 95 */ 96 #define OVSEC_KADM_ADMIN_SERVICE_P "ovsec_adm@admin" 97 #define OVSEC_KADM_CHANGEPW_SERVICE_P "ovsec_adm@changepw" 98 99 /* 100 * This enables us to set the keytab that gss_acquire_cred uses, but 101 * it also restricts us to linking against the Kv5 GSS-API library. 102 * Since this is *k*admind, that shouldn't be a problem. 103 */ 104 extern char *krb5_overridekeyname; 105 106 extern void krb5_iprop_prog_1(); 107 extern kadm5_ret_t kiprop_get_adm_host_srv_name( 108 krb5_context, 109 const char *, 110 char **); 111 112 static krb5_context context; /* XXX yuck. the signal handlers need this */ 113 114 in_port_t l_port = 0; /* global local port num, for BSM audits */ 115 116 int nofork = 0; /* global; don't fork (debug mode) */ 117 118 119 /* 120 * Function: usage 121 * 122 * Purpose: print out the server usage message 123 * 124 * Arguments: 125 * Requires: 126 * Effects: 127 * Modifies: 128 */ 129 130 void 131 usage() 132 { 133 fprintf(stderr, gettext("Usage: kadmind [-r realm] [-m] [-d] " 134 "[-p port-number]\n")); 135 exit(1); 136 } 137 138 /* 139 * Function: display_status 140 * 141 * Purpose: displays GSS-API messages 142 * 143 * Arguments: 144 * 145 * msg a string to be displayed with the message 146 * maj_stat the GSS-API major status code 147 * min_stat the GSS-API minor status code 148 * 149 * Effects: 150 * 151 * The GSS-API messages associated with maj_stat and min_stat are 152 * displayed on stderr, each preceeded by "GSS-API error <msg>: " and 153 * followed by a newline. 154 */ 155 static void display_status_1(); 156 157 void display_status(msg, maj_stat, min_stat) 158 char *msg; 159 OM_uint32 maj_stat; 160 OM_uint32 min_stat; 161 { 162 display_status_1(msg, maj_stat, GSS_C_GSS_CODE); 163 display_status_1(msg, min_stat, GSS_C_MECH_CODE); 164 } 165 166 static void display_status_1(m, code, type) 167 char *m; 168 OM_uint32 code; 169 int type; 170 { 171 OM_uint32 maj_stat, min_stat; 172 gss_buffer_desc msg; 173 OM_uint32 msg_ctx; 174 175 msg_ctx = 0; 176 while (1) { 177 maj_stat = gss_display_status(&min_stat, code, 178 type, GSS_C_NULL_OID, 179 &msg_ctx, &msg); 180 fprintf(stderr, "GSS-API error %s: %s\n", m, 181 (char *)msg.value); 182 (void) gss_release_buffer(&min_stat, &msg); 183 184 if (!msg_ctx) 185 break; 186 } 187 } 188 189 190 /* 191 * Solaris Kerberos: the following prototypes are needed because these are 192 * private interfaces that do not have prototypes in any .h 193 */ 194 195 extern struct hostent *res_getipnodebyaddr(const void *, size_t, int, int *); 196 extern void res_freehostent(struct hostent *); 197 198 static void 199 freedomnames(char **npp) 200 { 201 char **tpp; 202 203 if (npp) { 204 tpp = npp; 205 while (*tpp++) { 206 free(*(tpp-1)); 207 } 208 free(npp); 209 } 210 } 211 212 /* 213 * Construct a list of uniq FQDNs of all the net interfaces (except 214 * krb5.conf master dups) and return it in arg 'dnames'. 215 * 216 * On successful return (0), caller must call freedomnames() 217 * to free memory. 218 */ 219 static int 220 getdomnames(krb5_context ctx, char *realm, char ***dnames) 221 { 222 krb5_address **addresses = NULL; 223 krb5_address *a = NULL; 224 struct hostent *hp = NULL; 225 int ret, i, result=0, error; 226 char **npp = NULL, **tpp=NULL; 227 int dup=0, n = 0; 228 char *cfhost = NULL; /* krb5 conf file master hostname */ 229 230 if (ret = kadm5_get_master(ctx, realm, &cfhost)) { 231 return (ret); 232 } 233 234 ret = krb5_os_localaddr(ctx, &addresses); 235 if (ret != 0) { 236 if (nofork) 237 (void) fprintf(stderr, 238 "kadmind: get localaddrs failed: %s", 239 error_message(ret)); 240 result = ret; 241 goto err; 242 } 243 244 245 for (i=0; addresses[i]; i++) { 246 a = addresses[i]; 247 hp = res_getipnodebyaddr(a->contents, a->length, 248 a->addrtype == ADDRTYPE_INET 249 ? AF_INET : AF_INET6, 250 &error); 251 if (hp != NULL) { 252 253 /* skip master host in krb5.conf */ 254 if (strcasecmp(cfhost, hp->h_name) == 0) { 255 res_freehostent(hp); 256 hp = NULL; 257 continue; 258 } 259 260 dup = 0; 261 tpp = npp; 262 /* skip if hostname already exists in list */ 263 while (tpp && *tpp++) { 264 if (strcasecmp(*(tpp-1), hp->h_name) == 0) { 265 dup++; 266 break; 267 } 268 } 269 270 if (dup) { 271 res_freehostent(hp); 272 hp = NULL; 273 continue; 274 } 275 276 npp = realloc(npp, sizeof(char *) * (n + 2)); 277 if (!npp) { 278 result = ENOMEM; 279 goto err; 280 } 281 npp[n] = strdup(hp->h_name); 282 if (!npp[n]) { 283 result = ENOMEM; 284 goto err; 285 } 286 npp[n+1] = NULL; 287 n++; 288 289 res_freehostent(hp); 290 hp = NULL; 291 result = 0; 292 } 293 294 } 295 296 #ifdef DEBUG 297 printf("getdomnames: n=%d, i=%d, npp=%p\n", n, i, npp); 298 tpp = npp; 299 while (tpp && *tpp++) { 300 printf("tpp=%s\n", *(tpp-1)); 301 } 302 #endif 303 304 goto out; 305 306 err: 307 if (npp) { 308 freedomnames(npp); 309 npp = NULL; 310 } 311 312 if (hp) { 313 res_freehostent(hp); 314 hp = NULL; 315 } 316 317 out: 318 if (cfhost) { 319 free (cfhost); 320 cfhost = NULL; 321 } 322 if (addresses) { 323 krb5_free_addresses(ctx, addresses); 324 addresses = NULL; 325 } 326 327 if (result == 0) 328 *dnames = npp; 329 330 return (result); 331 } 332 333 /* 334 * Set the rpcsec_gss svc names for all net interfaces. 335 */ 336 static void 337 set_svc_domnames(char *svcname, char **dnames, 338 u_int program, u_int version) 339 { 340 bool_t ret; 341 char **tpp = dnames; 342 343 if (!tpp) 344 return; 345 346 while (*tpp++) { 347 /* MAX_NAME_LEN from rpc/rpcsec_gss.h */ 348 char name[MAXHOSTNAMELEN+MAX_NAME_LEN+2] = {0}; 349 (void) snprintf(name, sizeof(name), "%s@%s", 350 svcname, *(tpp-1)); 351 ret = rpc_gss_set_svc_name(name, 352 "kerberos_v5", 0, 353 program, version); 354 if (nofork && ret) 355 (void) fprintf(stderr, 356 "rpc_gss_set_svc_name success: %s\n", 357 name); 358 } 359 } 360 361 362 363 364 int 365 main(int argc, char *argv[]) 366 { 367 void kadm_1(struct svc_req *, SVCXPRT *); 368 SVCXPRT *transp; 369 extern char *optarg; 370 extern int optind, opterr; 371 int ret, rlen, oldnames = 0; 372 OM_uint32 OMret, major_status, minor_status; 373 char *whoami; 374 FILE *acl_file; 375 gss_buffer_desc in_buf; 376 struct servent *srv; 377 struct sockaddr_in addr; 378 struct sockaddr_in *sin; 379 int s; 380 int optchar; 381 struct netconfig *nconf; 382 void *handlep; 383 int fd; 384 struct t_info tinfo; 385 struct t_bind tbindstr, *tres; 386 387 struct t_optmgmt req, resp; 388 struct opthdr *opt; 389 char reqbuf[128]; 390 struct rlimit rl; 391 392 char *kiprop_name = NULL; /* IProp svc name */ 393 kdb_log_context *log_ctx; 394 kadm5_server_handle_t handle; 395 krb5_context ctx; 396 397 kadm5_config_params params; 398 auth_gssapi_name names[6]; 399 gss_buffer_desc gssbuf; 400 gss_OID nt_krb5_name_oid; 401 402 char **dnames = NULL; 403 int retdn; 404 405 /* This is OID value the Krb5_Name NameType */ 406 gssbuf.value = "{1 2 840 113554 1 2 2 1}"; 407 gssbuf.length = strlen(gssbuf.value); 408 major_status = gss_str_to_oid(&minor_status, &gssbuf, 409 &nt_krb5_name_oid); 410 if (major_status != GSS_S_COMPLETE) { 411 fprintf(stderr, 412 gettext("Couldn't create KRB5 Name NameType OID\n")); 413 display_status("str_to_oid", major_status, minor_status); 414 exit(1); 415 } 416 417 names[0].name = names[1].name = names[2].name = 418 names[3].name = names[4].name = names[5].name =NULL; 419 names[0].type = names[1].type = names[2].type = 420 names[3].type = names[4].type = names[5].type = 421 (gss_OID) nt_krb5_name_oid; 422 423 whoami = (strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]); 424 425 (void) setlocale(LC_ALL, ""); 426 427 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 428 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 429 #endif 430 431 (void) textdomain(TEXT_DOMAIN); 432 433 nofork = 0; 434 435 memset((char *) ¶ms, 0, sizeof (params)); 436 437 while ((optchar = getopt(argc, argv, "r:mdp:")) != EOF) { 438 switch (optchar) { 439 case 'r': 440 if (!optarg) 441 usage(); 442 params.realm = optarg; 443 params.mask |= KADM5_CONFIG_REALM; 444 break; 445 case 'm': 446 params.mkey_from_kbd = 1; 447 params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; 448 break; 449 case 'd': 450 nofork = 1; 451 break; 452 case 'p': 453 if (!optarg) 454 usage(); 455 params.kadmind_port = atoi(optarg); 456 params.mask |= KADM5_CONFIG_KADMIND_PORT; 457 break; 458 case '?': 459 default: 460 usage(); 461 } 462 } 463 464 465 if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { 466 rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, FD_SETSIZE); 467 setrlimit(RLIMIT_NOFILE, &rl); 468 } 469 470 if (!nofork && (ret = daemon(0, 0))) { 471 ret = errno; 472 krb5_klog_syslog(LOG_ERR, 473 gettext("Cannot detach from tty: %s"), 474 error_message(ret)); 475 fprintf(stderr, gettext("%s: Cannot detach from tty: %s\n"), 476 whoami, error_message(ret)); 477 krb5_klog_close(context); 478 exit(1); 479 } 480 481 if (ret = krb5_init_context(&context)) { 482 fprintf(stderr, 483 gettext("%s: %s while initializing context, aborting\n"), 484 whoami, error_message(ret)); 485 exit(1); 486 } 487 488 krb5_klog_init(context, "admin_server", whoami, 1); 489 490 491 /* 492 * When using the Horowitz/IETF protocol for 493 * password changing, the default port is 464 494 * (officially recognized by IANA) 495 * 496 * DEFAULT_KPASSWD_PORT -> 464 497 */ 498 chgpw_params.kpasswd_port = DEFAULT_KPASSWD_PORT; 499 chgpw_params.mask |= KADM5_CONFIG_KPASSWD_PORT; 500 chgpw_params.kpasswd_protocol = KRB5_CHGPWD_CHANGEPW_V2; 501 chgpw_params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL; 502 503 if (ret = kadm5_get_config_params(context, NULL, NULL, &chgpw_params, 504 &chgpw_params)) { 505 krb5_klog_syslog(LOG_ERR, gettext("%s: %s while initializing," 506 " aborting"), whoami, error_message(ret)); 507 fprintf(stderr, 508 gettext("%s: %s while initializing, aborting\n"), 509 whoami, error_message(ret)); 510 krb5_klog_close(context); 511 exit(1); 512 } 513 514 /* 515 * We now setup the socket and bind() to port 464, so that 516 * kadmind can now listen to and process change-pwd requests 517 * from non-Solaris Kerberos V5 clients such as Microsoft, 518 * MIT, AIX, HP etc 519 */ 520 if ((schpw = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 521 krb5_klog_syslog(LOG_ERR, gettext( "cannot create simple " 522 "chpw socket: %s"), error_message(errno)); 523 fprintf(stderr, gettext("Cannot create simple chpw " 524 "socket: %s"), error_message(errno)); 525 krb5_klog_close(context); 526 exit(1); 527 } 528 529 memset(&addr, 0, sizeof(addr)); 530 addr.sin_family = AF_INET; 531 addr.sin_addr.s_addr = INADDR_ANY; 532 addr.sin_port = htons(chgpw_params.kpasswd_port); 533 534 if (bind(schpw, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 535 char portbuf[32]; 536 int oerrno = errno; 537 fprintf(stderr, gettext("%s: Cannot bind socket.\n"), whoami); 538 fprintf(stderr, gettext("bind: %s\n"), error_message(oerrno)); 539 errno = oerrno; 540 (void) snprintf(portbuf, sizeof (portbuf), "%d", 541 ntohs(addr.sin_port)); 542 krb5_klog_syslog(LOG_ERR, gettext("cannot bind simple " 543 "chpw socket: %s"), error_message(oerrno)); 544 if(oerrno == EADDRINUSE) { 545 char *w = strrchr(whoami, '/'); 546 if (w) { 547 w++; 548 } 549 else { 550 w = whoami; 551 } 552 fprintf(stderr, gettext( 553 "This probably means that another %s process\n" 554 "is already running, or that another program\n" 555 "is using the server port (number %d).\n" 556 "If another %s is already running, you should\n" 557 "kill it before restarting the server.\n"), 558 w, ntohs(addr.sin_port), w); 559 } 560 krb5_klog_close(context); 561 exit(1); 562 } 563 564 if (ret = kadm5_get_config_params(context, NULL, NULL, ¶ms, 565 ¶ms)) { 566 krb5_klog_syslog(LOG_ERR, gettext("%s: %s while initializing," 567 " aborting"), whoami, error_message(ret)); 568 fprintf(stderr, 569 gettext("%s: %s while initializing, aborting\n"), 570 whoami, error_message(ret)); 571 krb5_klog_close(context); 572 exit(1); 573 } 574 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE | \ 575 KADM5_CONFIG_ADMIN_KEYTAB) 576 577 if ((params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { 578 krb5_klog_syslog(LOG_ERR, 579 gettext("%s: Missing required configuration values " 580 "while initializing, aborting"), whoami, 581 (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); 582 fprintf(stderr, 583 gettext("%s: Missing required configuration values " 584 "(%x) while initializing, aborting\n"), whoami, 585 (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); 586 krb5_klog_close(context); 587 exit(1); 588 } 589 memset((char *) &addr, 0, sizeof (struct sockaddr_in)); 590 addr.sin_family = AF_INET; 591 addr.sin_addr.s_addr = INADDR_ANY; 592 l_port = addr.sin_port = htons(params.kadmind_port); 593 sin = &addr; 594 595 if ((handlep = setnetconfig()) == (void *) NULL) { 596 (void) krb5_klog_syslog(LOG_ERR, 597 gettext("cannot get any transport information")); 598 krb5_klog_close(context); 599 exit(1); 600 } 601 while (nconf = getnetconfig(handlep)) { 602 if ((nconf->nc_semantics == NC_TPI_COTS_ORD) && 603 (strcmp(nconf->nc_protofmly, NC_INET) == 0) && 604 (strcmp(nconf->nc_proto, NC_TCP) == 0)) 605 break; 606 } 607 608 if (nconf == (struct netconfig *) NULL) { 609 (void) endnetconfig(handlep); 610 krb5_klog_close(context); 611 exit(1); 612 } 613 fd = t_open(nconf->nc_device, O_RDWR, &tinfo); 614 if (fd == -1) { 615 krb5_klog_syslog(LOG_ERR, 616 gettext("unable to open connection for ADMIN server")); 617 krb5_klog_close(context); 618 exit(1); 619 } 620 /* LINTED */ 621 opt = (struct opthdr *) reqbuf; 622 opt->level = SOL_SOCKET; 623 opt->name = SO_REUSEADDR; 624 opt->len = sizeof (int); 625 626 /* 627 * The option value is "1". This will allow the server to restart 628 * whilst the previous process is cleaning up after itself in a 629 * FIN_WAIT_2 or TIME_WAIT state. If another process is started 630 * outside of smf(5) then bind will fail anyway, which is what we want. 631 */ 632 reqbuf[sizeof (struct opthdr)] = 1; 633 634 req.flags = T_NEGOTIATE; 635 req.opt.len = sizeof (struct opthdr) + opt->len; 636 req.opt.buf = (char *) opt; 637 638 resp.flags = 0; 639 resp.opt.buf = reqbuf; 640 resp.opt.maxlen = sizeof (reqbuf); 641 642 if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) { 643 t_error("t_optmgmt"); 644 exit(1); 645 } 646 /* Transform addr to netbuf */ 647 648 tres = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR); 649 if (tres == NULL) { 650 (void) t_close(fd); 651 (void) krb5_klog_syslog(LOG_ERR, 652 gettext("cannot allocate netbuf")); 653 krb5_klog_close(context); 654 exit(1); 655 } 656 tbindstr.qlen = 8; 657 tbindstr.addr.buf = (char *) sin; 658 tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr); 659 sin = (struct sockaddr_in *) tbindstr.addr.buf; 660 /* SUNWresync121 XXX (void) memset(&addr, 0, sizeof(addr)); */ 661 662 if (t_bind(fd, &tbindstr, tres) < 0) { 663 int oerrno = errno; 664 fprintf(stderr, gettext("%s: Cannot bind socket.\n"), whoami); 665 fprintf(stderr, gettext("bind: %s\n"), error_message(oerrno)); 666 errno = oerrno; 667 krb5_klog_syslog(LOG_ERR, gettext("Cannot bind socket: %s"), 668 error_message(errno)); 669 if (oerrno == EADDRINUSE) { 670 char *w = strrchr(whoami, '/'); 671 672 if (w) { 673 w++; 674 } else { 675 w = whoami; 676 } 677 fprintf(stderr, gettext( 678 "This probably means that another %s " 679 "process is already\n" 680 "running, or that another program is using " 681 "the server port (number %d)\n" 682 "after being assigned it by the RPC " 683 "portmap deamon. If another\n" 684 "%s is already running, you should kill " 685 "it before\n" 686 "restarting the server. If, on the other hand, " 687 "another program is\n" 688 "using the server port, you should kill it " 689 "before running\n" 690 "%s, and ensure that the conflict does " 691 "not occur in the\n" 692 "future by making sure that %s is started " 693 "on reboot\n" 694 "before portmap.\n"), 695 w, ntohs(addr.sin_port), w, w, w); 696 krb5_klog_syslog(LOG_ERR, 697 gettext("Check for already-running %s or for " 698 "another process using port %d"), w, 699 htons(addr.sin_port)); 700 } 701 krb5_klog_close(context); 702 exit(1); 703 } 704 transp = svc_tli_create(fd, nconf, NULL, 0, 0); 705 (void) t_free((char *) tres, T_BIND); 706 if (transp == NULL) { 707 fprintf(stderr, gettext("%s: Cannot create RPC service.\n"), 708 whoami); 709 krb5_klog_syslog(LOG_ERR, gettext("Cannot create RPC service: %m")); 710 krb5_klog_close(context); 711 exit(1); 712 } 713 if (!svc_register(transp, KADM, KADMVERS, kadm_1, 0)) { 714 fprintf(stderr, 715 gettext("%s: Cannot register RPC service.\n"), whoami); 716 krb5_klog_syslog(LOG_ERR, 717 gettext("Cannot register RPC service, failing.")); 718 krb5_klog_close(context); 719 exit(1); 720 } 721 722 /* 723 * XXX krb5_defkeyname is an internal library global and should go 724 * away 725 */ 726 krb5_overridekeyname = params.admin_keytab; 727 728 (void) kadm5_get_adm_host_srv_name(context, 729 params.realm, &names[0].name); 730 (void) kadm5_get_cpw_host_srv_name(context, 731 params.realm, &names[1].name); 732 names[2].name = KADM5_ADMIN_SERVICE_P; 733 names[3].name = KADM5_CHANGEPW_SERVICE_P; 734 names[4].name = OVSEC_KADM_ADMIN_SERVICE_P; 735 names[5].name = OVSEC_KADM_CHANGEPW_SERVICE_P; 736 737 if (names[0].name == NULL || names[1].name == NULL || 738 names[2].name == NULL || names[3].name == NULL || 739 names[4].name == NULL || names[5].name == NULL) { 740 krb5_klog_syslog(LOG_ERR, 741 gettext("Cannot initialize GSS-API authentication, " 742 "failing.")); 743 fprintf(stderr, 744 gettext("%s: Cannot initialize " 745 "GSS-API authentication.\n"), 746 whoami); 747 krb5_klog_close(context); 748 exit(1); 749 } 750 751 752 /* 753 * Try to acquire creds for the old OV services as well as the new 754 * names, but if that fails just fall back on the new names. 755 */ 756 757 if (rpc_gss_set_svc_name(names[5].name, 758 "kerberos_v5", 0, KADM, KADMVERS) && 759 rpc_gss_set_svc_name(names[4].name, 760 "kerberos_v5", 0, KADM, KADMVERS)) 761 oldnames++; 762 if (rpc_gss_set_svc_name(names[3].name, 763 "kerberos_v5", 0, KADM, KADMVERS)) 764 oldnames++; 765 if (rpc_gss_set_svc_name(names[2].name, 766 "kerberos_v5", 0, KADM, KADMVERS)) 767 oldnames++; 768 if (rpc_gss_set_svc_name(names[0].name, 769 "kerberos_v5", 0, KADM, KADMVERS)) 770 oldnames++; 771 if (rpc_gss_set_svc_name(names[1].name, 772 "kerberos_v5", 0, KADM, KADMVERS)) 773 oldnames++; 774 775 retdn = getdomnames(context, params.realm, &dnames); 776 if (retdn == 0 && dnames) { 777 /* 778 * Multi-homed KDCs sometimes may need to set svc names 779 * for multiple net interfaces so we set them for 780 * all interfaces just in case. 781 */ 782 set_svc_domnames(KADM5_ADMIN_HOST_SERVICE, 783 dnames, KADM, KADMVERS); 784 set_svc_domnames(KADM5_CHANGEPW_HOST_SERVICE, 785 dnames, KADM, KADMVERS); 786 } 787 788 /* if set_names succeeded, this will too */ 789 in_buf.value = names[1].name; 790 in_buf.length = strlen(names[1].name) + 1; 791 (void) gss_import_name(&OMret, &in_buf, (gss_OID) nt_krb5_name_oid, 792 &gss_changepw_name); 793 if (oldnames) { 794 in_buf.value = names[3].name; 795 in_buf.length = strlen(names[3].name) + 1; 796 (void) gss_import_name(&OMret, &in_buf, 797 (gss_OID) nt_krb5_name_oid, 798 &gss_oldchangepw_name); 799 } 800 if (ret = acl_init(context, 0, params.acl_file)) { 801 krb5_klog_syslog(LOG_ERR, gettext("Cannot initialize acl file: %s"), 802 error_message(ret)); 803 fprintf(stderr, gettext("%s: Cannot initialize acl file: %s\n"), 804 whoami, error_message(ret)); 805 krb5_klog_close(context); 806 exit(1); 807 } 808 if ((ret = kadm5_init("kadmind", NULL, 809 NULL, ¶ms, 810 KADM5_STRUCT_VERSION, 811 KADM5_API_VERSION_2, 812 &global_server_handle)) != KADM5_OK) { 813 krb5_klog_syslog(LOG_ERR, 814 gettext("%s while initializing, aborting"), 815 error_message(ret)); 816 fprintf(stderr, 817 gettext("%s: %s while initializing, aborting\n"), 818 whoami, error_message(ret)); 819 krb5_klog_close(context); 820 exit(1); 821 } 822 823 handle = global_server_handle; 824 ctx = handle->context; 825 if (params.iprop_enabled == TRUE) 826 ulog_set_role(ctx, IPROP_MASTER); 827 else 828 ulog_set_role(ctx, IPROP_NULL); 829 830 log_ctx = ctx->kdblog_context; 831 832 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { 833 /* 834 * IProp is enabled, so let's map in the update log 835 * and setup the service. 836 */ 837 if (ret = ulog_map(ctx, ¶ms, FKADMIND)) { 838 fprintf(stderr, 839 gettext("%s: %s while mapping update log " 840 "(`%s.ulog')\n"), whoami, error_message(ret), 841 params.dbname); 842 krb5_klog_syslog(LOG_ERR, 843 gettext("%s while mapping update log " 844 "(`%s.ulog')"), error_message(ret), 845 params.dbname); 846 krb5_klog_close(ctx); 847 exit(1); 848 } 849 850 851 if (nofork) 852 fprintf(stderr, 853 "%s: create IPROP svc (PROG=%d, VERS=%d)\n", 854 whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS); 855 856 if (!svc_create(krb5_iprop_prog_1, 857 KRB5_IPROP_PROG, KRB5_IPROP_VERS, 858 "circuit_v")) { 859 fprintf(stderr, 860 gettext("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"), 861 whoami, 862 KRB5_IPROP_PROG, KRB5_IPROP_VERS); 863 krb5_klog_syslog(LOG_ERR, 864 gettext("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."), 865 KRB5_IPROP_PROG, KRB5_IPROP_VERS); 866 krb5_klog_close(ctx); 867 exit(1); 868 } 869 870 if (ret = kiprop_get_adm_host_srv_name(ctx, 871 params.realm, 872 &kiprop_name)) { 873 krb5_klog_syslog(LOG_ERR, 874 gettext("%s while getting IProp svc name, failing"), 875 error_message(ret)); 876 fprintf(stderr, 877 gettext("%s: %s while getting IProp svc name, failing\n"), 878 whoami, error_message(ret)); 879 krb5_klog_close(ctx); 880 exit(1); 881 } 882 883 if (!rpc_gss_set_svc_name(kiprop_name, "kerberos_v5", 0, 884 KRB5_IPROP_PROG, KRB5_IPROP_VERS)) { 885 rpc_gss_error_t err; 886 (void) rpc_gss_get_error(&err); 887 888 krb5_klog_syslog(LOG_ERR, 889 gettext("Unable to set RPCSEC_GSS service name (`%s'), failing."), 890 kiprop_name ? kiprop_name : "<null>"); 891 892 fprintf(stderr, 893 gettext("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"), 894 whoami, 895 kiprop_name ? kiprop_name : "<null>"); 896 897 if (nofork) { 898 fprintf(stderr, 899 "%s: set svc name (rpcsec err=%d, sys err=%d)\n", 900 whoami, 901 err.rpc_gss_error, 902 err.system_error); 903 } 904 905 exit(1); 906 } 907 free(kiprop_name); 908 909 if (retdn == 0 && dnames) { 910 set_svc_domnames(KADM5_KIPROP_HOST_SERVICE, 911 dnames, 912 KRB5_IPROP_PROG, KRB5_IPROP_VERS); 913 } 914 915 } else { 916 if (!oldnames) { 917 /* rpc_gss_set_svc_name failed for both kadmin/<fqdn> and 918 * changepw/<fqdn>. 919 */ 920 krb5_klog_syslog(LOG_ERR, 921 gettext("Unable to set RPCSEC_GSS service names " 922 "('%s, %s')"), 923 names[0].name, names[1].name); 924 fprintf(stderr, 925 gettext("%s: Unable to set RPCSEC_GSS service names " 926 "('%s, %s')\n"), 927 whoami, 928 names[0].name, names[1].name); 929 krb5_klog_close(context); 930 exit(1); 931 } 932 } 933 934 if (dnames) 935 freedomnames(dnames); 936 937 setup_signal_handlers(log_ctx->iproprole); 938 krb5_klog_syslog(LOG_INFO, gettext("starting")); 939 if (nofork) 940 fprintf(stderr, "%s: starting...\n", whoami); 941 942 943 /* 944 * We now call our own customized async event processing 945 * function kadm_svc_run(), as opposed to svc_run() earlier, 946 * since this enables kadmind to also listen-to/process 947 * non-RPCSEC_GSS based change-pwd requests apart from the 948 * regular, RPCSEC_GSS kpasswd requests from Solaris Krb5 clients. 949 */ 950 kadm_svc_run(); 951 952 krb5_klog_syslog(LOG_INFO, gettext("finished, exiting")); 953 kadm5_destroy(global_server_handle); 954 t_close(fd); 955 krb5_klog_close(context); 956 exit(0); 957 } 958 959 960 /* 961 * Function: kadm_svc_run 962 * 963 * Purpose: modified version of sunrpc svc_run. 964 * which closes the database every TIMEOUT seconds. 965 * 966 * Arguments: 967 * Requires: 968 * Effects: 969 * Modifies: 970 */ 971 void 972 kadm_svc_run(void) 973 { 974 struct pollfd *rfd = 0; 975 struct timeval timeout; 976 int pollret; 977 int nfds = 0; 978 int i; 979 980 while(signal_request_exit == 0) { 981 timeout.tv_sec = TIMEOUT; 982 timeout.tv_usec = 0; 983 984 if (nfds != svc_max_pollfd) { 985 rfd = realloc(rfd, sizeof (pollfd_t) * svc_max_pollfd); 986 nfds = svc_max_pollfd; 987 } 988 989 (void) memcpy(rfd, svc_pollfd, 990 sizeof (pollfd_t) * svc_max_pollfd); 991 992 for (i = 0; i < nfds; i++) { 993 if (rfd[i].fd == -1) { 994 rfd[i].fd = schpw; 995 rfd[i].events = POLLIN; 996 break; 997 } 998 } 999 1000 switch(pollret = poll(rfd, nfds, 1001 __rpc_timeval_to_msec(&timeout))) { 1002 case -1: 1003 if(errno == EINTR) 1004 continue; 1005 perror("poll"); 1006 return; 1007 case 0: 1008 continue; 1009 default: 1010 for (i = 0; i < nfds; i++) { 1011 if (rfd[i].revents & POLLIN) { 1012 if (rfd[i].fd == schpw) 1013 handle_chpw(context, schpw, 1014 global_server_handle, 1015 &chgpw_params); 1016 else 1017 svc_getreq_poll(rfd, pollret); 1018 break; 1019 } else { 1020 if (i == (nfds - 1)) 1021 perror("poll"); 1022 } 1023 } 1024 break; 1025 } 1026 } 1027 } 1028 1029 1030 /* 1031 * Function: setup_signal_handlers 1032 * 1033 * Purpose: Setup signal handling functions with System V's signal(). 1034 */ 1035 void setup_signal_handlers(iprop_role iproprole) { 1036 signal(SIGINT, sig_exit); 1037 signal(SIGTERM, sig_exit); 1038 signal(SIGQUIT, sig_exit); 1039 signal(SIGPIPE, sig_pipe); 1040 1041 /* 1042 * IProp will fork for a full-resync, we don't want to 1043 * wait on it and we don't want the living dead procs either. 1044 */ 1045 if (iproprole == IPROP_MASTER) 1046 (void) signal(SIGCHLD, SIG_IGN); 1047 1048 return; 1049 } 1050 1051 1052 /* 1053 * Function: sig_exit 1054 * 1055 * Purpose: sets flags saying the server got a signal and that it 1056 * should exit when convenient. 1057 * 1058 * Effects: 1059 * Modifies signal_request_exit which ideally makes the server exit 1060 * at some point. 1061 * 1062 * Modifies: 1063 * Signal_request_exit 1064 */ 1065 void sig_exit(int signum) 1066 { 1067 krb5_klog_syslog(LOG_NOTICE, gettext("Got signal to request exit")); 1068 signal_request_exit = 1; 1069 return; 1070 } 1071 1072 1073 /* 1074 * Function: sig_pipe 1075 * 1076 * Purpose: SIGPIPE handler 1077 * 1078 * Effects: krb5_klog_syslog a message that a SIGPIPE occurred and returns, 1079 * thus causing the read() or write() to fail and, presumable, the RPC 1080 * to recover. Otherwise, the process aborts. 1081 */ 1082 void 1083 sig_pipe(int unused) 1084 { 1085 krb5_klog_syslog(LOG_NOTICE, gettext("Warning: Received a SIGPIPE; " 1086 "probably a client aborted. Continuing.")); 1087 } 1088