/* $OpenBSD: kprop.c,v 1.6 1998/02/25 15:51:03 art Exp $ */ /* $KTH: kprop.c,v 1.30 1997/12/05 01:34:49 assar Exp $ */ /* * This source code is no longer held under any constraint of USA * `cryptographic laws' since it was exported legally. The cryptographic * functions were removed from the code and a "Bones" distribution was * made. A Commodity Jurisdiction Request #012-94 was filed with the * USA State Department, who handed it to the Commerce department. The * code was determined to fall under General License GTDA under ECCN 5D96G, * and hence exportable. The cryptographic interfaces were re-added by Eric * Young, and then KTH proceeded to maintain the code in the free world. */ /*- * Copyright (C) 1987 by the Massachusetts Institute of Technology * * Export of this software from the United States of America is assumed * to require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, 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 name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ #include #include #include #include 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, "unknown error" ); 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/kproptktXXXXXXXXXX"; 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 (gethostname (my_host_name, sizeof(my_host_name)) != 0) { warnx ("gethostname(%s): %s", my_host_name, "unknown error" ); 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; 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 (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 (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