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