$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;
 }
