1 /* 2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 * 5 * All rights reserved. 6 * 7 * Export of this software from the United States of America may require 8 * a specific license from the United States Government. It is the 9 * responsibility of any person or organization contemplating export to 10 * obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of FundsXpress. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. FundsXpress makes no representations about the suitability of 20 * this software for any purpose. It is provided "as is" without express 21 * or implied warranty. 22 * 23 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 25 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 /* 31 * slave/kpropd.c 32 * 33 * Copyright 1990,1991 by the Massachusetts Institute of Technology. 34 * All Rights Reserved. 35 * 36 * Export of this software from the United States of America may 37 * require a specific license from the United States Government. 38 * It is the responsibility of any person or organization contemplating 39 * export to obtain such a license before exporting. 40 * 41 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 42 * distribute this software and its documentation for any purpose and 43 * without fee is hereby granted, provided that the above copyright 44 * notice appear in all copies and that both that copyright notice and 45 * this permission notice appear in supporting documentation, and that 46 * the name of M.I.T. not be used in advertising or publicity pertaining 47 * to distribution of the software without specific, written prior 48 * permission. Furthermore if you modify this software you must label 49 * your software as modified software and not distribute it in such a 50 * fashion that it might be confused with the original M.I.T. software. 51 * M.I.T. makes no representations about the suitability of 52 * this software for any purpose. It is provided "as is" without express 53 * or implied warranty. 54 * 55 * 56 * XXX We need to modify the protocol so that an acknowledge is set 57 * after each block, instead after the entire series is sent over. 58 * The reason for this is so that error packets can get interpreted 59 * right away. If you don't do this, the sender may never get the 60 * error packet, because it will die an EPIPE trying to complete the 61 * write... 62 */ 63 64 65 #include <stdio.h> 66 #include <ctype.h> 67 #include <sys/file.h> 68 #include <signal.h> 69 #include <string.h> 70 #include <fcntl.h> 71 #include <sys/types.h> 72 #include <sys/time.h> 73 #include <sys/stat.h> 74 #include <sys/socket.h> 75 #include <sys/wait.h> 76 #include <netinet/in.h> 77 #include <arpa/inet.h> 78 #include <sys/param.h> 79 #include <netdb.h> 80 #include <syslog.h> 81 #include <libintl.h> 82 #include <locale.h> 83 #include <k5-int.h> 84 #include <socket-utils.h> 85 #include "com_err.h" 86 #include <errno.h> 87 88 #include "kprop.h" 89 #include <iprop_hdr.h> 90 #include "iprop.h" 91 #include <kadm5/admin.h> 92 #include <kdb/kdb_log.h> 93 94 #define SYSLOG_CLASS LOG_DAEMON 95 96 char *poll_time = NULL; 97 char *def_realm = NULL; 98 boolean_t runonce = B_FALSE; 99 100 /* 101 * This struct simulates the use of _kadm5_server_handle_t 102 */ 103 typedef struct _kadm5_iprop_handle_t { 104 krb5_ui_4 magic_number; 105 krb5_ui_4 struct_version; 106 krb5_ui_4 api_version; 107 char *cache_name; 108 int destroy_cache; 109 CLIENT *clnt; 110 krb5_context context; 111 kadm5_config_params params; 112 struct _kadm5_iprop_handle_t *lhandle; 113 } *kadm5_iprop_handle_t; 114 115 static char *kprop_version = KPROP_PROT_VERSION; 116 117 char *progname; 118 int debug = 0; 119 char *srvtab = 0; 120 int standalone = 0; 121 122 krb5_principal server; /* This is our server principal name */ 123 krb5_principal client; /* This is who we're talking to */ 124 krb5_context kpropd_context; 125 krb5_auth_context auth_context; 126 char *realm = NULL; /* Our realm */ 127 char *file = KPROPD_DEFAULT_FILE; 128 char *temp_file_name; 129 char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL; 130 char *kerb_database = NULL; 131 char *acl_file_name = KPROPD_ACL_FILE; 132 133 krb5_address sender_addr; 134 krb5_address receiver_addr; 135 short port = 0; 136 137 void PRS 138 (int, char**); 139 int do_standalone 140 (iprop_role iproprole); 141 void doit 142 (int); 143 krb5_error_code do_iprop(kdb_log_context *log_ctx); 144 145 void kerberos_authenticate 146 (krb5_context, 147 int, 148 krb5_principal *, 149 krb5_enctype *, 150 struct sockaddr_storage); 151 krb5_boolean authorized_principal 152 (krb5_context, 153 krb5_principal, 154 krb5_enctype); 155 void recv_database 156 (krb5_context, 157 int, 158 int, 159 krb5_data *); 160 void load_database 161 (krb5_context, 162 char *, 163 char *); 164 void send_error 165 (krb5_context, 166 int, 167 krb5_error_code, 168 char *); 169 void recv_error 170 (krb5_context, 171 krb5_data *); 172 int convert_polltime 173 (char *); 174 unsigned int backoff_from_master 175 (int *); 176 177 static void usage() 178 { 179 fprintf(stderr, 180 gettext("\nUsage: %s\n"), /* progname may be a long pathname */ 181 progname); 182 183 fprintf(stderr, 184 gettext("\t[-r realm] [-s srvtab] [-dS] [-f slave_file]\n")); 185 186 fprintf(stderr, 187 gettext("\t[-F kerberos_db_file ] [-p kdb5_util_pathname]\n")); 188 189 fprintf(stderr, gettext("\t[-P port] [-a acl_file]\n")); 190 191 exit(1); 192 } 193 194 int 195 main(argc, argv) 196 int argc; 197 char **argv; 198 { 199 krb5_error_code retval; 200 int ret = 0; 201 kdb_log_context *log_ctx; 202 int iprop_supported; 203 204 PRS(argc, argv); 205 206 log_ctx = kpropd_context->kdblog_context; 207 208 if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) { 209 /* 210 * We wanna do iprop ! 211 */ 212 retval = krb5_db_supports_iprop(kpropd_context, 213 &iprop_supported); 214 if (retval) { 215 com_err(progname, retval, 216 gettext("Can not determine if dbmodule plugin " 217 "supports iprop.\n")); 218 exit(1); 219 } 220 if (!iprop_supported) { 221 com_err(progname, retval, 222 gettext("Current dbmodule plugin does not support " 223 "iprop.\n")); 224 exit(1); 225 } 226 227 retval = do_iprop(log_ctx); 228 if (retval) { 229 com_err(progname, retval, 230 gettext("do_iprop failed.\n")); 231 exit(1); 232 } 233 234 } else { 235 if (standalone) 236 ret = do_standalone(IPROP_NULL); 237 else 238 doit(0); 239 } 240 241 exit(ret); 242 } 243 244 int do_standalone(iprop_role iproprole) 245 { 246 struct linger linger; 247 struct servent *sp; 248 int finet, fromlen, s; 249 int on = 1; 250 int ret, status = 0; 251 struct sockaddr_in6 sin6 = { AF_INET6 }; 252 int sin6_size = sizeof (sin6); 253 254 /* listen for either ipv4 or ipv6 */ 255 finet = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 256 if (finet < 0 ) { 257 com_err(progname, errno, gettext("while obtaining socket")); 258 exit(1); 259 } 260 261 if(!port) { 262 sp = getservbyname(KPROP_SERVICE, "tcp"); 263 if (sp == NULL) { 264 com_err(progname, 0, gettext("%s/tcp: unknown service"), 265 KPROP_SERVICE); 266 exit(1); 267 } 268 sin6.sin6_port = sp->s_port; 269 } else 270 sin6.sin6_port = port; 271 272 /* 273 * We need to close the socket immediately if iprop is enabled, 274 * since back-to-back full resyncs are possible, so we do not 275 * linger around for too long 276 */ 277 if (iproprole == IPROP_SLAVE) { 278 if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, 279 (char *)&on, sizeof(on)) < 0) 280 com_err(progname, errno, 281 gettext("in setsockopt(SO_REUSEADDR)")); 282 linger.l_onoff = 1; 283 linger.l_linger = 2; 284 if (setsockopt(finet, SOL_SOCKET, SO_LINGER, 285 (void *)&linger, sizeof(linger)) < 0) 286 com_err(progname, errno, 287 gettext("in setsockopt(SO_LINGER)")); 288 } 289 if ((ret = bind(finet, (struct sockaddr *)&sin6, sizeof(sin6))) < 0) { 290 if (debug) { 291 on = 1; 292 fprintf(stderr, 293 gettext("%s: attempting to rebind socket " 294 "with SO_REUSEADDR\n"), progname); 295 if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, 296 (char *)&on, sizeof(on)) < 0) { 297 com_err(progname, errno, 298 gettext("in setsockopt(SO_REUSEADDR)")); 299 } 300 ret = bind(finet, (struct sockaddr *) &sin6, sizeof(sin6)); 301 } 302 303 if (ret < 0) { 304 perror(gettext("bind")); 305 com_err(progname, errno, 306 gettext("while binding listener socket")); 307 exit(1); 308 } 309 } 310 if (!debug && (iproprole != IPROP_SLAVE)) 311 daemon(1, 0); 312 #ifdef PID_FILE 313 if ((pidfile = fopen(PID_FILE, "w")) != NULL) { 314 fprintf(pidfile, gettext("%d\n"), getpid()); 315 fclose(pidfile); 316 } else 317 com_err(progname, errno, 318 gettext("while opening pid file %s for writing"), 319 PID_FILE); 320 #endif 321 if (listen(finet, 5) < 0) { 322 com_err(progname, errno, gettext("in listen call")); 323 exit(1); 324 } 325 while (1) { 326 int child_pid; 327 328 s = accept(finet, (struct sockaddr *) &sin6, &sin6_size); 329 330 if (s < 0) { 331 if (errno != EINTR) 332 com_err(progname, errno, 333 gettext("from accept system call")); 334 continue; 335 } 336 if (debug && (iproprole != IPROP_SLAVE)) 337 child_pid = 0; 338 else 339 child_pid = fork(); 340 switch (child_pid) { 341 case -1: 342 com_err(progname, errno, gettext("while forking")); 343 exit(1); 344 /*NOTREACHED*/ 345 case 0: 346 /* child */ 347 (void) close(finet); 348 349 doit(s); 350 close(s); 351 _exit(0); 352 /*NOTREACHED*/ 353 default: 354 /* parent */ 355 if (wait(&status) < 0) { 356 com_err(progname, errno, 357 gettext("while waiting to receive database")); 358 exit(1); 359 } 360 361 close(s); 362 if (iproprole == IPROP_SLAVE) 363 close(finet); 364 365 if ((ret = WEXITSTATUS(status)) != 0) 366 return (ret); 367 } 368 369 if (iproprole == IPROP_SLAVE) 370 break; 371 } 372 373 return (0); 374 } 375 376 void doit(fd) 377 int fd; 378 { 379 struct sockaddr_storage from; 380 socklen_t fromlen; 381 int on = 1; 382 struct hostent *hp; 383 krb5_error_code retval; 384 krb5_data confmsg; 385 int lock_fd; 386 mode_t omask; 387 krb5_enctype etype; 388 int database_fd; 389 char ntop[NI_MAXHOST] = ""; 390 krb5_context doit_context; 391 kdb_log_context *log_ctx; 392 393 retval = krb5_init_context(&doit_context); 394 if (retval) { 395 com_err(progname, retval, gettext("while initializing krb5")); 396 exit(1); 397 } 398 log_ctx = kpropd_context->kdblog_context; 399 if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) 400 ulog_set_role(doit_context, IPROP_SLAVE); 401 402 fromlen = (socklen_t)sizeof (from); 403 if (getpeername(fd, (struct sockaddr *) &from, &fromlen) < 0) { 404 fprintf(stderr, "%s: ", progname); 405 perror(gettext("getpeername")); 406 exit(1); 407 } 408 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (caddr_t) &on, 409 sizeof (on)) < 0) { 410 com_err(progname, errno, 411 gettext("while attempting setsockopt (SO_KEEPALIVE)")); 412 } 413 414 if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), 415 NULL, 0, NI_NUMERICHOST) != 0) { 416 417 /* getnameifo failed so use inet_ntop() to get printable addresses */ 418 if (from.ss_family == AF_INET) { 419 420 inet_ntop(AF_INET, 421 (const void *)&ss2sin(&from)->sin_addr, 422 ntop, sizeof(ntop)); 423 424 } else if (from.ss_family == AF_INET6 && 425 ! IN6_IS_ADDR_V4MAPPED(&ss2sin6(&from)->sin6_addr)) { 426 427 ipaddr_t v4addr; 428 429 inet_ntop(AF_INET6, 430 (const void *)&ss2sin6(&from)->sin6_addr, ntop, 431 sizeof(ntop)); 432 } 433 /* ipv4 mapped ipv6 addrs handled later */ 434 } 435 436 if (from.ss_family == AF_INET || from.ss_family == AF_INET6) { 437 438 if (from.ss_family == AF_INET6 && 439 IN6_IS_ADDR_V4MAPPED(&ss2sin6(&from)->sin6_addr)) { 440 441 ipaddr_t v4addr; 442 443 /* coerce ipv4 mapped ipv6 addr to normal ipv4 addr */ 444 IN6_V4MAPPED_TO_IPADDR(&(ss2sin6(&from)->sin6_addr), 445 v4addr); 446 447 inet_ntop(AF_INET, (const void *) &v4addr, 448 ntop, sizeof(ntop)); 449 } 450 451 syslog(LOG_INFO, gettext("Connection from %s"), ntop); 452 453 if (debug) 454 printf("Connection from %s\n", ntop); 455 456 } else { 457 /* address family isn't either AF_INET || AF_INET6 */ 458 syslog(LOG_INFO, 459 gettext("Connection from unknown address family:%d"), 460 from.ss_family); 461 462 if (debug) { 463 printf(gettext("Connection from unknown address family:%d"), 464 from.ss_family); 465 } 466 } 467 468 /* 469 * Now do the authentication 470 */ 471 kerberos_authenticate(doit_context, fd, &client, &etype, from); 472 473 if (!authorized_principal(doit_context, client, etype)) { 474 char *name; 475 476 retval = krb5_unparse_name(doit_context, client, &name); 477 if (retval) { 478 com_err(progname, retval, 479 gettext("While unparsing client name")); 480 exit(1); 481 } 482 syslog(LOG_WARNING, 483 gettext("Rejected connection from unauthorized principal %s"), 484 name); 485 free(name); 486 exit(1); 487 } 488 omask = umask(077); 489 lock_fd = open(temp_file_name, O_RDWR|O_CREAT, 0600); 490 (void) umask(omask); 491 retval = krb5_lock_file(doit_context, lock_fd, 492 KRB5_LOCKMODE_EXCLUSIVE|KRB5_LOCKMODE_DONTBLOCK); 493 if (retval) { 494 com_err(progname, retval, 495 gettext("while trying to lock '%s'"), 496 temp_file_name); 497 exit(1); 498 } 499 if ((database_fd = open(temp_file_name, 500 O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { 501 com_err(progname, errno, 502 gettext("while opening database file, '%s'"), 503 temp_file_name); 504 exit(1); 505 } 506 recv_database(doit_context, fd, database_fd, &confmsg); 507 if (rename(temp_file_name, file)) { 508 com_err(progname, errno, 509 gettext("While renaming %s to %s"), 510 temp_file_name, file); 511 exit(1); 512 } 513 retval = krb5_lock_file(doit_context, lock_fd, KRB5_LOCKMODE_SHARED); 514 if (retval) { 515 com_err(progname, retval, 516 gettext("while downgrading lock on '%s'"), 517 temp_file_name); 518 exit(1); 519 } 520 load_database(doit_context, kdb5_util, file); 521 retval = krb5_lock_file(doit_context, lock_fd, KRB5_LOCKMODE_UNLOCK); 522 if (retval) { 523 com_err(progname, retval, 524 gettext("while unlocking '%s'"), temp_file_name); 525 exit(1); 526 } 527 (void)close(lock_fd); 528 529 /* 530 * Send the acknowledgement message generated in 531 * recv_database, then close the socket. 532 */ 533 retval = krb5_write_message(doit_context, (void *) &fd, &confmsg); 534 if (retval) { 535 krb5_free_data_contents(doit_context, &confmsg); 536 com_err(progname, retval, 537 gettext("while sending # of received bytes")); 538 exit(1); 539 } 540 krb5_free_data_contents(doit_context, &confmsg); 541 if (close(fd) < 0) { 542 com_err(progname, errno, 543 gettext("while trying to close database file")); 544 exit(1); 545 } 546 547 exit(0); 548 } 549 550 551 /* 552 * Routine to handle incremental update transfer(s) from master KDC 553 */ 554 krb5_error_code do_iprop(kdb_log_context *log_ctx) { 555 CLIENT *cl; 556 kadm5_ret_t retval; 557 kadm5_config_params params; 558 krb5_ccache cc; 559 krb5_principal iprop_svc_principal; 560 void *server_handle = NULL; 561 char *iprop_svc_princstr = NULL; 562 char *master_svc_princstr = NULL; 563 char *admin_server = NULL; 564 char *keytab_name = NULL; 565 unsigned int pollin, backoff_time; 566 int backoff_cnt = 0; 567 int reinit_cnt = 0; 568 int ret; 569 boolean_t frdone = B_FALSE; 570 571 kdb_incr_result_t *incr_ret; 572 static kdb_last_t mylast; 573 574 kdb_fullresync_result_t *full_ret; 575 char *full_resync_arg = NULL; 576 577 kadm5_iprop_handle_t handle; 578 kdb_hlog_t *ulog; 579 580 if (!debug) 581 daemon(0, 0); 582 583 pollin = (unsigned int)0; 584 (void) memset((char *)¶ms, 0, sizeof (params)); 585 ulog = log_ctx->ulog; 586 587 params.mask |= KADM5_CONFIG_REALM; 588 params.realm = def_realm; 589 590 if (master_svc_princstr == NULL) { 591 if (retval = kadm5_get_kiprop_host_srv_name(kpropd_context, 592 def_realm, &master_svc_princstr)) { 593 com_err(progname, retval, 594 gettext("%s: unable to get kiprop host based " 595 "service name for realm %s\n"), 596 progname, def_realm); 597 exit(1); 598 } 599 } 600 601 /* 602 * Set cc to the default credentials cache 603 */ 604 if (retval = krb5_cc_default(kpropd_context, &cc)) { 605 com_err(progname, retval, 606 gettext("while opening default " 607 "credentials cache")); 608 exit(1); 609 } 610 611 retval = krb5_sname_to_principal(kpropd_context, NULL, KIPROP_SVC_NAME, 612 KRB5_NT_SRV_HST, &iprop_svc_principal); 613 if (retval) { 614 com_err(progname, retval, gettext("while trying to construct " 615 "host service principal")); 616 exit(1); 617 } 618 619 if (retval = krb5_unparse_name(kpropd_context, iprop_svc_principal, 620 &iprop_svc_princstr)) { 621 com_err(progname, retval, 622 gettext("while canonicalizing " 623 "principal name")); 624 krb5_free_principal(kpropd_context, iprop_svc_principal); 625 exit(1); 626 } 627 krb5_free_principal(kpropd_context, iprop_svc_principal); 628 629 reinit: 630 /* 631 * Authentication, initialize rpcsec_gss handle etc. 632 */ 633 retval = kadm5_init_with_skey(iprop_svc_princstr, keytab_name, 634 master_svc_princstr, 635 ¶ms, 636 KADM5_STRUCT_VERSION, 637 KADM5_API_VERSION_2, 638 NULL, 639 &server_handle); 640 641 if (retval) { 642 if (retval == KADM5_RPC_ERROR) { 643 reinit_cnt++; 644 if (server_handle) 645 kadm5_destroy((void *) server_handle); 646 server_handle = (void *)NULL; 647 handle = (kadm5_iprop_handle_t)NULL; 648 649 com_err(progname, retval, gettext( 650 "while attempting to connect" 651 " to master KDC ... retrying")); 652 backoff_time = backoff_from_master(&reinit_cnt); 653 (void) sleep(backoff_time); 654 goto reinit; 655 } else { 656 com_err(progname, retval, 657 gettext("while initializing %s interface"), 658 progname); 659 if (retval == KADM5_BAD_CLIENT_PARAMS || 660 retval == KADM5_BAD_SERVER_PARAMS) 661 usage(); 662 exit(1); 663 } 664 } 665 666 /* 667 * Reset re-initialization count to zero now. 668 */ 669 reinit_cnt = backoff_time = 0; 670 671 /* 672 * Reset the handle to the correct type for the RPC call 673 */ 674 handle = server_handle; 675 676 /* 677 * If we have reached this far, we have succesfully established 678 * a RPCSEC_GSS connection; we now start polling for updates 679 */ 680 if (poll_time == NULL) { 681 if ((poll_time = (char *)strdup("2m")) == NULL) { 682 com_err(progname, ENOMEM, 683 gettext("Unable to allocate poll_time")); 684 exit(1); 685 } 686 } 687 688 if (pollin == (unsigned int)0) 689 pollin = convert_polltime(poll_time); 690 691 for (;;) { 692 incr_ret = NULL; 693 full_ret = NULL; 694 695 /* 696 * Get the most recent ulog entry sno + ts, which 697 * we package in the request to the master KDC 698 */ 699 mylast.last_sno = ulog->kdb_last_sno; 700 mylast.last_time = ulog->kdb_last_time; 701 702 /* 703 * Loop continuously on an iprop_get_updates_1(), 704 * so that we can keep probing the master for updates 705 * or (if needed) do a full resync of the krb5 db. 706 */ 707 708 incr_ret = iprop_get_updates_1(&mylast, handle->clnt); 709 if (incr_ret == (kdb_incr_result_t *)NULL) { 710 clnt_perror(handle->clnt, 711 "iprop_get_updates call failed"); 712 if (server_handle) 713 kadm5_destroy((void *)server_handle); 714 server_handle = (void *)NULL; 715 handle = (kadm5_iprop_handle_t)NULL; 716 goto reinit; 717 } 718 719 switch (incr_ret->ret) { 720 721 case UPDATE_FULL_RESYNC_NEEDED: 722 /* 723 * We dont do a full resync again, if the last 724 * X'fer was a resync and if the master sno is 725 * still "0", i.e. no updates so far. 726 */ 727 if ((frdone == B_TRUE) && (incr_ret->lastentry.last_sno 728 == 0)) { 729 break; 730 } else { 731 732 full_ret = iprop_full_resync_1((void *) 733 &full_resync_arg, handle->clnt); 734 735 if (full_ret == (kdb_fullresync_result_t *) 736 NULL) { 737 clnt_perror(handle->clnt, 738 "iprop_full_resync call failed"); 739 if (server_handle) 740 kadm5_destroy((void *) 741 server_handle); 742 server_handle = (void *)NULL; 743 handle = (kadm5_iprop_handle_t)NULL; 744 goto reinit; 745 } 746 } 747 748 switch (full_ret->ret) { 749 case UPDATE_OK: 750 backoff_cnt = 0; 751 /* 752 * We now listen on the kprop port for 753 * the full dump 754 */ 755 ret = do_standalone(log_ctx->iproprole); 756 if (ret) 757 syslog(LOG_WARNING, 758 gettext("kpropd: Full resync, " 759 "invalid return.")); 760 if (debug) 761 if (ret) 762 fprintf(stderr, 763 gettext("Full resync " 764 "was unsuccessful\n")); 765 else 766 fprintf(stderr, 767 gettext("Full resync " 768 "was successful\n")); 769 frdone = B_TRUE; 770 break; 771 772 case UPDATE_BUSY: 773 /* 774 * Exponential backoff 775 */ 776 backoff_cnt++; 777 break; 778 779 case UPDATE_FULL_RESYNC_NEEDED: 780 case UPDATE_NIL: 781 default: 782 backoff_cnt = 0; 783 frdone = B_FALSE; 784 syslog(LOG_ERR, gettext("kpropd: Full resync," 785 " invalid return from master KDC.")); 786 break; 787 788 case UPDATE_PERM_DENIED: 789 syslog(LOG_ERR, gettext("kpropd: Full resync," 790 " permission denied.")); 791 goto error; 792 793 case UPDATE_ERROR: 794 syslog(LOG_ERR, gettext("kpropd: Full resync," 795 " error returned from master KDC.")); 796 goto error; 797 } 798 break; 799 800 case UPDATE_OK: 801 backoff_cnt = 0; 802 frdone = B_FALSE; 803 804 /* 805 * ulog_replay() will convert the ulog updates to db 806 * entries using the kdb conv api and will commit 807 * the entries to the slave kdc database 808 */ 809 retval = ulog_replay(kpropd_context, incr_ret); 810 811 if (retval) { 812 syslog(LOG_ERR, gettext("kpropd: ulog_replay" 813 " failed, updates not registered.")); 814 break; 815 } 816 817 if (debug) 818 fprintf(stderr, gettext("Update transfer " 819 "from master was OK\n")); 820 break; 821 822 case UPDATE_PERM_DENIED: 823 syslog(LOG_ERR, gettext("kpropd: get_updates," 824 " permission denied.")); 825 goto error; 826 827 case UPDATE_ERROR: 828 syslog(LOG_ERR, gettext("kpropd: get_updates, error " 829 "returned from master KDC.")); 830 goto error; 831 832 case UPDATE_BUSY: 833 /* 834 * Exponential backoff 835 */ 836 backoff_cnt++; 837 break; 838 839 case UPDATE_NIL: 840 /* 841 * Master-slave are in sync 842 */ 843 if (debug) 844 fprintf(stderr, gettext("Master, slave KDC's " 845 "are in-sync, no updates\n")); 846 backoff_cnt = 0; 847 frdone = B_FALSE; 848 break; 849 850 default: 851 backoff_cnt = 0; 852 syslog(LOG_ERR, gettext("kpropd: get_updates," 853 " invalid return from master KDC.")); 854 break; 855 } 856 857 if (runonce == B_TRUE) 858 goto done; 859 860 /* 861 * Sleep for the specified poll interval (Default is 2 mts), 862 * or do a binary exponential backoff if we get an 863 * UPDATE_BUSY signal 864 */ 865 if (backoff_cnt > 0) { 866 backoff_time = backoff_from_master(&backoff_cnt); 867 if (debug) 868 fprintf(stderr, gettext("Busy signal received " 869 "from master, backoff for %d secs\n"), 870 backoff_time); 871 (void) sleep(backoff_time); 872 } 873 else 874 (void) sleep(pollin); 875 876 } 877 878 879 error: 880 if (debug) 881 fprintf(stderr, gettext("ERROR returned by master, bailing\n")); 882 syslog(LOG_ERR, gettext("kpropd: ERROR returned by master KDC," 883 " bailing.\n")); 884 done: 885 if (poll_time) 886 free(poll_time); 887 if(iprop_svc_princstr) 888 free(iprop_svc_princstr); 889 if (master_svc_princstr) 890 free(master_svc_princstr); 891 if (retval = krb5_cc_close(kpropd_context, cc)) { 892 com_err(progname, retval, 893 gettext("while closing default ccache")); 894 exit(1); 895 } 896 if (def_realm) 897 free(def_realm); 898 if (server_handle) 899 kadm5_destroy((void *)server_handle); 900 if (kpropd_context) 901 krb5_free_context(kpropd_context); 902 903 if (runonce == B_TRUE) 904 return (0); 905 else 906 exit(1); 907 } 908 909 910 /* 911 * Do exponential backoff, since master KDC is BUSY or down 912 */ 913 unsigned int backoff_from_master(int *cnt) { 914 unsigned int btime; 915 916 btime = (unsigned int)(2<<(*cnt)); 917 if (btime > MAX_BACKOFF) { 918 btime = MAX_BACKOFF; 919 *cnt--; 920 } 921 922 return (btime); 923 } 924 925 926 /* 927 * Routine to convert the `pollstr' string to seconds 928 */ 929 int convert_polltime(char *pollstr) { 930 char *tokenptr = NULL; 931 int len, polltime; 932 933 len = polltime = 0; 934 935 if ((len = strcspn(pollstr, "s")) < strlen(pollstr)) { 936 tokenptr = malloc((len + 1) * sizeof(char)); 937 (void) strlcpy(tokenptr, pollstr, len + 1); 938 polltime = atoi(tokenptr); 939 } 940 941 if ((len = strcspn(pollstr, "m")) < strlen(pollstr)) { 942 tokenptr = malloc((len + 1) * sizeof(char)); 943 (void) strlcpy(tokenptr, pollstr, len + 1); 944 polltime = atoi(tokenptr) * 60; 945 } 946 947 if ((len = strcspn(pollstr, "h")) < strlen(pollstr)) { 948 tokenptr = malloc((len + 1) * sizeof(char)); 949 (void) strlcpy(tokenptr, pollstr, len + 1); 950 polltime = atoi(tokenptr) * 3600; 951 } 952 953 if (tokenptr != NULL) 954 free(tokenptr); 955 /* 956 * If we have a bogus pollstr value, set polltime to the 957 * default of 2 mts (120 seconds). 958 */ 959 if (polltime == 0) 960 polltime = 120; 961 return (polltime); 962 } 963 964 static void 965 kpropd_com_err_proc(whoami, code, fmt, args) 966 const char *whoami; 967 long code; 968 const char *fmt; 969 va_list args; 970 { 971 char error_buf[8096]; 972 973 error_buf[0] = '\0'; 974 if (fmt) 975 vsprintf(error_buf, fmt, args); 976 syslog(LOG_ERR, "%s%s%s%s%s", whoami ? whoami : "", whoami ? ": " : "", 977 code ? error_message(code) : "", code ? " " : "", error_buf); 978 } 979 980 void PRS(argc,argv) 981 int argc; 982 char **argv; 983 { 984 register char *word, ch; 985 char *cp; 986 int c; 987 struct hostent *hp; 988 char my_host_name[MAXHOSTNAMELEN], buf[BUFSIZ]; 989 krb5_error_code retval; 990 static const char tmp[] = ".temp"; 991 kadm5_config_params params; 992 993 (void) setlocale(LC_ALL, ""); 994 995 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 996 #define TEXT_DOMAIN "KPROPD_TEST" /* Use this only if it weren't */ 997 #endif 998 999 (void) textdomain(TEXT_DOMAIN); 1000 1001 (void) memset((char *) ¶ms, 0, sizeof (params)); 1002 1003 retval = krb5_init_context(&kpropd_context); 1004 if (retval) { 1005 com_err(argv[0], retval, 1006 gettext("while initializing krb5")); 1007 exit(1); 1008 } 1009 progname = argv[0]; 1010 while ((c = getopt(argc, argv, "dtf:F:p:P:r:s:Sa:")) != EOF){ 1011 switch (c) { 1012 case 'd': 1013 debug++; 1014 break; 1015 case 't': 1016 /* 1017 * Undocumented option - for testing only. 1018 * 1019 * Option to run the kpropd server exactly 1020 * once (this is true only if iprop is enabled). 1021 */ 1022 runonce = B_TRUE; 1023 break; 1024 1025 case 'f': 1026 file = optarg; 1027 if (!file) 1028 usage(); 1029 break; 1030 case 'F': 1031 kerb_database = optarg; 1032 if (!kerb_database) 1033 usage(); 1034 break; 1035 case 'p': 1036 kdb5_util = optarg; 1037 if (!kdb5_util) 1038 usage(); 1039 break; 1040 case 'P': 1041 port = htons(atoi(optarg)); 1042 if (!port) 1043 usage(); 1044 break; 1045 case 'r': 1046 realm = optarg; 1047 if (!realm) 1048 usage(); 1049 params.realm = realm; 1050 params.mask |= KADM5_CONFIG_REALM; 1051 break; 1052 case 's': 1053 srvtab = optarg; 1054 if (!srvtab) 1055 usage(); 1056 break; 1057 case 'S': 1058 standalone++; 1059 break; 1060 case 'a': 1061 acl_file_name = optarg; 1062 if (!acl_file_name) 1063 usage(); 1064 break; 1065 case '?': 1066 default: 1067 usage(); 1068 } 1069 1070 } 1071 /* 1072 * If not in debug mode, switch com_err reporting to syslog 1073 */ 1074 if (! debug) { 1075 openlog("kpropd", LOG_PID | LOG_ODELAY, SYSLOG_CLASS); 1076 set_com_err_hook(kpropd_com_err_proc); 1077 } 1078 /* 1079 * Get my hostname, so we can construct my service name 1080 */ 1081 retval = krb5_sname_to_principal(kpropd_context, 1082 NULL, KPROP_SERVICE_NAME, 1083 KRB5_NT_SRV_HST, &server); 1084 if (retval) { 1085 com_err(progname, retval, 1086 gettext("While trying to construct my service name")); 1087 exit(1); 1088 } 1089 if (realm) { 1090 retval = krb5_set_principal_realm(kpropd_context, server, realm); 1091 if (retval) { 1092 com_err(progname, errno, 1093 gettext("while constructing my service realm")); 1094 exit(1); 1095 } 1096 } 1097 /* 1098 * Construct the name of the temporary file. 1099 */ 1100 if ((temp_file_name = (char *) malloc(strlen(file) + 1101 strlen(tmp) + 1)) == NULL) { 1102 com_err(progname, ENOMEM, 1103 gettext("while allocating filename for temp file")); 1104 exit(1); 1105 } 1106 strcpy(temp_file_name, file); 1107 strcat(temp_file_name, tmp); 1108 1109 retval = kadm5_get_config_params(kpropd_context, NULL, NULL, ¶ms, 1110 ¶ms); 1111 if (retval) { 1112 com_err(progname, retval, gettext("while initializing")); 1113 exit(1); 1114 } 1115 if (params.iprop_enabled == TRUE) { 1116 ulog_set_role(kpropd_context, IPROP_SLAVE); 1117 poll_time = params.iprop_polltime; 1118 1119 if (ulog_map(kpropd_context, ¶ms, FKPROPD)) { 1120 com_err(progname, errno, 1121 gettext("Unable to map log!\n")); 1122 exit(1); 1123 } 1124 } 1125 1126 /* 1127 * Grab the realm info and check if iprop is enabled. 1128 */ 1129 if (def_realm == NULL) { 1130 retval = krb5_get_default_realm(kpropd_context, &def_realm); 1131 if (retval) { 1132 com_err(progname, retval, 1133 gettext("Unable to get default realm")); 1134 exit(1); 1135 } 1136 } 1137 } 1138 1139 /* 1140 * Figure out who's calling on the other end of the connection.... 1141 */ 1142 void 1143 kerberos_authenticate(context, fd, clientp, etype, ss) 1144 krb5_context context; 1145 int fd; 1146 krb5_principal * clientp; 1147 krb5_enctype * etype; 1148 struct sockaddr_storage ss; 1149 { 1150 krb5_error_code retval; 1151 krb5_ticket * ticket; 1152 struct sockaddr_storage r_ss; 1153 int ss_length; 1154 krb5_keytab keytab = NULL; 1155 1156 /* 1157 * Set recv_addr and send_addr 1158 */ 1159 if (cvtkaddr(&ss, &sender_addr) == NULL) { 1160 com_err(progname, errno, 1161 gettext("while converting socket address")); 1162 exit(1); 1163 } 1164 1165 ss_length = sizeof (r_ss); 1166 if (getsockname(fd, (struct sockaddr *) &r_ss, &ss_length)) { 1167 com_err(progname, errno, 1168 gettext("while getting local socket address")); 1169 exit(1); 1170 } 1171 1172 if (cvtkaddr(&r_ss, &receiver_addr) == NULL) { 1173 com_err(progname, errno, 1174 gettext("while converting socket address")); 1175 exit(1); 1176 } 1177 1178 if (debug) { 1179 char *name; 1180 1181 retval = krb5_unparse_name(context, server, &name); 1182 if (retval) { 1183 com_err(progname, retval, gettext("While unparsing server name")); 1184 exit(1); 1185 } 1186 printf(gettext("krb5_recvauth(%d, %s, %s, ...)\n"), fd, kprop_version, 1187 name); 1188 free(name); 1189 } 1190 1191 retval = krb5_auth_con_init(context, &auth_context); 1192 if (retval) { 1193 syslog(LOG_ERR, gettext("Error in krb5_auth_con_init: %s"), 1194 error_message(retval)); 1195 exit(1); 1196 } 1197 1198 retval = krb5_auth_con_setflags(context, auth_context, 1199 KRB5_AUTH_CONTEXT_DO_SEQUENCE); 1200 if (retval) { 1201 syslog(LOG_ERR, gettext("Error in krb5_auth_con_setflags: %s"), 1202 error_message(retval)); 1203 exit(1); 1204 } 1205 1206 retval = krb5_auth_con_setaddrs(context, auth_context, &receiver_addr, 1207 &sender_addr); 1208 if (retval) { 1209 syslog(LOG_ERR, gettext("Error in krb5_auth_con_setaddrs: %s"), 1210 error_message(retval)); 1211 exit(1); 1212 } 1213 1214 if (srvtab) { 1215 retval = krb5_kt_resolve(context, srvtab, &keytab); 1216 if (retval) { 1217 syslog(LOG_ERR, gettext("Error in krb5_kt_resolve: %s"), error_message(retval)); 1218 exit(1); 1219 } 1220 } 1221 1222 retval = krb5_recvauth(context, &auth_context, (void *) &fd, 1223 kprop_version, server, 0, keytab, &ticket); 1224 if (retval) { 1225 syslog(LOG_ERR, gettext("Error in krb5_recvauth: %s"), error_message(retval)); 1226 exit(1); 1227 } 1228 1229 retval = krb5_copy_principal(context, ticket->enc_part2->client, clientp); 1230 if (retval) { 1231 syslog(LOG_ERR, gettext("Error in krb5_copy_prinicpal: %s"), 1232 error_message(retval)); 1233 exit(1); 1234 } 1235 1236 *etype = ticket->enc_part.enctype; 1237 1238 if (debug) { 1239 char * name; 1240 char etypebuf[100]; 1241 1242 retval = krb5_unparse_name(context, *clientp, &name); 1243 if (retval) { 1244 com_err(progname, retval, 1245 gettext("While unparsing client name")); 1246 exit(1); 1247 } 1248 1249 retval = krb5_enctype_to_string(*etype, etypebuf, sizeof(etypebuf)); 1250 if (retval) { 1251 com_err(progname, retval, gettext("While unparsing ticket etype")); 1252 exit(1); 1253 } 1254 1255 printf("authenticated client: %s (etype == %s)\n", name, etypebuf); 1256 free(name); 1257 } 1258 1259 krb5_free_ticket(context, ticket); 1260 } 1261 1262 krb5_boolean 1263 authorized_principal(context, p, auth_etype) 1264 krb5_context context; 1265 krb5_principal p; 1266 krb5_enctype auth_etype; 1267 { 1268 char *name, *ptr; 1269 char buf[1024]; 1270 krb5_error_code retval; 1271 FILE *acl_file; 1272 int end; 1273 krb5_enctype acl_etype; 1274 1275 retval = krb5_unparse_name(context, p, &name); 1276 if (retval) 1277 return FALSE; 1278 1279 acl_file = fopen(acl_file_name, "r"); 1280 if (!acl_file) 1281 return FALSE; 1282 1283 while (!feof(acl_file)) { 1284 if (!fgets(buf, sizeof(buf), acl_file)) 1285 break; 1286 end = strlen(buf) - 1; 1287 if (buf[end] == '\n') 1288 buf[end] = '\0'; 1289 if (!strncmp(name, buf, strlen(name))) { 1290 ptr = buf+strlen(name); 1291 1292 /* if the next character is not whitespace or nul, then 1293 the match is only partial. continue on to new lines. */ 1294 if (*ptr && !isspace((int) *ptr)) 1295 continue; 1296 1297 /* otherwise, skip trailing whitespace */ 1298 for (; *ptr && isspace((int) *ptr); ptr++) ; 1299 1300 /* now, look for an etype string. if there isn't one, 1301 return true. if there is an invalid string, continue. 1302 If there is a valid string, return true only if it 1303 matches the etype passed in, otherwise continue */ 1304 1305 if ((*ptr) && 1306 ((retval = krb5_string_to_enctype(ptr, &acl_etype)) || 1307 (acl_etype != auth_etype))) 1308 continue; 1309 1310 free(name); 1311 fclose(acl_file); 1312 return TRUE; 1313 } 1314 } 1315 free(name); 1316 fclose(acl_file); 1317 return FALSE; 1318 } 1319 1320 void 1321 recv_database(context, fd, database_fd, confmsg) 1322 krb5_context context; 1323 int fd; 1324 int database_fd; 1325 krb5_data *confmsg; 1326 { 1327 krb5_ui_4 database_size; /* This must be 4 bytes */ 1328 int received_size, n; 1329 char buf[1024]; 1330 krb5_data inbuf, outbuf; 1331 krb5_error_code retval; 1332 1333 /* 1334 * Receive and decode size from client 1335 */ 1336 retval = krb5_read_message(context, (void *) &fd, &inbuf); 1337 if (retval) { 1338 send_error(context, fd, retval, gettext("while reading database size")); 1339 com_err(progname, retval, 1340 gettext("while reading size of database from client")); 1341 exit(1); 1342 } 1343 if (krb5_is_krb_error(&inbuf)) 1344 recv_error(context, &inbuf); 1345 retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL); 1346 if (retval) { 1347 send_error(context, fd, retval, gettext( 1348 "while decoding database size")); 1349 krb5_free_data_contents(context, &inbuf); 1350 com_err(progname, retval, 1351 gettext("while decoding database size from client")); 1352 exit(1); 1353 } 1354 memcpy((char *) &database_size, outbuf.data, sizeof(database_size)); 1355 krb5_free_data_contents(context, &inbuf); 1356 krb5_free_data_contents(context, &outbuf); 1357 database_size = ntohl(database_size); 1358 1359 /* 1360 * Initialize the initial vector. 1361 */ 1362 retval = krb5_auth_con_initivector(context, auth_context); 1363 if (retval) { 1364 send_error(context, fd, retval, gettext( 1365 "failed while initializing i_vector")); 1366 com_err(progname, retval, gettext("while initializing i_vector")); 1367 exit(1); 1368 } 1369 1370 /* 1371 * Now start receiving the database from the net 1372 */ 1373 received_size = 0; 1374 while (received_size < database_size) { 1375 retval = krb5_read_message(context, (void *) &fd, &inbuf); 1376 if (retval) { 1377 snprintf(buf, sizeof (buf), 1378 gettext("while reading database block starting at offset %d"), 1379 received_size); 1380 com_err(progname, retval, buf); 1381 send_error(context, fd, retval, buf); 1382 exit(1); 1383 } 1384 if (krb5_is_krb_error(&inbuf)) 1385 recv_error(context, &inbuf); 1386 retval = krb5_rd_priv(context, auth_context, &inbuf, 1387 &outbuf, NULL); 1388 if (retval) { 1389 snprintf(buf, sizeof (buf), 1390 gettext("while decoding database block starting at offset %d"), 1391 received_size); 1392 com_err(progname, retval, buf); 1393 send_error(context, fd, retval, buf); 1394 krb5_free_data_contents(context, &inbuf); 1395 exit(1); 1396 } 1397 n = write(database_fd, outbuf.data, outbuf.length); 1398 if (n < 0) { 1399 snprintf(buf, sizeof (buf), 1400 gettext( 1401 "while writing database block starting at offset %d"), 1402 received_size); 1403 send_error(context, fd, errno, buf); 1404 } else if (n != outbuf.length) { 1405 snprintf(buf, sizeof (buf), 1406 gettext( 1407 "incomplete write while writing database block starting at\n" 1408 "offset %d (%d written, %d expected)"), 1409 received_size, n, outbuf.length); 1410 send_error(context, fd, KRB5KRB_ERR_GENERIC, buf); 1411 } 1412 received_size += outbuf.length; 1413 /* SUNWresync121: our krb5...contents sets length to 0 */ 1414 krb5_free_data_contents(context, &inbuf); 1415 krb5_free_data_contents(context, &outbuf); 1416 1417 } 1418 /* 1419 * OK, we've seen the entire file. Did we get too many bytes? 1420 */ 1421 if (received_size > database_size) { 1422 snprintf(buf, sizeof (buf), 1423 gettext("Received %d bytes, expected %d bytes for database file"), 1424 received_size, database_size); 1425 send_error(context, fd, KRB5KRB_ERR_GENERIC, buf); 1426 } 1427 /* 1428 * Create message acknowledging number of bytes received, but 1429 * don't send it until kdb5_util returns successfully. 1430 */ 1431 database_size = htonl(database_size); 1432 inbuf.data = (char *) &database_size; 1433 inbuf.length = sizeof(database_size); 1434 retval = krb5_mk_safe(context,auth_context,&inbuf,confmsg,NULL); 1435 if (retval) { 1436 com_err(progname, retval, 1437 gettext("while encoding # of receieved bytes")); 1438 send_error(context, fd, retval, 1439 gettext("while encoding # of received bytes")); 1440 exit(1); 1441 } 1442 } 1443 1444 1445 void 1446 send_error(context, fd, err_code, err_text) 1447 krb5_context context; 1448 int fd; 1449 krb5_error_code err_code; 1450 char *err_text; 1451 { 1452 krb5_error error; 1453 const char *text; 1454 krb5_data outbuf; 1455 char buf[1024]; 1456 1457 memset((char *)&error, 0, sizeof(error)); 1458 krb5_us_timeofday(context, &error.stime, &error.susec); 1459 error.server = server; 1460 error.client = client; 1461 1462 if (err_text) 1463 text = err_text; 1464 else 1465 text = error_message(err_code); 1466 1467 error.error = err_code - ERROR_TABLE_BASE_krb5; 1468 if (error.error > 127) { 1469 error.error = KRB_ERR_GENERIC; 1470 if (err_text) { 1471 sprintf(buf, "%s %s", error_message(err_code), 1472 err_text); 1473 text = buf; 1474 } 1475 } 1476 error.text.length = strlen(text) + 1; 1477 error.text.data = malloc(error.text.length); 1478 if (error.text.data) { 1479 strcpy(error.text.data, text); 1480 if (!krb5_mk_error(context, &error, &outbuf)) { 1481 (void) krb5_write_message(context, (void *)&fd,&outbuf); 1482 krb5_free_data_contents(context, &outbuf); 1483 } 1484 free(error.text.data); 1485 } 1486 } 1487 1488 void 1489 recv_error(context, inbuf) 1490 krb5_context context; 1491 krb5_data *inbuf; 1492 { 1493 krb5_error *error; 1494 krb5_error_code retval; 1495 1496 retval = krb5_rd_error(context, inbuf, &error); 1497 if (retval) { 1498 com_err(progname, retval, 1499 gettext("while decoding error packet from client")); 1500 exit(1); 1501 } 1502 if (error->error == KRB_ERR_GENERIC) { 1503 if (error->text.data) 1504 fprintf(stderr, 1505 gettext("Generic remote error: %s\n"), 1506 error->text.data); 1507 } else if (error->error) { 1508 com_err(progname, error->error + ERROR_TABLE_BASE_krb5, 1509 gettext("signalled from server")); 1510 if (error->text.data) 1511 fprintf(stderr, 1512 gettext("Error text from client: %s\n"), 1513 error->text.data); 1514 } 1515 krb5_free_error(context, error); 1516 exit(1); 1517 } 1518 1519 void 1520 load_database(context, kdb_util, database_file_name) 1521 krb5_context context; 1522 char *kdb_util; 1523 char *database_file_name; 1524 { 1525 static char *edit_av[10]; 1526 int error_ret, save_stderr = -1; 1527 int child_pid; 1528 int count; 1529 1530 /* <sys/param.h> has been included, so BSD will be defined on 1531 BSD systems */ 1532 #if BSD > 0 && BSD <= 43 1533 #ifndef WEXITSTATUS 1534 #define WEXITSTATUS(w) (w).w_retcode 1535 #endif 1536 union wait waitb; 1537 #else 1538 int waitb; 1539 #endif 1540 krb5_error_code retval; 1541 kdb_log_context *log_ctx; 1542 1543 if (debug) 1544 printf(gettext("calling kdb_util to load database\n")); 1545 1546 log_ctx = context->kdblog_context; 1547 1548 edit_av[0] = kdb_util; 1549 count = 1; 1550 if (realm) { 1551 edit_av[count++] = "-r"; 1552 edit_av[count++] = realm; 1553 } 1554 edit_av[count++] = "load"; 1555 if (kerb_database) { 1556 edit_av[count++] = "-d"; 1557 edit_av[count++] = kerb_database; 1558 } 1559 1560 if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) { 1561 edit_av[count++] = "-i"; 1562 } 1563 edit_av[count++] = database_file_name; 1564 edit_av[count++] = NULL; 1565 1566 switch(child_pid = fork()) { 1567 case -1: 1568 com_err(progname, errno, gettext("while trying to fork %s"), 1569 kdb_util); 1570 exit(1); 1571 /*NOTREACHED*/ 1572 case 0: 1573 if (!debug) { 1574 save_stderr = dup(2); 1575 close(0); 1576 close(1); 1577 close(2); 1578 open("/dev/null", O_RDWR); 1579 dup(0); 1580 dup(0); 1581 } 1582 1583 execv(kdb_util, edit_av); 1584 retval = errno; 1585 if (!debug) 1586 dup2(save_stderr, 2); 1587 com_err(progname, retval, gettext("while trying to exec %s"), 1588 kdb_util); 1589 _exit(1); 1590 /*NOTREACHED*/ 1591 default: 1592 if (debug) 1593 printf(gettext("Child PID is %d\n"), child_pid); 1594 if (wait(&waitb) < 0) { 1595 com_err(progname, errno, gettext("while waiting for %s"), 1596 kdb_util); 1597 exit(1); 1598 } 1599 } 1600 1601 error_ret = WEXITSTATUS(waitb); 1602 if (error_ret) { 1603 com_err(progname, 0, 1604 gettext("%s returned a bad exit status (%d)"), 1605 kdb_util, error_ret); 1606 exit(1); 1607 } 1608 return; 1609 } 1610