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