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 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 /* 31 * slave/kpropd.c 32 * 33 * Copyright 1990,1991 by the Massachusetts Institute of Technology. 34 * All Rights Reserved. 35 * 36 * Export of this software from the United States of America may 37 * require a specific license from the United States Government. 38 * It is the responsibility of any person or organization contemplating 39 * export to obtain such a license before exporting. 40 * 41 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 42 * distribute this software and its documentation for any purpose and 43 * without fee is hereby granted, provided that the above copyright 44 * notice appear in all copies and that both that copyright notice and 45 * this permission notice appear in supporting documentation, and that 46 * the name of M.I.T. not be used in advertising or publicity pertaining 47 * to distribution of the software without specific, written prior 48 * permission. Furthermore if you modify this software you must label 49 * your software as modified software and not distribute it in such a 50 * fashion that it might be confused with the original M.I.T. software. 51 * M.I.T. makes no representations about the suitability of 52 * this software for any purpose. It is provided "as is" without express 53 * or implied warranty. 54 * 55 * 56 * XXX We need to modify the protocol so that an acknowledge is set 57 * after each block, instead after the entire series is sent over. 58 * The reason for this is so that error packets can get interpreted 59 * right away. If you don't do this, the sender may never get the 60 * error packet, because it will die an EPIPE trying to complete the 61 * write... 62 */ 63 64 65 #include <stdio.h> 66 #include <ctype.h> 67 #include <sys/file.h> 68 #include <signal.h> 69 #include <string.h> 70 #include <fcntl.h> 71 #include <sys/types.h> 72 #include <sys/time.h> 73 #include <sys/stat.h> 74 #include <sys/socket.h> 75 #include <sys/wait.h> 76 #include <netinet/in.h> 77 #include <arpa/inet.h> 78 #include <sys/param.h> 79 #include <netdb.h> 80 #include <syslog.h> 81 #include <libintl.h> 82 #include <locale.h> 83 #include <k5-int.h> 84 #include <socket-utils.h> 85 #include "com_err.h" 86 #include <errno.h> 87 88 #include "kprop.h" 89 #include <iprop_hdr.h> 90 #include "iprop.h" 91 #include <kadm5/admin.h> 92 #include <kdb/kdb_log.h> 93 94 /* Solaris Kerberos */ 95 #include <libgen.h> 96 97 #define SYSLOG_CLASS LOG_DAEMON 98 99 char *poll_time = NULL; 100 char *def_realm = NULL; 101 boolean_t runonce = B_FALSE; 102 103 /* 104 * This struct simulates the use of _kadm5_server_handle_t 105 */ 106 typedef struct _kadm5_iprop_handle_t { 107 krb5_ui_4 magic_number; 108 krb5_ui_4 struct_version; 109 krb5_ui_4 api_version; 110 char *cache_name; 111 int destroy_cache; 112 CLIENT *clnt; 113 krb5_context context; 114 kadm5_config_params params; 115 struct _kadm5_iprop_handle_t *lhandle; 116 } *kadm5_iprop_handle_t; 117 118 static char *kprop_version = KPROP_PROT_VERSION; 119 120 char *progname; 121 int debug = 0; 122 char *srvtab = 0; 123 int standalone = 0; 124 125 krb5_principal server; /* This is our server principal name */ 126 krb5_principal client; /* This is who we're talking to */ 127 krb5_context kpropd_context; 128 krb5_auth_context auth_context; 129 char *realm = NULL; /* Our realm */ 130 char *file = KPROPD_DEFAULT_FILE; 131 char *temp_file_name; 132 char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL; 133 char *kerb_database = NULL; 134 char *acl_file_name = KPROPD_ACL_FILE; 135 136 krb5_address sender_addr; 137 krb5_address receiver_addr; 138 short port = 0; 139 140 void PRS 141 (int, char**); 142 int do_standalone 143 (iprop_role iproprole); 144 void doit 145 (int); 146 krb5_error_code do_iprop(kdb_log_context *log_ctx); 147 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 kerberos_authenticate(doit_context, fd, &client, &etype, from); 554 555 if (!authorized_principal(doit_context, client, etype)) { 556 char *name; 557 558 retval = krb5_unparse_name(doit_context, client, &name); 559 if (retval) { 560 /* Solaris Kerberos: Keep error messages consistent */ 561 com_err(progname, retval, 562 gettext("while unparsing client name")); 563 exit(1); 564 } 565 syslog(LOG_WARNING, 566 gettext("Rejected connection from unauthorized principal %s"), 567 name); 568 free(name); 569 exit(1); 570 } 571 omask = umask(077); 572 lock_fd = open(temp_file_name, O_RDWR|O_CREAT, 0600); 573 (void) umask(omask); 574 retval = krb5_lock_file(doit_context, lock_fd, 575 KRB5_LOCKMODE_EXCLUSIVE|KRB5_LOCKMODE_DONTBLOCK); 576 if (retval) { 577 com_err(progname, retval, 578 gettext("while trying to lock '%s'"), 579 temp_file_name); 580 exit(1); 581 } 582 if ((database_fd = open(temp_file_name, 583 O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { 584 com_err(progname, errno, 585 gettext("while opening database file, '%s'"), 586 temp_file_name); 587 exit(1); 588 } 589 recv_database(doit_context, fd, database_fd, &confmsg); 590 if (rename(temp_file_name, file)) { 591 /* Solaris Kerberos: Keep error messages consistent */ 592 com_err(progname, errno, 593 gettext("while renaming %s to %s"), 594 temp_file_name, file); 595 exit(1); 596 } 597 retval = krb5_lock_file(doit_context, lock_fd, KRB5_LOCKMODE_SHARED); 598 if (retval) { 599 com_err(progname, retval, 600 gettext("while downgrading lock on '%s'"), 601 temp_file_name); 602 exit(1); 603 } 604 load_database(doit_context, kdb5_util, file); 605 retval = krb5_lock_file(doit_context, lock_fd, KRB5_LOCKMODE_UNLOCK); 606 if (retval) { 607 com_err(progname, retval, 608 gettext("while unlocking '%s'"), temp_file_name); 609 exit(1); 610 } 611 (void)close(lock_fd); 612 613 /* 614 * Send the acknowledgement message generated in 615 * recv_database, then close the socket. 616 */ 617 retval = krb5_write_message(doit_context, (void *) &fd, &confmsg); 618 if (retval) { 619 krb5_free_data_contents(doit_context, &confmsg); 620 com_err(progname, retval, 621 gettext("while sending # of received bytes")); 622 exit(1); 623 } 624 krb5_free_data_contents(doit_context, &confmsg); 625 if (close(fd) < 0) { 626 com_err(progname, errno, 627 gettext("while trying to close database file")); 628 exit(1); 629 } 630 631 exit(0); 632 } 633 634 635 /* 636 * Routine to handle incremental update transfer(s) from master KDC 637 */ 638 krb5_error_code do_iprop(kdb_log_context *log_ctx) { 639 CLIENT *cl; 640 kadm5_ret_t retval; 641 kadm5_config_params params; 642 krb5_ccache cc; 643 krb5_principal iprop_svc_principal; 644 void *server_handle = NULL; 645 char *iprop_svc_princstr = NULL; 646 char *master_svc_princstr = NULL; 647 char *admin_server = NULL; 648 char *keytab_name = NULL; 649 unsigned int pollin, backoff_time; 650 int backoff_cnt = 0; 651 int reinit_cnt = 0; 652 int ret; 653 boolean_t frdone = B_FALSE; 654 655 kdb_incr_result_t *incr_ret; 656 static kdb_last_t mylast; 657 658 kdb_fullresync_result_t *full_ret; 659 char *full_resync_arg = NULL; 660 661 kadm5_iprop_handle_t handle; 662 kdb_hlog_t *ulog; 663 664 krb5_keytab kt; 665 krb5_keytab_entry entry; 666 char kt_name[MAX_KEYTAB_NAME_LEN]; 667 668 /* 669 * Solaris Kerberos: 670 * Delay daemonizing until some basic configuration checks have been 671 * performed 672 */ 673 #if 0 674 if (!debug) 675 daemon(0, 0); 676 #endif 677 pollin = (unsigned int)0; 678 (void) memset((char *)¶ms, 0, sizeof (params)); 679 ulog = log_ctx->ulog; 680 681 params.mask |= KADM5_CONFIG_REALM; 682 params.realm = def_realm; 683 684 if (master_svc_princstr == NULL) { 685 if (retval = kadm5_get_kiprop_host_srv_name(kpropd_context, 686 def_realm, &master_svc_princstr)) { 687 /* Solaris Kerberos: keep error messages consistent */ 688 com_err(progname, retval, 689 gettext("while getting kiprop host based " 690 "service name for realm %s"), def_realm); 691 exit(1); 692 } 693 } 694 695 /* 696 * Set cc to the default credentials cache 697 */ 698 if (retval = krb5_cc_default(kpropd_context, &cc)) { 699 com_err(progname, retval, 700 gettext("while opening default " 701 "credentials cache")); 702 exit(1); 703 } 704 705 retval = krb5_sname_to_principal(kpropd_context, NULL, KIPROP_SVC_NAME, 706 KRB5_NT_SRV_HST, &iprop_svc_principal); 707 if (retval) { 708 com_err(progname, retval, gettext("while trying to construct " 709 "host service principal")); 710 exit(1); 711 } 712 713 if (retval = krb5_unparse_name(kpropd_context, iprop_svc_principal, 714 &iprop_svc_princstr)) { 715 com_err(progname, retval, 716 gettext("while canonicalizing " 717 "principal name")); 718 krb5_free_principal(kpropd_context, iprop_svc_principal); 719 exit(1); 720 } 721 722 /* 723 * Solaris Kerberos: 724 * Check to see if kiprop/<fqdn>@REALM is in the keytab 725 */ 726 kt_name[0] = '\0'; 727 if (retval = krb5_kt_default_name(kpropd_context, kt_name, 728 MAX_KEYTAB_NAME_LEN)){ 729 com_err(progname, retval, gettext ("while resolving the " 730 "name of the default keytab")); 731 } 732 733 if (retval = krb5_kt_default(kpropd_context, &kt)) { 734 com_err(progname, retval, gettext ("while resolving default " 735 "keytab")); 736 krb5_free_principal(kpropd_context, iprop_svc_principal); 737 exit(1); 738 } 739 740 if (retval = krb5_kt_get_entry(kpropd_context, kt, iprop_svc_principal, 741 0, 0, &entry)) { 742 com_err(progname, retval, gettext("while retrieving entry %s " 743 "from %s"), iprop_svc_princstr, 744 kt_name[0] ? kt_name : "default keytab"); 745 krb5_kt_close(kpropd_context,kt); 746 krb5_free_principal(kpropd_context, iprop_svc_principal); 747 exit(1); 748 } 749 750 krb5_kt_close(kpropd_context,kt); 751 krb5_free_principal(kpropd_context, iprop_svc_principal); 752 753 if (!debug) { 754 /* Solaris Kerberos: Indicate where further messages will be sent */ 755 fprintf(stderr, gettext("%s: Logging to SYSLOG\n"), progname); 756 if (daemon(0, 0)) { 757 com_err(progname, errno, gettext("while daemonizing")); 758 exit(1); 759 } 760 rem_default_com_err_hook(); 761 } 762 763 reinit: 764 /* 765 * Authentication, initialize rpcsec_gss handle etc. 766 */ 767 retval = kadm5_init_with_skey(iprop_svc_princstr, keytab_name, 768 master_svc_princstr, 769 ¶ms, 770 KADM5_STRUCT_VERSION, 771 KADM5_API_VERSION_2, 772 NULL, 773 &server_handle); 774 775 if (retval) { 776 if (retval == KADM5_RPC_ERROR) { 777 reinit_cnt++; 778 if (server_handle) 779 kadm5_destroy((void *) server_handle); 780 server_handle = (void *)NULL; 781 handle = (kadm5_iprop_handle_t)NULL; 782 783 com_err(progname, retval, gettext( 784 "while attempting to connect" 785 " to master KDC ... retrying")); 786 backoff_time = backoff_from_master(&reinit_cnt); 787 (void) sleep(backoff_time); 788 goto reinit; 789 } else { 790 /* Solaris Kerberos: Be more verbose */ 791 com_err(progname, retval, 792 gettext("while initializing %s interface for " 793 "%s"), progname, iprop_svc_princstr); 794 if (retval == KADM5_BAD_CLIENT_PARAMS || 795 retval == KADM5_BAD_SERVER_PARAMS) 796 usage(); 797 exit(1); 798 } 799 } 800 801 /* 802 * Reset re-initialization count to zero now. 803 */ 804 reinit_cnt = backoff_time = 0; 805 806 /* 807 * Reset the handle to the correct type for the RPC call 808 */ 809 handle = server_handle; 810 811 /* 812 * If we have reached this far, we have succesfully established 813 * a RPCSEC_GSS connection; we now start polling for updates 814 */ 815 if (poll_time == NULL) { 816 if ((poll_time = (char *)strdup("2m")) == NULL) { 817 /* Solaris Kerberos: Keep error messages consistent */ 818 com_err(progname, ENOMEM, 819 gettext("while allocating poll_time")); 820 exit(1); 821 } 822 } 823 824 if (pollin == (unsigned int)0) 825 pollin = convert_polltime(poll_time); 826 827 for (;;) { 828 incr_ret = NULL; 829 full_ret = NULL; 830 831 /* 832 * Get the most recent ulog entry sno + ts, which 833 * we package in the request to the master KDC 834 */ 835 mylast.last_sno = ulog->kdb_last_sno; 836 mylast.last_time = ulog->kdb_last_time; 837 838 /* 839 * Loop continuously on an iprop_get_updates_1(), 840 * so that we can keep probing the master for updates 841 * or (if needed) do a full resync of the krb5 db. 842 */ 843 844 incr_ret = iprop_get_updates_1(&mylast, handle->clnt); 845 if (incr_ret == (kdb_incr_result_t *)NULL) { 846 clnt_perror(handle->clnt, 847 "iprop_get_updates call failed"); 848 if (server_handle) 849 kadm5_destroy((void *)server_handle); 850 server_handle = (void *)NULL; 851 handle = (kadm5_iprop_handle_t)NULL; 852 goto reinit; 853 } 854 855 switch (incr_ret->ret) { 856 857 case UPDATE_FULL_RESYNC_NEEDED: 858 /* 859 * We dont do a full resync again, if the last 860 * X'fer was a resync and if the master sno is 861 * still "0", i.e. no updates so far. 862 */ 863 if ((frdone == B_TRUE) && (incr_ret->lastentry.last_sno 864 == 0)) { 865 break; 866 } else { 867 868 full_ret = iprop_full_resync_1((void *) 869 &full_resync_arg, handle->clnt); 870 871 if (full_ret == (kdb_fullresync_result_t *) 872 NULL) { 873 clnt_perror(handle->clnt, 874 "iprop_full_resync call failed"); 875 if (server_handle) 876 kadm5_destroy((void *) 877 server_handle); 878 server_handle = (void *)NULL; 879 handle = (kadm5_iprop_handle_t)NULL; 880 goto reinit; 881 } 882 } 883 884 switch (full_ret->ret) { 885 case UPDATE_OK: 886 backoff_cnt = 0; 887 /* 888 * We now listen on the kprop port for 889 * the full dump 890 */ 891 ret = do_standalone(log_ctx->iproprole); 892 if (ret) 893 syslog(LOG_WARNING, 894 gettext("kpropd: Full resync, " 895 "invalid return.")); 896 if (debug) 897 if (ret) 898 fprintf(stderr, 899 gettext("Full resync " 900 "was unsuccessful\n")); 901 else 902 fprintf(stderr, 903 gettext("Full resync " 904 "was successful\n")); 905 frdone = B_TRUE; 906 break; 907 908 case UPDATE_BUSY: 909 /* 910 * Exponential backoff 911 */ 912 backoff_cnt++; 913 break; 914 915 case UPDATE_FULL_RESYNC_NEEDED: 916 case UPDATE_NIL: 917 default: 918 backoff_cnt = 0; 919 frdone = B_FALSE; 920 syslog(LOG_ERR, gettext("kpropd: Full resync," 921 " invalid return from master KDC.")); 922 break; 923 924 case UPDATE_PERM_DENIED: 925 syslog(LOG_ERR, gettext("kpropd: Full resync," 926 " permission denied.")); 927 goto error; 928 929 case UPDATE_ERROR: 930 syslog(LOG_ERR, gettext("kpropd: Full resync," 931 " error returned from master KDC.")); 932 goto error; 933 } 934 break; 935 936 case UPDATE_OK: 937 backoff_cnt = 0; 938 frdone = B_FALSE; 939 940 /* 941 * ulog_replay() will convert the ulog updates to db 942 * entries using the kdb conv api and will commit 943 * the entries to the slave kdc database 944 */ 945 retval = ulog_replay(kpropd_context, incr_ret); 946 947 if (retval) { 948 syslog(LOG_ERR, gettext("kpropd: ulog_replay" 949 " failed, updates not registered.")); 950 break; 951 } 952 953 if (debug) 954 fprintf(stderr, gettext("Update transfer " 955 "from master was OK\n")); 956 break; 957 958 case UPDATE_PERM_DENIED: 959 syslog(LOG_ERR, gettext("kpropd: get_updates," 960 " permission denied.")); 961 goto error; 962 963 case UPDATE_ERROR: 964 syslog(LOG_ERR, gettext("kpropd: get_updates, error " 965 "returned from master KDC.")); 966 goto error; 967 968 case UPDATE_BUSY: 969 /* 970 * Exponential backoff 971 */ 972 backoff_cnt++; 973 break; 974 975 case UPDATE_NIL: 976 /* 977 * Master-slave are in sync 978 */ 979 if (debug) 980 fprintf(stderr, gettext("Master, slave KDC's " 981 "are in-sync, no updates\n")); 982 backoff_cnt = 0; 983 frdone = B_FALSE; 984 break; 985 986 default: 987 backoff_cnt = 0; 988 syslog(LOG_ERR, gettext("kpropd: get_updates," 989 " invalid return from master KDC.")); 990 break; 991 } 992 993 if (runonce == B_TRUE) 994 goto done; 995 996 /* 997 * Sleep for the specified poll interval (Default is 2 mts), 998 * or do a binary exponential backoff if we get an 999 * UPDATE_BUSY signal 1000 */ 1001 if (backoff_cnt > 0) { 1002 backoff_time = backoff_from_master(&backoff_cnt); 1003 if (debug) 1004 fprintf(stderr, gettext("Busy signal received " 1005 "from master, backoff for %d secs\n"), 1006 backoff_time); 1007 (void) sleep(backoff_time); 1008 } 1009 else 1010 (void) sleep(pollin); 1011 1012 } 1013 1014 1015 error: 1016 if (debug) 1017 fprintf(stderr, gettext("ERROR returned by master, bailing\n")); 1018 syslog(LOG_ERR, gettext("kpropd: ERROR returned by master KDC," 1019 " bailing.\n")); 1020 done: 1021 if (poll_time) 1022 free(poll_time); 1023 if(iprop_svc_princstr) 1024 free(iprop_svc_princstr); 1025 if (master_svc_princstr) 1026 free(master_svc_princstr); 1027 if (retval = krb5_cc_close(kpropd_context, cc)) { 1028 com_err(progname, retval, 1029 gettext("while closing default ccache")); 1030 exit(1); 1031 } 1032 if (def_realm) 1033 free(def_realm); 1034 if (server_handle) 1035 kadm5_destroy((void *)server_handle); 1036 if (kpropd_context) 1037 krb5_free_context(kpropd_context); 1038 1039 if (runonce == B_TRUE) 1040 return (0); 1041 else 1042 exit(1); 1043 } 1044 1045 1046 /* 1047 * Do exponential backoff, since master KDC is BUSY or down 1048 */ 1049 unsigned int backoff_from_master(int *cnt) { 1050 unsigned int btime; 1051 1052 btime = (unsigned int)(2<<(*cnt)); 1053 if (btime > MAX_BACKOFF) { 1054 btime = MAX_BACKOFF; 1055 *cnt--; 1056 } 1057 1058 return (btime); 1059 } 1060 1061 1062 /* 1063 * Routine to convert the `pollstr' string to seconds 1064 */ 1065 int convert_polltime(char *pollstr) { 1066 char *tokenptr = NULL; 1067 int len, polltime; 1068 1069 len = polltime = 0; 1070 1071 if ((len = strcspn(pollstr, "s")) < strlen(pollstr)) { 1072 tokenptr = malloc((len + 1) * sizeof(char)); 1073 (void) strlcpy(tokenptr, pollstr, len + 1); 1074 polltime = atoi(tokenptr); 1075 } 1076 1077 if ((len = strcspn(pollstr, "m")) < strlen(pollstr)) { 1078 tokenptr = malloc((len + 1) * sizeof(char)); 1079 (void) strlcpy(tokenptr, pollstr, len + 1); 1080 polltime = atoi(tokenptr) * 60; 1081 } 1082 1083 if ((len = strcspn(pollstr, "h")) < strlen(pollstr)) { 1084 tokenptr = malloc((len + 1) * sizeof(char)); 1085 (void) strlcpy(tokenptr, pollstr, len + 1); 1086 polltime = atoi(tokenptr) * 3600; 1087 } 1088 1089 if (tokenptr != NULL) 1090 free(tokenptr); 1091 /* 1092 * If we have a bogus pollstr value, set polltime to the 1093 * default of 2 mts (120 seconds). 1094 */ 1095 if (polltime == 0) 1096 polltime = 120; 1097 return (polltime); 1098 } 1099 1100 static void 1101 kpropd_com_err_proc(whoami, code, fmt, args) 1102 const char *whoami; 1103 long code; 1104 const char *fmt; 1105 va_list args; 1106 { 1107 char error_buf[8096]; 1108 1109 error_buf[0] = '\0'; 1110 if (fmt) 1111 vsprintf(error_buf, fmt, args); 1112 syslog(LOG_ERR, "%s%s%s%s%s", whoami ? whoami : "", whoami ? ": " : "", 1113 code ? error_message(code) : "", code ? " " : "", error_buf); 1114 } 1115 1116 void PRS(argc,argv) 1117 int argc; 1118 char **argv; 1119 { 1120 register char *word, ch; 1121 char *cp; 1122 int c; 1123 struct hostent *hp; 1124 char my_host_name[MAXHOSTNAMELEN], buf[BUFSIZ]; 1125 krb5_error_code retval; 1126 static const char tmp[] = ".temp"; 1127 kadm5_config_params params; 1128 1129 (void) setlocale(LC_ALL, ""); 1130 1131 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 1132 #define TEXT_DOMAIN "KPROPD_TEST" /* Use this only if it weren't */ 1133 #endif 1134 1135 (void) textdomain(TEXT_DOMAIN); 1136 1137 (void) memset((char *) ¶ms, 0, sizeof (params)); 1138 1139 retval = krb5_init_context(&kpropd_context); 1140 if (retval) { 1141 com_err(argv[0], retval, 1142 gettext("while initializing krb5")); 1143 exit(1); 1144 } 1145 1146 /* Solaris Kerberos: Sanitize progname */ 1147 progname = basename(argv[0]); 1148 1149 while ((c = getopt(argc, argv, "dtf:F:p:P:r:s:Sa:")) != EOF){ 1150 switch (c) { 1151 case 'd': 1152 debug++; 1153 break; 1154 case 't': 1155 /* 1156 * Undocumented option - for testing only. 1157 * 1158 * Option to run the kpropd server exactly 1159 * once (this is true only if iprop is enabled). 1160 */ 1161 runonce = B_TRUE; 1162 break; 1163 1164 case 'f': 1165 file = optarg; 1166 if (!file) 1167 usage(); 1168 break; 1169 case 'F': 1170 kerb_database = optarg; 1171 if (!kerb_database) 1172 usage(); 1173 break; 1174 case 'p': 1175 kdb5_util = optarg; 1176 if (!kdb5_util) 1177 usage(); 1178 break; 1179 case 'P': 1180 port = htons(atoi(optarg)); 1181 if (!port) 1182 usage(); 1183 break; 1184 case 'r': 1185 realm = optarg; 1186 if (!realm) 1187 usage(); 1188 params.realm = realm; 1189 params.mask |= KADM5_CONFIG_REALM; 1190 break; 1191 case 's': 1192 srvtab = optarg; 1193 if (!srvtab) 1194 usage(); 1195 break; 1196 case 'S': 1197 standalone++; 1198 break; 1199 case 'a': 1200 acl_file_name = optarg; 1201 if (!acl_file_name) 1202 usage(); 1203 break; 1204 case '?': 1205 default: 1206 usage(); 1207 } 1208 1209 } 1210 /* 1211 * If not in debug mode, switch com_err reporting to syslog 1212 */ 1213 if (! debug) { 1214 openlog("kpropd", LOG_PID | LOG_ODELAY, SYSLOG_CLASS); 1215 /* 1216 * Solaris Kerberos: 1217 * Don't replace default logging. Add a new logging channel. 1218 * Stop logging to stderr when daemonizing 1219 */ 1220 add_com_err_hook(kpropd_com_err_proc); 1221 } 1222 /* 1223 * Get my hostname, so we can construct my service name 1224 */ 1225 retval = krb5_sname_to_principal(kpropd_context, 1226 NULL, KPROP_SERVICE_NAME, 1227 KRB5_NT_SRV_HST, &server); 1228 if (retval) { 1229 /* Solaris Kerberos: Keep error messages consistent */ 1230 com_err(progname, retval, 1231 gettext("while trying to construct my service name")); 1232 exit(1); 1233 } 1234 if (realm) { 1235 retval = krb5_set_principal_realm(kpropd_context, server, realm); 1236 if (retval) { 1237 com_err(progname, errno, 1238 gettext("while constructing my service realm")); 1239 exit(1); 1240 } 1241 } 1242 /* 1243 * Construct the name of the temporary file. 1244 */ 1245 if ((temp_file_name = (char *) malloc(strlen(file) + 1246 strlen(tmp) + 1)) == NULL) { 1247 com_err(progname, ENOMEM, 1248 gettext("while allocating filename for temp file")); 1249 exit(1); 1250 } 1251 strcpy(temp_file_name, file); 1252 strcat(temp_file_name, tmp); 1253 1254 retval = kadm5_get_config_params(kpropd_context, NULL, NULL, ¶ms, 1255 ¶ms); 1256 if (retval) { 1257 com_err(progname, retval, gettext("while initializing")); 1258 exit(1); 1259 } 1260 if (params.iprop_enabled == TRUE) { 1261 ulog_set_role(kpropd_context, IPROP_SLAVE); 1262 poll_time = params.iprop_polltime; 1263 1264 if (ulog_map(kpropd_context, ¶ms, FKPROPD)) { 1265 /* Solaris Kerberos: Keep error messages consistent */ 1266 com_err(progname, errno, 1267 gettext("while mapping log")); 1268 exit(1); 1269 } 1270 } 1271 1272 /* 1273 * Grab the realm info and check if iprop is enabled. 1274 */ 1275 if (def_realm == NULL) { 1276 retval = krb5_get_default_realm(kpropd_context, &def_realm); 1277 if (retval) { 1278 /* Solaris Kerberos: Keep error messages consistent */ 1279 com_err(progname, retval, 1280 gettext("while retrieving default realm")); 1281 exit(1); 1282 } 1283 } 1284 } 1285 1286 /* 1287 * Figure out who's calling on the other end of the connection.... 1288 */ 1289 void 1290 kerberos_authenticate(context, fd, clientp, etype, ss) 1291 krb5_context context; 1292 int fd; 1293 krb5_principal * clientp; 1294 krb5_enctype * etype; 1295 struct sockaddr_storage ss; 1296 { 1297 krb5_error_code retval; 1298 krb5_ticket * ticket; 1299 struct sockaddr_storage r_ss; 1300 int ss_length; 1301 krb5_keytab keytab = NULL; 1302 1303 /* 1304 * Set recv_addr and send_addr 1305 */ 1306 if (cvtkaddr(&ss, &sender_addr) == NULL) { 1307 com_err(progname, errno, 1308 gettext("while converting socket address")); 1309 exit(1); 1310 } 1311 1312 ss_length = sizeof (r_ss); 1313 if (getsockname(fd, (struct sockaddr *) &r_ss, &ss_length)) { 1314 com_err(progname, errno, 1315 gettext("while getting local socket address")); 1316 exit(1); 1317 } 1318 1319 if (cvtkaddr(&r_ss, &receiver_addr) == NULL) { 1320 com_err(progname, errno, 1321 gettext("while converting socket address")); 1322 exit(1); 1323 } 1324 1325 if (debug) { 1326 char *name; 1327 1328 retval = krb5_unparse_name(context, server, &name); 1329 if (retval) { 1330 /* Solaris Kerberos: Keep error messages consistent */ 1331 com_err(progname, retval, gettext("while unparsing server name")); 1332 exit(1); 1333 } 1334 printf(gettext("krb5_recvauth(%d, %s, %s, ...)\n"), fd, kprop_version, 1335 name); 1336 free(name); 1337 } 1338 1339 retval = krb5_auth_con_init(context, &auth_context); 1340 if (retval) { 1341 syslog(LOG_ERR, gettext("Error in krb5_auth_con_init: %s"), 1342 error_message(retval)); 1343 exit(1); 1344 } 1345 1346 retval = krb5_auth_con_setflags(context, auth_context, 1347 KRB5_AUTH_CONTEXT_DO_SEQUENCE); 1348 if (retval) { 1349 syslog(LOG_ERR, gettext("Error in krb5_auth_con_setflags: %s"), 1350 error_message(retval)); 1351 exit(1); 1352 } 1353 1354 retval = krb5_auth_con_setaddrs(context, auth_context, &receiver_addr, 1355 &sender_addr); 1356 if (retval) { 1357 syslog(LOG_ERR, gettext("Error in krb5_auth_con_setaddrs: %s"), 1358 error_message(retval)); 1359 exit(1); 1360 } 1361 1362 if (srvtab) { 1363 retval = krb5_kt_resolve(context, srvtab, &keytab); 1364 if (retval) { 1365 syslog(LOG_ERR, gettext("Error in krb5_kt_resolve: %s"), error_message(retval)); 1366 exit(1); 1367 } 1368 } 1369 1370 retval = krb5_recvauth(context, &auth_context, (void *) &fd, 1371 kprop_version, server, 0, keytab, &ticket); 1372 if (retval) { 1373 syslog(LOG_ERR, gettext("Error in krb5_recvauth: %s"), error_message(retval)); 1374 exit(1); 1375 } 1376 1377 retval = krb5_copy_principal(context, ticket->enc_part2->client, clientp); 1378 if (retval) { 1379 syslog(LOG_ERR, gettext("Error in krb5_copy_prinicpal: %s"), 1380 error_message(retval)); 1381 exit(1); 1382 } 1383 1384 *etype = ticket->enc_part.enctype; 1385 1386 if (debug) { 1387 char * name; 1388 char etypebuf[100]; 1389 1390 retval = krb5_unparse_name(context, *clientp, &name); 1391 if (retval) { 1392 /* Solaris Kerberos: Keep error messages consistent */ 1393 com_err(progname, retval, 1394 gettext("while unparsing client name")); 1395 exit(1); 1396 } 1397 1398 retval = krb5_enctype_to_string(*etype, etypebuf, sizeof(etypebuf)); 1399 if (retval) { 1400 /* Solaris Kerberos: Keep error messages consistent */ 1401 com_err(progname, retval, gettext("while unparsing ticket etype")); 1402 exit(1); 1403 } 1404 1405 printf("authenticated client: %s (etype == %s)\n", name, etypebuf); 1406 free(name); 1407 } 1408 1409 krb5_free_ticket(context, ticket); 1410 } 1411 1412 krb5_boolean 1413 authorized_principal(context, p, auth_etype) 1414 krb5_context context; 1415 krb5_principal p; 1416 krb5_enctype auth_etype; 1417 { 1418 char *name, *ptr; 1419 char buf[1024]; 1420 krb5_error_code retval; 1421 FILE *acl_file; 1422 int end; 1423 krb5_enctype acl_etype; 1424 1425 retval = krb5_unparse_name(context, p, &name); 1426 if (retval) 1427 return FALSE; 1428 1429 acl_file = fopen(acl_file_name, "r"); 1430 if (!acl_file) 1431 return FALSE; 1432 1433 while (!feof(acl_file)) { 1434 if (!fgets(buf, sizeof(buf), acl_file)) 1435 break; 1436 end = strlen(buf) - 1; 1437 if (buf[end] == '\n') 1438 buf[end] = '\0'; 1439 if (!strncmp(name, buf, strlen(name))) { 1440 ptr = buf+strlen(name); 1441 1442 /* if the next character is not whitespace or nul, then 1443 the match is only partial. continue on to new lines. */ 1444 if (*ptr && !isspace((int) *ptr)) 1445 continue; 1446 1447 /* otherwise, skip trailing whitespace */ 1448 for (; *ptr && isspace((int) *ptr); ptr++) ; 1449 1450 /* now, look for an etype string. if there isn't one, 1451 return true. if there is an invalid string, continue. 1452 If there is a valid string, return true only if it 1453 matches the etype passed in, otherwise continue */ 1454 1455 if ((*ptr) && 1456 ((retval = krb5_string_to_enctype(ptr, &acl_etype)) || 1457 (acl_etype != auth_etype))) 1458 continue; 1459 1460 free(name); 1461 fclose(acl_file); 1462 return TRUE; 1463 } 1464 } 1465 free(name); 1466 fclose(acl_file); 1467 return FALSE; 1468 } 1469 1470 void 1471 recv_database(context, fd, database_fd, confmsg) 1472 krb5_context context; 1473 int fd; 1474 int database_fd; 1475 krb5_data *confmsg; 1476 { 1477 krb5_ui_4 database_size; /* This must be 4 bytes */ 1478 int received_size, n; 1479 char buf[1024]; 1480 krb5_data inbuf, outbuf; 1481 krb5_error_code retval; 1482 1483 /* 1484 * Receive and decode size from client 1485 */ 1486 retval = krb5_read_message(context, (void *) &fd, &inbuf); 1487 if (retval) { 1488 send_error(context, fd, retval, gettext("while reading database size")); 1489 com_err(progname, retval, 1490 gettext("while reading size of database from client")); 1491 exit(1); 1492 } 1493 if (krb5_is_krb_error(&inbuf)) 1494 recv_error(context, &inbuf); 1495 retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL); 1496 if (retval) { 1497 send_error(context, fd, retval, gettext( 1498 "while decoding database size")); 1499 krb5_free_data_contents(context, &inbuf); 1500 com_err(progname, retval, 1501 gettext("while decoding database size from client")); 1502 exit(1); 1503 } 1504 memcpy((char *) &database_size, outbuf.data, sizeof(database_size)); 1505 krb5_free_data_contents(context, &inbuf); 1506 krb5_free_data_contents(context, &outbuf); 1507 database_size = ntohl(database_size); 1508 1509 /* 1510 * Initialize the initial vector. 1511 */ 1512 retval = krb5_auth_con_initivector(context, auth_context); 1513 if (retval) { 1514 send_error(context, fd, retval, gettext( 1515 "failed while initializing i_vector")); 1516 com_err(progname, retval, gettext("while initializing i_vector")); 1517 exit(1); 1518 } 1519 1520 /* 1521 * Now start receiving the database from the net 1522 */ 1523 received_size = 0; 1524 while (received_size < database_size) { 1525 retval = krb5_read_message(context, (void *) &fd, &inbuf); 1526 if (retval) { 1527 snprintf(buf, sizeof (buf), 1528 gettext("while reading database block starting at offset %d"), 1529 received_size); 1530 com_err(progname, retval, buf); 1531 send_error(context, fd, retval, buf); 1532 exit(1); 1533 } 1534 if (krb5_is_krb_error(&inbuf)) 1535 recv_error(context, &inbuf); 1536 retval = krb5_rd_priv(context, auth_context, &inbuf, 1537 &outbuf, NULL); 1538 if (retval) { 1539 snprintf(buf, sizeof (buf), 1540 gettext("while decoding database block starting at offset %d"), 1541 received_size); 1542 com_err(progname, retval, buf); 1543 send_error(context, fd, retval, buf); 1544 krb5_free_data_contents(context, &inbuf); 1545 exit(1); 1546 } 1547 n = write(database_fd, outbuf.data, outbuf.length); 1548 if (n < 0) { 1549 snprintf(buf, sizeof (buf), 1550 gettext( 1551 "while writing database block starting at offset %d"), 1552 received_size); 1553 send_error(context, fd, errno, buf); 1554 } else if (n != outbuf.length) { 1555 snprintf(buf, sizeof (buf), 1556 gettext( 1557 "incomplete write while writing database block starting at\n" 1558 "offset %d (%d written, %d expected)"), 1559 received_size, n, outbuf.length); 1560 send_error(context, fd, KRB5KRB_ERR_GENERIC, buf); 1561 } 1562 received_size += outbuf.length; 1563 /* SUNWresync121: our krb5...contents sets length to 0 */ 1564 krb5_free_data_contents(context, &inbuf); 1565 krb5_free_data_contents(context, &outbuf); 1566 1567 } 1568 /* 1569 * OK, we've seen the entire file. Did we get too many bytes? 1570 */ 1571 if (received_size > database_size) { 1572 snprintf(buf, sizeof (buf), 1573 gettext("Received %d bytes, expected %d bytes for database file"), 1574 received_size, database_size); 1575 send_error(context, fd, KRB5KRB_ERR_GENERIC, buf); 1576 } 1577 /* 1578 * Create message acknowledging number of bytes received, but 1579 * don't send it until kdb5_util returns successfully. 1580 */ 1581 database_size = htonl(database_size); 1582 inbuf.data = (char *) &database_size; 1583 inbuf.length = sizeof(database_size); 1584 retval = krb5_mk_safe(context,auth_context,&inbuf,confmsg,NULL); 1585 if (retval) { 1586 com_err(progname, retval, 1587 gettext("while encoding # of receieved bytes")); 1588 send_error(context, fd, retval, 1589 gettext("while encoding # of received bytes")); 1590 exit(1); 1591 } 1592 } 1593 1594 1595 void 1596 send_error(context, fd, err_code, err_text) 1597 krb5_context context; 1598 int fd; 1599 krb5_error_code err_code; 1600 char *err_text; 1601 { 1602 krb5_error error; 1603 const char *text; 1604 krb5_data outbuf; 1605 char buf[1024]; 1606 1607 memset((char *)&error, 0, sizeof(error)); 1608 krb5_us_timeofday(context, &error.stime, &error.susec); 1609 error.server = server; 1610 error.client = client; 1611 1612 if (err_text) 1613 text = err_text; 1614 else 1615 text = error_message(err_code); 1616 1617 error.error = err_code - ERROR_TABLE_BASE_krb5; 1618 if (error.error > 127) { 1619 error.error = KRB_ERR_GENERIC; 1620 if (err_text) { 1621 sprintf(buf, "%s %s", error_message(err_code), 1622 err_text); 1623 text = buf; 1624 } 1625 } 1626 error.text.length = strlen(text) + 1; 1627 error.text.data = malloc(error.text.length); 1628 if (error.text.data) { 1629 strcpy(error.text.data, text); 1630 if (!krb5_mk_error(context, &error, &outbuf)) { 1631 (void) krb5_write_message(context, (void *)&fd,&outbuf); 1632 krb5_free_data_contents(context, &outbuf); 1633 } 1634 free(error.text.data); 1635 } 1636 } 1637 1638 void 1639 recv_error(context, inbuf) 1640 krb5_context context; 1641 krb5_data *inbuf; 1642 { 1643 krb5_error *error; 1644 krb5_error_code retval; 1645 1646 retval = krb5_rd_error(context, inbuf, &error); 1647 if (retval) { 1648 com_err(progname, retval, 1649 gettext("while decoding error packet from client")); 1650 exit(1); 1651 } 1652 if (error->error == KRB_ERR_GENERIC) { 1653 if (error->text.data) 1654 fprintf(stderr, 1655 gettext("Generic remote error: %s\n"), 1656 error->text.data); 1657 } else if (error->error) { 1658 com_err(progname, error->error + ERROR_TABLE_BASE_krb5, 1659 gettext("signalled from server")); 1660 if (error->text.data) 1661 fprintf(stderr, 1662 gettext("Error text from client: %s\n"), 1663 error->text.data); 1664 } 1665 krb5_free_error(context, error); 1666 exit(1); 1667 } 1668 1669 void 1670 load_database(context, kdb_util, database_file_name) 1671 krb5_context context; 1672 char *kdb_util; 1673 char *database_file_name; 1674 { 1675 static char *edit_av[10]; 1676 int error_ret, save_stderr = -1; 1677 int child_pid; 1678 int count; 1679 1680 /* <sys/param.h> has been included, so BSD will be defined on 1681 BSD systems */ 1682 #if BSD > 0 && BSD <= 43 1683 #ifndef WEXITSTATUS 1684 #define WEXITSTATUS(w) (w).w_retcode 1685 #endif 1686 union wait waitb; 1687 #else 1688 int waitb; 1689 #endif 1690 krb5_error_code retval; 1691 kdb_log_context *log_ctx; 1692 1693 if (debug) 1694 printf(gettext("calling kdb_util to load database\n")); 1695 1696 log_ctx = context->kdblog_context; 1697 1698 edit_av[0] = kdb_util; 1699 count = 1; 1700 if (realm) { 1701 edit_av[count++] = "-r"; 1702 edit_av[count++] = realm; 1703 } 1704 edit_av[count++] = "load"; 1705 if (kerb_database) { 1706 edit_av[count++] = "-d"; 1707 edit_av[count++] = kerb_database; 1708 } 1709 1710 if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) { 1711 edit_av[count++] = "-i"; 1712 } 1713 edit_av[count++] = database_file_name; 1714 edit_av[count++] = NULL; 1715 1716 switch(child_pid = fork()) { 1717 case -1: 1718 com_err(progname, errno, gettext("while trying to fork %s"), 1719 kdb_util); 1720 exit(1); 1721 /*NOTREACHED*/ 1722 case 0: 1723 if (!debug) { 1724 save_stderr = dup(2); 1725 close(0); 1726 close(1); 1727 close(2); 1728 open("/dev/null", O_RDWR); 1729 dup(0); 1730 dup(0); 1731 } 1732 1733 execv(kdb_util, edit_av); 1734 retval = errno; 1735 if (!debug) 1736 dup2(save_stderr, 2); 1737 com_err(progname, retval, gettext("while trying to exec %s"), 1738 kdb_util); 1739 _exit(1); 1740 /*NOTREACHED*/ 1741 default: 1742 if (debug) 1743 printf(gettext("Child PID is %d\n"), child_pid); 1744 if (wait(&waitb) < 0) { 1745 com_err(progname, errno, gettext("while waiting for %s"), 1746 kdb_util); 1747 exit(1); 1748 } 1749 } 1750 1751 error_ret = WEXITSTATUS(waitb); 1752 if (error_ret) { 1753 com_err(progname, 0, 1754 gettext("%s returned a bad exit status (%d)"), 1755 kdb_util, error_ret); 1756 exit(1); 1757 } 1758 return; 1759 } 1760