#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <cx/map.h>
#include "../public/nsapi.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() {
log_ereport(
LOG_VERBOSE,
"auth_cache_init");
cache.map = calloc(
80,
sizeof(UserCacheElm));
cache.size =
80;
cache.count =
0;
cache.max_users =
64;
cache.head =
NULL;
cache.trail =
NULL;
}
User* auth_cache_get(
char *authdb,
const char *user) {
size_t authdblen = strlen(authdb);
size_t userlen = strlen(user);
size_t keylen = authdblen + userlen +
1;
unsigned char *key = malloc(keylen);
memcpy(key, authdb, authdblen);
key[authdblen] =
0;
memcpy(key + authdblen +
1, user, userlen);
CxHashKey mapkey = cx_hash_key_bytes(key, keylen);
time_t now = time(
NULL);
size_t slot = mapkey.hash%cache.size;
User *u =
NULL;
pthread_mutex_lock(&auth_cache_mutex);
UserCacheElm *elm = cache.map[slot];
while(elm && elm->key.hash != mapkey.hash) {
elm = elm->next_elm;
}
if(elm) {
int n = (mapkey.len > elm->key.len) ? elm->key.len : mapkey.len;
if (!memcmp(elm->key.data, mapkey.data, n)) {
if(now - elm->created >
120) {
UserCacheElm *e = cache.head;
while(e) {
if(e == elm) {
break;
}
UserCacheElm *nu = e->next_user;
auth_cache_remove_from_map(e);
e = nu;
}
cache.head = elm->next_user;
if(cache.trail == elm) {
cache.trail =
NULL;
}
auth_cache_remove_from_map(elm);
u =
NULL;
}
else {
u = (User*)elm->user;
}
}
}
pthread_mutex_unlock(&auth_cache_mutex);
free(key);
return u;
}
void auth_cache_add(
char *authdb,
User *user,
const char *password,
const char **groups,
size_t numgroups)
{
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 = numgroups ? calloc(numgroups,
sizeof(cxmutstr)) :
NULL;
cusr->numgroups = numgroups;
for(
int i=
0;i<numgroups;i++) {
cusr->groups[i] = cx_strdup(cx_str(groups[i]));
}
cusr->ref =
1;
time_t now = time(
NULL);
UserCacheElm *elm = malloc(
sizeof(UserCacheElm));
elm->user = cusr;
elm->created = now;
elm->next_elm =
NULL;
elm->next_user =
NULL;
size_t authdblen = strlen(authdb);
size_t userlen = strlen(user->name);
size_t keylen = authdblen + userlen +
1;
unsigned char *key = malloc(keylen);
memcpy(key, authdb, authdblen);
key[authdblen] =
0;
memcpy(key + authdblen +
1, user->name, userlen);
CxHashKey mapkey = cx_hash_key_bytes(key, keylen);
elm->key.data = key;
elm->key.len = mapkey.len;
elm->key.hash = mapkey.hash;
elm->slot = mapkey.hash%cache.size;
pthread_mutex_lock(&auth_cache_mutex);
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);
}
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;
}
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((
void*)elm->key.data);
cached_user_unref(elm->user);
free(elm);
cache.count--;
}
int cached_user_verify_password(CachedUser *user,
const char *password) {
if(!strcmp(user->password, password)) {
return 1;
}
else {
return 0;
}
}
int cached_user_check_group(CachedUser *user,
const char *group) {
cxstring grp = cx_str(group);
for(
int i=
0;i<user->numgroups;i++) {
if(!cx_strcmp(cx_strcast(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);
}
User* authdb_get_user(AuthDB *db, Session *sn, Request *rq,
const char *user) {
if(db->use_cache) {
User *u = auth_cache_get(db->name, user);
if(u) {
return u;
}
}
return db->get_user(db, sn, rq, user);
}
User* authdb_get_and_verify(AuthDB *db, Session *sn, Request *rq,
const char *user,
const char *password,
int *pw) {
User *u = authdb_get_user(db, sn, rq, 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;
}