X-Git-Url: https://git.hcoop.net/hcoop/debian/courier-authlib.git/blobdiff_plain/940be80e3e40dbbbd84161e1e5ae3abf0b2eadf6..01037b081eab5fb3f208489dc3e052ec3a2c8ba1:/authsqlitelib.c diff --git a/authsqlitelib.c b/authsqlitelib.c new file mode 100644 index 0000000..fa00a03 --- /dev/null +++ b/authsqlitelib.c @@ -0,0 +1,637 @@ +/* +** Copyright 2012 Double Precision, Inc. See COPYING for +** distribution information. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "authsqlite.h" +#include "authsqliterc.h" +#include "auth.h" +#include "courierauthdebug.h" + +#define err courier_auth_err +#define GET(c) ((c) < (n) && columns[(c)] ? columns[(c)]:"") + +static const char *read_env(const char *env) +{ + return authgetconfig(AUTHSQLITERC, env); +} + +static sqlite3 *dbh=0; + +sqlite3 *do_connect() +{ + const char *p; + + if (dbh) + return dbh; + + p=read_env("SQLITE_DATABASE"); + + if (!p) + return 0; + + if (access(p, R_OK)) + return 0; + + if (sqlite3_open_v2(p, &dbh, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) + { + if (dbh) + { + err("sqllite3_open(%s): %s", p, sqlite3_errmsg(dbh)); + sqlite3_close(dbh); + dbh=0; + } + return 0; + } + + return dbh; +} + +void auth_sqlite_cleanup() +{ + if (dbh) + { + sqlite3_close(dbh); + dbh=0; + } +} + +static char *escape_str(const char *c, size_t n) +{ + char *p=malloc(n+1), *q; + + if (!p) + { + perror("malloc"); + return 0; + } + + memcpy(p, c, n); + p[n]=0; + + q=sqlite3_mprintf("%q", p); + free(p); + + p=strdup(q); + sqlite3_free(q); + if (!p) + { + perror("malloc"); + return 0; + } + return p; +} + +static struct authsqliteuserinfo ui={0, 0, 0, 0, 0, 0, 0, 0}; +static int ui_cnt; + +static void initui() +{ + + if (ui.username) + free(ui.username); + if (ui.cryptpw) + free(ui.cryptpw); + if (ui.clearpw) + free(ui.clearpw); + if (ui.home) + free(ui.home); + if (ui.maildir) + free(ui.maildir); + if (ui.quota) + free(ui.quota); + if (ui.fullname) + free(ui.fullname); + if (ui.options) + free(ui.options); + memset(&ui, 0, sizeof(ui)); +} + +static int select_callback(void *dummy, int n, char **columns, char **names) +{ + if (ui_cnt++) + { + err("Multiple rows returned"); + return -1; + } + + initui(); + + if (n < 6) + { + err("Query came back with fewer than 6 columns"); + return -1; + } + + if ((ui.username=strdup(GET(0))) == NULL + || (ui.cryptpw=strdup(GET(1))) == NULL + || (ui.clearpw=strdup(GET(2))) == NULL + || (ui.home=strdup(GET(5))) == NULL + || (ui.maildir=strdup(GET(6))) == NULL + || (ui.quota=strdup(GET(7))) == NULL + || (ui.fullname=strdup(GET(8))) == NULL + || (ui.options=strdup(GET(9))) == NULL) + { + initui(); + return 0; + } + + ui.uid=atoi(GET(3)); + ui.gid=atoi(GET(4)); + + return 0; +} + +struct authsqliteuserinfo *auth_sqlite_getuserinfo(const char *username, + const char *service) +{ + const char *defdomain =NULL; + char *querybuf; + char *errmsg; + const char *select_clause; + +#define DEFAULT_SELECT_QUERY "SELECT %s, %s, %s, %s, %s, %s, %s, %s, %s, %s FROM %s WHERE %s = '%s%s%s' %s%s%s", \ + login_field, crypt_field, clear_field, uid_field,\ + gid_field, home_field, maildir_field, quota_field,\ + name_field, options_field, user_table, login_field,\ + username_escaped,\ + has_domain || !*defdomain ? "":"@", has_domain ? "":defdomain, \ + *where_clause ? " AND (":"", where_clause,\ + *where_clause ? ")":"" + + initui(); + + if (!do_connect()) + return &ui; + + select_clause=read_env("SQLITE_SELECT_CLAUSE"); + defdomain=read_env("DEFAULT_DOMAIN"); + if (!defdomain) defdomain=""; + + if (!select_clause) /* siefca@pld.org.pl */ + { + const char *user_table, + *crypt_field, + *clear_field, + *name_field, + *uid_field, + *gid_field, + *login_field, + *home_field, + *maildir_field, + *quota_field, + *options_field, + *where_clause; + char *username_escaped; + size_t query_size; + char dummy_buf[1]; + int has_domain; + + user_table=read_env("SQLITE_USER_TABLE"); + + if (!user_table) + { + err("authsqlite: SQLITE_USER_TABLE not set in " + AUTHSQLITERC "."); + return (0); + } + + crypt_field=read_env("SQLITE_CRYPT_PWFIELD"); + clear_field=read_env("SQLITE_CLEAR_PWFIELD"); + name_field=read_env("SQLITE_NAME_FIELD"); + + if (!crypt_field && !clear_field) + { + err("authsqlite: SQLITE_CRYPT_PWFIELD and " + "SQLITE_CLEAR_PWFIELD not set in " AUTHSQLITERC "."); + return (0); + } + if (!crypt_field) crypt_field="\"\""; + if (!clear_field) clear_field="\"\""; + if (!name_field) name_field="\"\""; + + uid_field = read_env("SQLITE_UID_FIELD"); + if (!uid_field) uid_field = "uid"; + + gid_field = read_env("SQLITE_GID_FIELD"); + if (!gid_field) gid_field = "gid"; + + login_field = read_env("SQLITE_LOGIN_FIELD"); + if (!login_field) login_field = "id"; + + home_field = read_env("SQLITE_HOME_FIELD"); + if (!home_field) home_field = "home"; + + maildir_field=read_env(service && strcmp(service, "courier")==0 + ? "SQLITE_DEFAULTDELIVERY" + : "SQLITE_MAILDIR_FIELD"); + if (!maildir_field) maildir_field="\"\""; + + quota_field=read_env("SQLITE_QUOTA_FIELD"); + if (!quota_field) quota_field="\"\""; + + options_field=read_env("SQLITE_AUXOPTIONS_FIELD"); + if (!options_field) options_field="\"\""; + + where_clause=read_env("SQLITE_WHERE_CLAUSE"); + if (!where_clause) where_clause = ""; + + username_escaped=escape_str(username, strlen(username)); + + if (!username_escaped) + { + perror("malloc"); + return (0); + } + + has_domain=strchr(username, '@') != NULL; + + query_size=snprintf(dummy_buf, 1, DEFAULT_SELECT_QUERY); + + querybuf=malloc(query_size+1); + + if (!querybuf) + { + free(username_escaped); + perror("malloc"); + return(0); + } + + snprintf(querybuf, query_size+1, DEFAULT_SELECT_QUERY); + free(username_escaped); + + } + else + { + /* siefca@pld.org.pl */ + querybuf=auth_parse_select_clause (escape_str, + select_clause, username, + defdomain, service); + if (!querybuf) + { + DPRINTF("parse_select_clause failed (DEFAULT_DOMAIN not set?)"); + return 0; + } + } + + DPRINTF("SQL query: %s", querybuf); + errmsg=0; + ui_cnt=0; + if (sqlite3_exec(dbh, querybuf, select_callback, + NULL, &errmsg) != SQLITE_OK) + { + if (errmsg) + { + err(errmsg); + sqlite3_free(errmsg); + } + + free(querybuf); + return 0; + } + + free(querybuf); + if (errmsg) + { + err(errmsg); + sqlite3_free(errmsg); + } + + return &ui; +} + +static int dummy_callback(void *dummy, int n, char **columns, char **names) +{ + return 0; +} + +int auth_sqlite_setpass(const char *user, const char *pass, + const char *oldpass) +{ + char *newpass_crypt; + char *sql_buf; + int rc=0; + char *errmsg; + char *clear_escaped; + char *crypt_escaped; + + const char *clear_field =NULL, + *crypt_field =NULL, + *defdomain =NULL, + *where_clause =NULL, + *user_table =NULL, + *login_field =NULL, + *chpass_clause =NULL; /* siefca@pld.org.pl */ + + if (!do_connect()) return (-1); + + if (!(newpass_crypt=authcryptpasswd(pass, oldpass))) + return (-1); + + clear_escaped=escape_str(pass, strlen(pass)); + + if (!clear_escaped) + { + perror("malloc"); + free(newpass_crypt); + return -1; + } + + crypt_escaped=escape_str(newpass_crypt, strlen(newpass_crypt)); + + if (!crypt_escaped) + { + perror("malloc"); + free(clear_escaped); + free(newpass_crypt); + return -1; + } + + /* siefca@pld.org.pl */ + chpass_clause=read_env("SQLITE_CHPASS_CLAUSE"); + defdomain=read_env("DEFAULT_DOMAIN"); + user_table=read_env("SQLITE_USER_TABLE"); + if (!chpass_clause) + { + int has_domain=strchr(user, '@') != NULL; + char *username_escaped; + char dummy_buf[1]; + size_t sql_buf_size; + + username_escaped=escape_str(user, strlen(user)); + + if (!username_escaped) + { + perror("malloc"); + free(clear_escaped); + free(crypt_escaped); + free(newpass_crypt); + return -1; + } + + login_field = read_env("SQLITE_LOGIN_FIELD"); + if (!login_field) login_field = "id"; + crypt_field=read_env("SQLITE_CRYPT_PWFIELD"); + clear_field=read_env("SQLITE_CLEAR_PWFIELD"); + where_clause=read_env("SQLITE_WHERE_CLAUSE"); + + if (!where_clause) + where_clause=""; + + if (!crypt_field) + crypt_field=""; + + if (!clear_field) + clear_field=""; + + if (!defdomain) + defdomain=""; + +#define DEFAULT_SETPASS_UPDATE \ + "UPDATE %s SET %s%s%s%s %s %s%s%s%s WHERE %s='%s%s%s' %s%s%s", \ + user_table, \ + *clear_field ? clear_field:"", \ + *clear_field ? "='":"", \ + *clear_field ? clear_escaped:"", \ + *clear_field ? "'":"", \ + \ + *clear_field && *crypt_field ? ",":"", \ + \ + *crypt_field ? crypt_field:"", \ + *crypt_field ? "='":"", \ + *crypt_field ? crypt_escaped:"", \ + *crypt_field ? "'":"", \ + login_field, \ + username_escaped, \ + has_domain || !*defdomain ? "":"@", \ + has_domain ? "":defdomain, \ + *where_clause ? " AND (":"", where_clause, \ + *where_clause ? ")":"" + + + sql_buf_size=snprintf(dummy_buf, 1, DEFAULT_SETPASS_UPDATE); + + sql_buf=malloc(sql_buf_size+1); + + if (sql_buf) + snprintf(sql_buf, sql_buf_size+1, + DEFAULT_SETPASS_UPDATE); + + free(username_escaped); + } + else + { + sql_buf=auth_parse_chpass_clause(escape_str, chpass_clause, + user, + defdomain, + clear_escaped, + crypt_escaped); + } + + free(clear_escaped); + free(crypt_escaped); + free(newpass_crypt); + + if (courier_authdebug_login_level >= 2) + { + DPRINTF("setpass SQL: %s", sql_buf); + } + + errmsg=NULL; + + if (sqlite3_exec(dbh, sql_buf, dummy_callback, + NULL, &errmsg) != SQLITE_OK) + { + rc=-1; + } + else + { + if (sqlite3_changes(dbh) > 0) + { + DPRINTF("authsqllite: password updated"); + } + else + { + rc=-1; + DPRINTF("authsqllite: password not updated"); + } + } + + if (errmsg) + { + err(errmsg); + sqlite3_free(errmsg); + } + + free(sql_buf); + return (rc); +} + +struct enumerate_user_cb { + + void (*cb_func)(const char *name, + uid_t uid, + gid_t gid, + const char *homedir, + const char *maildir, + const char *options, + void *void_arg); + void *void_arg; +}; + +static int enumerate_callback(void *closure, + int n, char **columns, char **names) +{ + struct enumerate_user_cb *cb=(struct enumerate_user_cb *)closure; + + const char *username; + uid_t uid; + gid_t gid; + const char *homedir; + const char *maildir; + const char *options; + + username=GET(0); + uid=atol(GET(1)); /* FIXME use strtol to validate */ + gid=atol(GET(2)); + homedir=GET(3); + maildir=GET(4); + options=GET(5); + + if (maildir && !*maildir) + maildir=NULL; + + if (options && !*options) + options=NULL; + + (*cb->cb_func)(username, uid, gid, homedir, + maildir, options, cb->void_arg); + return 0; +} + +void auth_sqlite_enumerate( void(*cb_func)(const char *name, + uid_t uid, + gid_t gid, + const char *homedir, + const char *maildir, + const char *options, + void *void_arg), + void *void_arg) +{ + const char *defdomain, *select_clause; + char *querybuf; + char *errmsg; + struct enumerate_user_cb cb; + + cb.cb_func=cb_func; + cb.void_arg=void_arg; + + if (!do_connect()) return; + + initui(); + + select_clause=read_env("SQLITE_ENUMERATE_CLAUSE"); + defdomain=read_env("DEFAULT_DOMAIN"); + if (!defdomain || !defdomain[0]) + defdomain="*"; /* otherwise parse_select_clause fails */ + + if (!select_clause) + { + const char *user_table, + *uid_field, + *gid_field, + *login_field, + *home_field, + *maildir_field, + *options_field, + *where_clause; + char dummy_buf[1]; + size_t query_len; + + user_table=read_env("SQLITE_USER_TABLE"); + + if (!user_table) + { + err("authsqlite: SQLITE_USER_TABLE not set in " + AUTHSQLITERC "."); + return; + } + + uid_field = read_env("SQLITE_UID_FIELD"); + if (!uid_field) uid_field = "uid"; + + gid_field = read_env("SQLITE_GID_FIELD"); + if (!gid_field) gid_field = "gid"; + + login_field = read_env("SQLITE_LOGIN_FIELD"); + if (!login_field) login_field = "id"; + + home_field = read_env("SQLITE_HOME_FIELD"); + if (!home_field) home_field = "home"; + + maildir_field=read_env("SQLITE_MAILDIR_FIELD"); + if (!maildir_field) maildir_field="\"\""; + + options_field=read_env("SQLITE_AUXOPTIONS_FIELD"); + if (!options_field) options_field="\"\""; + + where_clause=read_env("SQLITE_WHERE_CLAUSE"); + if (!where_clause) where_clause = ""; + + +#define DEFAULT_ENUMERATE_QUERY \ + "SELECT %s, %s, %s, %s, %s, %s FROM %s %s%s",\ + login_field, uid_field, gid_field, \ + home_field, maildir_field, \ + options_field, user_table, \ + *where_clause ? " WHERE ":"", \ + where_clause + + query_len=snprintf(dummy_buf, 1, DEFAULT_ENUMERATE_QUERY); + + querybuf=malloc(query_len+1); + + if (!querybuf) + { + perror("malloc"); + return; + } + + snprintf(querybuf, query_len+1, DEFAULT_ENUMERATE_QUERY); + } + else + { + /* siefca@pld.org.pl */ + querybuf=auth_parse_select_clause (escape_str, + select_clause, "*", + defdomain, "enumerate"); + if (!querybuf) + { + DPRINTF("authsqlite: parse_select_clause failed"); + return; + } + } + DPRINTF("authsqlite: enumerate query: %s", querybuf); + + errmsg=NULL; + + sqlite3_exec(dbh, querybuf, enumerate_callback, &cb, &errmsg); + + if (errmsg) + { + err(errmsg); + sqlite3_free(errmsg); + } + free(querybuf); + + (*cb.cb_func)(NULL, 0, 0, NULL, NULL, NULL, cb.void_arg); +}