/*
 * Copyright (c) 1996-2007, OpenFWTK Development Group
 * All rights reserved. See LICENSE.
 */

#include	<ctype.h>
#include	<string.h>
#include	<strings.h>
#include	<stdio.h>
#include	<sys/param.h>

#include	"firewall.h"
#include	"auth.h"
#include	"authdb.h"

#ifdef	AUTHPROTO_SNK
#ifdef	OpenBSD
#include	"ssl/des.h"
#else
#include	"openssl/des.h"
#endif

static	char*	moduleId ATTR_UNUSED = "$Id: snk.c,v 1.7 2007/09/10 02:49:13 arkenoi Exp $";
extern	long	randomnumber();
extern  size_t 	strlcat(char*,const char*,size_t);
extern  size_t 	strlcpy(char*,const char*,size_t);

static	int	challenged = 0;
static	char	challbuf[32];


int
snkchallng(user,buf,bs)
char	*user;
char	*buf;
int	bs;
{
	challenged = 1;
	strlcpy(buf,"SNK Challenge \"",MAX_STR);
	snprintf(challbuf,sizeof(challbuf),"%6.6lu",randomnumber() % 999999);
	strlcat(buf,challbuf,MAX_STR);
	strlcat(buf,"\": ",MAX_STR);
	return(0);
}


/* is this ugly or what? it's late and I don't feel clever */
static	int
make_key_sched(s,k)
char		*s;
des_cblock	k;
{
	unsigned int	k0;
	unsigned int	k1;
	unsigned int	k2;
	unsigned int	k3;
	unsigned int	k4;
	unsigned int	k5;
	unsigned int	k6;
	unsigned int	k7;
	int	x;

	x = sscanf(s, "%o %o %o %o %o %o %o %o",
		&k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7);
	if(x != 8)
		return(1);
	k[0] = k0;
	k[1] = k1;
	k[2] = k2;
	k[3] = k3;
	k[4] = k4;
	k[5] = k5;
	k[6] = k6;
	k[7] = k7;
	return(0);
}


int
snkverify(user,pass,ap,rbuf)
char	*user;
char	*pass;
Auth	*ap;
char	*rbuf;
{
	des_key_schedule	keysched;
	des_cblock		kblock;
	char			buf[12];
	char			cbuf[12];
	int			i;
	int			j;
	unsigned long		kval = 0;

	strlcpy(rbuf,"Permission Denied.",MAX_STR);
	if(!challenged)
		return(1);
	challenged = 0;

	/* lowercase the response code, in case it's hex */
	for(i=0; pass[i]; i++)
		if(isupper(pass[i]))
			pass[i] = tolower(pass[i]);

	/* set up a key from the shared secret */
	if(make_key_sched(ap->pw,kblock)) {
		strlcpy(rbuf,"Cannot decode user secret key",MAX_STR);
		return(1);
	}
	des_set_key(&kblock,keysched);

	/* zeroize the entire buffer */
	for(i = 0; i < 9; i++)
		buf[i] = '\0';
	strncpy(buf,challbuf,8);

	/* push it through the rotating knives */
	des_ecb_encrypt((des_cblock*) buf,(des_cblock*) cbuf,
			keysched,DES_ENCRYPT);

	/* pull some bits out of the ciphertext into a long */
	for(i=0; i<4; i++)
		for(j = 0; j < 8; j++)
			kval = (kval << 1) | ((cbuf[i] >> (7 - j)) & 1);

	/* crunch it into a hex string */
	snprintf(buf,sizeof(buf),"%08lx",kval);
	if(!strcmp(pass,buf)) {
		strlcpy(rbuf,"ok",MAX_STR);
		return(0);
	}

	/* crunch hex to decimal and try that */
	for(i=0; buf[i]; i++)
		if(buf[i] == 'a' || buf[i] == 'b' || buf[i] == 'c')
			buf[i] = '2';
		else
			if(buf[i] == 'd' || buf[i] == 'e' || buf[i] == 'f')
				buf[i] = '3';
	if(strcmp(pass,buf))
		return(1);
	strlcpy(rbuf,"ok",MAX_STR);
	return(0);
}

int
snkset(user,pass,ap,rbuf)
char	*user;
char	*pass;
Auth	*ap;
char	*rbuf;
{
	des_cblock		kblock;

	if(make_key_sched(pass,kblock)) {
		strlcpy(rbuf,"Cannot decode user secret key",MAX_STR);
		return(0);
	}
	if(strlen(pass) >= AUTH_PWSIZ) {
		strlcpy(rbuf,"Secret key too long",MAX_STR);
		return(0);
	}
	strlcpy(ap->pw,pass,MAX_STR);
	if(auth_dbputu(user,ap) == 0)
		strlcpy(rbuf,"Secret key changed",MAX_STR);
	else
		strlcpy(rbuf,"Database error.",MAX_STR);
	return(0);
}
#endif
