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