f97a38cd65
the slave server). NOTE: This code should not be built, there is no documentation, and this method of database transfer is highly suboptimal. It's here just for those of us who actually have multiple K4 servers and want something more secure than the other distribution mechanisms. Obtained from: MIT Project Athena
446 lines
10 KiB
C
446 lines
10 KiB
C
/*
|
|
* Copyright 1987 by the Massachusetts Institute of Technology.
|
|
*
|
|
* For copying and distribution information, please see the file
|
|
* MIT.Copyright.
|
|
*
|
|
* kprop/kpropd have been abandonded by Project Athena (for good reason)
|
|
* however they still form the basis for one of the better ways for
|
|
* distributing kerberos databases. This version of kpropd has been
|
|
* adapted from the MIT distribution to work properly in a 4.4BSD
|
|
* environment.
|
|
*
|
|
* $Revision: 4.5 $ $Date: 92/10/23 15:45:46 $ $State: Exp $
|
|
* $Source$
|
|
*
|
|
* Log: kpropd.c,v
|
|
* Revision 4.5 92/10/23 15:45:46 tytso Make it possible
|
|
* to specify the location of the kdb_util program.
|
|
*
|
|
* Revision 4.4 91/06/15 03:20:51 probe Fixed <sys/types.h> inclusion
|
|
*
|
|
* Revision 4.3 89/05/16 15:06:04 wesommer Fix operator precedence stuff.
|
|
* Programmer: John Kohl.
|
|
*
|
|
* Revision 4.2 89/03/23 10:24:00 jtkohl NOENCRYPTION changes
|
|
*
|
|
* Revision 4.1 89/01/24 20:33:48 root name change
|
|
*
|
|
* Revision 4.0 89/01/24 18:45:06 wesommer Original version; programmer:
|
|
* wesommer auditor: jon
|
|
*
|
|
* Revision 4.5 88/01/08 18:07:46 jon formatting and rcs header changes */
|
|
|
|
/*
|
|
* This program is run on slave servers, to catch updates "pushed" from the
|
|
* master kerberos server in a realm.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char rcsid_kpropd_c[] =
|
|
"$Header: /afs/net.mit.edu/project/krb4/src/slave/RCS/kpropd.c,v 4.5 92/10/23 15:45:46 tytso Exp $";
|
|
#endif /* lint */
|
|
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/file.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <syslog.h>
|
|
#include <krb.h>
|
|
#include <krb_db.h>
|
|
|
|
#include "kprop.h"
|
|
|
|
static char kprop_version[KPROP_PROT_VERSION_LEN] = KPROP_PROT_VERSION;
|
|
|
|
extern int errno;
|
|
int debug = 0;
|
|
|
|
int pause_int = 300; /* 5 minutes in seconds */
|
|
unsigned long get_data_checksum();
|
|
static void SlowDeath();
|
|
/* leave room for private msg overhead */
|
|
static char buf[KPROP_BUFSIZ + 64];
|
|
|
|
static void
|
|
usage()
|
|
{
|
|
fprintf(stderr, "\nUsage: kpropd [-r realm] [-s srvtab] [-P kdb_util] fname\n");
|
|
exit(2);
|
|
}
|
|
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
struct sockaddr_in from;
|
|
struct sockaddr_in sin;
|
|
struct servent *sp;
|
|
int s, s2, fd, n, fdlock;
|
|
int from_len;
|
|
char local_file[256];
|
|
char local_temp[256];
|
|
struct hostent *hp;
|
|
char hostname[256];
|
|
unsigned long cksum_read;
|
|
unsigned long cksum_calc;
|
|
char from_str[128];
|
|
u_long length;
|
|
long kerror;
|
|
AUTH_DAT auth_dat;
|
|
KTEXT_ST ticket;
|
|
char my_instance[INST_SZ];
|
|
char my_realm[REALM_SZ];
|
|
char cmd[1024];
|
|
short net_transfer_mode, transfer_mode;
|
|
Key_schedule session_sched;
|
|
char version[9];
|
|
int c;
|
|
extern char *optarg;
|
|
extern int optind;
|
|
int rflag;
|
|
char *srvtab = "";
|
|
char *local_db = DBM_FILE;
|
|
char *kdb_util = KPROP_KDB_UTIL;
|
|
|
|
if (argv[argc - 1][0] == 'k' && isdigit(argv[argc - 1][1])) {
|
|
argc--; /* ttys file hack */
|
|
}
|
|
while ((c = getopt(argc, argv, "r:s:d:P:")) != EOF) {
|
|
switch (c) {
|
|
case 'r':
|
|
rflag++;
|
|
strcpy(my_realm, optarg);
|
|
break;
|
|
case 's':
|
|
srvtab = optarg;
|
|
break;
|
|
case 'd':
|
|
local_db = optarg;
|
|
break;
|
|
case 'P':
|
|
kdb_util = optarg;
|
|
break;
|
|
default:
|
|
usage();
|
|
break;
|
|
}
|
|
}
|
|
if (optind != argc - 1)
|
|
usage();
|
|
|
|
openlog("kpropd", LOG_PID, LOG_AUTH);
|
|
|
|
strcpy(local_file, argv[optind]);
|
|
strcat(strcpy(local_temp, argv[optind]), ".tmp");
|
|
|
|
#ifdef STANDALONE
|
|
|
|
if ((sp = getservbyname("krb_prop", "tcp")) == NULL) {
|
|
syslog(LOG_ERR, "tcp/krb_prop: unknown service.");
|
|
SlowDeath();
|
|
}
|
|
bzero(&sin, sizeof sin);
|
|
sin.sin_port = sp->s_port;
|
|
sin.sin_family = AF_INET;
|
|
|
|
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
|
syslog(LOG_ERR, "socket: %m");
|
|
SlowDeath();
|
|
}
|
|
if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
|
|
syslog(LOG_ERR, "bind: %m");
|
|
SlowDeath();
|
|
}
|
|
|
|
#endif /* STANDALONE */
|
|
|
|
if (!rflag) {
|
|
kerror = krb_get_lrealm(my_realm, 1);
|
|
if (kerror != KSUCCESS) {
|
|
syslog(LOG_ERR, "can't get local realm. %s",
|
|
krb_err_txt[kerror]);
|
|
SlowDeath();
|
|
}
|
|
}
|
|
if (gethostname(my_instance, sizeof(my_instance)) != 0) {
|
|
syslog(LOG_ERR, "gethostname: %m");
|
|
SlowDeath();
|
|
}
|
|
|
|
#ifdef STANDALONE
|
|
listen(s, 5);
|
|
for (;;) {
|
|
from_len = sizeof from;
|
|
if ((s2 = accept(s, (struct sockaddr *)&from, &from_len)) < 0) {
|
|
syslog(LOG_ERR, "accept: %m");
|
|
continue;
|
|
}
|
|
#else /* !STANDALONE */
|
|
|
|
s2 = 0;
|
|
from_len = sizeof from;
|
|
if (getpeername(0, (struct sockaddr *)&from, &from_len) < 0) {
|
|
syslog(LOG_ERR, "getpeername: %m");
|
|
SlowDeath();
|
|
}
|
|
|
|
#endif /* !STANDALONE */
|
|
|
|
strcpy(from_str, inet_ntoa(from.sin_addr));
|
|
|
|
if ((hp = gethostbyaddr((char *) &(from.sin_addr.s_addr),
|
|
from_len, AF_INET)) == NULL) {
|
|
strcpy(hostname, "UNKNOWN");
|
|
} else {
|
|
strcpy(hostname, hp->h_name);
|
|
}
|
|
|
|
syslog(LOG_INFO, "connection from %s, %s", hostname, from_str);
|
|
|
|
/* for krb_rd_{priv, safe} */
|
|
n = sizeof sin;
|
|
if (getsockname(s2, (struct sockaddr *)&sin, &n) != 0) {
|
|
syslog(LOG_ERR, "can't get socketname: %m");
|
|
SlowDeath();
|
|
}
|
|
if (n != sizeof(sin)) {
|
|
syslog(LOG_ERR, "can't get socketname (length)");
|
|
SlowDeath();
|
|
}
|
|
if ((fdlock = open(local_temp, O_WRONLY | O_CREAT, 0600)) < 0) {
|
|
syslog(LOG_ERR, "open: %m");
|
|
SlowDeath();
|
|
}
|
|
if (flock(fdlock, LOCK_EX | LOCK_NB)) {
|
|
syslog(LOG_ERR, "flock: %m");
|
|
SlowDeath();
|
|
}
|
|
if ((fd = creat(local_temp, 0600)) < 0) {
|
|
syslog(LOG_ERR, "creat: %m");
|
|
SlowDeath();
|
|
}
|
|
if ((n = read(s2, buf, sizeof(kprop_version)))
|
|
!= sizeof(kprop_version)) {
|
|
syslog(LOG_ERR,
|
|
"can't read protocol version (%d bytes)", n);
|
|
SlowDeath();
|
|
}
|
|
if (strncmp(buf, kprop_version, sizeof(kprop_version)) != 0) {
|
|
syslog(LOG_ERR, "unsupported version %s", buf);
|
|
SlowDeath();
|
|
}
|
|
if ((n = read(s2, &net_transfer_mode,
|
|
sizeof(net_transfer_mode)))
|
|
!= sizeof(net_transfer_mode)) {
|
|
syslog(LOG_ERR, "can't read transfer mode");
|
|
SlowDeath();
|
|
}
|
|
transfer_mode = ntohs(net_transfer_mode);
|
|
kerror = krb_recvauth(KOPT_DO_MUTUAL, s2, &ticket,
|
|
KPROP_SERVICE_NAME,
|
|
my_instance,
|
|
&from,
|
|
&sin,
|
|
&auth_dat,
|
|
srvtab,
|
|
session_sched,
|
|
version);
|
|
if (kerror != KSUCCESS) {
|
|
syslog(LOG_ERR, "%s calling getkdata",
|
|
krb_err_txt[kerror]);
|
|
SlowDeath();
|
|
}
|
|
syslog(LOG_INFO, "connection from %s.%s@%s",
|
|
auth_dat.pname, auth_dat.pinst, auth_dat.prealm);
|
|
|
|
/*
|
|
* AUTHORIZATION is done here. We might want to expand this
|
|
* to read an acl file at some point, but allowing for now
|
|
* KPROP_SERVICE_NAME.KRB_MASTER@local-realm is fine ...
|
|
*/
|
|
|
|
if ((strcmp(KPROP_SERVICE_NAME, auth_dat.pname) != 0) ||
|
|
(strcmp(KRB_MASTER, auth_dat.pinst) != 0) ||
|
|
(strcmp(my_realm, auth_dat.prealm) != 0)) {
|
|
syslog(LOG_NOTICE, "authorization denied");
|
|
SlowDeath();
|
|
}
|
|
switch (transfer_mode) {
|
|
case KPROP_TRANSFER_PRIVATE:
|
|
recv_auth(s2, fd, 1 /* private */ , &from, &sin, &auth_dat);
|
|
break;
|
|
case KPROP_TRANSFER_SAFE:
|
|
recv_auth(s2, fd, 0 /* safe */ , &from, &sin, &auth_dat);
|
|
break;
|
|
case KPROP_TRANSFER_CLEAR:
|
|
recv_clear(s2, fd);
|
|
break;
|
|
default:
|
|
syslog(LOG_ERR, "bad transfer mode %d", transfer_mode);
|
|
SlowDeath();
|
|
}
|
|
|
|
if (transfer_mode != KPROP_TRANSFER_PRIVATE) {
|
|
syslog(LOG_ERR, "non-private transfers not supported\n");
|
|
SlowDeath();
|
|
#ifdef doesnt_work_yet
|
|
lseek(fd, (long) 0, L_SET);
|
|
if (auth_dat.checksum != get_data_checksum(fd, session_sched)) {
|
|
syslog(LOG_ERR, "checksum doesn't match");
|
|
SlowDeath();
|
|
}
|
|
#endif
|
|
} else {
|
|
struct stat st;
|
|
fstat(fd, &st);
|
|
if (st.st_size != auth_dat.checksum) {
|
|
syslog(LOG_ERR, "length doesn't match");
|
|
SlowDeath();
|
|
}
|
|
}
|
|
close(fd);
|
|
close(s2);
|
|
|
|
if (rename(local_temp, local_file) < 0) {
|
|
syslog(LOG_ERR, "rename: %m");
|
|
SlowDeath();
|
|
}
|
|
|
|
if (flock(fdlock, LOCK_UN)) {
|
|
syslog(LOG_ERR, "flock (unlock): %m");
|
|
SlowDeath();
|
|
}
|
|
close(fdlock);
|
|
sprintf(cmd, "%s load %s %s\n", kdb_util, local_file, local_db);
|
|
if (system(cmd) != 0) {
|
|
syslog(LOG_ERR, "couldn't load database");
|
|
SlowDeath();
|
|
}
|
|
|
|
#ifdef STANDALONE
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
recv_auth(in, out, private, remote, local, ad)
|
|
int in, out;
|
|
int private;
|
|
struct sockaddr_in *remote, *local;
|
|
AUTH_DAT *ad;
|
|
{
|
|
u_long length;
|
|
long kerror;
|
|
int n;
|
|
MSG_DAT msg_data;
|
|
Key_schedule session_sched;
|
|
|
|
if (private)
|
|
#ifdef NOENCRYPTION
|
|
bzero((char *) session_sched, sizeof(session_sched));
|
|
#else
|
|
if (key_sched(ad->session, session_sched)) {
|
|
syslog(LOG_ERR, "can't make key schedule");
|
|
SlowDeath();
|
|
}
|
|
#endif
|
|
|
|
while (1) {
|
|
n = krb_net_read(in, &length, sizeof length);
|
|
if (n == 0)
|
|
break;
|
|
if (n < 0) {
|
|
syslog(LOG_ERR, "read: %m");
|
|
SlowDeath();
|
|
}
|
|
length = ntohl(length);
|
|
if (length > sizeof buf) {
|
|
syslog(LOG_ERR, "read length %d, bigger than buf %d",
|
|
length, sizeof buf);
|
|
SlowDeath();
|
|
}
|
|
n = krb_net_read(in, buf, length);
|
|
if (n < 0) {
|
|
syslog(LOG_ERR, "kpropd: read: %m");
|
|
SlowDeath();
|
|
}
|
|
if (private)
|
|
kerror = krb_rd_priv(buf, n, session_sched, ad->session,
|
|
remote, local, &msg_data);
|
|
else
|
|
kerror = krb_rd_safe(buf, n, ad->session,
|
|
remote, local, &msg_data);
|
|
if (kerror != KSUCCESS) {
|
|
syslog(LOG_ERR, "%s: %s",
|
|
private ? "krb_rd_priv" : "krb_rd_safe",
|
|
krb_err_txt[kerror]);
|
|
SlowDeath();
|
|
}
|
|
if (write(out, msg_data.app_data, msg_data.app_length) !=
|
|
msg_data.app_length) {
|
|
syslog(LOG_ERR, "write: %m");
|
|
SlowDeath();
|
|
}
|
|
}
|
|
}
|
|
|
|
recv_clear(in, out)
|
|
int in, out;
|
|
{
|
|
int n;
|
|
|
|
while (1) {
|
|
n = read(in, buf, sizeof buf);
|
|
if (n == 0)
|
|
break;
|
|
if (n < 0) {
|
|
syslog(LOG_ERR, "read: %m");
|
|
SlowDeath();
|
|
}
|
|
if (write(out, buf, n) != n) {
|
|
syslog(LOG_ERR, "write: %m");
|
|
SlowDeath();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
SlowDeath()
|
|
{
|
|
#ifdef STANDALONE
|
|
sleep(pause_int);
|
|
#endif
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef doesnt_work_yet
|
|
unsigned long
|
|
get_data_checksum(fd, key_sched)
|
|
int fd;
|
|
Key_schedule key_sched;
|
|
{
|
|
unsigned long cksum = 0;
|
|
unsigned long cbc_cksum();
|
|
int n;
|
|
char buf[BUFSIZ];
|
|
char obuf[8];
|
|
|
|
while (n = read(fd, buf, sizeof buf)) {
|
|
if (n < 0) {
|
|
syslog(LOG_ERR, "read (in checksum test): %m");
|
|
SlowDeath();
|
|
}
|
|
#ifndef NOENCRYPTION
|
|
cksum += cbc_cksum(buf, obuf, n, key_sched, key_sched);
|
|
#endif
|
|
}
|
|
return cksum;
|
|
}
|
|
#endif
|