Increase priority
[debian/mod-auth-mysql] / mod_auth_mysql.c
1 /*
2  * Copyright (c) 2001 by J. R. Westmoreland <jr@jrw.org>
3  * Portions Copyright (c) 2002-2004 by Matthew Palmer <mpalmer@debian.org>
4  *
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>
11  *
12  * This version maintained by Matthew Palmer <mpalmer@debian.org>
13  *
14  * Please read the INSTALL and USAGE files for further information.
15  * 
16  * 2004-02-01 MURAKAMI, takeshi <takeshi@softagency.co.jp>
17  * add port, socket
18  * 2004-02-07 MURAKAMI, takeshi <takeshi@softagency.co.jp>
19  * apache2
20  * 2004-09-20 Joseph Walton <joe@kafsemo.org>
21  * SHA1 hash support
22  */
23
24 #define AUTH_MYSQL_VERSION "4.3.9"
25
26 #include "config.h"
27
28 #ifdef APACHE2
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)
35 #else
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)
42 #endif
43
44 #include <httpd.h>
45 #include <http_config.h>
46 #include <http_core.h>
47 #include <http_protocol.h>
48 #include <http_log.h>
49 #ifdef APACHE2
50 #include "http_request.h"   /* for ap_hook_(check_user_id | auth_checker)*/
51 #include <apr_md5.h>
52 #include <apr_sha1.h>
53 #else
54 #include <ap_md5.h>
55 #include <ap_sha1.h>
56 #endif
57
58 #include <mysql.h>
59 #include <errmsg.h>
60 #include <mysqld_error.h>
61
62 #ifdef HAVE_CRYPT_H
63 #include <crypt.h>
64 #endif
65
66 #ifndef TRUE
67 #define TRUE 1
68 #endif
69 #ifndef FALSE
70 #define FALSE 0
71 #endif
72
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.
75  */
76 static char     *auth_db_host = NULL,
77                 *auth_db_name = NULL,
78                 *auth_db_user = NULL,
79                 *auth_db_pwd = NULL;
80
81 static int      auth_db_override = 1;
82
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;
87
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).
90  */
91
92 #define PLAINTEXT_ENCRYPTION_FLAG       1<<0
93 #ifdef CRYPT_DES
94 #define CRYPT_DES_ENCRYPTION_FLAG       1<<1
95 #endif
96 #define MYSQL_ENCRYPTION_FLAG           1<<2
97 #ifdef CRYPT_MD5
98 #define CRYPT_MD5_ENCRYPTION_FLAG       1<<3
99 #endif
100 #define PHP_MD5_ENCRYPTION_FLAG         1<<4
101 #ifdef HAVE_CRYPT_H
102 #define CRYPT_ENCRYPTION_FLAG           1<<5
103 #endif
104 #define SHA1SUM_ENCRYPTION_FLAG         1<<6
105
106 static int check_no_encryption(const char *passwd, char *enc_passwd)
107 {
108         return (!strcmp(passwd, enc_passwd));
109 }
110
111
112 #ifdef CRYPT_DES
113 static int check_crypt_des_encryption(const char *passwd, char *enc_passwd)
114 {
115         /* Ensure that MD5 passwords aren't checked here */
116         if (!strncmp(enc_passwd, "$1$", 3)) {
117                 return 0;
118         }
119         return (!strcmp(crypt(passwd, enc_passwd), enc_passwd));
120 }
121 #endif
122
123 #ifdef CRYPT_MD5
124 static int check_crypt_MD5_encryption(const char *passwd, char *enc_passwd)
125 {
126         /* Make sure only MD5 passwords are checked */
127         if (strncmp(enc_passwd, "$1$", 3)) {
128                 return 0;
129         }
130         return (!strcmp(crypt(passwd, enc_passwd), enc_passwd));
131 }
132 #endif
133
134 #ifdef HAVE_CRYPT_H
135 static int check_crypt_encryption(const char *passwd, char *enc_passwd)
136 {
137         return (!strcmp(crypt(passwd, enc_passwd), enc_passwd));
138 }
139 #endif
140
141 char hex_digit(char c)
142 {
143         if (c < 10) {
144                 return c+'0';
145         } else {
146                 return c-10+'a';
147         }
148 }
149
150 static char *md5_hex_hash(const char *pass)
151 {
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];
156         int i;
157 #ifdef APACHE2
158         apr_md5_ctx_t ct;
159
160         apr_md5_init(&ct);
161         apr_md5_update(&ct, pass, strlen(pass));
162         apr_md5_final(hash, &ct);
163 #else
164         AP_MD5_CTX ct;
165
166         ap_MD5Init(&ct);
167         ap_MD5Update(&ct, pass, strlen(pass));
168         ap_MD5Final(hash, &ct);
169 #endif
170         
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;
175         }
176         for (i = 0; i < 32; i++) {
177                 real_hash[i] = hex_digit(real_hash[i]);
178         }
179         real_hash[32] = '\0';
180
181         return real_hash;
182 }
183
184 static int check_PHP_MD5_encryption(const char *passwd, char *enc_passwd)
185 {
186         return (!strcmp(md5_hex_hash(passwd), enc_passwd));
187 }
188
189 static char *sha1_hex_hash(const char *passwd)
190 {
191         int i;
192
193 #ifdef APACHE2
194         apr_sha1_ctx_t ct;
195         char hash[APR_SHA1_DIGESTSIZE];
196         static char real_hash[APR_SHA1_DIGESTSIZE * 2 + 1];
197
198         apr_sha1_init(&ct);
199         apr_sha1_update(&ct, passwd, strlen(passwd));
200         apr_sha1_final(hash, &ct);
201 #else
202         AP_SHA1_CTX ct;
203         char hash[SHA_DIGESTSIZE];
204         static char real_hash[SHA_DIGESTSIZE * 2 + 1];
205
206         ap_SHA1Init(&ct);
207         ap_SHA1Update(&ct, passwd, strlen(passwd));
208         ap_SHA1Final(hash, &ct);
209 #endif
210
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;
215         }
216         for (i = 0; i < sizeof(real_hash); i++) {
217                 real_hash[i] = hex_digit(real_hash[i]);
218         }
219         real_hash[sizeof(real_hash)-1] = '\0';
220
221         return real_hash;
222 }
223
224 static int check_SHA1Sum_encryption(const char *passwd, char *enc_passwd)
225 {
226         return (!strcmp(sha1_hex_hash(passwd), enc_passwd));
227 }
228
229
230 static int check_mysql_encryption(const char *passwd, char *enc_passwd)
231 {
232         char scrambled_passwd[32];
233         
234         make_scrambled_password(scrambled_passwd, passwd);
235         return (!strcmp(scrambled_passwd, enc_passwd));
236 }
237
238 typedef struct {
239         char *name;
240         int (*check_function)(const char *passwd, char *enc_passwd);
241         int flag;
242 } encryption_type_entry;
243
244 encryption_type_entry supported_encryption_types[] = {
245         { "Plaintext",          check_no_encryption,                    PLAINTEXT_ENCRYPTION_FLAG },
246 #if CRYPT_DES
247         { "Crypt_DES",          check_crypt_des_encryption,             CRYPT_DES_ENCRYPTION_FLAG },
248 #endif
249         { "MySQL",              check_mysql_encryption,                 MYSQL_ENCRYPTION_FLAG },
250 #if CRYPT_MD5
251         { "Crypt_MD5",          check_crypt_MD5_encryption,             CRYPT_MD5_ENCRYPTION_FLAG },
252 #endif
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 */
257         { NULL,                 NULL,                                   0 }
258 };
259
260 static int get_encryption_flag(const char *name)
261 {
262         register encryption_type_entry *ete=supported_encryption_types;
263         
264         while (ete->name) {
265                 if (!strcmp(ete->name, name)) {
266                         return ete->flag;
267                 }
268                 ete++;
269         }
270         return 0;
271 }
272
273 /* end of support for general-purpose encryption schemes */
274
275 /* Per-directory configuration structure.  One of these is created for each
276  * <Directory>...</Directory> and .htaccess file which requests authentication
277  */
278 typedef struct {
279         char *dir;
280
281         char *db_host;
282         char *db_socket;
283         unsigned int db_port;
284         char *db_user;
285         char *db_pwd;
286         char *db_name;
287         
288         MYSQL *dbh;
289
290         /* Boolean options */
291         unsigned char persistent;
292         unsigned char enable_mysql_auth;
293
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.
297          */
298         unsigned char dbh_error_lastchance;
299
300         char *user_table;
301         char *group_table;
302
303         char *user_field;
304         char *password_field;
305         char *group_field;
306         char *group_user_field;
307         char *group_where_clause;
308         char *password_where_clause;
309                 
310         int encryption_types;
311         unsigned char using_encryption_types;
312
313         unsigned char allow_empty_passwords;
314         unsigned char authoritative;
315
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...
320          */
321         char sacrificial_lamb[15];
322
323 } mysql_auth_config_rec;
324
325 module auth_mysql_module;
326
327 #ifdef APACHE2
328 static apr_status_t
329 #else
330 static void
331 #endif
332 auth_mysql_cleanup(void *ptr)
333 {
334         mysql_auth_config_rec *sec = ptr;
335
336         if (sec->dbh) {
337 #ifdef DEBUG
338                 syslog(LOG_DEBUG, "MAMDEBUG: Closing MySQL connection");
339 #endif
340                 mysql_close(sec->dbh);
341                 sec->dbh = NULL;
342         }
343 }
344
345 /* Do the magic required when the module is first loaded.
346  */
347 #ifdef APACHE2
348 void mysql_auth_init_handler(server_rec *s, apr_pool_t *p)
349 #else
350 void mysql_auth_init_handler(server_rec *s, pool *p)
351 #endif
352 {
353 #ifdef APACHE2
354 #else
355 #if MODULE_MAGIC_NUMBER >= 19980527
356     ap_add_version_component("AuthMySQL/" AUTH_MYSQL_VERSION);
357 #endif
358 #endif
359 }
360
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.
364  */
365 #ifdef APACHE2
366 void *create_mysql_auth_dir_config(apr_pool_t *p, char *d)
367 #else
368 void *create_mysql_auth_dir_config(pool *p, char *d)
369 #endif
370 {
371 #ifdef DEBUG
372         int i;
373 #endif
374
375         mysql_auth_config_rec *sec = (mysql_auth_config_rec *) PCALLOC(p, sizeof(mysql_auth_config_rec));
376         
377 #ifdef DEBUG
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));
381 #endif
382
383         sec->db_name = sec->db_socket = sec->db_user = sec->db_pwd = NULL;
384
385         sec->dbh = 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.
389          */
390 #ifdef APACHE2
391         apr_pool_cleanup_register(p, sec, auth_mysql_cleanup, apr_pool_cleanup_null);
392 #else
393         ap_register_cleanup(p, sec, auth_mysql_cleanup, ap_null_cleanup);
394 #endif
395
396         sec->dir = d;
397         
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;
402         
403         sec->authoritative = 1;
404         sec->allow_empty_passwords = 1;
405
406         sec->dbh_error_lastchance = 0;
407
408 #ifdef DEBUG
409         syslog(LOG_DEBUG, "MAMDEBUG: Enabling MySQL auth by default");
410 #endif
411         sec->enable_mysql_auth = 1;
412
413 #ifdef CRYPT_DES
414         sec->encryption_types = CRYPT_DES_ENCRYPTION_FLAG;
415         sec->using_encryption_types = 0;
416 #else
417         sec->encryption_types = 0;
418         sec->using_encryption_types = 0;
419 #endif
420
421         sec->db_port = -1;
422         
423 #ifdef DEBUG
424         syslog(LOG_DEBUG, "MAMDEBUG: Persistent is now ON");
425 #endif
426         sec->persistent = 1;
427
428 #ifdef DEBUG
429         for (i = 0; i < 15; i++)
430         {
431                 sec->sacrificial_lamb[i] = i % 10 + '0';
432         }
433 #endif
434         
435         return sec;
436 }
437
438 /* Helper function to make some decisions about whether to use crypted
439  * passwords in response to "AuthMySQL_Encrypted_Passwords on" in a config
440  * file.
441  * XXX DEPRECATED XXX
442  */
443 static const char *set_crypted_password_flag(cmd_parms *cmd, void *sconf, int arg)
444 {
445         mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
446         
447         if (sec->using_encryption_types) {
448                 /* This setting is ignored if we're using Encryption_Types */
449                 return NULL;
450         }
451 #ifdef CRYPT_DES
452         if (arg) {
453                 sec->encryption_types |= CRYPT_DES_ENCRYPTION_FLAG;
454         } else {
455                 sec->encryption_types &= ~CRYPT_DES_ENCRYPTION_FLAG;
456                 if (!sec->encryption_types) {
457                         sec->encryption_types = PLAINTEXT_ENCRYPTION_FLAG;
458                 }
459         }
460 #endif
461
462         return NULL;
463 }
464
465 /* Equivalent to set_crypted_password_flag above, except that this time we're
466  * talking about MySQL-style scrambled passwords instead.
467  * XXX DEPRECATED XXX
468  */
469 static const char *set_scrambled_password_flag(cmd_parms *cmd, void *sconf, int arg)
470 {
471         mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
472
473         if (sec->using_encryption_types) {
474                 /* This setting is ignored if we're using Encryption_Types */
475                 return NULL;
476         }
477         if (arg) {
478                 sec->encryption_types |= MYSQL_ENCRYPTION_FLAG;
479         } else {
480                 sec->encryption_types &= ~MYSQL_ENCRYPTION_FLAG;
481                 if (!sec->encryption_types) {
482                         sec->encryption_types = PLAINTEXT_ENCRYPTION_FLAG;
483                 }
484         }
485         return NULL;
486 }
487
488 /* Ensure that any string passed through us won't unduly upset the MySQL
489  * server when passed in as part of a query.
490  */
491 #ifdef APACHE2
492 static char *mysql_escape(char *str, apr_pool_t *p)
493 #else
494 static char *mysql_escape(char *str, pool *p)
495 #endif
496 {
497         char *dest;
498         
499         if (!str) {
500                 return NULL;
501         }
502
503         dest = (char *) PALLOC(p, strlen(str) * 2 + 1);
504         if (!dest) {
505                 return str;
506         }
507         
508         mysql_escape_string(dest, str, strlen(str));
509         
510         return dest;
511 }
512
513 /* Config helper to set the server-wide default database name.
514  */
515 static const char *set_auth_mysql_db(cmd_parms * parms, void *dummy, const char *db)
516 {
517         auth_db_name = (char *)db;
518         return NULL;
519 }
520
521 /* Config helper to set the server-wide default database host.
522  */
523 static const char *set_auth_mysql_host(cmd_parms *parms, void *dummy, const char *host)
524 {
525         auth_db_host = (char *) host;
526         return NULL;
527 }
528
529 /* Config helper to set server-wide defaults for database parameters.
530  */
531 static const char *set_auth_mysql_info(cmd_parms * parms, void *dummy, const char *host, const char *user, const char *pwd)
532 {
533         if (*host != '.') {
534                 auth_db_host = (char *) host;
535         }
536
537         if (*user != '.') {
538                 auth_db_user = (char *)user;
539         }
540
541         if (*pwd != '.') {
542                 auth_db_pwd = (char *)pwd;
543         }
544
545         return NULL;
546 }
547
548 /* Config helper to set the server-wide default database username.
549  */
550 static const char *set_auth_mysql_user(cmd_parms *parms, void *dummy, const char *user)
551 {
552         auth_db_user = (char *)user;
553         return NULL;
554 }
555
556 /* Config helper to set the server-wide default database password (coupled to
557  * the user specified above).
558  */
559 static const char *set_auth_mysql_pwd(cmd_parms *parms, void *dummy, const char *pwd)
560 {
561         auth_db_pwd = (char *)pwd;
562         return NULL;
563 }
564
565 /* Set the server-wide database server socket.
566  */
567 static const char *set_auth_mysql_socket(cmd_parms *parms, void *dummy, const char *sock)
568 {
569         auth_db_socket = (char *)socket;
570         return NULL;
571 }
572
573 /* Set the server-wide database server port.
574  */
575 static const char *set_auth_mysql_port(cmd_parms *parms, void *dummy, const char *port)
576 {
577         auth_db_port = (unsigned int) atoi(port);
578         return NULL;
579 }
580
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
585  * config thing.
586  */
587 static const char *set_auth_mysql_override(cmd_parms *parms, void *dummy, int arg)
588 {
589         auth_db_override = arg;
590         return NULL;
591 }
592
593 /* Config helper to set a selected encryption type.
594  */
595 static const char *set_encryption_types(cmd_parms *cmd, void *sconf, const char *arg)
596 {
597         mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
598
599         int new_encryption_flag = get_encryption_flag(arg);
600
601         if (!new_encryption_flag) {
602                 APACHELOG(APLOG_ERR, cmd, "Unsupported encryption type: %s", arg);
603                 return NULL;
604         }
605
606         if (!sec->using_encryption_types) {
607                 sec->encryption_types = 0;
608                 sec->using_encryption_types = 1;
609         }
610         
611         sec->encryption_types |= new_encryption_flag;
612         
613         return NULL;
614 }
615
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.
619  */
620 static const char *set_non_persistent(cmd_parms *cmd, void *sconf, int arg)
621 {
622         mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
623
624         sec->persistent = !arg;
625         APACHELOG(APLOG_DEBUG, cmd, "set_non_persistent: Setting persistent in %s to %i", sec->dir, sec->persistent);
626         return NULL;
627 }
628
629 static const char *set_persistent(cmd_parms *cmd, void *sconf, int arg)
630 {
631         mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
632
633         sec->persistent = arg;
634         APACHELOG(APLOG_DEBUG, cmd, "set_persistent: Setting persistent in %s to %i", sec->dir, sec->persistent);
635         return NULL;
636 }
637
638 static const char *enable_mysql(cmd_parms *cmd, void *sconf, int arg)
639 {
640         mysql_auth_config_rec *sec = (mysql_auth_config_rec *) sconf;
641
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);
644         return NULL;
645 }
646
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.
649  */
650
651 #ifdef APACHE2
652 static
653 command_rec mysql_auth_cmds[] = {
654    AP_INIT_TAKE3( "Auth_MySQL_Info",    set_auth_mysql_info,
655                   NULL,
656                   RSRC_CONF,    "host, user and password of the MySQL database" ),
657
658    AP_INIT_TAKE1( "AuthMySQL_DefaultHost",      set_auth_mysql_host,
659                   NULL,
660                   RSRC_CONF,    "Default MySQL host" ),
661
662    AP_INIT_TAKE1( "AuthMySQL_DefaultUser",      set_auth_mysql_user,
663                   NULL,
664                   RSRC_CONF,    "Default MySQL user" ),
665
666    AP_INIT_TAKE1( "AuthMySQL_DefaultPassword",  set_auth_mysql_pwd,
667                   NULL,
668                   RSRC_CONF,    "Default MySQL password" ),
669
670    AP_INIT_TAKE1( "Auth_MySQL_DefaultPort",     set_auth_mysql_port,
671                   NULL,
672                   RSRC_CONF,    "Default MySQL server port" ),
673         
674    AP_INIT_TAKE1( "Auth_MySQL_DefaultSocket",   set_auth_mysql_socket,
675                   NULL,
676                   RSRC_CONF,    "Default MySQL server socket" ),
677                 
678    AP_INIT_TAKE1( "Auth_MySQL_General_DB",      set_auth_mysql_db,
679                   NULL,
680                   RSRC_CONF,    "default database for MySQL authentication" ),
681
682    AP_INIT_TAKE1( "AuthMySQL_DefaultDB",        set_auth_mysql_db,
683                   NULL,
684                   RSRC_CONF,    "default database for MySQL authentication" ),
685
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" ),
689
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" ),
693
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" ),
697
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" ),
701
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" ),
705
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" ),
709
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" ),
713
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" ),
717
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" ),
721
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" ),
725
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" ),
729
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" ),
733
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" ),
737
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" ),
741
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." ),
745
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" ),
749
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." ),
753
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" ),
757
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" ),
761
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" ),
765
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)." ),
769
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)." ),
773
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." ),
777
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." ),
781
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." ),
785
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." ),
789
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." ),
793
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." ),
797
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." ),
801
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." ),
805
806    AP_INIT_FLAG( "AuthMySQL_AllowOverride",             set_auth_mysql_override,
807                  NULL,
808                  RSRC_CONF,     "Allow directory overrides of configuration" ),
809
810    AP_INIT_FLAG( "Auth_MySQL_Encrypted_Passwords",      set_crypted_password_flag,
811                  NULL,
812                  OR_AUTHCFG,    "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." ),
813
814    AP_INIT_FLAG( "AuthMySQL_Encrypted_Passwords",       set_crypted_password_flag,
815                   NULL,
816                   OR_AUTHCFG,   "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." ),
817
818    AP_INIT_FLAG( "Auth_MySQL_Scrambled_Passwords",      set_scrambled_password_flag,
819                  NULL,
820                  OR_AUTHCFG,    "When 'on' the password in the password table are taken to be scramble()d using mySQL's password() function." ),
821
822    AP_INIT_FLAG( "AuthMySQL_Scrambled_Passwords",       set_scrambled_password_flag,
823                  NULL,
824                  OR_AUTHCFG,    "When 'on' the password in the password table are taken to be scramble()d using mySQL's password() function." ),
825
826    AP_INIT_ITERATE( "Auth_MySQL_Encryption_Types",      set_encryption_types,
827                   NULL,
828                   OR_AUTHCFG,   "Encryption types to use" ),
829
830    AP_INIT_ITERATE( "AuthMySQL_Encryption_Types",               set_encryption_types,
831                   NULL,
832                   OR_AUTHCFG,   "Encryption types to use" ),
833
834    AP_INIT_FLAG( "Auth_MySQL_Non_Persistent",           set_non_persistent,
835                  NULL,
836                  OR_AUTHCFG,    "Use non-persistent MySQL links" ),
837
838    AP_INIT_FLAG( "AuthMySQL_Persistent",                set_persistent,
839                  NULL,
840                  OR_AUTHCFG,    "Use non-persistent MySQL links" ),
841
842    AP_INIT_FLAG( "Auth_MySQL",          enable_mysql,
843                  NULL,
844                  OR_AUTHCFG,    "Enable MySQL authentication" ),
845
846    AP_INIT_FLAG( "AuthMySQL",           enable_mysql,
847                  NULL,
848                  OR_AUTHCFG,    "Enable MySQL authentication" ),
849
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" ),
853
854   { NULL }
855 };
856 #else
857 command_rec mysql_auth_cmds[] = {
858         { "Auth_MySQL_Info",                    set_auth_mysql_info,
859           NULL,
860           RSRC_CONF,    TAKE3,  "host, user and password of the MySQL database" },
861
862         { "AuthMySQL_DefaultHost",              set_auth_mysql_host,
863           NULL,
864           RSRC_CONF,    TAKE1,  "Default MySQL host" },
865
866         { "AuthMySQL_DefaultUser",              set_auth_mysql_user,
867           NULL,
868           RSRC_CONF,    TAKE1,  "Default MySQL user" },
869
870         { "AuthMySQL_DefaultPassword",          set_auth_mysql_pwd,
871           NULL,
872           RSRC_CONF,    TAKE1,  "Default MySQL password" },
873
874         { "Auth_MySQL_DefaultPort",             set_auth_mysql_port,
875           NULL,
876           RSRC_CONF,    TAKE1,  "Default MySQL server port" },
877         
878         { "Auth_MySQL_DefaultSocket",           set_auth_mysql_socket,
879           NULL,
880           RSRC_CONF,    TAKE1,  "Default MySQL server socket" },
881                 
882         { "Auth_MySQL_General_DB",              set_auth_mysql_db,
883           NULL,
884           RSRC_CONF,    TAKE1,  "default database for MySQL authentication" },
885           
886         { "AuthMySQL_DefaultDB",                set_auth_mysql_db,
887           NULL,
888           RSRC_CONF,    TAKE1,  "default database for MySQL authentication" },
889
890         { "AuthMySQL_Host",                     ap_set_string_slot,
891           (void *) XtOffsetOf(mysql_auth_config_rec, db_host),
892           OR_AUTHCFG,   TAKE1,  "database host" },
893
894         { "Auth_MySQL_Host",                    ap_set_string_slot,
895           (void *) XtOffsetOf(mysql_auth_config_rec, db_host),
896           OR_AUTHCFG,   TAKE1,  "database host" },
897
898         { "Auth_MySQL_Socket",                  ap_set_string_slot,
899           (void *) XtOffsetOf(mysql_auth_config_rec, db_socket),
900           OR_AUTHCFG,   TAKE1,  "database host socket" },
901
902         { "Auth_MySQL_Port",                    ap_set_string_slot,
903           (void *) XtOffsetOf(mysql_auth_config_rec, db_port),
904           OR_AUTHCFG,   TAKE1,  "database host socket" },
905
906         { "Auth_MySQL_Username",                ap_set_string_slot,
907           (void *) XtOffsetOf(mysql_auth_config_rec, db_user),
908           OR_AUTHCFG,   TAKE1,  "database user" },
909           
910         { "AuthMySQL_User",                     ap_set_string_slot,
911           (void *) XtOffsetOf(mysql_auth_config_rec, db_user),
912           OR_AUTHCFG,   TAKE1,  "database user" },
913           
914         { "Auth_MySQL_Password",                ap_set_string_slot,
915           (void *) XtOffsetOf(mysql_auth_config_rec, db_pwd),
916           OR_AUTHCFG,   TAKE1,  "database password" },
917           
918         { "AuthMySQL_Password",                 ap_set_string_slot,
919           (void *) XtOffsetOf(mysql_auth_config_rec, db_pwd),
920           OR_AUTHCFG,   TAKE1,  "database password" },
921           
922         { "Auth_MySQL_DB",                      ap_set_string_slot,
923           (void *) XtOffsetOf(mysql_auth_config_rec, db_name),
924           OR_AUTHCFG,   TAKE1,  "database name" },
925           
926         { "AuthMySQL_DB",                       ap_set_string_slot,
927           (void *) XtOffsetOf(mysql_auth_config_rec, db_name),
928           OR_AUTHCFG,   TAKE1,  "database name" },
929           
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" },
933           
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" },
937           
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." },
941           
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" },
945           
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." },
949
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" },
953
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" },
957
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" },
961
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)." },
965           
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)." },
969           
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." },
973
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." },
977
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." },
981
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." },
985
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." },
989
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." },
993
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." },
997
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." },
1001
1002         { "AuthMySQL_AllowOverride",            set_auth_mysql_override,
1003           NULL,
1004           RSRC_CONF,    FLAG,   "Allow directory overrides of configuration" },
1005
1006         { "Auth_MySQL_Encrypted_Passwords",     set_crypted_password_flag,
1007           NULL,
1008           OR_AUTHCFG,   FLAG,   "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." },
1009
1010         { "AuthMySQL_Encrypted_Passwords",      set_crypted_password_flag,
1011           NULL,
1012           OR_AUTHCFG,   FLAG,   "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." },
1013
1014         { "Auth_MySQL_Scrambled_Passwords",     set_scrambled_password_flag,
1015           NULL,
1016           OR_AUTHCFG,   FLAG,   "When 'on' the password in the password table are taken to be scramble()d using mySQL's password() function." },
1017
1018         { "AuthMySQL_Scrambled_Passwords",      set_scrambled_password_flag,
1019           NULL,
1020           OR_AUTHCFG,   FLAG,   "When 'on' the password in the password table are taken to be scramble()d using mySQL's password() function." },
1021
1022         { "Auth_MySQL_Encryption_Types",        set_encryption_types,
1023           NULL,
1024           OR_AUTHCFG,   ITERATE,"Encryption types to use" },
1025
1026         { "AuthMySQL_Encryption_Types",         set_encryption_types,
1027           NULL,
1028           OR_AUTHCFG,   ITERATE,"Encryption types to use" },
1029
1030         { "Auth_MySQL_Non_Persistent",          set_non_persistent,
1031           NULL,
1032           OR_AUTHCFG,   FLAG,   "Use non-persistent MySQL links" },
1033
1034         { "AuthMySQL_Persistent",               set_persistent,
1035           NULL,
1036           OR_AUTHCFG,   FLAG,   "Use non-persistent MySQL links" },
1037
1038         { "Auth_MySQL",                         enable_mysql,
1039           NULL,
1040           OR_AUTHCFG,   FLAG,   "Enable MySQL authentication" },
1041
1042         { "AuthMySQL",                          enable_mysql,
1043           NULL,
1044           OR_AUTHCFG,   FLAG,   "Enable MySQL authentication" },
1045
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" },
1049
1050         { NULL }
1051 };
1052
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." },
1055 */
1056
1057
1058 #endif
1059
1060 #ifdef APACHE2
1061 static apr_status_t
1062 #else
1063 static void
1064 #endif
1065 auth_mysql_result_cleanup(void *result)
1066 {
1067         mysql_free_result((MYSQL_RES *) result);
1068 }
1069
1070 #ifdef APACHE2
1071 static void note_cleanups_for_mysql_auth_result(apr_pool_t *p, MYSQL_RES * result)
1072 #else
1073 static void note_cleanups_for_mysql_auth_result(pool *p, MYSQL_RES * result)
1074 #endif
1075 {
1076 #ifdef APACHE2
1077         apr_pool_cleanup_register(p, (void *) result, auth_mysql_result_cleanup, auth_mysql_result_cleanup);
1078 #else
1079         ap_register_cleanup(p, (void *) result, auth_mysql_result_cleanup, auth_mysql_result_cleanup);
1080 #endif
1081
1082 }
1083
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.
1087  */
1088 static int open_auth_dblink(request_rec *r, mysql_auth_config_rec *sec)
1089 {
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;
1095
1096         APACHELOG(APLOG_DEBUG, r, "Opening DB connection for %s", sec->dir);
1097         
1098         if (auth_db_host) {
1099                 host = auth_db_host;
1100         }
1101
1102         if (auth_db_socket)
1103         {
1104                 socket = auth_db_socket;
1105         }
1106
1107         if (auth_db_port != -1)
1108         {
1109                 port = auth_db_port;
1110         }
1111         
1112         if (auth_db_override)
1113         {
1114                 if (sec->db_socket)
1115                 {
1116                         socket = sec->db_socket;
1117                 }
1118         
1119                 if (sec->db_port != -1)
1120                 {
1121                         port = sec->db_port;
1122                 }
1123                 
1124                 if (sec->db_host)
1125                 {
1126                         host = sec->db_host;
1127                 }
1128
1129                 if (sec->db_user) {
1130                         user = sec->db_user;
1131                 }
1132
1133                 if (sec->db_pwd) {
1134                         pwd = sec->db_pwd;
1135                 }
1136
1137                 if (sec->db_name) {
1138                         dbname = sec->db_name;
1139                 }
1140         }
1141
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;
1147         }
1148
1149         /* MySQL likes to throw the odd SIGPIPE now and then - ignore it for now */
1150         sigpipe_handler = signal(SIGPIPE, SIG_IGN);
1151                 
1152         sec->dbh = mysql_init(NULL);
1153         
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);
1159                 sec->dbh = NULL;
1160                 return errno;
1161         }
1162
1163         signal(SIGPIPE, sigpipe_handler);
1164         
1165         APACHELOG(APLOG_DEBUG, r, "Persistent in %s is %i", sec->dir, sec->persistent);
1166
1167         if (!sec->persistent) {
1168                 APACHELOG(APLOG_DEBUG, r, "Registering non-persistent for %s", sec->dir);
1169 #ifdef APACHE2
1170                 apr_pool_cleanup_register(r->pool, sec, auth_mysql_cleanup, apr_pool_cleanup_null);
1171 #else
1172                 ap_block_alarms();
1173                 ap_register_cleanup(r->pool, sec, auth_mysql_cleanup, ap_null_cleanup);
1174                 ap_unblock_alarms();
1175 #endif
1176         }
1177
1178         /* W00t!  We made it! */
1179         return 0;
1180 }
1181
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.
1187  */
1188 static int safe_mysql_query(request_rec *r, char *query, mysql_auth_config_rec *sec)
1189 {
1190         int error = CR_UNKNOWN_ERROR;
1191
1192         APACHELOG(APLOG_DEBUG, r, "sec->dbh in %s is %p", sec->dir, sec->dbh);
1193         if (sec->dbh_error_lastchance)
1194         {
1195                 APACHELOG(APLOG_DEBUG, r, "Last chance, bub");
1196         }
1197         else
1198         {
1199                 APACHELOG(APLOG_DEBUG, r, "Ordinary query");
1200         }
1201         
1202         if (!sec->dbh) {
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);
1208                         return error;
1209                 }
1210
1211                 APACHELOG(APLOG_DEBUG, r,
1212                         "Correctly opened a new DB connection");
1213         }
1214
1215         APACHELOG(APLOG_DEBUG, r,
1216                 "Running query: [%s]", query);
1217
1218         if (mysql_query(sec->dbh, query)) {
1219                 error = mysql_errno(sec->dbh);
1220                 
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)
1227                 {
1228                         /* No matter what error, we're moving out */
1229                         return error;
1230                 }
1231                 else if (error == CR_SERVER_LOST || error == CR_SERVER_GONE_ERROR)
1232                 {
1233                         /* Try again, once more only */
1234                         sec->dbh_error_lastchance = 1;
1235                         sec->dbh = NULL;
1236                         APACHELOG(APLOG_DEBUG, r, "Retrying query");
1237                         return safe_mysql_query(r, query, sec);
1238                 }
1239                 else
1240                 {
1241                         return error;
1242                 }
1243         }
1244
1245         return 0;
1246 }
1247
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.
1252  */
1253 #ifdef APACHE2
1254 static MYSQL_RES *safe_mysql_store_result(apr_pool_t *p, mysql_auth_config_rec *sec)
1255 #else
1256 static MYSQL_RES *safe_mysql_store_result(pool *p, mysql_auth_config_rec *sec)
1257 #endif
1258 {
1259         MYSQL_RES *result;
1260 #ifdef APACHE2
1261 #else   
1262         ap_block_alarms();
1263 #endif
1264
1265         result = mysql_store_result(sec->dbh);
1266 #ifdef DEBUG
1267         syslog(LOG_DEBUG, "MAMDEBUG: Got %p for result", result);
1268 #endif
1269
1270         if (result) {
1271                 note_cleanups_for_mysql_auth_result(p, result);
1272         }
1273 #ifdef APACHE2
1274 #else
1275         ap_unblock_alarms();
1276 #endif
1277
1278         return result;
1279 }
1280
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.
1284  */
1285 static int check_password(const char *plaintext, char *hashed, request_rec *r, mysql_auth_config_rec *sec)
1286 {
1287         encryption_type_entry *ete;
1288         
1289         /* empty password support */
1290         if (sec->allow_empty_passwords && !strlen(hashed)) {
1291                 APACHELOG(APLOG_INFO, r, "User successful on empty password");
1292                 return 1;
1293         }
1294                         
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");
1301                                 return 1;
1302                         }
1303                 }
1304         }
1305         APACHELOG(APLOG_DEBUG, r, "User failed all encryption types");
1306         return 0;
1307 }
1308
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.
1312  */
1313 static int mysql_check_user_password(request_rec *r, char *user, const char *password, mysql_auth_config_rec *sec)
1314 {
1315         char *auth_table = "mysql_auth", *auth_user_field = "username",
1316                 *auth_password_field = "passwd", *auth_password_clause = "";
1317         char *query;
1318         char *esc_user = mysql_escape(user, r->pool);
1319         MYSQL_RES *result;
1320         MYSQL_ROW sql_row;
1321         int rv;
1322                 
1323         if (sec->user_table) {
1324                 auth_table = sec->user_table;
1325         }
1326         if (sec->user_field) {
1327                 auth_user_field = sec->user_field;
1328         }
1329         if (sec->password_field) {
1330                 auth_password_field = sec->password_field;
1331         }
1332         if (sec->password_where_clause) {
1333                 auth_password_clause = sec->password_where_clause;
1334         }
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);
1339
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);
1344         if (!query) {
1345                 APACHELOG(APLOG_ERR, r,
1346                         "Failed to create query string - we're in deep poopy");
1347                 return -1;
1348         }
1349
1350         if ((rv = safe_mysql_query(r, query, sec))) {
1351                 if (sec->dbh)
1352                 {
1353                         APACHELOG(APLOG_ERR, r,
1354                                 "Query call failed: %s (%i)", mysql_error(sec->dbh), rv);
1355                 }
1356
1357                 APACHELOG(APLOG_DEBUG, r, "Failed query was: [%s]", query);
1358                 return -1;
1359         }
1360
1361         result = safe_mysql_store_result(r->pool, sec);
1362         if (!result) {
1363                 APACHELOG(APLOG_ERR, r,
1364                         "Failed to get MySQL result structure : %s", mysql_error(sec->dbh));
1365                 return -1;
1366         }
1367         switch (mysql_num_rows(result)) {
1368                 case 0:
1369                         APACHELOG(APLOG_INFO, r, "User not found");
1370                         return 0;
1371                         break;
1372                 case 1:
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));
1378                                 return -1;
1379                         }
1380                         
1381                         rv = check_password(password, sql_row[0], r, sec);
1382                         if (rv == 0)
1383                         {
1384                                 APACHELOG(APLOG_INFO, r,
1385                                         "Authentication failed for user %s", user);
1386                         }
1387                         return rv;
1388                         break;
1389
1390                 default:
1391                         APACHELOG(APLOG_ERR, r,
1392                                 "Multiple password rows returned - this is what is known, in the industry, as a Bad Thing");
1393                         return -1;
1394                         break;
1395         }
1396
1397         APACHELOG(APLOG_CRIT, r, "Can't happen - dropped out of switch!");
1398         return -1;
1399 }
1400
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.
1403  */
1404 static int mysql_check_group(request_rec *r, char *user, char *group, mysql_auth_config_rec *sec)
1405 {
1406         char *auth_table = "mysql_auth", *auth_group_field="groups", *auth_group_clause="";
1407         char *query;
1408         char *esc_user = mysql_escape(user, r->pool);
1409         char *esc_group = mysql_escape(group, r->pool);
1410         MYSQL_RES *result;
1411         MYSQL_ROW row;
1412         char *auth_user_field = "username";
1413
1414         if (!group) {
1415                 APACHELOG(APLOG_ERR, r, "No group specified");
1416                 return 0;
1417         }
1418         
1419         if (sec->group_table) {
1420                 auth_table = sec->group_table;
1421         }
1422
1423         if (sec->user_field)
1424         {
1425                 auth_user_field = sec->user_field;
1426         }
1427
1428         if (sec->group_user_field) {
1429                 auth_user_field = sec->group_user_field;
1430         }
1431                 
1432         if (sec->group_field) {
1433                 auth_group_field = sec->group_field;
1434         }
1435         if (sec->group_where_clause) {
1436                 auth_group_clause = sec->group_where_clause;
1437         }
1438
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);
1443
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);
1448
1449         APACHELOG(APLOG_DEBUG, r, "Group query created; [%s]", query);
1450                 
1451         if (!query) {
1452                 APACHELOG(APLOG_CRIT, r,
1453                         "Failed to create group-check query - ran out of memory!");
1454                 return -1;
1455         }
1456         if (safe_mysql_query(r, query, sec)) {
1457                 APACHELOG(APLOG_CRIT, r, "Group query failed!");
1458                 return -1;
1459         }
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!");
1463                 return -1;
1464         }
1465
1466         return atoi(row[0]);
1467 }
1468
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.
1474  */
1475 int mysql_authenticate_basic_user(request_rec *r)
1476 {
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;
1480         int res;
1481
1482         APACHELOG(APLOG_DEBUG, r, "Handling an authentication request for section %s", sec->dir);
1483
1484 #ifdef DEBUG
1485         for (res = 0; res < 512; res++)
1486         {
1487                 if (sec->sacrificial_lamb[res] == '\0')
1488                 {
1489                         sec->sacrificial_lamb[res] = 'n';
1490                 }
1491                 if (!isgraph(sec->sacrificial_lamb[res]))
1492                 {
1493                         sec->sacrificial_lamb[res] = ' ';
1494                 }
1495         }
1496         sec->sacrificial_lamb[511] = '\0';
1497         
1498         syslog(LOG_DEBUG, "The contents of the lamb are %s", sec->sacrificial_lamb);
1499 #endif
1500
1501         if (!sec->enable_mysql_auth) {
1502                 APACHELOG(APLOG_DEBUG, r,
1503                         "Not running mod-auth-mysql for %s - disabled", r->unparsed_uri);
1504                 return DECLINED;
1505         }
1506
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);
1511                 return DECLINED;
1512         }
1513
1514         /* obtain sent password */
1515         if ((res = ap_get_basic_auth_pw(r, &sent_pw))) {
1516                 return res;
1517         }
1518
1519 #ifdef APACHE2
1520         APACHELOG(APLOG_DEBUG, r,
1521                 "Starting basic user auth for [%s] in %s, child pid %i",
1522                 r->user,
1523                 sec->dir, getpid());
1524 #else
1525         APACHELOG(APLOG_DEBUG, r,
1526                 "Starting basic user auth for [%s] in %s, child pid %i",
1527                 c->user,
1528                 sec->dir, getpid());
1529 #endif
1530
1531 #ifdef APACHE2
1532         switch (mysql_check_user_password(r, r->user, sent_pw, sec)) {
1533 #else
1534         switch (mysql_check_user_password(r, c->user, sent_pw, sec)) {
1535 #endif
1536                 case 0:
1537                         ap_note_basic_auth_failure(r);
1538                         return HTTP_UNAUTHORIZED;
1539                         break;
1540                 case 1:
1541                         return OK;
1542                         break;
1543                 case -1:
1544                 default:
1545                         APACHELOG(APLOG_DEBUG, r,
1546                                 "mysql_check_user_password returned error");
1547                         return HTTP_INTERNAL_SERVER_ERROR;
1548                         break;
1549         }
1550 }
1551
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
1554  * otherwise.
1555  */
1556 int check_mysql_auth_require(char *user, const char *t, request_rec *r)
1557 {
1558         mysql_auth_config_rec *sec = (mysql_auth_config_rec *) ap_get_module_config(r->per_dir_config, &auth_mysql_module);
1559         const char *w;
1560         int rv;
1561         
1562         w = ap_getword(r->pool, &t, ' ');
1563         /* If they're letting any old authenticated user, we're off the
1564          * hook!
1565          */
1566         if (!strcmp(w, "valid-user")) {
1567                 return OK;
1568         }
1569
1570         /* Checking a list of usernames */
1571         if (!strcmp(w, "user")) {
1572                 while (t[0]) {
1573                         w = ap_getword_conf(r->pool, &t);
1574                         if (!strcmp(user, w)) {
1575                                 return OK;
1576                         }
1577                 }
1578                 /* Not found */
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.
1583                  */
1584                 while (t[0])
1585                 {
1586                         w = ap_getword_conf(r->pool, &t);
1587                         rv = mysql_check_group(r, user, (char *)w, sec);
1588                         
1589                         if (rv == 1)
1590                         {
1591                                 /* Yep, we're all good */
1592                                 return OK;
1593                         }
1594                         else if (rv == -1)
1595                         {
1596                                 return HTTP_INTERNAL_SERVER_ERROR;
1597                         }
1598                 }
1599                 /* Distinct lack of foundage */
1600                 return HTTP_UNAUTHORIZED;
1601         }
1602         else
1603         {
1604                 APACHELOG(APLOG_ERR, r, "Invalid argument to require: %s", w);
1605                 return HTTP_INTERNAL_SERVER_ERROR;
1606         }
1607
1608         APACHELOG(APLOG_ERR, r, "CAN'T HAPPEN: Dropped out of the bottom of check_mysql_auth_require!");
1609         return HTTP_INTERNAL_SERVER_ERROR;
1610 }
1611
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.
1619  */
1620 int mysql_check_auth(request_rec *r)
1621 {
1622         mysql_auth_config_rec *sec = (mysql_auth_config_rec *) ap_get_module_config(r->per_dir_config, &auth_mysql_module);
1623 #ifdef APACHE2
1624         char *user = r->user;
1625 #else
1626         char *user = r->connection->user;
1627 #endif
1628         int m = r->method_number;
1629         int rv;
1630         register int x;
1631         const char *t;
1632 #ifdef APACHE2
1633         const apr_array_header_t *reqs_arr = ap_requires(r);
1634 #else
1635         const array_header *reqs_arr = ap_requires(r);
1636 #endif
1637         require_line *reqs;
1638
1639         /* use MySQL auth only if we have a database */
1640         if (!auth_db_name && !sec->db_name) {
1641                 return DECLINED;
1642         }
1643
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.
1646          */
1647         if (!reqs_arr) {
1648                 if (sec->authoritative) {
1649                         APACHELOG(APLOG_ERR, r, "No requires line available");
1650                         return HTTP_UNAUTHORIZED;
1651                 } else {
1652                         return DECLINED;
1653                 }
1654         }
1655
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.
1663          */
1664         reqs = (require_line *) reqs_arr->elts;
1665
1666         for (x = 0; x < reqs_arr->nelts; x++) {
1667                 /* mjp: WTF is this? */
1668                 if (!(reqs[x].method_mask & (1 << m))) {
1669                         continue;
1670                 }
1671
1672                 t = reqs[x].requirement;
1673
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.
1680                  */
1681                 if ((rv = check_mysql_auth_require(user, t, r))
1682                         != HTTP_UNAUTHORIZED)
1683                 {
1684                         return rv;
1685                 }
1686         }
1687
1688         /* We don't know, and we don't really care */
1689         if (!(sec->authoritative)) {
1690                 return DECLINED;
1691         }
1692
1693         ap_note_basic_auth_failure(r);
1694         return HTTP_UNAUTHORIZED;
1695 }
1696
1697
1698
1699 #ifdef APACHE2
1700 static void register_hooks(apr_pool_t *p)
1701 {
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);
1704 }
1705 #endif
1706
1707 #ifdef APACHE2
1708 module AP_MODULE_DECLARE_DATA auth_mysql_module =
1709 {
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 */
1717 };
1718 #else
1719 module auth_mysql_module =
1720 {
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 */
1735         NULL                            /* logger */
1736 #if MODULE_MAGIC_NUMBER >= 19970103
1737         ,NULL                           /* header parser */
1738 #endif
1739 #if MODULE_MAGIC_NUMBER >= 19970719
1740         ,NULL                           /* child_init */
1741 #endif
1742 #if MODULE_MAGIC_NUMBER >= 19970728
1743         ,NULL                           /* child_exit */
1744 #endif
1745 #if MODULE_MAGIC_NUMBER >= 19970902
1746         ,NULL                           /* post read-request */
1747 #endif
1748 };
1749 #endif