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