/* ==================================================================== * 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 */ };