Let it work with mysql >= 4.x, add support for socket connections,

plain MD5 encryption and an option to specify additional conditions.

Problem reported by Simon Hamilton, request by Markus Liljergren,
tested by both.
This commit is contained in:
simon 2008-03-25 23:23:15 +00:00
parent f2c0c83c8d
commit 4f5fe03cbf
5 changed files with 485 additions and 10 deletions

View File

@ -1,10 +1,10 @@
# $OpenBSD: Makefile,v 1.12 2007/09/15 20:38:22 merdely Exp $
# $OpenBSD: Makefile,v 1.13 2008/03/25 23:23:15 simon Exp $
COMMENT= Apache MySQL authentication module
VERSION= 3.2
DISTNAME= mod_auth_mysql-${VERSION}
PKGNAME= ${DISTNAME}p2
PKGNAME= ${DISTNAME}p3
CATEGORIES= www
HOMEPAGE= http://sourceforge.net/projects/mod-auth-mysql

View File

@ -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 @@
$OpenBSD: patch-Makefile,v 1.2 2008/03/25 23:23:15 simon Exp $
--- 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

View File

@ -0,0 +1,45 @@
$OpenBSD: patch-README,v 1.1 2008/03/25 23:23:15 simon Exp $
--- 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 <jr@jrw.org>
+
+
+
+===================================================================
+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: takeshi@softagency.co.jp
+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.
+ takeshi@softagency.co.jp
+
+2001-10-03
+ change: note_basic_auth_failure() -> ap_note_basic_auth_failure()
+ add: MD5 auth
+ takeshi@softagency.co.jp
+
+2001-04-20
+ add: WHERE clause
+ takeshi@softagency.co.jp
+
+2000-04
+ add: port/socket
+ takeshi@softagency.co.jp
+

View File

@ -1,7 +1,11 @@
$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 @@
$OpenBSD: patch-USAGE,v 1.2 2008/03/25 23:23:15 simon Exp $
--- USAGE.orig Mon Sep 10 16:11:37 2001
+++ USAGE Wed Mar 26 00:10:48 2008
@@ -28,10 +28,11 @@ 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 (
@ -10,3 +14,111 @@ $OpenBSD: patch-USAGE,v 1.1.1.1 2002/09/15 19:28:36 jakob Exp $
-> 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'

View File

@ -0,0 +1,318 @@
$OpenBSD: patch-mod_auth_mysql_c,v 1.1 2008/03/25 23:23:15 simon Exp $
--- 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 <bmetz@thor.tjhsst.edu>
*
* Please read the README and USAGE for further information.
+
+** changes (2.20) **
+2001-04-20
+ add: WHERE clause
+ takeshi@softagency.co.jp
+
+2001-10-03
+ change: note_basic_auth_failure() -> ap_note_basic_auth_failure()
+ add: MD5 auth
+ takeshi@softagency.co.jp
+
+2001-10-23
+ change: select count(*) -> select count(id)
+ add: write to apache error log file.
+ takeshi@softagency.co.jp
+
+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
+ takeshi@softagency.co.jp
*/
#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 cleanup!@$ */
+#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;
}