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