4 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
36 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <sys/param.h>
49 static char *kprop_version = KPROP_PROT_VERSION;
56 char *file = KPROP_DEFAULT_FILE;
59 krb5_principal my_principal; /* The Kerberos principal we'll be */
60 /* running under, initialized in */
62 krb5_ccache ccache; /* Credentials cache which we'll be using */
63 /* krb5_creds my_creds; /* My credentials */
65 krb5_address sender_addr;
66 krb5_address receiver_addr;
69 PROTOTYPE((int, char **));
71 PROTOTYPE((krb5_context));
74 krb5_error_code open_connection
75 PROTOTYPE((char *, int *, char *));
76 void kerberos_authenticate
77 PROTOTYPE((krb5_context, krb5_auth_context *,
78 int, krb5_principal, krb5_creds **));
80 PROTOTYPE((krb5_context, char *, int *));
82 PROTOTYPE((krb5_context, int));
84 PROTOTYPE((krb5_context, krb5_auth_context, krb5_creds *,
87 PROTOTYPE((krb5_context, krb5_creds *, int, char *, krb5_error_code));
88 void update_last_prop_file
89 PROTOTYPE((char *, char *));
93 fprintf(stderr, "\nUsage: %s [-r realm] [-f file] [-d] [-P port] [-s srvtab] slave_host\n\n",
103 int fd, database_fd, database_size;
104 krb5_error_code retval;
105 krb5_context context;
106 krb5_creds *my_creds;
107 krb5_auth_context auth_context;
110 retval = krb5_init_context(&context);
112 com_err(argv[0], retval, "while initializing krb5");
116 get_tickets(context);
118 database_fd = open_database(context, file, &database_size);
119 if (retval = open_connection(slave_host, &fd, Errmsg)) {
120 com_err(progname, retval, "%s while opening connection to %s",
125 fprintf(stderr, "%s: %s while opening connection to %s\n",
126 progname, Errmsg, slave_host);
129 kerberos_authenticate(context, &auth_context, fd, my_principal,
131 xmit_database(context, auth_context, my_creds, fd, database_fd,
133 update_last_prop_file(slave_host, file);
134 printf("Database propagation to %s: SUCCEEDED\n", slave_host);
135 krb5_free_cred_contents(context, my_creds);
136 close_database(context, database_fd);
144 register char *word, ch;
147 while (--argc && (word = *argv++)) {
150 while (word && (ch = *word++)) {
175 port = htons(atoi(word));
177 port = htons(atoi(*argv++));
207 void get_tickets(context)
208 krb5_context context;
210 char my_host_name[MAXHOSTNAMELEN];
214 krb5_error_code retval;
215 static char tkstring[] = "/tmp/kproptktXXXXXX";
216 krb5_keytab keytab = NULL;
219 * Figure out what tickets we'll be using to send stuff
221 retval = krb5_sname_to_principal(context, NULL, NULL,
222 KRB5_NT_SRV_HST, &my_principal);
224 com_err(progname, errno, "while setting client principal name");
228 (void) krb5_xfree(krb5_princ_realm(context, my_principal)->data);
229 krb5_princ_set_realm_length(context, my_principal, strlen(realm));
230 krb5_princ_set_realm_data(context, my_principal, strdup(realm));
233 krb5_princ_type(context, my_principal) = KRB5_NT_PRINCIPAL;
237 * Initialize cache file which we're going to be using
239 (void) mktemp(tkstring);
240 sprintf(buf, "FILE:%s", tkstring);
241 if (retval = krb5_cc_resolve(context, buf, &ccache)) {
242 com_err(progname, retval, "while opening credential cache %s",
246 if (retval = krb5_cc_initialize(context, ccache, my_principal)) {
247 com_err (progname, retval, "when initializing cache %s",
253 * Get the tickets we'll need.
255 * Construct the principal name for the slave host.
257 memset((char *)&creds, 0, sizeof(creds));
258 retval = krb5_sname_to_principal(context,
259 slave_host, KPROP_SERVICE_NAME,
260 KRB5_NT_SRV_HST, &creds.server);
262 com_err(progname, errno, "while setting server principal name");
263 (void) krb5_cc_destroy(context, ccache);
267 (void) krb5_xfree(krb5_princ_realm(context, creds.server)->data);
268 krb5_princ_set_realm_length(context, creds.server, strlen(realm));
269 krb5_princ_set_realm_data(context, creds.server, strdup(realm));
273 * Now fill in the client....
275 if (retval = krb5_copy_principal(context, my_principal, &creds.client)) {
276 com_err(progname, retval, "While copying client principal");
277 (void) krb5_cc_destroy(context, ccache);
281 if (retval = krb5_kt_resolve(context, srvtab, &keytab)) {
282 com_err(progname, retval, "while resolving keytab");
283 (void) krb5_cc_destroy(context, ccache);
288 retval = krb5_get_in_tkt_with_keytab(context, 0, 0, NULL,
289 NULL, keytab, ccache, &creds, 0);
291 com_err(progname, retval, "while getting initial ticket\n");
292 (void) krb5_cc_destroy(context, ccache);
297 (void) krb5_kt_close(context, keytab);
300 * Now destroy the cache right away --- the credentials we
301 * need will be in my_creds.
303 if (retval = krb5_cc_destroy(context, ccache)) {
304 com_err(progname, retval, "while destroying ticket cache");
310 open_connection(host, fd, Errmsg)
316 krb5_error_code retval;
319 register struct servent *sp;
320 struct sockaddr_in sin;
323 hp = gethostbyname(host);
325 (void) sprintf(Errmsg, "%s: unknown host", host);
329 sin.sin_family = hp->h_addrtype;
330 memcpy((char *)&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
332 sp = getservbyname(KPROP_SERVICE, "tcp");
334 (void) strcpy(Errmsg, KPROP_SERVICE);
335 (void) strcat(Errmsg, "/tcp: unknown service");
339 sin.sin_port = sp->s_port;
342 s = socket(AF_INET, SOCK_STREAM, 0);
345 (void) sprintf(Errmsg, "in call to socket");
348 if (connect(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
351 (void) sprintf(Errmsg, "in call to connect");
357 * Set receiver_addr and sender_addr.
359 receiver_addr.addrtype = ADDRTYPE_INET;
360 receiver_addr.length = sizeof(sin.sin_addr);
361 receiver_addr.contents = (krb5_octet *) malloc(sizeof(sin.sin_addr));
362 memcpy((char *) receiver_addr.contents, (char *) &sin.sin_addr,
363 sizeof(sin.sin_addr));
365 socket_length = sizeof(sin);
366 if (getsockname(s, (struct sockaddr *)&sin, &socket_length) < 0) {
369 (void) sprintf(Errmsg, "in call to getsockname");
372 sender_addr.addrtype = ADDRTYPE_INET;
373 sender_addr.length = sizeof(sin.sin_addr);
374 sender_addr.contents = (krb5_octet *) malloc(sizeof(sin.sin_addr));
375 memcpy((char *) sender_addr.contents, (char *) &sin.sin_addr,
376 sizeof(sin.sin_addr));
382 void kerberos_authenticate(context, auth_context, fd, me, new_creds)
383 krb5_context context;
384 krb5_auth_context *auth_context;
387 krb5_creds ** new_creds;
389 krb5_error_code retval;
390 krb5_error *error = NULL;
391 krb5_ap_rep_enc_part *rep_result;
393 if (retval = krb5_auth_con_init(context, auth_context))
396 krb5_auth_con_setflags(context, *auth_context,
397 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
399 if (retval = krb5_auth_con_setaddrs(context, *auth_context, &sender_addr,
401 com_err(progname, retval, "in krb5_auth_con_setaddrs");
405 if (retval = krb5_sendauth(context, auth_context, (void *)&fd,
406 kprop_version, me, creds.server,
407 AP_OPTS_MUTUAL_REQUIRED, NULL, &creds, NULL,
408 &error, &rep_result, new_creds)) {
409 com_err(progname, retval, "while authenticating to server");
411 if (error->error == KRB_ERR_GENERIC) {
412 if (error->text.data)
414 "Generic remote error: %s\n",
416 } else if (error->error) {
418 error->error + ERROR_TABLE_BASE_krb5,
419 "signalled from server");
420 if (error->text.data)
422 "Error text from server: %s\n",
425 krb5_free_error(context, error);
429 krb5_free_ap_rep_enc_part(context, rep_result);
434 * Open the Kerberos database dump file. Takes care of locking it
435 * and making sure that the .ok file is more recent that the database
438 * Returns the file descriptor of the database dump file. Also fills
439 * in the size of the database file.
442 open_database(context, data_fn, size)
443 krb5_context context;
449 struct stat stbuf, stbuf_ok;
451 static char ok[] = ".dump_ok";
453 dbpathname = strdup(data_fn);
455 com_err(progname, ENOMEM, "allocating database file name '%s'",
459 if ((fd = open(dbpathname, O_RDONLY)) < 0) {
460 com_err(progname, errno, "while trying to open %s",
465 err = krb5_lock_file(context, fd,
466 KRB5_LOCKMODE_SHARED|KRB5_LOCKMODE_DONTBLOCK);
467 if (err == EAGAIN || err == EWOULDBLOCK || errno == EACCES) {
468 com_err(progname, 0, "database locked");
471 com_err(progname, err, "while trying to lock '%s'", dbpathname);
474 if (fstat(fd, &stbuf)) {
475 com_err(progname, errno, "while trying to stat %s",
479 if ((data_ok_fn = (char *) malloc(strlen(data_fn)+strlen(ok)+1))
481 com_err(progname, ENOMEM, "while trying to malloc data_ok_fn");
484 strcat(strcpy(data_ok_fn, data_fn), ok);
485 if (stat(data_ok_fn, &stbuf_ok)) {
486 com_err(progname, errno, "while trying to stat %s",
492 if (stbuf.st_mtime > stbuf_ok.st_mtime) {
493 com_err(progname, 0, "'%s' more recent than '%s'.",
494 data_fn, data_ok_fn);
497 *size = stbuf.st_size;
502 close_database(context, fd)
503 krb5_context context;
507 if (err = krb5_lock_file(context, fd, KRB5_LOCKMODE_UNLOCK))
508 com_err(progname, err, "while unlocking database '%s'", dbpathname);
515 * Now we send over the database. We use the following protocol:
516 * Send over a KRB_SAFE message with the size. Then we send over the
517 * database in blocks of KPROP_BLKSIZE, encrypted using KRB_PRIV.
518 * Then we expect to see a KRB_SAFE message with the size sent back.
520 * At any point in the protocol, we may send a KRB_ERROR message; this
521 * will abort the entire operation.
524 xmit_database(context, auth_context, my_creds, fd, database_fd, database_size)
525 krb5_context context;
526 krb5_auth_context auth_context;
527 krb5_creds *my_creds;
532 krb5_int32 send_size, sent_size, n;
533 krb5_data inbuf, outbuf;
534 char buf[KPROP_BUFSIZ];
535 krb5_error_code retval;
541 send_size = htonl(database_size);
542 inbuf.data = (char *) &send_size;
543 inbuf.length = sizeof(send_size); /* must be 4, really */
544 /* KPROP_CKSUMTYPE */
545 if (retval = krb5_mk_safe(context, auth_context, &inbuf,
547 com_err(progname, retval, "while encoding database size");
548 send_error(context, my_creds, fd, "while encoding database size", retval);
551 if (retval = krb5_write_message(context, (void *) &fd, &outbuf)) {
552 krb5_free_data_contents(context, &outbuf);
553 com_err(progname, retval, "while sending database size");
556 krb5_free_data_contents(context, &outbuf);
558 * Initialize the initial vector.
560 if (retval = krb5_auth_con_initivector(context, auth_context)) {
561 send_error(context, my_creds, fd,
562 "failed while initializing i_vector", retval);
563 com_err(progname, retval, "while allocating i_vector");
567 * Send over the file, block by block....
571 while (n = read(database_fd, buf, sizeof(buf))) {
573 if (retval = krb5_mk_priv(context, auth_context, &inbuf,
576 "while encoding database block starting at %d",
578 com_err(progname, retval, buf);
579 send_error(context, my_creds, fd, buf, retval);
582 if (retval = krb5_write_message(context, (void *)&fd,&outbuf)) {
583 krb5_free_data_contents(context, &outbuf);
584 com_err(progname, retval,
585 "while sending database block starting at %d",
589 krb5_free_data_contents(context, &outbuf);
592 printf("%d bytes sent.\n", sent_size);
594 if (sent_size != database_size) {
595 com_err(progname, 0, "Premature EOF found for database file!");
596 send_error(context, my_creds, fd,"Premature EOF found for database file!",
597 KRB5KRB_ERR_GENERIC);
601 * OK, we've sent the database; now let's wait for a success
602 * indication from the remote end.
604 if (retval = krb5_read_message(context, (void *) &fd, &inbuf)) {
605 com_err(progname, retval,
606 "while reading response from server");
610 * If we got an error response back from the server, display
613 if (krb5_is_krb_error(&inbuf)) {
614 if (retval = krb5_rd_error(context, &inbuf, &error)) {
615 com_err(progname, retval,
616 "while decoding error response from server");
619 if (error->error == KRB_ERR_GENERIC) {
620 if (error->text.data)
622 "Generic remote error: %s\n",
624 } else if (error->error) {
625 com_err(progname, error->error + ERROR_TABLE_BASE_krb5,
626 "signalled from server");
627 if (error->text.data)
629 "Error text from server: %s\n",
632 krb5_free_error(context, error);
635 if (retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL)) {
636 com_err(progname, retval,
637 "while decoding final size packet from server");
640 memcpy((char *)&send_size, outbuf.data, sizeof(send_size));
641 send_size = ntohl(send_size);
642 if (send_size != database_size) {
644 "Kpropd sent database size %d, expecting %d",
645 send_size, database_size);
653 send_error(context, my_creds, fd, err_text, err_code)
654 krb5_context context;
655 krb5_creds *my_creds;
658 krb5_error_code err_code;
664 memset((char *)&error, 0, sizeof(error));
665 krb5_us_timeofday(context, &error.ctime, &error.cusec);
666 error.server = my_creds->server;
667 error.client = my_principal;
668 error.error = err_code - ERROR_TABLE_BASE_krb5;
669 if (error.error > 127)
670 error.error = KRB_ERR_GENERIC;
674 text = error_message(err_code);
675 error.text.length = strlen(text) + 1;
676 if (error.text.data = malloc(error.text.length)) {
677 strcpy(error.text.data, text);
678 if (!krb5_mk_error(context, &error, &outbuf)) {
679 (void) krb5_write_message(context, (void *)&fd,&outbuf);
680 krb5_free_data_contents(context, &outbuf);
682 free(error.text.data);
686 void update_last_prop_file(hostname, file_name)
690 /* handle slave locking/failure stuff */
691 char *file_last_prop;
693 static char last_prop[]=".last_prop";
695 if ((file_last_prop = (char *)malloc(strlen(file_name) +
696 strlen(hostname) + 1 +
697 strlen(last_prop) + 1)) == NULL) {
698 com_err(progname, ENOMEM,
699 "while allocating filename for update_last_prop_file");
702 strcpy(file_last_prop, file_name);
703 strcat(file_last_prop, ".");
704 strcat(file_last_prop, hostname);
705 strcat(file_last_prop, last_prop);
706 if ((fd = THREEPARAMOPEN(file_last_prop, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
707 com_err(progname, errno,
708 "while creating 'last_prop' file, '%s'",
710 free(file_last_prop);
714 free(file_last_prop);