(file) Return to credcache.c CVS log (file) (dir) Up to [OMI] / omi / base

  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) */

ViewCVS 0.9.2