Home | History | Annotate | Download | only in slave
      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 *)&params, 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 				    &params,
    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 *) &params, 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, &params,
   1363 	    &params);
   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, &params, 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