1 mike 1.1 /*
2 **==============================================================================
3 **
4 ** Open Management Infrastructure (OMI)
5 **
6 ** Copyright (c) Microsoft Corporation
7 **
8 ** Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 ** use this file except in compliance with the License. You may obtain a copy
10 ** of the License at
11 **
12 ** http://www.apache.org/licenses/LICENSE-2.0
13 **
14 ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
16 ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
17 ** MERCHANTABLITY OR NON-INFRINGEMENT.
18 **
19 ** See the Apache 2 License for the specific language governing permissions
20 ** and limitations under the License.
21 **
22 mike 1.1 **==============================================================================
23 */
24
25 #include "credcache.h"
26 #include "log.h"
27 #include <base/time.h>
28
29 #if defined (CONFIG_POSIX)
30 # include <openssl/evp.h>
31 # include <openssl/rand.h>
32
33 /*
34 **==============================================================================
35 **
36 ** Local definitions:
37 **
38 **==============================================================================
39 */
40 /* how long entry in cache is valid */
41 #define CRED_CACHE_TIME_TO_KEEP_USEC (((MI_Uint64)120) * 1000000)
42 /* since pre-defined structures are used, user name is limited in size */
43 mike 1.1 #define CRED_USER_NAME_MAX_LEN 32
44 /* Max hash length (sha512) */
45 #define CRED_HASH_MAX_LEN 64
46 /* Salt size */
47 #define CRED_SALT_SIZE 16
48 /* Max entries in cache (number of simultaneously identified users) */
49 #define CRED_ITEMS_MAX 4
50
51
52 typedef struct _CredItem
53 {
54 char user[CRED_USER_NAME_MAX_LEN];
55 unsigned char hash[CRED_HASH_MAX_LEN];
56 MI_Uint64 timestamp;
57 } CredItem;
58
59 static unsigned char s_salt[CRED_SALT_SIZE];
60 static CredItem s_cache[CRED_ITEMS_MAX];
61 static int s_init;
62 static int s_initAttempted;
63 static const EVP_MD* s_md;
64 mike 1.1 static MI_Uint64 s_expirationTime_us = CRED_CACHE_TIME_TO_KEEP_USEC;
65
66 static int _Init()
67 {
68 /* Avoid mulitple attempts to init if it failed */
69 if (s_initAttempted)
70 return -1;
71
72 s_initAttempted = 1;
73
74 /* Initialize salt */
75 if (0 == RAND_load_file("/dev/urandom", 1024))
76 {
77 LOGW_CHAR(("failed to load /dev/urandom"));
78 return -1;
79 }
80
81 if (0 == RAND_bytes(s_salt, sizeof(s_salt)))
82 {
83 LOGW_CHAR(("failed to init salt"));
84 return -1;
85 mike 1.1 }
86
87 /* Find digest */
88 OpenSSL_add_all_digests();
89
90 /* Find digest, starting with strongest */
91 if (!(s_md = EVP_get_digestbyname("sha512")) &&
92 !(s_md = EVP_get_digestbyname("sha384")) &&
93 !(s_md = EVP_get_digestbyname("sha256")) &&
94 !(s_md = EVP_get_digestbyname("sha224")) &&
95 !(s_md = EVP_get_digestbyname("sha1")))
96 {
97 LOGW_CHAR(("no digest available"));
98 return -1;
99 }
100
101 s_init = 1;
102 return 0;
103 }
104
105 /* Calculates hash:
106 mike 1.1 uses 3 parts:
107 - user name
108 - pwd
109 - salt
110 this way hash values are unique per user, per startup sequence
111 */
112 static void _Hash(
113 const char* data1,
114 int size1,
115 const char* data2,
116 int size2,
117 unsigned char hash[CRED_HASH_MAX_LEN])
118 {
119 EVP_MD_CTX ctx;
120 unsigned int hashSize = CRED_HASH_MAX_LEN;
121
122 EVP_DigestInit(&ctx, s_md);
123 EVP_DigestUpdate(&ctx, data1, size1);
124 EVP_DigestUpdate(&ctx, data2, size2);
125 EVP_DigestUpdate(&ctx, s_salt, sizeof(s_salt));
126 EVP_DigestFinal(&ctx, hash, &hashSize);
127 mike 1.1 }
128
129 /* Find position to add/update user:
130 if user is already in cache, it returns this position,
131 otherwise if empty item available - retunrs it,
132 otherwise retunrs oldest element */
133 static int _FindUserEmptyOldest(
134 const char* user)
135 {
136 int posEmpty = -1, posOldest = 0, pos;
137 MI_Uint64 timestampOldest = s_cache[0].timestamp;
138
139 for (pos = 0; pos < MI_COUNT(s_cache); pos++)
140 {
141 /* Did we find user? */
142 if (strcmp(user,s_cache[pos].user) == 0)
143 return pos;
144
145 /* Is it empty? */
146 if (0 == s_cache[pos].user[0])
147 {
148 mike 1.1 posEmpty = pos;
149 }
150 else if (-1 == posEmpty)
151 {
152 /* Is it oldest with no epmty? */
153 if (timestampOldest > s_cache[pos].timestamp)
154 {
155 timestampOldest = s_cache[pos].timestamp;
156 posOldest = pos;
157 }
158 }
159 }
160
161 if (-1 != posEmpty)
162 return posEmpty;
163
164 return posOldest;
165 }
166
167 /* Find position with given user:
168 Returns:
169 mike 1.1 user posiiton if found; -1 otherwise
170 */
171 static int _Find(
172 const char* user)
173 {
174 int pos;
175
176 for (pos = 0; pos < MI_COUNT(s_cache); pos++)
177 {
178 /* Did we find user? */
179 if (strcmp(user,s_cache[pos].user) == 0)
180 return pos;
181 }
182
183 return -1;
184 }
185
186 /*
187 Adds user name and password into cache
188 */
189 void CredCache_PutUser(const char* user, const char* password)
190 mike 1.1 {
191 int pos;
192 int userLen;
193
194 if (!s_init && 0 != _Init())
195 {
196 return;
197 }
198
199 /* Check if user name is too long for cache */
200 userLen = strlen(user);
201 if (userLen >= CRED_USER_NAME_MAX_LEN)
202 return;
203
204 /* find position for user */
205 pos = _FindUserEmptyOldest(user);
206
207 /* timestamp */
208 if (MI_RESULT_OK != Time_Now(&s_cache[pos].timestamp))
209 return;
210
211 mike 1.1 /* user name */
212 strcpy(s_cache[pos].user, user);
213
214 /* hash */
215 _Hash(user, userLen, password, strlen(password), s_cache[pos].hash);
216 }
217
218 /*
219 Checks if user credentials matches the one in cache
220 Returns:
221 '0' if user account matches entry in cache
222 '-1' if user is not in cache, if password does not match or
223 record expired
224 */
225 int CredCache_CheckUser(const char* user, const char* password)
226 {
227 int pos;
228 unsigned char hash[CRED_HASH_MAX_LEN];
229 MI_Uint64 now;
230
231 /* 'no' if not initialized */
232 mike 1.1 if (!s_init)
233 return -1;
234
235 /* Does user exisit in cache */
236 if (-1 == (pos = _Find(user)))
237 return -1;
238
239 /* Is it expired? */
240 if (MI_RESULT_OK != Time_Now(&now))
241 return -1;
242
243 if (s_cache[pos].timestamp + s_expirationTime_us < now)
244 return -1;
245
246 /* Hash matches? */
247 memset(hash, 0, sizeof(hash));
248 _Hash(user, strlen(user), password, strlen(password), hash);
249
250 assert(pos < MI_COUNT(s_cache));
251
252 if (0 != memcmp(hash, s_cache[pos].hash, sizeof(hash)))
253 mike 1.1 return -1;
254
255 /* Credentials are valid */
256 return 0;
257 }
258
259 /* Unit-test support - updating expiration timeout */
260 void CredCache_SetExpirationTimeout(MI_Uint64 expirationTimeUS)
261 {
262 if (expirationTimeUS)
263 s_expirationTime_us = expirationTimeUS;
264 else
265 s_expirationTime_us = CRED_CACHE_TIME_TO_KEEP_USEC;
266 }
267
268 /* Unit-test support mostly - clear all cached items */
269 void CredCache_Clean()
270 {
271 memset(s_cache, 0, sizeof(s_cache));
272 }
273
274 mike 1.1 /*
275 Generates crypto-suitable random data
276 Parameters:
277 buf - bufer for random data
278 size - number of bytes to generate
279
280 Returns:
281 0 - success
282 -1 - failed
283 */
284 int CredCache_GenerateRandom(
285 char* buf,
286 size_t size)
287 {
288
289 /* Initialize if needed */
290 if (!s_init && 0 != _Init())
291 {
292 return -1;
293 }
294
295 mike 1.1 if (0 == RAND_bytes((unsigned char*)buf, size))
296 {
297 LOGW_CHAR(("failed to produce random data"));
298 return -1;
299 }
300
301 return 0;
302 }
303
304 #endif /* defined(CONFIG_POSIX) */
|