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