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