LCOV - code coverage report
Current view: top level - lib - auth.c (source / functions) Hit Total Coverage
Test: smtpd.info Lines: 60 77 77.9 %
Date: 2015-11-25 19:06:20 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /* This file is part of the cmail project (http://cmail.rocks/)
       2             :  * (c) 2015 Fabian "cbdev" Stumpf
       3             :  * License: Simplified BSD (2-Clause)
       4             :  * For further information, consult LICENSE.txt
       5             :  */
       6             : 
       7           7 : int auth_base64decode(LOGGER log, char* in){
       8             :         uint32_t decode_buffer;
       9             :         int group, len, i;
      10             :         char* idx;
      11             : 
      12           7 :         char* base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      13           7 :         len = strlen(in);
      14             : 
      15           7 :         if(len % 4){
      16           1 :                 logprintf(log, LOG_WARNING, "Input has invalid length for base64\n");
      17           1 :                 return -1;
      18             :         }
      19             : 
      20             :         //decode to code point indices
      21          74 :         for(i = 0; i < len; i++){
      22          69 :                 if(in[i] == '='){
      23             :                         //'=' is only allowed as trailing character, so fail if it is within valid base64
      24             :                         //this is marked MUST by some rfcs (5034)
      25           1 :                         for(i++; i < len; i++){
      26           1 :                                 if(in[i] != '='){
      27           1 :                                         logprintf(log, LOG_WARNING, "Input string contains = as non-trailing character\n");
      28           1 :                                         return -1;
      29             :                                 }
      30             :                         }
      31             : 
      32           0 :                         in[i] = 0;
      33           0 :                         break;
      34             :                 }
      35             : 
      36          68 :                 idx = index(base64_alphabet, in[i]);
      37          68 :                 if(!idx){
      38           0 :                         logprintf(log, LOG_WARNING, "Input string contains invalid characters\n");
      39           0 :                         return -1;
      40             :                 }
      41          68 :                 in[i] = idx - base64_alphabet;
      42             :         }
      43             : 
      44          19 :         for(group = 0; group < (len / 4); group++){
      45             :                 //stuff the buffer
      46          14 :                 decode_buffer = 0 | (in[group * 4] << 18);
      47          14 :                 decode_buffer |= (in[(group * 4) + 1] << 12);
      48          14 :                 decode_buffer |= (in[(group * 4) + 2] << 6);
      49          14 :                 decode_buffer |= (in[(group * 4) + 3]);
      50             : 
      51             :                 //read back decoded characters
      52          14 :                 in[(group * 3)] = (decode_buffer & 0xFF0000) >> 16;
      53          14 :                 in[(group * 3) + 1] = (decode_buffer & 0xFF00) >> 8;
      54          14 :                 in[(group * 3) + 2] = (decode_buffer & 0xFF);
      55          14 :                 in[(group * 3) + 3] = 0;
      56             :         }
      57             : 
      58           5 :         return (group * 3) + 3;
      59             : }
      60             : 
      61           3 : int auth_hash(char* hash, unsigned hash_bytes, char* salt, unsigned salt_bytes, char* pass, unsigned pass_bytes){
      62             :         struct sha256_ctx hash_context;
      63             :         uint8_t digest[SHA256_DIGEST_SIZE];
      64             : 
      65           3 :         if(hash_bytes < BASE16_ENCODE_LENGTH(SHA256_DIGEST_SIZE)){
      66           0 :                 return -1;
      67             :         }
      68             : 
      69           3 :         sha256_init(&hash_context);
      70             : 
      71           3 :         sha256_update(&hash_context, salt_bytes, (uint8_t*)salt);
      72           3 :         sha256_update(&hash_context, pass_bytes, (uint8_t*)pass);
      73             : 
      74           3 :         sha256_digest(&hash_context, SHA256_DIGEST_SIZE, digest);
      75           3 :         base16_encode_update((uint8_t*)hash, SHA256_DIGEST_SIZE, digest);
      76           3 :         return BASE16_ENCODE_LENGTH(SHA256_DIGEST_SIZE);
      77             : }
      78             : 
      79             : #ifdef CMAIL_HAVE_DATABASE_TYPE
      80           5 : int auth_validate(LOGGER log, DATABASE* database, char* user, char* password, char** authorized_identity){
      81           5 :         int status, rv = -1;
      82             :         char* user_salt;
      83             :         char* stored_hash;
      84             :         char digest_b16[BASE16_ENCODE_LENGTH(SHA256_DIGEST_SIZE) + 1];
      85           5 :         char* auth_id = NULL;
      86             : 
      87           5 :         if(!user || !password){
      88           0 :                 return -1;
      89             :         }
      90             : 
      91           5 :         memset(digest_b16, 0, sizeof(digest_b16));
      92           5 :         logprintf(log, LOG_DEBUG, "Trying to authenticate %s\n", user);
      93             : 
      94           5 :         if(sqlite3_bind_text(database->query_authdata, 1, user, -1, SQLITE_STATIC) != SQLITE_OK){
      95           0 :                 logprintf(log, LOG_ERROR, "Failed to bind auth data query parameter\n");
      96           0 :                 sqlite3_reset(database->query_authdata);
      97           0 :                 sqlite3_clear_bindings(database->query_authdata);
      98           0 :                 return -1;
      99             :         }
     100             : 
     101           5 :         status = sqlite3_step(database->query_authdata);
     102           5 :         switch(status){
     103             :                 case SQLITE_ROW:
     104           3 :                         user_salt = (char*)sqlite3_column_text(database->query_authdata, 0);
     105           3 :                         if(user_salt){
     106           3 :                                 stored_hash = index(user_salt, ':');
     107           3 :                                 if(!stored_hash){
     108           0 :                                         logprintf(log, LOG_INFO, "Rejecting authentication for user %s, database entry invalid\n", user);
     109           0 :                                         break;
     110             :                                 }
     111             : 
     112             :                                 //calculate credentials hash
     113           3 :                                 auth_hash(digest_b16, sizeof(digest_b16), user_salt, stored_hash - user_salt, password, strlen(password));
     114             : 
     115           3 :                                 if(!strcmp(stored_hash + 1, digest_b16)){
     116           3 :                                         auth_id = (char*)sqlite3_column_text(database->query_authdata, 1);
     117           3 :                                         logprintf(log, LOG_INFO, "Credentials for user %s OK, authorized identity: %s\n", user, auth_id ? auth_id:user);
     118             : 
     119             :                                         //handle aliasing
     120           3 :                                         if(authorized_identity){
     121           3 :                                                 if(auth_id){
     122           0 :                                                         *authorized_identity = common_strdup(auth_id);
     123             :                                                 }
     124             :                                                 else{
     125           3 :                                                         *authorized_identity = common_strdup(user);
     126             :                                                 }
     127             :                                         }
     128           3 :                                         rv = 0;
     129             :                                 }
     130             :                                 else{
     131           0 :                                         logprintf(log, LOG_INFO, "Credentials check failed for user %s: %s\n", user, digest_b16);
     132             :                                 }
     133             :                         }
     134             :                         else{
     135           0 :                                 logprintf(log, LOG_INFO, "Rejecting authentication for user %s, not enabled in database\n", user);
     136             :                         }
     137           3 :                         break;
     138             :                 case SQLITE_DONE:
     139           2 :                         logprintf(log, LOG_INFO, "Unknown user %s\n", user);
     140           2 :                         break;
     141             :                 default:
     142           0 :                         logprintf(log, LOG_INFO, "Unhandled return value from auth data query: %d (%s)\n", status, sqlite3_errmsg(database->conn));
     143           0 :                         break;
     144             :         }
     145             : 
     146           5 :         sqlite3_reset(database->query_authdata);
     147           5 :         sqlite3_clear_bindings(database->query_authdata);
     148             : 
     149           5 :         return rv;
     150             : }
     151             : #endif

Generated by: LCOV version 1.11