Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/kprop/kpropd.c
34889 views
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
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
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
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
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
222
usr1_handler(int sig)
223
{
224
/* Nothing to do, just let the signal interrupt sleep(). */
225
}
226
227
static void
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
243
atexit_kill_do_standalone(void)
244
{
245
if (fullprop_child > 0)
246
kill(fullprop_child, SIGHUP);
247
}
248
249
int
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
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
1632