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