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>
44 #ifdef NEED_SYS_FCNTL_H
45 #include <sys/fcntl.h>
50 static char *kprop_version = KPROP_PROT_VERSION;
57 char *file = KPROP_DEFAULT_FILE;
60 krb5_principal my_principal; /* The Kerberos principal we'll be */
61 /* running under, initialized in */
63 krb5_ccache ccache; /* Credentials cache which we'll be using */
64 /* krb5_creds my_creds; /* My credentials */
66 krb5_address sender_addr;
67 krb5_address receiver_addr;
70 PROTOTYPE((krb5_context, char **));
72 PROTOTYPE((krb5_context));
75 krb5_error_code open_connection
76 PROTOTYPE((char *, int *, char *));
77 void kerberos_authenticate
78 PROTOTYPE((krb5_context, krb5_auth_context **,
79 int, krb5_principal, krb5_creds **));
81 PROTOTYPE((krb5_context, char *, int *));
83 PROTOTYPE((krb5_context, int));
85 PROTOTYPE((krb5_context, krb5_auth_context *, krb5_creds *,
88 PROTOTYPE((krb5_context, krb5_creds *, int, char *, krb5_error_code));
89 void update_last_prop_file
90 PROTOTYPE((char *, char *));
94 fprintf(stderr, "\nUsage: %s [-r realm] [-f file] [-d] [-P port] [-s srvtab] slave_host\n\n",
104 int fd, database_fd, database_size;
105 krb5_error_code retval;
106 krb5_context context;
107 krb5_creds *my_creds;
108 krb5_auth_context * auth_context;
112 krb5_init_context(&context);
113 get_tickets(context);
115 database_fd = open_database(context, file, &database_size);
116 if (retval = open_connection(slave_host, &fd, Errmsg)) {
117 com_err(progname, retval, "%s while opening connection to %s",
122 fprintf(stderr, "%s: %s while opening connection to %s\n",
123 progname, Errmsg, slave_host);
126 kerberos_authenticate(context, &auth_context, fd, my_principal,
128 xmit_database(context, auth_context, my_creds, fd, database_fd,
130 update_last_prop_file(slave_host, file);
131 printf("Database propagation to %s: SUCCEEDED\n", slave_host);
132 krb5_free_cred_contents(context, my_creds);
133 close_database(context, database_fd);
137 void PRS(context, argv)
138 krb5_context context;
141 register char *word, ch;
143 krb5_init_context(&context);
144 krb5_init_ets(context);
146 while (word = *argv++) {
149 while (word && (ch = *word++)) {
174 port = htons(atoi(word));
176 port = htons(atoi(*argv++));
206 void get_tickets(context)
207 krb5_context context;
209 char my_host_name[MAXHOSTNAMELEN];
213 krb5_error_code retval;
214 static char tkstring[] = "/tmp/kproptktXXXXXX";
215 krb5_keytab keytab = NULL;
218 * Figure out what tickets we'll be using to send stuff
220 if (gethostname (my_host_name, sizeof(my_host_name)) != 0) {
221 com_err(progname, errno, "while getting my hostname");
224 /* get canonicalized service instance name */
225 if (!(hp = gethostbyname(my_host_name))) {
226 fprintf(stderr, "Couldn't get my cannonicalized host name!\n");
229 for (cp=hp->h_name; *cp; cp++)
233 sprintf(buf, "host/%s@%s", hp->h_name, realm);
235 sprintf(buf, "host/%s", hp->h_name);
236 if (retval = krb5_parse_name(context, buf, &my_principal)) {
237 com_err (progname, retval, "when parsing name %s",buf);
242 * Initialize cache file which we're going to be using
244 (void) mktemp(tkstring);
245 sprintf(buf, "FILE:%s", tkstring);
246 if (retval = krb5_cc_resolve(context, buf, &ccache)) {
247 com_err(progname, retval, "while opening crednetials cache %s",
251 if (retval = krb5_cc_initialize(context, ccache, my_principal)) {
252 com_err (progname, retval, "when initializing cache %s",
258 * Get the tickets we'll need.
260 * Construct the principal name for the slave host.
262 memset((char *)&creds, 0, sizeof(creds));
263 if (!(hp = gethostbyname(slave_host))) {
265 "Couldn't get cannonicalized name for slave\n");
268 for (cp=hp->h_name; *cp; cp++)
271 if (!(slave_host = malloc(strlen(hp->h_name) + 1))) {
272 com_err(progname, ENOMEM,
273 "while allocate space for canonicalized slave host");
276 strcpy(slave_host, hp->h_name);
278 sprintf(buf, "%s/%s@%s", KPROP_SERVICE_NAME, slave_host,
281 sprintf(buf, "%s/%s", KPROP_SERVICE_NAME, hp->h_name);
282 if (retval = krb5_parse_name(context, buf, &creds.server)) {
283 com_err(progname, retval,
284 "while parsing slave principal name");
288 * Now fill in the client....
290 if (retval = krb5_copy_principal(context, my_principal, &creds.client)) {
291 com_err(progname, retval, "While copying client principal");
295 if (retval = krb5_kt_resolve(context, srvtab, &keytab)) {
296 com_err(progname, retval, "while resolving keytab");
301 retval = krb5_get_in_tkt_with_keytab(context, 0, 0, NULL,
302 NULL, keytab, ccache, &creds, 0);
304 com_err(progname, retval, "while getting initial ticket\n");
308 * Now destroy the cache right away --- the credentials we
309 * need will be in my_creds.
311 if (retval = krb5_cc_destroy(context, ccache)) {
312 com_err(progname, retval, "while destroying ticket cache");
318 open_connection(host, fd, Errmsg)
324 krb5_error_code retval;
327 register struct servent *sp;
328 struct sockaddr_in sin;
331 hp = gethostbyname(host);
333 (void) sprintf(Errmsg, "%s: unknown host", host);
337 sin.sin_family = hp->h_addrtype;
338 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
340 sp = getservbyname(KPROP_SERVICE, "tcp");
342 (void) strcpy(Errmsg, KPROP_SERVICE);
343 (void) strcat(Errmsg, "/tcp: unknown service");
347 sin.sin_port = sp->s_port;
350 s = socket(AF_INET, SOCK_STREAM, 0);
353 (void) sprintf(Errmsg, "in call to socket");
356 if (connect(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
359 (void) sprintf(Errmsg, "in call to connect");
365 * Set receiver_addr and sender_addr.
367 receiver_addr.addrtype = ADDRTYPE_INET;
368 receiver_addr.length = sizeof(sin.sin_addr);
369 receiver_addr.contents = (krb5_octet *) malloc(sizeof(sin.sin_addr));
370 memcpy((char *) receiver_addr.contents, (char *) &sin.sin_addr,
371 sizeof(sin.sin_addr));
373 socket_length = sizeof(sin);
374 if (getsockname(s, (struct sockaddr *)&sin, &socket_length) < 0) {
377 (void) sprintf(Errmsg, "in call to getsockname");
380 sender_addr.addrtype = ADDRTYPE_INET;
381 sender_addr.length = sizeof(sin.sin_addr);
382 sender_addr.contents = (krb5_octet *) malloc(sizeof(sin.sin_addr));
383 memcpy((char *) sender_addr.contents, (char *) &sin.sin_addr,
384 sizeof(sin.sin_addr));
390 void kerberos_authenticate(context, auth_context, fd, me, new_creds)
391 krb5_context context;
392 krb5_auth_context **auth_context;
395 krb5_creds ** new_creds;
397 krb5_error_code retval;
398 krb5_error *error = NULL;
399 krb5_ap_rep_enc_part *rep_result;
401 if (retval = krb5_auth_con_init(context, auth_context))
404 krb5_auth_con_setflags(context, *auth_context,
405 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
407 if (retval = krb5_auth_con_setaddrs(context, *auth_context, &sender_addr,
409 com_err(progname, retval, "in krb5_auth_con_setaddrs");
413 if (retval = krb5_sendauth(context, auth_context, (void *)&fd,
414 kprop_version, me, creds.server,
415 AP_OPTS_MUTUAL_REQUIRED, NULL, &creds, NULL,
416 &error, &rep_result, new_creds)) {
417 com_err(progname, retval, "while authenticating to server");
419 if (error->error == KRB_ERR_GENERIC) {
420 if (error->text.data)
422 "Generic remote error: %s\n",
424 } else if (error->error) {
426 error->error + ERROR_TABLE_BASE_krb5,
427 "signalled from server");
428 if (error->text.data)
430 "Error text from server: %s\n",
433 krb5_free_error(context, error);
437 krb5_free_ap_rep_enc_part(context, rep_result);
443 * Open the Kerberos database dump file. Takes care of locking it
444 * and making sure that the .ok file is more recent that the database
447 * Returns the file descriptor of the database dump file. Also fills
448 * in the size of the database file.
451 open_database(context, data_fn, size)
452 krb5_context context;
458 struct stat stbuf, stbuf_ok;
460 static char ok[] = ".dump_ok";
462 dbpathname = strdup(data_fn);
464 com_err(progname, ENOMEM, "allocating database file name '%s'",
468 if ((dbfp = fopen(dbpathname, "r")) == 0) {
469 com_err(progname, errno, "while trying to open %s",
474 err = krb5_lock_file(context, dbfp, dbpathname,
475 KRB5_LOCKMODE_SHARED|KRB5_LOCKMODE_DONTBLOCK);
476 if (err == EAGAIN || err == EWOULDBLOCK || errno == EACCES) {
477 com_err(progname, 0, "database locked");
480 com_err(progname, err, "while trying to lock '%s'", dbpathname);
484 if (fstat(fd, &stbuf)) {
485 com_err(progname, errno, "while trying to stat %s",
489 if ((data_ok_fn = (char *) malloc(strlen(data_fn)+strlen(ok)+1))
491 com_err(progname, ENOMEM, "while trying to malloc data_ok_fn");
494 strcat(strcpy(data_ok_fn, data_fn), ok);
495 if (stat(data_ok_fn, &stbuf_ok)) {
496 com_err(progname, errno, "while trying to stat %s",
502 if (stbuf.st_mtime > stbuf_ok.st_mtime) {
503 com_err(progname, 0, "'%s' more recent than '%s'.",
504 data_fn, data_ok_fn);
507 *size = stbuf.st_size;
512 close_database(context, fd)
513 krb5_context context;
517 if (fd != fileno(dbfp)) {
518 com_err(progname, 0, "bad fd passed to close_database");
521 err = krb5_lock_file(context, dbfp, dbpathname, KRB5_LOCKMODE_UNLOCK);
523 com_err(progname, err, "while unlocking database '%s'", dbpathname);
530 * Now we send over the database. We use the following protocol:
531 * Send over a KRB_SAFE message with the size. Then we send over the
532 * database in blocks of KPROP_BLKSIZE, encrypted using KRB_PRIV.
533 * Then we expect to see a KRB_SAFE message with the size sent back.
535 * At any point in the protocol, we may send a KRB_ERROR message; this
536 * will abort the entire operation.
539 xmit_database(context, auth_context, my_creds, fd, database_fd, database_size)
540 krb5_context context;
541 krb5_auth_context * auth_context;
542 krb5_creds *my_creds;
547 krb5_int32 send_size, sent_size, n;
548 krb5_data inbuf, outbuf;
549 char buf[KPROP_BUFSIZ];
550 krb5_error_code retval;
556 send_size = htonl(database_size);
557 inbuf.data = (char *) &send_size;
558 inbuf.length = sizeof(send_size); /* must be 4, really */
559 /* KPROP_CKSUMTYPE */
560 if (retval = krb5_mk_safe(context, auth_context, &inbuf,
562 com_err(progname, retval, "while encoding database size");
563 send_error(context, my_creds, fd, "while encoding database size", retval);
566 if (retval = krb5_write_message(context, (void *) &fd, &outbuf)) {
567 krb5_xfree(outbuf.data);
568 com_err(progname, retval, "while sending database size");
571 krb5_xfree(outbuf.data);
573 * Initialize the initial vector.
575 if (retval = krb5_auth_con_initivector(context, auth_context)) {
576 send_error(context, my_creds, fd,
577 "failed while initializing i_vector", retval);
578 com_err(progname, retval, "while allocating i_vector");
582 * Send over the file, block by block....
586 while (n = read(database_fd, buf, sizeof(buf))) {
588 if (retval = krb5_mk_priv(context, auth_context, &inbuf,
591 "while encoding database block starting at %d",
593 com_err(progname, retval, buf);
594 send_error(context, my_creds, fd, buf, retval);
597 if (retval = krb5_write_message(context, (void *)&fd,&outbuf)) {
598 krb5_xfree(outbuf.data);
599 com_err(progname, retval,
600 "while sending database block starting at %d",
604 krb5_xfree(outbuf.data);
607 printf("%d bytes sent.\n", sent_size);
609 if (sent_size != database_size) {
610 com_err(progname, 0, "Premature EOF found for database file!");
611 send_error(context, my_creds, fd,"Premature EOF found for database file!",
612 KRB5KRB_ERR_GENERIC);
616 * OK, we've sent the database; now let's wait for a success
617 * indication from the remote end.
619 if (retval = krb5_read_message(context, (void *) &fd, &inbuf)) {
620 com_err(progname, retval,
621 "while reading response from server");
625 * If we got an error response back from the server, display
628 if (krb5_is_krb_error(&inbuf)) {
629 if (retval = krb5_rd_error(context, &inbuf, &error)) {
630 com_err(progname, retval,
631 "while decoding error response from server");
634 if (error->error == KRB_ERR_GENERIC) {
635 if (error->text.data)
637 "Generic remote error: %s\n",
639 } else if (error->error) {
640 com_err(progname, error->error + ERROR_TABLE_BASE_krb5,
641 "signalled from server");
642 if (error->text.data)
644 "Error text from server: %s\n",
647 krb5_free_error(context, error);
650 if (retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL)) {
651 com_err(progname, retval,
652 "while decoding final size packet from server");
655 memcpy((char *)&send_size, outbuf.data, sizeof(send_size));
656 send_size = ntohl(send_size);
657 if (send_size != database_size) {
659 "Kpropd sent database size %d, expecting %d",
660 send_size, database_size);
668 send_error(context, my_creds, fd, err_text, err_code)
669 krb5_context context;
670 krb5_creds *my_creds;
673 krb5_error_code err_code;
679 memset((char *)&error, 0, sizeof(error));
680 krb5_us_timeofday(context, &error.ctime, &error.cusec);
681 error.server = my_creds->server;
682 error.client = my_principal;
683 error.error = err_code - ERROR_TABLE_BASE_krb5;
684 if (error.error > 127)
685 error.error = KRB_ERR_GENERIC;
689 text = error_message(err_code);
690 error.text.length = strlen(text) + 1;
691 if (error.text.data = malloc(error.text.length)) {
692 strcpy(error.text.data, text);
693 if (!krb5_mk_error(context, &error, &outbuf)) {
694 (void) krb5_write_message(context, (void *)&fd,&outbuf);
695 krb5_xfree(outbuf.data);
697 free(error.text.data);
701 void update_last_prop_file(hostname, file_name)
705 /* handle slave locking/failure stuff */
706 char *file_last_prop;
708 static char last_prop[]=".last_prop";
710 if ((file_last_prop = (char *)malloc(strlen(file_name) +
711 strlen(hostname) + 1 +
712 strlen(last_prop) + 1)) == NULL) {
713 com_err(progname, ENOMEM,
714 "while allocating filename for update_last_prop_file");
717 strcpy(file_last_prop, file_name);
718 strcat(file_last_prop, ".");
719 strcat(file_last_prop, hostname);
720 strcat(file_last_prop, last_prop);
721 if ((fd = open(file_last_prop, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
722 com_err(progname, errno,
723 "while creating 'last_prop' file, '%s'",
725 free(file_last_prop);
728 free(file_last_prop);