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