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