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