/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* .
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
/*
* http_auth: authentication
*
* Rob McCool & Brian Behlendorf.
*
* Adapted to Apache by rst.
*
* dirkx - Added Authoritative control to allow passing on to lower
* modules if and only if the userid is not known to this
* module. A known user with a faulty or absent password still
* causes an AuthRequired. The default is 'Authoritative', i.e.
* no control is passed along.
*
* ueli@maillink.ch
* - changed from dbm to mysql-support based on the module http_auth_msql
* added features:
* - each virtualhost maintains its own usernamespace
* - you do not need to add hostaliases to the database
* - you can share the username-space between different virtualhosts
* - module can switched on/off per VirtualHost config
* - possebilty to use the Servername or the Hostname for the queries
* if you use hostname you need to add every hostalias to the
* host_info table (usefull for the mod_vhost_alias module)
*
* - allow to configure the table-names and filed-names for the
* sql-queries
*
* for a SQL-tablelayout look in the file htpasswd.sql
*/
/*
* $Id: mod_auth_mysql.c,v 1.12 2007-05-19 19:00:22 ueli Exp $
*/
#define MODULE_RELEASE "1.10"
/* #define SHOW_VERSION_COMPONENT */
#include "apr_lib.h"
#define APR_WANT_STRFUNC
#include "apr_want.h"
#include "apr_strings.h"
#include "apr_dbm.h"
#include "apr_md5.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h" /* for ap_hook_(check_user_id | auth_checker)*/
#include
static char *version = "$Id: mod_auth_mysql.c,v 1.12 2007-05-19 19:00:22 ueli Exp $ 2006 ueli heuer";
typedef struct {
char *db_host; /* host name of db server */
char *db_username; /* user ID to connect to db server */
unsigned int db_port; /* Port of the Mysql Database */
char *db_password; /* password to connect to db server */
char *db_name; /* DB name */
char *db_user_table; /* "user_info" */
char *db_user_field_name; /* "user_name" */
char *db_user_field_passwd; /* "user_passwd" */
char *db_user_field_hostgroup; /* "host_group" */
char *db_user_condition; /* Additional SQL condition */
char *db_host_table; /* "host_info" */
char *db_host_field_hostname; /* "host" */
char *db_host_field_hostgroup; /* "host_group" */
char *db_group_table; /* "user_group" */
char *db_group_field_username; /* "user_name" */
char *db_group_field_groupname; /* "user_group" */
char *db_group_field_hostgroup; /* "host_group" */
char *db_group_condition; /* Additional SQL condition */
int db_keepalive; /* keep connection persistent? */
int auth_dbauthoritative; /* are we authoritative? */
int auth_enable; /* module enabled? */
int auth_virtualhost; /* use VirtualHostHostname in the queris */
/* MYSQL *mysql_handle; /* the mysql-handle */
#ifdef MYSQL_USE_SSL
int db_client_use_ssl; /* MySQL Client SSL flag */
char *db_client_ssl_key; /* MySQL Client Key path */
char *db_client_ssl_cert; /* MySQL Client Certificate path */
char *db_client_ssl_ca; /* MySQL Client Certificate-Authority path */
char *db_client_ssl_cipher; /* MySQL Client Cipher to use */
int db_client_verify_cert; /* MySQL Client Verify Server Cert flag */
#endif
} auth_mysql_config_rec;
/*
* Global handle to db. If not null, assume it is still valid.
* MySQL in recent incarnations will re-connect automatically if the
* connection is closed, so we don't worry about that here.
*/
static MYSQL *mysql_handle = NULL;
/*
* Callback to close mysql handle when necessary. As of Apache 1.2.5,
* there is no way to call this when a child httpd process is terminated.
*/
static apr_status_t mod_auth_mysql_cleanup (void *ignored)
{
if (mysql_handle) {
mysql_close(mysql_handle);
mysql_handle = NULL; /* make sure we don't try to use it later */
}
return 0;
}
/*
* empty function necessary because register_cleanup requires it as one
* of its parameters
*/
static apr_status_t mod_auth_mysql_cleanup_child (void *ignored)
{
return 0;
}
#ifdef MYSQL_USE_SSL
/*
* Check to see if the specified file can be opened for the given
* access.
*/
static int accessible(apr_pool_t *pool, char *fname, int mode)
{
apr_file_t *f = NULL;
if (fname == NULL) {
return 0;
}
if (apr_file_open(&f, fname, mode, APR_OS_DEFAULT, pool) != APR_SUCCESS) {
return 0;
}
apr_file_close(f);
return 1;
}
#endif
/*
* Configuration defaults
*/
static void *create_db_auth_dir_config(apr_pool_t *p, char *d)
{
auth_mysql_config_rec *conf = apr_palloc(p, sizeof(*conf));
/* defaults values */
conf->db_host = "localhost";
conf->db_name = "htpasswd";
conf->db_port = 3306; /* use default port */
conf->db_username = "htpasswd";
conf->db_password = NULL;
conf->db_user_table = "user_info";
conf->db_user_field_name = "user_name";
conf->db_user_field_passwd = "user_passwd";
conf->db_user_field_hostgroup = "host_group";
conf->db_user_condition = "1"; /* do not use any addition condition */
conf->db_host_table = "host_info";
conf->db_host_field_hostname = "host";
conf->db_host_field_hostgroup = "host_group";
conf->db_group_table = "user_group";
conf->db_group_field_username = "user_name";
conf->db_group_field_groupname = "user_group";
conf->db_group_field_hostgroup = "host_group";
conf->db_group_condition = "1"; /* do not use any addition condition */
conf->db_keepalive = 0; /* do not keep persistent connection */
conf->auth_dbauthoritative = 1; /* we are authoritative source for users */
conf->auth_enable = 1; /* module is enabled */
conf->auth_virtualhost = 0; /* we use ServeName / not the virtualhostname */
#ifdef MYSQL_USE_SSL
conf->db_client_use_ssl = 0; /* MySQL Client SSL flag */
conf->db_client_ssl_key = NULL; /* MySQL Client Key path */
conf->db_client_ssl_cert = NULL; /* MySQL Client Certificate path */
conf->db_client_ssl_ca = NULL; /* MySQL Client Certificate-Authority path */
conf->db_client_ssl_cipher = NULL; /* MySQL Client Cipher to use */
conf->db_client_verify_cert = 0; /* MySQL Client Verify Server Cert flag */
#endif
return conf;
}
/*
* Configuration keys and callbacks
*/
static const command_rec db_auth_cmds[] =
{
AP_INIT_TAKE1("AuthMySQLHost", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_host),
OR_AUTHCFG, "DB Server Hostname"),
AP_INIT_TAKE1("AuthMySQLUser", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_username),
OR_AUTHCFG, "DB Username"),
AP_INIT_TAKE1("AuthMySQLPort", ap_set_int_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_port),
OR_AUTHCFG, "DB Port"),
AP_INIT_TAKE1("AuthMySQLPassword", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_password),
OR_AUTHCFG, "DB Password for AuthMySQLUser"),
AP_INIT_TAKE1("AuthMySQLDB", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_name),
OR_AUTHCFG, "DB Name"),
AP_INIT_FLAG("AuthMySQLKeepAlive", ap_set_flag_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_keepalive),
OR_AUTHCFG, "DB connection kept open across requests if On"),
AP_INIT_FLAG("AuthMySQLAuthoritative", ap_set_flag_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, auth_dbauthoritative),
OR_AUTHCFG, "Set to 'no' to allow access control to be passed along to "
"lower modules, if the UserID is not known in this module"),
AP_INIT_FLAG("AuthMySQLEnable", ap_set_flag_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, auth_enable),
OR_AUTHCFG, "Set to 'no' if you want switch off the module for a "
"<VirtualHost>"),
AP_INIT_FLAG("AuthMySQLVirtualHost", ap_set_flag_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, auth_virtualhost),
OR_AUTHCFG, "Set to 'yes' if you want to use the virtualhostname in "
"the queries"),
/* the user table */
AP_INIT_TAKE1("AuthMySQLUserTable", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_user_table),
OR_AUTHCFG, "DB User Table"),
AP_INIT_TAKE1("AuthMySQLTableUserName", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_user_field_name),
OR_AUTHCFG, "Table User Fieldname: user_name"),
AP_INIT_TAKE1("AuthMySQLTableUserPasswd", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_user_field_passwd),
OR_AUTHCFG, "Table User Fieldname: user_passwd"),
AP_INIT_TAKE1("AuthMySQLTableUserHostGroup", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_user_field_hostgroup),
OR_AUTHCFG, "Table User Fieldname: host_group"),
/* the host table */
AP_INIT_TAKE1("AuthMySQLHostTable", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_host_table),
OR_AUTHCFG, "DB Host Table"),
AP_INIT_TAKE1("AuthMySQLTableHostName", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_host_field_hostname),
OR_AUTHCFG, "Table Host Fieldname: host_name"),
AP_INIT_TAKE1("AuthMySQLTableHostHostGroup", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_host_field_hostgroup),
OR_AUTHCFG, "Table Host Fieldname: host_group"),
/* the host table */
AP_INIT_TAKE1("AuthMySQLGroupTable", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_group_table),
OR_AUTHCFG, "DB Group Table"),
AP_INIT_TAKE1("AuthMySQLTableGroupName", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_group_field_username),
OR_AUTHCFG, "Table Group Fieldname: user_name"),
AP_INIT_TAKE1("AuthMySQLTableGroupGroupName", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_group_field_groupname),
OR_AUTHCFG, "Table Group Fieldname: user_group"),
AP_INIT_TAKE1("AuthMySQLTableGroupHostGroup", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_group_field_hostgroup),
OR_AUTHCFG, "Table Group Fieldname: host_group"),
/* Additionl condition*/
AP_INIT_TAKE1("AuthMySQLUserQueryCondition", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_user_condition),
OR_AUTHCFG, "Additional condition to the password query "
"like active = 'y', ...: default is none"),
AP_INIT_TAKE1("AuthMySQLGroupQueryCondition", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_group_condition),
OR_AUTHCFG, "Additional condition to the groupmember query "
"like expired = 'n', ...: default is none"),
#ifdef MYSQL_USE_SSL
/*Client SSL Certificate settings */
AP_INIT_FLAG("AuthMySQLClientUseSSL", ap_set_flag_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_client_use_ssl),
OR_AUTHCFG, "DB connection uses SSL encryption if On"),
AP_INIT_TAKE1("AuthMySQLClientKey", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_client_ssl_key),
OR_AUTHCFG, "mysql client key path to be used for SSL connections"),
AP_INIT_TAKE1("AuthMySQLClientCert", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_client_ssl_cert),
OR_AUTHCFG, "mysql client cert path to be used for SSL connections"),
AP_INIT_TAKE1("AuthMySQLClientCA", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_client_ssl_ca),
OR_AUTHCFG, "mysql client CA path to be used for SSL connections"),
AP_INIT_TAKE1("AuthMySQLClientCipher", ap_set_string_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_client_ssl_cipher),
OR_AUTHCFG, "mysql client cipher to be used for SSL connections"),
AP_INIT_FLAG("AuthMySQLClientVerifyCert", ap_set_flag_slot,
(void *) APR_OFFSETOF(auth_mysql_config_rec, db_client_verify_cert),
OR_AUTHCFG, "DB client verify the server cert"),
#endif
{NULL}
};
module AP_MODULE_DECLARE_DATA auth_mysql_module;
/*
* Open a mysql handle
*/
static int open_db_handle(request_rec *r, auth_mysql_config_rec *conf)
{
static MYSQL mysql_conn;
char *db_host;
unsigned int db_port = 0;
if (!conf->auth_enable) {
return -1;
}
/*
* check if the server is still alive
*/
if (mysql_handle) {
if (mysql_ping(mysql_handle) == 0) {
return 0; /* already open */
} else {
ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: MYSQL ERROR: %s :: mysql ping failed",
mysql_error(&mysql_conn));
mysql_close(mysql_handle);
mysql_handle = NULL;
}
}
if (!conf->db_host || strcmp(conf->db_host,"localhost") == 0 ||
strcmp(conf->db_host,"127.0.0.1") == 0) {
db_host = NULL;
db_port = 0;
} else {
db_host = conf->db_host;
db_port = conf->db_port;
}
mysql_init(&mysql_conn);
#ifdef MYSQL_USE_SSL
/* if SSL wanted and not on localhost */
if (conf->db_client_use_ssl && db_host) {
int cert_error = 0;
if (conf->db_client_ssl_key != NULL && !accessible(r->pool,conf->db_client_ssl_key,APR_READ)) {
cert_error++;
ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: CERT ERROR: %s :: cant access SSL client key",
conf->db_client_ssl_key);
}
if ( conf->db_client_ssl_cert != NULL && !accessible(r->pool,conf->db_client_ssl_cert,APR_READ)) {
cert_error++;
ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: CERT ERROR: %s :: cant access SSL client cert",
conf->db_client_ssl_cert);
}
if (!accessible(r->pool,conf->db_client_ssl_ca,APR_READ)) {
cert_error++;
ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: CERT ERROR: %s :: cant access SSL client ca",
conf->db_client_ssl_ca);
}
if (!cert_error) {
#if (((MYSQL_VERSION_ID >= 50027) && (MYSQL_VERSION_ID < 50100)) || (MYSQL_VERSION_ID >= 50116))
static my_bool opt_ssl_verify_server_cert = 1;
if (conf->db_client_verify_cert) {
mysql_options(&mysql_conn,MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
(const char *)&opt_ssl_verify_server_cert);
}
#endif
mysql_ssl_set(&mysql_conn,conf->db_client_ssl_key,
conf->db_client_ssl_cert,conf->db_client_ssl_ca,
NULL,conf->db_client_ssl_cipher);
}
}
#endif
mysql_handle=mysql_real_connect(&mysql_conn,db_host,
conf->db_username,conf->db_password,conf->db_name,db_port,NULL,0);
if (mysql_handle) {
if (!conf->db_keepalive) {
/* close when request done */
apr_pool_cleanup_register(r->pool,
(void *)NULL,
mod_auth_mysql_cleanup,
mod_auth_mysql_cleanup_child);
} /* ELSE...
* Child process not notified when it is terminated so we can never
* do a graceful close to the server. Fix this for Apache 1.3.
*
*/
} else { /* failed to get MySQL connection */
ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: MYSQL ERROR: %s :: connect to DB",
mysql_error(&mysql_conn));
return -1;
}
return 0;
}
/*
* Checks if is host in db.
*/
static char *is_virtual_in_db(request_rec *r, auth_mysql_config_rec *conf)
{
static char *host = NULL;
char query[MAX_STRING_LEN];
MYSQL_RES *result;
if (!conf->auth_enable) {
return NULL;
}
if(open_db_handle(r,conf)) {
return NULL; /* failure reason already logged */
}
if (mysql_select_db(mysql_handle,conf->db_name) != 0) {
ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: MYSQL ERROR %s: '%s'", mysql_error(mysql_handle),
"Select Database is_virtual_in_db");
return NULL;
}
apr_snprintf(query,sizeof(query)-1,
"select %s "
"from %s "
"where %s = lower('%s')",
conf->db_host_field_hostname,
conf->db_host_table,
conf->db_host_field_hostname,
conf->auth_virtualhost ? r->hostname : r->server->server_hostname);
if (mysql_query(mysql_handle, query) != 0) {
ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: MYSQL ERROR %s: '%s' %s",
mysql_error(mysql_handle), query, r->uri);
}
result = mysql_store_result(mysql_handle);
if (result && (mysql_num_rows(result) == 1)) {
MYSQL_ROW data = mysql_fetch_row(result);
if (data[0]) {
host = apr_pstrdup(r->pool, data[0]);
} else { /* No matching host found in table */
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: Host %s (%s) there are to many hosts ....",
r->server->server_hostname, r->hostname);
mysql_free_result(result);
return NULL;
}
} else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: Host %s (%s) is not in the database (table: '%s')",
r->server->server_hostname, r->hostname, conf->db_host_table);
}
if (result) {
mysql_free_result(result);
}
return host;
}
/* {{{ static char *get_db_pw()
* Returns the hashed password from the db.
*/
static char *get_db_pw(request_rec *r, char *user, auth_mysql_config_rec *conf)
{
char *pw = NULL;
char *escaped_user = NULL;
int user_len = 0;
MYSQL_RES *result;
char query[MAX_STRING_LEN];
if (!conf->auth_enable) {
return NULL;
}
if(open_db_handle(r,conf)) {
return NULL; /* failure reason already logged */
}
if (mysql_select_db(mysql_handle,conf->db_name) != 0) {
ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,"MYSQL ERROR %s: '%s'",
mysql_error(mysql_handle),"Select Database get_db_pw");
return NULL;
}
user_len = strlen(user);
escaped_user = apr_pcalloc(r->pool,user_len*2+1);
mysql_escape_string(escaped_user,user,user_len);
apr_snprintf(query,sizeof(query)-1,
"select %s.%s "
"from %s, %s "
"where %s.%s = %s.%s "
"and lower('%s') like %s.%s "
"and %s.%s='%s' "
"and ( %s )",
conf->db_user_table, conf->db_user_field_passwd,
conf->db_user_table, conf->db_host_table,
conf->db_user_table, conf->db_user_field_hostgroup,
conf->db_host_table, conf->db_host_field_hostgroup,
conf->auth_virtualhost ? r->hostname : r->server->server_hostname,
conf->db_host_table, conf->db_host_field_hostname,
conf->db_user_table, conf->db_user_field_name,
escaped_user,
conf->db_user_condition);
if (mysql_query(mysql_handle, query) != 0) {
ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: MYSQL ERROR %s: '%s' %s",
mysql_error(mysql_handle),query,r->uri);
return NULL;
}
result = mysql_store_result(mysql_handle);
if (result && (mysql_num_rows(result) == 1)) {
MYSQL_ROW data = mysql_fetch_row(result);
if (data[0]) {
pw = apr_pstrdup(r->pool, data[0]);
} else {
/* no password in mysql table returns NULL
* this should never happen, but test for it anyhow
*/
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: User %s@%s has no valid password: %s",
user,
conf->auth_virtualhost ? r->hostname : r->server->server_hostname,
r->uri);
mysql_free_result(result);
return NULL;
}
}
if (result) {
mysql_free_result(result);
}
return pw;
} // }}}
/* {{{
* Returns a list of groups by a user.
*/
static char **get_db_grp(request_rec *r,char *user,auth_mysql_config_rec *conf)
{
MYSQL_RES *result;
MYSQL_ROW data;
char **list = NULL;
char *escaped_user = NULL;
int user_len = 0;
char query[MAX_STRING_LEN];
if (!conf->auth_enable) {
return NULL;
}
if(open_db_handle(r,conf)) {
return NULL; /* failure reason already logged */
}
if (mysql_select_db(mysql_handle,conf->db_name) != 0) {
ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
"MOD_AUTH_MYSQL: MySQL ERROR %s: '%s' %s", mysql_error(mysql_handle),
"Select Database get_db_grp",r->uri);
return NULL;
}
user_len = strlen(user);
escaped_user = apr_pcalloc(r->pool,user_len*2+1);
mysql_escape_string(escaped_user,user,user_len);
apr_snprintf(query,sizeof(query)-1,
"select %s.%s "
"from %s, %s "
"where %s.%s = %s.%s "
"and lower('%s') like %s.%s "
"and %s.%s = '%s' "
"and ( %s ) ",
conf->db_group_table, conf->db_group_field_groupname,
conf->db_group_table, conf->db_host_table,
conf->db_group_table, conf->db_group_field_hostgroup,
conf->db_host_table, conf->db_host_field_hostgroup,
conf->auth_virtualhost ? r->hostname : r->server->server_hostname,
conf->db_host_table, conf->db_host_field_hostname,
conf->db_group_table, conf->db_group_field_username, escaped_user,
conf->db_group_condition);
if (mysql_query(mysql_handle, query) != 0) {
ap_log_rerror (APLOG_MARK, APLOG_ERR,0, r,
"MOD_AUTH_MYSQL: MYSQL ERROR %s: '%s' %s", mysql_error(mysql_handle),
query,r->uri);
return NULL;
}
/* list should be a comma limited list of all groups
* this is at the moment wrong!
*/
result = mysql_store_result(mysql_handle);
if (result && (mysql_num_rows(result) > 0)) {
int i = mysql_num_rows(result);
list = (char **)apr_pcalloc(r->pool, sizeof(char *) * (i+1));
list[i] = NULL; /* last element in array is NULL */
while (i--) { /* populate the array elements */
data = mysql_fetch_row(result);
if (data[0]) {
list[i] = apr_pstrdup(r->pool, data[0]);
} else {
list[i] = ""; /* if no data, make it empty, not NULL */
}
}
}
if (result) {
mysql_free_result(result);
}
return list;
}
/*
* ... do the job ;)
*/
static int db_authenticate_basic_user(request_rec *r)
{
auth_mysql_config_rec *conf = ap_get_module_config(r->per_dir_config,
&auth_mysql_module);
const char *sent_pw;
char *real_pw, *colon_pw;
apr_status_t invalid_pw;
int res;
if ((res = ap_get_basic_auth_pw(r, &sent_pw))) {
return res;
}
if (!conf->auth_enable ) {
return DECLINED;
}
if ( is_virtual_in_db(r,conf) == NULL ) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"host (%s) not found in db",
conf->auth_virtualhost ? r->hostname : r->server->server_hostname);
return DECLINED;
}
if (!(real_pw = get_db_pw(r, r->user, conf))) {
if (!(conf->auth_dbauthoritative)) {
return DECLINED;
}
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"user not found on %s: http://%s:%s@%s%s",
conf->auth_virtualhost ? r->hostname : r->server->server_hostname,
r->user, sent_pw, r->hostname, r->uri);
ap_note_basic_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
/* Password is up to first : if exists */
colon_pw = strchr(real_pw, ':');
if (colon_pw) {
*colon_pw = '\0';
}
invalid_pw = apr_password_validate(sent_pw, real_pw);
if (invalid_pw != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"password mismatch on %s: http://%s:%s@%s%s",
conf->auth_virtualhost ? r->hostname : r->server->server_hostname,
r->user, sent_pw, r->hostname, r->uri);
ap_note_basic_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
return OK;
}
/*
* Checking ID
*/
static int db_check_auth(request_rec *r)
{
auth_mysql_config_rec *conf = ap_get_module_config(r->per_dir_config,
&auth_mysql_module);
char *user = r->user;
int m = r->method_number;
const apr_array_header_t *reqs_arr = ap_requires(r);
require_line *reqs = reqs_arr ? (require_line *) reqs_arr->elts : NULL;
register int x;
const char *t;
char *w;
if ( is_virtual_in_db(r,conf) == NULL ) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"host (%s) not found in db (within mysql_check_auth)",
conf->auth_virtualhost ? r->hostname : r->server->server_hostname);
return DECLINED;
}
if (!reqs_arr) {
return DECLINED;
}
for (x = 0; x < reqs_arr->nelts; x++) {
if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) {
continue;
}
t = reqs[x].requirement;
w = ap_getword_white(r->pool, &t);
if (!strcmp(w, "group")) {
char **groups;
if (!(groups = get_db_grp(r, user, conf))) {
if (!(conf->auth_dbauthoritative)) {
return DECLINED;
}
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"user not in any group :: http://%s@%s%s",
user, r->hostname, r->filename);
ap_note_basic_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
while (t[0]) {
int i = 0;
w = ap_getword_white(r->pool, &t);
while (groups[i]) {
if (!strcmp(groups[i], w)) {
return OK;
}
i++;
}
}
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"user not in right group: http://%s@%s%s",
user, r->hostname,r->uri);
ap_note_basic_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
}
return DECLINED;
}
/*
* Initialize the module
*/
static int auth_mysql_init(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, server_rec * s)
{
#ifdef SHOW_VERSION_COMPONENT
ap_add_version_component(p, "mod_auth_mysql/" MODULE_RELEASE);
#endif
return OK;
}
/*
* Callback for register hooks
*/
static void register_hooks(apr_pool_t *p)
{
ap_hook_post_config(auth_mysql_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_check_user_id(db_authenticate_basic_user, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_auth_checker(db_check_auth, NULL, NULL, APR_HOOK_MIDDLE);
}
/*
* Module informations
*/
module AP_MODULE_DECLARE_DATA auth_mysql_module =
{
STANDARD20_MODULE_STUFF,
create_db_auth_dir_config, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
db_auth_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};