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