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