/*
 * lib/krb425/rd_req.c
 *
 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 *   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.
 * 
 *
 * krb_rd_req for krb425
 */


#include "krb425.h"
#include <sys/param.h>

static krb5_error_code
setkey_key_proc(DECLARG(krb5_pointer,arg),
		DECLARG(krb5_principal,princ),
		DECLARG(krb5_kvno,kvno),
		DECLARG(krb5_keyblock **,retkey))
OLDDECLARG(krb5_pointer,arg)
OLDDECLARG(krb5_principal,princ)
OLDDECLARG(krb5_kvno,kvno)
OLDDECLARG(krb5_keyblock **,retkey)
{
    return krb5_copy_keyblock(&_krb425_servkey, retkey);
}

int
krb_rd_req(authent, service, instance, from_addr, ad, fn)
KTEXT authent;
char *service;
char *instance;
u_long from_addr;
AUTH_DAT *ad;
char *fn;
{
	krb5_address peer;
	krb5_tkt_authent *authdat;
	char addr[4];
	krb5_principal server;
	krb5_error_code r;
	krb5_data authe;
	extern int gethostname();
	int use_set_key = 0;
	char file_name[MAXPATHLEN];

	if (from_addr) {
		peer.addrtype = ADDRTYPE_INET;
		peer.length = 4;
		peer.contents = (krb5_octet *)addr;
		memcpy(addr, (char *)&from_addr + (sizeof(from_addr) - 4), 4);
	}

	if (!_krb425_local_realm)
		if (r = krb5_get_default_realm(&_krb425_local_realm))
			return(krb425error(r));

	if (!strcmp(instance, "*")) {
		static char hostname[64] = { 0 };

		if (!hostname[0]) {
			struct hostent *h;
	
			gethostname(hostname, sizeof(hostname));
			if (h = gethostbyname(hostname)) {
				char *p;

				strncpy(hostname, h->h_name, sizeof(hostname));
				hostname[sizeof(hostname)-1] = 0;
				p = hostname;
				do {
					if (isupper(*p)) *p=tolower(*p);
				} while (*p++);
			}
		}
		instance = hostname;
	}
	if (r = krb5_build_principal(&server,
				     strlen(_krb425_local_realm),
				     _krb425_local_realm,
				     service,
				     instance,
				     0)) {
	    return(krb425error(r));
	}
	
	authe.length = authent->length;
	authe.data = (char *)authent->dat;
	if (!fn) {
	    use_set_key = 1;
	    fn = (char *)0;
	} else if (!*fn) {
	    fn = (char *)0;
	} else {
	    strcpy(file_name, "FILE:");
	    strncpy(file_name + 5, fn, MAXPATHLEN-5);
	    file_name[sizeof(file_name)-1] = '\0';
	    fn = file_name;
	}
	    

#ifdef  EBUG
        EPRINT "Calling krb5_rd_req with:\n");
        EPRINT "        Realm   : "); show5(srvdata[0]); ENEWLINE
        EPRINT "        Service : "); show5(srvdata[1]); ENEWLINE
        EPRINT "        Instance: "); show5(srvdata[2]); ENEWLINE
	EPRINT "Authenenticator : %d bytes\n", authe.length);
	EPRINT "Filename        : %s\n", fn ? fn : "none given");
	if (from_addr) {
		EPRINT "Address type    : %s\n",
			peer.addrtype == ADDRTYPE_INET ? "inet" :
			peer.addrtype == ADDRTYPE_CHAOS ? "chaos" :
			peer.addrtype == ADDRTYPE_XNS ? "xns" :
			peer.addrtype == ADDRTYPE_ISO ? "iso" :
			peer.addrtype == ADDRTYPE_DDP ? "ddp" : "unknown type");
		EPRINT "Address length  : %d\n", peer.length);
		EPRINT "Address         :");
		{
			int x;
			for (x = 0; x < peer.length && x < 8; ++x)
				fprintf(stderr, " %d", peer.contents[x]);
			if (x < peer.length)
				fprintf(stderr, " (%d)", peer.length);
			fprintf(stderr, "\n");
		}
	}
#endif

/* ? : will break some compilers when dealing with function pointers */
	if (use_set_key)
		r = krb5_rd_req(&authe,
				server,
				from_addr ? &peer : 0,
				fn, setkey_key_proc,
				0, 0, &authdat);
	else
		r = krb5_rd_req(&authe,
				server,
				from_addr ? &peer : 0,
				fn, 0,
				0, 0, &authdat);
	krb5_free_principal(server);
	if (r) {
#ifdef	EBUG
		ERROR(r)
#endif
		return(krb425error(r));
	}

	ad->k_flags = 0;

#ifdef	EBUG
	r = 0;
	while (authdat->authenticator->client[r]) {
		EPRINT "Client[%d]: ", r); show5((*authdat->authenticator->client[r])); ENEWLINE
		++r;
	}
	r = 0;
	while (authdat->ticket->server[r]) {
		EPRINT "Server[%d]: ", r); show5((*authdat->ticket->server[r])); ENEWLINE
		++r;
	}
	r = 0;
#endif
	set_string(ad->pname, ANAME_SZ,
		   krb5_princ_component(authdat->authenticator->client, 0));

	if (authdat->authenticator->client->length > 1) {
     		set_string(ad->pinst, INST_SZ,
			   krb5_princ_component(authdat->authenticator->client,
						1));
	}
	else {
		ad->pinst[0] = '\0';
	}

	set_string(ad->prealm, REALM_SZ,
		   krb5_princ_realm(authdat->authenticator->client));
  
	ad->checksum = *(long *)authdat->authenticator->checksum->contents;

	if (authdat->ticket->enc_part2->session->keytype != KEYTYPE_DES) {
		r = KFAILURE;
		goto out;
	} else
		memcpy((char*)ad->session,
		       (char*)authdat->ticket->enc_part2->session->contents,
		       sizeof(C_Block));

	ad->life = authdat->ticket->enc_part2->times.endtime;
	ad->time_sec = authdat->authenticator->ctime;
	ad->address = 0;

	if (authdat->ticket->enc_part2->caddrs[0]->addrtype != ADDRTYPE_INET) {
		r = KFAILURE;
		goto out;
	} else
		memcpy((char*)&ad->address + sizeof(ad->address) - 4,
		       (char*)authdat->ticket->enc_part2->caddrs[0]->contents, 4);

	if (authdat->ticket->enc_part2->authorization_data &&
	    authdat->ticket->enc_part2->authorization_data[0]) {
		ad->reply.length = authdat->ticket->enc_part2->authorization_data[0]->length;
		memcpy((char*)ad->reply.dat,
		       (char*)authdat->ticket->enc_part2->authorization_data[0]->contents,
		       min(ad->reply.length, MAX_KTXT_LEN));
		ad->reply.mbz = 0;
	}
out:
	krb5_free_tkt_authent(authdat);
	return(r);
}
