diff -r 14722c5f8856 -r 74babc0082b7 src/server/daemon/auth.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/daemon/auth.c Sun May 26 12:12:07 2013 +0200 @@ -0,0 +1,329 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "../ucx/map.h" +#include "../util/atomic.h" +#include "auth.h" + +static pthread_mutex_t auth_cache_mutex = PTHREAD_MUTEX_INITIALIZER; +static UserCache cache; + +void auth_cache_init() { + printf("auth_cache_init\n"); + // TODO: config parameters + //pthread_mutex_init(&auth_cache_mutex, NULL); + cache.map = calloc(64, sizeof(UserCacheElm)); + cache.size = 96; + cache.count = 0; + cache.max_users = 64; + cache.head = NULL; + cache.trail = NULL; +} + +User* auth_cache_get(char *authdb, char *user) { + //printf("auth_cache_get: %s\n", user); + /* + * create the key to access the map + * key: authdb\0user + */ + size_t authdblen = strlen(authdb); + size_t userlen = strlen(user); + + size_t keylen = authdblen + userlen + 1; + char *key = malloc(keylen); + memcpy(key, authdb, authdblen); + key[authdblen] = 0; + memcpy(key + authdblen + 1, user, userlen); + + UcxKey mapkey = ucx_key(key, keylen); + + // get cached user from map + time_t now = time(NULL); + size_t slot = mapkey.hash%cache.size; + + pthread_mutex_lock(&auth_cache_mutex); + + UserCacheElm *elm = cache.map[slot]; + while(elm && elm->key.hash != mapkey.hash) { + elm = elm->next_elm; + } + // if we have an elm, the hash is correct + if(elm) { + // compare the key data to be sure it is the correct user + int n = (mapkey.len > elm->key.len) ? elm->key.len : mapkey.len; + if (memcmp(elm->key.data, mapkey.data, n)) { + free(key); + pthread_mutex_unlock(&auth_cache_mutex); + return NULL; + } + } else { + free(key); + pthread_mutex_unlock(&auth_cache_mutex); + return NULL; + } + + // elm is now the correct UserCacheElm + // TODO: use configuration for expire time + if(now - elm->created > 120) { + // cached user expired + // remove all users from the list from the first to this one + UserCacheElm *e = cache.head; + while(e) { + if(e == elm) { + break; + } + UserCacheElm *n = e->next_user; + auth_cache_remove_from_map(e); + e = n; + } + cache.head = elm->next_user; + if(cache.trail == elm) { + cache.trail = NULL; + } + auth_cache_remove_from_map(elm); + free(key); + pthread_mutex_unlock(&auth_cache_mutex); + return NULL; + } + + pthread_mutex_unlock(&auth_cache_mutex); + free(key); + return (User*)elm->user; +} + +void auth_cache_add( + char *authdb, + User *user, + char *password, + char **groups, + size_t numgroups) +{ + //printf("auth_cache_add: %s\n", user->name); + /* + * this function does not check, if the user is already in the map + * use it only after auth_cache_get + */ + + CachedUser *cusr = malloc(sizeof(CachedUser)); + cusr->user.name = strdup(user->name); + cusr->user.uid = user->uid; + cusr->user.gid = user->gid; + cusr->user.verify_password = + (user_verify_passwd_f)cached_user_verify_password; + cusr->user.check_group = (user_check_group_f)cached_user_check_group; + cusr->user.free = (user_free_f)cached_user_unref; + + cusr->authdb = strdup(authdb); + cusr->password = strdup(password); + cusr->groups = calloc(numgroups, sizeof(sstr_t)); + cusr->numgroups = numgroups; + for(int i=0;igroups[i] = sstrdup(sstr(groups[i])); + } + cusr->ref = 1; + + /* + * add the user to the auth cache + * the auth cache is a list of all cached users + */ + + // create list element + time_t now = time(NULL); + UserCacheElm *elm = malloc(sizeof(UserCacheElm)); + elm->user = cusr; + elm->created = now; + elm->next_elm = NULL; + elm->next_user = NULL; + + // create map key + size_t authdblen = strlen(authdb); + size_t userlen = strlen(user->name); + size_t keylen = authdblen + userlen + 1; + char *key = malloc(keylen); + memcpy(key, authdb, authdblen); + key[authdblen] = 0; + memcpy(key + authdblen + 1, user->name, userlen); + UcxKey mapkey = ucx_key(key, keylen); + + elm->key = mapkey; + elm->slot = mapkey.hash%cache.size; + + // add user to list and map + pthread_mutex_lock(&auth_cache_mutex); + + // remove the first cached user if expired or the cache is full + if(cache.head && + (cache.count >= cache.max_users || now-cache.head->created > 120)) + { + UserCacheElm *first = cache.head; + cache.head = first->next_user; + if(!cache.head) { + cache.trail = NULL; + } + auth_cache_remove_from_map(first); + } + + // add to map + UserCacheElm *prevelm = cache.map[elm->slot]; + if(prevelm) { + for(;;) { + if(!prevelm->next_elm) { + break; + } + prevelm = prevelm->next_elm; + } + } + if(prevelm) { + prevelm->next_elm = elm; + } else { + cache.map[elm->slot] = elm; + } + + // add to list + if(cache.head) { + cache.trail->next_user = elm; + cache.trail = elm; + } else { + cache.head = elm; + cache.trail = elm; + } + + cache.count++; + + pthread_mutex_unlock(&auth_cache_mutex); +} + +void auth_cache_remove_from_map(UserCacheElm *elm) { + UserCacheElm *prevelm = NULL; + UserCacheElm *e = cache.map[elm->slot]; + while(e) { + if(e == elm) { + break; + } else { + prevelm = e; + } + e = e->next_elm; + } + if(prevelm) { + prevelm->next_elm = elm->next_elm; + } else { + cache.map[elm->slot] = elm->next_elm; + } + + free(elm->key.data); + cached_user_unref(elm->user); + free(elm); + + cache.count--; +} + +int cached_user_verify_password(CachedUser *user, char *password) { + if(!strcmp(user->password, password)) { + return 1; + } else { + return 0; + } +} + +int cached_user_check_group(CachedUser *user, char *group) { + sstr_t grp = sstr(group); + for(int i=0;inumgroups;i++) { + if(!sstrcmp(user->groups[i], grp)) { + return 1; + } + } + return 0; +} + +void cached_user_unref(CachedUser *user) { + uint32_t ref = ws_atomic_dec32(&user->ref); + if(ref == 0) { + cached_user_delete(user); + } +} + +void cached_user_delete(CachedUser *user) { + free(user->user.name); + free(user->authdb); + free(user->password); + free(user->groups); + free(user); +} + + +/* + * public API + * from public/auth.h + */ + +User* authdb_get_user(AuthDB *db, char *user) { + if(db->use_cache) { + User *u = auth_cache_get(db->name, user); + if(u) { + return u; + } + } + return db->get_user(db, user); +} + +User* authdb_get_and_verify(AuthDB *db, char *user, char *password, int *pw) { + User *u = NULL; + // try getting the user from the cache + if(db->use_cache) { + u = auth_cache_get(db->name, user); + if(u) { + if(u->verify_password(u, password)) { + *pw = 1; + } else { + *pw = 0; + u->free(u); + u = NULL; + } + return u; + } + } + // user not cached + u = db->get_user(db, user); + if(u) { + if(u->verify_password(u, password)) { + if(db->use_cache) { + auth_cache_add(db->name, u, password, NULL, 0); + } + *pw = 1; + } else { + *pw = 0; + u->free(u); + u = NULL; + } + } + return u; +}