freebsd-dev/crypto/kerberosIV/slave/kprop.c

531 lines
14 KiB
C
Raw Normal View History

/*
Copyright 1987, 1988 by the Student Information Processing Board
of the Massachusetts Institute of Technology
Permission to use, copy, modify, and distribute this software
and its documentation for any purpose and without fee is
hereby granted, provided that the above copyright notice
appear in all copies and that both that copyright notice and
this permission notice appear in supporting documentation,
and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
used in advertising or publicity pertaining to distribution
of the software without specific, written prior permission.
M.I.T. and the M.I.T. S.I.P.B. make no representations about
the suitability of this software for any purpose. It is
provided "as is" without express or implied warranty.
*/
#include "slav_locl.h"
RCSID("$Id: kprop.c,v 1.29 1997/05/25 02:43:54 joda Exp $");
#include "kprop.h"
static char kprop_version[KPROP_PROT_VERSION_LEN] = KPROP_PROT_VERSION;
int debug = 0;
char my_realm[REALM_SZ];
int princ_data_size = 3 * sizeof(int32_t) + 3 * sizeof(unsigned char);
short transfer_mode, net_transfer_mode;
int force_flag;
static char ok[] = ".dump_ok";
struct slave_host {
u_int32_t net_addr;
char *name;
char *instance;
char *realm;
int not_time_yet;
int succeeded;
struct slave_host *next;
};
static
int get_slaves(struct slave_host **psl, char *file, time_t ok_mtime)
{
FILE *fin;
char namebuf[128], *inst;
char *pc;
struct hostent *host;
struct slave_host **th;
char path[256];
char *ppath;
struct stat stbuf;
if ((fin = fopen(file, "r")) == NULL)
err (1, "open(%s)", file);
strcpy(path, file);
if ((ppath = strrchr(path, '/'))) {
ppath += 1;
} else {
ppath = path;
}
th = psl;
while(fgets(namebuf, sizeof(namebuf), fin)){
if ((pc = strchr(namebuf, '\n'))) {
*pc = '\0';
} else {
if(strlen(namebuf) == sizeof(namebuf) - 1){
warnx ("Hostname too long (>= %d chars) in '%s'.",
(int) sizeof(namebuf), file);
do{
if(fgets(namebuf, sizeof(namebuf), fin) == NULL)
break;
}while(strchr(namebuf, '\n') == NULL);
continue;
}
}
if(namebuf[0] == 0 || namebuf[0] == '#')
continue;
host = gethostbyname(namebuf);
if (host == NULL) {
warnx ("Ignoring host '%s' in '%s': %s",
namebuf, file,
#ifdef HAVE_H_ERRNO
hstrerror(h_errno)
#else
"unknown error"
#endif
);
continue;
}
(*th) = (struct slave_host *) malloc(sizeof(struct slave_host));
if (!*th)
errx (1, "No memory reading host list from '%s'.",
file);
memset(*th, 0, sizeof(struct slave_host));
(*th)->name = strdup(namebuf);
if ((*th)->name == NULL)
errx (1, "No memory reading host list from '%s'.",
file);
/* get kerberos cannonical instance name */
inst = krb_get_phost ((*th)->name);
(*th)->instance = strdup(inst);
if ((*th)->instance == NULL)
errx (1, "No memory reading host list from '%s'.",
file);
/* what a concept, slave servers in different realms! */
(*th)->realm = my_realm;
memcpy(&(*th)->net_addr, host->h_addr, sizeof((*th)->net_addr));
(*th)->not_time_yet = 0;
(*th)->succeeded = 0;
(*th)->next = NULL;
strcat(strcpy(ppath, (*th)->name), "-last-prop");
if (!force_flag && !stat(path, &stbuf) && stbuf.st_mtime > ok_mtime) {
(*th)->not_time_yet = 1;
(*th)->succeeded = 1; /* no change since last success */
}
th = &(*th)->next;
}
fclose(fin);
return (1);
}
/* The master -> slave protocol looks like this:
1) 8 byte version string
2) 2 bytes of "transfer mode" (net byte order of course)
3) ticket/authentication send by sendauth
4) 4 bytes of "block" length (u_int32_t)
5) data
4 and 5 repeat til EOF ...
*/
static int
prop_to_slaves(struct slave_host *sl, int fd, char *fslv)
{
u_char buf[KPROP_BUFSIZ];
u_char obuf[KPROP_BUFSIZ + 64]; /* leave room for private msg overhead */
struct sockaddr_in sin, my_sin;
int i, n, s;
struct slave_host *cs; /* current slave */
char path[256], my_host_name[MaxHostNameLen], *p_my_host_name;
char kprop_service_instance[INST_SZ];
char *pc;
u_int32_t cksum;
u_int32_t length, nlength;
long kerror;
KTEXT_ST ticket;
CREDENTIALS cred;
MSG_DAT msg_dat;
static char tkstring[] = "/tmp/kproptktXXXXXX";
des_key_schedule session_sched;
close(mkstemp(tkstring));
krb_set_tkt_string(tkstring);
memset(&sin, 0, sizeof sin);
sin.sin_family = AF_INET;
sin.sin_port = k_getportbyname ("krb_prop", "tcp", htons(KPROP_PORT));
sin.sin_addr.s_addr = INADDR_ANY;
strcpy(path, fslv);
if ((pc = strrchr(path, '/'))) {
pc += 1;
} else {
pc = path;
}
for (i = 0; i < 5; i++) { /* try each slave five times max */
for (cs = sl; cs; cs = cs->next) {
if (!cs->succeeded) {
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err (1, "socket");
memcpy(&sin.sin_addr, &cs->net_addr,
sizeof cs->net_addr);
if (connect(s, (struct sockaddr *) &sin, sizeof sin) < 0) {
warn ("connect(%s)", cs->name);
close(s);
continue; /*** NEXT SLAVE ***/
}
/* for krb_mk_{priv, safe} */
memset(&my_sin, 0, sizeof my_sin);
n = sizeof my_sin;
if (getsockname (s, (struct sockaddr *) &my_sin, &n) != 0) {
warn ("getsockname(%s)", cs->name);
close (s);
continue; /*** NEXT SLAVE ***/
}
if (n != sizeof (my_sin)) {
warnx ("can't get socketname %s length", cs->name);
close (s);
continue; /*** NEXT SLAVE ***/
}
/* Get ticket */
kerror = krb_mk_req (&ticket, KPROP_SERVICE_NAME,
cs->instance, cs->realm, (u_int32_t) 0);
/* if ticket has expired try to get a new one, but
* first get a TGT ...
*/
if (kerror != MK_AP_OK) {
if (k_gethostname (my_host_name, sizeof(my_host_name)) != 0) {
warnx ("gethostname(%s): %s",
my_host_name,
#ifdef HAVE_H_ERRNO
hstrerror(h_errno)
#else
"unknown error"
#endif
);
close (s);
break; /* next one can't work either! */
}
/* get canonical kerberos service instance name */
p_my_host_name = krb_get_phost (my_host_name);
/* copy it to make sure gethostbyname static doesn't
* screw us. */
strcpy (kprop_service_instance, p_my_host_name);
kerror = krb_get_svc_in_tkt (KPROP_SERVICE_NAME,
#if 0
kprop_service_instance,
#else
KRB_MASTER,
#endif
my_realm,
KRB_TICKET_GRANTING_TICKET,
my_realm,
96,
KPROP_SRVTAB);
if (kerror != INTK_OK) {
warnx ("%s: %s. While getting initial ticket\n",
cs->name, krb_get_err_text(kerror));
close (s);
goto punt;
}
kerror = krb_mk_req (&ticket, KPROP_SERVICE_NAME,
cs->instance, cs->realm, (u_int32_t) 0);
}
if (kerror != MK_AP_OK) {
warnx ("%s: krb_mk_req: %s",
cs->name, krb_get_err_text(kerror));
close (s);
continue; /*** NEXT SLAVE ***/
}
if (write(s, kprop_version, sizeof(kprop_version))
!= sizeof(kprop_version)) {
warn ("%s", cs->name);
close (s);
continue; /*** NEXT SLAVE ***/
}
net_transfer_mode = htons (transfer_mode);
if (write(s, &net_transfer_mode, sizeof(net_transfer_mode))
!= sizeof(net_transfer_mode)) {
warn ("write(%s)", cs->name);
close (s);
continue; /*** NEXT SLAVE ***/
}
kerror = krb_get_cred (KPROP_SERVICE_NAME, cs->instance,
cs->realm, &cred);
if (kerror != KSUCCESS) {
warnx ("%s: %s. Getting session key.",
cs->name, krb_get_err_text(kerror));
close (s);
continue; /*** NEXT SLAVE ***/
}
#ifdef NOENCRYPTION
memset(session_sched, 0, sizeof(session_sched));
#else
if (des_key_sched (&cred.session, session_sched)) {
warnx ("%s: can't make key schedule.",
cs->name);
close (s);
continue; /*** NEXT SLAVE ***/
}
#endif
/* SAFE (quad_cksum) and CLEAR are just not good enough */
cksum = 0;
#ifdef not_working_yet
if (transfer_mode != KPROP_TRANSFER_PRIVATE) {
cksum = get_data_checksum(fd, session_sched);
lseek(fd, 0L, 0);
}
else
#endif
{
struct stat st;
fstat (fd, &st);
cksum = st.st_size;
}
kerror = krb_sendauth(KOPT_DO_MUTUAL,
s,
&ticket,
KPROP_SERVICE_NAME,
cs->instance,
cs->realm,
cksum,
&msg_dat,
&cred,
session_sched,
&my_sin,
&sin,
KPROP_PROT_VERSION);
if (kerror != KSUCCESS) {
warnx ("%s: krb_sendauth: %s.",
cs->name, krb_get_err_text(kerror));
close (s);
continue; /*** NEXT SLAVE ***/
}
lseek(fd, 0L, SEEK_SET); /* Rewind file before rereading it. */
while ((n = read(fd, buf, sizeof buf))) {
if (n < 0)
err (1, "read");
switch (transfer_mode) {
case KPROP_TRANSFER_PRIVATE:
case KPROP_TRANSFER_SAFE:
if (transfer_mode == KPROP_TRANSFER_PRIVATE)
length = krb_mk_priv (buf, obuf, n,
session_sched, &cred.session,
&my_sin, &sin);
else
length = krb_mk_safe (buf, obuf, n,
&cred.session,
&my_sin, &sin);
if (length == -1) {
warnx ("%s: %s failed.",
cs->name,
(transfer_mode == KPROP_TRANSFER_PRIVATE)
? "krb_rd_priv" : "krb_rd_safe");
close (s);
continue; /*** NEXT SLAVE ***/
}
nlength = htonl(length);
if (write(s, &nlength, sizeof nlength)
!= sizeof nlength) {
warn ("write(%s)", cs->name);
close (s);
continue; /*** NEXT SLAVE ***/
}
if (write(s, obuf, length) != length) {
warn ("write(%s)", cs->name);
close(s);
continue; /*** NEXT SLAVE ***/
}
break;
case KPROP_TRANSFER_CLEAR:
if (write(s, buf, n) != n) {
warn ("write(%s)", cs->name);
close(s);
continue; /*** NEXT SLAVE ***/
}
break;
}
}
close(s);
cs->succeeded = 1;
fprintf(stderr, "%s: success.\n", cs->name);
strcat(strcpy(pc, cs->name), "-last-prop");
unlink(path);
close(creat(path, 0600));
}
}
}
punt:
dest_tkt();
for (cs = sl; cs; cs = cs->next) {
if (!cs->succeeded)
return (0); /* didn't get this slave */
}
return (1);
}
static void
usage()
{
/* already got floc and fslv, what is this? */
fprintf(stderr,
"\nUsage: kprop [-force] [-realm realm] [-private"
#ifdef not_safe_yet
"|-safe|-clear"
#endif
"] [data_file [slaves_file]]\n\n");
exit(1);
}
int
main(int argc, char **argv)
{
int fd, i;
char *floc, *floc_ok;
char *fslv;
struct stat stbuf, stbuf_ok;
time_t l_init, l_final;
char *pc;
int l_diff;
static struct slave_host *slave_host_list = NULL;
struct slave_host *sh;
set_progname (argv[0]);
transfer_mode = KPROP_TRANSFER_PRIVATE;
time(&l_init);
pc = ctime(&l_init);
pc[strlen(pc) - 1] = '\0';
printf("\nStart slave propagation: %s\n", pc);
floc = NULL;
fslv = NULL;
if (krb_get_lrealm(my_realm,1) != KSUCCESS)
errx (1, "Getting my kerberos realm. Check krb.conf");
for (i = 1; i < argc; i++)
switch (argv[i][0]) {
case '-':
if (strcmp (argv[i], "-private") == 0)
transfer_mode = KPROP_TRANSFER_PRIVATE;
#ifdef not_safe_yet
else if (strcmp (argv[i], "-safe") == 0)
transfer_mode = KPROP_TRANSFER_SAFE;
else if (strcmp (argv[i], "-clear") == 0)
transfer_mode = KPROP_TRANSFER_CLEAR;
#endif
else if (strcmp (argv[i], "-realm") == 0) {
i++;
if (i < argc)
strcpy(my_realm, argv[i]);
else
usage();
} else if (strcmp (argv[i], "-force") == 0)
force_flag++;
else {
warnx("unknown control argument %s.", argv[i]);
usage ();
}
break;
default:
/* positional arguments are marginal at best ... */
if (floc == NULL)
floc = argv[i];
else {
if (fslv == NULL)
fslv = argv[i];
else
usage();
}
}
if(floc == NULL)
floc = DB_DIR "/slave_dump";
if(fslv == NULL)
fslv = DB_DIR "/slaves";
asprintf (&floc_ok, "%s%s", floc, ok);
if (floc_ok == NULL)
errx (1, "out of memory in copying %s", floc);
if ((fd = open(floc, O_RDONLY)) < 0)
err (1, "open(%s)", floc);
if (k_flock(fd, K_LOCK_SH | K_LOCK_NB))
err (1, "flock(%s)", floc);
if (stat(floc, &stbuf))
err (1, "stat(%s)", floc);
if (stat(floc_ok, &stbuf_ok))
err (1, "stat(%s)", floc_ok);
if (stbuf.st_mtime > stbuf_ok.st_mtime)
errx (1, "'%s' more recent than '%s'.", floc, floc_ok);
if (!get_slaves(&slave_host_list, fslv, stbuf_ok.st_mtime))
errx (1, "can't read slave host file '%s'.", fslv);
#ifdef KPROP_DBG
{
struct slave_host *sh;
int i;
fprintf(stderr, "\n\n");
fflush(stderr);
for (sh = slave_host_list; sh; sh = sh->next) {
fprintf(stderr, "slave %d: %s, %s", i++, sh->name,
inet_ntoa(sh->net_addr));
fflush(stderr);
}
}
#endif /* KPROP_DBG */
if (!prop_to_slaves(slave_host_list, fd, fslv))
errx (1, "propagation failed.");
if (k_flock(fd, K_LOCK_UN))
err (1, "flock(%s, LOCK_UN)", floc);
fprintf(stderr, "\n\n");
for (sh = slave_host_list; sh; sh = sh->next) {
fprintf(stderr, "%s:\t\t%s\n", sh->name,
(sh->not_time_yet? "Not time yet" : (sh->succeeded ? "Succeeded" : "FAILED")));
}
time(&l_final);
l_diff = l_final - l_init;
printf("propagation finished, %d:%02d:%02d elapsed\n",
l_diff / 3600, (l_diff % 3600) / 60, l_diff % 60);
exit(0);
}
#ifdef doesnt_work_yet
u_long get_data_checksum(fd, key_sched)
int fd;
des_key_schedule key_sched;
{
u_int32_t cksum = 0;
int n;
char buf[BUFSIZ];
u_int32_t obuf[2];
while (n = read(fd, buf, sizeof buf)) {
if (n < 0)
err (1, "read");
cksum = cbc_cksum(buf, obuf, n, key_sched, key_sched);
}
return cksum;
}
#endif