On Thu, Feb 14, 2008 at 11:38:50AM +0100, Markus Liljergren wrote:
> I'm using pure MD5, which is probably why it fails. I would very much
> appreciate that patch.
>
Here's a new diff that merges the previous patch from FreeBSD with
another one i found that provides a few new things:
- plain MD5 password encryption support
- the ability to specify host:port or socket-path
- additional where clause for sql statements
- better logging of errors
Please test and provide feedback as i didn't do more than building it.
Kind regards,
Simon
Index: Makefile
===================================================================
RCS file: /cvs/ports/www/mod_auth_mysql/Makefile,v
retrieving revision 1.12
diff -u -p -r1.12 Makefile
--- Makefile 15 Sep 2007 20:38:22 -0000 1.12
+++ Makefile 15 Feb 2008 15:17:50 -0000
@@ -4,7 +4,7 @@ COMMENT= Apache MySQL authentication mod
VERSION= 3.2
DISTNAME= mod_auth_mysql-${VERSION}
-PKGNAME= ${DISTNAME}p2
+PKGNAME= ${DISTNAME}p3
CATEGORIES= www
HOMEPAGE= http://sourceforge.net/projects/mod-auth-mysql
Index: patches/patch-Makefile
===================================================================
RCS file: /cvs/ports/www/mod_auth_mysql/patches/patch-Makefile,v
retrieving revision 1.1.1.1
diff -u -p -r1.1.1.1 patch-Makefile
--- patches/patch-Makefile 15 Sep 2002 19:28:36 -0000 1.1.1.1
+++ patches/patch-Makefile 15 Feb 2008 15:17:50 -0000
@@ -1,7 +1,7 @@
$OpenBSD: patch-Makefile,v 1.1.1.1 2002/09/15 19:28:36 jakob Exp $
---- Makefile Mon Sep 10 15:12:08 2001
-+++ Makefile Tue Sep 10 20:17:09 2002
-@@ -3,7 +3,7 @@
+--- Makefile.orig Mon Sep 10 16:12:08 2001
++++ Makefile Fri Feb 15 16:14:36 2008
+@@ -3,7 +3,7 @@ APXSFLAGS =
DSO = mod_auth_mysql.so
SRCS = mod_auth_mysql.c
HDRS = mod_auth_mysql.h
Index: patches/patch-README
===================================================================
RCS file: patches/patch-README
diff -N patches/patch-README
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-README 15 Feb 2008 15:17:50 -0000
@@ -0,0 +1,45 @@
+$OpenBSD$
+--- README.orig Mon Sep 10 16:11:37 2001
++++ README Thu Feb 14 13:32:37 2008
+@@ -193,3 +193,41 @@ Author
+ ------
+
+ Email: J. R. Westmoreland <[EMAIL PROTECTED]>
++
++
++
++===================================================================
++Additional feature
++------------------
++I added next feature to mod_auth_mysql-2.20:
++
++1. socket/port specify
++2. where clauses
++3. MD5
++4. to write error log
++5. little change Makefile.in
++
++Email: [EMAIL PROTECTED]
++URL: http://www.softagency.co.jp/mysql/modauth.html
++
++
++Change log of this patch
++
++2001-10-23
++ change: select count(*) -> select count(id)
++ add: write to apache error log file.
++ [EMAIL PROTECTED]
++
++2001-10-03
++ change: note_basic_auth_failure() -> ap_note_basic_auth_failure()
++ add: MD5 auth
++ [EMAIL PROTECTED]
++
++2001-04-20
++ add: WHERE clause
++ [EMAIL PROTECTED]
++
++2000-04
++ add: port/socket
++ [EMAIL PROTECTED]
++
Index: patches/patch-USAGE
===================================================================
RCS file: /cvs/ports/www/mod_auth_mysql/patches/patch-USAGE,v
retrieving revision 1.1.1.1
diff -u -p -r1.1.1.1 patch-USAGE
--- patches/patch-USAGE 15 Sep 2002 19:28:36 -0000 1.1.1.1
+++ patches/patch-USAGE 15 Feb 2008 15:17:50 -0000
@@ -1,12 +1,119 @@
$OpenBSD: patch-USAGE,v 1.1.1.1 2002/09/15 19:28:36 jakob Exp $
---- USAGE Mon Sep 10 15:11:37 2001
-+++ USAGE Tue Sep 10 21:17:21 2002
-@@ -31,7 +31,7 @@
+--- USAGE.orig Thu Feb 14 10:37:00 2008
++++ USAGE Thu Feb 14 13:32:37 2008
+@@ -28,6 +28,7 @@ can skip to the next phase. Otherwise:
+ NOTE: You *don't* have to have this table in a seperate database, you
+ can skip creating a new database and use an existing database if it fits
+ your needs.
++
2. Create the auth table, e.g.:
prompt> mysql http_auth
mysql> create table mysql_auth (
-- -> username char(25),
-+ -> username char(25) not null,
- -> passwd char(25),
- -> groups char(25),
- -> primary key (username)
+@@ -48,6 +49,7 @@ can skip to the next phase. Otherwise:
+ that, you should have one row in the username/passwd table, and
+ multiple rows in the username/group table, one for each group
+ the user is in.
++
+ 3. Insert the information into the table. Both the username and group fields
+ are plaintext, whereas the password field should contain standard UNIX DES
+ encrypted passwords (this can be overriden using a directive as well, but
+@@ -62,40 +64,58 @@ Telling apache to protect the page using that informat
+ server, and/or you need to specify a password for that user, you'd need
+ to add the following line somewhere in your httpd.conf (doesn't really
+ matter where):
++
+ Auth_MySQL_Info <host> <user> <password>
++
+ This information can *only* be specified in the server's httpd.conf, since
+ it's used server-wide.
++
++ you can specify socket name or port number in <host>.
++ ex.1: Auth_MySQL_Info 'localhost:/tmp/mysql.sock' <user> <password>
++ ex.2: Auth_MySQL_Info 'remotesrv:3333' <user> <password>
++
+ 2. If you're going to use mainly one MySQL database for all of your pages,
+ you should probably add the following line to your httpd.conf as well:
++
+ Auth_MySQL_General_DB <database_name>
++
+ The database can be set on a per-directory basis using a different
+ directive in .htaccess, as mentioned later in this file.
++
+ 3. Create (or update) a file named .htaccess inside the directory you would
+ like to protect. Here are a few simple .htaccess files (full
+ documentation about the various possible non-MySQL-auth specific
directives
+ can be obtained from the apache docs):
++
+
+ (I) Protect your company's financial information (not recommended to put on
+ the web:) to any user that's in the SQL auth table:
++
+ AuthName My Company's Financial Information <-- the realm name, use
some informative name
+ AuthType Basic <-- keep it that way
+ require valid-user <-- allow any valid user
to access
+
++
+ (II) Allow access only to specific users:
++
+ AuthName My Company's Financial Information <-- the realm name, use
some informative name
+ AuthType Basic <-- keep it that way
+ require user johndoe devnull <-- let only johndoe and
devnull access
+
++
+ (III) Allow only members of group 'executives' access the information:
++
+ AuthName My Company's Financial Information <-- the realm name, use
some informative name
+ AuthType Basic <-- keep it that way
+ require group executives <-- allow only members
of this group to access
+
+ Note that with Apache 1.3, you would have to encapsulate the AuthName
+ with double quotes if it contains spaces, e.g.
++
+ AuthName "My Company's Financial Information"
+
+
++
+ 4. Take a look at the following directives, and see if you need to
+ use any of them:
+
+@@ -130,16 +150,25 @@ Auth_MySQL_Empty_Passwords on/off
+ the page by just specifying their username without any password checking.
+ If this is 'Off', they would be denied access. Default: On.
+
+-Auth_MySQL_Encryption_Types [Plaintext, Crypt_DES, Crypt_MD5, MySQL]
++Auth_MySQL_Encryption_Types [Plaintext, Crypt_DES, Crypt_MD5, MySQL, MD5]
+ This directive tells the authentication module which encryption type(s)
+ to use. It overrides the Auth_MySQL_Scrambled_Passwords and
+ Auth_MySQL_Encrypted_Passwords directives if it appears after them.
+ More than one encryption type may be specified, to instruct the module to
+ check each password through more than one encryption scheme. For example,
++
+ Auth_MySQL_Encryption_Types Plaintext Crypt_DES
++
+ will instruct the module to check each password both as-is, and through
+ DES crypt.
+
++ Crypt_MD5: if your system support crypt function which can handle md5,
++ apache compare password strings by using md5.
++
++ MD5: if you choise MD5, your passwd field must have md5 encrypted strings.
++ in this case, md5 password are stored into MySQL, and,
++ your system does not need to have crypt_md5 function.
++
+ Auth_MySQL_Encrypted_Passwords on/off
+ Whether or not to use standard UNIX DES encrypted passwords. If turned
+ on, the module expects the password field to contain standard UNIX DES
+@@ -178,3 +207,13 @@ Auth_MYSQL on/off
+ authentication modules (e.g. the flatfile auth module). If it's on,
+ and a database name was specified - the MySQL module will be used for
+ authentication.
++
++
++Auth_MySQL_Where "strings..."
++ if you set:
++
++ Auth_MySQL_Where "active='Y'"
++
++ then, mod_mysql send next SQL statment to MySQL server:
++
++ SELECT passwd FROM mysql_auth WHERE username='id' AND active='Y'
Index: patches/patch-mod_auth_mysql_c
===================================================================
RCS file: patches/patch-mod_auth_mysql_c
diff -N patches/patch-mod_auth_mysql_c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-mod_auth_mysql_c 15 Feb 2008 15:17:50 -0000
@@ -0,0 +1,318 @@
+$OpenBSD$
+--- mod_auth_mysql.c.orig Mon Sep 10 16:12:08 2001
++++ mod_auth_mysql.c Fri Feb 15 16:01:37 2008
+@@ -10,6 +10,34 @@
+ * and Brent Metz <[EMAIL PROTECTED]>
+ *
+ * Please read the README and USAGE for further information.
++
++** changes (2.20) **
++2001-04-20
++ add: WHERE clause
++ [EMAIL PROTECTED]
++
++2001-10-03
++ change: note_basic_auth_failure() -> ap_note_basic_auth_failure()
++ add: MD5 auth
++ [EMAIL PROTECTED]
++
++2001-10-23
++ change: select count(*) -> select count(id)
++ add: write to apache error log file.
++ [EMAIL PROTECTED]
++
++2002-07-03
++ fix: Auth_MySQL_Empty_Passwords could not work correctly. original bug.
++ change: Auth_MySQL_Empty_Passwords default is `off'
++
++2002-07-04
++ fix: some fears for empty passwd
++
++*****
++
++2001-12-08
++ merge my patch for 2.20 and mod_auth_mysql 3.1
++ [EMAIL PROTECTED]
+ */
+
+ #define AUTH_MYSQL_VERSION "3.1"
+@@ -35,6 +63,8 @@
+
+ static MYSQL auth_sql_server, *mysql_auth = NULL;
+ static char *auth_db_host = NULL, *auth_db_name = NULL, *auth_db_user = NULL,
*auth_db_pwd = NULL;
++static char *socket_file = NULL, *tmp_host = NULL;
++static unsigned int port_num = 0;
+
+ #define MYSQL_ERROR(mysql) ((mysql)?(mysql_error(mysql)):"mysql server has
gone away")
+
+@@ -45,6 +75,7 @@ static char *auth_db_host = NULL, *auth_db_name = NULL
+ #define CRYPT_DES_ENCRYPTION_FLAG 1<<1
+ #define MYSQL_ENCRYPTION_FLAG 1<<2
+ #define CRYPT_MD5_ENCRYPTION_FLAG 1<<3
++#define MD5_ENCRYPTION_FLAG 1<<4
+
+ static int check_no_encryption(const char *passwd, char *enc_passwd)
+ {
+@@ -68,8 +99,14 @@ static int check_crypt_MD5_encryption(const char *pass
+
+ static int check_mysql_encryption(const char *passwd, char *enc_passwd)
+ {
+- char scrambled_passwd[32];
+-
++ /* Make more then big enough */
++ char scrambled_passwd[256];
++
++#if MYSQL_VERSION_ID >= 40000
++ make_scrambled_password_323(scrambled_passwd, passwd);
++ if (strcmp(scrambled_passwd, enc_passwd) == 0) return 1;
++#endif /* MYSQL_VERSION_ID >= 40000 */
++
+ make_scrambled_password(scrambled_passwd, passwd);
+ return (!strcmp(scrambled_passwd, enc_passwd));
+ }
+@@ -90,6 +127,7 @@ encryption_type_entry supported_encryption_types[] = {
+ #if MD5_CRYPT
+ { "Crypt_MD5", check_crypt_MD5_encryption,
CRYPT_MD5_ENCRYPTION_FLAG },
+ #endif
++ { "MD5", check_no_encryption,
MD5_ENCRYPTION_FLAG },
+ /* add additional encryption types below */
+ { NULL, NULL,
0 }
+ };
+@@ -128,6 +166,8 @@ typedef struct {
+ unsigned char assume_authoritative;
+ unsigned char enable_mysql_auth;
+ unsigned char non_persistent;
++
++ char *where;
+ } mysql_auth_config_rec;
+
+ module auth_mysql_module;
+@@ -149,13 +189,15 @@ void *create_mysql_auth_dir_config(pool *p, char *d)
+ sec->user_field = sec->password_field = sec->group_field = NULL;
+
+ sec->assume_authoritative = 1;
+- sec->allow_empty_passwords = 1;
++ sec->allow_empty_passwords = 0;
+ sec->enable_mysql_auth = 1;
+
+ sec->encryption_types = CRYPT_DES_ENCRYPTION_FLAG;
+ sec->encryption_types_initialized = 0;
+
+ sec->non_persistent = 0;
++
++ sec->where = NULL;
+
+ return sec;
+ }
+@@ -223,8 +265,27 @@ static const char *my_set_string_slot(cmd_parms *cmd,
+
+ static const char *set_auth_mysql_info(cmd_parms * parms, void *dummy, char
*host, char *user, char *pwd)
+ {
++ size_t len;
++ int i;
++ /* host:3306 or host:/tmp/mysql.sock */
++
+ if (*host != '.') {
+- auth_db_host = host;
++ len = strlen(host) + 2;
++ tmp_host = (char *)calloc(len, sizeof(char));
++ strlcpy(tmp_host, host, len);
++
++ for (i=0; i<strlen(host); i++) {
++ if ( *(host + i) == ':' ) {
++ tmp_host[i] = '\0';
++
++ if ( *( host + i + 1 ) == '/' )
++ socket_file = (host + i + 1);
++ else
++ port_num = (unsigned int)atoi( (host +
i + 1) );
++ }
++ }
++
++ auth_db_host = tmp_host;
+ }
+ if (*user != '.') {
+ auth_db_user = user;
+@@ -286,6 +347,9 @@ command_rec mysql_auth_cmds[] = {
+ { "Auth_MySQL",
my_set_mysql_auth_flag, NULL, OR_AUTHCFG, FLAG, "Enable
(on) or disable (off) MySQL authentication." },
+ { "Auth_MySQL_Encryption_Types", my_set_encryption_types,
NULL, OR_AUTHCFG, ITERATE,"Encryption types to use" },
+ { "Auth_MySQL_Non_Persistent", my_set_non_persistent,
NULL, OR_AUTHCFG, FLAG, "Use non-persistent MySQL links" },
++ { "Auth_MySQL_Where", my_set_string_slot,
++ (void *) XtOffsetOf(mysql_auth_config_rec, where),
++ OR_AUTHCFG, TAKE1, "WHERE clause" },
+ { NULL }
+ };
+
+@@ -388,7 +452,12 @@ static void open_auth_dblink(request_rec *r, mysql_aut
+ }
+ if (name != NULL) { /* open an SQL link */
+ /* link to the MySQL database and register its [EMAIL
PROTECTED] */
++#if MYSQL_VERSION_ID >= 40000
++ mysql_init(&auth_sql_server);
++ mysql_auth = mysql_real_connect(&auth_sql_server, auth_db_host,
user, pwd, name, 0, NULL, 0);
++#else /* MYSQL_VERSION_ID < 40000 */
+ mysql_auth = mysql_connect(&auth_sql_server, auth_db_host,
user, pwd);
++#endif /* MYSQL_VERSION_ID < 40000 */
+ if (sec->non_persistent && mysql_auth) {
+ note_cleanups_for_mysql_auth(r->pool, mysql_auth);
+ }
+@@ -460,6 +529,7 @@ static int mysql_check_user_password(request_rec *r, c
+ MYSQL_RES *result;
+ MYSQL_ROW sql_row;
+ encryption_type_entry *ete;
++ conn_rec *c = r->connection;
+
+ if (sec->user_table) {
+ auth_table = sec->user_table;
+@@ -470,49 +540,89 @@ static int mysql_check_user_password(request_rec *r, c
+ if (sec->password_field) {
+ auth_password_field = sec->password_field;
+ }
+- query = (char *) pstrcat(r->pool, "select ", auth_password_field, "
from ", auth_table,
+- " where ", auth_user_field, "='", esc_user,
"'", NULL);
+- if (!query) {
+- return -1;
++
++ if (sec->where && strlen(sec->where)>0 ) {
++ if (sec->encryption_types == MD5_ENCRYPTION_FLAG)
++ query = (char *) pstrcat(r->pool, "SELECT ",
auth_password_field, ",MD5('", password, "') FROM ", auth_table,
++ " WHERE ", auth_user_field, "='", esc_user, "' AND
", sec->where, NULL);
++ else
++ query = (char *) pstrcat(r->pool, "SELECT ",
auth_password_field, " FROM ", auth_table,
++ " WHERE ", auth_user_field, "='", esc_user, "' AND
", sec->where, NULL);
++ } else {
++ if (sec->encryption_types == MD5_ENCRYPTION_FLAG)
++ query = (char *) pstrcat(r->pool, "SELECT ",
auth_password_field, ",MD5('", password, "') FROM ", auth_table,
++ " WHERE ", auth_user_field, "='", esc_user, "'",
NULL);
++ else
++ query = (char *) pstrcat(r->pool, "SELECT ",
auth_password_field, " FROM ", auth_table,
++ " WHERE ", auth_user_field, "='", esc_user, "'",
NULL);
+ }
+- if (safe_mysql_query(r, query, sec)) {
++ if (!query || safe_mysql_query(r, query, sec) ||
++ !(result = safe_mysql_store_result(r->pool))) {
++ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
++ "MySQL auth: can not check user %s, unknown error was
occured: %s", c->user, r->uri);
+ return -1;
+ }
+- result = safe_mysql_store_result(r->pool);
+- if (!result) {
+- return -1;
+- }
+ switch (mysql_num_rows(result)) {
+ case 0:
++ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
++ "MySQL auth: user %s not found: %s", c->user,
r->uri);
+ return 0;
+ break;
+ case 1:
+ sql_row = mysql_fetch_row(result);
+ /* ensure we have a row, and non NULL value */
+ if (!sql_row || !sql_row[0]) {
++ ap_log_rerror(APLOG_MARK,
APLOG_NOERRNO|APLOG_ERR, r,
++ "MySQL auth: user %s not found, no record:
%s", c->user, r->uri);
+ return -1;
+ }
+
+ /* empty password support */
+- if (sec->allow_empty_passwords && !strlen(sql_row[0])) {
+- return 1;
++ if (sec->allow_empty_passwords && strlen(sql_row[0])==0
&& strlen(password)==0) {
++ ap_log_rerror(APLOG_MARK,
APLOG_NOERRNO|APLOG_WARNING, r,
++ "MySQL auth: user %s: empty passwd login:
\"%s\"",
++ c->user, r->uri);
++ return 1; /* Success */
+ }
+
++ if (!sec->allow_empty_passwords) {
++ if (strlen(password) <1 ||
strlen(sql_row[0])<1) {
++ ap_log_rerror(APLOG_MARK,
APLOG_NOERRNO|APLOG_ERR, r,
++ "MySQL auth: user %s:
authentication failure for \"%s\": empty password",
++ c->user, r->uri);
++ return 0; /*false*/
++ }
++ }
++
+ for (ete=supported_encryption_types; ete->name; ete++) {
+ if (sec->encryption_types & ete->flag) {
+- if (ete->check_function(password,
sql_row[0])) {
+- return 1;
++ if (sec->encryption_types ==
MD5_ENCRYPTION_FLAG) {
++ if (!sql_row[1])
++ return -1;
++ if
(ete->check_function(sql_row[0], sql_row[1]))
++ return 1; /* Success */
+ }
++ else {
++ if
(ete->check_function(password, sql_row[0]))
++ return 1; /* Success */
++ }
+ }
+ }
++ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
++ "MySQL auth: user %s: authentication failure for
\"%s\": invalid password",
++ c->user, r->uri);
+ return 0;
+
+
+ break;
+ default:
++ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
++ "MySQL auth: can not check user %s, unknown error
was occured: %s", c->user, r->uri);
+ return -1;
+ break;
+ }
++ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
++ "MySQL auth: can not check user %s, unknown error was occured: %s",
c->user, r->uri);
+ return -1;
+ }
+
+@@ -537,9 +647,15 @@ static int mysql_check_group(request_rec *r, char *use
+ auth_user_field = sec->user_field;
+ }
+
+- query = pstrcat(r->pool,"select count(*) from ",auth_table,
+- " where ",auth_user_field,"='",esc_user,"'"
+- " and (",groups_query,")",NULL);
++ if (sec->where && strlen(sec->where)>0 )
++ query = pstrcat(r->pool, "SELECT COUNT(", auth_user_field,
++ ") FROM ", auth_table, " WHERE ", auth_user_field,
++ "='", esc_user, "' AND (", groups_query, ") AND ",
++ sec->where, NULL);
++ else
++ query = pstrcat(r->pool, "SELECT COUNT(", auth_user_field,
++ ") FROM ", auth_table, " WHERE ", auth_user_field,
++ "='", esc_user, "' AND (", groups_query, ")", NULL);
+
+ if (!query) {
+ return -1;
+@@ -575,6 +691,10 @@ int mysql_authenticate_basic_user(request_rec *r)
+
+ switch (mysql_check_user_password(r, c->user, sent_pw, sec)) {
+ case 0:
++ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
++ "user %s: authentication failure for \"%s\": %s",
++ c->user, r->uri);
++ ap_note_basic_auth_failure(r);
+ note_basic_auth_failure(r);
+ return AUTH_REQUIRED;
+ break;
+@@ -598,6 +718,7 @@ int mysql_check_auth(request_rec *r)
+ {
+ mysql_auth_config_rec *sec = (mysql_auth_config_rec *)
get_module_config(r->per_dir_config, &auth_mysql_module);
+ char *user = r->connection->user;
++ conn_rec *c = r->connection;
+ int m = r->method_number;
+ int method_restricted = 0;
+ register int x;
+@@ -669,6 +790,10 @@ int mysql_check_auth(request_rec *r)
+ if (!(sec->assume_authoritative)) {
+ return DECLINED;
+ }
++ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
++ "user %s: authentication failure for \"%s\": %s",
++ c->user, r->uri);
++ ap_note_basic_auth_failure(r);
+ note_basic_auth_failure(r);
+ return AUTH_REQUIRED;
+ }