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