5 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
8 * Export of this software from the United States of America is assumed
9 * to require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
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 M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. M.I.T. makes no representations about the suitability of
21 * this software for any purpose. It is provided "as is" without express
22 * or implied warranty.
27 #if !defined(lint) && !defined(SABER)
28 static char rcsid_kprop_c[] =
30 #endif /* !lint && !SABER */
32 #include <krb5/krb5.h>
33 #include <krb5/asn1.h>
34 #include <krb5/osconf.h>
36 #include <krb5/kdb_dbm.h>
37 #include <krb5/ext-proto.h>
38 #include <krb5/los-proto.h>
47 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <sys/param.h>
57 static char *kprop_version = KPROP_PROT_VERSION;
63 char *file = KPROP_DEFAULT_FILE;
65 krb5_principal my_principal; /* The Kerberos principal we'll be */
66 /* running under, initialized in */
68 krb5_ccache ccache; /* Credentials cache which we'll be using */
69 krb5_creds my_creds; /* My credentials */
70 krb5_int32 my_seq_num; /* Sequence number to use for connection */
71 krb5_int32 his_seq_num; /* Remote sequence number */
72 krb5_address sender_addr;
73 krb5_address receiver_addr;
78 krb5_error_code open_connection();
79 void kerberos_authenticate();
83 void update_last_prop_file();
87 fprintf(stderr, "\nUsage: %s [-r realm] [-f file] [-d] slave_host\n\n",
97 int fd, database_fd, database_size;
98 krb5_error_code retval;
104 database_fd = open_database(file, &database_size);
105 if (retval = open_connection(slave_host, &fd, Errmsg)) {
106 com_err(progname, retval, "%s while opening connection to %s",
111 fprintf(stderr, "%s: %s while opening connection to %s\n",
112 progname, Errmsg, slave_host);
115 kerberos_authenticate(fd, my_principal);
117 printf("My sequence number: %d\n", my_seq_num);
118 printf("His sequence number: %d\n", his_seq_num);
120 xmit_database(fd, database_fd, database_size);
121 update_last_prop_file(slave_host, file);
122 printf("Database propagation to %s: SUCCEEDED\n", slave_host);
129 register char *word, ch;
133 while (word = *argv++) {
136 while (word && (ch = *word++)) {
177 char my_host_name[MAXHOSTNAMELEN];
181 krb5_address **my_addresses;
182 krb5_error_code retval;
183 static char tkstring[] = "/tmp/kproptktXXXXXX";
186 * Figure out what tickets we'll be using to send stuff
188 if (gethostname (my_host_name, sizeof(my_host_name)) != 0) {
189 com_err(progname, errno, "while getting my hostname");
192 /* get canonicalized service instance name */
193 if (!(hp = gethostbyname(my_host_name))) {
194 fprintf(stderr, "Couldn't get my cannonicalized host name!\n");
197 for (cp=hp->h_name; *cp; cp++)
201 sprintf(buf, "host/%s@%s", hp->h_name, realm);
203 sprintf(buf, "host/%s", hp->h_name);
204 if (retval = krb5_parse_name(buf, &my_principal)) {
205 com_err (progname, retval, "when parsing name %s",buf);
210 * Initialize cache file which we're going to be using
212 (void) mktemp(tkstring);
213 sprintf(buf, "FILE:%s", tkstring);
214 if (retval = krb5_cc_resolve(buf, &ccache)) {
215 com_err(progname, retval, "while opening crednetials cache %s",
219 if (retval = krb5_cc_initialize(ccache, my_principal)) {
220 com_err (progname, retval, "when initializing cache %s",
226 * Get the tickets we'll need.
228 * Construct the principal name for the slave host.
230 memset((char *)&my_creds, 0, sizeof(my_creds));
231 if (!(hp = gethostbyname(slave_host))) {
233 "Couldn't get cannonicalized name for slave\n");
236 for (cp=hp->h_name; *cp; cp++)
239 if (!(slave_host = malloc(strlen(hp->h_name) + 1))) {
240 com_err(progname, ENOMEM,
241 "while allocate space for canonicalized slave host");
244 strcpy(slave_host, hp->h_name);
246 sprintf(buf, "%s/%s@%s", KPROP_SERVICE_NAME, slave_host,
249 sprintf(buf, "%s/%s", KPROP_SERVICE_NAME, hp->h_name);
250 if (retval = krb5_parse_name(buf, &my_creds.server)) {
251 com_err(progname, retval,
252 "while parsing slave principal name");
256 * Now fill in the client....
258 if (retval = krb5_copy_principal(my_principal, &my_creds.client)) {
259 com_err(progname, retval, "While copying client principal");
265 retval = krb5_os_localaddr(&my_addresses);
267 com_err(progname, retval,
268 "when getting my address");
271 retval = krb5_get_in_tkt_with_skey(0, my_addresses,
274 0, ccache, &my_creds, 0);
276 com_err(progname, retval, "while getting initial ticket\n");
280 * Now destroy the cache right away --- the credentials we
281 * need will be in my_creds.
283 if (retval = krb5_cc_destroy(ccache)) {
284 com_err(progname, retval, "while destroying ticket cache");
290 open_connection(host, fd, Errmsg)
296 krb5_error_code retval;
299 register struct servent *sp;
300 struct sockaddr_in sin;
303 hp = gethostbyname(host);
305 (void) sprintf(Errmsg, "%s: unknown host", host);
309 sp = getservbyname(KPROP_SERVICE, "tcp");
311 (void) strcpy(Errmsg, KPROP_SERVICE);
312 (void) strcat(Errmsg, "/tcp: unknown service");
316 sin.sin_family = hp->h_addrtype;
317 bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
318 sin.sin_port = sp->s_port;
319 s = socket(AF_INET, SOCK_STREAM, 0);
322 (void) sprintf(Errmsg, "in call to socket");
325 if (connect(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
328 (void) sprintf(Errmsg, "in call to connect");
334 * Set receiver_addr and sender_addr.
336 receiver_addr.addrtype = ADDRTYPE_INET;
337 receiver_addr.length = sizeof(sin.sin_addr);
338 receiver_addr.contents = (krb5_octet *) malloc(sizeof(sin.sin_addr));
339 memcpy((char *) receiver_addr.contents, (char *) &sin.sin_addr,
340 sizeof(sin.sin_addr));
342 socket_length = sizeof(sin);
343 if (getpeername(s, (struct sockaddr *)&sin, &socket_length) < 0) {
346 (void) sprintf(Errmsg, "in call to getpeername");
349 sender_addr.addrtype = ADDRTYPE_INET;
350 sender_addr.length = sizeof(sin.sin_addr);
351 sender_addr.contents = (krb5_octet *) malloc(sizeof(sin.sin_addr));
352 memcpy((char *) sender_addr.contents, (char *) &sin.sin_addr,
353 sizeof(sin.sin_addr));
359 void kerberos_authenticate(fd, me)
363 krb5_error_code retval;
364 krb5_error *error = NULL;
365 krb5_ap_rep_enc_part *rep_result;
367 if (retval = krb5_sendauth((void *)&fd, kprop_version, me,
368 my_creds.server, AP_OPTS_MUTUAL_REQUIRED,
369 NULL, &my_creds, NULL, &my_seq_num, NULL,
370 &error, &rep_result)) {
371 com_err(progname, retval, "while authenticating to server");
373 if (error->error == KRB_ERR_GENERIC) {
374 if (error->text.data)
376 "Generic remote error: %s\n",
378 } else if (error->error) {
380 error->error + ERROR_TABLE_BASE_krb5,
381 "signalled from server");
382 if (error->text.data)
384 "Error text from server: %s\n",
387 krb5_free_error(error);
391 his_seq_num = rep_result->seq_number;
392 krb5_free_ap_rep_enc_part(rep_result);
396 * Open the Kerberos database dump file. Takes care of locking it
397 * and making sure that the .ok file is more recent that the database
400 * Returns the file descriptor of the database dump file. Also fills
401 * in the size of the database file.
404 open_database(data_fn, size)
409 struct stat stbuf, stbuf_ok;
411 static char ok[] = ".dump_ok";
413 if ((fd = open(data_fn, O_RDONLY)) < 0) {
414 com_err(progname, errno, "while trying to open %s",
419 #ifdef POSIX_FILE_LOCKS
420 if (lockf(fd, LOCK_SH | LOCK_NB, 0) < 0) {
421 if (errno == EWOULDBLOCK || errno == EAGAIN)
422 com_err(progname, 0, "database locked");
424 com_err(progname, errno, "while trying to flock %s",
429 if (flock(fd, LOCK_SH | LOCK_NB) < 0) {
430 if (errno == EWOULDBLOCK || errno == EAGAIN)
431 com_err(progname, 0, "database locked");
433 com_err(progname, errno, "while trying to flock %s",
438 if (fstat(fd, &stbuf)) {
439 com_err(progname, errno, "while trying to stat %s",
443 if ((data_ok_fn = (char *) malloc(strlen(data_fn)+strlen(ok)+1))
445 com_err(progname, ENOMEM, "while trying to malloc data_ok_fn");
448 strcat(strcpy(data_ok_fn, data_fn), ok);
449 if (stat(data_ok_fn, &stbuf_ok)) {
450 com_err(progname, errno, "while trying to stat %s",
456 if (stbuf.st_mtime > stbuf_ok.st_mtime) {
457 com_err(progname, 0, "'%s' more recent than '%s'.",
458 data_fn, data_ok_fn);
461 *size = stbuf.st_size;
466 * Now we send over the database. We use the following protocol:
467 * Send over a KRB_SAFE message with the size. Then we send over the
468 * database in blocks of KPROP_BLKSIZE, encrypted using KRB_PRIV.
469 * Then we expect to see a KRB_SAFE message with the size sent back.
471 * At any point in the protocol, we may send a KRB_ERROR message; this
472 * will abort the entire operation.
475 xmit_database(fd, database_fd, database_size)
480 int send_size, sent_size, n, eblock_size;
481 krb5_data inbuf, outbuf;
482 char buf[KPROP_BUFSIZ];
484 krb5_error_code retval;
490 send_size = htonl(database_size);
491 inbuf.data = (char *) &send_size;
492 inbuf.length = sizeof(send_size); /* must be 4, really */
493 if (retval = krb5_mk_safe(&inbuf, KPROP_CKSUMTYPE,
495 &sender_addr, &receiver_addr,
497 KRB5_PRIV_DOSEQUENCE|KRB5_SAFE_NOTIME,
498 0, /* no rcache when NOTIME */
500 com_err(progname, retval, "while encoding database size");
501 send_error(fd, "while encoding database size", retval);
504 if (retval = krb5_write_message((void *) &fd, &outbuf)) {
506 com_err(progname, retval, "while sending database size");
511 * Initialize the initial vector.
513 eblock_size = krb5_keytype_array[my_creds.keyblock.keytype]->
514 system->block_length;
515 if (!(i_vector=malloc(eblock_size))) {
516 com_err(progname, ENOMEM, "while allocating i_vector");
517 send_error(fd, "malloc failed while allocating i_vector",
521 memset(i_vector, 0, eblock_size);
523 * Send over the file, block by block....
527 while (n = read(database_fd, buf, sizeof(buf))) {
529 if (retval = krb5_mk_priv(&inbuf, ETYPE_DES_CBC_CRC,
534 KRB5_PRIV_DOSEQUENCE|KRB5_PRIV_NOTIME,
535 0, /* again, no rcache */
539 "while encoding database block starting at %d",
541 com_err(progname, retval, buf);
542 send_error(fd, buf, retval);
545 if (retval = krb5_write_message((void *) &fd, &outbuf)) {
547 com_err(progname, retval,
548 "while sending database block starting at %d",
555 printf("%d bytes sent.\n", sent_size);
557 if (sent_size != database_size) {
558 com_err(progname, 0, "Premature EOF found for database file!");
559 send_error(fd, "Premature EOF found for database file!",
560 KRB5KRB_ERR_GENERIC);
564 * OK, we've sent the database; now let's wait for a success
565 * indication from the remote end.
567 if (retval = krb5_read_message((void *) &fd, &inbuf)) {
568 com_err(progname, retval,
569 "while reading response from server");
573 * If we got an error response back from the server, display
576 if (krb5_is_krb_error(&inbuf)) {
577 if (retval = krb5_rd_error(&inbuf, &error)) {
578 com_err(progname, retval,
579 "while decoding error response from server");
582 if (error->error == KRB_ERR_GENERIC) {
583 if (error->text.data)
585 "Generic remote error: %s\n",
587 } else if (error->error) {
588 com_err(progname, error->error + ERROR_TABLE_BASE_krb5,
589 "signalled from server");
590 if (error->text.data)
592 "Error text from server: %s\n",
595 krb5_free_error(error);
598 if (retval = krb5_rd_safe(&inbuf, &my_creds.keyblock, &receiver_addr,
599 &sender_addr, his_seq_num++,
600 KRB5_SAFE_DOSEQUENCE|KRB5_SAFE_NOTIME,
602 com_err(progname, retval,
603 "while decoding final size packet from server");
606 memcpy((char *)&send_size, outbuf.data, sizeof(send_size));
607 send_size = ntohl(send_size);
608 if (send_size != database_size) {
610 "Kpropd sent database size %d, expecting %d",
611 send_size, database_size);
619 send_error(fd, err_text, err_code)
622 krb5_error_code err_code;
628 memset((char *)&error, 0, sizeof(error));
629 krb5_us_timeofday(&error.ctime, &error.cusec);
630 error.server = my_creds.server;
631 error.client = my_principal;
632 error.error = err_code - ERROR_TABLE_BASE_krb5;
633 if (error.error > 127)
634 error.error = KRB_ERR_GENERIC;
638 text = error_message(err_code);
639 error.text.length = strlen(text) + 1;
640 if (error.text.data = malloc(error.text.length)) {
641 strcpy(error.text.data, text);
642 if (!krb5_mk_error(&error, &outbuf)) {
643 (void) krb5_write_message((void *) &fd, &outbuf);
646 free(error.text.data);
650 void update_last_prop_file(hostname, file_name)
654 /* handle slave locking/failure stuff */
655 char *file_last_prop;
657 static char last_prop[]=".last_prop";
659 if ((file_last_prop = (char *)malloc(strlen(file_name) +
660 strlen(hostname) + 1 +
661 strlen(last_prop) + 1)) == NULL) {
662 com_err(progname, ENOMEM,
663 "while allocating filename for update_last_prop_file");
666 strcpy(file_last_prop, file_name);
667 strcat(file_last_prop, ".");
668 strcat(file_last_prop, hostname);
669 strcat(file_last_prop, last_prop);
670 if ((fd = open(file_last_prop, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
671 com_err(progname, errno,
672 "while creating 'last_prop' file, '%s'",
674 free(file_last_prop);
677 free(file_last_prop);