/* ==================================================================== * Copyright (c) 1996 Vidya Media Ventures, Inc. 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 this source code or a derived source code must * retain the above copyright notice, this list of conditions and the * following disclaimer. * * 2. Redistributions of this module or a derived module 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. * * THIS SOFTWARE IS PROVIDED BY VIDYA MEDIA VENTURES, INC. ``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 VIDYA MEDIA VENTURES, INC. * OR ITS EMPLOYEES 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 is a contribution to and makes use of the Apache HTTP * server which is written, maintained and copywritten by the Apache Group. * See http://www.apache.org/ for more information. * * This software makes use of libpq which an interface to the PostgreSQL * database. PostgreSQL is copyright (c) 1994 by the Regents of the * University of California. As of this writing, more information on * PostgreSQL can be found at http://www.postgresSQL.org/ * */ /* * * PostgreSQL authentication * * * Needs libpq-fe.h and libpq.a * * Outline: * * - Authentication * One database, and one (or two) tables. One table holds the username and * the encryped (or plain) password. The other table holds the username and the names * of the group to which the user belongs. It is possible to have username, * groupname and password in the same table. * - Access Logging * Every authentication access is logged in the same database of the * authentication table, but in different table. * User name and date of the request are logged. * As option, it can log password, ip address, request line. * * Module Directives: See html documentation * * Changelog: See html documentation * * see http://www.postgreSQL.org/ * * * * Homepage http://www.giuseppetanzilli.it/mod_auth_pgsql/ * * Latest sources http://www.giuseppetanzilli.it/mod_auth_pgsql/dist/ * * Maintainer: * Giuseppe Tanzilli * info@giuseppetanzilli.it * g.tanzilli@gruppocsf.com * * Original source: * Adam Sussman (asussman@vidya.com) Feb, 1996 * Matthias Eckermann * eckerman@lrz.uni-muenchen.de * */ #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_protocol.h" #include "ap_md5.h" #include "ap_alloc.h" #include #define AUTH_PGSQL_VERSION "0.9.12" /* We need Apache 1.3.1 */ #if (MODULE_MAGIC_NUMBER >= 19980713) #define _const const #define IS_APACHE_13 1 #endif #ifndef MAX_STRING_LEN #define MAX_STRING_LEN 3000 #endif /* Cache table size */ #ifndef MAX_TABLE_LEN #define MAX_TABLE_LEN 50 #endif #define AUTH_PG_HASH_TYPE_CRYPT 1 #define AUTH_PG_HASH_TYPE_MD5 2 typedef struct { char *auth_pg_host; char *auth_pg_database; char *auth_pg_port; char *auth_pg_options; char *auth_pg_user; char *auth_pg_pwd; char *auth_pg_pwd_table; char *auth_pg_grp_table; char *auth_pg_uname_field; char *auth_pg_pwd_field; char *auth_pg_grp_field; char *auth_pg_pwd_whereclause; char *auth_pg_grp_whereclause; int auth_pg_nopasswd; int auth_pg_authoritative; int auth_pg_lowercaseuid; int auth_pg_uppercaseuid; int auth_pg_pwdignorecase; int auth_pg_encrypted; int auth_pg_hash_type; int auth_pg_cache_passwords; char *auth_pg_log_table; char *auth_pg_log_addrs_field; char *auth_pg_log_uname_field; char *auth_pg_log_pwd_field; char *auth_pg_log_date_field; char *auth_pg_log_uri_field; table *cache_pass_table; } pg_auth_config_rec; static pool *auth_pgsql_pool = NULL; void * create_pg_auth_dir_config (pool * p, char *d) { pg_auth_config_rec *new_rec; new_rec = ap_pcalloc (p, sizeof (pg_auth_config_rec)); if (new_rec == NULL) return NULL; if (auth_pgsql_pool == NULL) auth_pgsql_pool = ap_make_sub_pool (NULL); if (auth_pgsql_pool == NULL) return NULL; /* sane defaults */ new_rec->auth_pg_host = NULL; new_rec->auth_pg_database = NULL; new_rec->auth_pg_port = NULL; new_rec->auth_pg_options = NULL; new_rec->auth_pg_user = NULL; new_rec->auth_pg_pwd = NULL; new_rec->auth_pg_pwd_table = NULL; new_rec->auth_pg_grp_table = NULL; new_rec->auth_pg_uname_field = NULL; new_rec->auth_pg_pwd_field = NULL; new_rec->auth_pg_grp_field = NULL; new_rec->auth_pg_pwd_whereclause = NULL; new_rec->auth_pg_grp_whereclause = NULL; new_rec->auth_pg_nopasswd = 0; new_rec->auth_pg_authoritative = 1; new_rec->auth_pg_lowercaseuid = 0; new_rec->auth_pg_uppercaseuid = 0; new_rec->auth_pg_pwdignorecase = 0; new_rec->auth_pg_encrypted = 1; new_rec->auth_pg_hash_type = AUTH_PG_HASH_TYPE_CRYPT; new_rec->auth_pg_cache_passwords = 0; new_rec->auth_pg_log_table = NULL; new_rec->auth_pg_log_addrs_field = NULL; new_rec->auth_pg_log_uname_field = NULL; new_rec->auth_pg_log_pwd_field = NULL; new_rec->auth_pg_log_date_field = NULL; new_rec->auth_pg_log_uri_field = NULL; // make a per directory cache table new_rec->cache_pass_table = ap_make_table (auth_pgsql_pool, MAX_TABLE_LEN); if (new_rec->cache_pass_table == NULL) return NULL; return new_rec; } _const char * pg_set_hash_type (cmd_parms * cmd, pg_auth_config_rec * sec, const char *hash_type) { if (!strcasecmp (hash_type, "MD5")) sec->auth_pg_hash_type = AUTH_PG_HASH_TYPE_MD5; else if (!strcasecmp (hash_type, "CRYPT")) sec->auth_pg_hash_type = AUTH_PG_HASH_TYPE_CRYPT; else return ap_pstrcat (cmd->pool, "Invalid hash type for Auth_PG_hash_type: ", hash_type, NULL); return NULL; } _const char * pg_set_cache_passwords_flag (cmd_parms * cmd, pg_auth_config_rec * sec, int arg) { sec->auth_pg_cache_passwords = arg; return NULL; } _const char * pg_set_passwd_flag (cmd_parms * cmd, pg_auth_config_rec * sec, int arg) { sec->auth_pg_nopasswd = arg; return NULL; } _const char * pg_set_encrypted_flag (cmd_parms * cmd, pg_auth_config_rec * sec, int arg) { sec->auth_pg_encrypted = arg; return NULL; } _const char * pg_set_authoritative_flag (cmd_parms * cmd, pg_auth_config_rec * sec, int arg) { sec->auth_pg_authoritative = arg; return NULL; } _const char* pg_set_lowercaseuid_flag (cmd_parms *cmd, pg_auth_config_rec *sec, int arg) { sec->auth_pg_lowercaseuid=arg; sec->auth_pg_uppercaseuid=0; return NULL; } _const char* pg_set_uppercaseuid_flag (cmd_parms *cmd, pg_auth_config_rec *sec, int arg) { sec->auth_pg_uppercaseuid=arg; sec->auth_pg_lowercaseuid=0; return NULL; } _const char* pg_set_pwdignorecase_flag (cmd_parms *cmd, pg_auth_config_rec *sec, int arg) { sec->auth_pg_pwdignorecase=arg; return NULL; } command_rec pg_auth_cmds[] = { {"Auth_PG_host", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_host), OR_AUTHCFG, TAKE1, "the host name of the postgreSQL server."}, {"Auth_PG_database", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_database), OR_AUTHCFG, TAKE1, "the name of the database that contains authorization information. "}, {"Auth_PG_port", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_port), OR_AUTHCFG, TAKE1, "the port the postmaster is running on. "}, {"Auth_PG_options", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_options), OR_AUTHCFG, TAKE1, "an options string to be sent to the postgres backed process. "}, {"Auth_PG_user", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_user), OR_AUTHCFG, TAKE1, "user name connect as"}, {"Auth_PG_pwd", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_pwd), OR_AUTHCFG, TAKE1, "user name connect as"}, {"Auth_PG_pwd_table", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_pwd_table), OR_AUTHCFG, TAKE1, "the name of the table containing username/password tuples."}, {"Auth_PG_grp_table", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_grp_table), OR_AUTHCFG, TAKE1, "the name of the table containing username/group tuples."}, {"Auth_PG_pwd_field", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_pwd_field), OR_AUTHCFG, TAKE1, "the name of the password field."}, {"Auth_PG_uid_field", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_uname_field), OR_AUTHCFG, TAKE1, "the name of the user-id field."}, {"Auth_PG_gid_field", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_grp_field), OR_AUTHCFG, TAKE1, "the name of the group-name field."}, {"Auth_PG_nopasswd", pg_set_passwd_flag, NULL, OR_AUTHCFG, FLAG, "'on' or 'off'"}, {"Auth_PG_encrypted", pg_set_encrypted_flag, NULL, OR_AUTHCFG, FLAG, "'on' or 'off'"}, {"Auth_PG_hash_type", pg_set_hash_type, NULL, OR_AUTHCFG, TAKE1, "password hash type (CRYPT|MD5)."}, {"Auth_PG_cache_passwords", pg_set_cache_passwords_flag, NULL, OR_AUTHCFG, FLAG, "'on' or 'off'"}, {"Auth_PG_authoritative", pg_set_authoritative_flag, NULL, OR_AUTHCFG, FLAG, "'on' or 'off'"}, { "Auth_PG_lowercase_uid", pg_set_lowercaseuid_flag, NULL, OR_AUTHCFG, FLAG, "'on' or 'off'" }, { "Auth_PG_uppercase_uid", pg_set_uppercaseuid_flag, NULL, OR_AUTHCFG, FLAG, "'on' or 'off'" }, { "Auth_PG_pwd_ignore_case", pg_set_pwdignorecase_flag, NULL, OR_AUTHCFG, FLAG, "'on' or 'off'" }, {"Auth_PG_grp_whereclause", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_grp_whereclause), OR_AUTHCFG, TAKE1, "an SQL fragement that can be attached to the end of a where clause."}, {"Auth_PG_pwd_whereclause", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_pwd_whereclause), OR_AUTHCFG, TAKE1, "an SQL fragement that can be attached to the end of a where clause."}, {"Auth_PG_log_table", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_log_table), OR_AUTHCFG, TAKE1, "the name of the table containing log tuples."}, {"Auth_PG_log_addrs_field", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_log_addrs_field), OR_AUTHCFG, TAKE1, "the name of the field containing addrs in the log table (type char)."}, {"Auth_PG_log_uname_field", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_log_uname_field), OR_AUTHCFG, TAKE1, "the name of the field containing username in the log table (type char)."}, {"Auth_PG_log_pwd_field", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_log_pwd_field), OR_AUTHCFG, TAKE1, "the name of the field containing password in the log table (type char)."}, {"Auth_PG_log_date_field", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_log_date_field), OR_AUTHCFG, TAKE1, "the name of the field containing date in the log table (type char)."}, {"Auth_PG_log_uri_field", ap_set_string_slot, (void *) XtOffsetOf (pg_auth_config_rec, auth_pg_log_uri_field), OR_AUTHCFG, TAKE1, "the name of the field containing uri (Object fetched) in the log table (type char)."}, {NULL} }; module MODULE_VAR_EXPORT auth_pgsql_module; char pg_errstr[MAX_STRING_LEN]; /* global errno to be able to handle config/sql * failures separately */ char * auth_pg_md5 (char *pw) { AP_MD5_CTX ctx; unsigned char digest[16]; static char md5hash[33]; int i; ap_MD5Init (&ctx); ap_MD5Update (&ctx, pw, strlen (pw)); ap_MD5Final (digest, &ctx); for (i = 0; i < 16; i++) sprintf (&md5hash[i + i], "%02x", digest[i]); md5hash[32] = '\0'; return md5hash; } /* Got from POstgreSQL 7.2 */ /* --------------- * Escaping arbitrary strings to get valid SQL strings/identifiers. * * Replaces "\\" with "\\\\" and "'" with "''". * length is the length of the buffer pointed to by * from. The buffer at to must be at least 2*length + 1 characters * long. A terminating NUL character is written. * --------------- */ size_t pg_check_string (char *to, const char *from, size_t length) { const char *source = from; char *target = to; unsigned int remaining = length; while (remaining > 0) { switch (*source) { case '\\': *target = '\\'; target++; *target = '\\'; /* target and remaining are updated below. */ break; case '\'': *target = '\''; target++; *target = '\''; /* target and remaining are updated below. */ break; default: *target = *source; /* target and remaining are updated below. */ } source++; target++; remaining--; } /* Write the terminating NUL character. */ *target = '\0'; return target - to; } /* Do a query and return the (0,0) value. The query is assumed to be * a select. */ char * do_pg_query (request_rec * r, char *query, pg_auth_config_rec * sec) { PGconn *pg_conn; PGresult *pg_result; char *val; char *result = NULL; pg_errstr[0] = '\0'; pg_conn = PQsetdbLogin (sec->auth_pg_host, sec->auth_pg_port, sec->auth_pg_options, NULL, sec->auth_pg_database, sec->auth_pg_user, sec->auth_pg_pwd); if (PQstatus (pg_conn) != CONNECTION_OK) { snprintf (pg_errstr, MAX_STRING_LEN, "PGSQL 1: %s", PQerrorMessage (pg_conn)); return NULL; } pg_result = PQexec (pg_conn, query); if (pg_result == NULL) { snprintf (pg_errstr, MAX_STRING_LEN, "PGSQL 2: %s -- Query: %s ", PQerrorMessage (pg_conn), query); PQfinish (pg_conn); return NULL; } if (PQresultStatus (pg_result) == PGRES_EMPTY_QUERY) { PQclear (pg_result); PQfinish (pg_conn); return NULL; } if (PQresultStatus (pg_result) != PGRES_TUPLES_OK) { snprintf (pg_errstr, MAX_STRING_LEN, "PGSQL 3: %s -- Query: %s", PQerrorMessage (pg_conn), query); PQclear (pg_result); PQfinish (pg_conn); return NULL; } if (PQntuples (pg_result) == 1) { val = PQgetvalue (pg_result, 0, 0); if (val == NULL) { snprintf (pg_errstr, MAX_STRING_LEN, "PGSQL 4: %s", PQerrorMessage (pg_conn)); PQclear (pg_result); PQfinish (pg_conn); return NULL; } if (!(result = (char *) ap_palloc (r->pool, strlen (val) + 1))) { snprintf (pg_errstr, MAX_STRING_LEN, "Could not get memory for Postgres query."); PQclear (pg_result); PQfinish (pg_conn); return NULL; } strcpy (result, val); } /* ignore errors here ! */ PQclear (pg_result); PQfinish (pg_conn); return result; } char * get_pg_pw (request_rec * r, char *user, pg_auth_config_rec * sec) { char query[MAX_STRING_LEN]; char safe_user[1 + 2 * strlen (user)]; int n; pg_check_string (safe_user, user, strlen (user)); if ((!sec->auth_pg_pwd_table) || (!sec->auth_pg_pwd_field) || (!sec->auth_pg_uname_field)) { snprintf (pg_errstr, MAX_STRING_LEN, "PG: Missing parameters for password lookup: %s%s%s", (sec->auth_pg_pwd_table ? "" : "Password table "), (sec->auth_pg_pwd_field ? "" : "Password field name "), (sec->auth_pg_uname_field ? "" : "UserID field name ")); return NULL; }; if (sec->auth_pg_lowercaseuid) { /* and force it to lowercase */ n=0; while(safe_user[n] && n < (MAX_STRING_LEN-1)) { if (isupper(safe_user[n])) { safe_user[n] = tolower(safe_user[n]); } n++; } } if (sec->auth_pg_uppercaseuid) { /* and force it to uppercase */ n=0; while(safe_user[n] && n < (MAX_STRING_LEN-1)) { if (islower(safe_user[n])) { safe_user[n] = toupper(safe_user[n]); } n++; } } n = snprintf (query, MAX_STRING_LEN, "select %s from %s where %s='%s' %s", sec->auth_pg_pwd_field, sec->auth_pg_pwd_table, sec->auth_pg_uname_field, safe_user, sec->auth_pg_pwd_whereclause ? sec-> auth_pg_pwd_whereclause : ""); if (n < 0 || n > MAX_STRING_LEN) { snprintf (pg_errstr, MAX_STRING_LEN, "PG: Detected SQL-truncation attack. Auth aborted."); return NULL; } return do_pg_query (r, query, sec); } char * get_pg_grp (request_rec * r, char *group, char *user, pg_auth_config_rec * sec) { char query[MAX_STRING_LEN]; char safe_user[1 + 2 * strlen (user)]; char safe_group[1 + 2 * strlen (group)]; int n; query[0] = '\0'; pg_check_string (safe_user, user, strlen (user)); pg_check_string (safe_group, group, strlen (group)); if ((!sec->auth_pg_grp_table) || (!sec->auth_pg_grp_field) || (!sec->auth_pg_uname_field)) { snprintf (pg_errstr, MAX_STRING_LEN, "PG: Missing parameters for password lookup: %s%s%s", (sec->auth_pg_grp_table ? "" : "Group table "), (sec->auth_pg_grp_field ? "" : "GroupID field name "), (sec->auth_pg_uname_field ? "" : "UserID field name ")); return NULL; }; n = snprintf (query, MAX_STRING_LEN, "select %s from %s where %s='%s' and %s='%s' %s", sec->auth_pg_grp_field, sec->auth_pg_grp_table, sec->auth_pg_uname_field, safe_user, sec->auth_pg_grp_field, safe_group, sec->auth_pg_grp_whereclause ? sec-> auth_pg_grp_whereclause : ""); if (n < 0 || n > MAX_STRING_LEN) { snprintf (pg_errstr, MAX_STRING_LEN, "PG: Detected SQL-truncation attack. Auth aborted."); return NULL; } return do_pg_query (r, query, sec); } /* Process authentication request from Apache*/ int pg_authenticate_basic_user (request_rec * r) { pg_auth_config_rec *sec = (pg_auth_config_rec *) ap_get_module_config (r->per_dir_config, &auth_pgsql_module); conn_rec *c = r->connection; char *val = NULL; char *sent_pw, *real_pw; int res; if ((res = ap_get_basic_auth_pw (r, (const char **) &sent_pw))) return res; /* if *password* checking is configured in any way, i.e. then * handle it, if not decline and leave it to the next in line.. * We do not check on dbase, group, userid or host name, as it is * perfectly possible to only do group control and leave * user control to the next guy in line. */ if ((!sec->auth_pg_pwd_table) && (!sec->auth_pg_pwd_field)) return DECLINED; pg_errstr[0] = '\0'; if (sec->auth_pg_cache_passwords && (!ap_is_empty_table (sec->cache_pass_table))) { val = (char *) ap_table_get (sec->cache_pass_table, c->user); if (val) real_pw = val; else real_pw = get_pg_pw (r, c->user, sec); } else real_pw = get_pg_pw (r, c->user, sec); if (!real_pw) { if (pg_errstr[0]) { res = SERVER_ERROR; } else { if (sec->auth_pg_authoritative) { /* force error and access denied */ snprintf (pg_errstr, MAX_STRING_LEN, "mod_auth_pgsql: Password for user %s not found (PG-Authoritative)", c->user); ap_note_basic_auth_failure (r); res = AUTH_REQUIRED; } else { /* allow fall through to another module */ return DECLINED; } } ap_log_reason (pg_errstr, r->filename, r); return res; } /* allow no password, if the flag is set and the password * is empty. But be sure to log this. */ if ((sec->auth_pg_nopasswd) && (!strlen (real_pw))) { snprintf (pg_errstr, MAX_STRING_LEN, "PG: user %s: Empty password accepted", c->user); ap_log_reason (pg_errstr, r->uri, r); pg_log_auth_user (r, sec, c->user, sent_pw); return OK; }; /* if the flag is off however, keep that kind of stuff at * an arms length. */ if ((!strlen (real_pw)) || (!strlen (sent_pw))) { snprintf (pg_errstr, MAX_STRING_LEN, "PG: user %s: Empty Password(s) Rejected", c->user); ap_log_reason (pg_errstr, r->uri, r); ap_note_basic_auth_failure (r); return AUTH_REQUIRED; }; if (sec->auth_pg_encrypted) sent_pw = (sec->auth_pg_hash_type == AUTH_PG_HASH_TYPE_MD5) ? auth_pg_md5 (sent_pw) : (char *) crypt (sent_pw, real_pw); if ((sec->auth_pg_hash_type == AUTH_PG_HASH_TYPE_MD5 || sec->auth_pg_pwdignorecase != 0) ? strcasecmp (real_pw, sent_pw) : strcmp (real_pw, sent_pw)) { snprintf (pg_errstr, MAX_STRING_LEN, "PG user %s: password mismatch", c->user); ap_log_reason (pg_errstr, r->uri, r); ap_note_basic_auth_failure (r); return AUTH_REQUIRED; } if (sec->auth_pg_cache_passwords && !val && sec->cache_pass_table) { if ((ap_table_elts (sec->cache_pass_table))->nelts >= MAX_TABLE_LEN) { ap_clear_table (sec->cache_pass_table); } ap_table_set (sec->cache_pass_table, c->user, real_pw); } pg_log_auth_user (r, sec, c->user, sent_pw); return OK; } /* Checking ID */ int pg_check_auth (request_rec * r) { pg_auth_config_rec *sec = (pg_auth_config_rec *) ap_get_module_config (r->per_dir_config, &auth_pgsql_module); char *user = r->connection->user; int m = r->method_number; int group_result = DECLINED; array_header *reqs_arr = (array_header *) ap_requires (r); require_line *reqs = reqs_arr ? (require_line *) reqs_arr->elts : NULL; register int x, res; const char *t; char *w; pg_errstr[0] = '\0'; /* if we cannot do it; leave it to some other guy */ if ((!sec->auth_pg_grp_table) && (!sec->auth_pg_grp_field)) return DECLINED; if (!reqs_arr) { if (sec->auth_pg_authoritative) { /* force error and access denied */ snprintf (pg_errstr, MAX_STRING_LEN, "mod_auth_pgsql: user %s denied, no access rules specified (PG-Authoritative)", user); ap_log_reason (pg_errstr, r->uri, r); ap_note_basic_auth_failure (r); res = AUTH_REQUIRED; } else { return DECLINED; } } for (x = 0; x < reqs_arr->nelts; x++) { if (!(reqs[x].method_mask & (1 << m))) continue; t = reqs[x].requirement; w = ap_getword (r->pool, &t, ' '); if (!strcmp (w, "valid-user")) return OK; if (!strcmp (w, "user")) { while (t[0]) { w = ap_getword_conf (r->pool, &t); if (!strcmp (user, w)) return OK; } if (sec->auth_pg_authoritative) { /* force error and access denied */ snprintf (pg_errstr, MAX_STRING_LEN, "mod_auth_pgsql: user %s denied, no access rules specified (PG-Authoritative)", user); ap_log_reason (pg_errstr, r->uri, r); ap_note_basic_auth_failure (r); return AUTH_REQUIRED; } } else if (!strcmp (w, "group")) { /* look up the membership for each of the groups in the table */ pg_errstr[0] = '\0'; while (t[0]) { if (get_pg_grp (r, ap_getword (r->pool, &t, ' '), user, sec)) { group_result = OK; }; }; if (pg_errstr[0]) { ap_log_reason (pg_errstr, r->filename, r); return SERVER_ERROR; } if (group_result == OK) return OK; if (sec->auth_pg_authoritative) { snprintf (pg_errstr, MAX_STRING_LEN, "user %s not in right groups (PG-Authoritative)", user); ap_log_reason (pg_errstr, r->uri, r); ap_note_basic_auth_failure (r); return AUTH_REQUIRED; }; } } return DECLINED; } /* Send the authentication to the log table */ int pg_log_auth_user (request_rec * r, pg_auth_config_rec * sec, char *user, char *sent_pw) { char sql[MAX_STRING_LEN]; char *s; char fields[MAX_STRING_LEN]; char values[MAX_STRING_LEN]; char safe_user[1 + 2 * strlen (user)]; char safe_pw[1 + 2 * strlen (sent_pw)]; char safe_req[1 + 2 * strlen (r->the_request)]; char ts[MAX_STRING_LEN]; /* time in string format */ struct tm *t; /* time of request start */ /* we do not want to process internal redirect */ if (!ap_is_initial_req (r)) return DECLINED; if ((!sec->auth_pg_log_table) || (!sec->auth_pg_log_uname_field) || (!sec->auth_pg_log_date_field)) { // At least table name, username and date field are specified // send error message and exit return DECLINED; } /* AUD: MAX_STRING_LEN probably isn't always correct */ pg_check_string (safe_user, user, strlen (user)); pg_check_string (safe_pw, sent_pw, strlen (sent_pw)); pg_check_string (safe_req, r->the_request, strlen (r->the_request)); /* time field format */ t = localtime (&r->request_time); snprintf (ts, 100, "%04d-%02d-%02d %02d:%02d:%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); /* SQL Statement, required fields: Username, Date */ snprintf (fields, MAX_STRING_LEN, "%s,%s", sec->auth_pg_log_uname_field, sec->auth_pg_log_date_field); snprintf (values, MAX_STRING_LEN, "'%s','%s'", safe_user, ts); /* Optional parameters */ if (sec->auth_pg_log_addrs_field) { /* IP Address field */ snprintf (sql, MAX_STRING_LEN, ", %s", sec->auth_pg_log_addrs_field); strncat (fields, sql, MAX_STRING_LEN - strlen (fields) - 1); snprintf (sql, MAX_STRING_LEN, ", '%s'", r->connection->remote_ip); strncat (values, sql, MAX_STRING_LEN - strlen (values) - 1); } if (sec->auth_pg_log_pwd_field) { /* Password field , clear WARNING */ snprintf (sql, MAX_STRING_LEN, ", %s", sec->auth_pg_log_pwd_field); strncat (fields, sql, MAX_STRING_LEN - strlen (fields) - 1); snprintf (sql, MAX_STRING_LEN, ", '%s'", safe_pw); strncat (values, sql, MAX_STRING_LEN - strlen (values) - 1); } if (sec->auth_pg_log_uri_field) { /* request string */ snprintf (sql, MAX_STRING_LEN, ", %s", sec->auth_pg_log_uri_field); strncat (fields, sql, MAX_STRING_LEN - strlen (fields) - 1); snprintf (sql, MAX_STRING_LEN, ", '%s'", safe_req); strncat (values, sql, MAX_STRING_LEN - strlen (values) - 1); } snprintf (sql, MAX_STRING_LEN, "insert into %s (%s) values(%s) ; ", sec->auth_pg_log_table, fields, values); s = do_pg_query (r, sql, sec); return (0); } void pg_auth_init_handler (server_rec * s, pool * p) { ap_add_version_component ("mod_auth_pgsql/" AUTH_PGSQL_VERSION); } void * pg_auth_server_init (pool * p, server_rec * s) { // Init the module private memory pool, user for the per directory cache tables if (auth_pgsql_pool == NULL) auth_pgsql_pool = ap_make_sub_pool (NULL); return NULL; } module MODULE_VAR_EXPORT auth_pgsql_module = { STANDARD_MODULE_STUFF, pg_auth_init_handler, /* initializer */ create_pg_auth_dir_config, /* dir config creater */ NULL, /* dir merger --- default is to override */ pg_auth_server_init, /* server config */ NULL, /* merge server config */ pg_auth_cmds, /* command table */ NULL, /* handlers */ NULL, /* filename translation */ pg_authenticate_basic_user, /* check_user_id */ pg_check_auth, /* check auth */ NULL, /* check access */ NULL, /* type_checker */ NULL, /* pre-run fixups */ NULL, /* logger */ NULL, /* header parser */ NULL, /* child_init */ NULL, /* child_exit */ NULL /* post read-request */ };