xref: /freebsd/crypto/krb5/src/kprop/kpropd.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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                                   &params,
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(&params, 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, &params, &params);
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