src/server/daemon/auth.c

changeset 66
74babc0082b7
parent 59
ab25c0a231d0
child 67
50505dc3f8a6
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#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;i<numgroups;i++) {
+        cusr->groups[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;i<user->numgroups;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;
+}

mercurial