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