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
usage()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
main(argc,argv)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
resync_alarm(int sn)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
do_standalone(iprop_role iproprole)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
doit(fd)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 */
do_iprop(kdb_log_context * log_ctx)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 *)¶ms, 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 ¶ms,
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 */
backoff_from_master(int * cnt)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 */
convert_polltime(char * pollstr)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
kpropd_com_err_proc(whoami,code,fmt,args)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
PRS(argc,argv)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 *) ¶ms, 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, ¶ms,
1363 ¶ms);
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, ¶ms, 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
kerberos_authenticate(context,fd,clientp,etype,ss)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
authorized_principal(context,p,auth_etype)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
recv_database(context,fd,database_fd,confmsg)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
send_error(context,fd,err_code,err_text)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
recv_error(context,inbuf)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
load_database(context,kdb_util,database_file_name)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