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