2 ** Copyright 2016 Double Precision, Inc. See COPYING for
3 ** distribution information.
11 #include <sys/types.h>
16 #include "authsqliterc.h"
18 #include "courierauthdebug.h"
23 #include "authsqlite.h"
24 #include "authconfigfile.h"
26 #define err courier_auth_err
28 #define GET(c) ((c) < (n) && columns[(c)] ? columns[(c)]:"")
30 class authsqlite_connection
{
35 class authsqliterc_vars
{
38 std::string select_clause
, defdomain
;
39 std::string user_table
, crypt_field
, clear_field
,
40 name_field
, uid_field
, gid_field
, login_field
,
41 home_field
, maildir_field
, defaultdelivery_field
,
42 quota_field
, options_field
,
43 where_clause
, chpass_clause
, enumerate_clause
;
47 class authsqliterc_file
: public courier::auth::config_file
,
48 public authsqliterc_vars
{
50 authsqlite_connection
&conn
;
54 authsqliterc_file
&operator=(const authsqliterc_file
&o
);
55 authsqliterc_file(authsqlite_connection
&connArg
);
62 authsqliterc_file config_file
;
64 authsqlite_connection() : dbh(NULL
), config_file(*this)
68 ~authsqlite_connection()
82 std::string
escape(const std::string
&s
);
84 sqlite3
*do_connect();
86 static authsqlite_connection
*singleton
;
88 static authsqlite_connection
*connect();
90 bool getuserinfo(const char *username
,
92 authsqliteuserinfo
&ui
);
94 void enumerate( void(*cb_func
)(const char *name
,
103 bool setpass(const char *user
, const char *pass
,
104 const char *oldpass
);
108 authsqlite_connection::authsqliterc_file
109 &authsqlite_connection::authsqliterc_file
110 ::operator=(const authsqlite_connection::authsqliterc_file
&o
)
112 courier::auth::config_file::operator=(o
);
113 authsqliterc_vars::operator=(o
);
117 authsqlite_connection::authsqliterc_file
118 ::authsqliterc_file(authsqlite_connection
&connArg
)
119 : courier::auth::config_file(AUTHSQLITERC
),
124 authsqlite_connection::authsqliterc_file::~authsqliterc_file()
128 bool authsqlite_connection::authsqliterc_file::do_load()
130 if (!config("SQLITE_DATABASE", database
, true))
133 defdomain
=config("DEFAULT_DOMAIN");
135 select_clause
=config("SQLITE_SELECT_CLAUSE");
136 chpass_clause
=config("SQLITE_CHPASS_CLAUSE");
137 enumerate_clause
=config("SQLITE_ENUMERATE_CLAUSE");
139 if (select_clause
.empty() || chpass_clause
.empty() ||
140 enumerate_clause
.empty())
142 if (!config("SQLITE_USER_TABLE", user_table
,
146 crypt_field
=config("SQLITE_CRYPT_PWFIELD", "''");
148 clear_field
=config("SQLITE_CLEAR_PWFIELD", "''");
150 if (crypt_field
+ clear_field
== "''''")
152 err("SQLITE_CLEAR_PWFIELD and SQLITE_CLEAR_FIELD not set in " AUTHSQLITERC
);
156 name_field
=config("SQLITE_NAME_FIELD", "''");
158 uid_field
=config("SQLITE_UID_FIELD", "uid");
159 gid_field
=config("SQLITE_GID_FIELD", "gid");
160 login_field
=config("SQLITE_LOGIN_FIELD", "id");
161 home_field
=config("SQLITE_HOME_FIELD", "home");
163 maildir_field
=config("SQLITE_MAILDIR_FIELD", "''");
164 defaultdelivery_field
=config("SQLITE_DEFAULTDELIVERY", "''");
166 quota_field
=config("SQLITE_QUOTA_FIELD", "''");
167 options_field
=config("SQLITE_AUXOPTIONS_FIELD", "''");
168 where_clause
=config("SQLITE_WHERE_CLAUSE", "1=1");
174 void authsqlite_connection::authsqliterc_file::do_reload()
176 authsqliterc_file
new_file(conn
);
178 if (new_file
.load(true))
181 DPRINTF("authsqlite: reloaded %s", filename
);
183 // Disconnect from the server, new login
184 // parameters, perhaps.
190 authsqlite_connection
*authsqlite_connection::singleton
=NULL
;
192 authsqlite_connection
*authsqlite_connection::connect()
194 if (authsqlite_connection::singleton
)
196 authsqlite_connection::singleton
->config_file
.load(true);
197 return authsqlite_connection::singleton
;
200 authsqlite_connection
*new_conn
=new authsqlite_connection
;
202 if (new_conn
->config_file
.load())
204 authsqlite_connection::singleton
=new_conn
;
212 sqlite3
*authsqlite_connection::do_connect()
219 p
=config_file
.database
.c_str();
221 if (sqlite3_open_v2(p
, &dbh
, SQLITE_OPEN_READWRITE
, NULL
) != SQLITE_OK
)
225 err("sqllite3_open(%s): %s", p
, sqlite3_errmsg(dbh
));
235 static void do_auth_sqlite_cleanup()
237 if (authsqlite_connection::singleton
)
239 delete authsqlite_connection::singleton
;
240 authsqlite_connection::singleton
=NULL
;
244 std::string
authsqlite_connection::escape(const std::string
&s
)
246 char *q
=sqlite3_mprintf("%q", s
.c_str());
254 class select_callback_info
{
256 authsqliteuserinfo
&ui
;
259 select_callback_info(authsqliteuserinfo
&uiArg
)
260 : ui(uiArg
), ui_cnt(0)
265 static int select_callback(void *dummy
, int n
, char **columns
, char **names
)
267 select_callback_info
*cb
=
268 reinterpret_cast<select_callback_info
*>(dummy
);
272 err("Multiple rows returned");
278 err("Query came back with fewer than 6 columns");
282 cb
->ui
.username
=GET(0);
283 cb
->ui
.cryptpw
=GET(1);
284 cb
->ui
.clearpw
=GET(2);
286 cb
->ui
.maildir
=GET(6);
288 cb
->ui
.fullname
=GET(8);
289 cb
->ui
.options
=GET(9);
291 std::istringstream(GET(3)) >> cb
->ui
.uid
;
292 std::istringstream(GET(4)) >> cb
->ui
.gid
;
296 bool authsqlite_connection::getuserinfo(const char *username
,
298 authsqliteuserinfo
&ui
)
305 std::string querybuf
;
307 if (config_file
.select_clause
.empty())
309 std::ostringstream o
;
312 << config_file
.login_field
<< ", "
313 << config_file
.crypt_field
<< ", "
314 << config_file
.clear_field
<< ", "
315 << config_file
.uid_field
<< ", "
316 << config_file
.gid_field
<< ", "
317 << config_file
.home_field
<< ", "
318 << (strcmp(service
, "courier") == 0 ?
319 config_file
.defaultdelivery_field
:
320 config_file
.maildir_field
) << ", "
321 << config_file
.quota_field
<< ", "
322 << config_file
.name_field
<< ", "
323 << config_file
.options_field
324 << " FROM " << config_file
.user_table
325 << " WHERE " << config_file
.login_field
326 << " = '" << escape(username
);
328 if (strchr(username
, '@') == NULL
&&
329 !config_file
.defdomain
.empty())
331 o
<< "@" << config_file
.defdomain
;
334 o
<< "' AND (" << config_file
.where_clause
<< ")";
340 std::map
<std::string
, std::string
> parameters
;
342 parameters
["service"]=service
;
345 .parse_custom_query(config_file
.select_clause
,
347 config_file
.defdomain
,
351 DPRINTF("SQL query: %s", querybuf
.c_str());
354 select_callback_info
info(ui
);
355 if (sqlite3_exec(dbh
, querybuf
.c_str(), select_callback
,
356 reinterpret_cast<void *>(&info
), &errmsg
) != SQLITE_OK
)
361 sqlite3_free(errmsg
);
369 sqlite3_free(errmsg
);
375 static int dummy_callback(void *dummy
, int n
, char **columns
, char **names
)
380 bool authsqlite_connection::setpass(const char *user
, const char *pass
,
386 if (!do_connect()) return false;
388 std::string newpass_crypt
;
393 if (!(p
=authcryptpasswd(pass
, oldpass
)))
400 std::string clear_escaped
=escape(pass
);
402 std::string crypt_escaped
=escape(newpass_crypt
);
406 if (config_file
.chpass_clause
.size() == 0)
408 std::string username_escaped
=escape(user
);
410 bool has_domain
=strchr(user
, '@') != NULL
;
412 std::ostringstream o
;
414 o
<< "UPDATE " << config_file
.user_table
<< " SET ";
416 if (config_file
.clear_field
!= "''")
417 o
<< config_file
.clear_field
<< "='"
418 << clear_escaped
<< "'";
420 if (config_file
.crypt_field
!= "''")
422 if (config_file
.clear_field
!= "''") o
<< ", ";
423 o
<< config_file
.crypt_field
<< "='" << crypt_escaped
<< "'";
426 o
<< " WHERE " << config_file
.login_field
<< "='"
429 if (!has_domain
&& config_file
.defdomain
.size())
430 o
<< "@" << config_file
.defdomain
;
437 std::map
<std::string
, std::string
> parameters
;
439 parameters
["newpass"]=clear_escaped
;
440 parameters
["newpass_crypt"]=crypt_escaped
;
443 .parse_custom_query(config_file
.chpass_clause
,
445 config_file
.defdomain
,
449 if (courier_authdebug_login_level
>= 2)
451 DPRINTF("setpass SQL: %s", sql_buf
.c_str());
456 if (sqlite3_exec(dbh
, sql_buf
.c_str(), dummy_callback
,
457 NULL
, &errmsg
) != SQLITE_OK
)
463 if (sqlite3_changes(dbh
) > 0)
465 DPRINTF("authsqllite: password updated");
470 DPRINTF("authsqllite: password not updated");
477 sqlite3_free(errmsg
);
483 struct enumerate_user_cb
{
485 void (*cb_func
)(const char *name
,
495 static int enumerate_callback(void *closure
,
496 int n
, char **columns
, char **names
)
498 struct enumerate_user_cb
*cb
=(struct enumerate_user_cb
*)closure
;
500 const char *username
;
508 uid
=atol(GET(1)); /* FIXME use strtol to validate */
514 if (maildir
&& !*maildir
)
517 if (options
&& !*options
)
520 (*cb
->cb_func
)(username
, uid
, gid
, homedir
,
521 maildir
, options
, cb
->void_arg
);
525 void authsqlite_connection::enumerate( void(*cb_func
)(const char *name
,
535 struct enumerate_user_cb cb
;
536 std::string querybuf
;
539 cb
.void_arg
=void_arg
;
541 if (!do_connect()) return;
543 if (config_file
.enumerate_clause
.empty())
545 std::ostringstream o
;
548 << config_file
.login_field
<< ", "
549 << config_file
.uid_field
<< ", "
550 << config_file
.gid_field
<< ", "
551 << config_file
.home_field
<< ", "
552 << config_file
.maildir_field
<< ", "
553 << config_file
.options_field
554 << " FROM " << config_file
.user_table
555 << " WHERE " << config_file
.where_clause
;
561 std::map
<std::string
, std::string
> parameters
;
563 parameters
["service"]="enumerate";
565 .parse_custom_query(config_file
.enumerate_clause
, "*",
566 config_file
.defdomain
, parameters
);
569 DPRINTF("authsqlite: enumerate query: %s", querybuf
.c_str());
573 sqlite3_exec(dbh
, querybuf
.c_str(), enumerate_callback
,
574 reinterpret_cast<void *>(&cb
), &errmsg
);
579 sqlite3_free(errmsg
);
582 (*cb
.cb_func
)(NULL
, 0, 0, NULL
, NULL
, NULL
, cb
.void_arg
);
585 /////////////////////////////////////////////////////////////////////////////
587 int auth_sqlite_setpass(const char *user
, const char *pass
,
590 authsqlite_connection
*conn
=authsqlite_connection::connect();
592 if (conn
&& !conn
->setpass(user
, pass
, oldpass
))
597 void auth_sqlite_enumerate( void(*cb_func
)(const char *name
,
606 authsqlite_connection
*conn
=authsqlite_connection::connect();
609 conn
->enumerate(cb_func
, void_arg
);
612 bool auth_sqlite_getuserinfo(const char *username
,
614 authsqliteuserinfo
&ui
)
616 authsqlite_connection
*conn
=authsqlite_connection::connect();
619 return conn
->getuserinfo(username
, service
, ui
);
624 void auth_sqlite_cleanup()
626 do_auth_sqlite_cleanup();