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 4 : int auth_base64decode(LOGGER log, char* in){
8 : uint32_t decode_buffer;
9 : int group, len, i;
10 : char* idx;
11 :
12 4 : char* base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
13 4 : len = strlen(in);
14 :
15 4 : 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 44 : for(i = 0; i < len; i++){
22 43 : 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 2 : 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 1 : in[i] = 0;
33 1 : break;
34 : }
35 :
36 41 : idx = index(base64_alphabet, in[i]);
37 41 : if(!idx){
38 0 : logprintf(log, LOG_WARNING, "Input string contains invalid characters\n");
39 0 : return -1;
40 : }
41 41 : in[i] = idx - base64_alphabet;
42 : }
43 :
44 10 : for(group = 0; group < (len / 4); group++){
45 : //stuff the buffer
46 8 : decode_buffer = 0 | (in[group * 4] << 18);
47 8 : decode_buffer |= (in[(group * 4) + 1] << 12);
48 8 : decode_buffer |= (in[(group * 4) + 2] << 6);
49 8 : decode_buffer |= (in[(group * 4) + 3]);
50 :
51 : //read back decoded characters
52 8 : in[(group * 3)] = (decode_buffer & 0xFF0000) >> 16;
53 8 : in[(group * 3) + 1] = (decode_buffer & 0xFF00) >> 8;
54 8 : in[(group * 3) + 2] = (decode_buffer & 0xFF);
55 8 : in[(group * 3) + 3] = 0;
56 : }
57 :
58 2 : return (group * 3) + 3;
59 : }
60 :
61 10 : 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 10 : if(hash_bytes < BASE16_ENCODE_LENGTH(SHA256_DIGEST_SIZE)){
66 0 : return -1;
67 : }
68 :
69 10 : sha256_init(&hash_context);
70 :
71 10 : sha256_update(&hash_context, salt_bytes, (uint8_t*)salt);
72 10 : sha256_update(&hash_context, pass_bytes, (uint8_t*)pass);
73 :
74 10 : sha256_digest(&hash_context, SHA256_DIGEST_SIZE, digest);
75 10 : base16_encode_update((uint8_t*)hash, SHA256_DIGEST_SIZE, digest);
76 10 : return BASE16_ENCODE_LENGTH(SHA256_DIGEST_SIZE);
77 : }
78 :
79 : #ifdef CMAIL_HAVE_DATABASE_TYPE
80 12 : int auth_validate(LOGGER log, DATABASE* database, char* user, char* password, char** authorized_identity){
81 12 : int status, rv = -1;
82 : char* user_salt;
83 : char* stored_hash;
84 : char digest_b16[BASE16_ENCODE_LENGTH(SHA256_DIGEST_SIZE) + 1];
85 12 : char* auth_id = NULL;
86 :
87 12 : if(!user || !password){
88 0 : return -1;
89 : }
90 :
91 12 : memset(digest_b16, 0, sizeof(digest_b16));
92 12 : logprintf(log, LOG_DEBUG, "Trying to authenticate %s\n", user);
93 :
94 12 : 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 12 : status = sqlite3_step(database->query_authdata);
102 12 : switch(status){
103 : case SQLITE_ROW:
104 10 : user_salt = (char*)sqlite3_column_text(database->query_authdata, 0);
105 10 : if(user_salt){
106 10 : stored_hash = index(user_salt, ':');
107 10 : 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 10 : auth_hash(digest_b16, sizeof(digest_b16), user_salt, stored_hash - user_salt, password, strlen(password));
114 :
115 10 : if(!strcmp(stored_hash + 1, digest_b16)){
116 7 : auth_id = (char*)sqlite3_column_text(database->query_authdata, 1);
117 7 : logprintf(log, LOG_INFO, "Credentials for user %s OK, authorized identity: %s\n", user, auth_id ? auth_id:user);
118 :
119 : //handle aliasing
120 7 : if(authorized_identity){
121 7 : if(auth_id){
122 0 : *authorized_identity = common_strdup(auth_id);
123 : }
124 : else{
125 7 : *authorized_identity = common_strdup(user);
126 : }
127 : }
128 7 : rv = 0;
129 : }
130 : else{
131 3 : 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 10 : 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 12 : sqlite3_reset(database->query_authdata);
147 12 : sqlite3_clear_bindings(database->query_authdata);
148 :
149 12 : return rv;
150 : }
151 : #endif
|