--- /dev/null
+/*
+** Copyright 2012 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#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);
+}