03656ac1b0
the "core" Kerberos functionality. The rest of the userland will get their own changes later.
847 lines
20 KiB
C
847 lines
20 KiB
C
/*
|
|
* Copyright 1985, 1986, 1987, 1988 by the Massachusetts Institute
|
|
* of Technology.
|
|
*
|
|
* For copying and distribution information, please see the file
|
|
* <mit-copyright.h>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "protos.h"
|
|
|
|
RCSID("$Id: kerberos.c,v 1.64 1997/05/20 18:40:46 bg Exp $");
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
|
|
#ifdef TIME_WITH_SYS_TIME
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#elif defined(HAVE_SYS_TIME_H)
|
|
#include <sys/time.h>
|
|
#else
|
|
#include <time.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
#include <sys/socket.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#ifdef HAVE_ARPA_INET_H
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#ifdef HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif
|
|
#if defined(HAVE_SYS_IOCTL_H) && SunOS != 4
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_FILIO_H
|
|
#include <sys/filio.h>
|
|
#endif /* HAVE_SYS_FILIO_H */
|
|
|
|
#ifdef HAVE_NETDB_H
|
|
#include <netdb.h>
|
|
#endif
|
|
#include <err.h>
|
|
|
|
#ifdef SOCKS
|
|
#include <socks.h>
|
|
#endif
|
|
|
|
#include <roken.h>
|
|
|
|
#include <des.h>
|
|
#include <krb.h>
|
|
#include <krb_db.h>
|
|
#include <prot.h>
|
|
#include <klog.h>
|
|
|
|
#include <kdc.h>
|
|
|
|
static des_key_schedule master_key_schedule;
|
|
static des_cblock master_key;
|
|
|
|
static struct timeval kerb_time;
|
|
static u_char master_key_version;
|
|
static char k_instance[INST_SZ];
|
|
static char *lt;
|
|
static int more;
|
|
|
|
static int mflag; /* Are we invoked manually? */
|
|
static char *log_file; /* name of alt. log file */
|
|
static int nflag; /* don't check max age */
|
|
static int rflag; /* alternate realm specified */
|
|
|
|
/* fields within the received request packet */
|
|
static char *req_name_ptr;
|
|
static char *req_inst_ptr;
|
|
static char *req_realm_ptr;
|
|
static u_int32_t req_time_ws;
|
|
|
|
static char local_realm[REALM_SZ];
|
|
|
|
/* options */
|
|
static int max_age = -1;
|
|
static int pause_int = -1;
|
|
|
|
/*
|
|
* Print usage message and exit.
|
|
*/
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr, "Usage: %s [-s] [-m] [-n] [-p pause_seconds]"
|
|
" [-a max_age] [-l log_file] [-r realm] [database_pathname]\n",
|
|
__progname);
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* kerb_err_reply creates an error reply packet and sends it to the
|
|
* client.
|
|
*/
|
|
|
|
static void
|
|
kerb_err_reply(int f, struct sockaddr_in *client, int err, char *string)
|
|
{
|
|
static KTEXT_ST e_pkt_st;
|
|
KTEXT e_pkt = &e_pkt_st;
|
|
static char e_msg[128];
|
|
|
|
strcpy(e_msg, "\nKerberos error -- ");
|
|
strcat(e_msg, string);
|
|
cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr,
|
|
req_time_ws, err, e_msg);
|
|
sendto(f, (char*)e_pkt->dat, e_pkt->length, 0, (struct sockaddr *)client,
|
|
sizeof(*client));
|
|
}
|
|
|
|
static void
|
|
hang(void)
|
|
{
|
|
if (pause_int == -1) {
|
|
klog(L_KRB_PERR, "Kerberos will pause so as not to loop init");
|
|
for (;;)
|
|
pause();
|
|
} else {
|
|
char buf[256];
|
|
snprintf(buf, sizeof(buf),
|
|
"Kerberos will wait %d seconds before dying so as not to loop init",
|
|
pause_int);
|
|
klog(L_KRB_PERR, buf);
|
|
sleep(pause_int);
|
|
klog(L_KRB_PERR, "Do svedania....\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static int
|
|
check_princ(char *p_name, char *instance, unsigned int lifetime, Principal *p)
|
|
{
|
|
static int n;
|
|
static int more;
|
|
|
|
n = kerb_get_principal(p_name, instance, p, 1, &more);
|
|
|
|
if (n < 0) {
|
|
lt = klog(L_KRB_PERR, "Database unavailable!");
|
|
hang();
|
|
}
|
|
|
|
/*
|
|
* if more than one p_name, pick one, randomly create a session key,
|
|
* compute maximum lifetime, lookup authorizations if applicable,
|
|
* and stuff into cipher.
|
|
*/
|
|
if (n == 0) {
|
|
/* service unknown, log error, skip to next request */
|
|
lt = klog(L_ERR_UNK, "UNKNOWN %s.%s", p_name, instance);
|
|
return KERB_ERR_PRINCIPAL_UNKNOWN;
|
|
}
|
|
if (more) {
|
|
/* not unique, log error */
|
|
lt = klog(L_ERR_NUN, "Principal not unique %s.%s", p_name, instance);
|
|
return KERB_ERR_PRINCIPAL_NOT_UNIQUE;
|
|
}
|
|
/* If the user's key is null, we want to return an error */
|
|
if ((p->key_low == 0) && (p->key_high == 0)) {
|
|
/* User has a null key */
|
|
lt = klog(L_ERR_NKY, "Null key %s.%s", p_name, instance);
|
|
return KERB_ERR_NULL_KEY;
|
|
}
|
|
if (master_key_version != p->kdc_key_ver) {
|
|
/* log error reply */
|
|
lt = klog(L_ERR_MKV,
|
|
"Incorrect master key version for %s.%s: %d (should be %d)",
|
|
p->name, p->instance, p->kdc_key_ver, master_key_version);
|
|
return KERB_ERR_NAME_MAST_KEY_VER;
|
|
}
|
|
/* make sure the service hasn't expired */
|
|
if ((u_int32_t) p->exp_date < (u_int32_t) kerb_time.tv_sec) {
|
|
/* service did expire, log it */
|
|
time_t t = p->exp_date;
|
|
lt = klog(L_ERR_SEXP,
|
|
"Principal %s.%s expired at %s", p->name, p->instance,
|
|
krb_stime(&t));
|
|
return KERB_ERR_NAME_EXP;
|
|
}
|
|
/* ok is zero */
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
unseal(des_cblock *key)
|
|
{
|
|
kdb_encrypt_key(key, key, &master_key, master_key_schedule, DES_DECRYPT);
|
|
}
|
|
|
|
|
|
/* Set the key for krb_rd_req so we can check tgt */
|
|
static int
|
|
set_tgtkey(char *r)
|
|
/* Realm for desired key */
|
|
{
|
|
int n;
|
|
static char lastrealm[REALM_SZ];
|
|
Principal p_st;
|
|
Principal *p = &p_st;
|
|
des_cblock key;
|
|
|
|
if (!strcmp(lastrealm, r))
|
|
return (KSUCCESS);
|
|
|
|
klog(L_ALL_REQ, "Getting key for %s", r);
|
|
|
|
n = kerb_get_principal(KRB_TICKET_GRANTING_TICKET, r, p, 1, &more);
|
|
if (n == 0)
|
|
return (KFAILURE);
|
|
|
|
/* unseal tgt key from master key */
|
|
copy_to_key(&p->key_low, &p->key_high, key);
|
|
unseal(&key);
|
|
krb_set_key(key, 0);
|
|
strcpy(lastrealm, r);
|
|
return (KSUCCESS);
|
|
}
|
|
|
|
|
|
static int
|
|
kerberos(unsigned char *buf, int len, struct in_addr client, KTEXT rpkt)
|
|
{
|
|
int pvno;
|
|
int msg_type;
|
|
int lsb;
|
|
int life;
|
|
int flags = 0;
|
|
char name[ANAME_SZ], inst[INST_SZ], realm[REALM_SZ];
|
|
char service[SNAME_SZ], sinst[INST_SZ];
|
|
u_int32_t req_time;
|
|
static KTEXT_ST ticket, cipher, adat;
|
|
KTEXT tk = &ticket, ciph = &cipher, auth = &adat;
|
|
AUTH_DAT ad;
|
|
des_cblock session, key;
|
|
int err;
|
|
Principal a_name, s_name;
|
|
|
|
char *msg;
|
|
|
|
|
|
unsigned char *p = buf;
|
|
if(len < 2){
|
|
strcpy((char*)rpkt->dat, "Packet too short");
|
|
return KFAILURE;
|
|
}
|
|
|
|
gettimeofday(&kerb_time, NULL);
|
|
|
|
pvno = *p++;
|
|
if(pvno != KRB_PROT_VERSION){
|
|
msg = klog(L_KRB_PERR, "KRB protocol version mismatch (%d)", pvno);
|
|
strcpy((char*)rpkt->dat, msg);
|
|
return KERB_ERR_PKT_VER;
|
|
}
|
|
msg_type = *p++;
|
|
lsb = msg_type & 1;
|
|
msg_type &= ~1;
|
|
switch(msg_type){
|
|
case AUTH_MSG_KDC_REQUEST:
|
|
/* XXX range check */
|
|
p += krb_get_nir(p, name, inst, realm);
|
|
p += krb_get_int(p, &req_time, 4, lsb);
|
|
life = *p++;
|
|
p += krb_get_nir(p, service, sinst, NULL);
|
|
klog(L_INI_REQ, "AS REQ %s.%s@%s for %s.%s from %s",
|
|
name, inst, realm, service, sinst, inet_ntoa(client));
|
|
if((err = check_princ(name, inst, 0, &a_name))){
|
|
strcpy((char*)rpkt->dat, krb_get_err_text(err));
|
|
return err;
|
|
}
|
|
tk->length = 0;
|
|
if((err = check_princ(service, sinst, 0, &s_name))){
|
|
strcpy((char*)rpkt->dat, krb_get_err_text(err));
|
|
return err;
|
|
}
|
|
life = min(life, s_name.max_life);
|
|
life = min(life, a_name.max_life);
|
|
|
|
des_new_random_key(&session);
|
|
copy_to_key(&s_name.key_low, &s_name.key_high, key);
|
|
unseal(&key);
|
|
krb_create_ticket(tk, flags, a_name.name, a_name.instance,
|
|
local_realm, client.s_addr, session,
|
|
life, kerb_time.tv_sec,
|
|
s_name.name, s_name.instance, &key);
|
|
copy_to_key(&a_name.key_low, &a_name.key_high, key);
|
|
unseal(&key);
|
|
create_ciph(ciph, session, s_name.name, s_name.instance,
|
|
local_realm, life, s_name.key_version, tk,
|
|
kerb_time.tv_sec, &key);
|
|
memset(&session, 0, sizeof(session));
|
|
memset(&key, 0, sizeof(key));
|
|
{
|
|
KTEXT r;
|
|
r = create_auth_reply(name, inst, realm, req_time, 0,
|
|
a_name.exp_date, a_name.key_version, ciph);
|
|
memcpy(rpkt, r, sizeof(*rpkt));
|
|
}
|
|
return 0;
|
|
case AUTH_MSG_APPL_REQUEST:
|
|
strcpy(realm, (char*)buf + 3);
|
|
if((err = set_tgtkey(realm))){
|
|
msg = klog(L_ERR_UNK, "Unknown realm %s from %s",
|
|
realm, inet_ntoa(client));
|
|
strcpy((char*)rpkt->dat, msg);
|
|
return err;
|
|
}
|
|
p = buf + strlen(realm) + 4;
|
|
p = p + p[0] + p[1] + 2;
|
|
auth->length = p - buf;
|
|
memcpy(auth->dat, buf, auth->length);
|
|
err = krb_rd_req(auth, KRB_TICKET_GRANTING_TICKET,
|
|
realm, client.s_addr, &ad, 0);
|
|
if(err){
|
|
msg = klog(L_ERR_UNK, "krb_rd_req from %s: %s",
|
|
inet_ntoa(client), krb_get_err_text(err));
|
|
strcpy((char*)rpkt->dat, msg);
|
|
return err;
|
|
}
|
|
p += krb_get_int(p, &req_time, 4, lsb);
|
|
life = *p++;
|
|
p += krb_get_nir(p, service, sinst, NULL);
|
|
klog(L_APPL_REQ, "APPL REQ %s.%s@%s for %s.%s from %s",
|
|
ad.pname, ad.pinst, ad.prealm,
|
|
service, sinst,
|
|
inet_ntoa(client));
|
|
if(strcmp(ad.prealm, realm)){
|
|
msg = klog(L_ERR_UNK, "Can't hop realms: %s -> %s",
|
|
realm, ad.prealm);
|
|
strcpy((char*)rpkt->dat, msg);
|
|
return KERB_ERR_PRINCIPAL_UNKNOWN;
|
|
}
|
|
|
|
if(!strcmp(service, "changepw")){
|
|
strcpy((char*)rpkt->dat,
|
|
"Can't authorize password changed based on TGT");
|
|
return KERB_ERR_PRINCIPAL_UNKNOWN;
|
|
}
|
|
|
|
err = check_princ(service, sinst, life, &s_name);
|
|
if(err){
|
|
strcpy((char*)rpkt->dat, krb_get_err_text(err));
|
|
return err;
|
|
}
|
|
life = min(life,
|
|
krb_time_to_life(kerb_time.tv_sec,
|
|
krb_life_to_time(ad.time_sec,
|
|
ad.life)));
|
|
life = min(life, s_name.max_life);
|
|
copy_to_key(&s_name.key_low, &s_name.key_high, key);
|
|
unseal(&key);
|
|
des_new_random_key(&session);
|
|
krb_create_ticket(tk, flags, ad.pname, ad.pinst, ad.prealm,
|
|
client.s_addr, &session, life, kerb_time.tv_sec,
|
|
s_name.name, s_name.instance,
|
|
&key);
|
|
|
|
memset(&key, 0, sizeof(key));
|
|
|
|
create_ciph(ciph, session, service, sinst, local_realm,
|
|
life, s_name.key_version, tk,
|
|
kerb_time.tv_sec, &ad.session);
|
|
|
|
memset(&session, 0, sizeof(session));
|
|
memset(ad.session, 0, sizeof(ad.session));
|
|
{
|
|
KTEXT r;
|
|
r =create_auth_reply(ad.pname, ad.pinst, ad.prealm,
|
|
req_time, 0, 0, 0, ciph);
|
|
memcpy(rpkt, r, sizeof(*rpkt));
|
|
}
|
|
memset(&s_name, 0, sizeof(s_name));
|
|
return 0;
|
|
|
|
case AUTH_MSG_ERR_REPLY:
|
|
return -1;
|
|
default:
|
|
msg = klog(L_KRB_PERR, "Unknown message type: %d from %s",
|
|
msg_type, inet_ntoa(client));
|
|
strcpy((char*)rpkt->dat, msg);
|
|
return KFAILURE;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
kerberos_wrap(int s, KTEXT data, struct sockaddr_in *client)
|
|
{
|
|
KTEXT_ST pkt;
|
|
int err = kerberos(data->dat, data->length, client->sin_addr, &pkt);
|
|
if(err == -1)
|
|
return;
|
|
if(err){
|
|
kerb_err_reply(s, client, err, (char*)pkt.dat);
|
|
return;
|
|
}
|
|
sendto(s, pkt.dat, pkt.length, 0, (struct sockaddr *)client,
|
|
sizeof(*client));
|
|
}
|
|
|
|
|
|
/*
|
|
* setup_disc
|
|
*
|
|
* disconnect all descriptors, remove ourself from the process
|
|
* group that spawned us.
|
|
*/
|
|
|
|
static void
|
|
setup_disc(void)
|
|
{
|
|
int s;
|
|
|
|
for (s = 0; s < 3; s++) {
|
|
close(s);
|
|
}
|
|
|
|
open("/dev/null", 0);
|
|
dup2(0, 1);
|
|
dup2(0, 2);
|
|
|
|
setsid();
|
|
|
|
chdir("/tmp");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Make sure that database isn't stale.
|
|
*
|
|
* Exit if it is; we don't want to tell lies.
|
|
*/
|
|
|
|
static void
|
|
check_db_age(void)
|
|
{
|
|
long age;
|
|
|
|
if (max_age != -1) {
|
|
/* Requires existance of kerb_get_db_age() */
|
|
gettimeofday(&kerb_time, 0);
|
|
age = kerb_get_db_age();
|
|
if (age == 0) {
|
|
klog(L_KRB_PERR, "Database currently being updated!");
|
|
hang();
|
|
}
|
|
if ((age + max_age) < kerb_time.tv_sec) {
|
|
klog(L_KRB_PERR, "Database out of date!");
|
|
hang();
|
|
/* NOTREACHED */
|
|
}
|
|
}
|
|
}
|
|
|
|
struct descr{
|
|
int s;
|
|
KTEXT_ST buf;
|
|
int type;
|
|
int timeout;
|
|
};
|
|
|
|
static void
|
|
mksocket(struct descr *d, struct in_addr addr, int type,
|
|
const char *service, int port)
|
|
{
|
|
struct sockaddr_in sina;
|
|
int on = 1;
|
|
int sock;
|
|
|
|
memset(d, 0, sizeof(struct descr));
|
|
if ((sock = socket(AF_INET, type, 0)) < 0)
|
|
err (1, "socket");
|
|
#if defined(SO_REUSEADDR) && defined(HAVE_SETSOCKOPT)
|
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
|
|
sizeof(on)) < 0)
|
|
warn ("setsockopt (SO_REUSEADDR)");
|
|
#endif
|
|
memset(&sina, 0, sizeof(sina));
|
|
sina.sin_family = AF_INET;
|
|
sina.sin_port = port;
|
|
sina.sin_addr = addr;
|
|
if (bind(sock, (struct sockaddr *)&sina, sizeof(sina)) < 0)
|
|
err (1, "bind '%s/%s' (%d)",
|
|
service, (type == SOCK_DGRAM) ? "udp" : "tcp",
|
|
ntohs(sina.sin_port));
|
|
|
|
if(type == SOCK_STREAM)
|
|
listen(sock, SOMAXCONN);
|
|
d->s = sock;
|
|
d->type = type;
|
|
}
|
|
|
|
|
|
static void loop(struct descr *fds, int maxfd);
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int child;
|
|
int c;
|
|
struct descr *fds;
|
|
int nfds;
|
|
int i;
|
|
int n;
|
|
int kerror;
|
|
|
|
umask(077); /* Create protected files */
|
|
|
|
set_progname (argv[0]);
|
|
|
|
while ((c = getopt(argc, argv, "snmp:a:l:r:")) != EOF) {
|
|
switch(c) {
|
|
case 's':
|
|
/*
|
|
* Set parameters to slave server defaults.
|
|
*/
|
|
if (max_age == -1 && !nflag)
|
|
max_age = ONE_DAY; /* 24 hours */
|
|
if (pause_int == -1)
|
|
pause_int = FIVE_MINUTES; /* 5 minutes */
|
|
#if 0
|
|
if (log_file == NULL) {
|
|
/* this is only silly */
|
|
log_file = KRBSLAVELOG;
|
|
}
|
|
#endif
|
|
break;
|
|
case 'n':
|
|
max_age = -1; /* don't check max age. */
|
|
nflag++;
|
|
break;
|
|
case 'm':
|
|
mflag++; /* running manually; prompt for master key */
|
|
break;
|
|
case 'p':
|
|
/* Set pause interval. */
|
|
if (!isdigit(optarg[0]))
|
|
usage();
|
|
pause_int = atoi(optarg);
|
|
if ((pause_int < 5) || (pause_int > ONE_HOUR)) {
|
|
fprintf(stderr, "pause_int must be between 5 and 3600 seconds.\n");
|
|
usage();
|
|
}
|
|
break;
|
|
case 'a':
|
|
/* Set max age. */
|
|
if (!isdigit(optarg[0]))
|
|
usage();
|
|
max_age = atoi(optarg);
|
|
if ((max_age < ONE_HOUR) || (max_age > THREE_DAYS)) {
|
|
fprintf(stderr, "max_age must be between one hour and three days, in seconds\n");
|
|
usage();
|
|
}
|
|
break;
|
|
case 'l':
|
|
/* Set alternate log file */
|
|
log_file = optarg;
|
|
break;
|
|
case 'r':
|
|
/* Set realm name */
|
|
rflag++;
|
|
strcpy(local_realm, optarg);
|
|
break;
|
|
default:
|
|
usage();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(log_file == NULL)
|
|
log_file = KRBLOG;
|
|
|
|
if (optind == (argc-1)) {
|
|
if (kerb_db_set_name(argv[optind]) != 0) {
|
|
fprintf(stderr, "Could not set alternate database name\n");
|
|
exit(1);
|
|
}
|
|
optind++;
|
|
}
|
|
|
|
if (optind != argc)
|
|
usage();
|
|
|
|
printf("Kerberos server starting\n");
|
|
|
|
if ((!nflag) && (max_age != -1))
|
|
printf("\tMaximum database age: %d seconds\n", max_age);
|
|
if (pause_int != -1)
|
|
printf("\tSleep for %d seconds on error\n", pause_int);
|
|
else
|
|
printf("\tSleep forever on error\n");
|
|
if (mflag)
|
|
printf("\tMaster key will be entered manually\n");
|
|
|
|
printf("\tLog file is %s\n", log_file);
|
|
|
|
kset_logfile(log_file);
|
|
|
|
/* find our hostname, and use it as the instance */
|
|
if (k_gethostname(k_instance, INST_SZ))
|
|
err (1, "gethostname");
|
|
|
|
/*
|
|
* Yes this looks backwards but it has to be this way to enable a
|
|
* smooth migration to the new port 88.
|
|
*/
|
|
{
|
|
int p1, p2;
|
|
struct in_addr *a;
|
|
|
|
p1 = k_getportbyname ("kerberos-iv", "udp", htons(750));
|
|
p2 = k_getportbyname ("kerberos-sec", "udp", htons(88));
|
|
|
|
if (p1 == p2)
|
|
{
|
|
fprintf(stderr, "Either define kerberos-iv/udp as 750\n");
|
|
fprintf(stderr, " and kerberos-sec/udp as 88\n");
|
|
fprintf(stderr, "or the other way around!");
|
|
exit(1);
|
|
}
|
|
|
|
nfds = k_get_all_addrs (&a);
|
|
if (nfds < 0) {
|
|
struct in_addr any;
|
|
|
|
any.s_addr = INADDR_ANY;
|
|
|
|
fprintf (stderr, "Could not get local addresses, "
|
|
"binding to INADDR_ANY\n");
|
|
nfds = 1;
|
|
a = malloc(sizeof(*a) * nfds);
|
|
memcpy(a, &any, sizeof(struct in_addr));
|
|
}
|
|
nfds *= 4;
|
|
fds = (struct descr*)malloc(nfds * sizeof(struct descr));
|
|
for (i = 0; i < nfds/4; i++) {
|
|
mksocket(fds + 4 * i + 0, a[i], SOCK_DGRAM, "kerberos-iv", p1);
|
|
mksocket(fds + 4 * i + 1, a[i], SOCK_DGRAM, "kerberos-sec", p2);
|
|
mksocket(fds + 4 * i + 2, a[i], SOCK_STREAM, "kerberos-iv", p1);
|
|
mksocket(fds + 4 * i + 3, a[i], SOCK_STREAM, "kerberos-sec", p2);
|
|
}
|
|
free (a);
|
|
}
|
|
/* do all the database and cache inits */
|
|
if ((n = kerb_init())) {
|
|
if (mflag) {
|
|
printf("Kerberos db and cache init ");
|
|
printf("failed = %d ...exiting\n", n);
|
|
exit (1);
|
|
} else {
|
|
klog(L_KRB_PERR,
|
|
"Kerberos db and cache init failed = %d ...exiting", n);
|
|
hang();
|
|
}
|
|
}
|
|
|
|
/* Make sure database isn't stale */
|
|
check_db_age();
|
|
|
|
/* setup master key */
|
|
if (kdb_get_master_key (mflag, &master_key, master_key_schedule) != 0) {
|
|
klog (L_KRB_PERR, "kerberos: couldn't get master key.\n");
|
|
exit (1);
|
|
}
|
|
kerror = kdb_verify_master_key (&master_key, master_key_schedule, stdout);
|
|
if (kerror < 0) {
|
|
klog (L_KRB_PERR, "Can't verify master key.");
|
|
memset(master_key, 0, sizeof (master_key));
|
|
memset (master_key_schedule, 0, sizeof (master_key_schedule));
|
|
exit (1);
|
|
}
|
|
|
|
master_key_version = (u_char) kerror;
|
|
|
|
fprintf(stdout, "\nCurrent Kerberos master key version is %d\n",
|
|
master_key_version);
|
|
des_init_random_number_generator(&master_key);
|
|
|
|
if (!rflag) {
|
|
/* Look up our local realm */
|
|
krb_get_lrealm(local_realm, 1);
|
|
}
|
|
fprintf(stdout, "Local realm: %s\n", local_realm);
|
|
fflush(stdout);
|
|
|
|
if (set_tgtkey(local_realm)) {
|
|
/* Ticket granting service unknown */
|
|
klog(L_KRB_PERR, "Ticket granting ticket service unknown");
|
|
fprintf(stderr, "Ticket granting ticket service unknown\n");
|
|
exit(1);
|
|
}
|
|
if (mflag) {
|
|
if ((child = fork()) != 0) {
|
|
printf("Kerberos started, PID=%d\n", child);
|
|
exit(0);
|
|
}
|
|
setup_disc();
|
|
}
|
|
|
|
klog(L_ALL_REQ, "Starting Kerberos for %s (kvno %d)",
|
|
local_realm, master_key_version);
|
|
|
|
/* receive loop */
|
|
loop(fds, nfds);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
static void
|
|
loop(struct descr *fds, int nfds)
|
|
{
|
|
for (;;) {
|
|
int ret;
|
|
fd_set readfds;
|
|
struct timeval tv;
|
|
int maxfd = 0;
|
|
struct descr *n, *minfree;
|
|
|
|
FD_ZERO(&readfds);
|
|
gettimeofday(&tv, NULL);
|
|
maxfd = 0;
|
|
minfree = NULL;
|
|
/* Remove expired TCP sockets, and add all other
|
|
to the set we are selecting on */
|
|
for(n = fds; n < fds + nfds; n++){
|
|
if(n->s >= 0 && n->timeout && tv.tv_sec > n->timeout){
|
|
kerb_err_reply(n->s, NULL, KERB_ERR_TIMEOUT, "Timeout");
|
|
close(n->s);
|
|
n->s = -1;
|
|
}
|
|
if(n->s < 0){
|
|
if(minfree == NULL) minfree = n;
|
|
continue;
|
|
}
|
|
FD_SET(n->s, &readfds);
|
|
maxfd = max(maxfd, n->s);
|
|
}
|
|
/* add more space for sockets */
|
|
if(minfree == NULL){
|
|
int i = nfds;
|
|
struct descr *new;
|
|
nfds *=2;
|
|
new = realloc(fds, sizeof(struct descr) * nfds);
|
|
if(new){
|
|
fds = new;
|
|
minfree = fds + i;
|
|
for(; i < nfds; i++) fds[i].s = -1;
|
|
}
|
|
}
|
|
ret = select(maxfd + 1, &readfds, 0, 0, 0);
|
|
for (n = fds; n < fds + nfds; n++){
|
|
if(n->s < 0) continue;
|
|
if (FD_ISSET(n->s, &readfds)){
|
|
if(n->type == SOCK_STREAM && n->timeout == 0){
|
|
/* add accepted socket to list of sockets we are
|
|
selecting on */
|
|
int s = accept(n->s, NULL, 0);
|
|
if(minfree == NULL){
|
|
kerb_err_reply(s, NULL, KFAILURE, "Out of memory");
|
|
close(s);
|
|
}else{
|
|
minfree->s = s;
|
|
minfree->type = SOCK_STREAM;
|
|
gettimeofday(&tv, NULL);
|
|
minfree->timeout = tv.tv_sec + 4; /* XXX */
|
|
}
|
|
}else{
|
|
int b;
|
|
struct sockaddr_in from;
|
|
int fromlen = sizeof(from);
|
|
b = recvfrom(n->s, n->buf.dat + n->buf.length,
|
|
MAX_PKT_LEN - n->buf.length, 0,
|
|
(struct sockaddr *)&from, &fromlen);
|
|
if(b < 0){
|
|
if(n->type == SOCK_STREAM){
|
|
close(n->s);
|
|
n->s = -1;
|
|
}
|
|
n->buf.length = 0;
|
|
continue;
|
|
}
|
|
n->buf.length += b;
|
|
if(n->type == SOCK_STREAM){
|
|
if(n->buf.length >= 4 && n->buf.dat[0] == 0){
|
|
/* if this is a new type of packet (with
|
|
the length attached to the head of the
|
|
packet), and there is no more data to
|
|
be read, fake an old packet, so the
|
|
code below will work */
|
|
u_int32_t len;
|
|
krb_get_int(n->buf.dat, &len, 4, 0);
|
|
if(n->buf.length == len + 4){
|
|
memmove(n->buf.dat, n->buf.dat + 4, len);
|
|
b = 0;
|
|
}
|
|
}
|
|
if(b == 0){
|
|
/* handle request if there are
|
|
no more bytes to read */
|
|
fromlen = sizeof(from);
|
|
getpeername(n->s,(struct sockaddr*)&from, &fromlen);
|
|
kerberos_wrap(n->s, &n->buf, &from);
|
|
n->buf.length = 0;
|
|
close(n->s);
|
|
n->s = -1;
|
|
}
|
|
}else{
|
|
/* udp packets are atomic */
|
|
kerberos_wrap(n->s, &n->buf, &from);
|
|
n->buf.length = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|