/*
Copyright (c) 2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/


/**	@file	uatcs.c	Tools for client and server. */


/** In the uatcs module. */
#define UATCS_C	1



#include "useraudi.h"




#line 51 "uatcs.ctr"





/** @defgroup	filename	Default file names. */
/*@{*/
/** Default file name for configuration file.
*/
char uatcsDefaultConfigFileName[] = {
#ifndef USERAUD_CONFIG
  DK_SYSCONFDIR "/useraud/useraud.conf"
#else
  USERAUD_CONFIG
#endif
};



/** Default file name for database.
*/
char uatcsDefaultDatabaseName[] = {
#ifndef USERAUD_DATABASE
  "bdb:" DK_LOCALSTATEDIR "/useraud/useraud.db"
#else
  USERAUD_DATABASE
#endif
};



/** Default file name for socket.
*/
char uatcsDefaultSocketName[] = {
#ifndef USERAUD_SOCKET
  DK_LOCALSTATEDIR "/run/useraud/useraud.sock"
#else
  USERAUD_SOCKET
#endif
};



/** Default file name for log file.
*/
char uatcsDefaultLogFileName[] = {
#ifndef USERAUD_LOGFILE
  DK_LOCALSTATEDIR "/log/useraud/useraud.log"
#else
  USERAUD_LOGFILE
#endif
};


/** Default file name for the random seed.
*/
char uatcsDefaultRandomSeed[] = {
#ifndef USERAUD_SEEDFILE
  DK_LOCALSTATEDIR "/useraud/useraud.seed"
#else
  USERAUD_SEEDFILE
#endif
};
/*@}*/


/** Sub-types for the crypt() hash.
*/
static char *crypt_sub_types[] = {
"1", "2a", "5", "6",
NULL
};



/** Separator character list.
*/
static char whspc[] = { ", \t\r\n" };



/** Hash types, names assigned to numeric values.
*/
static HT ht[] = {
  { USERAUD_HASH_CRYPT,		"crypt" },
  { USERAUD_HASH_MD5,		"md5" },
  { USERAUD_HASH_SHA1,		"sha-1" },
  { USERAUD_HASH_RIPEMD160,	"ripemd-160" },
  { USERAUD_HASH_SHA224,	"sha-224" },
  { USERAUD_HASH_SHA256,	"sha-256" },
  { USERAUD_HASH_SHA384,	"sha-384" },
  { USERAUD_HASH_SHA512,	"sha-512" }

  /* +++++ Further hash types here and in useraudi.h USERAUD_HASH_xxx. +++++ */

};

/** Number of entries in the \a ht table. */
static size_t szht = sizeof(ht)/sizeof(HT);


/** List of alphanumeric characters.
*/
static char alnum[] = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
};



/** @defgroup	retrievedefaults Functions to retrieve default file names. */
/*@{*/

char *
uatcs_get_default_config_file_name DK_P0() {
  return uatcsDefaultConfigFileName;
}



char *
uatcs_get_default_database_name DK_P0() {
  return uatcsDefaultDatabaseName;
}



char *
uatcs_get_default_socket_name DK_P0() {
  return uatcsDefaultSocketName;
}



char *
uatcs_get_default_log_file_name DK_P0() {
  return uatcsDefaultLogFileName;
}


char *
uatcs_get_default_random_seed DK_P0() {
  return uatcsDefaultRandomSeed;
}

/*@}*/



unsigned long
uatcs_dotted_string_to_ip DK_P1(char *, hn)
{
  unsigned long back = 0UL;
  unsigned long u1 = 0UL, u2 = 0UL, u3 = 0UL, u4 = 0UL, u = 0UL;
  int ende, state; char *ptr;
  
  if(hn) {
    state = 0;
    u = u1 = u2 = u3 = u4 = 0UL;
    ptr = hn; ende = 0;
    while(!ende) {
      if(*ptr) { 
	if(isdigit(*ptr)) {
	  u = 0UL;
	  switch(*ptr) {
	    case '0': u = 0UL; break;
	    case '1': u = 1UL; break;
	    case '2': u = 2UL; break;
	    case '3': u = 3UL; break;
	    case '4': u = 4UL; break;
	    case '5': u = 5UL; break;
	    case '6': u = 6UL; break;
	    case '7': u = 7UL; break;
	    case '8': u = 8UL; break;
	    case '9': u = 9UL; break;
	  }
	  switch(state) {
	    case 0: u1 = 10UL * u1 + u; break;
	    case 1: u2 = 10UL * u2 + u; break;
	    case 2: u3 = 10UL * u3 + u; break;
	    case 3: u4 = 10UL * u4 + u; break;
	  }
	} else {
	  if(*ptr == '.') {
	    state++;
	    if(state >= 4) {
	      ende = 1;
	    }
	  }
	}
	ptr++;
      } else {
	ende = 1;
      } 
    }
  } 
  u1 = u1 << 24; u1 = u1 & 0xFF000000UL;
  u2 = u2 << 16; u2 = u2 & 0x00FF0000UL;
  u3 = u3 <<  8; u3 = u3 & 0x0000FF00UL;
  u4 = u4 & 0x000000FFUL;
  back = u1 | u2 | u3 | u4;
  
  return back;
}



int
uatcs_one_hash DK_P6(UAC *,uac,char *,d,size_t,szd,char *,s,char *,salt,int,ht)
{
  int back = 0;		/* Function result. */
  char tb[64];		/* Temporary buffer for binary digest. */
  size_t	bl;	/* Length of binary data. */
  size_t	sl;	/* Salt length. */
  
  d[0] = '\0';
  sl = strlen(salt);
  switch(ht) {
    case USERAUD_HASH_SHA512: {
#if DK_HAVE_OPENSSL_SHA_H && DK_HAVE_SHA512
      SHA512_CTX	sha512ctx;
      if((81 + sl) < szd) {	/* 81 * upper(64 * 5 / 4) */
        bl = dkenc_ra85string_to_bin(d, szd, salt);
        if(bl > 0) {
          SHA512_Init(&sha512ctx);
	  SHA512_Update(&sha512ctx, d, bl);
	  SHA512_Update(&sha512ctx, s, strlen(s));
	  SHA512_Final((unsigned char *)tb, &sha512ctx);
	  strcpy(d, salt);
	  dkenc_bin_to_ra85(&(d[sl]), (szd - sl), tb, 64);
	  back = 1;
        } else { d[0] = '\0'; }
      }
#endif
    } break;
    case USERAUD_HASH_SHA384: {
#if DK_HAVE_OPENSSL_SHA_H && DK_HAVE_SHA384
      SHA512_CTX        sha512ctx;
      if((61 + sl) < szd) {
        bl = dkenc_ra85string_to_bin(d, szd, salt);
        if(bl > 0) {
          SHA384_Init(&sha512ctx);
	  SHA384_Update(&sha512ctx, d, bl);
	  SHA384_Update(&sha512ctx, s, strlen(s));
	  SHA384_Final((unsigned char *)tb, &sha512ctx);
	  strcpy(d, salt);
	  dkenc_bin_to_ra85(&(d[sl]), (szd - sl), tb, 48);
	  back = 1;
        } else { d[0] = '\0'; }
      }
#endif
    } break;
    case USERAUD_HASH_SHA256: {
#if DK_HAVE_OPENSSL_SHA_H && DK_HAVE_SHA256
      SHA256_CTX	sha256ctx;
      if((41 + sl) < szd) {
        bl = dkenc_ra85string_to_bin(d, szd, salt);
        if(bl > 0) {
          SHA256_Init(&sha256ctx);
	  SHA256_Update(&sha256ctx, d, bl);
	  SHA256_Update(&sha256ctx, s, strlen(s));
	  SHA256_Final((unsigned char *)tb, &sha256ctx);
	  strcpy(d, salt);
	  dkenc_bin_to_ra85(&(d[sl]), (szd - sl), tb, 32);
	  back = 1;
        } else { d[0] = '\0'; }
      }
#endif
    } break;
    case USERAUD_HASH_SHA224: {
#if DK_HAVE_OPENSSL_SHA_H && DK_HAVE_SHA224
      SHA256_CTX	sha256ctx;
      if((36 + sl) < szd) {
        bl = dkenc_ra85string_to_bin(d, szd, salt);
        if(bl > 0) {
          SHA224_Init(&sha256ctx);
	  SHA224_Update(&sha256ctx, d, bl);
	  SHA224_Update(&sha256ctx, s, strlen(s));
	  SHA224_Final((unsigned char *)tb, &sha256ctx);
	  strcpy(d, salt);
	  dkenc_bin_to_ra85(&(d[sl]), (szd - sl), tb, 28);
	  back = 1;
        } else { d[0] = '\0'; }
      }
#endif
    } break;
    case USERAUD_HASH_RIPEMD160: {
#if DK_HAVE_OPENSSL_RIPEMD_H
      RIPEMD160_CTX	r160ctx;
      if((26 + sl) < szd) {
        bl = dkenc_ra85string_to_bin(d, szd, salt);
        if(bl > 0) {
          RIPEMD160_Init(&r160ctx);
	  RIPEMD160_Update(&r160ctx, d, bl);
	  RIPEMD160_Update(&r160ctx, s, strlen(s));
	  RIPEMD160_Final((unsigned char *)tb, &r160ctx);
	  strcpy(d, salt);
	  dkenc_bin_to_ra85(&(d[sl]), (szd - sl), tb, 20);
	  back = 1;
        } else { d[0] = '\0'; }
      }
#endif
    } break;
    case USERAUD_HASH_SHA1: {
#if DK_HAVE_OPENSSL_SHA_H
      SHA_CTX	sha1ctx;
      if((26 + sl) < szd) {
        bl = dkenc_ra85string_to_bin(d, szd, salt);
        if(bl > 0) {
          SHA1_Init(&sha1ctx);
	  SHA1_Update(&sha1ctx, d, bl);
	  SHA1_Update(&sha1ctx, s, strlen(s));
	  SHA1_Final((unsigned char *)tb, &sha1ctx);
	  strcpy(d, salt);
	  dkenc_bin_to_ra85(&(d[sl]), (szd - sl), tb, 20);
	  back = 1;
        } else { d[0] = '\0'; }
      }
#endif
    } break;
    case USERAUD_HASH_MD5: {
#if DK_HAVE_OPENSSL_MD5_H
      MD5_CTX md5ctx;
      if((21 + sl) < szd) {
        bl = dkenc_ra85string_to_bin(d, szd, salt);
        if(bl > 0) {
          MD5_Init(&md5ctx);
	  MD5_Update(&md5ctx, d, bl);
	  MD5_Update(&md5ctx, s, strlen(s));
	  MD5_Final((unsigned char *)tb, &md5ctx);
	  strcpy(d, salt);
	  dkenc_bin_to_ra85(&(d[sl]), (szd - sl), tb, 16);
	  back = 1;
        } else { d[0] = '\0'; }
      }
#endif
    } break;
    case USERAUD_HASH_CRYPT: {
#if DK_HAVE_CRYPT_H
      char *ptr;
      ptr = crypt(s, salt);
      if(ptr) {
        if(strlen(ptr) < szd) {
	  strcpy(d, ptr);
	  back = 1;
	} else { d[0] = '\0'; }
      } else { d[0] = '\0'; }
#endif
    } break;

    /* +++++ further hash types here +++++ */

  } 
  return back;
}



void
uatcs_correct_crypt_st_sl DK_P4(UAC *,uac, int *,st, int *,sl, char *,hash) {
  char *p1, *p2, *p3;
  
  if(*hash == '$') {		
    p1 = hash; p1++;
    p2 = dkstr_chr(p1, '$');
    if(p2) {			
      *p2 = '\0';
      p3 = p2; p3++; p3 = dkstr_chr(p3, '$');
      switch(dkstr_array_index(crypt_sub_types, p1, 0)) {
        case 0: {
	  *st = USERAUD_HASHSUB_CRYPT_MD5;
	} break;
	case 1: {
	  *st = USERAUD_HASHSUB_CRYPT_BLOWFISH;
	} break;
	case 2: {
	  *st = USERAUD_HASHSUB_CRYPT_SHA256;
	} break;
	case 3: {
	  *st = USERAUD_HASHSUB_CRYPT_SHA512;
	} break;
      }
      *p2 = '$';
      if(p3) {			
        *p3 = '\0';
	*sl = 1 + strlen(hash);
	*p3 = '$';
      }
    }
  } else {			
    *st = USERAUD_HASHSUB_CRYPT_DES;
    *sl = 2;
  } 
}



int
uatcs_hash_type DK_P1(char *,n) {
  int back = 0;
  int i;
  if(n) {
    for(i = 0; ((i < szht) && (back == 0)); i++) {
      if(dkstr_casecmp(ht[i].n, n) == 0) {
        back = ht[i].t;
      }
    }
  }
  return back;
}



int
uatcs_all_hash_types DK_P1(char *,n) {
  int back = 0;
  char *p1;
  char *p2;
  if(n) {
    p1 = dkstr_start(n, whspc);
    while(p1) {
      p2 = dkstr_next(p1, whspc);
      back |= uatcs_hash_type(p1);
      p1 = p2;
    }
  }
  return back;
}



int
uatcs_create_salt DK_P5(UAC *,uac, char *,d, size_t,szd, int,ht, int,st) {
  int back = 0;
  int lgt;
  int k;
  unsigned i;
  unsigned char buffer[128];
  
  if(ht != USERAUD_HASH_CRYPT) {		
    if(RAND_bytes(buffer, 64) == 1) {		
      lgt = dkenc_bin_to_ra85(d, szd, buffer, 64);	
      if(lgt > 0) {				
        back = 1;		
      }
    }
  } else {
    lgt = strlen(alnum);
    if(st != USERAUD_HASHSUB_CRYPT_DES) {
      if(st != USERAUD_HASHSUB_CRYPT_BIG) {
        back = 1;
	for(k = 0; k < 8; k++) {
	  if(RAND_bytes((unsigned char *)(&i), sizeof(i)) == 1) {
	    buffer[k] = alnum[ i % lgt ];
	  } else {
	    back = 0;
	  }
	}
	buffer[8] = '\0';
      }
    }
    switch(st) {
      case USERAUD_HASHSUB_CRYPT_DES:
      case USERAUD_HASHSUB_CRYPT_BIG:{
        back = 0;
        if(szd > 2) {
	  if(RAND_bytes((unsigned char *)(&i), sizeof(i)) == 1) {
	    d[0] = alnum[ i % lgt ];
	    if(RAND_bytes((unsigned char *)(&i), sizeof(i)) == 1) {
	      d[1] = alnum[ i % lgt ];
	      d[2] = '\0';
	      back = 1;
	    }
	  }
	}
      } break;
      case USERAUD_HASHSUB_CRYPT_MD5: {
        if((back) && (szd > 15)) {
	  sprintf(d, "$1$%s$", buffer);
	} else {
	  back = 0;
	}
      } break;
      case USERAUD_HASHSUB_CRYPT_SHA256: {
        if((back) && (szd > 15)) {
	  sprintf(d, "$5$%s$", buffer);
	} else {
	  back = 0;
	}
      } break;
      case USERAUD_HASHSUB_CRYPT_SHA512: {
        if((back) && (szd > 15)) {
	  sprintf(d, "$6$%s$", buffer);
	} else {
	  back = 0;
	}
      } break;
      case USERAUD_HASHSUB_CRYPT_BLOWFISH: {
        if((back) && (szd > 15)) {
	  sprintf(d, "$2a$%s$", buffer);
	} else {
	  back = 0;
	}
      } break;

    }
  } 
  return back;
}



char *
uatcs_get_hash_name DK_P1(int,t) {
  char *back = NULL;
  switch(t) {
    case USERAUD_HASH_SHA512: {
      back = ht[7].n;
    } break;
    case USERAUD_HASH_SHA384: {
      back = ht[6].n;
    } break;
    case USERAUD_HASH_SHA256: {
      back = ht[5].n;
    } break;
    case USERAUD_HASH_SHA224: {
      back = ht[4].n;
    } break;
    case USERAUD_HASH_RIPEMD160: {
      back = ht[3].n;
    } break;
    case USERAUD_HASH_SHA1: {
      back = ht[2].n;
    } break;
    case USERAUD_HASH_MD5: {
      back = ht[1].n;
    } break;
    case USERAUD_HASH_CRYPT: {
      back = ht[0].n;
    } break;
  }
  return back;
}



char
uatcs_get_alnum DK_P1(unsigned,i) {
  char back;
  back = alnum[ i % (sizeof(alnum)-1) ];
  return back;
}



int
uatcs_apply_challenge DK_P7(UAC *,uac, char *,ct, char *,ch, char *,pw, char *, o, size_t,szo, int,rf)
{
  int back = 0;
  char	b1[USERAUD_LINESIZE];	/* Salt for step. */
  char	b2[sizeof(b1)];		/* Step input. */
  char	b3[sizeof(b1)];		/* Step output. */
  char	*chpc = NULL;		/* Current challenge step. */
  char	*chpn = NULL;		/* Next challenge step. */
  char	*chps = NULL;		/* Salt length pointer. */
  size_t	lgtsl = 0;	/* Length of salt available. */
  size_t	su = 0;		/* Salt used. */
  size_t	sip = 0;	/* Salt length in pass. */
  size_t	i = 0;		/* Copy salt to salt buffer. */
  int		passno = 0;	/* Current pass number. */
  unsigned	u = 0;		/* Used to retrieve pass salt length. */
  int		ht = 0;		/* Hash type for current pass. */
  
  if((ct) && (ch) && (pw) && (o) && (szo)) {
    lgtsl = strlen(ch);					
    if((lgtsl > 0) && (strlen(pw) < sizeof(b2))) {	
      strcpy(b2, pw);
      chpc = dkstr_start(ct, NULL);
      if(chpc) {				
        back = 1; passno = 0; su = 0;
	while((chpc) && (back)) {		
	  ht = 0;
	  chpn = dkstr_next(chpc, whspc);
	  chps = dkstr_chr(chpc, ':');
	  sip = 0;
	  if(chps) {				
	    *(chps++) = '\0';
	    if(sscanf(chps, "%u", &u) == 1) {	
	      sip = (size_t)u;
	      if(sip > lgtsl) sip = lgtsl;
	    } else {				
	      back = 0;
	    }
	  } else {				
	    sip = lgtsl;
	  }
	  if(sip > 0) {				
	    if(back) {				
	      for(i = 0; i < sip; i++) {
	        b1[i] = ch[su + i];
	      } b1[i] = '\0';			
	      if((passno > 0) || (rf)) {	
	        ht = uatcs_hash_type(chpc);
		if(ht) {			
	          if(!uatcs_one_hash(uac, b3, sizeof(b3), b2, b1, ht)) {
		    back = 0;			
		  }
		} else {			
		  back = 0;
		}
	      } else {
	        strcpy(b3, b2);
	      }
	    }
	  } else {				
	    back = 0;
	  }
	  if(back) {
	    strcpy(b2, b3);
	  }
	  lgtsl = lgtsl - sip;
	  su += sip;
	  chpc = chpn; passno++;
	} 
	if(back) {				
	  if(strlen(b3) < szo) {		
	    strcpy(o, b3);
	  } else {
	    back = 0;				
	  }
	}
      }
    }
  } 
  return back;
}




