/* 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" $(trace-include) /** @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; $? "+ dotted_string_to_ip %s", TR_STR(hn) if(hn) { state = 0; u = u1 = u2 = u3 = u4 = 0UL; ptr = hn; ende = 0; while(!ende) { if(*ptr) { $? ". \"%s\", %lu.%lu.%lu.%lu", ptr, u1, u2, u3, u4 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; } $? ". %lu.%lu.%lu.%lu", u1, u2, u3, u4 } } $? ". %lu.%lu.%lu.%lu", u1, u2, u3, u4 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; $? "- dotted_string_to_ip %08lx", back 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. */ $? "+ uatcs_one_hash pw=\"%s\" salt=\"%s\"", TR_STR(s), TR_STR(salt) 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 +++++ */ } $? "- uatcs_one_hash %d %s", back, d return back; } void uatcs_correct_crypt_st_sl DK_P4(UAC *,uac, int *,st, int *,sl, char *,hash) { char *p1, *p2, *p3; $? "+ uatcs_correct_crypt_st_sl" if(*hash == '$') { $? ". must correct sl" p1 = hash; p1++; p2 = dkstr_chr(p1, '$'); if(p2) { $? ". second $ found" *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) { $? ". third $ found" *p3 = '\0'; *sl = 1 + strlen(hash); *p3 = '$'; } } } else { $? ". no correction necessary" *st = USERAUD_HASHSUB_CRYPT_DES; *sl = 2; } $? "- uatcs_correct_crypt_st_sl \"%s\"", hash } 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]; $? "+ uatcs_create_salt %d/%d", ht, st if(ht != USERAUD_HASH_CRYPT) { $? ". not crypt" if(RAND_bytes(buffer, 64) == 1) { $? ". RAND_bytes() ok" lgt = dkenc_bin_to_ra85(d, szd, buffer, 64); $? ". ra85 ok" if(lgt > 0) { $? ". length ok" back = 1; $? ". salt = \"%s\"", d } } } 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; } } $? "- uatcs_create_salt %d", back 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. */ $? "+ uatcs_apply_challenge" if((ct) && (ch) && (pw) && (o) && (szo)) { lgtsl = strlen(ch); $? ". args ok" if((lgtsl > 0) && (strlen(pw) < sizeof(b2))) { $? ". length ok" strcpy(b2, pw); chpc = dkstr_start(ct, NULL); if(chpc) { $? ". challenge type ok" back = 1; passno = 0; su = 0; while((chpc) && (back)) { $? ". yet another pass" ht = 0; chpn = dkstr_next(chpc, whspc); chps = dkstr_chr(chpc, ':'); sip = 0; if(chps) { $? ". salt length specified" *(chps++) = '\0'; if(sscanf(chps, "%u", &u) == 1) { $? ". length numeric, ok" sip = (size_t)u; if(sip > lgtsl) sip = lgtsl; } else { $? "! salt length not numeric" back = 0; } } else { $? ". use all remaining salt" sip = lgtsl; } if(sip > 0) { $? ". salt available %u", (unsigned)sip if(back) { $? ". no error yet" for(i = 0; i < sip; i++) { b1[i] = ch[su + i]; } b1[i] = '\0'; $? ". pass salt \"%s\"", b1 if((passno > 0) || (rf)) { $? ". pass matters" ht = uatcs_hash_type(chpc); if(ht) { $? ". hash type ok" if(!uatcs_one_hash(uac, b3, sizeof(b3), b2, b1, ht)) { back = 0; $? "! error applying hash" } } else { $? "! hash type not found" back = 0; } } else { strcpy(b3, b2); } } } else { $? "! no more salt available" back = 0; } if(back) { strcpy(b2, b3); } lgtsl = lgtsl - sip; su += sip; chpc = chpn; passno++; } $? ". all passes finished" if(back) { $? ". no error yet" if(strlen(b3) < szo) { $? ". result buffer length ok" strcpy(o, b3); } else { back = 0; $? "! result buffer too small" } } } } } $? "- uatcs_apply_challenge %d", back return back; }