1 /* 2 * Copyright 2004 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 "@(#)kpropd.c 1.7 04/09/08 SMI" 28 | #pragma ident "@(#)kpropd.c 1.6 04/07/27 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 #include <stdio.h> 65 #include <ctype.h> 66 #include <sys/file.h> 67 #include <signal.h> 68 #include <string.h> 69 #ifndef POSIX_TERMIOS 70 #include <sgtty.h> 71 #endif 72 #include <fcntl.h> 73 #include <sys/types.h> 74 #include <sys/time.h> 75 #include <sys/stat.h> 76 #include <sys/socket.h> 77 #include <sys/wait.h> 78 #include <netinet/in.h> 79 #include <arpa/inet.h> 80 #include <sys/param.h> 81 #include <netdb.h> 82 #include <syslog.h> 83 #include <libintl.h> 84 #include <locale.h> 85 #include <k5-int.h> 86 #include <socket-utils.h> 87 #include "com_err.h" 88 #include <errno.h> 89 90 #include "kprop.h" 91 + #include <iprop_hdr.h> 92 + #include "iprop.h" 93 + #include <kadm5/admin.h> 94 + #include <kdb/kdb_log.h> 95 96 #define SYSLOG_CLASS LOG_DAEMON 97 98 + char *poll_time = NULL; 99 + char *def_realm = NULL; 100 + boolean_t runonce = B_FALSE; 101 + 102 + /* 103 + * This struct simulates the use of _kadm5_server_handle_t 104 + */ 105 + typedef struct _kadm5_iprop_handle_t { 106 + krb5_ui_4 magic_number; 107 + krb5_ui_4 struct_version; 108 + krb5_ui_4 api_version; 109 + char *cache_name; 110 + int destroy_cache; 111 + CLIENT *clnt; 112 + krb5_context context; 113 + kadm5_config_params params; 114 + struct _kadm5_iprop_handle_t *lhandle; 115 + } *kadm5_iprop_handle_t; 116 + 117 static char *kprop_version = KPROP_PROT_VERSION; 118 119 char *progname; 120 int debug = 0; 121 char *srvtab = 0; 122 int standalone = 0; 123 124 krb5_principal server; /* This is our server principal name */ 125 krb5_principal client; /* This is who we're talking to */ 126 krb5_context kpropd_context; 127 krb5_auth_context auth_context; 128 char *realm = NULL; /* Our realm */ 129 char *file = KPROPD_DEFAULT_FILE; 130 char *temp_file_name; 131 char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL; 132 char *kerb_database = NULL; 133 char *acl_file_name = KPROPD_ACL_FILE; 134 135 int database_fd; 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 120 | (void); 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 155 krb5_boolean authorized_principal 156 (krb5_context, 157 krb5_principal, 158 krb5_enctype); 159 void recv_database 160 (krb5_context, 161 int, 162 int, 163 krb5_data *); 164 void load_database 165 (krb5_context, 166 char *, 167 char *); 168 void send_error 169 (krb5_context, 170 int, 171 krb5_error_code, 172 char *); 173 void recv_error 174 (krb5_context, 175 krb5_data *); 176 + int convert_polltime 177 + (char *); 178 + unsigned int backoff_from_master 179 + (int *); 180 181 static void usage() 182 { 183 fprintf(stderr, 184 gettext("\nUsage: %s\n"), /* progname may be a long pathname */ 185 progname); 186 187 fprintf(stderr, 188 gettext("\t[-r realm] [-s srvtab] [-dS] [-f slave_file]\n")); 189 190 fprintf(stderr, 191 gettext("\t[-F kerberos_db_file ] [-p kdb5_util_pathname]\n")); 192 193 fprintf(stderr, gettext("\t[-P port] [-a acl_file]\n")); 194 195 exit(1); 196 } 197 198 int 199 main(argc, argv) 200 int argc; 201 char **argv; 202 { 203 + krb5_error_code retval; 204 + int ret = 0; 205 + kdb_log_context *log_ctx; 206 + 207 PRS(argc, argv); 208 209 + log_ctx = kpropd_context->kdblog_context; 210 + 211 + if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) { 212 + /* 213 + * We wanna do iprop ! 214 + */ 215 + retval = do_iprop(log_ctx); 216 + if (retval) { 217 + com_err(progname, retval, 218 + gettext("do_iprop failed.\n")); 219 + exit(1); 220 + } 221 + 222 + } else { 223 if (standalone) 177 | do_standalone(); 224 | ret = do_standalone(IPROP_NULL); 225 else 180 - exit(0); 226 doit(0); 227 } 228 183 | int do_standalone() 229 | exit(ret); 230 + } 231 + 232 + int do_standalone(iprop_role iproprole) 233 { 234 struct linger linger; 235 struct servent *sp; 236 int finet, fromlen, s; 237 int on = 1; 238 int ret, status = 0; 239 struct sockaddr_in6 sin6 = { AF_INET6 }; 240 int sin6_size = sizeof (sin6); 241 + 242 /* listen for either ipv4 or ipv6 */ 243 finet = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 244 if (finet < 0 ) { 245 com_err(progname, errno, gettext("while obtaining socket")); 246 exit(1); 247 } 248 249 if(!port) { 250 sp = getservbyname(KPROP_SERVICE, "tcp"); 251 if (sp == NULL) { 252 com_err(progname, 0, gettext("%s/tcp: unknown service"), 253 KPROP_SERVICE); 254 exit(1); 255 + } 256 sin6.sin6_port = sp->s_port; 257 } else 258 sin6.sin6_port = port; 259 + 260 + /* 261 + * We need to close the socket immediately if iprop is enabled, 262 + * since back-to-back full resyncs are possible, so we do not 263 + * linger around for too long 264 + */ 265 + if (iproprole == IPROP_SLAVE) { 266 + if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, 267 + (char *)&on, sizeof(on)) < 0) 268 + com_err(progname, errno, 269 + gettext("in setsockopt(SO_REUSEADDR)")); 270 + linger.l_onoff = 1; 271 + linger.l_linger = 2; 272 + if (setsockopt(finet, SOL_SOCKET, SO_LINGER, 273 + (void *)&linger, sizeof(linger)) < 0) 274 + com_err(progname, errno, 275 + gettext("in setsockopt(SO_LINGER)")); 276 + } 277 if ((ret = bind(finet, (struct sockaddr *)&sin6, sizeof(sin6))) < 0) { 278 if (debug) { 279 on = 1; 280 fprintf(stderr, 281 gettext("%s: attempting to rebind socket " 282 "with SO_REUSEADDR\n"), progname); 283 if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, 284 (char *)&on, sizeof(on)) < 0) { 285 com_err(progname, errno, 286 gettext("in setsockopt(SO_REUSEADDR)")); 287 } 288 ret = bind(finet, (struct sockaddr *) &sin6, sizeof(sin6)); 289 } 290 291 if (ret < 0) { 292 perror(gettext("bind")); 293 com_err(progname, errno, 294 gettext("while binding listener socket")); 295 exit(1); 296 } 297 } 298 230 | if (!debug) 299 | if (!debug && (iproprole != IPROP_SLAVE)) 300 daemon(1, 0); 301 302 #ifdef PID_FILE 303 if ((pidfile = fopen(PID_FILE, "w")) != NULL) { 304 fprintf(pidfile, gettext("%d\n"), getpid()); 305 fclose(pidfile); 306 } else 307 com_err(progname, errno, 308 gettext("while opening pid file %s for writing"), 309 PID_FILE); 310 #endif 311 312 if (listen(finet, 5) < 0) { 313 com_err(progname, errno, gettext("in listen call")); 314 exit(1); 315 } 316 + 317 while (1) { 318 int child_pid; 319 + 320 s = accept(finet, (struct sockaddr *) &sin6, &sin6_size); 321 322 if (s < 0) { 323 if (errno != EINTR) 324 com_err(progname, errno, 325 gettext("from accept system call")); 326 continue; 327 } 328 258 | if (debug) 329 | if (debug && (iproprole != IPROP_SLAVE)) 330 child_pid = 0; 331 else 332 child_pid = fork(); 333 + 334 switch (child_pid) { 335 case -1: 336 com_err(progname, errno, gettext("while forking")); 337 exit(1); 338 /*NOTREACHED*/ 339 case 0: 340 /* child */ 341 (void) close(finet); 342 doit(s); 343 close(s); 344 _exit(0); 345 /*NOTREACHED*/ 346 default: 347 /* parent */ 276 | wait(0); 348 | if (wait(&status) < 0) { 349 + com_err(progname, errno, 350 + gettext("while waiting to receive database")); 351 + exit(1); 352 + } 353 + 354 close(s); 355 + if (iproprole == IPROP_SLAVE) 356 + close(finet); 357 358 + if ((ret = WEXITSTATUS(status)) != 0) 359 + return (ret); 360 } 361 362 + if (iproprole == IPROP_SLAVE) 363 + break; 364 } 365 + 366 + return (0); 367 } 368 369 void doit(fd) 370 int fd; 371 { 372 struct sockaddr_storage from; 373 socklen_t fromlen; 374 int on = 1; 375 struct hostent *hp; 376 krb5_error_code retval; 377 krb5_data confmsg; 378 int lock_fd; 379 int omask; 380 krb5_enctype etype; 381 char ntop[NI_MAXHOST] = ""; 382 krb5_context doit_context; 383 + kdb_log_context *log_ctx; 384 385 retval = krb5_init_context(&doit_context); 386 if (retval) { 387 com_err(progname, retval, gettext("while initializing krb5")); 388 exit(1); 389 } 390 + log_ctx = kpropd_context->kdblog_context; 391 + if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) 392 + ulog_set_role(doit_context, IPROP_SLAVE); 393 394 fromlen = (socklen_t)sizeof (from); 395 396 if (getpeername(fd, (struct sockaddr *) &from, &fromlen) < 0) { 397 fprintf(stderr, "%s: ", progname); 398 perror(gettext("getpeername")); 399 exit(1); 400 } 401 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (caddr_t) &on, 402 sizeof (on)) < 0) { 403 com_err(progname, errno, 404 gettext("while attempting setsockopt (SO_KEEPALIVE)")); 405 } 406 407 if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), 408 NULL, 0, NI_NUMERICHOST) != 0) { 409 410 /* getnameifo failed so use inet_ntop() to get printable addresses */ 411 if (from.ss_family == AF_INET) { 412 413 inet_ntop(AF_INET, 414 (const void *)&ss2sin(&from)->sin_addr, 415 ntop, sizeof(ntop)); 416 417 } else if (from.ss_family == AF_INET6 && 418 ! IN6_IS_ADDR_V4MAPPED(&ss2sin6(&from)->sin6_addr)) { 419 420 ipaddr_t v4addr; 421 422 inet_ntop(AF_INET6, 423 (const void *)&ss2sin6(&from)->sin6_addr, ntop, 424 sizeof(ntop)); 425 } 426 /* ipv4 mapped ipv6 addrs handled later */ 427 } 428 429 if (from.ss_family == AF_INET || from.ss_family == AF_INET6) { 430 431 if (from.ss_family == AF_INET6 && 432 IN6_IS_ADDR_V4MAPPED(&ss2sin6(&from)->sin6_addr)) { 433 434 ipaddr_t v4addr; 435 436 /* coerce ipv4 mapped ipv6 addr to normal ipv4 addr */ 437 IN6_V4MAPPED_TO_IPADDR(&(ss2sin6(&from)->sin6_addr), 438 v4addr); 439 440 inet_ntop(AF_INET, (const void *) &v4addr, 441 ntop, sizeof(ntop)); 442 } 443 444 syslog(LOG_INFO, gettext("Connection from %s"), ntop); 445 446 if (debug) 447 printf("Connection from %s\n", ntop); 448 449 } else { 450 /* address family isn't either AF_INET || AF_INET6 */ 451 syslog(LOG_INFO, 452 gettext("Connection from unknown address family:%d"), 453 from.ss_family); 454 455 if (debug) { 456 printf(gettext("Connection from unknown address family:%d"), 457 from.ss_family); 458 } 459 } 460 461 /* 462 * Now do the authentication 463 */ 464 kerberos_authenticate(doit_context, fd, &client, &etype, from); 465 466 if (!authorized_principal(doit_context, client, etype)) { 467 char *name; 468 469 if (retval = krb5_unparse_name(doit_context, client, &name)) { 470 com_err(progname, retval, 471 gettext("While unparsing client name")); 472 exit(1); 473 } 474 syslog(LOG_WARNING, 475 gettext("Rejected connection from unauthorized principal %s"), 476 name); 477 free(name); 478 exit(1); 479 } 480 omask = umask(077); 481 lock_fd = open(temp_file_name, O_RDWR|O_CREAT, 0600); 482 (void) umask(omask); 483 retval = krb5_lock_file(doit_context, lock_fd, 484 KRB5_LOCKMODE_EXCLUSIVE|KRB5_LOCKMODE_DONTBLOCK); 485 if (retval) { 486 com_err(progname, retval, 487 gettext("while trying to lock '%s'"), 488 temp_file_name); 489 exit(1); 490 } 491 if ((database_fd = open(temp_file_name, 492 O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { 493 com_err(progname, errno, 494 gettext("while opening database file, '%s'"), 495 temp_file_name); 496 exit(1); 497 } 498 recv_database(doit_context, fd, database_fd, &confmsg); 499 if (rename(temp_file_name, file)) { 500 com_err(progname, errno, 501 gettext("While renaming %s to %s"), 502 temp_file_name, file); 503 exit(1); 504 } 505 retval = krb5_lock_file(doit_context, lock_fd, KRB5_LOCKMODE_SHARED); 506 if (retval) { 507 com_err(progname, retval, 508 gettext("while downgrading lock on '%s'"), 509 temp_file_name); 510 exit(1); 511 } 512 load_database(doit_context, kdb5_util, file); 513 retval = krb5_lock_file(doit_context, lock_fd, KRB5_LOCKMODE_UNLOCK); 514 if (retval) { 515 com_err(progname, retval, 516 gettext("while unlocking '%s'"), temp_file_name); 517 exit(1); 518 } 519 (void)close(lock_fd); 520 521 /* 522 * Send the acknowledgement message generated in 523 * recv_database, then close the socket. 524 */ 525 if (retval = krb5_write_message(doit_context, (void *) &fd, 526 &confmsg)) { 527 krb5_free_data_contents(doit_context, &confmsg); 528 com_err(progname, retval, 529 gettext("while sending # of received bytes")); 530 exit(1); 531 } 532 krb5_free_data_contents(doit_context, &confmsg); 533 if (close(fd) < 0) { 534 com_err(progname, errno, 535 gettext("while trying to close database file")); 536 exit(1); 537 } 538 539 exit(0); 540 } 541 542 + 543 + /* 544 + * Routine to handle incremental update transfer(s) from master KDC 545 + */ 546 + krb5_error_code do_iprop(kdb_log_context *log_ctx) { 547 + CLIENT *cl; 548 + kadm5_ret_t retval; 549 + kadm5_config_params params; 550 + krb5_ccache cc; 551 + krb5_principal iprop_svc_principal; 552 + void *server_handle = NULL; 553 + char *iprop_svc_princstr = NULL; 554 + char *master_svc_princstr = NULL; 555 + char *admin_server = NULL; 556 + char *keytab_name = NULL; 557 + unsigned int pollin, backoff_time; 558 + int backoff_cnt = 0; 559 + int reinit_cnt = 0; 560 + int ret; 561 + boolean_t frdone = B_FALSE; 562 + 563 + kdb_incr_result_t *incr_ret; 564 + static kdb_last_t mylast; 565 + 566 + kdb_fullresync_result_t *full_ret; 567 + char *full_resync_arg = NULL; 568 + 569 + kadm5_iprop_handle_t handle; 570 + kdb_hlog_t *ulog; 571 + 572 + if (!debug) 573 + daemon(0, 0); 574 + 575 + pollin = (unsigned int)0; 576 + (void) memset((char *)¶ms, 0, sizeof (params)); 577 + ulog = log_ctx->ulog; 578 + 579 + params.mask |= KADM5_CONFIG_REALM; 580 + params.realm = def_realm; 581 + 582 + if (master_svc_princstr == NULL) { 583 + if (retval = kadm5_get_kiprop_host_srv_name(kpropd_context, 584 + def_realm, &master_svc_princstr)) { 585 + com_err(progname, retval, 586 + gettext("%s: unable to get kiprop host based " 587 + "service name for realm %s\n"), 588 + progname, def_realm); 589 + exit(1); 590 + } 591 + } 592 + 593 + /* 594 + * Set cc to the default credentials cache 595 + */ 596 + if (retval = krb5_cc_default(kpropd_context, &cc)) { 597 + com_err(progname, retval, 598 + gettext("while opening default " 599 + "credentials cache")); 600 + exit(1); 601 + } 602 + 603 + retval = krb5_sname_to_principal(kpropd_context, NULL, KIPROP_SVC_NAME, 604 + KRB5_NT_SRV_HST, &iprop_svc_principal); 605 + if (retval) { 606 + com_err(progname, retval, gettext("while trying to construct " 607 + "host service principal")); 608 + exit(1); 609 + } 610 + 611 + if (retval = krb5_unparse_name(kpropd_context, iprop_svc_principal, 612 + &iprop_svc_princstr)) { 613 + com_err(progname, retval, 614 + gettext("while canonicalizing " 615 + "principal name")); 616 + krb5_free_principal(kpropd_context, iprop_svc_principal); 617 + exit(1); 618 + } 619 + krb5_free_principal(kpropd_context, iprop_svc_principal); 620 + 621 + reinit: 622 + /* 623 + * Authentication, initialize rpcsec_gss handle etc. 624 + */ 625 + retval = kadm5_init_with_skey(iprop_svc_princstr, keytab_name, 626 + master_svc_princstr, 627 + ¶ms, 628 + KADM5_STRUCT_VERSION, 629 + KADM5_API_VERSION_2, 630 + &server_handle); 631 + 632 + if (retval) { 633 + if (retval == KADM5_RPC_ERROR) { 634 + reinit_cnt++; 635 + if (server_handle) 636 + kadm5_destroy((void *) server_handle); 637 + server_handle = (void *)NULL; 638 + handle = (kadm5_iprop_handle_t)NULL; 639 + 640 + com_err(progname, retval, gettext( 641 + "while attempting to connect" 642 + " to master KDC ... retrying")); 643 + backoff_time = backoff_from_master(&reinit_cnt); 644 + (void) sleep(backoff_time); 645 + goto reinit; 646 + } else { 647 + com_err(progname, retval, 648 + gettext("while initializing %s interface"), 649 + progname); 650 + if (retval == KADM5_BAD_CLIENT_PARAMS || 651 + retval == KADM5_BAD_SERVER_PARAMS) 652 + usage(); 653 + exit(1); 654 + } 655 + } 656 + 657 + /* 658 + * Reset re-initialization count to zero now. 659 + */ 660 + reinit_cnt = backoff_time = 0; 661 + 662 + /* 663 + * Reset the handle to the correct type for the RPC call 664 + */ 665 + handle = server_handle; 666 + 667 + /* 668 + * If we have reached this far, we have succesfully established 669 + * a RPCSEC_GSS connection; we now start polling for updates 670 + */ 671 + if (poll_time == NULL) { 672 + if ((poll_time = (char *)strdup("2m")) == NULL) { 673 + com_err(progname, ENOMEM, 674 + gettext("Unable to allocate poll_time")); 675 + exit(1); 676 + } 677 + } 678 + 679 + if (pollin == (unsigned int)0) 680 + pollin = convert_polltime(poll_time); 681 + 682 + for (;;) { 683 + incr_ret = NULL; 684 + full_ret = NULL; 685 + 686 + /* 687 + * Get the most recent ulog entry sno + ts, which 688 + * we package in the request to the master KDC 689 + */ 690 + mylast.last_sno = ulog->kdb_last_sno; 691 + mylast.last_time = ulog->kdb_last_time; 692 + 693 + /* 694 + * Loop continuously on an iprop_get_updates_1(), 695 + * so that we can keep probing the master for updates 696 + * or (if needed) do a full resync of the krb5 db. 697 + */ 698 + 699 + incr_ret = iprop_get_updates_1(&mylast, handle->clnt); 700 + if (incr_ret == (kdb_incr_result_t *)NULL) { 701 + clnt_perror(handle->clnt, 702 + "iprop_get_updates call failed"); 703 + if (server_handle) 704 + kadm5_destroy((void *)server_handle); 705 + server_handle = (void *)NULL; 706 + handle = (kadm5_iprop_handle_t)NULL; 707 + goto reinit; 708 + } 709 + 710 + switch (incr_ret->ret) { 711 + 712 + case UPDATE_FULL_RESYNC_NEEDED: 713 + /* 714 + * We dont do a full resync again, if the last 715 + * X'fer was a resync and if the master sno is 716 + * still "0", i.e. no updates so far. 717 + */ 718 + if ((frdone == B_TRUE) && (incr_ret->lastentry.last_sno 719 + == 0)) { 720 + break; 721 + } else { 722 + 723 + full_ret = iprop_full_resync_1((void *) 724 + &full_resync_arg, handle->clnt); 725 + 726 + if (full_ret == (kdb_fullresync_result_t *) 727 + NULL) { 728 + clnt_perror(handle->clnt, 729 + "iprop_full_resync call failed"); 730 + if (server_handle) 731 + kadm5_destroy((void *) 732 + server_handle); 733 + server_handle = (void *)NULL; 734 + handle = (kadm5_iprop_handle_t)NULL; 735 + goto reinit; 736 + } 737 + } 738 + 739 + switch (full_ret->ret) { 740 + case UPDATE_OK: 741 + backoff_cnt = 0; 742 + /* 743 + * We now listen on the kprop port for 744 + * the full dump 745 + */ 746 + ret = do_standalone(log_ctx->iproprole); 747 + if (ret) 748 + syslog(LOG_WARNING, 749 + gettext("kpropd: Full resync, " 750 + "invalid return.")); 751 + if (debug) 752 + if (ret) 753 + fprintf(stderr, 754 + gettext("Full resync " 755 + "was unsuccessful\n")); 756 + else 757 + fprintf(stderr, 758 + gettext("Full resync " 759 + "was successful\n")); 760 + frdone = B_TRUE; 761 + break; 762 + 763 + case UPDATE_BUSY: 764 + /* 765 + * Exponential backoff 766 + */ 767 + backoff_cnt++; 768 + break; 769 + 770 + case UPDATE_FULL_RESYNC_NEEDED: 771 + case UPDATE_NIL: 772 + default: 773 + backoff_cnt = 0; 774 + frdone = B_FALSE; 775 + syslog(LOG_ERR, gettext("kpropd: Full resync," 776 + " invalid return from master KDC.")); 777 + break; 778 + 779 + case UPDATE_PERM_DENIED: 780 + syslog(LOG_ERR, gettext("kpropd: Full resync," 781 + " permission denied.")); 782 + goto error; 783 + 784 + case UPDATE_ERROR: 785 + syslog(LOG_ERR, gettext("kpropd: Full resync," 786 + " error returned from master KDC.")); 787 + goto error; 788 + } 789 + break; 790 + 791 + case UPDATE_OK: 792 + backoff_cnt = 0; 793 + frdone = B_FALSE; 794 + 795 + /* 796 + * ulog_replay() will convert the ulog updates to db 797 + * entries using the kdb conv api and will commit 798 + * the entries to the slave kdc database 799 + */ 800 + retval = ulog_replay(kpropd_context, incr_ret); 801 + 802 + if (retval) { 803 + syslog(LOG_ERR, gettext("kpropd: ulog_replay" 804 + " failed, updates not registered.")); 805 + break; 806 + } 807 + 808 + if (debug) 809 + fprintf(stderr, gettext("Update transfer " 810 + "from master was OK\n")); 811 + break; 812 + 813 + case UPDATE_PERM_DENIED: 814 + syslog(LOG_ERR, gettext("kpropd: get_updates," 815 + " permission denied.")); 816 + goto error; 817 + 818 + case UPDATE_ERROR: 819 + syslog(LOG_ERR, gettext("kpropd: get_updates, error " 820 + "returned from master KDC.")); 821 + goto error; 822 + 823 + case UPDATE_BUSY: 824 + /* 825 + * Exponential backoff 826 + */ 827 + backoff_cnt++; 828 + break; 829 + 830 + case UPDATE_NIL: 831 + /* 832 + * Master-slave are in sync 833 + */ 834 + if (debug) 835 + fprintf(stderr, gettext("Master, slave KDC's " 836 + "are in-sync, no updates\n")); 837 + backoff_cnt = 0; 838 + frdone = B_FALSE; 839 + break; 840 + 841 + default: 842 + backoff_cnt = 0; 843 + syslog(LOG_ERR, gettext("kpropd: get_updates," 844 + " invalid return from master KDC.")); 845 + break; 846 + } 847 + 848 + if (runonce == B_TRUE) 849 + goto done; 850 + 851 + /* 852 + * Sleep for the specified poll interval (Default is 2 mts), 853 + * or do a binary exponential backoff if we get an 854 + * UPDATE_BUSY signal 855 + */ 856 + if (backoff_cnt > 0) { 857 + backoff_time = backoff_from_master(&backoff_cnt); 858 + if (debug) 859 + fprintf(stderr, gettext("Busy signal received " 860 + "from master, backoff for %d secs\n"), 861 + backoff_time); 862 + (void) sleep(backoff_time); 863 + } 864 + else 865 + (void) sleep(pollin); 866 + 867 + } 868 + 869 + 870 + error: 871 + if (debug) 872 + fprintf(stderr, gettext("ERROR returned by master, bailing\n")); 873 + syslog(LOG_ERR, gettext("kpropd: ERROR returned by master KDC," 874 + " bailing.\n")); 875 + done: 876 + if (poll_time) 877 + free(poll_time); 878 + if(iprop_svc_princstr) 879 + free(iprop_svc_princstr); 880 + if (master_svc_princstr) 881 + free(master_svc_princstr); 882 + if (retval = krb5_cc_close(kpropd_context, cc)) { 883 + com_err(progname, retval, 884 + gettext("while closing default ccache")); 885 + exit(1); 886 + } 887 + if (def_realm) 888 + free(def_realm); 889 + if (server_handle) 890 + kadm5_destroy((void *)server_handle); 891 + if (kpropd_context) 892 + krb5_free_context(kpropd_context); 893 + 894 + if (runonce == B_TRUE) 895 + return (0); 896 + else 897 + exit(1); 898 + } 899 + 900 + 901 + /* 902 + * Do exponential backoff, since master KDC is BUSY or down 903 + */ 904 + unsigned int backoff_from_master(int *cnt) { 905 + unsigned int btime; 906 + 907 + btime = (unsigned int)(2<<(*cnt)); 908 + if (btime > MAX_BACKOFF) { 909 + btime = MAX_BACKOFF; 910 + *cnt--; 911 + } 912 + 913 + return (btime); 914 + } 915 + 916 + 917 + /* 918 + * Routine to convert the `pollstr' string to seconds 919 + */ 920 + int convert_polltime(char *pollstr) { 921 + char *tokenptr = NULL; 922 + int len, polltime; 923 + 924 + len = polltime = 0; 925 + 926 + if ((len = strcspn(pollstr, "s")) < strlen(pollstr)) { 927 + tokenptr = malloc((len + 1) * sizeof(char)); 928 + (void) strlcpy(tokenptr, pollstr, len + 1); 929 + polltime = atoi(tokenptr); 930 + } 931 + 932 + if ((len = strcspn(pollstr, "m")) < strlen(pollstr)) { 933 + tokenptr = malloc((len + 1) * sizeof(char)); 934 + (void) strlcpy(tokenptr, pollstr, len + 1); 935 + polltime = atoi(tokenptr) * 60; 936 + } 937 + 938 + if ((len = strcspn(pollstr, "h")) < strlen(pollstr)) { 939 + tokenptr = malloc((len + 1) * sizeof(char)); 940 + (void) strlcpy(tokenptr, pollstr, len + 1); 941 + polltime = atoi(tokenptr) * 3600; 942 + } 943 + 944 + if (tokenptr != NULL) 945 + free(tokenptr); 946 + /* 947 + * If we have a bogus pollstr value, set polltime to the 948 + * default of 2 mts (120 seconds). 949 + */ 950 + if (polltime == 0) 951 + polltime = 120; 952 + return (polltime); 953 + } 954 + 955 static void 956 kpropd_com_err_proc(whoami, code, fmt, args) 957 const char *whoami; 958 long code; 959 const char *fmt; 960 va_list args; 961 { 962 char error_buf[8096]; 963 964 error_buf[0] = '\0'; 965 if (fmt) 966 vsprintf(error_buf, fmt, args); 967 syslog(LOG_ERR, "%s%s%s%s%s", whoami ? whoami : "", whoami ? ": " : "", 968 code ? error_message(code) : "", code ? " " : "", error_buf); 969 } 970 971 void PRS(argc,argv) 972 int argc; 973 char **argv; 974 { 975 register char *word, ch; 976 char *cp; 977 int c; 978 struct hostent *hp; 979 char my_host_name[MAXHOSTNAMELEN], buf[BUFSIZ]; 980 krb5_error_code retval; 480 - 981 static const char tmp[] = ".temp"; 982 kadm5_config_params params; 983 + 984 (void) setlocale(LC_ALL, ""); 985 986 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 987 #define TEXT_DOMAIN "KPROPD_TEST" /* Use this only if it weren't */ 988 #endif 989 990 (void) textdomain(TEXT_DOMAIN); 991 992 (void) memset((char *) ¶ms, 0, sizeof (params)); 993 994 retval = krb5_init_context(&kpropd_context); 995 if (retval) { 996 com_err(argv[0], retval, 997 gettext("while initializing krb5")); 998 exit(1); 999 } 1000 progname = argv[0]; 499 | while ((c = getopt(argc, argv, "df:F:p:P:r:s:Sa:")) != EOF){ 1001 | while ((c = getopt(argc, argv, "dtf:F:p:P:r:s:Sa:")) != EOF){ 1002 switch (c) { 1003 case 'd': 1004 debug++; 1005 break; 1006 + case 't': 1007 + /* 1008 + * Undocumented option - for testing only. 1009 + * 1010 + * Option to run the kpropd server exactly 1011 + * once (this is true only if iprop is enabled). 1012 + */ 1013 + runonce = B_TRUE; 1014 + break; 1015 + 1016 case 'f': 1017 file = optarg; 1018 if (!file) 1019 usage(); 1020 break; 1021 case 'F': 1022 kerb_database = optarg; 1023 if (!kerb_database) 1024 usage(); 1025 break; 1026 case 'p': 1027 kdb5_util = optarg; 1028 if (!kdb5_util) 1029 usage(); 1030 break; 1031 case 'P': 1032 port = htons(atoi(optarg)); 1033 if (!port) 1034 usage(); 1035 break; 1036 case 'r': 1037 realm = optarg; 1038 if (!realm) 528 - break; 529 - case 's': 1039 usage(); 1040 params.realm = realm; 1041 params.mask |= KADM5_CONFIG_REALM; 1042 + break; 1043 + case 's': 1044 srvtab = optarg; 1045 if (!srvtab) 1046 usage(); 1047 break; 1048 case 'S': 1049 standalone++; 1050 break; 1051 case 'a': 1052 acl_file_name = optarg; 1053 if (!acl_file_name) 1054 usage(); 1055 break; 1056 case '?': 1057 default: 1058 usage(); 1059 } 1060 1061 } 1062 /* 1063 * If not in debug mode, switch com_err reporting to syslog 1064 */ 1065 if (! debug) { 1066 openlog("kpropd", LOG_PID | LOG_ODELAY, SYSLOG_CLASS); 1067 set_com_err_hook(kpropd_com_err_proc); 1068 } 1069 /* 1070 * Get my hostname, so we can construct my service name 1071 */ 1072 retval = krb5_sname_to_principal(kpropd_context, 1073 NULL, KPROP_SERVICE_NAME, 1074 KRB5_NT_SRV_HST, &server); 1075 if (retval) { 1076 com_err(progname, retval, 1077 gettext("While trying to construct my service name")); 1078 exit(1); 1079 } 1080 if (realm) { 1081 (void) krb5_xfree(krb5_princ_realm(context, server)->data); 1082 krb5_princ_set_realm_length(context, server, strlen(realm)); 1083 krb5_princ_set_realm_data(context, server, strdup(realm)); 1084 } 1085 /* 1086 * Construct the name of the temporary file. 1087 */ 1088 if ((temp_file_name = (char *) malloc(strlen(file) + 1089 strlen(tmp) + 1)) == NULL) { 1090 com_err(progname, ENOMEM, 1091 gettext("while allocating filename for temp file")); 1092 exit(1); 1093 } 1094 strcpy(temp_file_name, file); 1095 strcat(temp_file_name, tmp); 1096 + 1097 + retval = kadm5_get_config_params(kpropd_context, NULL, NULL, ¶ms, 1098 + ¶ms); 1099 + if (retval) { 1100 + com_err(progname, retval, gettext("while initializing")); 1101 + exit(1); 1102 } 1103 + if (params.iprop_enabled == TRUE) { 1104 + ulog_set_role(kpropd_context, IPROP_SLAVE); 1105 + poll_time = params.iprop_polltime; 1106 1107 + if (ulog_map(kpropd_context, ¶ms, FKPROPD)) { 1108 + com_err(progname, errno, 1109 + gettext("Unable to map log!\n")); 1110 + exit(1); 1111 + } 1112 + } 1113 + 1114 /* 1115 + * Grab the realm info and check if iprop is enabled. 1116 + */ 1117 + if (def_realm == NULL) { 1118 + retval = krb5_get_default_realm(kpropd_context, &def_realm); 1119 + if (retval) { 1120 + com_err(progname, retval, 1121 + gettext("Unable to get default realm")); 1122 + exit(1); 1123 + } 1124 + } 1125 + } 1126 + 1127 + /* 1128 * Figure out who's calling on the other end of the connection.... 1129 */ 1130 void 1131 kerberos_authenticate(context, fd, clientp, etype, ss) 1132 krb5_context context; 1133 int fd; 1134 krb5_principal * clientp; 1135 krb5_enctype * etype; 1136 struct sockaddr_storage ss; 1137 { 1138 krb5_error_code retval; 1139 krb5_ticket * ticket; 1140 struct sockaddr_storage r_ss; 1141 int ss_length; 1142 krb5_keytab keytab = NULL; 1143 1144 /* 1145 * Set recv_addr and send_addr 1146 */ 1147 if (cvtkaddr(&ss, &sender_addr) == NULL) { 1148 com_err(progname, errno, 1149 gettext("while converting socket address")); 1150 exit(1); 1151 } 1152 1153 ss_length = sizeof (r_ss); 1154 if (getsockname(fd, (struct sockaddr *) &r_ss, &ss_length)) { 1155 com_err(progname, errno, 1156 gettext("while getting local socket address")); 1157 exit(1); 1158 } 1159 1160 if (cvtkaddr(&r_ss, &receiver_addr) == NULL) { 1161 com_err(progname, errno, 1162 gettext("while converting socket address")); 1163 exit(1); 1164 } 1165 1166 if (debug) { 1167 char *name; 1168 if (retval = krb5_unparse_name(context, server, &name)) { 1169 com_err(progname, retval, gettext("While unparsing server name")); 1170 exit(1); 1171 } 1172 printf(gettext("krb5_recvauth(%d, %s, %s, ...)\n"), fd, kprop_version, 1173 name); 1174 free(name); 1175 } 1176 1177 if (retval = krb5_auth_con_init(context, &auth_context)) { 1178 syslog(LOG_ERR, gettext("Error in krb5_auth_con_init: %s"), 1179 error_message(retval)); 1180 exit(1); 1181 } 1182 1183 if (retval = krb5_auth_con_setflags(context, auth_context, 1184 KRB5_AUTH_CONTEXT_DO_SEQUENCE)) { 1185 syslog(LOG_ERR, gettext("Error in krb5_auth_con_setflags: %s"), 1186 error_message(retval)); 1187 exit(1); 1188 } 1189 1190 if (retval = krb5_auth_con_setaddrs(context, auth_context, &receiver_addr, 1191 &sender_addr)) { 1192 syslog(LOG_ERR, gettext("Error in krb5_auth_con_setaddrs: %s"), 1193 error_message(retval)); 1194 exit(1); 1195 } 1196 1197 if (srvtab) { 1198 if (retval = krb5_kt_resolve(context, srvtab, &keytab)) { 1199 syslog(LOG_ERR, gettext("Error in krb5_kt_resolve: %s"), error_message(retval)); 1200 exit(1); 1201 } 1202 } 1203 1204 if (retval = krb5_recvauth(context, &auth_context, (void *) &fd, 1205 kprop_version, server, 0, keytab, &ticket)){ 1206 syslog(LOG_ERR, gettext("Error in krb5_recvauth: %s"), 1207 error_message(retval)); 1208 exit(1); 1209 } 1210 1211 if (retval = krb5_copy_principal(context, 1212 ticket->enc_part2->client, clientp)) { 1213 syslog(LOG_ERR, gettext("Error in krb5_copy_prinicpal: %s"), 1214 error_message(retval)); 1215 exit(1); 1216 } 1217 1218 *etype = ticket->enc_part.enctype; 1219 1220 if (debug) { 1221 char * name; 1222 char etypebuf[100]; 1223 1224 if (retval = krb5_unparse_name(context, *clientp, &name)) { 1225 com_err(progname, retval, 1226 gettext("While unparsing client name")); 1227 exit(1); 1228 } 1229 1230 if (retval = krb5_enctype_to_string(*etype, etypebuf, 1231 sizeof(etypebuf))) { 1232 com_err(progname, retval, gettext("While unparsing ticket etype")); 1233 exit(1); 1234 } 1235 1236 printf("authenticated client: %s (etype == %s)\n", name, etypebuf); 1237 free(name); 1238 } 1239 1240 krb5_free_ticket(context, ticket); 1241 } ----Unchanged portion omitted---- 1488 1489 void 1490 load_database(context, kdb5_util, database_file_name) 1491 krb5_context context; 1492 char *kdb5_util; 1493 char *database_file_name; 1494 { 1495 static char *edit_av[10]; 1496 int error_ret, save_stderr; 1497 int child_pid; 1498 int count; 1499 int waitb; 1500 krb5_error_code retval; 1501 + kdb_log_context *log_ctx; 1502 1503 if (debug) 1504 printf(gettext("calling kdb5_util to load database\n")); 1505 1506 + log_ctx = context->kdblog_context; 1507 + 1508 edit_av[0] = kdb5_util; 1509 count = 1; 1510 if (realm) { 1511 edit_av[count++] = "-r"; 1512 edit_av[count++] = realm; 1513 } 1514 edit_av[count++] = "load"; 1515 if (kerb_database) { 1516 edit_av[count++] = "-d"; 1517 edit_av[count++] = kerb_database; 1518 } 1519 + 1520 + if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) { 1521 + edit_av[count++] = "-i"; 1522 + } 1523 edit_av[count++] = database_file_name; 1524 edit_av[count++] = NULL; 1525 1526 switch(child_pid = fork()) { 1527 case -1: 1528 com_err(progname, errno, gettext("while trying to fork %s"), 1529 kdb5_util); 1530 exit(1); 1531 /*NOTREACHED*/ 1532 case 0: 1533 if (!debug) { 1534 save_stderr = dup(2); 1535 close(0); 1536 close(1); 1537 close(2); 1538 open("/dev/null", O_RDWR); 1539 dup(0); 1540 dup(0); 1541 } 1542 1543 execv(kdb5_util, edit_av); 1544 retval = errno; 1545 if (!debug) 1546 dup2(save_stderr, 2); 1547 com_err(progname, retval, gettext("while trying to exec %s"), 1548 kdb5_util); 1549 _exit(1); 1550 /*NOTREACHED*/ 1551 default: 1552 if (debug) 1553 printf(gettext("Child PID is %d\n"), child_pid); 1554 if (wait(&waitb) < 0) { 1555 com_err(progname, errno, gettext("while waiting for %s"), 1556 kdb5_util); 1557 exit(1); 1558 } 1559 } 1560 1561 if ((error_ret = WEXITSTATUS(waitb)) != 0) { 1562 com_err(progname, 0, 1563 gettext("%s returned a bad exit status (%d)"), kdb5_util, 1564 error_ret); 1565 exit(1); 1566 } 1567 return; 1568 } ----Unchanged portion omitted----