1 /* 2 * Copyright 2008 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_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 [-x db_args]* [-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 char **db_args = NULL; 438 int db_args_size = 0; 439 auth_gssapi_name names[6]; 440 gss_buffer_desc gssbuf; 441 gss_OID nt_krb5_name_oid; 442 443 char **dnames = NULL; 444 int retdn; 445 int iprop_supported; 446 447 /* This is OID value the Krb5_Name NameType */ 448 gssbuf.value = "{1 2 840 113554 1 2 2 1}"; 449 gssbuf.length = strlen(gssbuf.value); 450 major_status = gss_str_to_oid(&minor_status, &gssbuf, 451 &nt_krb5_name_oid); 452 if (major_status != GSS_S_COMPLETE) { 453 fprintf(stderr, 454 gettext("Couldn't create KRB5 Name NameType OID\n")); 455 display_status("str_to_oid", major_status, minor_status); 456 exit(1); 457 } 458 459 names[0].name = names[1].name = names[2].name = 460 names[3].name = names[4].name = names[5].name =NULL; 461 names[0].type = names[1].type = names[2].type = 462 names[3].type = names[4].type = names[5].type = 463 (gss_OID) nt_krb5_name_oid; 464 465 whoami = (strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]); 466 467 (void) setlocale(LC_ALL, ""); 468 469 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 470 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 471 #endif 472 473 (void) textdomain(TEXT_DOMAIN); 474 475 nofork = 0; 476 477 memset((char *) ¶ms, 0, sizeof (params)); 478 479 while ((optchar = getopt(argc, argv, "r:mdp:x:")) != EOF) { 480 switch (optchar) { 481 case 'r': 482 if (!optarg) 483 usage(); 484 params.realm = optarg; 485 params.mask |= KADM5_CONFIG_REALM; 486 break; 487 case 'm': 488 params.mkey_from_kbd = 1; 489 params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; 490 break; 491 case 'd': 492 nofork = 1; 493 break; 494 case 'p': 495 if (!optarg) 496 usage(); 497 params.kadmind_port = atoi(optarg); 498 params.mask |= KADM5_CONFIG_KADMIND_PORT; 499 break; 500 case 'x': 501 if (!optarg) 502 usage(); 503 db_args_size++; 504 { 505 char **temp = realloc( db_args, 506 sizeof(char*) * (db_args_size+1)); /* one for NULL */ 507 if( temp == NULL ) 508 { 509 fprintf(stderr, gettext("%s: cannot initialize. Not enough memory\n"), 510 whoami); 511 exit(1); 512 } 513 db_args = temp; 514 } 515 db_args[db_args_size-1] = optarg; 516 db_args[db_args_size] = NULL; 517 break; 518 case '?': 519 default: 520 usage(); 521 } 522 } 523 524 525 if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { 526 rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, FD_SETSIZE); 527 (void) setrlimit(RLIMIT_NOFILE, &rl); 528 (void) enable_extended_FILE_stdio(-1, -1); 529 } 530 531 if (ret = krb5_init_context(&context)) { 532 fprintf(stderr, 533 gettext("%s: %s while initializing context, aborting\n"), 534 whoami, error_message(ret)); 535 exit(1); 536 } 537 538 krb5_klog_init(context, "admin_server", whoami, 1); 539 /* SUNW14resync */ 540 #if 0 541 krb5_klog_syslog(LOG_INFO, "Seeding random number generator"); 542 ret = krb5_c_random_os_entropy(context, 1, NULL); 543 if(ret) { 544 krb5_klog_syslog(LOG_ERR, "Error getting random seed: %s, aborting", 545 error_message(ret)); 546 exit(1); 547 } 548 #endif 549 550 /* 551 * When using the Horowitz/IETF protocol for 552 * password changing, the default port is 464 553 * (officially recognized by IANA) 554 * 555 * DEFAULT_KPASSWD_PORT -> 464 556 */ 557 chgpw_params.kpasswd_port = DEFAULT_KPASSWD_PORT; 558 chgpw_params.mask |= KADM5_CONFIG_KPASSWD_PORT; 559 chgpw_params.kpasswd_protocol = KRB5_CHGPWD_CHANGEPW_V2; 560 chgpw_params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL; 561 562 if (ret = kadm5_get_config_params(context, NULL, NULL, &chgpw_params, 563 &chgpw_params)) { 564 krb5_klog_syslog(LOG_ERR, gettext("%s: %s while initializing," 565 " aborting"), whoami, error_message(ret)); 566 fprintf(stderr, 567 gettext("%s: %s while initializing, aborting\n"), 568 whoami, error_message(ret)); 569 krb5_klog_close(context); 570 exit(1); 571 } 572 573 /* 574 * We now setup the socket and bind() to port 464, so that 575 * kadmind can now listen to and process change-pwd requests 576 * from non-Solaris Kerberos V5 clients such as Microsoft, 577 * MIT, AIX, HP etc 578 */ 579 if ((schpw = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 580 krb5_klog_syslog(LOG_ERR, gettext( "cannot create simple " 581 "chpw socket: %s"), error_message(errno)); 582 fprintf(stderr, gettext("Cannot create simple chpw " 583 "socket: %s"), error_message(errno)); 584 krb5_klog_close(context); 585 exit(1); 586 } 587 588 memset(&addr, 0, sizeof(addr)); 589 addr.sin_family = AF_INET; 590 addr.sin_addr.s_addr = INADDR_ANY; 591 addr.sin_port = htons(chgpw_params.kpasswd_port); 592 593 if (bind(schpw, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 594 char portbuf[32]; 595 int oerrno = errno; 596 fprintf(stderr, gettext("%s: Cannot bind socket.\n"), whoami); 597 fprintf(stderr, gettext("bind: %s\n"), error_message(oerrno)); 598 errno = oerrno; 599 (void) snprintf(portbuf, sizeof (portbuf), "%d", 600 ntohs(addr.sin_port)); 601 krb5_klog_syslog(LOG_ERR, gettext("cannot bind simple " 602 "chpw socket: %s"), error_message(oerrno)); 603 if(oerrno == EADDRINUSE) { 604 char *w = strrchr(whoami, '/'); 605 if (w) { 606 w++; 607 } 608 else { 609 w = whoami; 610 } 611 fprintf(stderr, gettext( 612 "This probably means that another %s process\n" 613 "is already running, or that another program\n" 614 "is using the server port (number %d).\n" 615 "If another %s is already running, you should\n" 616 "kill it before restarting the server.\n"), 617 w, ntohs(addr.sin_port), w); 618 } 619 krb5_klog_close(context); 620 exit(1); 621 } 622 623 if (ret = kadm5_get_config_params(context, NULL, NULL, ¶ms, 624 ¶ms)) { 625 krb5_klog_syslog(LOG_ERR, gettext("%s: %s while initializing," 626 " aborting"), whoami, error_message(ret)); 627 fprintf(stderr, 628 gettext("%s: %s while initializing, aborting\n"), 629 whoami, error_message(ret)); 630 krb5_klog_close(context); 631 exit(1); 632 } 633 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE) 634 635 if ((params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { 636 krb5_klog_syslog(LOG_ERR, 637 gettext("%s: Missing required configuration values " 638 "while initializing, aborting"), whoami, 639 (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); 640 fprintf(stderr, 641 gettext("%s: Missing required configuration values " 642 "(%lx) while initializing, aborting\n"), whoami, 643 (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); 644 krb5_klog_close(context); 645 exit(1); 646 } 647 memset((char *) &addr, 0, sizeof (struct sockaddr_in)); 648 addr.sin_family = AF_INET; 649 addr.sin_addr.s_addr = INADDR_ANY; 650 l_port = addr.sin_port = htons(params.kadmind_port); 651 sin = &addr; 652 653 if ((handlep = setnetconfig()) == (void *) NULL) { 654 (void) krb5_klog_syslog(LOG_ERR, 655 gettext("cannot get any transport information")); 656 krb5_klog_close(context); 657 exit(1); 658 } 659 while (nconf = getnetconfig(handlep)) { 660 if ((nconf->nc_semantics == NC_TPI_COTS_ORD) && 661 (strcmp(nconf->nc_protofmly, NC_INET) == 0) && 662 (strcmp(nconf->nc_proto, NC_TCP) == 0)) 663 break; 664 } 665 666 if (nconf == (struct netconfig *) NULL) { 667 (void) endnetconfig(handlep); 668 krb5_klog_close(context); 669 exit(1); 670 } 671 fd = t_open(nconf->nc_device, O_RDWR, &tinfo); 672 if (fd == -1) { 673 krb5_klog_syslog(LOG_ERR, 674 gettext("unable to open connection for ADMIN server")); 675 krb5_klog_close(context); 676 exit(1); 677 } 678 /* LINTED */ 679 opt = (struct opthdr *) reqbuf; 680 opt->level = SOL_SOCKET; 681 opt->name = SO_REUSEADDR; 682 opt->len = sizeof (int); 683 684 /* 685 * The option value is "1". This will allow the server to restart 686 * whilst the previous process is cleaning up after itself in a 687 * FIN_WAIT_2 or TIME_WAIT state. If another process is started 688 * outside of smf(5) then bind will fail anyway, which is what we want. 689 */ 690 reqbuf[sizeof (struct opthdr)] = 1; 691 692 req.flags = T_NEGOTIATE; 693 req.opt.len = sizeof (struct opthdr) + opt->len; 694 req.opt.buf = (char *) opt; 695 696 resp.flags = 0; 697 resp.opt.buf = reqbuf; 698 resp.opt.maxlen = sizeof (reqbuf); 699 700 if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) { 701 t_error("t_optmgmt"); 702 exit(1); 703 } 704 /* Transform addr to netbuf */ 705 706 tres = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR); 707 if (tres == NULL) { 708 (void) t_close(fd); 709 (void) krb5_klog_syslog(LOG_ERR, 710 gettext("cannot allocate netbuf")); 711 krb5_klog_close(context); 712 exit(1); 713 } 714 tbindstr.qlen = 8; 715 tbindstr.addr.buf = (char *) sin; 716 tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr); 717 sin = (struct sockaddr_in *) tbindstr.addr.buf; 718 /* SUNWresync121 XXX (void) memset(&addr, 0, sizeof(addr)); */ 719 720 if (t_bind(fd, &tbindstr, tres) < 0) { 721 int oerrno = errno; 722 fprintf(stderr, gettext("%s: Cannot bind socket.\n"), whoami); 723 fprintf(stderr, gettext("bind: %s\n"), error_message(oerrno)); 724 errno = oerrno; 725 krb5_klog_syslog(LOG_ERR, gettext("Cannot bind socket: %s"), 726 error_message(errno)); 727 if (oerrno == EADDRINUSE) { 728 char *w = strrchr(whoami, '/'); 729 730 if (w) { 731 w++; 732 } else { 733 w = whoami; 734 } 735 fprintf(stderr, gettext( 736 "This probably means that another %s " 737 "process is already\n" 738 "running, or that another program is using " 739 "the server port (number %d)\n" 740 "after being assigned it by the RPC " 741 "portmap deamon. If another\n" 742 "%s is already running, you should kill " 743 "it before\n" 744 "restarting the server. If, on the other hand, " 745 "another program is\n" 746 "using the server port, you should kill it " 747 "before running\n" 748 "%s, and ensure that the conflict does " 749 "not occur in the\n" 750 "future by making sure that %s is started " 751 "on reboot\n" 752 "before portmap.\n"), 753 w, ntohs(addr.sin_port), w, w, w); 754 krb5_klog_syslog(LOG_ERR, 755 gettext("Check for already-running %s or for " 756 "another process using port %d"), w, 757 htons(addr.sin_port)); 758 } 759 krb5_klog_close(context); 760 exit(1); 761 } 762 transp = svc_tli_create(fd, nconf, NULL, 0, 0); 763 (void) t_free((char *) tres, T_BIND); 764 if (transp == NULL) { 765 fprintf(stderr, gettext("%s: Cannot create RPC service.\n"), 766 whoami); 767 krb5_klog_syslog(LOG_ERR, gettext("Cannot create RPC service: %m")); 768 krb5_klog_close(context); 769 exit(1); 770 } 771 if (!svc_register(transp, KADM, KADMVERS, kadm_1, 0)) { 772 fprintf(stderr, 773 gettext("%s: Cannot register RPC service.\n"), whoami); 774 krb5_klog_syslog(LOG_ERR, 775 gettext("Cannot register RPC service, failing.")); 776 krb5_klog_close(context); 777 exit(1); 778 } 779 780 /* 781 * XXX krb5_defkeyname is an internal library global and should go 782 * away 783 */ 784 krb5_overridekeyname = params.admin_keytab; 785 786 /* Solaris Kerberos: 787 * The only service principals which matter here are 788 * -> names[0].name (kadmin/<fqdn>) 789 * -> names[1].name (changepw/<fqdn>) 790 * KADM5_ADMIN_SERVICE_P, KADM5_CHANGEPW_SERVICE_P, 791 * OVSEC_KADM_ADMIN_SERVICE_P, OVSEC_KADM_CHANGEPW_SERVICE_P 792 * are all legacy service princs and calls to rpc_gss_set_svc_name() 793 * using these principals will always fail as they are not host 794 * based principals. 795 */ 796 797 (void) kadm5_get_adm_host_srv_name(context, 798 params.realm, &names[0].name); 799 (void) kadm5_get_cpw_host_srv_name(context, 800 params.realm, &names[1].name); 801 names[2].name = KADM5_ADMIN_SERVICE_P; 802 names[3].name = KADM5_CHANGEPW_SERVICE_P; 803 names[4].name = OVSEC_KADM_ADMIN_SERVICE_P; 804 names[5].name = OVSEC_KADM_CHANGEPW_SERVICE_P; 805 806 if (names[0].name == NULL || names[1].name == NULL || 807 names[2].name == NULL || names[3].name == NULL || 808 names[4].name == NULL || names[5].name == NULL) { 809 krb5_klog_syslog(LOG_ERR, 810 gettext("Cannot initialize GSS-API authentication, " 811 "failing.")); 812 fprintf(stderr, 813 gettext("%s: Cannot initialize " 814 "GSS-API authentication.\n"), 815 whoami); 816 krb5_klog_close(context); 817 exit(1); 818 } 819 820 821 /* 822 * Try to acquire creds for the old OV services as well as the new 823 * names, but if that fails just fall back on the new names. 824 */ 825 826 if (rpc_gss_set_svc_name(names[5].name, 827 "kerberos_v5", 0, KADM, KADMVERS) && 828 rpc_gss_set_svc_name(names[4].name, 829 "kerberos_v5", 0, KADM, KADMVERS)) 830 oldnames++; 831 if (rpc_gss_set_svc_name(names[3].name, 832 "kerberos_v5", 0, KADM, KADMVERS)) 833 oldnames++; 834 if (rpc_gss_set_svc_name(names[2].name, 835 "kerberos_v5", 0, KADM, KADMVERS)) 836 oldnames++; 837 838 /* If rpc_gss_set_svc_name() fails for either kadmin/<fqdn> or 839 * for changepw/<fqdn> then try to determine if this is caused 840 * by a missing keytab file or entry. If so, log it and continue. 841 */ 842 if (rpc_gss_set_svc_name(names[0].name, 843 "kerberos_v5", 0, KADM, KADMVERS)) 844 oldnames++; 845 else 846 log_kt_error(names[0].name, whoami); 847 if (rpc_gss_set_svc_name(names[1].name, 848 "kerberos_v5", 0, KADM, KADMVERS)) 849 oldnames++; 850 else 851 log_kt_error(names[1].name, whoami); 852 853 retdn = getdomnames(context, params.realm, &dnames); 854 if (retdn == 0 && dnames) { 855 /* 856 * Multi-homed KDCs sometimes may need to set svc names 857 * for multiple net interfaces so we set them for 858 * all interfaces just in case. 859 */ 860 set_svc_domnames(KADM5_ADMIN_HOST_SERVICE, 861 dnames, KADM, KADMVERS); 862 set_svc_domnames(KADM5_CHANGEPW_HOST_SERVICE, 863 dnames, KADM, KADMVERS); 864 } 865 866 /* if set_names succeeded, this will too */ 867 in_buf.value = names[1].name; 868 in_buf.length = strlen(names[1].name) + 1; 869 (void) gss_import_name(&OMret, &in_buf, (gss_OID) nt_krb5_name_oid, 870 &gss_changepw_name); 871 if (oldnames) { 872 in_buf.value = names[3].name; 873 in_buf.length = strlen(names[3].name) + 1; 874 (void) gss_import_name(&OMret, &in_buf, 875 (gss_OID) nt_krb5_name_oid, 876 &gss_oldchangepw_name); 877 } 878 if (ret = kadm5int_acl_init(context, 0, params.acl_file)) { 879 krb5_klog_syslog(LOG_ERR, gettext("Cannot initialize acl file: %s"), 880 error_message(ret)); 881 fprintf(stderr, gettext("%s: Cannot initialize acl file: %s\n"), 882 whoami, error_message(ret)); 883 krb5_klog_close(context); 884 exit(1); 885 } 886 if ((ret = kadm5_init("kadmind", NULL, 887 NULL, ¶ms, 888 KADM5_STRUCT_VERSION, 889 KADM5_API_VERSION_2, 890 db_args, 891 &global_server_handle)) != KADM5_OK) { 892 krb5_klog_syslog(LOG_ERR, 893 gettext("%s while initializing, aborting"), 894 error_message(ret)); 895 fprintf(stderr, 896 gettext("%s: %s while initializing, aborting\n"), 897 whoami, error_message(ret)); 898 krb5_klog_close(context); 899 exit(1); 900 } 901 902 if (!nofork && (ret = daemon(0, 0))) { 903 ret = errno; 904 krb5_klog_syslog(LOG_ERR, 905 gettext("Cannot detach from tty: %s"), 906 error_message(ret)); 907 fprintf(stderr, gettext("%s: Cannot detach from tty: %s\n"), 908 whoami, error_message(ret)); 909 krb5_klog_close(context); 910 exit(1); 911 } 912 913 if( db_args ) 914 { 915 free(db_args), db_args=NULL; 916 } 917 918 handle = global_server_handle; 919 ctx = handle->context; 920 if (params.iprop_enabled == TRUE) { 921 if (ret = krb5_db_supports_iprop(ctx, &iprop_supported)) { 922 fprintf(stderr, 923 gettext("%s: %s while trying to determine if KDB " 924 "plugin supports iprop\n"), whoami, 925 error_message(ret)); 926 krb5_klog_syslog(LOG_ERR, 927 gettext("%s while trying to determine if KDB " 928 "plugin supports iprop"), error_message(ret)); 929 krb5_klog_close(ctx); 930 exit(1); 931 } 932 933 if (!iprop_supported) { 934 fprintf(stderr, 935 gettext("%s: Warning, current KDB " 936 "plugin does not support iprop, continuing " 937 "with iprop disabled\n"), whoami); 938 krb5_klog_syslog(LOG_WARNING, 939 gettext("Warning, current KDB " 940 "plugin does not support iprop, continuing " 941 "with iprop disabled")); 942 943 ulog_set_role(ctx, IPROP_NULL); 944 } else 945 ulog_set_role(ctx, IPROP_MASTER); 946 } else 947 ulog_set_role(ctx, IPROP_NULL); 948 949 log_ctx = ctx->kdblog_context; 950 951 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { 952 /* 953 * IProp is enabled, so let's map in the update log 954 * and setup the service. 955 */ 956 if (ret = ulog_map(ctx, ¶ms, FKADMIND)) { 957 fprintf(stderr, 958 gettext("%s: %s while mapping update log " 959 "(`%s.ulog')\n"), whoami, error_message(ret), 960 params.dbname); 961 krb5_klog_syslog(LOG_ERR, 962 gettext("%s while mapping update log " 963 "(`%s.ulog')"), error_message(ret), 964 params.dbname); 965 krb5_klog_close(ctx); 966 exit(1); 967 } 968 969 970 if (nofork) 971 fprintf(stderr, 972 "%s: create IPROP svc (PROG=%d, VERS=%d)\n", 973 whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS); 974 975 if (!svc_create(krb5_iprop_prog_1, 976 KRB5_IPROP_PROG, KRB5_IPROP_VERS, 977 "circuit_v")) { 978 fprintf(stderr, 979 gettext("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"), 980 whoami, 981 KRB5_IPROP_PROG, KRB5_IPROP_VERS); 982 krb5_klog_syslog(LOG_ERR, 983 gettext("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."), 984 KRB5_IPROP_PROG, KRB5_IPROP_VERS); 985 krb5_klog_close(ctx); 986 exit(1); 987 } 988 989 if (ret = kiprop_get_adm_host_srv_name(ctx, 990 params.realm, 991 &kiprop_name)) { 992 krb5_klog_syslog(LOG_ERR, 993 gettext("%s while getting IProp svc name, failing"), 994 error_message(ret)); 995 fprintf(stderr, 996 gettext("%s: %s while getting IProp svc name, failing\n"), 997 whoami, error_message(ret)); 998 krb5_klog_close(ctx); 999 exit(1); 1000 } 1001 1002 if (!rpc_gss_set_svc_name(kiprop_name, "kerberos_v5", 0, 1003 KRB5_IPROP_PROG, KRB5_IPROP_VERS)) { 1004 rpc_gss_error_t err; 1005 (void) rpc_gss_get_error(&err); 1006 1007 /* Try to determine if the error was caused by a missing keytab or 1008 * missing keytab entries (and log it). 1009 */ 1010 log_kt_error(kiprop_name, whoami); 1011 krb5_klog_syslog(LOG_ERR, 1012 gettext("Unable to set RPCSEC_GSS service name (`%s'), failing."), 1013 kiprop_name ? kiprop_name : "<null>"); 1014 fprintf(stderr, 1015 gettext("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"), 1016 whoami, 1017 kiprop_name ? kiprop_name : "<null>"); 1018 1019 if (nofork) { 1020 fprintf(stderr, 1021 "%s: set svc name (rpcsec err=%d, sys err=%d)\n", 1022 whoami, 1023 err.rpc_gss_error, 1024 err.system_error); 1025 } 1026 1027 exit(1); 1028 } 1029 free(kiprop_name); 1030 1031 if (retdn == 0 && dnames) { 1032 set_svc_domnames(KADM5_KIPROP_HOST_SERVICE, 1033 dnames, 1034 KRB5_IPROP_PROG, KRB5_IPROP_VERS); 1035 } 1036 1037 } else { 1038 if (!oldnames) { 1039 /* rpc_gss_set_svc_name failed for both kadmin/<fqdn> and 1040 * changepw/<fqdn>. 1041 */ 1042 krb5_klog_syslog(LOG_ERR, 1043 gettext("Unable to set RPCSEC_GSS service names " 1044 "('%s, %s')"), 1045 names[0].name, names[1].name); 1046 fprintf(stderr, 1047 gettext("%s: Unable to set RPCSEC_GSS service names " 1048 "('%s, %s')\n"), 1049 whoami, 1050 names[0].name, names[1].name); 1051 krb5_klog_close(context); 1052 exit(1); 1053 } 1054 } 1055 1056 if (dnames) 1057 freedomnames(dnames); 1058 1059 setup_signal_handlers(log_ctx->iproprole); 1060 krb5_klog_syslog(LOG_INFO, gettext("starting")); 1061 if (nofork) 1062 fprintf(stderr, "%s: starting...\n", whoami); 1063 1064 1065 /* 1066 * We now call our own customized async event processing 1067 * function kadm_svc_run(), as opposed to svc_run() earlier, 1068 * since this enables kadmind to also listen-to/process 1069 * non-RPCSEC_GSS based change-pwd requests apart from the 1070 * regular, RPCSEC_GSS kpasswd requests from Solaris Krb5 clients. 1071 */ 1072 kadm_svc_run(); 1073 1074 krb5_klog_syslog(LOG_INFO, gettext("finished, exiting")); 1075 kadm5_destroy(global_server_handle); 1076 t_close(fd); 1077 krb5_klog_close(context); 1078 exit(0); 1079 } 1080 1081 1082 /* 1083 * Function: kadm_svc_run 1084 * 1085 * Purpose: modified version of sunrpc svc_run. 1086 * which closes the database every TIMEOUT seconds. 1087 * 1088 * Arguments: 1089 * Requires: 1090 * Effects: 1091 * Modifies: 1092 */ 1093 void 1094 kadm_svc_run(void) 1095 { 1096 struct pollfd *rfd = 0; 1097 struct timeval timeout; 1098 int pollret; 1099 int nfds = 0; 1100 int i; 1101 1102 while(signal_request_exit == 0) { 1103 timeout.tv_sec = TIMEOUT; 1104 timeout.tv_usec = 0; 1105 1106 if (nfds != svc_max_pollfd) { 1107 rfd = realloc(rfd, sizeof (pollfd_t) * svc_max_pollfd); 1108 nfds = svc_max_pollfd; 1109 } 1110 1111 (void) memcpy(rfd, svc_pollfd, 1112 sizeof (pollfd_t) * svc_max_pollfd); 1113 1114 for (i = 0; i < nfds; i++) { 1115 if (rfd[i].fd == -1) { 1116 rfd[i].fd = schpw; 1117 rfd[i].events = POLLIN; 1118 break; 1119 } 1120 } 1121 1122 switch(pollret = poll(rfd, nfds, 1123 __rpc_timeval_to_msec(&timeout))) { 1124 case -1: 1125 if(errno == EINTR) 1126 continue; 1127 perror("poll"); 1128 return; 1129 case 0: 1130 continue; 1131 default: 1132 for (i = 0; i < nfds; i++) { 1133 if (rfd[i].revents & POLLIN) { 1134 if (rfd[i].fd == schpw) 1135 handle_chpw(context, schpw, 1136 global_server_handle, 1137 &chgpw_params); 1138 else 1139 svc_getreq_poll(rfd, pollret); 1140 break; 1141 } else { 1142 if (i == (nfds - 1)) 1143 perror("poll"); 1144 } 1145 } 1146 break; 1147 } 1148 } 1149 } 1150 1151 1152 /* 1153 * Function: setup_signal_handlers 1154 * 1155 * Purpose: Setup signal handling functions with System V's signal(). 1156 */ 1157 void setup_signal_handlers(iprop_role iproprole) { 1158 signal(SIGINT, sig_exit); 1159 signal(SIGTERM, sig_exit); 1160 signal(SIGQUIT, sig_exit); 1161 signal(SIGPIPE, sig_pipe); 1162 1163 /* 1164 * IProp will fork for a full-resync, we don't want to 1165 * wait on it and we don't want the living dead procs either. 1166 */ 1167 if (iproprole == IPROP_MASTER) 1168 (void) signal(SIGCHLD, SIG_IGN); 1169 1170 return; 1171 } 1172 1173 1174 /* 1175 * Function: sig_exit 1176 * 1177 * Purpose: sets flags saying the server got a signal and that it 1178 * should exit when convenient. 1179 * 1180 * Effects: 1181 * Modifies signal_request_exit which ideally makes the server exit 1182 * at some point. 1183 * 1184 * Modifies: 1185 * Signal_request_exit 1186 */ 1187 void sig_exit(int signum) 1188 { 1189 krb5_klog_syslog(LOG_NOTICE, gettext("Got signal to request exit")); 1190 signal_request_exit = 1; 1191 return; 1192 } 1193 1194 1195 /* 1196 * Function: sig_pipe 1197 * 1198 * Purpose: SIGPIPE handler 1199 * 1200 * Effects: krb5_klog_syslog a message that a SIGPIPE occurred and returns, 1201 * thus causing the read() or write() to fail and, presumable, the RPC 1202 * to recover. Otherwise, the process aborts. 1203 */ 1204 void 1205 sig_pipe(int unused) 1206 { 1207 krb5_klog_syslog(LOG_NOTICE, gettext("Warning: Received a SIGPIPE; " 1208 "probably a client aborted. Continuing.")); 1209 } 1210 1211 1212 /* 1213 * Given a service name (s_name) determine if the keytab file exists 1214 * and if the keytab entry is present. Log missing keytab 1215 * at LOG_ERR and log missing keytab entries at LOG_WARNING. 1216 * If any of krb5_* (or strdup) fail it will return the failure. 1217 */ 1218 krb5_error_code log_kt_error(char *s_name, char *whoami) { 1219 krb5_keytab kt; 1220 krb5_principal princ; 1221 krb5_keytab_entry entry; 1222 krb5_error_code code = 0; 1223 char kt_name[MAX_KEYTAB_NAME_LEN]; 1224 char *service; 1225 char *host; 1226 1227 service = strdup(s_name); 1228 if(!service) 1229 return ENOMEM; 1230 1231 host = strchr(service, '@'); 1232 *host++ = '\0'; 1233 if (code = krb5_sname_to_principal(context, host, 1234 service, KRB5_NT_SRV_HST, &princ)) { 1235 krb5_klog_syslog(LOG_ERR, 1236 gettext("krb5_sname_to_principal failed: %s"), 1237 error_message(code)); 1238 fprintf(stderr, 1239 gettext("%s: krb5_sname_to_principal failed: %s"), 1240 whoami, error_message(code)); 1241 free(service); 1242 return code; 1243 } 1244 1245 if (code = krb5_kt_default_name(context, kt_name, sizeof (kt_name))) { 1246 krb5_klog_syslog(LOG_ERR, 1247 gettext("krb5_kt_default_name failed: %s"), 1248 error_message(code)); 1249 fprintf(stderr, 1250 gettext("%s: krb5_kt_default_name failed: %s"), 1251 whoami, error_message(code)); 1252 krb5_free_principal(context, princ); 1253 free(service); 1254 return code; 1255 } 1256 1257 if (code = krb5_kt_default(context, &kt)) { 1258 krb5_klog_syslog(LOG_ERR, 1259 gettext("krb5_kt_default failed: %s"), 1260 error_message(code)); 1261 fprintf(stderr, 1262 gettext("%s: krb5_kt_default failed: %s"), 1263 whoami, error_message(code)); 1264 krb5_free_principal(context, princ); 1265 free(service); 1266 return code; 1267 } 1268 1269 code = krb5_kt_get_entry(context, kt, princ, 0, 0, &entry); 1270 1271 switch (code) { 1272 case 0: 1273 krb5_kt_free_entry(context, &entry); 1274 break; 1275 case KRB5_KT_NOTFOUND: 1276 krb5_klog_syslog(LOG_WARNING, 1277 gettext("Keytab entry \"%s/%s\" is missing from \"%s\""), 1278 service, host, 1279 kt_name); 1280 fprintf(stderr, 1281 gettext("%s: Keytab entry \"%s/%s\" is missing from \"%s\".\n"), 1282 whoami, 1283 service, host, 1284 kt_name); 1285 break; 1286 case ENOENT: 1287 krb5_klog_syslog(LOG_ERR, 1288 gettext("Keytab file \"%s\" does not exist"), 1289 kt_name); 1290 fprintf(stderr, 1291 gettext("%s: Keytab file \"%s\" does not exist.\n"), 1292 whoami, 1293 kt_name); 1294 break; 1295 } 1296 krb5_kt_close(context,kt); 1297 krb5_free_principal(context, princ); 1298 free(service); 1299 return code; 1300 } 1301 1302