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. M.I.T. makes no representations about the suitability of
20 * this software for any purpose. It is provided "as is" without express
21 * or implied warranty.
33 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <sys/param.h>
41 #include <krb5/krb5.h>
42 #include <krb5/asn1.h>
43 #include <krb5/osconf.h>
45 #include <krb5/kdb_dbm.h>
46 #include <krb5/ext-proto.h>
47 #include <krb5/los-proto.h>
48 #include <krb5/libos.h>
51 #ifdef NEED_SYS_FCNTL_H
52 #include <sys/fcntl.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;
76 PROTOTYPE((krb5_context, char **));
78 PROTOTYPE((krb5_context));
81 krb5_error_code open_connection
82 PROTOTYPE((char *, int *, char *));
83 void kerberos_authenticate
84 PROTOTYPE((krb5_context, int, krb5_principal));
86 PROTOTYPE((krb5_context, char *, int *));
88 PROTOTYPE((krb5_context, int));
90 PROTOTYPE((krb5_context, int, int, int));
92 PROTOTYPE((krb5_context, int, char *, krb5_error_code));
93 void update_last_prop_file
94 PROTOTYPE((char *, char *));
98 fprintf(stderr, "\nUsage: %s [-r realm] [-f file] [-d] slave_host\n\n",
108 int fd, database_fd, database_size;
109 krb5_error_code retval;
110 krb5_context context;
114 get_tickets(context);
116 database_fd = open_database(context, file, &database_size);
117 if (retval = open_connection(slave_host, &fd, Errmsg)) {
118 com_err(progname, retval, "%s while opening connection to %s",
123 fprintf(stderr, "%s: %s while opening connection to %s\n",
124 progname, Errmsg, slave_host);
127 kerberos_authenticate(context, fd, my_principal);
129 printf("My sequence number: %d\n", my_seq_num);
130 printf("His sequence number: %d\n", his_seq_num);
132 xmit_database(context, fd, database_fd, database_size);
133 update_last_prop_file(slave_host, file);
134 printf("Database propagation to %s: SUCCEEDED\n", slave_host);
135 close_database(context, database_fd);
139 void PRS(context, argv)
140 krb5_context context;
143 register char *word, ch;
145 krb5_init_context(&context);
146 krb5_init_ets(context);
148 while (word = *argv++) {
151 while (word && (ch = *word++)) {
190 void get_tickets(context)
191 krb5_context context;
193 char my_host_name[MAXHOSTNAMELEN];
197 krb5_address **my_addresses;
198 krb5_error_code retval;
199 static char tkstring[] = "/tmp/kproptktXXXXXX";
202 * Figure out what tickets we'll be using to send stuff
204 if (gethostname (my_host_name, sizeof(my_host_name)) != 0) {
205 com_err(progname, errno, "while getting my hostname");
208 /* get canonicalized service instance name */
209 if (!(hp = gethostbyname(my_host_name))) {
210 fprintf(stderr, "Couldn't get my cannonicalized host name!\n");
213 for (cp=hp->h_name; *cp; cp++)
217 sprintf(buf, "host/%s@%s", hp->h_name, realm);
219 sprintf(buf, "host/%s", hp->h_name);
220 if (retval = krb5_parse_name(context, buf, &my_principal)) {
221 com_err (progname, retval, "when parsing name %s",buf);
226 * Initialize cache file which we're going to be using
228 (void) mktemp(tkstring);
229 sprintf(buf, "FILE:%s", tkstring);
230 if (retval = krb5_cc_resolve(context, buf, &ccache)) {
231 com_err(progname, retval, "while opening crednetials cache %s",
235 if (retval = krb5_cc_initialize(context, ccache, my_principal)) {
236 com_err (progname, retval, "when initializing cache %s",
242 * Get the tickets we'll need.
244 * Construct the principal name for the slave host.
246 memset((char *)&my_creds, 0, sizeof(my_creds));
247 if (!(hp = gethostbyname(slave_host))) {
249 "Couldn't get cannonicalized name for slave\n");
252 for (cp=hp->h_name; *cp; cp++)
255 if (!(slave_host = malloc(strlen(hp->h_name) + 1))) {
256 com_err(progname, ENOMEM,
257 "while allocate space for canonicalized slave host");
260 strcpy(slave_host, hp->h_name);
262 sprintf(buf, "%s/%s@%s", KPROP_SERVICE_NAME, slave_host,
265 sprintf(buf, "%s/%s", KPROP_SERVICE_NAME, hp->h_name);
266 if (retval = krb5_parse_name(context, buf, &my_creds.server)) {
267 com_err(progname, retval,
268 "while parsing slave principal name");
272 * Now fill in the client....
274 if (retval = krb5_copy_principal(context, my_principal, &my_creds.client)) {
275 com_err(progname, retval, "While copying client principal");
281 retval = krb5_os_localaddr(&my_addresses);
283 com_err(progname, retval,
284 "when getting my address");
287 retval = krb5_get_in_tkt_with_skey(context, 0, my_addresses,
290 0, ccache, &my_creds, 0);
292 com_err(progname, retval, "while getting initial ticket\n");
296 * Now destroy the cache right away --- the credentials we
297 * need will be in my_creds.
299 if (retval = krb5_cc_destroy(context, ccache)) {
300 com_err(progname, retval, "while destroying ticket cache");
306 open_connection(host, fd, Errmsg)
312 krb5_error_code retval;
315 register struct servent *sp;
316 struct sockaddr_in sin;
319 hp = gethostbyname(host);
321 (void) sprintf(Errmsg, "%s: unknown host", host);
325 sp = getservbyname(KPROP_SERVICE, "tcp");
327 (void) strcpy(Errmsg, KPROP_SERVICE);
328 (void) strcat(Errmsg, "/tcp: unknown service");
332 sin.sin_family = hp->h_addrtype;
333 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
334 sin.sin_port = sp->s_port;
335 s = socket(AF_INET, SOCK_STREAM, 0);
338 (void) sprintf(Errmsg, "in call to socket");
341 if (connect(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
344 (void) sprintf(Errmsg, "in call to connect");
350 * Set receiver_addr and sender_addr.
352 receiver_addr.addrtype = ADDRTYPE_INET;
353 receiver_addr.length = sizeof(sin.sin_addr);
354 receiver_addr.contents = (krb5_octet *) malloc(sizeof(sin.sin_addr));
355 memcpy((char *) receiver_addr.contents, (char *) &sin.sin_addr,
356 sizeof(sin.sin_addr));
358 socket_length = sizeof(sin);
359 if (getsockname(s, (struct sockaddr *)&sin, &socket_length) < 0) {
362 (void) sprintf(Errmsg, "in call to getsockname");
365 sender_addr.addrtype = ADDRTYPE_INET;
366 sender_addr.length = sizeof(sin.sin_addr);
367 sender_addr.contents = (krb5_octet *) malloc(sizeof(sin.sin_addr));
368 memcpy((char *) sender_addr.contents, (char *) &sin.sin_addr,
369 sizeof(sin.sin_addr));
375 void kerberos_authenticate(context, fd, me)
376 krb5_context context;
380 krb5_error_code retval;
381 krb5_error *error = NULL;
382 krb5_ap_rep_enc_part *rep_result;
384 if (retval = krb5_sendauth(context, (void *)&fd, kprop_version, me,
385 my_creds.server, AP_OPTS_MUTUAL_REQUIRED,
386 NULL, &my_creds, NULL, &my_seq_num, NULL,
387 &error, &rep_result)) {
388 com_err(progname, retval, "while authenticating to server");
390 if (error->error == KRB_ERR_GENERIC) {
391 if (error->text.data)
393 "Generic remote error: %s\n",
395 } else if (error->error) {
397 error->error + ERROR_TABLE_BASE_krb5,
398 "signalled from server");
399 if (error->text.data)
401 "Error text from server: %s\n",
404 krb5_free_error(context, error);
408 his_seq_num = rep_result->seq_number;
409 krb5_free_ap_rep_enc_part(context, rep_result);
415 * Open the Kerberos database dump file. Takes care of locking it
416 * and making sure that the .ok file is more recent that the database
419 * Returns the file descriptor of the database dump file. Also fills
420 * in the size of the database file.
423 open_database(context, data_fn, size)
424 krb5_context context;
430 struct stat stbuf, stbuf_ok;
432 static char ok[] = ".dump_ok";
434 dbpathname = strdup(data_fn);
436 com_err(progname, ENOMEM, "allocating database file name '%s'",
440 if ((dbfp = fopen(dbpathname, "r")) == 0) {
441 com_err(progname, errno, "while trying to open %s",
446 err = krb5_lock_file(context, dbfp, dbpathname,
447 KRB5_LOCKMODE_SHARED|KRB5_LOCKMODE_DONTBLOCK);
448 if (err == EAGAIN || err == EWOULDBLOCK || errno == EACCES) {
449 com_err(progname, 0, "database locked");
452 com_err(progname, err, "while trying to lock '%s'", dbpathname);
456 if (fstat(fd, &stbuf)) {
457 com_err(progname, errno, "while trying to stat %s",
461 if ((data_ok_fn = (char *) malloc(strlen(data_fn)+strlen(ok)+1))
463 com_err(progname, ENOMEM, "while trying to malloc data_ok_fn");
466 strcat(strcpy(data_ok_fn, data_fn), ok);
467 if (stat(data_ok_fn, &stbuf_ok)) {
468 com_err(progname, errno, "while trying to stat %s",
474 if (stbuf.st_mtime > stbuf_ok.st_mtime) {
475 com_err(progname, 0, "'%s' more recent than '%s'.",
476 data_fn, data_ok_fn);
479 *size = stbuf.st_size;
484 close_database(context, fd)
485 krb5_context context;
489 if (fd != fileno(dbfp)) {
490 com_err(progname, 0, "bad fd passed to close_database");
493 err = krb5_lock_file(context, dbfp, dbpathname, KRB5_LOCKMODE_UNLOCK);
495 com_err(progname, err, "while unlocking database '%s'", dbpathname);
502 * Now we send over the database. We use the following protocol:
503 * Send over a KRB_SAFE message with the size. Then we send over the
504 * database in blocks of KPROP_BLKSIZE, encrypted using KRB_PRIV.
505 * Then we expect to see a KRB_SAFE message with the size sent back.
507 * At any point in the protocol, we may send a KRB_ERROR message; this
508 * will abort the entire operation.
511 xmit_database(context, fd, database_fd, database_size)
512 krb5_context context;
517 int send_size, sent_size, n, eblock_size;
518 krb5_data inbuf, outbuf;
519 char buf[KPROP_BUFSIZ];
521 krb5_error_code retval;
527 send_size = htonl(database_size);
528 inbuf.data = (char *) &send_size;
529 inbuf.length = sizeof(send_size); /* must be 4, really */
530 if (retval = krb5_mk_safe(context, &inbuf, KPROP_CKSUMTYPE,
532 &sender_addr, &receiver_addr,
534 KRB5_PRIV_DOSEQUENCE|KRB5_SAFE_NOTIME,
535 0, /* no rcache when NOTIME */
537 com_err(progname, retval, "while encoding database size");
538 send_error(context, fd, "while encoding database size", retval);
541 if (retval = krb5_write_message(context, (void *) &fd, &outbuf)) {
542 krb5_xfree(outbuf.data);
543 com_err(progname, retval, "while sending database size");
546 krb5_xfree(outbuf.data);
548 * Initialize the initial vector.
550 eblock_size = krb5_keytype_array[my_creds.keyblock.keytype]->
551 system->block_length;
552 if (!(i_vector=malloc(eblock_size))) {
553 com_err(progname, ENOMEM, "while allocating i_vector");
554 send_error(context, fd,
555 "malloc failed while allocating i_vector", ENOMEM);
558 memset(i_vector, 0, eblock_size);
560 * Send over the file, block by block....
564 while (n = read(database_fd, buf, sizeof(buf))) {
566 if (retval = krb5_mk_priv(context, &inbuf, ETYPE_DES_CBC_CRC,
571 KRB5_PRIV_DOSEQUENCE|KRB5_PRIV_NOTIME,
572 0, /* again, no rcache */
576 "while encoding database block starting at %d",
578 com_err(progname, retval, buf);
579 send_error(context, fd, buf, retval);
582 if (retval = krb5_write_message(context, (void *)&fd,&outbuf)) {
583 krb5_xfree(outbuf.data);
584 com_err(progname, retval,
585 "while sending database block starting at %d",
589 krb5_xfree(outbuf.data);
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, 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, &inbuf, &my_creds.keyblock,
636 &receiver_addr, &sender_addr, his_seq_num++,
637 KRB5_SAFE_DOSEQUENCE|KRB5_SAFE_NOTIME,
639 com_err(progname, retval,
640 "while decoding final size packet from server");
643 memcpy((char *)&send_size, outbuf.data, sizeof(send_size));
644 send_size = ntohl(send_size);
645 if (send_size != database_size) {
647 "Kpropd sent database size %d, expecting %d",
648 send_size, database_size);
656 send_error(context, fd, err_text, err_code)
657 krb5_context context;
660 krb5_error_code err_code;
666 memset((char *)&error, 0, sizeof(error));
667 krb5_us_timeofday(context, &error.ctime, &error.cusec);
668 error.server = my_creds.server;
669 error.client = my_principal;
670 error.error = err_code - ERROR_TABLE_BASE_krb5;
671 if (error.error > 127)
672 error.error = KRB_ERR_GENERIC;
676 text = error_message(err_code);
677 error.text.length = strlen(text) + 1;
678 if (error.text.data = malloc(error.text.length)) {
679 strcpy(error.text.data, text);
680 if (!krb5_mk_error(context, &error, &outbuf)) {
681 (void) krb5_write_message(context, (void *)&fd,&outbuf);
682 krb5_xfree(outbuf.data);
684 free(error.text.data);
688 void update_last_prop_file(hostname, file_name)
692 /* handle slave locking/failure stuff */
693 char *file_last_prop;
695 static char last_prop[]=".last_prop";
697 if ((file_last_prop = (char *)malloc(strlen(file_name) +
698 strlen(hostname) + 1 +
699 strlen(last_prop) + 1)) == NULL) {
700 com_err(progname, ENOMEM,
701 "while allocating filename for update_last_prop_file");
704 strcpy(file_last_prop, file_name);
705 strcat(file_last_prop, ".");
706 strcat(file_last_prop, hostname);
707 strcat(file_last_prop, last_prop);
708 if ((fd = open(file_last_prop, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
709 com_err(progname, errno,
710 "while creating 'last_prop' file, '%s'",
712 free(file_last_prop);
715 free(file_last_prop);