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

static	char	RcsId[] = "";
#include <stdio.h>
#include <syslog.h>
#include <errno.h>
#include <sys/types.h>

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

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


#ifdef	AUTHPROTO_SECURID

#define  SECURID_MAX_SEED_LEN 100     /* NOTE!!! SECURID_MAX_SEED_LEN * SECURID_MAX_TOK_NUM * SECURID_MAX_TOK_LEN
                                       *         should not be greater AUTH_PROTO_BUF_LEN (500 bytes currently)     */
#define  SECURID_MAX_TOK_NUM  40
#define  SECURID_MAX_TOK_LEN  10

typedef struct {
        char seed[SECURID_MAX_SEED_LEN];
        char used_toks[SECURID_MAX_TOK_NUM][SECURID_MAX_TOK_LEN];
} t_securid_db;


/*
 * Copyright (C) 2002 e_costin@yahoo.com
 *
 */


#ifdef _MSC_VER
    #pragma intrinsic           (_lrotr, _lrotl)
#else /* GCC or CC */
    #define __int64             long long
    #define __forceinline       __inline__
    #define _lrotr(x, n)        ((((unsigned long)(x)) >> ((int) ((n) & 31))) | (((unsigned long)(x)) << ((int) ((-(n)) & 31))))
    #define _lrotl(x, n)        ((((unsigned long)(x)) << ((int) ((n) & 31))) | (((unsigned long)(x)) >> ((int) ((-(n)) & 31))))
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <syslog.h>

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>


#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT

#define ror32(x, n)             _lrotr (x, n)
#define rol32(x, n)             _lrotl (x, n)
#define bswap32(x)              (rol32 ((unsigned long)(x), 8) & 0x00ff00ff | ror32 ((unsigned long)(x), 8) & 0xff00ff00)

static __forceinline unsigned char      ror8 (const unsigned char x, const int n) { return (x >> (n & 7)) | (x << ((-n) & 7)); }
static __forceinline unsigned __int64   rol64 (const unsigned __int64 x, const int n) { return (x << (n & 63)) | (x >> ((-n) & 63)); }
static __forceinline unsigned __int64   bswap64 (const unsigned __int64 x) { unsigned long a = (unsigned long) x, b = (unsigned long) (x >> 32); return (((unsigned __int64) bswap32 (a)) << 32) | bswap32(b); }

typedef union _OCTET
{
    unsigned __int64            Q[1];
    unsigned long               D[2];
    unsigned short              W[4];
    unsigned char               B[8];
}   OCTET;

static __forceinline unsigned __int64   rol64_bitstring (const unsigned __int64 x, int n)
{
	OCTET out,  in;

	in.Q[0] = x;

	if((n&63) == 0) {
		out.Q[0] = in.Q[0];
	} else {
		out.D[0] = htonl((unsigned long)((unsigned __int64)ntohl(in.D[!((n&63)<=32)]) << (((n-1) & 31) + 1)
		                                  | (unsigned long)ntohl(in.D[  (n&63)<=32 ]) >> ((-n) & 31)));
		out.D[1] = htonl((unsigned long)((unsigned __int64)ntohl(in.D[  (n&63)<=32 ]) << (((n-1) & 31) + 1)
		                                  | (unsigned long)ntohl(in.D[!((n&63)<=32)]) >> ((-n) & 31)));
	}

	return out.Q[0];
}



static int ctrl  = 0;



void securid_expand_key_to_4_bit_per_byte (const OCTET source, char *target)
{
    int     i;

    for (i = 0; i < 8; i++)
    {
        target[i*2  ] = source.B[i] >> 4;
        target[i*2+1] = source.B[i] & 0x0F;
    }
}

void securid_expand_data_to_1_bit_per_byte (const OCTET source, char *target)
{
    int     i, j, k;

    for (i = 0, k = 0; i < 8; i++) for (j = 7; j >= 0; j--) target[k++] = (source.B[i] >> j) & 1;
}

void securid_reassemble_64_bit_from_64_byte (const unsigned char *source, OCTET *target)
{
    int     i = 0, j, k = 0;

    for (target->Q[0] = 0; i < 8; i++) for (j = 7; j >= 0; j--) target->B[i] |= source[k++] << j;
}

void securid_permute_data (OCTET *data, const OCTET key)
{
    unsigned char       bit_data[128];
    unsigned char       hex_key[16];

    unsigned long       i, k, b, m, bit;
    unsigned char       j;
    unsigned char       *hkw, *permuted_bit;

    memset (bit_data, 0, sizeof (bit_data));

    securid_expand_data_to_1_bit_per_byte (*data, bit_data);
    securid_expand_key_to_4_bit_per_byte (key, hex_key);

    for (bit = 32, hkw = hex_key, m = 0; bit <= 32; hkw += 8, bit -= 32)
    {
        permuted_bit = bit_data + 64 + bit;
        for (k = 0, b = 28; k < 8; k++, b -= 4)
        {
            for (j = hkw[k]; j; j--)
            {
                bit_data[(bit + b + m + 4) & 0x3F] = bit_data[m];
                m = (m + 1) & 0x3F;
            }

            for (i = 0; i < 4; i++)
            {
                permuted_bit[b + i] |= bit_data[(bit + b + m + i) & 0x3F];
            }
        }
    }

    securid_reassemble_64_bit_from_64_byte (bit_data + 64, data);
}

void securid_do_4_rounds (OCTET *data, OCTET *key)
{
    unsigned char       round, i, j;
    unsigned char       t;

    for (round = 0; round < 4; round++)
    {
        for (i = 0; i < 8; i++)
        {
            for (j = 0; j < 8; j++)
            {
                if ((((key->B[i] >> (j ^ 7)) ^ (data->B[0] >> 7)) & 1) != 0)
                {
                    t = data->B[4];
                    data->B[4] = 100 - data->B[0];
                    data->B[0] = t;
                }
                else
                {
                    data->B[0] = (unsigned char) (ror8 ((unsigned char) (ror8 (data->B[0], 1) - 1), 1) - 1) ^ data->B[4];
                }
/*
#ifdef I_AM_LITTLE_ENDIAN
                data->Q[0] = bswap64 (rol64 (bswap64 (data->Q[0]), 1));
#else
                data->Q[0] = rol64 (data->Q[0], 1);
#endif
*/
                data->Q[0] = rol64_bitstring (data->Q[0], 1);

            }
        }
        key->Q[0] ^= data->Q[0];
    }
}

void securid_convert_to_decimal (OCTET *data, const OCTET key)
{
    unsigned long       i;
    unsigned char       c, hi, lo;

    c = (key.B[7] & 0x0F) % 5;

    for (i = 0; i < 8; i++)
    {
        hi = data->B[i] >>   4;
        lo = data->B[i] & 0x0F;
        c = (c + (key.B[i] >>   4)) % 5; if (hi > 9) data->B[i] = ((hi = (hi - (c + 1) * 2) % 10) << 4) | lo;
        c = (c + (key.B[i] & 0x0F)) % 5; if (lo > 9) data->B[i] = (lo = ((lo - (c + 1) * 2) % 10)) | (hi << 4);
    }
}

void securid_hash_data (OCTET *data, OCTET key, unsigned char convert_to_decimal)
{
    securid_permute_data (data, key);
    securid_do_4_rounds (data, &key);
    securid_permute_data (data, key);
    if (convert_to_decimal)
        securid_convert_to_decimal (data, key);
}

void securid_hash_time (unsigned long time, OCTET *hash, OCTET key)
{
    hash->B[0] = (unsigned char) (time >> 16);
    hash->B[1] = (unsigned char) (time >> 8);
    hash->B[2] = (unsigned char) time;
    hash->B[3] = (unsigned char) time;
    hash->B[4] = (unsigned char) (time >> 16);
    hash->B[5] = (unsigned char) (time >> 8);
    hash->B[6] = (unsigned char) time;
    hash->B[7] = (unsigned char) time;

    securid_hash_data (hash, key, 1);
}

/*
 *
 * Looks up an user name in a database and checks the password
 *
 * return values:
 *	1 = user not found
 *	0 = Auth OK
 *     -1 = password incorrect
 *     -2 = system error
 *	password file = /etc/securid.pass
 *	used codes file = /etc/securid.codes
 */

static int user_lookup(const char *user, const char *pass, Auth	*ap)
{
    int			found, i;
    signed long       /*  j, k,*/ tt;
    OCTET               key, hi, hj;
    FILE                *fi, *fs;
    char                *m, *p, *serv, s[46], inkey[17], inkey1[9], inkey2[9], tmp_cod[20], serial[10], okey[17];
	t_securid_db *securid_db;
	int tok_num;
//    unsigned __int64    key64;
	unsigned long my_time = 1079609897l;
	char buf[1000];


	/* Reread the s/key record NOW and leave DB  (and open) */
	/*
	 * Obtain an exclusive lock on the DB file so the same password
	 * cannot be used twice to get in to the system.
	 */
	if(auth_dbgetu_forupdate(user, ap) || !ap->authproto_rec.active) {
	  auth_dbclose();
	  return(1);
	}

	securid_db = (t_securid_db *)ap->authproto_rec.buf;

/*
#ifdef I_AM_LITTLE_ENDIAN
	for (i = 0 ; i < 8; i++) inkey1[i] = securid_db->seed[i];
	inkey1[8] = '\00';
	for (i = 0 ; i < 8; i++) inkey2[i] = securid_db->seed[i+8];
	inkey2[8] = '\00';

	key.D[0] = strtoul (inkey2, &m, 16);
	key.D[1] = strtoul (inkey1, &m, 16);
#else
	for (i = 0; i < 8; i++)
	{
	    okey[2*i] = securid_db->seed[15-2*i-1];
	    okey[2*i+1] = securid_db->seed[15-2*i];
	}
	okey[16] = '\00';
	key.Q[0] = strtoull (okey, &m, 16);
#endif
*/

	for (i = 0 ; i < 8; i++) inkey1[i] = securid_db->seed[i];
	inkey1[8] = '\00';
	for (i = 0 ; i < 8; i++) inkey2[i] = securid_db->seed[i+8];
	inkey2[8] = '\00';

	key.D[1] = htonl(strtoul (inkey2, &m, 16));
	key.D[0] = htonl(strtoul (inkey1, &m, 16));
	key.Q[0] = bswap64(key.Q[0]);


    for(tok_num=0; tok_num < SECURID_MAX_TOK_NUM && securid_db->used_toks[tok_num][0] != '\0'; tok_num++)
	{
			if (strncmp(securid_db->used_toks[tok_num],pass,6) == 0)            /* the token used before */
			{
				auth_dbclose();
				return 1;
			}
	}

	if(tok_num >= SECURID_MAX_TOK_NUM) {
		sosay(0, "list of used tokens is full");
		auth_dbclose();  /* Unlock DB file and close it */
		return(1);
	}

/*
#ifdef I_AM_LITTLE_ENDIAN
	hj.D[0] = strtoul (pass, &m, 16);
#else
	hj.D[0] = bswap32(strtoul (pass, &m, 16));
#endif
*/
	hj.D[0] = bswap32(htonl(strtoul (pass, &m, 16)));

	found = 0;


	tt = (time (NULL) / 60 - 0x806880) * 2; // (t & -4) for 60 sec periods, (t & -8) for 120 sec periods, etc.


	securid_hash_time (((tt-4) & -4), &hi, key);
	if ((hi.B[3] == hj.B[2]) && (hi.B[4] == hj.B[1]) && (hi.B[5] == hj.B[0])) found = 1;

	securid_hash_time ((tt & -4), &hi, key);
	if ((hi.B[0] == hj.B[2]) && (hi.B[1] == hj.B[1]) && (hi.B[2] == hj.B[0])) found = 1;
	if ((hi.B[3] == hj.B[2]) && (hi.B[4] == hj.B[1]) && (hi.B[5] == hj.B[0])) found = 1;

	securid_hash_time (((tt+4) & -4), &hi, key);
	if ((hi.B[0] == hj.B[2]) && (hi.B[1] == hj.B[1]) && (hi.B[2] == hj.B[0])) found = 1;

	if (found == 1)
	{
		strncpy(securid_db->used_toks[tok_num], pass, SECURID_MAX_TOK_LEN);

		if(tok_num < SECURID_MAX_TOK_NUM - 1)
			securid_db->used_toks[tok_num + 1][0] = '\0';

		if(auth_dbputu(user,ap)) {
			sosay(0, "database error");
			auth_dbclose();  /* Unlock DB file and close it */
			return(1);
		}

		auth_dbclose();   /* ok */
		return 0;
	}


    auth_dbclose();  /* wrong token */
    return(1);
}




int secichallng(user,buf,bs)
char	*user;
char	*buf;
int	bs;
{
	return(0);
}


int seciverify(user,pass,ap,rbuf)
char	*user;
char	*pass;
Auth	*ap;
char	*rbuf;
{
	int	ret_val;

	strlcpy(rbuf,"Permission Denied.",MAXSTR);
//	if(!challenged)
//		return(1);

	ret_val = user_lookup(user, pass, ap);

	if(ret_val == 0) 	
		strlcpy(rbuf,"ok",MAXSTR);

	return(ret_val);
}



seciset(user,pass,ap,rbuf)
char	*user;
char	*pass;
Auth	*ap;
char	*rbuf;
{
	int status;
	Cfg	*cfp;
	char	*sdhost = NULL;
	t_securid_db *securid_db;


	if((pass == (char *)0) || (*pass == '\0') {
		strlcpy(rbuf,"NULL passwords not permitted for SecurID.",MAXSTR);
		return(1);
	}

	if(ap == NULL) {
		strlcpy(rbuf,"Database error.",MAXSTR);
		return(1);
	}


	securid_db = (t_securid_db *)ap->authproto_rec.buf;

	if(!ap->authproto_rec.active || strncmp(securid_db->seed, pass, SECURID_MAX_TOK_LEN))  {
		securid_db->used_toks[0][0] = '\0';  /* clean out the used tok list if the seed has changed */
	}

	strncpy(securid_db->seed, pass, SECURID_MAX_SEED_LEN);

	ap->authproto_rec.active = 1;

	if(auth_dbputu(user,ap)) {
                sosay(0, "database error");
                return(1);
        }

	snprintf(rbuf,MAXSTR,"SecurID seed for %s is %s",user, pass);
	return(0);


}
#endif
