· 9 years ago · Sep 18, 2017, 08:12 PM
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 */
76static char *auth_db_host = NULL,
77 *auth_db_name = NULL,
78 *auth_db_user = NULL,
79 *auth_db_pwd = NULL;
80
81static int auth_db_override = 1;
82
83char *tmp_host = NULL;
84char *auth_db_socket = NULL;
85long auth_db_port = -1;
86unsigned 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
106static int check_no_encryption(const char *passwd, char *enc_passwd)
107{
108 return (!strcmp(passwd, enc_passwd));
109}
110
111
112#ifdef CRYPT_DES
113static 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
124static 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
135static int check_crypt_encryption(const char *passwd, char *enc_passwd)
136{
137 return (!strcmp(crypt(passwd, enc_passwd), enc_passwd));
138}
139#endif
140
141char hex_digit(char c)
142{
143 if (c < 10) {
144 return c+'0';
145 } else {
146 return c-10+'a';
147 }
148}
149
150static 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
184static int check_PHP_MD5_encryption(const char *passwd, char *enc_passwd)
185{
186 return (!strcmp(md5_hex_hash(passwd), enc_passwd));
187}
188
189static 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
224static int check_SHA1Sum_encryption(const char *passwd, char *enc_passwd)
225{
226 return (!strcmp(sha1_hex_hash(passwd), enc_passwd));
227}
228
229
230static 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
238typedef struct {
239 char *name;
240 int (*check_function)(const char *passwd, char *enc_passwd);
241 int flag;
242} encryption_type_entry;
243
244encryption_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
260static 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 */
278typedef 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
325module auth_mysql_module;
326
327#ifdef APACHE2
328static apr_status_t
329#else
330static void
331#endif
332auth_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
348void mysql_auth_init_handler(server_rec *s, apr_pool_t *p)
349#else
350void 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
366void *create_mysql_auth_dir_config(apr_pool_t *p, char *d)
367#else
368void *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 */
443static 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 */
469static 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
492static char *mysql_escape(char *str, apr_pool_t *p)
493#else
494static 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 */
515static 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 */
523static 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 */
531static 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 */
550static 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 */
559static 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 */
567static 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 */
575static 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 */
587static 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 */
595static 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 */
620static 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
629static 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
638static 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
652static
653command_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
857command_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
1061static apr_status_t
1062#else
1063static void
1064#endif
1065auth_mysql_result_cleanup(void *result)
1066{
1067 mysql_free_result((MYSQL_RES *) result);
1068}
1069
1070#ifdef APACHE2
1071static void note_cleanups_for_mysql_auth_result(apr_pool_t *p, MYSQL_RES * result)
1072#else
1073static 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 */
1088static 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 */
1188static 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
1254static MYSQL_RES *safe_mysql_store_result(apr_pool_t *p, mysql_auth_config_rec *sec)
1255#else
1256static 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 */
1285static 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 */
1313static 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 */
1404static 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 */
1475int 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 */
1556int 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 */
1620int 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
1700static 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
1708module AP_MODULE_DECLARE_DATA auth_mysql_module =
1709{
1710STANDARD20_MODULE_STUFF,
1711create_mysql_auth_dir_config, /* dir config creater */
1712NULL, /* dir merger --- default is to override */
1713NULL, /* server config */
1714NULL, /* merge server config */
1715mysql_auth_cmds, /* command apr_table_t */
1716register_hooks /* register hooks */
1717};
1718#else
1719module 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