2 * Copyright (c) 2001 by J. R. Westmoreland <jr@jrw.org>
3 * Portions Copyright (c) 2002-2004 by Matthew Palmer <mpalmer@debian.org>
5 * Original module/version: mod_auth_mysql v2.20
6 * Originally written and maintained by Zeev Suraski <bourbon@netvision.net.il>
7 * A couple of fixes by Marschall Peter <Peter.Marschall@gedos.de>
8 * and Brent Metz <bmetz@thor.tjhsst.edu>
9 * MySQL/PHP style MD5 hashes, and an integration with the mod-auth-mysql
10 * maintained by Bill Joned by Matthew Palmer <mpalmer@debian.org>
12 * This version maintained by Matthew Palmer <mpalmer@debian.org>
14 * Please read the INSTALL and USAGE files for further information.
16 * 2004-02-01 MURAKAMI, takeshi <takeshi@softagency.co.jp>
18 * 2004-02-07 MURAKAMI, takeshi <takeshi@softagency.co.jp>
20 * 2004-09-20 Joseph Walton <joe@kafsemo.org>
24 #define AUTH_MYSQL_VERSION "4.3.9"
29 #define PALLOC apr_palloc
30 #define PCALLOC apr_pcalloc
31 #define SNPRINTF apr_snprintf
32 #define PSTRDUP apr_pstrdup
33 #define PSTRCAT apr_pstrcat
34 #define APACHELOG(severity, handle, message...) ap_log_error(APLOG_MARK, APLOG_NOERRNO | severity, 0, handle->server, message)
36 #define PALLOC ap_palloc
37 #define PCALLOC ap_pcalloc
38 #define SNPRINTF ap_snprintf
39 #define PSTRDUP ap_pstrdup
40 #define PSTRCAT ap_pstrcat
41 #define APACHELOG(severity, handle, message...) ap_log_error(APLOG_MARK, APLOG_NOERRNO | severity, handle->server, message)
45 #include <http_config.h>
46 #include <http_core.h>
47 #include <http_protocol.h>
50 #include "http_request.h" /* for ap_hook_(check_user_id | auth_checker)*/
60 #include <mysqld_error.h>
73 /* This are the system-wide config options; the more specific options live in
74 * a mysql_auth_config_rec structure, one for each MySQL-configured directory.
76 static char *auth_db_host = NULL,
81 static int auth_db_override = 1;
83 char *tmp_host = NULL;
84 char *auth_db_socket = NULL;
85 long auth_db_port = -1;
86 unsigned long auth_db_client_flag = 0;
88 /* Support for general-purpose encryption schemes. Should be fairly straightforward.
89 * We have a checking routine and a name for it (for AuthMySQL_Encryption_Types).
92 #define PLAINTEXT_ENCRYPTION_FLAG 1<<0
94 #define CRYPT_DES_ENCRYPTION_FLAG 1<<1
96 #define MYSQL_ENCRYPTION_FLAG 1<<2
98 #define CRYPT_MD5_ENCRYPTION_FLAG 1<<3
100 #define PHP_MD5_ENCRYPTION_FLAG 1<<4
102 #define CRYPT_ENCRYPTION_FLAG 1<<5
104 #define SHA1SUM_ENCRYPTION_FLAG 1<<6
106 static int check_no_encryption(const char *passwd, char *enc_passwd)
108 return (!strcmp(passwd, enc_passwd));
113 static int check_crypt_des_encryption(const char *passwd, char *enc_passwd)
115 /* Ensure that MD5 passwords aren't checked here */
116 if (!strncmp(enc_passwd, "$1$", 3)) {
119 return (!strcmp(crypt(passwd, enc_passwd), enc_passwd));
124 static int check_crypt_MD5_encryption(const char *passwd, char *enc_passwd)
126 /* Make sure only MD5 passwords are checked */
127 if (strncmp(enc_passwd, "$1$", 3)) {
130 return (!strcmp(crypt(passwd, enc_passwd), enc_passwd));
135 static int check_crypt_encryption(const char *passwd, char *enc_passwd)
137 return (!strcmp(crypt(passwd, enc_passwd), enc_passwd));
141 char hex_digit(char c)
150 static char *md5_hex_hash(const char *pass)
152 unsigned char hash[16];
153 /* This makes this function *very* specialised. Change this to
154 * use dynamic memory if you want to reuse it somewhere else */
155 static char real_hash[33];
161 apr_md5_update(&ct, pass, strlen(pass));
162 apr_md5_final(hash, &ct);
167 ap_MD5Update(&ct, pass, strlen(pass));
168 ap_MD5Final(hash, &ct);
171 /* Now we convert the 16 octet hash to a 32 byte hex string */
172 for (i = 0; i < 16; i++) {
173 real_hash[2*i+1] = hash[i] & 0xF;
174 real_hash[2*i] = (hash[i] & 0xF0) >> 4;
176 for (i = 0; i < 32; i++) {
177 real_hash[i] = hex_digit(real_hash[i]);
179 real_hash[32] = '\0';
184 static int check_PHP_MD5_encryption(const char *passwd, char *enc_passwd)
186 return (!strcmp(md5_hex_hash(passwd), enc_passwd));
189 static char *sha1_hex_hash(const char *passwd)
195 char hash[APR_SHA1_DIGESTSIZE];
196 static char real_hash[APR_SHA1_DIGESTSIZE * 2 + 1];
199 apr_sha1_update(&ct, passwd, strlen(passwd));
200 apr_sha1_final(hash, &ct);
203 char hash[SHA_DIGESTSIZE];
204 static char real_hash[SHA_DIGESTSIZE * 2 + 1];
207 ap_SHA1Update(&ct, passwd, strlen(passwd));
208 ap_SHA1Final(hash, &ct);
211 /* Now we convert the 20 octet hash to a 40 byte hex string */
212 for (i = 0; i < sizeof(hash); i++) {
213 real_hash[2*i+1] = hash[i] & 0xF;
214 real_hash[2*i] = (hash[i] & 0xF0) >> 4;
216 for (i = 0; i < sizeof(real_hash); i++) {
217 real_hash[i] = hex_digit(real_hash[i]);
219 real_hash[sizeof(real_hash)-1] = '\0';
224 static int check_SHA1Sum_encryption(const char *passwd, char *enc_passwd)
226 return (!strcmp(sha1_hex_hash(passwd), enc_passwd));
230 static int check_mysql_encryption(const char *passwd, char *enc_passwd)
232 char scrambled_passwd[32];
234 make_scrambled_password(scrambled_passwd, passwd);
235 return (!strcmp(scrambled_passwd, enc_passwd));
240 int (*check_function)(const char *passwd, char *enc_passwd);
242 } encryption_type_entry;
244 encryption_type_entry supported_encryption_types[] = {
245 { "Plaintext", check_no_encryption, PLAINTEXT_ENCRYPTION_FLAG },
247 { "Crypt_DES", check_crypt_des_encryption, CRYPT_DES_ENCRYPTION_FLAG },
249 { "MySQL", check_mysql_encryption, MYSQL_ENCRYPTION_FLAG },
251 { "Crypt_MD5", check_crypt_MD5_encryption, CRYPT_MD5_ENCRYPTION_FLAG },
253 { "Crypt", check_crypt_encryption, CRYPT_ENCRYPTION_FLAG },
254 { "PHP_MD5", check_PHP_MD5_encryption, PHP_MD5_ENCRYPTION_FLAG },
255 { "SHA1Sum", check_SHA1Sum_encryption, SHA1SUM_ENCRYPTION_FLAG},
256 /* add additional encryption types below */
260 static int get_encryption_flag(const char *name)
262 register encryption_type_entry *ete=supported_encryption_types;
265 if (!strcmp(ete->name, name)) {
273 /* end of support for general-purpose encryption schemes */
275 /* Per-directory configuration structure. One of these is created for each
276 * <Directory>...</Directory> and .htaccess file which requests authentication
283 unsigned int db_port;
290 /* Boolean options */
291 unsigned char persistent;
292 unsigned char enable_mysql_auth;
294 /* Some MySQL errors are retryable; if we retry the operation
295 * by recursing into the same function, we set this so we don't
296 * recurse indefinitely if it's a permanent error.
298 unsigned char dbh_error_lastchance;
304 char *password_field;
306 char *group_user_field;
307 char *group_where_clause;
308 char *password_where_clause;
310 int encryption_types;
311 unsigned char using_encryption_types;
313 unsigned char allow_empty_passwords;
314 unsigned char authoritative;
316 /* You're not going to believe this, but, near as I can tell, apache
317 * doesn't respect the last part of the config_rec. May be an
318 * underflow in some code somewhere, but I'm not taking no chances
319 * with *my* config variables...
321 char sacrificial_lamb[15];
323 } mysql_auth_config_rec;
325 module auth_mysql_module;
332 auth_mysql_cleanup(void *ptr)
334 mysql_auth_config_rec *sec = ptr;
338 syslog(LOG_DEBUG, "MAMDEBUG: Closing MySQL connection");
340 mysql_close(sec->dbh);
345 /* Do the magic required when the module is first loaded.
348 void mysql_auth_init_handler(server_rec *s, apr_pool_t *p)
350 void mysql_auth_init_handler(server_rec *s, pool *p)
355 #if MODULE_MAGIC_NUMBER >= 19980527
356 ap_add_version_component("AuthMySQL/" AUTH_MYSQL_VERSION);
361 /* Called each and every time a new per-directory configuration is
362 * created. We just initialise variables and set defaults. This is
363 * run *before* actual config takes place.
366 void *create_mysql_auth_dir_config(apr_pool_t *p, char *d)
368 void *create_mysql_auth_dir_config(pool *p, char *d)
375 mysql_auth_config_rec *sec = (mysql_auth_config_rec *) PCALLOC(p, sizeof(mysql_auth_config_rec));
378 syslog(LOG_DEBUG, "MAMDEBUG: Now configuring server config for %s", d);
379 syslog(LOG_DEBUG, "MAMDEBUG: sizeof(mysql_auth_config_rec) = %i",
380 sizeof(mysql_auth_config_rec));
383 sec->db_name = sec->db_socket = sec->db_user = sec->db_pwd = NULL;
386 /* When the memory for this connection record is cleaned, we must
387 * be sure to close the DB connection, if it exists. If this does
388 * not happen, we are in a world of pain.
391 apr_pool_cleanup_register(p, sec, auth_mysql_cleanup, apr_pool_cleanup_null);
393 ap_register_cleanup(p, sec, auth_mysql_cleanup, ap_null_cleanup);
398 sec->user_table = sec->group_table = NULL;
399 sec->user_field = sec->password_field = sec->group_field = NULL;
400 sec->group_where_clause = sec->password_where_clause = NULL;
401 sec->group_user_field = NULL;
403 sec->authoritative = 1;
404 sec->allow_empty_passwords = 1;
406 sec->dbh_error_lastchance = 0;
409 syslog(LOG_DEBUG, "MAMDEBUG: Enabling MySQL auth by default");
411 sec->enable_mysql_auth = 1;
414 sec->encryption_types = CRYPT_DES_ENCRYPTION_FLAG;
415 sec->using_encryption_types = 0;
417 sec->encryption_types = 0;
418 sec->using_encryption_types = 0;
424 syslog(LOG_DEBUG, "MAMDEBUG: Persistent is now ON");
429 for (i = 0; i < 15; i++)
431 sec->sacrificial_lamb[i] = i % 10 + '0';
438 /* Helper function to make some decisions about whether to use crypted
439 * passwords in response to "AuthMySQL_Encrypted_Passwords on" in a config
443 static const char *set_crypted_password_flag(cmd_parms *cmd, void *sconf, int arg)
445 mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
447 if (sec->using_encryption_types) {
448 /* This setting is ignored if we're using Encryption_Types */
453 sec->encryption_types |= CRYPT_DES_ENCRYPTION_FLAG;
455 sec->encryption_types &= ~CRYPT_DES_ENCRYPTION_FLAG;
456 if (!sec->encryption_types) {
457 sec->encryption_types = PLAINTEXT_ENCRYPTION_FLAG;
465 /* Equivalent to set_crypted_password_flag above, except that this time we're
466 * talking about MySQL-style scrambled passwords instead.
469 static const char *set_scrambled_password_flag(cmd_parms *cmd, void *sconf, int arg)
471 mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
473 if (sec->using_encryption_types) {
474 /* This setting is ignored if we're using Encryption_Types */
478 sec->encryption_types |= MYSQL_ENCRYPTION_FLAG;
480 sec->encryption_types &= ~MYSQL_ENCRYPTION_FLAG;
481 if (!sec->encryption_types) {
482 sec->encryption_types = PLAINTEXT_ENCRYPTION_FLAG;
488 /* Ensure that any string passed through us won't unduly upset the MySQL
489 * server when passed in as part of a query.
492 static char *mysql_escape(char *str, apr_pool_t *p)
494 static char *mysql_escape(char *str, pool *p)
503 dest = (char *) PALLOC(p, strlen(str) * 2 + 1);
508 mysql_escape_string(dest, str, strlen(str));
513 /* Config helper to set the server-wide default database name.
515 static const char *set_auth_mysql_db(cmd_parms * parms, void *dummy, const char *db)
517 auth_db_name = (char *)db;
521 /* Config helper to set the server-wide default database host.
523 static const char *set_auth_mysql_host(cmd_parms *parms, void *dummy, const char *host)
525 auth_db_host = (char *) host;
529 /* Config helper to set server-wide defaults for database parameters.
531 static const char *set_auth_mysql_info(cmd_parms * parms, void *dummy, const char *host, const char *user, const char *pwd)
534 auth_db_host = (char *) host;
538 auth_db_user = (char *)user;
542 auth_db_pwd = (char *)pwd;
548 /* Config helper to set the server-wide default database username.
550 static const char *set_auth_mysql_user(cmd_parms *parms, void *dummy, const char *user)
552 auth_db_user = (char *)user;
556 /* Config helper to set the server-wide default database password (coupled to
557 * the user specified above).
559 static const char *set_auth_mysql_pwd(cmd_parms *parms, void *dummy, const char *pwd)
561 auth_db_pwd = (char *)pwd;
565 /* Set the server-wide database server socket.
567 static const char *set_auth_mysql_socket(cmd_parms *parms, void *dummy, const char *sock)
569 auth_db_socket = (char *)socket;
573 /* Set the server-wide database server port.
575 static const char *set_auth_mysql_port(cmd_parms *parms, void *dummy, const char *port)
577 auth_db_port = (unsigned int) atoi(port);
581 /* Config helper to judge whether to allow per-directory configs to override
582 * the server-wide defaults for database parameters. The only reason this
583 * exists (instead of using an ap_set_flag_slot) is because this isn't part
584 * of a config structure, and I'm not sure how to set globals from the Apache
587 static const char *set_auth_mysql_override(cmd_parms *parms, void *dummy, int arg)
589 auth_db_override = arg;
593 /* Config helper to set a selected encryption type.
595 static const char *set_encryption_types(cmd_parms *cmd, void *sconf, const char *arg)
597 mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
599 int new_encryption_flag = get_encryption_flag(arg);
601 if (!new_encryption_flag) {
602 APACHELOG(APLOG_ERR, cmd, "Unsupported encryption type: %s", arg);
606 if (!sec->using_encryption_types) {
607 sec->encryption_types = 0;
608 sec->using_encryption_types = 1;
611 sec->encryption_types |= new_encryption_flag;
616 /* This pair of config helpers exist only because of varying semantics
617 * in the two versions of mod_auth_mysql I merged. As soon as we have a
618 * consistent set of configuration primitives, these are going.
620 static const char *set_non_persistent(cmd_parms *cmd, void *sconf, int arg)
622 mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
624 sec->persistent = !arg;
625 APACHELOG(APLOG_DEBUG, cmd, "set_non_persistent: Setting persistent in %s to %i", sec->dir, sec->persistent);
629 static const char *set_persistent(cmd_parms *cmd, void *sconf, int arg)
631 mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
633 sec->persistent = arg;
634 APACHELOG(APLOG_DEBUG, cmd, "set_persistent: Setting persistent in %s to %i", sec->dir, sec->persistent);
638 static const char *enable_mysql(cmd_parms *cmd, void *sconf, int arg)
640 mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
642 sec->enable_mysql_auth = arg;
643 APACHELOG(APLOG_DEBUG, cmd, "enable_mysql: Setting enable_mysql_auth in %s to %i", sec->dir, sec->enable_mysql_auth);
647 /* The command list. What it's called, when it's legal to use it, and
648 * what to do when we find it. Pretty cool, IMHO.
653 command_rec mysql_auth_cmds[] = {
654 AP_INIT_TAKE3( "Auth_MySQL_Info", set_auth_mysql_info,
656 RSRC_CONF, "host, user and password of the MySQL database" ),
658 AP_INIT_TAKE1( "AuthMySQL_DefaultHost", set_auth_mysql_host,
660 RSRC_CONF, "Default MySQL host" ),
662 AP_INIT_TAKE1( "AuthMySQL_DefaultUser", set_auth_mysql_user,
664 RSRC_CONF, "Default MySQL user" ),
666 AP_INIT_TAKE1( "AuthMySQL_DefaultPassword", set_auth_mysql_pwd,
668 RSRC_CONF, "Default MySQL password" ),
670 AP_INIT_TAKE1( "Auth_MySQL_DefaultPort", set_auth_mysql_port,
672 RSRC_CONF, "Default MySQL server port" ),
674 AP_INIT_TAKE1( "Auth_MySQL_DefaultSocket", set_auth_mysql_socket,
676 RSRC_CONF, "Default MySQL server socket" ),
678 AP_INIT_TAKE1( "Auth_MySQL_General_DB", set_auth_mysql_db,
680 RSRC_CONF, "default database for MySQL authentication" ),
682 AP_INIT_TAKE1( "AuthMySQL_DefaultDB", set_auth_mysql_db,
684 RSRC_CONF, "default database for MySQL authentication" ),
686 AP_INIT_TAKE1( "AuthMySQL_Host", ap_set_string_slot,
687 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_host),
688 OR_AUTHCFG, "database host" ),
690 AP_INIT_TAKE1( "Auth_MySQL_Host", ap_set_string_slot,
691 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_host),
692 OR_AUTHCFG, "database host" ),
694 AP_INIT_TAKE1( "Auth_MySQL_Socket", ap_set_string_slot,
695 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_socket),
696 OR_AUTHCFG, "database host socket" ),
698 AP_INIT_TAKE1( "AuthMySQL_Socket", ap_set_string_slot,
699 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_socket),
700 OR_AUTHCFG, "database host socket" ),
702 AP_INIT_TAKE1( "Auth_MySQL_Port", ap_set_string_slot,
703 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_port),
704 OR_AUTHCFG, "database host port" ),
706 AP_INIT_TAKE1( "AuthMySQL_Port", ap_set_string_slot,
707 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_port),
708 OR_AUTHCFG, "database host port" ),
710 AP_INIT_TAKE1( "Auth_MySQL_Username", ap_set_string_slot,
711 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_user),
712 OR_AUTHCFG, "database user" ),
714 AP_INIT_TAKE1( "AuthMySQL_User", ap_set_string_slot,
715 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_user),
716 OR_AUTHCFG, "database user" ),
718 AP_INIT_TAKE1( "Auth_MySQL_Password", ap_set_string_slot,
719 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_pwd),
720 OR_AUTHCFG, "database password" ),
722 AP_INIT_TAKE1( "AuthMySQL_Password", ap_set_string_slot,
723 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_pwd),
724 OR_AUTHCFG, "database password" ),
726 AP_INIT_TAKE1( "Auth_MySQL_DB", ap_set_string_slot,
727 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_name),
728 OR_AUTHCFG, "database name" ),
730 AP_INIT_TAKE1( "AuthMySQL_DB", ap_set_string_slot,
731 (void*)APR_XtOffsetOf(mysql_auth_config_rec, db_name),
732 OR_AUTHCFG, "database name" ),
734 AP_INIT_TAKE1( "Auth_MySQL_Password_Table", ap_set_string_slot,
735 (void*)APR_XtOffsetOf(mysql_auth_config_rec, user_table),
736 OR_AUTHCFG, "Name of the MySQL table containing the password/user-name combination" ),
738 AP_INIT_TAKE1( "AuthMySQL_Password_Table", ap_set_string_slot,
739 (void*)APR_XtOffsetOf(mysql_auth_config_rec, user_table),
740 OR_AUTHCFG, "Name of the MySQL table containing the password/user-name combination" ),
742 AP_INIT_TAKE1( "Auth_MySQL_Group_Table", ap_set_string_slot,
743 (void*)APR_XtOffsetOf(mysql_auth_config_rec, group_table),
744 OR_AUTHCFG, "Name of the MySQL table containing the group-name/user-name combination; can be the same as the password-table." ),
746 AP_INIT_TAKE1( "Auth_MySQL_Group_Clause", ap_set_string_slot,
747 (void*)APR_XtOffsetOf(mysql_auth_config_rec, group_where_clause),
748 OR_AUTHCFG, "Additional WHERE clause for group/user-name lookup" ),
750 AP_INIT_TAKE1( "AuthMySQL_Group_Table", ap_set_string_slot,
751 (void*)APR_XtOffsetOf(mysql_auth_config_rec, group_table),
752 OR_AUTHCFG, "Name of the MySQL table containing the group-name/user-name combination; can be the same as the password-table." ),
754 AP_INIT_TAKE1( "Auth_MySQL_Password_Field", ap_set_string_slot,
755 (void*)APR_XtOffsetOf(mysql_auth_config_rec, password_field),
756 OR_AUTHCFG, "The name of the field in the MySQL password table" ),
758 AP_INIT_TAKE1( "AuthMySQL_Password_Field", ap_set_string_slot,
759 (void*)APR_XtOffsetOf(mysql_auth_config_rec, password_field),
760 OR_AUTHCFG, "The name of the field in the MySQL password table" ),
762 AP_INIT_TAKE1( "Auth_MySQL_Password_Clause", ap_set_string_slot,
763 (void*)APR_XtOffsetOf(mysql_auth_config_rec, password_where_clause),
764 OR_AUTHCFG, "Additional WHERE clause for group password/user-name lookup" ),
766 AP_INIT_TAKE1( "Auth_MySQL_Username_Field", ap_set_string_slot,
767 (void*)APR_XtOffsetOf(mysql_auth_config_rec, user_field),
768 OR_AUTHCFG, "The name of the user-name field in the MySQL password (and possibly group) table(s)." ),
770 AP_INIT_TAKE1( "AuthMySQL_Username_Field", ap_set_string_slot,
771 (void*)APR_XtOffsetOf(mysql_auth_config_rec, user_field),
772 OR_AUTHCFG, "The name of the user-name field in the MySQL password (and possibly group) table(s)." ),
774 AP_INIT_TAKE1( "Auth_MySQL_Group_Field", ap_set_string_slot,
775 (void*)APR_XtOffsetOf(mysql_auth_config_rec, group_field),
776 OR_AUTHCFG, "The name of the group field in the MySQL group table; must be set if you want to use groups." ),
778 AP_INIT_TAKE1( "AuthMySQL_Group_Field", ap_set_string_slot,
779 (void*)APR_XtOffsetOf(mysql_auth_config_rec, group_field),
780 OR_AUTHCFG, "The name of the group field in the MySQL group table; must be set if you want to use groups." ),
782 AP_INIT_TAKE1( "Auth_MySQL_Group_User_Field", ap_set_string_slot,
783 (void*)APR_XtOffsetOf(mysql_auth_config_rec, group_user_field),
784 OR_AUTHCFG, "The name of the user-name field in the MySQL group table; defaults to the same as the username field for the password table." ),
786 AP_INIT_TAKE1( "AuthMySQL_Group_User_Field", ap_set_string_slot,
787 (void*)APR_XtOffsetOf(mysql_auth_config_rec, group_user_field),
788 OR_AUTHCFG, "The name of the user-name field in the MySQL group table; defaults to the same as the username field for the password table." ),
790 AP_INIT_FLAG( "Auth_MySQL_Empty_Passwords", ap_set_flag_slot,
791 (void*)APR_XtOffsetOf(mysql_auth_config_rec, allow_empty_passwords),
792 OR_AUTHCFG, "Enable (on) or disable (off) empty password strings; in which case any user password is accepted." ),
794 AP_INIT_FLAG( "AuthMySQL_Empty_Passwords", ap_set_flag_slot,
795 (void*)APR_XtOffsetOf(mysql_auth_config_rec, allow_empty_passwords),
796 OR_AUTHCFG, "Enable (on) or disable (off) empty password strings; in which case any user password is accepted." ),
798 AP_INIT_FLAG( "Auth_MySQL_Authoritative", ap_set_flag_slot,
799 (void*)APR_XtOffsetOf(mysql_auth_config_rec, authoritative),
800 OR_AUTHCFG, "When 'on' the MySQL database is taken to be authoritative and access control is not passed along to other db or access modules." ),
802 AP_INIT_FLAG( "AuthMySQL_Authoritative", ap_set_flag_slot,
803 (void*)APR_XtOffsetOf(mysql_auth_config_rec, authoritative),
804 OR_AUTHCFG, "When 'on' the MySQL database is taken to be authoritative and access control is not passed along to other db or access modules." ),
806 AP_INIT_FLAG( "AuthMySQL_AllowOverride", set_auth_mysql_override,
808 RSRC_CONF, "Allow directory overrides of configuration" ),
810 AP_INIT_FLAG( "Auth_MySQL_Encrypted_Passwords", set_crypted_password_flag,
812 OR_AUTHCFG, "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." ),
814 AP_INIT_FLAG( "AuthMySQL_Encrypted_Passwords", set_crypted_password_flag,
816 OR_AUTHCFG, "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." ),
818 AP_INIT_FLAG( "Auth_MySQL_Scrambled_Passwords", set_scrambled_password_flag,
820 OR_AUTHCFG, "When 'on' the password in the password table are taken to be scramble()d using mySQL's password() function." ),
822 AP_INIT_FLAG( "AuthMySQL_Scrambled_Passwords", set_scrambled_password_flag,
824 OR_AUTHCFG, "When 'on' the password in the password table are taken to be scramble()d using mySQL's password() function." ),
826 AP_INIT_ITERATE( "Auth_MySQL_Encryption_Types", set_encryption_types,
828 OR_AUTHCFG, "Encryption types to use" ),
830 AP_INIT_ITERATE( "AuthMySQL_Encryption_Types", set_encryption_types,
832 OR_AUTHCFG, "Encryption types to use" ),
834 AP_INIT_FLAG( "Auth_MySQL_Non_Persistent", set_non_persistent,
836 OR_AUTHCFG, "Use non-persistent MySQL links" ),
838 AP_INIT_FLAG( "AuthMySQL_Persistent", set_persistent,
840 OR_AUTHCFG, "Use non-persistent MySQL links" ),
842 AP_INIT_FLAG( "Auth_MySQL", enable_mysql,
844 OR_AUTHCFG, "Enable MySQL authentication" ),
846 AP_INIT_FLAG( "AuthMySQL", enable_mysql,
848 OR_AUTHCFG, "Enable MySQL authentication" ),
850 AP_INIT_TAKE1( "Auth_MySQL_Where", ap_set_string_slot,
851 (void*)APR_XtOffsetOf(mysql_auth_config_rec, password_where_clause),
852 OR_AUTHCFG, "Additional WHERE clause for group password/user-name lookup" ),
857 command_rec mysql_auth_cmds[] = {
858 { "Auth_MySQL_Info", set_auth_mysql_info,
860 RSRC_CONF, TAKE3, "host, user and password of the MySQL database" },
862 { "AuthMySQL_DefaultHost", set_auth_mysql_host,
864 RSRC_CONF, TAKE1, "Default MySQL host" },
866 { "AuthMySQL_DefaultUser", set_auth_mysql_user,
868 RSRC_CONF, TAKE1, "Default MySQL user" },
870 { "AuthMySQL_DefaultPassword", set_auth_mysql_pwd,
872 RSRC_CONF, TAKE1, "Default MySQL password" },
874 { "Auth_MySQL_DefaultPort", set_auth_mysql_port,
876 RSRC_CONF, TAKE1, "Default MySQL server port" },
878 { "Auth_MySQL_DefaultSocket", set_auth_mysql_socket,
880 RSRC_CONF, TAKE1, "Default MySQL server socket" },
882 { "Auth_MySQL_General_DB", set_auth_mysql_db,
884 RSRC_CONF, TAKE1, "default database for MySQL authentication" },
886 { "AuthMySQL_DefaultDB", set_auth_mysql_db,
888 RSRC_CONF, TAKE1, "default database for MySQL authentication" },
890 { "AuthMySQL_Host", ap_set_string_slot,
891 (void *) XtOffsetOf(mysql_auth_config_rec, db_host),
892 OR_AUTHCFG, TAKE1, "database host" },
894 { "Auth_MySQL_Host", ap_set_string_slot,
895 (void *) XtOffsetOf(mysql_auth_config_rec, db_host),
896 OR_AUTHCFG, TAKE1, "database host" },
898 { "Auth_MySQL_Socket", ap_set_string_slot,
899 (void *) XtOffsetOf(mysql_auth_config_rec, db_socket),
900 OR_AUTHCFG, TAKE1, "database host socket" },
902 { "Auth_MySQL_Port", ap_set_string_slot,
903 (void *) XtOffsetOf(mysql_auth_config_rec, db_port),
904 OR_AUTHCFG, TAKE1, "database host socket" },
906 { "Auth_MySQL_Username", ap_set_string_slot,
907 (void *) XtOffsetOf(mysql_auth_config_rec, db_user),
908 OR_AUTHCFG, TAKE1, "database user" },
910 { "AuthMySQL_User", ap_set_string_slot,
911 (void *) XtOffsetOf(mysql_auth_config_rec, db_user),
912 OR_AUTHCFG, TAKE1, "database user" },
914 { "Auth_MySQL_Password", ap_set_string_slot,
915 (void *) XtOffsetOf(mysql_auth_config_rec, db_pwd),
916 OR_AUTHCFG, TAKE1, "database password" },
918 { "AuthMySQL_Password", ap_set_string_slot,
919 (void *) XtOffsetOf(mysql_auth_config_rec, db_pwd),
920 OR_AUTHCFG, TAKE1, "database password" },
922 { "Auth_MySQL_DB", ap_set_string_slot,
923 (void *) XtOffsetOf(mysql_auth_config_rec, db_name),
924 OR_AUTHCFG, TAKE1, "database name" },
926 { "AuthMySQL_DB", ap_set_string_slot,
927 (void *) XtOffsetOf(mysql_auth_config_rec, db_name),
928 OR_AUTHCFG, TAKE1, "database name" },
930 { "Auth_MySQL_Password_Table", ap_set_string_slot,
931 (void *) XtOffsetOf(mysql_auth_config_rec, user_table),
932 OR_AUTHCFG, TAKE1, "Name of the MySQL table containing the password/user-name combination" },
934 { "AuthMySQL_Password_Table", ap_set_string_slot,
935 (void *) XtOffsetOf(mysql_auth_config_rec, user_table),
936 OR_AUTHCFG, TAKE1, "Name of the MySQL table containing the password/user-name combination" },
938 { "Auth_MySQL_Group_Table", ap_set_string_slot,
939 (void *) XtOffsetOf(mysql_auth_config_rec, group_table),
940 OR_AUTHCFG, TAKE1, "Name of the MySQL table containing the group-name/user-name combination; can be the same as the password-table." },
942 { "Auth_MySQL_Group_Clause", ap_set_string_slot,
943 (void *) XtOffsetOf(mysql_auth_config_rec, group_where_clause),
944 OR_AUTHCFG, TAKE1, "Additional WHERE clause for group/user-name lookup" },
946 { "AuthMySQL_Group_Table", ap_set_string_slot,
947 (void *) XtOffsetOf(mysql_auth_config_rec, group_table),
948 OR_AUTHCFG, TAKE1, "Name of the MySQL table containing the group-name/user-name combination; can be the same as the password-table." },
950 { "Auth_MySQL_Password_Field", ap_set_string_slot,
951 (void *) XtOffsetOf(mysql_auth_config_rec, password_field),
952 OR_AUTHCFG, TAKE1, "The name of the field in the MySQL password table" },
954 { "AuthMySQL_Password_Field", ap_set_string_slot,
955 (void *) XtOffsetOf(mysql_auth_config_rec, password_field),
956 OR_AUTHCFG, TAKE1, "The name of the field in the MySQL password table" },
958 { "Auth_MySQL_Password_Clause", ap_set_string_slot,
959 (void *) XtOffsetOf(mysql_auth_config_rec, password_where_clause),
960 OR_AUTHCFG, TAKE1, "Additional WHERE clause for group password/user-name lookup" },
962 { "Auth_MySQL_Username_Field", ap_set_string_slot,
963 (void *) XtOffsetOf(mysql_auth_config_rec, user_field),
964 OR_AUTHCFG, TAKE1, "The name of the user-name field in the MySQL password (and possibly group) table(s)." },
966 { "AuthMySQL_Username_Field", ap_set_string_slot,
967 (void *) XtOffsetOf(mysql_auth_config_rec, user_field),
968 OR_AUTHCFG, TAKE1, "The name of the user-name field in the MySQL password (and possibly group) table(s)." },
970 { "Auth_MySQL_Group_Field", ap_set_string_slot,
971 (void *) XtOffsetOf(mysql_auth_config_rec, group_field),
972 OR_AUTHCFG, TAKE1, "The name of the group field in the MySQL group table; must be set if you want to use groups." },
974 { "AuthMySQL_Group_Field", ap_set_string_slot,
975 (void *) XtOffsetOf(mysql_auth_config_rec, group_field),
976 OR_AUTHCFG, TAKE1, "The name of the group field in the MySQL group table; must be set if you want to use groups." },
978 { "Auth_MySQL_Group_User_Field", ap_set_string_slot,
979 (void *) XtOffsetOf(mysql_auth_config_rec, group_user_field),
980 OR_AUTHCFG, TAKE1, "The name of the user-name field in the MySQL group table; defaults to the same as the username field for the password table." },
982 { "AuthMySQL_Group_User_Field", ap_set_string_slot,
983 (void *) XtOffsetOf(mysql_auth_config_rec, group_user_field),
984 OR_AUTHCFG, TAKE1, "The name of the user-name field in the MySQL group table; defaults to the same as the username field for the password table." },
986 { "Auth_MySQL_Empty_Passwords", ap_set_flag_slot,
987 (void *) XtOffsetOf(mysql_auth_config_rec, allow_empty_passwords),
988 OR_AUTHCFG, FLAG, "Enable (on) or disable (off) empty password strings; in which case any user password is accepted." },
990 { "AuthMySQL_Empty_Passwords", ap_set_flag_slot,
991 (void *) XtOffsetOf(mysql_auth_config_rec, allow_empty_passwords),
992 OR_AUTHCFG, FLAG, "Enable (on) or disable (off) empty password strings; in which case any user password is accepted." },
994 { "Auth_MySQL_Authoritative", ap_set_flag_slot,
995 (void *) XtOffsetOf(mysql_auth_config_rec, authoritative),
996 OR_AUTHCFG, FLAG, "When 'on' the MySQL database is taken to be authoritative and access control is not passed along to other db or access modules." },
998 { "AuthMySQL_Authoritative", ap_set_flag_slot,
999 (void *) XtOffsetOf(mysql_auth_config_rec, authoritative),
1000 OR_AUTHCFG, FLAG, "When 'on' the MySQL database is taken to be authoritative and access control is not passed along to other db or access modules." },
1002 { "AuthMySQL_AllowOverride", set_auth_mysql_override,
1004 RSRC_CONF, FLAG, "Allow directory overrides of configuration" },
1006 { "Auth_MySQL_Encrypted_Passwords", set_crypted_password_flag,
1008 OR_AUTHCFG, FLAG, "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." },
1010 { "AuthMySQL_Encrypted_Passwords", set_crypted_password_flag,
1012 OR_AUTHCFG, FLAG, "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." },
1014 { "Auth_MySQL_Scrambled_Passwords", set_scrambled_password_flag,
1016 OR_AUTHCFG, FLAG, "When 'on' the password in the password table are taken to be scramble()d using mySQL's password() function." },
1018 { "AuthMySQL_Scrambled_Passwords", set_scrambled_password_flag,
1020 OR_AUTHCFG, FLAG, "When 'on' the password in the password table are taken to be scramble()d using mySQL's password() function." },
1022 { "Auth_MySQL_Encryption_Types", set_encryption_types,
1024 OR_AUTHCFG, ITERATE,"Encryption types to use" },
1026 { "AuthMySQL_Encryption_Types", set_encryption_types,
1028 OR_AUTHCFG, ITERATE,"Encryption types to use" },
1030 { "Auth_MySQL_Non_Persistent", set_non_persistent,
1032 OR_AUTHCFG, FLAG, "Use non-persistent MySQL links" },
1034 { "AuthMySQL_Persistent", set_persistent,
1036 OR_AUTHCFG, FLAG, "Use non-persistent MySQL links" },
1038 { "Auth_MySQL", enable_mysql,
1040 OR_AUTHCFG, FLAG, "Enable MySQL authentication" },
1042 { "AuthMySQL", enable_mysql,
1044 OR_AUTHCFG, FLAG, "Enable MySQL authentication" },
1046 { "Auth_MySQL_Where", ap_set_string_slot,
1047 (void *) XtOffsetOf(mysql_auth_config_rec, password_where_clause),
1048 OR_AUTHCFG, TAKE1, "Additional WHERE clause for group password/user-name lookup" },
1053 /* { "Auth_MySQL", ap_set_flag_slot, (void *) XtOffsetOf(mysql_auth_config_rec, enable_mysql_auth), OR_AUTHCFG, FLAG, "Enable (on) or disable (off) MySQL authentication." },
1054 { "AuthMySQL", ap_set_flag_slot, (void *) XtOffsetOf(mysql_auth_config_rec, enable_mysql_auth), OR_AUTHCFG, FLAG, "Enable (on) or disable (off) MySQL authentication." },
1065 auth_mysql_result_cleanup(void *result)
1067 mysql_free_result((MYSQL_RES *) result);
1071 static void note_cleanups_for_mysql_auth_result(apr_pool_t *p, MYSQL_RES * result)
1073 static void note_cleanups_for_mysql_auth_result(pool *p, MYSQL_RES * result)
1077 apr_pool_cleanup_register(p, (void *) result, auth_mysql_result_cleanup, auth_mysql_result_cleanup);
1079 ap_register_cleanup(p, (void *) result, auth_mysql_result_cleanup, auth_mysql_result_cleanup);
1084 /* Make a MySQL database link open and ready for business. Returns 0 on
1085 * success, or the MySQL error number which caused the failure if there was
1086 * some sort of problem.
1088 static int open_auth_dblink(request_rec *r, mysql_auth_config_rec *sec)
1090 char *host = "localhost", *socket = NULL;
1091 unsigned int port = 3306;
1092 char *dbname = auth_db_name, *user = auth_db_user, *pwd = auth_db_pwd;
1093 void (*sigpipe_handler)();
1094 unsigned long client_flag = 0;
1096 APACHELOG(APLOG_DEBUG, r, "Opening DB connection for %s", sec->dir);
1099 host = auth_db_host;
1104 socket = auth_db_socket;
1107 if (auth_db_port != -1)
1109 port = auth_db_port;
1112 if (auth_db_override)
1116 socket = sec->db_socket;
1119 if (sec->db_port != -1)
1121 port = sec->db_port;
1126 host = sec->db_host;
1130 user = sec->db_user;
1138 dbname = sec->db_name;
1142 if (!dbname || !dbname[0]) {
1143 /* It would be preferred if we had somewhere to connect to... */
1144 APACHELOG(APLOG_CRIT, r,
1145 "No database given - rather a problem. Bailing out.");
1146 return CR_WRONG_HOST_INFO;
1149 /* MySQL likes to throw the odd SIGPIPE now and then - ignore it for now */
1150 sigpipe_handler = signal(SIGPIPE, SIG_IGN);
1152 sec->dbh = mysql_init(NULL);
1154 if (!mysql_real_connect(sec->dbh, host, user, pwd, dbname, port, socket, client_flag)) {
1155 APACHELOG(APLOG_ERR, r,
1156 "Connection error: %s", mysql_error(sec->dbh));
1157 errno = mysql_errno(sec->dbh);
1158 mysql_close(sec->dbh);
1163 signal(SIGPIPE, sigpipe_handler);
1165 APACHELOG(APLOG_DEBUG, r, "Persistent in %s is %i", sec->dir, sec->persistent);
1167 if (!sec->persistent) {
1168 APACHELOG(APLOG_DEBUG, r, "Registering non-persistent for %s", sec->dir);
1170 apr_pool_cleanup_register(r->pool, sec, auth_mysql_cleanup, apr_pool_cleanup_null);
1173 ap_register_cleanup(r->pool, sec, auth_mysql_cleanup, ap_null_cleanup);
1174 ap_unblock_alarms();
1178 /* W00t! We made it! */
1182 /* Run a query against the database. Doesn't assume nearly anything about
1183 * the state of affairs regarding the database connection.
1184 * Returns 0 on a successful query run, or the MySQL error number on
1185 * error. It is the responsibility of the calling function to retrieve any
1186 * data which may have been obtained through the running of this function.
1188 static int safe_mysql_query(request_rec *r, char *query, mysql_auth_config_rec *sec)
1190 int error = CR_UNKNOWN_ERROR;
1192 APACHELOG(APLOG_DEBUG, r, "sec->dbh in %s is %p", sec->dir, sec->dbh);
1193 if (sec->dbh_error_lastchance)
1195 APACHELOG(APLOG_DEBUG, r, "Last chance, bub");
1199 APACHELOG(APLOG_DEBUG, r, "Ordinary query");
1203 APACHELOG(APLOG_DEBUG, r,
1204 "No DB connection open - firing one up");
1205 if ((error = open_auth_dblink(r, sec))) {
1206 APACHELOG(APLOG_DEBUG, r,
1207 "open_auth_dblink returned %i", error);
1211 APACHELOG(APLOG_DEBUG, r,
1212 "Correctly opened a new DB connection");
1215 APACHELOG(APLOG_DEBUG, r,
1216 "Running query: [%s]", query);
1218 if (mysql_query(sec->dbh, query)) {
1219 error = mysql_errno(sec->dbh);
1221 APACHELOG(APLOG_DEBUG, r,
1222 "Query maybe-failed: %s (%i), lastchance=%i", mysql_error(sec->dbh), error, sec->dbh_error_lastchance);
1223 APACHELOG(APLOG_DEBUG, r,
1224 "Error numbers of interest are %i (SG) and %i (SL)",
1225 CR_SERVER_GONE_ERROR, CR_SERVER_LOST);
1226 if (sec->dbh_error_lastchance)
1228 /* No matter what error, we're moving out */
1231 else if (error == CR_SERVER_LOST || error == CR_SERVER_GONE_ERROR)
1233 /* Try again, once more only */
1234 sec->dbh_error_lastchance = 1;
1236 APACHELOG(APLOG_DEBUG, r, "Retrying query");
1237 return safe_mysql_query(r, query, sec);
1248 /* Store the result of a query in a result structure, and return it. It's
1249 * "safe" in the fact that a cleanup function is registered for the structure
1250 * so it will be tidied up after the request.
1251 * Returns the result data on success, or NULL if there was no data to retrieve.
1254 static MYSQL_RES *safe_mysql_store_result(apr_pool_t *p, mysql_auth_config_rec *sec)
1256 static MYSQL_RES *safe_mysql_store_result(pool *p, mysql_auth_config_rec *sec)
1265 result = mysql_store_result(sec->dbh);
1267 syslog(LOG_DEBUG, "MAMDEBUG: Got %p for result", result);
1271 note_cleanups_for_mysql_auth_result(p, result);
1275 ap_unblock_alarms();
1281 /* Check the plaintext password given against the hashed version. Go
1282 * through all configured encryption types looking for a match.
1283 * Returns 1 on a match, 0 on no match, and -1 on error.
1285 static int check_password(const char *plaintext, char *hashed, request_rec *r, mysql_auth_config_rec *sec)
1287 encryption_type_entry *ete;
1289 /* empty password support */
1290 if (sec->allow_empty_passwords && !strlen(hashed)) {
1291 APACHELOG(APLOG_INFO, r, "User successful on empty password");
1295 for (ete=supported_encryption_types; ete->name; ete++) {
1296 if (sec->encryption_types & ete->flag) {
1297 APACHELOG(APLOG_DEBUG, r,
1298 "Checking with %s", ete->name);
1299 if (ete->check_function(plaintext, hashed)) {
1300 APACHELOG(APLOG_DEBUG, r, "Auth succeeded");
1305 APACHELOG(APLOG_DEBUG, r, "User failed all encryption types");
1309 /* Checks whether the username and plaintext password match the user data
1310 * stored in the database, against all configured encryption schemes.
1311 * Returns 1 on successful match, 0 unsuccessful match, -1 on error.
1313 static int mysql_check_user_password(request_rec *r, char *user, const char *password, mysql_auth_config_rec *sec)
1315 char *auth_table = "mysql_auth", *auth_user_field = "username",
1316 *auth_password_field = "passwd", *auth_password_clause = "";
1318 char *esc_user = mysql_escape(user, r->pool);
1323 if (sec->user_table) {
1324 auth_table = sec->user_table;
1326 if (sec->user_field) {
1327 auth_user_field = sec->user_field;
1329 if (sec->password_field) {
1330 auth_password_field = sec->password_field;
1332 if (sec->password_where_clause) {
1333 auth_password_clause = sec->password_where_clause;
1335 APACHELOG(APLOG_DEBUG, r,
1336 "Constructing password collection query with "
1337 "passfield=[%s], table=[%s], userfield=[%s], where_clause=[%s]", auth_password_field
1338 , auth_table, esc_user,auth_password_clause);
1340 query = (char *) PSTRCAT(r->pool, "SELECT ", auth_password_field,
1341 " FROM ", auth_table, " WHERE ",
1342 auth_user_field, "='", esc_user, "'",
1343 auth_password_clause, NULL);
1345 APACHELOG(APLOG_ERR, r,
1346 "Failed to create query string - we're in deep poopy");
1350 if ((rv = safe_mysql_query(r, query, sec))) {
1353 APACHELOG(APLOG_ERR, r,
1354 "Query call failed: %s (%i)", mysql_error(sec->dbh), rv);
1357 APACHELOG(APLOG_DEBUG, r, "Failed query was: [%s]", query);
1361 result = safe_mysql_store_result(r->pool, sec);
1363 APACHELOG(APLOG_ERR, r,
1364 "Failed to get MySQL result structure : %s", mysql_error(sec->dbh));
1367 switch (mysql_num_rows(result)) {
1369 APACHELOG(APLOG_INFO, r, "User not found");
1373 sql_row = mysql_fetch_row(result);
1374 /* ensure we have a row, and non NULL value */
1375 if (!sql_row || !sql_row[0]) {
1376 APACHELOG(APLOG_INFO, r,
1377 "No row returned or NULL value: %s", mysql_error(sec->dbh));
1381 rv = check_password(password, sql_row[0], r, sec);
1384 APACHELOG(APLOG_INFO, r,
1385 "Authentication failed for user %s", user);
1391 APACHELOG(APLOG_ERR, r,
1392 "Multiple password rows returned - this is what is known, in the industry, as a Bad Thing");
1397 APACHELOG(APLOG_CRIT, r, "Can't happen - dropped out of switch!");
1401 /* Has a look to see if the given user is a member of the named group.
1402 * Returns 0 if user is not a part of the group, 1 if he is, -1 on error.
1404 static int mysql_check_group(request_rec *r, char *user, char *group, mysql_auth_config_rec *sec)
1406 char *auth_table = "mysql_auth", *auth_group_field="groups", *auth_group_clause="";
1408 char *esc_user = mysql_escape(user, r->pool);
1409 char *esc_group = mysql_escape(group, r->pool);
1412 char *auth_user_field = "username";
1415 APACHELOG(APLOG_ERR, r, "No group specified");
1419 if (sec->group_table) {
1420 auth_table = sec->group_table;
1423 if (sec->user_field)
1425 auth_user_field = sec->user_field;
1428 if (sec->group_user_field) {
1429 auth_user_field = sec->group_user_field;
1432 if (sec->group_field) {
1433 auth_group_field = sec->group_field;
1435 if (sec->group_where_clause) {
1436 auth_group_clause = sec->group_where_clause;
1439 APACHELOG(APLOG_DEBUG, r,
1440 "Making group query with auth_table=[%s], auth_user_field=[%s], "
1441 "esc_user=[%s], esc_group=[%s], auth_group_field=[%s], where_clause=[%s]",
1442 auth_table, auth_user_field, esc_user, esc_group, auth_group_field,auth_group_clause);
1444 query = (char *) PSTRCAT(r->pool, "SELECT count(*) FROM ", auth_table,
1445 " WHERE ", auth_user_field, "='", esc_user, "'",
1446 " and FIND_IN_SET('", esc_group, "',", auth_group_field, ")",
1447 auth_group_clause, NULL);
1449 APACHELOG(APLOG_DEBUG, r, "Group query created; [%s]", query);
1452 APACHELOG(APLOG_CRIT, r,
1453 "Failed to create group-check query - ran out of memory!");
1456 if (safe_mysql_query(r, query, sec)) {
1457 APACHELOG(APLOG_CRIT, r, "Group query failed!");
1460 result = safe_mysql_store_result(r->pool, sec);
1461 if (!result || (row=mysql_fetch_row(result))==NULL || !row[0]) {
1462 APACHELOG(APLOG_CRIT, r, "Store result failed - erp!");
1466 return atoi(row[0]);
1469 /* The apache-called function. Note that this function says nothing about
1470 * what the user should be allowed to do - merely that they have proved they
1471 * are who they say they are. Return OK if the user has proved their
1472 * identity, DECLINED if we are not taking any responsibility for them, or
1473 * some Apache error if there was a problem.
1475 int mysql_authenticate_basic_user(request_rec *r)
1477 mysql_auth_config_rec *sec = (mysql_auth_config_rec *) ap_get_module_config(r->per_dir_config, &auth_mysql_module);
1478 conn_rec *c = r->connection;
1479 const char *sent_pw;
1482 APACHELOG(APLOG_DEBUG, r, "Handling an authentication request for section %s", sec->dir);
1485 for (res = 0; res < 512; res++)
1487 if (sec->sacrificial_lamb[res] == '\0')
1489 sec->sacrificial_lamb[res] = 'n';
1491 if (!isgraph(sec->sacrificial_lamb[res]))
1493 sec->sacrificial_lamb[res] = ' ';
1496 sec->sacrificial_lamb[511] = '\0';
1498 syslog(LOG_DEBUG, "The contents of the lamb are %s", sec->sacrificial_lamb);
1501 if (!sec->enable_mysql_auth) {
1502 APACHELOG(APLOG_DEBUG, r,
1503 "Not running mod-auth-mysql for %s - disabled", r->unparsed_uri);
1507 /* use MySQL auth only if we have a database */
1508 if (!auth_db_name && !sec->db_name) {
1509 APACHELOG(APLOG_ERR, r,
1510 "Failed to run mod-auth-mysql for %s: No database name specified", r->unparsed_uri);
1514 /* obtain sent password */
1515 if ((res = ap_get_basic_auth_pw(r, &sent_pw))) {
1520 APACHELOG(APLOG_DEBUG, r,
1521 "Starting basic user auth for [%s] in %s, child pid %i",
1523 sec->dir, getpid());
1525 APACHELOG(APLOG_DEBUG, r,
1526 "Starting basic user auth for [%s] in %s, child pid %i",
1528 sec->dir, getpid());
1532 switch (mysql_check_user_password(r, r->user, sent_pw, sec)) {
1534 switch (mysql_check_user_password(r, c->user, sent_pw, sec)) {
1537 ap_note_basic_auth_failure(r);
1538 return HTTP_UNAUTHORIZED;
1545 APACHELOG(APLOG_DEBUG, r,
1546 "mysql_check_user_password returned error");
1547 return HTTP_INTERNAL_SERVER_ERROR;
1552 /* Go through a 'requires' line configured for the module, and return OK
1553 * if the user satisfies the line, or some sort of failure return code
1556 int check_mysql_auth_require(char *user, const char *t, request_rec *r)
1558 mysql_auth_config_rec *sec = (mysql_auth_config_rec *) ap_get_module_config(r->per_dir_config, &auth_mysql_module);
1562 w = ap_getword(r->pool, &t, ' ');
1563 /* If they're letting any old authenticated user, we're off the
1566 if (!strcmp(w, "valid-user")) {
1570 /* Checking a list of usernames */
1571 if (!strcmp(w, "user")) {
1573 w = ap_getword_conf(r->pool, &t);
1574 if (!strcmp(user, w)) {
1579 return HTTP_UNAUTHORIZED;
1580 } else if (!strcmp(w, "group")) {
1581 /* This is the prickly one; checking whether the
1582 * user is a member of a listed group.
1586 w = ap_getword_conf(r->pool, &t);
1587 rv = mysql_check_group(r, user, (char *)w, sec);
1591 /* Yep, we're all good */
1596 return HTTP_INTERNAL_SERVER_ERROR;
1599 /* Distinct lack of foundage */
1600 return HTTP_UNAUTHORIZED;
1604 APACHELOG(APLOG_ERR, r, "Invalid argument to require: %s", w);
1605 return HTTP_INTERNAL_SERVER_ERROR;
1608 APACHELOG(APLOG_ERR, r, "CAN'T HAPPEN: Dropped out of the bottom of check_mysql_auth_require!");
1609 return HTTP_INTERNAL_SERVER_ERROR;
1612 /* This is the authorization step. We're presuming that the user has
1613 * successfully negotiated the step of "I am who I say I am", now we're
1614 * checking to see if the user has permission to access this particular
1615 * resource. As with mysql_authenticate_basic_user, above, we return OK if
1616 * the user is fit to proceed, DECLINED if we don't want to make a decision
1617 * either way, HTTP_UNAUTHORIZED if the user is not allowed, or some apache
1618 * error if there was a major problem.
1620 int mysql_check_auth(request_rec *r)
1622 mysql_auth_config_rec *sec = (mysql_auth_config_rec *) ap_get_module_config(r->per_dir_config, &auth_mysql_module);
1624 char *user = r->user;
1626 char *user = r->connection->user;
1628 int m = r->method_number;
1633 const apr_array_header_t *reqs_arr = ap_requires(r);
1635 const array_header *reqs_arr = ap_requires(r);
1639 /* use MySQL auth only if we have a database */
1640 if (!auth_db_name && !sec->db_name) {
1644 /* What do we do if there's no requires line available? Either say
1645 * "bad puppy" if we're king shit, or say "not my problem" otherwise.
1648 if (sec->authoritative) {
1649 APACHELOG(APLOG_ERR, r, "No requires line available");
1650 return HTTP_UNAUTHORIZED;
1656 /* This is an array of all the requires lines which apply to us.
1657 * There may be several, as in the case of something like:
1658 * require user foo bar
1659 * require group wombat
1660 * That is, the user either has to belong to the group 'wombat' or
1661 * be 'foo' or 'bar'.
1662 * We have to check them all. Yuck.
1664 reqs = (require_line *) reqs_arr->elts;
1666 for (x = 0; x < reqs_arr->nelts; x++) {
1667 /* mjp: WTF is this? */
1668 if (!(reqs[x].method_mask & (1 << m))) {
1672 t = reqs[x].requirement;
1674 /* OK, this might seem a little weird. The logic is that,
1675 * if the user is approved, that's sufficient, so we can
1676 * return OK straight away. Alternately, if there's an
1677 * error, we bomb the check and die. The only circumstance
1678 * where we continue looping is when the user didn't pass this
1679 * check, but might pass a future one, so keep looking.
1681 if ((rv = check_mysql_auth_require(user, t, r))
1682 != HTTP_UNAUTHORIZED)
1688 /* We don't know, and we don't really care */
1689 if (!(sec->authoritative)) {
1693 ap_note_basic_auth_failure(r);
1694 return HTTP_UNAUTHORIZED;
1700 static void register_hooks(apr_pool_t *p)
1702 ap_hook_check_user_id(mysql_authenticate_basic_user, NULL, NULL, APR_HOOK_MIDDLE);
1703 ap_hook_auth_checker(mysql_check_auth, NULL, NULL, APR_HOOK_MIDDLE);
1708 module AP_MODULE_DECLARE_DATA auth_mysql_module =
1710 STANDARD20_MODULE_STUFF,
1711 create_mysql_auth_dir_config, /* dir config creater */
1712 NULL, /* dir merger --- default is to override */
1713 NULL, /* server config */
1714 NULL, /* merge server config */
1715 mysql_auth_cmds, /* command apr_table_t */
1716 register_hooks /* register hooks */
1719 module auth_mysql_module =
1721 STANDARD_MODULE_STUFF,
1722 mysql_auth_init_handler, /* initializer */
1723 create_mysql_auth_dir_config, /* dir config creater */
1724 NULL, /* dir merger --- default is to override */
1725 NULL, /* server config */
1726 NULL, /* merge server config */
1727 mysql_auth_cmds, /* command table */
1728 NULL, /* handlers */
1729 NULL, /* filename translation */
1730 mysql_authenticate_basic_user, /* check_user_id */
1731 mysql_check_auth, /* check auth */
1732 NULL, /* check access */
1733 NULL, /* type_checker */
1734 NULL, /* pre-run fixups */
1736 #if MODULE_MAGIC_NUMBER >= 19970103
1737 ,NULL /* header parser */
1739 #if MODULE_MAGIC_NUMBER >= 19970719
1740 ,NULL /* child_init */
1742 #if MODULE_MAGIC_NUMBER >= 19970728
1743 ,NULL /* child_exit */
1745 #if MODULE_MAGIC_NUMBER >= 19970902
1746 ,NULL /* post read-request */