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 <ucx/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, 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 char *key = malloc(keylen); 66 memcpy(key, authdb, authdblen); 67 key[authdblen] = 0; 68 memcpy(key + authdblen + 1, user, userlen); 69 70 UcxKey mapkey = ucx_key(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 char *password, 123 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(sstr_t)) : NULL; 144 cusr->numgroups = numgroups; 145 for(int i=0;i<numgroups;i++) { 146 cusr->groups[i] = sstrdup(sstr(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 char *key = malloc(keylen); 168 memcpy(key, authdb, authdblen); 169 key[authdblen] = 0; 170 memcpy(key + authdblen + 1, user->name, userlen); 171 UcxKey mapkey = ucx_key(key, keylen); 172 173 elm->key = mapkey; 174 elm->slot = mapkey.hash%cache.size; 175 176 // add user to list and map 177 pthread_mutex_lock(&auth_cache_mutex); 178 179 // remove the first cached user if expired or the cache is full 180 if(cache.head && 181 (cache.count >= cache.max_users || now-cache.head->created > 120)) 182 { 183 UserCacheElm *first = cache.head; 184 cache.head = first->next_user; 185 if(!cache.head) { 186 cache.trail = NULL; 187 } 188 auth_cache_remove_from_map(first); 189 } 190 191 // add to map 192 UserCacheElm *prevelm = cache.map[elm->slot]; 193 if(prevelm) { 194 for(;;) { 195 if(!prevelm->next_elm) { 196 break; 197 } 198 prevelm = prevelm->next_elm; 199 } 200 } 201 if(prevelm) { 202 prevelm->next_elm = elm; 203 } else { 204 cache.map[elm->slot] = elm; 205 } 206 207 // add to list 208 if(cache.head) { 209 cache.trail->next_user = elm; 210 cache.trail = elm; 211 } else { 212 cache.head = elm; 213 cache.trail = elm; 214 } 215 216 cache.count++; 217 218 pthread_mutex_unlock(&auth_cache_mutex); 219 } 220 221 void auth_cache_remove_from_map(UserCacheElm *elm) { 222 UserCacheElm *prevelm = NULL; 223 UserCacheElm *e = cache.map[elm->slot]; 224 while(e) { 225 if(e == elm) { 226 break; 227 } else { 228 prevelm = e; 229 } 230 e = e->next_elm; 231 } 232 if(prevelm) { 233 prevelm->next_elm = elm->next_elm; 234 } else { 235 cache.map[elm->slot] = elm->next_elm; 236 } 237 238 free(elm->key.data); 239 cached_user_unref(elm->user); 240 free(elm); 241 242 cache.count--; 243 } 244 245 int cached_user_verify_password(CachedUser *user, char *password) { 246 if(!strcmp(user->password, password)) { 247 return 1; 248 } else { 249 return 0; 250 } 251 } 252 253 int cached_user_check_group(CachedUser *user, char *group) { 254 sstr_t grp = sstr(group); 255 for(int i=0;i<user->numgroups;i++) { 256 if(!sstrcmp(user->groups[i], grp)) { 257 return 1; 258 } 259 } 260 return 0; 261 } 262 263 void cached_user_unref(CachedUser *user) { 264 uint32_t ref = ws_atomic_dec32(&user->ref); 265 if(ref == 0) { 266 cached_user_delete(user); 267 } 268 } 269 270 void cached_user_delete(CachedUser *user) { 271 free(user->user.name); 272 free(user->authdb); 273 free(user->password); 274 free(user->groups); 275 free(user); 276 } 277 278 279 /* 280 * public API 281 * from public/auth.h 282 */ 283 284 User* authdb_get_user(AuthDB *db, char *user) { 285 if(db->use_cache) { 286 User *u = auth_cache_get(db->name, user); 287 if(u) { 288 return u; 289 } 290 } 291 return db->get_user(db, user); 292 } 293 294 User* authdb_get_and_verify(AuthDB *db, char *user, char *password, int *pw) { 295 User *u = NULL; 296 // try getting the user from the cache 297 if(db->use_cache) { 298 u = auth_cache_get(db->name, user); 299 if(u) { 300 if(u->verify_password(u, password)) { 301 *pw = 1; 302 } else { 303 *pw = 0; 304 u->free(u); 305 u = NULL; 306 } 307 return u; 308 } 309 } 310 // user not cached 311 u = db->get_user(db, user); 312 if(u) { 313 if(u->verify_password(u, password)) { 314 if(db->use_cache) { 315 auth_cache_add(db->name, u, password, NULL, 0); 316 } 317 *pw = 1; 318 } else { 319 *pw = 0; 320 u->free(u); 321 u = NULL; 322 } 323 } 324 return u; 325 } 326