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