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
|