Imported Debian patch 0.66.1-1
[hcoop/debian/courier-authlib.git] / authsqlitelib.c
diff --git a/authsqlitelib.c b/authsqlitelib.c
new file mode 100644 (file)
index 0000000..fa00a03
--- /dev/null
@@ -0,0 +1,637 @@
+/*
+** 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);
+}