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