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