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