1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kprop/kpropd.c */
3 /*
4 * Copyright (C) 1998 by the FundsXpress, INC.
5 *
6 * All rights reserved.
7 *
8 * Export of this software from the United States of America may require
9 * a specific license from the United States Government. It is the
10 * responsibility of any person or organization contemplating export to
11 * obtain such a license before exporting.
12 *
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of FundsXpress. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. FundsXpress makes no representations about the suitability of
21 * this software for any purpose. It is provided "as is" without express
22 * or implied warranty.
23 *
24 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
26 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27 */
28
29 /*
30 * Copyright 1990,1991,2007 by the Massachusetts Institute of Technology.
31 * All Rights Reserved.
32 *
33 * Export of this software from the United States of America may
34 * require a specific license from the United States Government.
35 * It is the responsibility of any person or organization contemplating
36 * export to obtain such a license before exporting.
37 *
38 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
39 * distribute this software and its documentation for any purpose and
40 * without fee is hereby granted, provided that the above copyright
41 * notice appear in all copies and that both that copyright notice and
42 * this permission notice appear in supporting documentation, and that
43 * the name of M.I.T. not be used in advertising or publicity pertaining
44 * to distribution of the software without specific, written prior
45 * permission. Furthermore if you modify this software you must label
46 * your software as modified software and not distribute it in such a
47 * fashion that it might be confused with the original M.I.T. software.
48 * M.I.T. makes no representations about the suitability of
49 * this software for any purpose. It is provided "as is" without express
50 * or implied warranty.
51 */
52
53
54 #include "k5-int.h"
55 #include "com_err.h"
56 #include "fake-addrinfo.h"
57
58 #include <inttypes.h>
59 #include <locale.h>
60 #include <ctype.h>
61 #include <sys/file.h>
62 #include <signal.h>
63 #include <fcntl.h>
64 #include <sys/types.h>
65 #include <sys/time.h>
66 #include <sys/stat.h>
67 #include <sys/socket.h>
68 #include <sys/wait.h>
69 #include <netinet/in.h>
70 #include <arpa/inet.h>
71 #include <sys/param.h>
72 #include <netdb.h>
73 #include <syslog.h>
74
75 #include "kprop.h"
76 #include <iprop_hdr.h>
77 #include "iprop.h"
78 #include <kadm5/admin.h>
79 #include <kdb_log.h>
80
81 #ifndef GETSOCKNAME_ARG3_TYPE
82 #define GETSOCKNAME_ARG3_TYPE unsigned int
83 #endif
84 #ifndef GETPEERNAME_ARG3_TYPE
85 #define GETPEERNAME_ARG3_TYPE unsigned int
86 #endif
87
88 #if defined(NEED_DAEMON_PROTO)
89 extern int daemon(int, int);
90 #endif
91
92 #define SYSLOG_CLASS LOG_DAEMON
93
94 int runonce = 0;
95
96 /*
97 * This struct simulates the use of _kadm5_server_handle_t
98 *
99 * This is a COPY of kadm5_server_handle_t from
100 * lib/kadm5/clnt/client_internal.h!
101 */
102 typedef struct _kadm5_iprop_handle_t {
103 krb5_ui_4 magic_number;
104 krb5_ui_4 struct_version;
105 krb5_ui_4 api_version;
106 char *cache_name;
107 int destroy_cache;
108 CLIENT *clnt;
109 krb5_context context;
110 kadm5_config_params params;
111 struct _kadm5_iprop_handle_t *lhandle;
112 } *kadm5_iprop_handle_t;
113
114 static char *kprop_version = KPROP_PROT_VERSION;
115
116 static kadm5_config_params params;
117
118 static char *progname;
119 static int debug = 0;
120 static int nodaemon = 0;
121 static char *keytab_path = NULL;
122 static int standalone = 0;
123 static const char *pid_file = NULL;
124
125 static pid_t fullprop_child = (pid_t)-1;
126
127 static krb5_principal server; /* This is our server principal name */
128 static krb5_principal client; /* This is who we're talking to */
129 static krb5_context kpropd_context;
130 static krb5_auth_context auth_context;
131 static char *realm = NULL; /* Our realm */
132 static char *def_realm = NULL; /* Ref pointer for default realm */
133 static char *file = KPROPD_DEFAULT_FILE;
134 static char *temp_file_name;
135 static char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL;
136 static char *kerb_database = NULL;
137 static char *acl_file_name = KPROPD_ACL_FILE;
138
139 static krb5_address *receiver_addr;
140 static const char *port = KPROP_SERVICE;
141
142 static char **db_args = NULL;
143 static size_t db_args_size = 0;
144
145 static void parse_args(int argc, char **argv);
146 static void do_standalone(void);
147 static void doit(int fd);
148 static krb5_error_code do_iprop(void);
149 static void kerberos_authenticate(krb5_context context, int fd,
150 krb5_principal *clientp, krb5_enctype *etype,
151 struct sockaddr_storage *my_sin);
152 static krb5_boolean authorized_principal(krb5_context context,
153 krb5_principal p,
154 krb5_enctype auth_etype);
155 static void recv_database(krb5_context context, int fd, int database_fd,
156 krb5_data *confmsg);
157 static void load_database(krb5_context context, char *kdb_util,
158 char *database_file_name);
159 static void send_error(krb5_context context, int fd, krb5_error_code err_code,
160 char *err_text);
161 static void recv_error(krb5_context context, krb5_data *inbuf);
162 static unsigned int backoff_from_primary(int *cnt);
163 static kadm5_ret_t kadm5_get_kiprop_host_srv_name(krb5_context context,
164 const char *realm_name,
165 char **host_service_name);
166
167 static void
usage(void)168 usage(void)
169 {
170 fprintf(stderr,
171 _("\nUsage: %s [-r realm] [-s keytab] [-d] [-D] [-S]\n"
172 "\t[-f replica_file] [-F kerberos_db_file ]\n"
173 "\t[-p kdb5_util_pathname] [-x db_args]* [-P port]\n"
174 "\t[-a acl_file] [-A admin_server] [--pid-file=pid_file]\n"),
175 progname);
176 exit(1);
177 }
178
179 static krb5_error_code
write_pid_file(const char * path)180 write_pid_file(const char *path)
181 {
182 FILE *fp;
183 unsigned long pid;
184 int st1, st2;
185
186 fp = fopen(path, "w");
187 if (fp == NULL)
188 return errno;
189 pid = (unsigned long)getpid();
190 st1 = (fprintf(fp, "%ld\n", pid) < 0) ? errno : 0;
191 st2 = (fclose(fp) == EOF) ? errno : 0;
192 return st1 ? st1 : st2;
193 }
194
195 typedef void (*sig_handler_fn)(int sig);
196
197 static void
signal_wrapper(int sig,sig_handler_fn handler)198 signal_wrapper(int sig, sig_handler_fn handler)
199 {
200 #ifdef POSIX_SIGNALS
201 struct sigaction s_action;
202
203 memset(&s_action, 0, sizeof(s_action));
204 sigemptyset(&s_action.sa_mask);
205 s_action.sa_handler = handler;
206 sigaction(sig, &s_action, NULL);
207 #else
208 signal(sig, handler);
209 #endif
210 }
211
212 static void
alarm_handler(int sig)213 alarm_handler(int sig)
214 {
215 static char *timeout_msg = "Full propagation timed out\n";
216
217 write(STDERR_FILENO, timeout_msg, strlen(timeout_msg));
218 exit(1);
219 }
220
221 static void
usr1_handler(int sig)222 usr1_handler(int sig)
223 {
224 /* Nothing to do, just let the signal interrupt sleep(). */
225 }
226
227 static void
kill_do_standalone(int sig)228 kill_do_standalone(int sig)
229 {
230 if (fullprop_child > 0) {
231 if (debug) {
232 fprintf(stderr, _("Killing fullprop child (%d)\n"),
233 (int)fullprop_child);
234 }
235 kill(fullprop_child, sig);
236 }
237 /* Make sure our exit status code reflects our having been signaled */
238 signal_wrapper(sig, SIG_DFL);
239 kill(getpid(), sig);
240 }
241
242 static void
atexit_kill_do_standalone(void)243 atexit_kill_do_standalone(void)
244 {
245 if (fullprop_child > 0)
246 kill(fullprop_child, SIGHUP);
247 }
248
249 int
main(int argc,char ** argv)250 main(int argc, char **argv)
251 {
252 krb5_error_code retval;
253 kdb_log_context *log_ctx;
254 int devnull, sock;
255 struct stat st;
256
257 setlocale(LC_ALL, "");
258 parse_args(argc, argv);
259
260 if (fstat(0, &st) == -1) {
261 com_err(progname, errno, _("while checking if stdin is a socket"));
262 exit(1);
263 }
264 /*
265 * Detect whether we're running from inetd; if not then we're in
266 * standalone mode.
267 */
268 standalone = !S_ISSOCK(st.st_mode);
269
270 log_ctx = kpropd_context->kdblog_context;
271
272 signal_wrapper(SIGPIPE, SIG_IGN);
273
274 if (standalone) {
275 /* "ready" is a sentinel for the test framework. */
276 if (!debug && !nodaemon) {
277 daemon(0, 0);
278 } else {
279 printf(_("ready\n"));
280 fflush(stdout);
281 }
282 if (pid_file != NULL) {
283 retval = write_pid_file(pid_file);
284 if (retval) {
285 syslog(LOG_ERR, _("Could not write pid file %s: %s"),
286 pid_file, strerror(errno));
287 exit(1);
288 }
289 }
290 } else {
291 /*
292 * We're an inetd nowait service. Let's not risk anything
293 * read/write from/to the inetd socket unintentionally.
294 */
295 devnull = open("/dev/null", O_RDWR);
296 if (devnull == -1) {
297 syslog(LOG_ERR, _("Could not open /dev/null: %s"),
298 strerror(errno));
299 exit(1);
300 }
301
302 sock = dup(0);
303 if (sock == -1) {
304 syslog(LOG_ERR, _("Could not dup the inetd socket: %s"),
305 strerror(errno));
306 exit(1);
307 }
308
309 dup2(devnull, STDIN_FILENO);
310 dup2(devnull, STDOUT_FILENO);
311 dup2(devnull, STDERR_FILENO);
312 close(devnull);
313 doit(sock);
314 exit(0);
315 }
316
317 if (log_ctx == NULL || log_ctx->iproprole != IPROP_REPLICA) {
318 do_standalone();
319 /* do_standalone() should never return */
320 assert(0);
321 }
322
323 /*
324 * This is the iprop case. We'll fork a child to run do_standalone(). The
325 * parent will run do_iprop(). We try to kill the child if we get killed.
326 * Catch SIGUSR1, which can be used to interrupt the sleep timer and force
327 * an iprop request.
328 */
329 signal_wrapper(SIGHUP, kill_do_standalone);
330 signal_wrapper(SIGINT, kill_do_standalone);
331 signal_wrapper(SIGQUIT, kill_do_standalone);
332 signal_wrapper(SIGTERM, kill_do_standalone);
333 signal_wrapper(SIGSEGV, kill_do_standalone);
334 signal_wrapper(SIGUSR1, usr1_handler);
335 atexit(atexit_kill_do_standalone);
336 fullprop_child = fork();
337 switch (fullprop_child) {
338 case -1:
339 com_err(progname, errno, _("do_iprop failed.\n"));
340 break;
341 case 0:
342 do_standalone();
343 /* do_standalone() should never return */
344 /* NOTREACHED */
345 break;
346 default:
347 retval = do_iprop();
348 /* do_iprop() can return due to failures and runonce. */
349 kill(fullprop_child, SIGHUP);
350 wait(NULL);
351 if (retval)
352 com_err(progname, retval, _("do_iprop failed.\n"));
353 else
354 exit(0);
355 }
356
357 exit(1);
358 }
359
360 /* Use getaddrinfo to determine a wildcard listener address, preferring
361 * IPv6 if available. */
362 static int
get_wildcard_addr(struct addrinfo ** res)363 get_wildcard_addr(struct addrinfo **res)
364 {
365 struct addrinfo hints;
366 int error;
367
368 memset(&hints, 0, sizeof(hints));
369 hints.ai_socktype = SOCK_STREAM;
370 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
371 hints.ai_family = AF_INET6;
372 error = getaddrinfo(NULL, port, &hints, res);
373 if (error == 0)
374 return 0;
375 hints.ai_family = AF_INET;
376 return getaddrinfo(NULL, port, &hints, res);
377 }
378
379 static void
do_standalone(void)380 do_standalone(void)
381 {
382 struct sockaddr_in frominet;
383 struct addrinfo *res;
384 GETPEERNAME_ARG3_TYPE fromlen;
385 int finet, s, ret, error, val, status;
386 pid_t child_pid;
387 pid_t wait_pid;
388
389 error = get_wildcard_addr(&res);
390 if (error != 0) {
391 fprintf(stderr, _("getaddrinfo: %s\n"), gai_strerror(error));
392 exit(1);
393 }
394
395 finet = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
396 if (finet < 0) {
397 com_err(progname, errno, _("while obtaining socket"));
398 exit(1);
399 }
400
401 val = 1;
402 if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
403 com_err(progname, errno, _("while setting SO_REUSEADDR option"));
404
405 #if defined(IPV6_V6ONLY)
406 /* Make sure dual-stack support is enabled on IPv6 listener sockets if
407 * possible. */
408 val = 0;
409 if (res->ai_family == AF_INET6 &&
410 setsockopt(finet, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) < 0)
411 com_err(progname, errno, _("while unsetting IPV6_V6ONLY option"));
412 #endif
413
414 ret = bind(finet, res->ai_addr, res->ai_addrlen);
415 if (ret < 0) {
416 com_err(progname, errno, _("while binding listener socket"));
417 exit(1);
418 }
419 if (listen(finet, 5) < 0) {
420 com_err(progname, errno, "in listen call");
421 exit(1);
422 }
423 for (;;) {
424 memset(&frominet, 0, sizeof(frominet));
425 fromlen = sizeof(frominet);
426 if (debug)
427 fprintf(stderr, _("waiting for a kprop connection\n"));
428 s = accept(finet, (struct sockaddr *) &frominet, &fromlen);
429
430 if (s < 0) {
431 int e = errno;
432 if (e != EINTR) {
433 com_err(progname, e, _("while accepting connection"));
434 }
435 }
436 child_pid = fork();
437 switch (child_pid) {
438 case -1:
439 com_err(progname, errno, _("while forking"));
440 exit(1);
441 case 0:
442 close(finet);
443
444 doit(s);
445 close(s);
446 _exit(0);
447 default:
448 do {
449 wait_pid = waitpid(child_pid, &status, 0);
450 } while (wait_pid == -1 && errno == EINTR);
451 if (wait_pid == -1) {
452 /* Something bad happened; panic. */
453 if (debug) {
454 fprintf(stderr, _("waitpid() failed to wait for doit() "
455 "(%d %s)\n"), errno, strerror(errno));
456 }
457 com_err(progname, errno,
458 _("while waiting to receive database"));
459 exit(1);
460 }
461 if (debug) {
462 fprintf(stderr, _("Database load process for full propagation "
463 "completed.\n"));
464 }
465
466 close(s);
467
468 /* If we are the fullprop child in iprop mode, notify the parent
469 * process that it should poll for incremental updates. */
470 if (fullprop_child == 0)
471 kill(getppid(), SIGUSR1);
472 else if (runonce)
473 exit(0);
474 }
475 }
476 exit(0);
477 }
478
479 static void
doit(int fd)480 doit(int fd)
481 {
482 struct sockaddr_storage from;
483 int on = 1;
484 GETPEERNAME_ARG3_TYPE fromlen;
485 krb5_error_code retval;
486 krb5_data confmsg;
487 int lock_fd;
488 mode_t omask;
489 krb5_enctype etype;
490 int database_fd;
491 char host[INET6_ADDRSTRLEN + 1];
492
493 signal_wrapper(SIGALRM, alarm_handler);
494 alarm(params.iprop_resync_timeout);
495 fromlen = sizeof(from);
496 if (getpeername(fd, (struct sockaddr *)&from, &fromlen) < 0) {
497 #ifdef ENOTSOCK
498 if (errno == ENOTSOCK && fd == 0 && !standalone) {
499 fprintf(stderr,
500 _("%s: Standard input does not appear to be a network "
501 "socket.\n"
502 "\t(Not run from inetd, and missing the -S option?)\n"),
503 progname);
504 exit(1);
505 }
506 #endif
507 fprintf(stderr, "%s: ", progname);
508 perror("getpeername");
509 exit(1);
510 }
511 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) {
512 com_err(progname, errno,
513 _("while attempting setsockopt (SO_KEEPALIVE)"));
514 }
515
516 if (getnameinfo((const struct sockaddr *) &from, fromlen,
517 host, sizeof(host), NULL, 0, 0) == 0) {
518 syslog(LOG_INFO, _("Connection from %s"), host);
519 if (debug)
520 fprintf(stderr, "Connection from %s\n", host);
521 }
522
523 /*
524 * Now do the authentication
525 */
526 kerberos_authenticate(kpropd_context, fd, &client, &etype, &from);
527
528 if (!authorized_principal(kpropd_context, client, etype)) {
529 char *name;
530
531 retval = krb5_unparse_name(kpropd_context, client, &name);
532 if (retval) {
533 com_err(progname, retval, "While unparsing client name");
534 exit(1);
535 }
536 if (debug) {
537 fprintf(stderr,
538 _("Rejected connection from unauthorized principal %s\n"),
539 name);
540 }
541 syslog(LOG_WARNING,
542 _("Rejected connection from unauthorized principal %s"),
543 name);
544 free(name);
545 exit(1);
546 }
547 omask = umask(077);
548 lock_fd = open(temp_file_name, O_RDWR | O_CREAT, 0600);
549 (void)umask(omask);
550 retval = krb5_lock_file(kpropd_context, lock_fd,
551 KRB5_LOCKMODE_EXCLUSIVE | KRB5_LOCKMODE_DONTBLOCK);
552 if (retval) {
553 com_err(progname, retval, _("while trying to lock '%s'"),
554 temp_file_name);
555 exit(1);
556 }
557 database_fd = open(temp_file_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
558 if (database_fd < 0) {
559 com_err(progname, errno, _("while opening database file, '%s'"),
560 temp_file_name);
561 exit(1);
562 }
563 recv_database(kpropd_context, fd, database_fd, &confmsg);
564 if (rename(temp_file_name, file)) {
565 com_err(progname, errno, _("while renaming %s to %s"),
566 temp_file_name, file);
567 exit(1);
568 }
569 retval = krb5_lock_file(kpropd_context, lock_fd, KRB5_LOCKMODE_SHARED);
570 if (retval) {
571 com_err(progname, retval, _("while downgrading lock on '%s'"),
572 temp_file_name);
573 exit(1);
574 }
575 load_database(kpropd_context, kdb5_util, file);
576 retval = krb5_lock_file(kpropd_context, lock_fd, KRB5_LOCKMODE_UNLOCK);
577 if (retval) {
578 com_err(progname, retval, _("while unlocking '%s'"), temp_file_name);
579 exit(1);
580 }
581 close(lock_fd);
582
583 /*
584 * Send the acknowledgement message generated in
585 * recv_database, then close the socket.
586 */
587 retval = krb5_write_message(kpropd_context, &fd, &confmsg);
588 if (retval) {
589 krb5_free_data_contents(kpropd_context, &confmsg);
590 com_err(progname, retval, _("while sending # of received bytes"));
591 exit(1);
592 }
593 krb5_free_data_contents(kpropd_context, &confmsg);
594 if (close(fd) < 0) {
595 com_err(progname, errno,
596 _("while trying to close database file"));
597 exit(1);
598 }
599
600 exit(0);
601 }
602
603 /* Default timeout can be changed using clnt_control() */
604 static struct timeval full_resync_timeout = { 25, 0 };
605
606 static kdb_fullresync_result_t *
full_resync(CLIENT * clnt)607 full_resync(CLIENT *clnt)
608 {
609 static kdb_fullresync_result_t clnt_res;
610 uint32_t vers = IPROPX_VERSION_1; /* max version we support */
611 enum clnt_stat status;
612
613 memset(&clnt_res, 0, sizeof(clnt_res));
614
615 status = clnt_call(clnt, IPROP_FULL_RESYNC_EXT, (xdrproc_t)xdr_u_int32,
616 &vers, (xdrproc_t)xdr_kdb_fullresync_result_t,
617 &clnt_res, full_resync_timeout);
618 if (status == RPC_PROCUNAVAIL) {
619 status = clnt_call(clnt, IPROP_FULL_RESYNC, (xdrproc_t)xdr_void,
620 &vers, (xdrproc_t)xdr_kdb_fullresync_result_t,
621 &clnt_res, full_resync_timeout);
622 }
623
624 return (status == RPC_SUCCESS) ? &clnt_res : NULL;
625 }
626
627 /*
628 * Beg for incrementals from the KDC.
629 *
630 * Returns 0 on success IFF runonce is true.
631 * Returns non-zero on failure due to errors.
632 */
633 krb5_error_code
do_iprop(void)634 do_iprop(void)
635 {
636 kadm5_ret_t retval;
637 krb5_principal iprop_svc_principal = NULL;
638 void *server_handle = NULL;
639 char *iprop_svc_princstr = NULL, *primary_svc_princstr = NULL;
640 unsigned int pollin, backoff_time;
641 int backoff_cnt = 0, reinit_cnt = 0;
642 struct timeval iprop_start, iprop_end;
643 unsigned long usec;
644 time_t frrequested = 0, now;
645 kdb_incr_result_t *incr_ret;
646 kdb_last_t mylast;
647 kdb_fullresync_result_t *full_ret;
648 kadm5_iprop_handle_t handle;
649
650 if (debug)
651 fprintf(stderr, _("Incremental propagation enabled\n"));
652
653 pollin = params.iprop_poll_time;
654 if (pollin == 0)
655 pollin = 10;
656
657 retval = kadm5_get_kiprop_host_srv_name(kpropd_context, realm,
658 &primary_svc_princstr);
659 if (retval) {
660 com_err(progname, retval, _("%s: unable to get kiprop host based "
661 "service name for realm %s\n"),
662 progname, realm);
663 goto done;
664 }
665
666 retval = sn2princ_realm(kpropd_context, NULL, KIPROP_SVC_NAME, realm,
667 &iprop_svc_principal);
668 if (retval) {
669 com_err(progname, retval,
670 _("while trying to construct host service principal"));
671 goto done;
672 }
673
674 retval = krb5_unparse_name(kpropd_context, iprop_svc_principal,
675 &iprop_svc_princstr);
676 if (retval) {
677 com_err(progname, retval,
678 _("while canonicalizing principal name"));
679 goto done;
680 }
681
682 reinit:
683 /*
684 * Authentication, initialize rpcsec_gss handle etc.
685 */
686 if (debug) {
687 fprintf(stderr, _("Initializing kadm5 as client %s\n"),
688 iprop_svc_princstr);
689 }
690 retval = kadm5_init_with_skey(kpropd_context, iprop_svc_princstr,
691 keytab_path,
692 primary_svc_princstr,
693 ¶ms,
694 KADM5_STRUCT_VERSION,
695 KADM5_API_VERSION_4,
696 db_args,
697 &server_handle);
698
699 if (retval) {
700 if (debug)
701 fprintf(stderr, _("kadm5 initialization failed!\n"));
702 if (retval == KADM5_RPC_ERROR) {
703 reinit_cnt++;
704 if (server_handle)
705 kadm5_destroy(server_handle);
706 server_handle = NULL;
707 handle = NULL;
708
709 com_err(progname, retval, _(
710 "while attempting to connect"
711 " to primary KDC ... retrying"));
712 backoff_time = backoff_from_primary(&reinit_cnt);
713 if (debug) {
714 fprintf(stderr, _("Sleeping %d seconds to re-initialize "
715 "kadm5 (RPC ERROR)\n"), backoff_time);
716 }
717 sleep(backoff_time);
718 goto reinit;
719 } else {
720 if (retval == KADM5_BAD_CLIENT_PARAMS ||
721 retval == KADM5_BAD_SERVER_PARAMS) {
722 com_err(progname, retval,
723 _("while initializing %s interface"),
724 progname);
725
726 usage();
727 }
728 reinit_cnt++;
729 com_err(progname, retval,
730 _("while initializing %s interface, retrying"),
731 progname);
732 backoff_time = backoff_from_primary(&reinit_cnt);
733 if (debug) {
734 fprintf(stderr, _("Sleeping %d seconds to re-initialize "
735 "kadm5 (krb5kdc not running?)\n"),
736 backoff_time);
737 }
738 sleep(backoff_time);
739 goto reinit;
740 }
741 }
742
743 if (debug)
744 fprintf(stderr, _("kadm5 initialization succeeded\n"));
745
746 /*
747 * Reset re-initialization count to zero now.
748 */
749 reinit_cnt = backoff_time = 0;
750
751 /*
752 * Reset the handle to the correct type for the RPC call
753 */
754 handle = server_handle;
755
756 for (;;) {
757 incr_ret = NULL;
758 full_ret = NULL;
759
760 /*
761 * Get the most recent ulog entry sno + ts, which
762 * we package in the request to the primary KDC
763 */
764 retval = ulog_get_last(kpropd_context, &mylast);
765 if (retval) {
766 com_err(progname, retval, _("reading update log header"));
767 goto done;
768 }
769
770 /*
771 * Loop continuously on an iprop_get_updates_1(),
772 * so that we can keep probing the primary for updates
773 * or (if needed) do a full resync of the krb5 db.
774 */
775
776 if (debug) {
777 fprintf(stderr, _("Calling iprop_get_updates_1 "
778 "(sno=%u sec=%u usec=%u)\n"),
779 (unsigned int)mylast.last_sno,
780 (unsigned int)mylast.last_time.seconds,
781 (unsigned int)mylast.last_time.useconds);
782 }
783 gettimeofday(&iprop_start, NULL);
784 incr_ret = iprop_get_updates_1(&mylast, handle->clnt);
785 if (incr_ret == (kdb_incr_result_t *)NULL) {
786 clnt_perror(handle->clnt,
787 _("iprop_get_updates call failed"));
788 if (server_handle)
789 kadm5_destroy(server_handle);
790 server_handle = NULL;
791 handle = (kadm5_iprop_handle_t)NULL;
792 if (debug) {
793 fprintf(stderr, _("Reinitializing iprop because get updates "
794 "failed\n"));
795 }
796 goto reinit;
797 }
798
799 switch (incr_ret->ret) {
800
801 case UPDATE_FULL_RESYNC_NEEDED:
802 /*
803 * If we're already asked for a full resync and we still
804 * need one and the last one hasn't timed out then just keep
805 * asking for updates as eventually the resync will finish
806 * (or, if it times out we'll just try again). Note that
807 * doit() also applies a timeout to the full resync, thus
808 * it's OK for us to do the same here.
809 */
810 now = time(NULL);
811 if (frrequested &&
812 (now - frrequested) < params.iprop_resync_timeout) {
813 if (debug)
814 fprintf(stderr, _("Still waiting for full resync\n"));
815 break;
816 } else {
817 frrequested = now;
818 if (debug)
819 fprintf(stderr, _("Full resync needed\n"));
820 syslog(LOG_INFO, _("kpropd: Full resync needed."));
821
822 full_ret = full_resync(handle->clnt);
823 if (full_ret == NULL) {
824 clnt_perror(handle->clnt,
825 _("iprop_full_resync call failed"));
826 kadm5_destroy(server_handle);
827 server_handle = NULL;
828 handle = NULL;
829 goto reinit;
830 }
831 }
832
833 switch (full_ret->ret) {
834 case UPDATE_OK:
835 if (debug)
836 fprintf(stderr, _("Full resync request granted\n"));
837 syslog(LOG_INFO, _("Full resync request granted."));
838 backoff_cnt = 0;
839 break;
840
841 case UPDATE_BUSY:
842 /*
843 * Exponential backoff
844 */
845 if (debug)
846 fprintf(stderr, _("Exponential backoff\n"));
847 backoff_cnt++;
848 break;
849
850 case UPDATE_PERM_DENIED:
851 if (debug)
852 fprintf(stderr, _("Full resync permission denied\n"));
853 syslog(LOG_ERR, _("Full resync, permission denied."));
854 goto error;
855
856 case UPDATE_ERROR:
857 if (debug)
858 fprintf(stderr, _("Full resync error from primary\n"));
859 syslog(LOG_ERR, _(" Full resync, "
860 "error returned from primary KDC."));
861 goto error;
862
863 default:
864 backoff_cnt = 0;
865 if (debug) {
866 fprintf(stderr,
867 _("Full resync invalid result from primary\n"));
868 }
869 syslog(LOG_ERR, _("Full resync, "
870 "invalid return from primary KDC."));
871 break;
872 }
873 break;
874
875 case UPDATE_OK:
876 backoff_cnt = 0;
877 frrequested = 0;
878
879 /*
880 * ulog_replay() will convert the ulog updates to db
881 * entries using the kdb conv api and will commit
882 * the entries to the replica kdc database
883 */
884 if (debug) {
885 fprintf(stderr, _("Got incremental updates "
886 "(sno=%u sec=%u usec=%u)\n"),
887 (unsigned int)incr_ret->lastentry.last_sno,
888 (unsigned int)incr_ret->lastentry.last_time.seconds,
889 (unsigned int)incr_ret->lastentry.last_time.useconds);
890 }
891 retval = ulog_replay(kpropd_context, incr_ret, db_args);
892
893 if (retval) {
894 const char *msg =
895 krb5_get_error_message(kpropd_context, retval);
896 if (debug) {
897 fprintf(stderr, _("ulog_replay failed (%s), updates not "
898 "registered\n"), msg);
899 }
900 syslog(LOG_ERR, _("ulog_replay failed (%s), updates "
901 "not registered."), msg);
902 krb5_free_error_message(kpropd_context, msg);
903 break;
904 }
905
906 gettimeofday(&iprop_end, NULL);
907 usec = (iprop_end.tv_sec - iprop_start.tv_sec) * 1000000 +
908 iprop_end.tv_usec - iprop_start.tv_usec;
909 syslog(LOG_INFO, _("Incremental updates: %d updates / %lu us"),
910 incr_ret->updates.kdb_ulog_t_len, usec);
911 if (debug) {
912 fprintf(stderr, _("Incremental updates: %d updates / "
913 "%lu us\n"),
914 incr_ret->updates.kdb_ulog_t_len, usec);
915 }
916 break;
917
918 case UPDATE_PERM_DENIED:
919 if (debug)
920 fprintf(stderr, _("get_updates permission denied\n"));
921 syslog(LOG_ERR, _("get_updates, permission denied."));
922 goto error;
923
924 case UPDATE_ERROR:
925 if (debug)
926 fprintf(stderr, _("get_updates error from primary\n"));
927 syslog(LOG_ERR,
928 _("get_updates, error returned from primary KDC."));
929 goto error;
930
931 case UPDATE_BUSY:
932 /*
933 * Exponential backoff
934 */
935 if (debug)
936 fprintf(stderr, _("get_updates primary busy; backoff\n"));
937 backoff_cnt++;
938 break;
939
940 case UPDATE_NIL:
941 /*
942 * Primary-replica are in sync
943 */
944 if (debug)
945 fprintf(stderr, _("KDC is synchronized with primary.\n"));
946 backoff_cnt = 0;
947 frrequested = 0;
948 break;
949
950 default:
951 backoff_cnt = 0;
952 if (debug) {
953 fprintf(stderr,
954 _("get_updates invalid result from primary\n"));
955 }
956 syslog(LOG_ERR,
957 _("get_updates, invalid return from primary KDC."));
958 break;
959 }
960
961 if (runonce == 1 && incr_ret->ret != UPDATE_FULL_RESYNC_NEEDED)
962 goto done;
963
964 /*
965 * Sleep for the specified poll interval (Default is 2 mts),
966 * or do a binary exponential backoff if we get an
967 * UPDATE_BUSY signal
968 */
969 if (backoff_cnt > 0) {
970 backoff_time = backoff_from_primary(&backoff_cnt);
971 if (debug) {
972 fprintf(stderr, _("Busy signal received "
973 "from primary, backoff for %d secs\n"),
974 backoff_time);
975 }
976 sleep(backoff_time);
977 } else {
978 if (debug) {
979 fprintf(stderr, _("Waiting for %d seconds before checking "
980 "for updates again\n"), pollin);
981 }
982 sleep(pollin);
983 }
984
985 }
986
987
988 error:
989 if (debug)
990 fprintf(stderr, _("ERROR returned by primary, bailing\n"));
991 syslog(LOG_ERR, _("ERROR returned by primary KDC, bailing.\n"));
992 done:
993 free(iprop_svc_princstr);
994 free(primary_svc_princstr);
995 krb5_free_principal(kpropd_context, iprop_svc_principal);
996 krb5_free_default_realm(kpropd_context, def_realm);
997 kadm5_destroy(server_handle);
998 krb5_db_fini(kpropd_context);
999 ulog_fini(kpropd_context);
1000 krb5_free_context(kpropd_context);
1001
1002 return (runonce == 1) ? 0 : 1;
1003 }
1004
1005
1006 /* Do exponential backoff, since primary KDC is BUSY or down. */
1007 static unsigned int
backoff_from_primary(int * cnt)1008 backoff_from_primary(int *cnt)
1009 {
1010 unsigned int btime;
1011
1012 btime = (unsigned int)(2<<(*cnt));
1013 if (btime > MAX_BACKOFF) {
1014 btime = MAX_BACKOFF;
1015 (*cnt)--;
1016 }
1017
1018 return btime;
1019 }
1020
1021 static void
1022 kpropd_com_err_proc(const char *whoami, long code, const char *fmt,
1023 va_list args)
1024 #if !defined(__cplusplus) && (__GNUC__ > 2)
1025 __attribute__((__format__(__printf__, 3, 0)))
1026 #endif
1027 ;
1028
1029 static void
kpropd_com_err_proc(const char * whoami,long code,const char * fmt,va_list args)1030 kpropd_com_err_proc(const char *whoami, long code, const char *fmt,
1031 va_list args)
1032 {
1033 char error_buf[8096];
1034
1035 error_buf[0] = '\0';
1036 if (fmt)
1037 vsnprintf(error_buf, sizeof(error_buf), fmt, args);
1038 syslog(LOG_ERR, "%s%s%s%s%s", whoami ? whoami : "", whoami ? ": " : "",
1039 code ? error_message(code) : "", code ? " " : "", error_buf);
1040 }
1041
1042 static void
parse_args(int argc,char ** argv)1043 parse_args(int argc, char **argv)
1044 {
1045 char **newargs;
1046 int c;
1047 krb5_error_code retval;
1048 enum { PID_FILE = 256 };
1049 struct option long_options[] = {
1050 { "pid-file", 1, NULL, PID_FILE },
1051 { NULL, 0, NULL, 0 },
1052 };
1053
1054 memset(¶ms, 0, sizeof(params));
1055
1056 /* Since we may modify the KDB with ulog_replay(), we must read the KDC
1057 * profile. */
1058 retval = krb5int_init_context_kdc(&kpropd_context);
1059 if (retval) {
1060 com_err(argv[0], retval, _("while initializing krb5"));
1061 exit(1);
1062 }
1063
1064 progname = argv[0];
1065 while ((c = getopt_long(argc, argv, "A:f:F:p:P:r:s:DdSa:tx:",
1066 long_options, NULL)) != -1) {
1067 switch (c) {
1068 case 'A':
1069 params.mask |= KADM5_CONFIG_ADMIN_SERVER;
1070 params.admin_server = optarg;
1071 break;
1072 case 'f':
1073 file = optarg;
1074 break;
1075 case 'F':
1076 kerb_database = optarg;
1077 break;
1078 case 'p':
1079 kdb5_util = optarg;
1080 break;
1081 case 'P':
1082 port = optarg;
1083 break;
1084 case 'r':
1085 realm = optarg;
1086 break;
1087 case 's':
1088 keytab_path = optarg;
1089 break;
1090 case 'D':
1091 nodaemon++;
1092 break;
1093 case 'd':
1094 debug++;
1095 break;
1096 case 'S':
1097 /* Standalone mode is now auto-detected; see main(). */
1098 break;
1099 case 'a':
1100 acl_file_name = optarg;
1101 break;
1102 case 't':
1103 /* Undocumented option - for testing only. Run the kpropd
1104 * server exactly once. */
1105 runonce = 1;
1106 break;
1107 case 'x':
1108 newargs = realloc(db_args, (db_args_size + 2) * sizeof(*db_args));
1109 if (newargs == NULL) {
1110 com_err(argv[0], errno, _("copying db args"));
1111 exit(1);
1112 }
1113 db_args = newargs;
1114 db_args[db_args_size] = optarg;
1115 db_args[db_args_size + 1] = NULL;
1116 db_args_size++;
1117 break;
1118 case PID_FILE:
1119 pid_file = optarg;
1120 break;
1121 default:
1122 usage();
1123 }
1124 }
1125 if (optind != argc)
1126 usage();
1127
1128 openlog("kpropd", LOG_PID | LOG_ODELAY, SYSLOG_CLASS);
1129 if (!debug)
1130 set_com_err_hook(kpropd_com_err_proc);
1131
1132 if (realm == NULL) {
1133 retval = krb5_get_default_realm(kpropd_context, &def_realm);
1134 if (retval) {
1135 com_err(progname, retval, _("Unable to get default realm"));
1136 exit(1);
1137 }
1138 realm = def_realm;
1139 } else {
1140 retval = krb5_set_default_realm(kpropd_context, realm);
1141 if (retval) {
1142 com_err(progname, retval, _("Unable to set default realm"));
1143 exit(1);
1144 }
1145 }
1146
1147 /* Construct service name from local hostname. */
1148 retval = sn2princ_realm(kpropd_context, NULL, KPROP_SERVICE_NAME, realm,
1149 &server);
1150 if (retval) {
1151 com_err(progname, retval,
1152 _("while trying to construct my service name"));
1153 exit(1);
1154 }
1155
1156 /* Construct the name of the temporary file. */
1157 if (asprintf(&temp_file_name, "%s.temp", file) < 0) {
1158 com_err(progname, ENOMEM,
1159 _("while allocating filename for temp file"));
1160 exit(1);
1161 }
1162
1163 params.realm = realm;
1164 params.mask |= KADM5_CONFIG_REALM;
1165 retval = kadm5_get_config_params(kpropd_context, 1, ¶ms, ¶ms);
1166 if (retval) {
1167 com_err(progname, retval, _("while initializing"));
1168 exit(1);
1169 }
1170 if (params.iprop_enabled == TRUE) {
1171 ulog_set_role(kpropd_context, IPROP_REPLICA);
1172
1173 if (ulog_map(kpropd_context, params.iprop_logfile,
1174 params.iprop_ulogsize)) {
1175 com_err(progname, errno, _("Unable to map log!\n"));
1176 exit(1);
1177 }
1178 }
1179 }
1180
1181 /*
1182 * Figure out who's calling on the other end of the connection....
1183 */
1184 static void
kerberos_authenticate(krb5_context context,int fd,krb5_principal * clientp,krb5_enctype * etype,struct sockaddr_storage * my_sin)1185 kerberos_authenticate(krb5_context context, int fd, krb5_principal *clientp,
1186 krb5_enctype *etype, struct sockaddr_storage *my_sin)
1187 {
1188 krb5_error_code retval;
1189 krb5_address addr;
1190 krb5_ticket *ticket;
1191 struct sockaddr_storage r_sin;
1192 GETSOCKNAME_ARG3_TYPE sin_length;
1193 krb5_keytab keytab = NULL;
1194 char *name, etypebuf[100];
1195
1196 sin_length = sizeof(r_sin);
1197 if (getsockname(fd, (struct sockaddr *)&r_sin, &sin_length)) {
1198 com_err(progname, errno, _("while getting local socket address"));
1199 exit(1);
1200 }
1201
1202 if (k5_sockaddr_to_address(ss2sa(my_sin), FALSE, &addr) != 0)
1203 addr = k5_addr_directional_accept;
1204 retval = krb5_copy_addr(context, &addr, &receiver_addr);
1205 if (retval) {
1206 com_err(progname, retval, _("while converting local address"));
1207 exit(1);
1208 }
1209
1210 if (debug) {
1211 retval = krb5_unparse_name(context, server, &name);
1212 if (retval) {
1213 com_err(progname, retval, _("while unparsing client name"));
1214 exit(1);
1215 }
1216 fprintf(stderr, "krb5_recvauth(%d, %s, %s, ...)\n", fd, kprop_version,
1217 name);
1218 free(name);
1219 }
1220
1221 retval = krb5_auth_con_init(context, &auth_context);
1222 if (retval) {
1223 syslog(LOG_ERR, _("Error in krb5_auth_con_ini: %s"),
1224 error_message(retval));
1225 exit(1);
1226 }
1227
1228 retval = krb5_auth_con_setflags(context, auth_context,
1229 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
1230 if (retval) {
1231 syslog(LOG_ERR, _("Error in krb5_auth_con_setflags: %s"),
1232 error_message(retval));
1233 exit(1);
1234 }
1235
1236 /*
1237 * Do not set a remote address, to allow replication over a NAT that
1238 * changes the client address. A reflection attack against kpropd is
1239 * impossible because kpropd only sends one message at the end.
1240 */
1241 retval = krb5_auth_con_setaddrs(context, auth_context, receiver_addr,
1242 NULL);
1243 if (retval) {
1244 syslog(LOG_ERR, _("Error in krb5_auth_con_setaddrs: %s"),
1245 error_message(retval));
1246 exit(1);
1247 }
1248
1249 if (keytab_path != NULL) {
1250 retval = krb5_kt_resolve(context, keytab_path, &keytab);
1251 if (retval) {
1252 syslog(LOG_ERR, _("Error in krb5_kt_resolve: %s"),
1253 error_message(retval));
1254 exit(1);
1255 }
1256 }
1257
1258 retval = krb5_recvauth(context, &auth_context, &fd, kprop_version, server,
1259 0, keytab, &ticket);
1260 if (retval) {
1261 syslog(LOG_ERR, _("Error in krb5_recvauth: %s"),
1262 error_message(retval));
1263 exit(1);
1264 }
1265
1266 retval = krb5_copy_principal(context, ticket->enc_part2->client, clientp);
1267 if (retval) {
1268 syslog(LOG_ERR, _("Error in krb5_copy_prinicpal: %s"),
1269 error_message(retval));
1270 exit(1);
1271 }
1272
1273 *etype = ticket->enc_part.enctype;
1274
1275 if (debug) {
1276 retval = krb5_unparse_name(context, *clientp, &name);
1277 if (retval) {
1278 com_err(progname, retval, _("while unparsing client name"));
1279 exit(1);
1280 }
1281
1282 retval = krb5_enctype_to_name(*etype, FALSE, etypebuf,
1283 sizeof(etypebuf));
1284 if (retval) {
1285 com_err(progname, retval, _("while unparsing ticket etype"));
1286 exit(1);
1287 }
1288
1289 fprintf(stderr, _("authenticated client: %s (etype == %s)\n"),
1290 name, etypebuf);
1291 free(name);
1292 }
1293
1294 krb5_free_ticket(context, ticket);
1295 }
1296
1297 static krb5_boolean
authorized_principal(krb5_context context,krb5_principal p,krb5_enctype auth_etype)1298 authorized_principal(krb5_context context, krb5_principal p,
1299 krb5_enctype auth_etype)
1300 {
1301 krb5_boolean ok = FALSE;
1302 char *name = NULL, *ptr, buf[1024];
1303 krb5_error_code retval;
1304 FILE *acl_file = NULL;
1305 int end;
1306 krb5_enctype acl_etype;
1307
1308 retval = krb5_unparse_name(context, p, &name);
1309 if (retval)
1310 goto cleanup;
1311
1312 acl_file = fopen(acl_file_name, "r");
1313 if (acl_file == NULL)
1314 goto cleanup;
1315
1316 while (!feof(acl_file)) {
1317 if (!fgets(buf, sizeof(buf), acl_file))
1318 break;
1319 end = strlen(buf) - 1;
1320 if (buf[end] == '\n')
1321 buf[end] = '\0';
1322 if (!strncmp(name, buf, strlen(name))) {
1323 ptr = buf + strlen(name);
1324
1325 /* If the next character is not whitespace or null, then the match
1326 * is only partial. Continue on to new lines. */
1327 if (*ptr != '\0' && !isspace((int)*ptr))
1328 continue;
1329
1330 /* Otherwise, skip trailing whitespace. */
1331 for (; *ptr != '\0' && isspace((int)*ptr); ptr++) ;
1332
1333 /*
1334 * Now, look for an etype string. If there isn't one, return true.
1335 * If there is an invalid string, continue. If there is a valid
1336 * string, return true only if it matches the etype passed in,
1337 * otherwise continue.
1338 */
1339 if (*ptr != '\0' &&
1340 ((retval = krb5_string_to_enctype(ptr, &acl_etype)) ||
1341 (acl_etype != auth_etype)))
1342 continue;
1343
1344 ok = TRUE;
1345 goto cleanup;
1346 }
1347 }
1348
1349 cleanup:
1350 free(name);
1351 if (acl_file != NULL)
1352 fclose(acl_file);
1353 return ok;
1354 }
1355
1356 static void
recv_database(krb5_context context,int fd,int database_fd,krb5_data * confmsg)1357 recv_database(krb5_context context, int fd, int database_fd,
1358 krb5_data *confmsg)
1359 {
1360 uint64_t database_size, received_size;
1361 int n;
1362 char buf[1024];
1363 char dbsize_buf[KPROP_DBSIZE_MAX_BUFSIZ];
1364 krb5_data inbuf, outbuf;
1365 krb5_error_code retval;
1366
1367 /* Receive and decode size from client. */
1368 retval = krb5_read_message(context, &fd, &inbuf);
1369 if (retval) {
1370 send_error(context, fd, retval, "while reading database size");
1371 com_err(progname, retval,
1372 _("while reading size of database from client"));
1373 exit(1);
1374 }
1375 if (krb5_is_krb_error(&inbuf))
1376 recv_error(context, &inbuf);
1377 retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL);
1378 if (retval) {
1379 send_error(context, fd, retval, "while decoding database size");
1380 krb5_free_data_contents(context, &inbuf);
1381 com_err(progname, retval,
1382 _("while decoding database size from client"));
1383 exit(1);
1384 }
1385
1386 retval = decode_database_size(&outbuf, &database_size);
1387 if (retval) {
1388 send_error(context, fd, retval, "malformed database size message");
1389 com_err(progname, retval,
1390 _("malformed database size message from client"));
1391 exit(1);
1392 }
1393
1394 krb5_free_data_contents(context, &inbuf);
1395 krb5_free_data_contents(context, &outbuf);
1396
1397 /* Initialize the initial vector. */
1398 retval = krb5_auth_con_initivector(context, auth_context);
1399 if (retval) {
1400 send_error(context, fd, retval,
1401 "failed while initializing i_vector");
1402 com_err(progname, retval, _("while initializing i_vector"));
1403 exit(1);
1404 }
1405
1406 if (debug)
1407 fprintf(stderr, _("Full propagation transfer started.\n"));
1408
1409 /* Now start receiving the database from the net. */
1410 received_size = 0;
1411 while (received_size < database_size) {
1412 retval = krb5_read_message(context, &fd, &inbuf);
1413 if (retval) {
1414 snprintf(buf, sizeof(buf),
1415 "while reading database block starting at offset %"PRIu64,
1416 received_size);
1417 com_err(progname, retval, "%s", buf);
1418 send_error(context, fd, retval, buf);
1419 exit(1);
1420 }
1421 if (krb5_is_krb_error(&inbuf))
1422 recv_error(context, &inbuf);
1423 retval = krb5_rd_priv(context, auth_context, &inbuf, &outbuf, NULL);
1424 if (retval) {
1425 snprintf(buf, sizeof(buf),
1426 "while decoding database block starting at offset %"
1427 PRIu64, received_size);
1428 com_err(progname, retval, "%s", buf);
1429 send_error(context, fd, retval, buf);
1430 krb5_free_data_contents(context, &inbuf);
1431 exit(1);
1432 }
1433 n = write(database_fd, outbuf.data, outbuf.length);
1434 krb5_free_data_contents(context, &inbuf);
1435 if (n < 0) {
1436 snprintf(buf, sizeof(buf),
1437 "while writing database block starting at offset %"PRIu64,
1438 received_size);
1439 send_error(context, fd, errno, buf);
1440 } else if ((unsigned int)n != outbuf.length) {
1441 snprintf(buf, sizeof(buf),
1442 "incomplete write while writing database block starting "
1443 "at \noffset %"PRIu64" (%d written, %d expected)",
1444 received_size, n, outbuf.length);
1445 send_error(context, fd, KRB5KRB_ERR_GENERIC, buf);
1446 }
1447 received_size += outbuf.length;
1448 krb5_free_data_contents(context, &outbuf);
1449 }
1450
1451 /* OK, we've seen the entire file. Did we get too many bytes? */
1452 if (received_size > database_size) {
1453 snprintf(buf, sizeof(buf),
1454 "Received %"PRIu64" bytes, expected %"PRIu64
1455 " bytes for database file",
1456 received_size, database_size);
1457 send_error(context, fd, KRB5KRB_ERR_GENERIC, buf);
1458 }
1459
1460 if (debug)
1461 fprintf(stderr, _("Full propagation transfer finished.\n"));
1462
1463 /* Create message acknowledging number of bytes received, but
1464 * don't send it until kdb5_util returns successfully. */
1465 inbuf = make_data(dbsize_buf, sizeof(dbsize_buf));
1466 encode_database_size(database_size, &inbuf);
1467 retval = krb5_mk_safe(context,auth_context,&inbuf,confmsg,NULL);
1468 if (retval) {
1469 com_err(progname, retval, "while encoding # of received bytes");
1470 send_error(context, fd, retval, "while encoding # of received bytes");
1471 exit(1);
1472 }
1473 }
1474
1475
1476 static void
send_error(krb5_context context,int fd,krb5_error_code err_code,char * err_text)1477 send_error(krb5_context context, int fd, krb5_error_code err_code,
1478 char *err_text)
1479 {
1480 krb5_error error;
1481 const char *text;
1482 krb5_data outbuf;
1483 char buf[1024];
1484
1485 memset(&error, 0, sizeof(error));
1486 krb5_us_timeofday(context, &error.stime, &error.susec);
1487 error.server = server;
1488 error.client = client;
1489
1490 text = (err_text != NULL) ? err_text : error_message(err_code);
1491
1492 error.error = err_code - ERROR_TABLE_BASE_krb5;
1493 if (error.error > 127) {
1494 error.error = KRB_ERR_GENERIC;
1495 if (err_text) {
1496 snprintf(buf, sizeof(buf), "%s %s", error_message(err_code),
1497 err_text);
1498 text = buf;
1499 }
1500 }
1501 error.text.length = strlen(text) + 1;
1502 error.text.data = strdup(text);
1503 if (error.text.data) {
1504 if (!krb5_mk_error(context, &error, &outbuf)) {
1505 (void)krb5_write_message(context, &fd, &outbuf);
1506 krb5_free_data_contents(context, &outbuf);
1507 }
1508 free(error.text.data);
1509 }
1510 }
1511
1512 void
recv_error(krb5_context context,krb5_data * inbuf)1513 recv_error(krb5_context context, krb5_data *inbuf)
1514 {
1515 krb5_error *error;
1516 krb5_error_code retval;
1517
1518 retval = krb5_rd_error(context, inbuf, &error);
1519 if (retval) {
1520 com_err(progname, retval,
1521 _("while decoding error packet from client"));
1522 exit(1);
1523 }
1524 if (error->error == KRB_ERR_GENERIC) {
1525 if (error->text.data)
1526 fprintf(stderr, _("Generic remote error: %s\n"), error->text.data);
1527 } else if (error->error) {
1528 com_err(progname,
1529 (krb5_error_code)error->error + ERROR_TABLE_BASE_krb5,
1530 _("signaled from server"));
1531 if (error->text.data) {
1532 fprintf(stderr, _("Error text from client: %s\n"),
1533 error->text.data);
1534 }
1535 }
1536 krb5_free_error(context, error);
1537 exit(1);
1538 }
1539
1540 static void
load_database(krb5_context context,char * kdb_util,char * database_file_name)1541 load_database(krb5_context context, char *kdb_util, char *database_file_name)
1542 {
1543 static char *edit_av[10];
1544 int error_ret, child_pid, count;
1545
1546 /* <sys/param.h> has been included, so BSD will be defined on
1547 * BSD systems. */
1548 #if BSD > 0 && BSD <= 43
1549 #ifndef WEXITSTATUS
1550 #define WEXITSTATUS(w) (w).w_retcode
1551 #endif
1552 union wait waitb;
1553 #else
1554 int waitb;
1555 #endif
1556 kdb_log_context *log_ctx;
1557
1558 if (debug)
1559 fprintf(stderr, "calling kdb5_util to load database\n");
1560
1561 log_ctx = context->kdblog_context;
1562
1563 edit_av[0] = kdb_util;
1564 count = 1;
1565 if (realm) {
1566 edit_av[count++] = "-r";
1567 edit_av[count++] = realm;
1568 }
1569 edit_av[count++] = "load";
1570 if (kerb_database) {
1571 edit_av[count++] = "-d";
1572 edit_av[count++] = kerb_database;
1573 }
1574 if (log_ctx && log_ctx->iproprole == IPROP_REPLICA)
1575 edit_av[count++] = "-i";
1576 edit_av[count++] = database_file_name;
1577 edit_av[count++] = NULL;
1578
1579 switch (child_pid = fork()) {
1580 case -1:
1581 com_err(progname, errno, _("while trying to fork %s"), kdb_util);
1582 exit(1);
1583 case 0:
1584 execv(kdb_util, edit_av);
1585 com_err(progname, errno, _("while trying to exec %s"), kdb_util);
1586 _exit(1);
1587 /*NOTREACHED*/
1588 default:
1589 if (debug)
1590 fprintf(stderr, "Load PID is %d\n", child_pid);
1591 if (wait(&waitb) < 0) {
1592 com_err(progname, errno, _("while waiting for %s"), kdb_util);
1593 exit(1);
1594 }
1595 }
1596
1597 if (!WIFEXITED(waitb)) {
1598 com_err(progname, 0, _("%s load terminated"), kdb_util);
1599 exit(1);
1600 }
1601
1602 error_ret = WEXITSTATUS(waitb);
1603 if (error_ret) {
1604 com_err(progname, 0, _("%s returned a bad exit status (%d)"),
1605 kdb_util, error_ret);
1606 exit(1);
1607 }
1608 return;
1609 }
1610
1611 /*
1612 * Get the host base service name for the kiprop principal. Returns
1613 * KADM5_OK on success. Caller must free the storage allocated
1614 * for host_service_name.
1615 */
1616 static kadm5_ret_t
kadm5_get_kiprop_host_srv_name(krb5_context context,const char * realm_name,char ** host_service_name)1617 kadm5_get_kiprop_host_srv_name(krb5_context context, const char *realm_name,
1618 char **host_service_name)
1619 {
1620 char *name, *host;
1621
1622 host = params.admin_server; /* XXX */
1623 if (asprintf(&name, "%s/%s", KADM5_KIPROP_HOST_SERVICE, host) < 0) {
1624 free(host);
1625 return ENOMEM;
1626 }
1627 *host_service_name = name;
1628
1629 return KADM5_OK;
1630 }
1631