#include "c2s.h"
#ifdef STORAGE_DB
#include <db.h>
typedef struct creds_st
{
char username[257];
char realm[257];
char password[257];
char token[11];
int sequence;
char hash[41];
} *creds_t;
typedef struct moddata_st
{
DB_ENV *env;
char *path;
int sync;
xht realms;
DB *def_realm;
} *moddata_t;
static DB *_ar_db_get_realm_db(authreg_t ar, char *realm)
{
moddata_t data = (moddata_t) ar->private;
DB *db;
int err;
if(realm[0] == '\0')
db = data->def_realm;
else
db = xhash_get(data->realms, realm);
if(db != NULL)
return db;
log_debug(ZONE, "creating new db handle for realm '%s'", realm);
err = db_create(&db, data->env, 0);
if(err != 0)
{
log_write(ar->c2s->log, LOG_ERR, "db: couldn't create db: %s", db_strerror(err));
return NULL;
}
err = db->open(db, NULL, "authreg.db", realm, DB_HASH, DB_CREATE, 0);
if(err != 0)
{
log_write(ar->c2s->log, LOG_ERR, "db: couldn't open db for realm '%s': %s", realm, db_strerror(err));
db->close(db, 0);
return NULL;
}
if(realm[0] == '\0')
data->def_realm = db;
else
xhash_put(data->realms, pstrdup(xhash_pool(data->realms), realm), (void *) db);
log_debug(ZONE, "db for realm '%s' is online", realm);
return db;
}
static creds_t _ar_db_fetch_user(authreg_t ar, char *username, char *realm)
{
DB *db;
DBT key, val;
int err;
creds_t creds;
log_debug(ZONE, "fetching auth creds for user '%s' realm '%s'", username, realm);
db = _ar_db_get_realm_db(ar, realm);
if(db == NULL)
return NULL;
memset(&key, 0, sizeof(DBT));
memset(&val, 0, sizeof(DBT));
key.data = username;
key.size = strlen(username);
err = db->get(db, NULL, &key, &val, 0);
if(err == 0)
creds = (creds_t) val.data;
else if(err == DB_NOTFOUND)
creds = NULL;
else
{
log_write(ar->c2s->log, LOG_ERR, "db: couldn't fetch auth creds for user '%s' (realm '%s'): %s", username, realm, db_strerror(err));
return NULL;
}
log_debug(ZONE, "auth creds: %X", creds);
return creds;
}
static int _ar_db_store_user(authreg_t ar, creds_t creds)
{
moddata_t data = (moddata_t) ar->private;
DB *db;
DBT key, val;
int err;
log_debug(ZONE, "storing auth creds for user '%s' realm '%s'", creds->username, creds->realm);
db = _ar_db_get_realm_db(ar, creds->realm);
if(db == NULL)
return 1;
memset(&key, 0, sizeof(DBT));
memset(&val, 0, sizeof(DBT));
key.data = creds->username;
key.size = strlen(creds->username);
val.data = creds;
val.size = sizeof(struct creds_st);
err = db->put(db, NULL, &key, &val, 0);
if(err != 0)
{
log_write(ar->c2s->log, LOG_ERR, "db: couldn't store auth creds for user '%s' (realm '%s'): %s", creds->username, creds->realm, db_strerror(err));
return 1;
}
if(data->sync)
db->sync(db, 0);
return 0;
}
static int _ar_db_user_exists(authreg_t ar, char *username, char *realm)
{
return (int) _ar_db_fetch_user(ar, username, realm);
}
static int _ar_db_get_password(authreg_t ar, char *username, char *realm, char password[257])
{
creds_t creds;
if((creds = _ar_db_fetch_user(ar, username, realm)) == NULL)
return 1;
strcpy(password, creds->password);
return 0;
}
static int _ar_db_set_password(authreg_t ar, char *username, char *realm, char password[257])
{
creds_t creds;
if((creds = _ar_db_fetch_user(ar, username, realm)) == NULL)
return 1;
strcpy(creds->password, password);
if(_ar_db_store_user(ar, creds) != 0)
return 1;
return 0;
}
static int _ar_db_get_zerok(authreg_t ar, char *username, char *realm, char hash[41], char token[11], int *sequence)
{
creds_t creds;
if((creds = _ar_db_fetch_user(ar, username, realm)) == NULL)
return 1;
strcpy(hash, creds->hash);
strcpy(token, creds->token);
*sequence = creds->sequence;
return 0;
}
static int _ar_db_set_zerok(authreg_t ar, char *username, char *realm, char hash[41], char token[11], int sequence)
{
creds_t creds;
if((creds = _ar_db_fetch_user(ar, username, realm)) == NULL)
return 1;
strcpy(creds->hash, hash);
strcpy(creds->token, token);
creds->sequence = sequence;
if(_ar_db_store_user(ar, creds) != 0)
return 1;
return 0;
}
static int _ar_db_create_user(authreg_t ar, char *username, char *realm)
{
creds_t creds;
int ret;
if((creds = _ar_db_fetch_user(ar, username, realm)) != NULL)
return 1;
creds = (creds_t) malloc(sizeof(struct creds_st));
memset(creds, 0, sizeof(struct creds_st));
strcpy(creds->username, username);
strcpy(creds->realm, realm);
ret = _ar_db_store_user(ar, creds);
free(creds);
return ret;
}
static int _ar_db_delete_user(authreg_t ar, char *username, char *realm)
{
DB *db;
DBT key;
int err;
if(_ar_db_fetch_user(ar, username, realm) == NULL)
return 1;
db = _ar_db_get_realm_db(ar, realm);
if(db == NULL)
return 1;
memset(&key, 0, sizeof(DBT));
key.data = username;
key.size = strlen(username);
err = db->del(db, NULL, &key, 0);
if(err != 0)
log_write(ar->c2s->log, LOG_ERR, "db: couldn't delete auth creds for user '%s' (realm '%s'): %s", username, realm, db_strerror(err));
return err;
}
static void _ar_db_free_walker(xht realms, const char *realm, void *val, void *arg)
{
DB *db = (DB *) val;
log_debug(ZONE, "closing '%s' db", realm);
db->close(db, 0);
}
static void _ar_db_free(authreg_t ar)
{
DB_ENV *env;
moddata_t data = (moddata_t) ar->private;
log_debug(ZONE, "db module shutting down");
xhash_walk(data->realms, _ar_db_free_walker, NULL);
xhash_free(data->realms);
data->env->close(data->env, 0);
if (db_env_create(&env, 0) == 0)
env->remove(env, data->path, 0);
free(data);
}
static void _ar_db_panic(DB_ENV *env, int errval)
{
log_t log = (log_t) env->app_private;
log_write(log, LOG_CRIT, "db: corruption detected! close all jabberd processes and run db_recover");
exit(2);
}
int ar_db_init(authreg_t ar)
{
char *path;
int err;
DB_ENV *env;
moddata_t data;
path = config_get_one(ar->c2s->config, "authreg.db.path", 0);
if(path == NULL)
{
log_write(ar->c2s->log, LOG_ERR, "db: no authreg path specified in config file");
return 1;
}
err = db_env_create(&env, 0);
if(err != 0)
{
log_write(ar->c2s->log, LOG_ERR, "db: couldn't create environment: %s", db_strerror(err));
return 1;
}
err = env->set_paniccall(env, _ar_db_panic);
if(err != 0)
{
log_write(ar->c2s->log, LOG_ERR, "db: couldn't set panic call: %s", db_strerror(err));
return 1;
}
env->app_private = ar->c2s->log;
err = env->set_flags(env, DB_AUTO_COMMIT, 1);
if(err != 0)
{
log_write(ar->c2s->log, LOG_ERR, "db: couldn't set environment for automatic transaction commit: %s", db_strerror(err));
env->close(env, 0);
return 1;
}
err = env->open(env, path, DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_TXN | DB_CREATE, 0);
if(err != 0)
{
log_write(ar->c2s->log, LOG_ERR, "db: couldn't open environment: %s", db_strerror(err));
env->close(env, 0);
return 1;
}
data = (moddata_t) malloc(sizeof(struct moddata_st));
memset(data, 0, sizeof(struct moddata_st));
data->env = env;
data->path = path;
if(config_get_one(ar->c2s->config, "authreg.db.sync", 0) != NULL)
data->sync = 1;
data->realms = xhash_new(51);
ar->private = data;
ar->user_exists = _ar_db_user_exists;
ar->get_password = _ar_db_get_password;
ar->set_password = _ar_db_set_password;
ar->get_zerok = _ar_db_get_zerok;
ar->set_zerok = _ar_db_set_zerok;
ar->create_user = _ar_db_create_user;
ar->delete_user = _ar_db_delete_user;
ar->free = _ar_db_free;
return 0;
}
#endif