UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2013 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <pthread.h> 33 34 #include <cx/map.h> 35 36 #include "../public/nsapi.h" 37 #include "../util/atomic.h" 38 #include "auth.h" 39 40 static pthread_mutex_t auth_cache_mutex = PTHREAD_MUTEX_INITIALIZER; 41 static UserCache cache; 42 43 void auth_cache_init() { 44 log_ereport(LOG_VERBOSE, "auth_cache_init"); 45 // TODO: config parameters 46 //pthread_mutex_init(&auth_cache_mutex, NULL); 47 cache.map = calloc(80, sizeof(UserCacheElm)); 48 cache.size = 80; 49 cache.count = 0; 50 cache.max_users = 64; 51 cache.head = NULL; 52 cache.trail = NULL; 53 } 54 55 User* auth_cache_get(char *authdb, const char *user) { 56 //printf("auth_cache_get: %s\n", user); 57 /* 58 * create the key to access the map 59 * key: authdb\0user 60 */ 61 size_t authdblen = strlen(authdb); 62 size_t userlen = strlen(user); 63 64 size_t keylen = authdblen + userlen + 1; 65 unsigned char *key = malloc(keylen); 66 memcpy(key, authdb, authdblen); 67 key[authdblen] = 0; 68 memcpy(key + authdblen + 1, user, userlen); 69 70 CxHashKey mapkey = cx_hash_key_bytes(key, keylen); 71 72 // get cached user from map 73 time_t now = time(NULL); 74 size_t slot = mapkey.hash%cache.size; 75 76 User *u = NULL; 77 pthread_mutex_lock(&auth_cache_mutex); 78 79 UserCacheElm *elm = cache.map[slot]; 80 while(elm && elm->key.hash != mapkey.hash) { 81 elm = elm->next_elm; 82 } 83 // if we have an elm, the hash is correct 84 if(elm) { 85 // compare the key data to be sure it is the correct user 86 int n = (mapkey.len > elm->key.len) ? elm->key.len : mapkey.len; 87 if (!memcmp(elm->key.data, mapkey.data, n)) { 88 // elm is now the correct UserCacheElm 89 // TODO: use configuration for expire time 90 if(now - elm->created > 120) { 91 // cached user expired 92 // remove all users from the list from the first to this one 93 UserCacheElm *e = cache.head; 94 while(e) { 95 if(e == elm) { 96 break; 97 } 98 UserCacheElm *nu = e->next_user; 99 auth_cache_remove_from_map(e); 100 e = nu; 101 } 102 cache.head = elm->next_user; 103 if(cache.trail == elm) { 104 cache.trail = NULL; 105 } 106 auth_cache_remove_from_map(elm); 107 u = NULL; 108 } else { 109 u = (User*)elm->user; 110 } 111 } 112 } 113 114 pthread_mutex_unlock(&auth_cache_mutex); 115 free(key); 116 return u; 117 } 118 119 void auth_cache_add( 120 char *authdb, 121 User *user, 122 const char *password, 123 const char **groups, 124 size_t numgroups) 125 { 126 //printf("auth_cache_add: %s\n", user->name); 127 /* 128 * this function does not check, if the user is already in the map 129 * use it only after auth_cache_get 130 */ 131 132 CachedUser *cusr = malloc(sizeof(CachedUser)); 133 cusr->user.name = strdup(user->name); 134 cusr->user.uid = user->uid; 135 cusr->user.gid = user->gid; 136 cusr->user.verify_password = 137 (user_verify_passwd_f)cached_user_verify_password; 138 cusr->user.check_group = (user_check_group_f)cached_user_check_group; 139 cusr->user.free = (user_free_f)cached_user_unref; 140 141 cusr->authdb = strdup(authdb); 142 cusr->password = strdup(password); 143 cusr->groups = numgroups ? calloc(numgroups, sizeof(cxmutstr)) : NULL; 144 cusr->numgroups = numgroups; 145 for(int i=0;i<numgroups;i++) { 146 cusr->groups[i] = cx_strdup(cx_str(groups[i])); 147 } 148 cusr->ref = 1; 149 150 /* 151 * add the user to the auth cache 152 * the auth cache is a list of all cached users 153 */ 154 155 // create list element 156 time_t now = time(NULL); 157 UserCacheElm *elm = malloc(sizeof(UserCacheElm)); 158 elm->user = cusr; 159 elm->created = now; 160 elm->next_elm = NULL; 161 elm->next_user = NULL; 162 163 // create map key 164 size_t authdblen = strlen(authdb); 165 size_t userlen = strlen(user->name); 166 size_t keylen = authdblen + userlen + 1; 167 unsigned char *key = malloc(keylen); 168 memcpy(key, authdb, authdblen); 169 key[authdblen] = 0; 170 memcpy(key + authdblen + 1, user->name, userlen); 171 CxHashKey mapkey = cx_hash_key_bytes(key, keylen); 172 173 elm->key.data = key; 174 elm->key.len = mapkey.len; 175 elm->key.hash = mapkey.hash; 176 elm->slot = mapkey.hash%cache.size; 177 178 // add user to list and map 179 pthread_mutex_lock(&auth_cache_mutex); 180 181 // remove the first cached user if expired or the cache is full 182 if(cache.head && 183 (cache.count >= cache.max_users || now-cache.head->created > 120)) 184 { 185 UserCacheElm *first = cache.head; 186 cache.head = first->next_user; 187 if(!cache.head) { 188 cache.trail = NULL; 189 } 190 auth_cache_remove_from_map(first); 191 } 192 193 // add to map 194 UserCacheElm *prevelm = cache.map[elm->slot]; 195 if(prevelm) { 196 for(;;) { 197 if(!prevelm->next_elm) { 198 break; 199 } 200 prevelm = prevelm->next_elm; 201 } 202 } 203 if(prevelm) { 204 prevelm->next_elm = elm; 205 } else { 206 cache.map[elm->slot] = elm; 207 } 208 209 // add to list 210 if(cache.head) { 211 cache.trail->next_user = elm; 212 cache.trail = elm; 213 } else { 214 cache.head = elm; 215 cache.trail = elm; 216 } 217 218 cache.count++; 219 220 pthread_mutex_unlock(&auth_cache_mutex); 221 } 222 223 void auth_cache_remove_from_map(UserCacheElm *elm) { 224 UserCacheElm *prevelm = NULL; 225 UserCacheElm *e = cache.map[elm->slot]; 226 while(e) { 227 if(e == elm) { 228 break; 229 } else { 230 prevelm = e; 231 } 232 e = e->next_elm; 233 } 234 if(prevelm) { 235 prevelm->next_elm = elm->next_elm; 236 } else { 237 cache.map[elm->slot] = elm->next_elm; 238 } 239 240 free((void*)elm->key.data); 241 cached_user_unref(elm->user); 242 free(elm); 243 244 cache.count--; 245 } 246 247 int cached_user_verify_password(CachedUser *user, const char *password) { 248 if(!strcmp(user->password, password)) { 249 return 1; 250 } else { 251 return 0; 252 } 253 } 254 255 int cached_user_check_group(CachedUser *user, const char *group) { 256 cxstring grp = cx_str(group); 257 for(int i=0;i<user->numgroups;i++) { 258 if(!cx_strcmp(cx_strcast(user->groups[i]), grp)) { 259 return 1; 260 } 261 } 262 return 0; 263 } 264 265 void cached_user_unref(CachedUser *user) { 266 uint32_t ref = ws_atomic_dec32(&user->ref); 267 if(ref == 0) { 268 cached_user_delete(user); 269 } 270 } 271 272 void cached_user_delete(CachedUser *user) { 273 free(user->user.name); 274 free(user->authdb); 275 free(user->password); 276 free(user->groups); 277 free(user); 278 } 279 280 281 /* 282 * public API 283 * from public/auth.h 284 */ 285 286 User* authdb_get_user(AuthDB *db, Session *sn, Request *rq, const char *user) { 287 if(db->use_cache) { 288 User *u = auth_cache_get(db->name, user); 289 if(u) { 290 return u; 291 } 292 } 293 return db->get_user(db, sn, rq, user); 294 } 295 296 User* authdb_get_and_verify(AuthDB *db, Session *sn, Request *rq, const char *user, const char *password, int *pw) { 297 User *u = authdb_get_user(db, sn, rq, user); 298 if(u) { 299 if(u->verify_password(u, password)) { 300 if(db->use_cache) { 301 auth_cache_add(db->name, u, password, NULL, 0); 302 } 303 *pw = 1; 304 } else { 305 *pw = 0; 306 u->free(u); 307 u = NULL; 308 } 309 } 310 return u; 311 } 312