/*
 * Copyright 2000, International Business Machines Corporation and others.
 * All Rights Reserved.
 *
 * This software has been released under the terms of the IBM Public
 * License.  For details, see the LICENSE file in the top-level source
 * directory or online at http://www.openafs.org/dl/license10.html
 */

/* This file contains the code to handle UDP requests to the authentication
   server using the MIT Kerberos protocol for obtaining tickets.  It will only
   handle authentication and get ticket requests to provide read-only protocol
   level compatibility with the standard Kerberos servers. */

#include <afsconfig.h>
#include <afs/param.h>


#include <afs/stds.h>
#include <sys/types.h>
#include <errno.h>
#ifdef AFS_NT40_ENV
#include <winsock2.h>
#include <afs/errmap_nt.h>
#define snprintf _snprintf
#else
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#endif
#include <string.h>
#include <afs/afsutil.h>
#include <time.h>
#include <afs/com_err.h>
#include <lwp.h>
#include <des.h>
#include <des_prototypes.h>
#include <rx/xdr.h>
#include <rx/rx.h>
#include <rx/rxkad.h>
#include <afs/auth.h>
#include <ubik.h>

#include "kauth.h"
#include "kautils.h"
#include "kauth_internal.h"
#include "kaserver.h"
#include "prot.h"		/* protocol definitions */
#include "kaport.h"
#include "afs/audit.h"
#include "kalog.h"
#include "kadatabase.h"

/* my kerberos error codes */
#define KERB_ERR_BAD_MSG_TYPE  99
#define KERB_ERR_BAD_LIFETIME  98
#define KERB_ERR_NONNULL_REALM 97
#define KERB_ERR_PKT_LENGTH    96
#define KERB_ERR_TEXT_LENGTH   95
#ifndef	KDC_GEN_ERR
#define	KDC_GEN_ERR	20
#endif

#ifndef AFS_NT40_ENV
#define closesocket close
#endif

int krb_udp_debug = 0;

static int sock_kerb = -1;	/* the socket we're using */
static int sock_kerb5 = -1;	/* the socket we're using */

struct packet {
    int len;
    struct sockaddr_in from;
    int byteOrder;
    char *name;
    char *inst;
    char *realm;
    Date time;
    char *rest;			/* remaining bytes of packet */
    char data[MAX_PKT_LEN];
};

extern char *lastOperation;
extern char lrealm[];

char udpAuthPrincipal[256];
char udptgsPrincipal[256];
char udptgsServerPrincipal[256];

#define putstr(name) if ((slen = strlen(name)) >= MAXKTCNAMELEN) return -1;\
		     else strcpy (answer, name), answer += slen+1
#define putint(num) num = htonl(num), \
		    memcpy(answer, &num, sizeof(afs_int32)), \
		    answer += sizeof(afs_int32)

#define getstr(name) if ((slen = strlen(packet)) >= sizeof(name)) return -1;\
		     strcpy (name, packet), packet += slen+1
#define getint(num) memcpy(&num, packet, sizeof(afs_int32)), \
		    num = ktohl (byteOrder, num), \
		    packet += sizeof(afs_int32)

/* this is just to close the log every five minutes to rm works */

int fiveminutes = 300;

static void *
FiveMinuteCheckLWP(void *unused)
{

    printf("start 5 min check lwp\n");

    while (1) {
	IOMGR_Sleep(fiveminutes);
	/* close the log so it can be removed */
	ReOpenLog(AFSDIR_SERVER_KALOG_FILEPATH);	/* no trunc, just append */
    }
    return NULL;
}


static afs_int32
create_cipher(char *cipher, int *cipherLen,
	      struct ktc_encryptionKey *sessionKey, char *sname,
	      char *sinst, Date start, Date end, afs_int32 kvno,
	      char *ticket, int ticketLen, struct ktc_encryptionKey *key)
{
    char *answer;
    int slen;
    unsigned char life = time_to_life(start, end);
    int len;
    des_key_schedule schedule;
    afs_int32 code;

    answer = cipher;
    len =
	sizeof(*sessionKey) + strlen(sname) + strlen(sinst) + strlen(lrealm) +
	3 /*nulls */  + 3 + ticketLen + sizeof(Date);
    if (len > *cipherLen)
	return KAANSWERTOOLONG;
    if (ticketLen > 255)
	return KAANSWERTOOLONG;
    if (kvno > 255)
	return KAANSWERTOOLONG;

    memcpy(answer, sessionKey, sizeof(*sessionKey));
    answer += sizeof(*sessionKey);
    putstr(sname);
    putstr(sinst);
    putstr(lrealm);
    *answer++ = life;
    *answer++ = (unsigned char)kvno;
    *answer++ = (unsigned char)ticketLen;
    memcpy(answer, ticket, ticketLen);
    answer += ticketLen;
    putint(start);

    if (krb_udp_debug) {
	printf("Printing ticket (%d) and date: ", ticketLen);
	ka_PrintBytes(ticket, ticketLen);
	ka_PrintBytes(answer - 4, 4);
	printf("\n");
    }

    if ((code = des_key_sched(ktc_to_cblock(key), schedule)))
	printf("In KAAuthenticate: key_sched returned %d\n", code);
    des_pcbc_encrypt(cipher, cipher, len, schedule, ktc_to_cblockptr(key), ENCRYPT);
    *cipherLen = round_up_to_ebs(len);

    if (krb_udp_debug) {
	printf("Printing cipher (%d): ", *cipherLen);
	ka_PrintBytes(cipher, *cipherLen);
	printf("\n");
    }
    return 0;
}

static afs_int32
create_reply(struct packet *ans, char *name, char *inst, Date startTime,
	     Date endTime, afs_int32 kvno, char *cipher, int cipherLen)
{
    char *answer = ans->data;
    int slen;

    ans->len = 2 + strlen(name) + strlen(inst) + 3 /*nulls */  +
	sizeof(afs_int32) + 1 /*ntkts */  + sizeof(afs_int32) + 1 /*kvno */  +
	sizeof(short) + cipherLen;
    if (ans->len > sizeof(ans->data))
	return KAANSWERTOOLONG;
    if (kvno > 255)
	return KAANSWERTOOLONG;

    *answer++ = (unsigned char)KRB_PROT_VERSION;
    *answer++ = (unsigned char)AUTH_MSG_KDC_REPLY;
    /* always send claiming network byte order */
    putstr(name);
    putstr(inst);
    putstr("");
    putint(startTime);
    *answer++ = 1;		/* undocumented number of tickets! */
    putint(endTime);
    *answer++ = (unsigned char)kvno;
    {
	short w = (short)cipherLen;
	w = htons(w);
	memcpy(answer, &w, sizeof(short));
	answer += sizeof(short);
    }
    memcpy(answer, cipher, cipherLen);
    return 0;
}

static afs_int32
check_auth(struct packet *pkt, char *auth, int authLen,
	   struct ktc_encryptionKey *key, char *name, char *inst,
	   char *cell)
{
    char *packet;
    des_key_schedule schedule;
    afs_int32 cksum;
    afs_int32 time_sec;
    int byteOrder = pkt->byteOrder;

    des_key_sched(ktc_to_cblock(key), schedule);
    des_pcbc_encrypt(auth, auth, authLen, schedule, ktc_to_cblockptr(key), DECRYPT);
    packet = auth;
    if (strcmp(packet, name) != 0)
	return KABADTICKET;
    packet += strlen(packet) + 1;
    if (strcmp(packet, inst) != 0)
	return KABADTICKET;
    packet += strlen(packet) + 1;
    if (strcmp(packet, cell) != 0)
	return KABADTICKET;
    packet += strlen(packet) + 1;
    getint(cksum);
    /* Comments in the original IBM source suggest this byte was/is "time_msec" */
    packet++;
    getint(time_sec);
    if ((packet - auth) > authLen)
	return KABADTICKET;
    return 0;
}

afs_int32
UDP_Authenticate(int ksoc, struct sockaddr_in *client, char *name,
		 char *inst, Date startTime, Date endTime, char *sname,
		 char *sinst)
{
    struct ubik_trans *tt;
    afs_int32 to;		/* offset of block */
    struct kaentry tentry;
    afs_int32 tgskvno;		/* key version of service key */
    struct ktc_encryptionKey tgskey;	/* service key for encrypting ticket */
    int tgt;
    Date now = time(0);
    afs_int32 code;

    char ticket[MAXKTCTICKETLEN];	/* our copy of the ticket */
    int ticketLen;
    struct ktc_encryptionKey sessionKey;	/* we have to invent a session key */

    char cipher[2 * MAXKTCTICKETLEN];	/* put encrypted part of answer here */
    int cipherLen;
    struct packet ans;

    COUNT_REQ(UAuthenticate);
    if (!name_instance_legal(name, inst))
	return KERB_ERR_NAME_EXP;	/* KABADNAME */
    if ((code = InitAuthServ(&tt, LOCKREAD, this_op)))
	return code;

    code = FindBlock(tt, name, inst, &to, &tentry);
    if (code)
	goto abort;
    if (to) {			/* if user exists check other stuff */
	afs_int32 sto;
	struct kaentry sentry;
	save_principal(udpAuthPrincipal, name, inst, 0);

	tgt = ((strcmp(sname, KA_TGS_NAME) == 0)
	       && (strcmp(sinst, lrealm) == 0));
	if ((ntohl(tentry.user_expiration) < now)
	    || (tgt && (ntohl(tentry.flags) & KAFNOTGS))) {
	    code = KERB_ERR_NAME_EXP;	/* KABADUSER */
	    goto abort;
	}
	code = FindBlock(tt, KA_TGS_NAME, lrealm, &sto, &sentry);
	if (code)
	    goto abort;
	if (sto == 0) {
	    code = KANOENT;
	    goto abort;
	}
	if ((ntohl(sentry.user_expiration) < now)) {
	    code = KERB_ERR_NAME_EXP;	/* XXX Could use another error code XXX */
	    goto abort;
	}
	if (check_ka_skew(startTime, now, KTC_TIME_UNCERTAINTY)) {
	    code = KERB_ERR_SERVICE_EXP;	/* was KABADREQUEST */
	    goto abort;
	}

	if (tentry.misc_auth_bytes) {
	    unsigned char misc_auth_bytes[4];
	    afs_uint32 temp;	/* unsigned for safety */
	    afs_uint32 pwexpires;

	    memcpy(&temp, tentry.misc_auth_bytes, sizeof(afs_uint32));
	    temp = ntohl(temp);
	    unpack_long(temp, misc_auth_bytes);
	    pwexpires = misc_auth_bytes[0];
	    if (pwexpires) {
		pwexpires =
		    ntohl(tentry.change_password_time) +
		    24 * 60 * 60 * pwexpires;
		if (pwexpires < now) {
		    code = KERB_ERR_AUTH_EXP;	/* was KAPWEXPIRED */
		    goto abort;
		}
	    }
	}

	/* make the ticket */
	code = des_random_key(ktc_to_cblock(&sessionKey));
	if (code) {
	    code = KERB_ERR_NULL_KEY;	/* was KANOKEYS */
	    goto abort;
	}
	endTime =
	    umin(endTime, startTime + ntohl(tentry.max_ticket_lifetime));
	if ((code = ka_LookupKey(tt, sname, sinst, &tgskvno, &tgskey))
	    || (code =
		tkt_MakeTicket(ticket, &ticketLen, &tgskey, name, inst,
			       lrealm, startTime, endTime, &sessionKey,
			       htonl(client->sin_addr.s_addr), sname, sinst)))
	    goto abort;

	cipherLen = sizeof(cipher);
	code =
	    create_cipher(cipher, &cipherLen, &sessionKey, sname, sinst,
			  startTime, endTime, tgskvno, ticket, ticketLen,
			  &tentry.key);
	if (code)
	    goto abort;
    } else {			/* no such user */
	cipherLen = 0;
	tentry.key_version = 0;
    }
    code = ubik_EndTrans(tt);
    if (code)
	goto fail;

    code =
	create_reply(&ans, name, inst, startTime, endTime,
		     ntohl(tentry.key_version), cipher, cipherLen);
    if (code)
	goto fail;

    if (krb_udp_debug) {
	printf("Sending %d bytes ending in: ", ans.len);
	ka_PrintBytes(ans.data + ans.len - 8, 8);
	printf("\n");
    }

    code =
	sendto(ksoc, ans.data, ans.len, 0, (struct sockaddr *)client,
	       sizeof(*client));
    if (code != ans.len) {
	perror("calling sendto");
	code = -1;
	goto fail;
    }
    KALOG(name, inst, sname, sinst, NULL, client->sin_addr.s_addr,
	  LOG_AUTHENTICATE);

    if (cipherLen != 0) {
	KALOG(name, inst, sname, sinst, NULL, client->sin_addr.s_addr,
	      LOG_TGTREQUEST);
    }
    osi_audit(UDPAuthenticateEvent, 0, AUD_STR, name, AUD_STR, inst, AUD_END);
    return 0;

  abort:
    COUNT_ABO;
    ubik_AbortTrans(tt);

  fail:
    osi_audit(UDPAuthenticateEvent, code, AUD_STR, name, AUD_STR, inst,
	      AUD_END);
    return code;
}

afs_int32
UDP_GetTicket(int ksoc, struct packet *pkt, afs_int32 kvno,
	      char *authDomain, char *ticket, int ticketLen, char *auth,
	      int authLen)
{
    afs_int32 code;
    struct ktc_encryptionKey tgskey;
    char name[MAXKTCNAMELEN];
    char inst[MAXKTCNAMELEN];
    char cell[MAXKTCREALMLEN];
    struct ktc_encryptionKey authSessionKey;
    afs_int32 host;
    Date start;
    Date authEnd;
    Date now = time(0);
    int celllen;
    int import;

    char *packet;
    int slen;
    int byteOrder = pkt->byteOrder;
    char sname[MAXKTCNAMELEN];
    char sinst[MAXKTCNAMELEN];
    afs_int32 time_ws;
    unsigned char life;

    struct ubik_trans *tt;
    afs_int32 to;
    struct kaentry caller;
    struct kaentry server;
    Date reqEnd;
    struct ktc_encryptionKey sessionKey;
    int newTicketLen;
    char newTicket[MAXKTCTICKETLEN];

    char cipher[2 * MAXKTCTICKETLEN];	/* put encrypted part of answer here */
    int cipherLen;
    struct packet ans;

    COUNT_REQ(UGetTicket);

    if ((code = InitAuthServ(&tt, LOCKREAD, this_op)))
	goto fail;
    code =
	ka_LookupKvno(tt, KA_TGS_NAME,
		      ((strlen(authDomain) > 0) ? authDomain : lrealm), kvno,
		      &tgskey);
    if (code)
	goto abort;

    code =
	tkt_DecodeTicket(ticket, ticketLen, &tgskey, name, inst, cell,
			 &authSessionKey, &host, &start, &authEnd);
    pkt->name = name;
    pkt->inst = inst;
    pkt->realm = cell;
    if (code) {
	code = KERB_ERR_AUTH_EXP;	/* was KANOAUTH */
	goto abort;
    }
    save_principal(udptgsPrincipal, name, inst, cell);
    code = tkt_CheckTimes(start, authEnd, now);
    if (code <= 0) {
	if (code == -1) {
	    code = KERB_ERR_SERVICE_EXP;	/* was RXKADEXPIRED */
	    goto abort;
	}
	code = KERB_ERR_AUTH_EXP;	/* was KANOAUTH */
	goto abort;
    }
    celllen = strlen(cell);
    import = 0;
    if ((strlen(authDomain) > 0) && (strcmp(authDomain, lrealm) != 0))
	import = 1;
    if (import && (celllen == 0)) {
	code = KERB_ERR_PKT_VER;	/* was KABADTICKET */
	goto abort;
    }
    if (celllen == 0) {
	strncpy(cell, lrealm, MAXKTCREALMLEN - 1);
	cell[MAXKTCREALMLEN - 1] = 0;
    };

    if (!krb4_cross && strcmp(lrealm, cell) != 0) {
	code = KERB_ERR_PRINCIPAL_UNKNOWN;
	goto abort;
    }

    if (krb_udp_debug) {
	printf("UGetTicket: got ticket from '%s'.'%s'@'%s'\n", name, inst,
	       cell);
    }

    code = check_auth(pkt, auth, authLen, &authSessionKey, name, inst, cell);
    if (code)
	goto abort;

    /* authenticator and all is OK so read actual request */
    packet = pkt->rest;
    getint(time_ws);
    life = *(unsigned char *)packet++;
    getstr(sname);
    getstr(sinst);
    start = now;
    reqEnd = life_to_time(start, life);
    if (krb_udp_debug) {
	printf("UGetTicket: request for server '%s'.'%s'\n", sname, sinst);
    }
    save_principal(udptgsServerPrincipal, sname, sinst, 0);

    if (import) {
	strcpy(caller.userID.name, name);
	strcpy(caller.userID.instance, inst);
	caller.max_ticket_lifetime = htonl(MAXKTCTICKETLIFETIME);
    } else {
	code = FindBlock(tt, name, inst, &to, &caller);
	if (code)
	    goto abort;
	if (to == 0) {
	    ka_PrintUserID("GetTicket: User ", name, inst, " unknown.\n");
	    code = KERB_ERR_PRINCIPAL_UNKNOWN;	/* KANOENT */
	    goto abort;
	}
	if (ntohl(caller.flags) & KAFNOTGS) {
	    code = KERB_ERR_AUTH_EXP;	/* was KABADUSER */
	    goto abort;
	}
    }

    code = FindBlock(tt, sname, sinst, &to, &server);	/* get server's entry */
    if (code)
	goto abort;
    if (to == 0) {		/* entry not found */
	ka_PrintUserID("GetTicket: Server ", sname, sinst, " unknown.\n");
	code = KERB_ERR_PRINCIPAL_UNKNOWN;	/* KANOENT */
	goto abort;
    }
    code = ubik_EndTrans(tt);
    if (code)
	goto fail;

    if (ntohl(server.flags) & KAFNOSEAL)
	return KABADSERVER;

    code = des_random_key(ktc_to_cblock(&sessionKey));
    if (code) {
	code = KERB_ERR_NULL_KEY;	/* was KANOKEYS */
	goto fail;
    }

    reqEnd =
	umin(umin(reqEnd, authEnd),
	     umin(start + ntohl(caller.max_ticket_lifetime),
		  start + ntohl(server.max_ticket_lifetime)));

    code =
	tkt_MakeTicket(newTicket, &newTicketLen, &server.key,
		       caller.userID.name, caller.userID.instance, cell,
		       start, reqEnd, &sessionKey,
		       htonl(pkt->from.sin_addr.s_addr), server.userID.name,
		       server.userID.instance);
    if (code)
	goto fail;

    cipherLen = sizeof(cipher);
    code =
	create_cipher(cipher, &cipherLen, &sessionKey, sname, sinst, start,
		      reqEnd, ntohl(server.key_version), newTicket,
		      newTicketLen, &authSessionKey);
    if (code)
	goto fail;

    code =
	create_reply(&ans, name, inst, start, reqEnd, 0, cipher, cipherLen);
    if (code)
	goto fail;

    code =
	sendto(ksoc, ans.data, ans.len, 0, (struct sockaddr *)&pkt->from,
	       sizeof(pkt->from));
    if (code != ans.len) {
	perror("calling sendto");
	code = -1;
	goto fail;
    }

    if (cipherLen != 0) {
	KALOG(name, inst, sname, sinst, NULL, host, LOG_GETTICKET);
    }
    osi_audit(UDPGetTicketEvent, 0, AUD_STR, name, AUD_STR, inst, AUD_STR,
	      cell, AUD_STR, sname, AUD_STR, sinst, AUD_END);
    return 0;

  abort:
    ubik_AbortTrans(tt);
  fail:
    osi_audit(UDPGetTicketEvent, code, AUD_STR, name, AUD_STR, inst, AUD_STR,
	      NULL, AUD_STR, NULL, AUD_STR, NULL, AUD_END);
    return code;
}

static int
err_packet(int ksoc, struct packet *pkt, afs_int32 code, char *reason)
{
    struct packet ans;
    char *answer = ans.data;
    int slen;
    char buf[256];

    if (reason == 0)
	reason = "";
    snprintf(buf, 255, "code = %d: %s", code, reason);

    if (krb_udp_debug) {
	printf("Sending error packet to '%s'.'%s'@'%s' containing %s\n",
	       pkt->name, pkt->inst, pkt->realm, buf);
    }

    ans.len =
	2 + strlen(pkt->name) + strlen(pkt->inst) + strlen(pkt->realm) +
	3 /* nulls */  + (2 * sizeof(afs_int32)) + strlen(buf) + 1;
    if (ans.len > sizeof(ans.data)) {
	printf("Answer packet too long\n");
	return -1;
    }

    *answer++ = (unsigned char)KRB_PROT_VERSION;
    *answer++ = (unsigned char)AUTH_MSG_ERR_REPLY;
    /* always send claiming network byte order */
    putstr(pkt->name);
    putstr(pkt->inst);
    putstr(pkt->realm);
    putint(pkt->time);
    if ((code < 0) || (code > KERB_ERR_MAXIMUM)) {
	/* It could be because of kauth errors so we should return something instead of success!! */
	code = KDC_GEN_ERR;
    }
    putint(code);
    putstr(buf);

    code =
	sendto(ksoc, ans.data, ans.len, 0, (struct sockaddr *)&pkt->from,
	       sizeof(pkt->from));
    if (code != ans.len) {
	if (code >= 0)
	    printf
		("call to sendto returned %d, sending error packet %d bytes long\n",
		 code, ans.len);
	else
	    perror("err_packet: sendto");
    }
    return 0;
}

int
process_udp_auth(int ksoc, struct packet *pkt)
{
    char *packet = pkt->rest;
    char name[MAXKTCNAMELEN];
    char inst[MAXKTCNAMELEN];
    char realm[MAXKTCREALMLEN];
    char sname[MAXKTCNAMELEN];
    char sinst[MAXKTCNAMELEN];
    int slen;
    Date now = time(0);
    Date startTime, endTime;
    unsigned char lifetime;
    afs_int32 code;

    pkt->name = packet;
    getstr(name);
    pkt->inst = packet;
    getstr(inst);
    pkt->realm = packet;
    getstr(realm);
    if (krb_udp_debug) {
	printf("Processing KDC Request from '%s'.'%s'@'%s'\n", name, inst,
	       realm);
    }

    if ((strlen(realm) > 0) && (strcmp(realm, lrealm) != 0)) {
	err_packet(ksoc, pkt, KERB_ERR_NONNULL_REALM,
		   "null realm name not allowed");
	return -1;
    }
    memcpy(&startTime, packet, sizeof(startTime));
    packet += sizeof(startTime);
    startTime = ktohl(pkt->byteOrder, startTime);
    pkt->time = startTime;
    lifetime = *packet++;
    endTime = life_to_time(startTime, lifetime);
    code = tkt_CheckTimes(startTime, endTime, now);
    if (code < 0) {
	err_packet(ksoc, pkt, KERB_ERR_BAD_LIFETIME,
		   "requested ticket lifetime invalid");
	return -1;
    }
    getstr(sname);
    getstr(sinst);
    if ((packet - pkt->data) != pkt->len) {
	err_packet(ksoc, pkt, KERB_ERR_PKT_LENGTH,
		   "packet length inconsistent");
	return -1;
    }
    pkt->rest = packet;
    code =
	UDP_Authenticate(ksoc, &pkt->from, name, inst, startTime, endTime,
			 sname, sinst);
    if (code) {
	if (code == KANOENT) {
	    code = KERB_ERR_PRINCIPAL_UNKNOWN;
	    err_packet(ksoc, pkt, code, (char *)afs_error_message(code));
	} else if (code == KAPWEXPIRED) {
	    code = KERB_ERR_NAME_EXP;
	    err_packet(ksoc, pkt, code, "password has expired");
	} else
	    err_packet(ksoc, pkt, code, (char *)afs_error_message(code));
    }
    return 0;
}

int
process_udp_appl(int ksoc, struct packet *pkt)
{
    char *packet = pkt->rest;
    afs_int32 kvno;
    char realm[MAXKTCREALMLEN];
    char ticket[MAXKTCTICKETLEN];
    char auth[3 * MAXKTCNAMELEN + 4 + 5];
    int slen;
    int ticketLen, authLen;
    afs_int32 code;

    if (krb_udp_debug) {
	printf("Processing APPL Request\n");
    }
    kvno = *packet++;
    getstr(realm);
    ticketLen = *(unsigned char *)packet++;
    authLen = *(unsigned char *)packet++;
    if (ticketLen > sizeof(ticket)) {
	err_packet(ksoc, pkt, KERB_ERR_TEXT_LENGTH, "ticket too long");
	return -1;
    }
    memcpy(ticket, packet, ticketLen);
    packet += ticketLen;
    if (authLen > sizeof(auth)) {
	err_packet(ksoc, pkt, KERB_ERR_TEXT_LENGTH, "authenticator too long");
	return -1;
    }
    memcpy(auth, packet, authLen);
    pkt->rest = packet + authLen;
    code =
	UDP_GetTicket(ksoc, pkt, kvno, realm, ticket, ticketLen, auth,
		      authLen);
    if (code) {
	if (code == KANOENT)
	    code = KERB_ERR_PRINCIPAL_UNKNOWN;
	err_packet(ksoc, pkt, code, (char *)afs_error_message(code));
	return -1;
    }
    return 0;
}

void
process_udp_request(int ksoc, struct packet *pkt)
{
    char *packet = pkt->data;
    unsigned char version, auth_msg_type;

    version = *packet++;
    if (version != KRB_PROT_VERSION) {
	err_packet(ksoc, pkt, KERB_ERR_PKT_VER,
		   "packet version number unknown");
	return;
    }
    auth_msg_type = *packet++;
    pkt->byteOrder = auth_msg_type & 1;
    pkt->rest = packet;
    switch (auth_msg_type & ~1) {
    case AUTH_MSG_KDC_REQUEST:
	process_udp_auth(ksoc, pkt);
	break;
    case AUTH_MSG_APPL_REQUEST:
	process_udp_appl(ksoc, pkt);
	break;
    default:
	printf("unknown msg type 0x%x\n", auth_msg_type);
	err_packet(ksoc, pkt, KERB_ERR_BAD_MSG_TYPE,
		   "message type not supported");
	return;
    }
    return;
}

static void *
SocketListener(void *unused)
{
    fd_set rfds;
    struct timeval tv;
    struct packet packet;
    socklen_t fromLen;
    afs_int32 code;
    char hoststr[16];

    printf("Starting to listen for UDP packets\n");
    while (1) {
	FD_ZERO(&rfds);
	if (sock_kerb >= 0)
	    FD_SET(sock_kerb, &rfds);
	if (sock_kerb5 >= 0)
	    FD_SET(sock_kerb5, &rfds);

	tv.tv_sec = 100;
	tv.tv_usec = 0;
	/* write and exception fd_set's are null */
	code = IOMGR_Select(32, &rfds, NULL, NULL, &tv);
	if (code == 0) {	/* timeout */
	    /* printf ("Timeout\n"); */
	    continue;
	} else if (code < 0) {
	    perror("calling IOMGR_Select");
	    break;
	}

	fromLen = sizeof(packet.from);
	if ((sock_kerb >= 0) && FD_ISSET(sock_kerb, &rfds)) {
	    code =
		recvfrom(sock_kerb, packet.data, sizeof(packet.data), 0,
			 (struct sockaddr *)&packet.from, &fromLen);
	    if (code < 0) {
		if (errno == EAGAIN || errno == ECONNREFUSED)
		    goto try_kerb5;
		perror("calling recvfrom");
		break;
	    }
	    packet.len = code;
	    if (krb_udp_debug) {
		printf("Kerb:udp: Got %d bytes from addr %s which are '",
		       code, afs_inet_ntoa_r(packet.from.sin_addr.s_addr, hoststr));
		ka_PrintBytes(packet.data, packet.len);
		printf("'\n");
	    }
	    packet.name = packet.inst = packet.realm = "";
	    packet.time = 0;
	    process_udp_request(sock_kerb, &packet);
	}
      try_kerb5:
	if ((sock_kerb5 >= 0) && FD_ISSET(sock_kerb5, &rfds)) {
	    code =
		recvfrom(sock_kerb5, packet.data, sizeof(packet.data), 0,
			 (struct sockaddr *)&packet.from, &fromLen);
	    if (code < 0) {
		if (errno == EAGAIN || errno == ECONNREFUSED)
		    continue;
		perror("calling recvfrom");
		break;
	    }
	    packet.len = code;
	    if (krb_udp_debug) {
		printf("Kerb5:udp: Got %d bytes from addr %s which are '",
		       code, afs_inet_ntoa_r(packet.from.sin_addr.s_addr, hoststr));
		ka_PrintBytes(packet.data, packet.len);
		printf("'\n");
	    }
	    packet.name = packet.inst = packet.realm = "";
	    packet.time = 0;
	    process_udp_request(sock_kerb5, &packet);
	}
    }
    if (sock_kerb >= 0) {
	closesocket(sock_kerb);
	sock_kerb = -1;
    }
    if (sock_kerb5 >= 0) {
	closesocket(sock_kerb5);
	sock_kerb5 = -1;
    }
    printf("UDP SocketListener exiting due to error\n");

    return NULL;
}

#if MAIN

#include "AFS_component_version_number.c"

int
main(int, char **)
#else
afs_int32
init_krb_udp(void)
#endif
{
    struct sockaddr_in taddr;
    static PROCESS slPid;	/* socket listener pid */
    static PROCESS checkPid;	/* fiveminute check */
    afs_int32 code;
    char *krb4name;		/* kerberos version4 service */

#if MAIN
    PROCESS junk;
#endif
    struct servent *sp;
    static int inited = 0;
    afs_int32 kerb_port;

    if (inited)
	return -1;
    inited = 1;

    memset(&taddr, 0, sizeof(taddr));
    krb4name = "kerberos4";
    sp = getservbyname(krb4name, "udp");
    taddr.sin_family = AF_INET;	/* added for NCR port */
#ifdef STRUCT_SOCKADDR_HAS_SA_LEN
    taddr.sin_len = sizeof(struct sockaddr_in);
#endif
    if (!sp) {
	/* if kerberos-4 is not available, try "kerberos-iv" */
	krb4name = "kerberos-iv";
	sp = getservbyname(krb4name, "udp");
    }
    if (!sp) {
	/* if kerberos-iv is not available, try "kerberos" */
	krb4name = "kerberos";
	sp = getservbyname(krb4name, "udp");
    }
    if (!sp) {
	fprintf(stderr,
		"kerberos/udp is unknown; check /etc/services.  Using port=%d as default\n",
		KRB_PORT);
	taddr.sin_port = htons(KRB_PORT);
    } else {
	/* copy the port number */
	fprintf(stderr, "%s/udp port=%hu\n", krb4name,
		(unsigned short)sp->s_port);
	taddr.sin_port = sp->s_port;
    }
    kerb_port = taddr.sin_port;
    sock_kerb = socket(AF_INET, SOCK_DGRAM, 0);
    code = bind(sock_kerb, (struct sockaddr *)&taddr, sizeof(taddr));
    if (code < 0) {
	perror("calling bind");
	sock_kerb = -1;
    }

    sp = getservbyname("kerberos5", "udp");
    if (!sp) {
	fprintf(stderr,
		"kerberos5/udp is unknown; check /etc/services.  Using port=%d as default\n",
		KRB5_PORT);
	taddr.sin_port = htons(KRB5_PORT);
    } else {
	/* copy the port number */
	fprintf(stderr, "kerberos5/udp port=%hu\n",
		(unsigned short)sp->s_port);
	taddr.sin_port = sp->s_port;
    }
    if (taddr.sin_port != kerb_port) {	/* a different port */
	sock_kerb5 = socket(AF_INET, SOCK_DGRAM, 0);
	code = bind(sock_kerb5, (struct sockaddr *)&taddr, sizeof(taddr));
	if (code < 0) {
	    perror("calling bind");
	    sock_kerb5 = -1;
	}
    }

    /* Bail out if we can't bind with any port */
    if ((sock_kerb < 0) && (sock_kerb5 < 0))
	return -1;

#if MAIN
    /* this has already been done */
    LWP_InitializeProcessSupport(LWP_NORMAL_PRIORITY, &junk);
    IOMGR_Initialize();
#endif
    LWP_CreateProcess(SocketListener, /*stacksize */ 16000,
		      LWP_NORMAL_PRIORITY, (void *)0, "Socket Listener",
		      &slPid);

    /* just to close the log every five minutes */

    LWP_CreateProcess(FiveMinuteCheckLWP, 24 * 1024, LWP_MAX_PRIORITY - 2,
		      (void *)&fiveminutes, "FiveMinuteChecks", &checkPid);

#if MAIN
    initialize_ka_error_table();
    initialize_rxk_error_table();
    while (1)			/* don't just stand there, run it */
	IOMGR_Sleep(60);
#else
    return 0;
#endif

}

#if MAIN
char *lastOperation;		/* name of last operation */
char *lrealm = "REALMNAME";
struct kadstats dynamic_statistics;

static int
InitAuthServ(tt, lock, this_op)
     struct ubik_trans **tt;
     int lock;			/* read or write transaction */
     int *this_op;		/* op of RCP routine, for COUNT_ABO */
{
    int code = 0;

    *tt = 0;
    printf("Calling InitAuthServ\n");
    return code;
}

static int
ubik_EndTrans(tt)
     struct ubik_trans *tt;
{
    printf("Calling ubik_EndTrans\n");
    return 0;
}

static int
ubik_AbortTrans(tt)
     struct ubik_trans *tt;
{
    printf("Calling ubik_AbortTrans\n");
    return 0;
}

static int
FindBlock(at, aname, ainstance, tentry)
     struct ubik_trans *at;
     char *aname;
     char *ainstance;
     struct kaentry *tentry;
{
    printf("Calling FindBlock with '%s'.'%s'\n", aname, ainstance);
    strcpy(tentry->userID.name, aname);
    strcpy(tentry->userID.instance, ainstance);
    tentry->key_version = htonl(17);
    des_string_to_key("toa", &tentry->key);
    tentry->flags = htonl(KAFNORMAL);
    tentry->user_expiration = htonl(NEVERDATE);
    tentry->max_ticket_lifetime = htonl(MAXKTCTICKETLIFETIME);
    return 323232;
}

static int
ka_LookupKey(tt, name, inst, kvno, key)
     struct ubik_trans *tt;
     char *name;
     char *inst;
     afs_int32 *kvno;		/* returned */
     struct ktc_encryptionKey *key;	/* copied out */
{
    printf("Calling ka_LookupKey with '%s'.'%s'\n", name, inst);
    *kvno = 23;
    des_string_to_key("applexx", key);
}

static afs_int32
kvno_tgs_key(authDomain, kvno, tgskey)
     char *authDomain;
     afs_int32 kvno;
     struct ktc_encryptionKey *tgskey;
{
    if (strcmp(authDomain, lrealm) != 0)
	printf("Called with wrong %s as authDomain\n", authDomain);
    if (kvno != 23)
	printf("kvno_tgs_key: being called with wrong kvno: %d\n", kvno);
    des_string_to_key("applexx", tgskey);
    return 0;
}

save_principal()
{
}

name_instance_legal()
{
    return 1;
}
#endif
