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