1 /* Copyright 2004-2007 Thimo Eichstaedt <apache-mod@digithi.de>
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 *
15 * Based on code from
16 * Dirk.vanGulik@jrc.it - original module
17 * mjd@pobox.com - bug fixes, conversion from mSQL to MySQL
18 *
19 */
20
21 /********************************************************************************
22 * includes *
23 ********************************************************************************/
24 #include <string.h>
25 #include <mysql.h>
26 #include "apr_strings.h"
27 #include "httpd.h"
28 #include "http_config.h"
29 #include "http_core.h"
30 #include "http_request.h"
31 #include "http_log.h"
32
33 #include "mod_auth_cookie_sql2.h"
34
35 static MYSQL *dbh = NULL;
36
37 /********************************************************************************
38 * functions *
39 ********************************************************************************/
40
41 /* try to open connection */
open_db(auth_cookie_sql2_config_rec * conf,request_rec * r)42 int open_db(auth_cookie_sql2_config_rec *conf, request_rec *r) {
43
44 if (dbh != NULL) {
45 if (mysql_ping(dbh) == 0) {
46 return RET_OK;
47 } else {
48 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "database connection died, trying to establish a new one.");
49 mysql_close(dbh);
50 dbh = NULL;
51 }
52 }
53
54 if ((dbh=mysql_init(NULL)) == NULL) {
55 return RET_ERR;
56 }
57
58 mysql_options(dbh,MYSQL_READ_DEFAULT_GROUP,MY_MYSQL_APPNAME);
59
60 if (mysql_real_connect(dbh, conf->dbhost, conf->dbuser, conf->dbpassword, conf->dbname, 0, NULL, 0) == NULL) {
61 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "couldn't connect to database: %s", mysql_error(dbh));
62
63 return RET_ERR;
64 } else {
65 return RET_OK;
66 }
67 }
68
69 /* close the database connection */
close_db(auth_cookie_sql2_config_rec * conf,request_rec * r,int force)70 int close_db(auth_cookie_sql2_config_rec *conf, request_rec *r, int force) {
71 if (dbh == NULL) {
72 // do nothing
73 } else if (conf == NULL) {
74 mysql_close(dbh);
75 dbh=NULL;
76 } else if (conf->dbpersistent == 0 || force == 1) {
77 mysql_close(dbh);
78 dbh=NULL;
79 }
80
81 return RET_OK;
82 }
83
84 /* check given cookie against db */
check_against_db(auth_cookie_sql2_config_rec * conf,request_rec * r,char * cookiename,char * cookieval,char * username,char * remoteip,char * addon,time_t tc)85 int check_against_db(auth_cookie_sql2_config_rec *conf, request_rec *r, char *cookiename, char *cookieval, char *username, char *remoteip, char *addon, time_t tc) {
86 MYSQL_RES *res;
87 MYSQL_ROW row;
88 apr_pool_t *p = r->pool;
89 char *esc_cookiename, *esc_cookieval;
90 char *query, *queryopt;
91 int ulen;
92 int ret=RET_ERR; // default
93
94 if (open_db(conf,r) != RET_OK) {
95 ret=RET_ERR;
96 goto check_against_db_mysql_close;
97 }
98
99 /* escape cookiename, size of cookiename can grow double as big as before */
100 ulen=strlen(cookiename);
101 if (! (esc_cookiename = apr_palloc(p, (ulen*2) + 1))) {
102 ret=RET_ERR;
103 goto check_against_db_mysql_close;
104 }
105
106 mysql_real_escape_string(dbh, esc_cookiename, cookiename, ulen);
107
108 /* escape cookieval, can grow to double size, too */
109 ulen=strlen(cookieval);
110 if ((esc_cookieval = apr_palloc(p, (ulen*2) + 1)) == NULL) {
111 ret=RET_ERR;
112 goto check_against_db_mysql_close;
113 }
114
115 mysql_real_escape_string(dbh, esc_cookieval, cookieval, ulen);
116
117 /* prepare query string, queryopt contains optional arguments */
118 if ((queryopt = apr_palloc(r->pool,sizeof(char))) == NULL) {
119 ret=RET_ERR;
120 goto check_against_db_mysql_close;
121 }
122
123 *queryopt = '\0';
124
125 /* Check for cookie Expiry ? */
126 if (conf->dbexpiry_field) {
127 queryopt = apr_psprintf(p, "%s AND %s > %lu", queryopt, conf->dbexpiry_field,tc);
128 }
129
130 /* Check for right remote ip ? */
131 if (conf->dbremoteip_field) {
132 queryopt = apr_psprintf(p, "%s AND %s='%s'", queryopt, conf->dbremoteip_field, remoteip);
133 }
134
135 if (addon) {
136 queryopt = apr_psprintf(p,"%s %s", queryopt, addon);
137 }
138
139 /* Generate query */
140 query = apr_psprintf(p, "SELECT %s FROM %s WHERE %s='%s' AND %s='%s'%s",
141 conf->dbusername_field,
142 conf->dbtable,
143 conf->dbsessname_field,
144 esc_cookiename,
145 conf->dbsessval_field,
146 esc_cookieval,
147 queryopt);
148
149 if (query == NULL) {
150 ret=RET_ERR;
151 goto check_against_db_mysql_close;
152 }
153
154 if (mysql_query(dbh, query) != 0) {
155 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "error in MySQL query \"%s\": %s", query, mysql_error(dbh));
156
157 ret=RET_ERR;
158 goto check_against_db_mysql_close;
159 }
160
161 if ((res = mysql_store_result(dbh)) == NULL) {
162 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "couldn't store query result: %s", mysql_error(dbh));
163
164 ret=RET_ERR;
165 goto check_against_db_mysql_close;
166 }
167
168 /* if any other number of results than 1 found, clean up and return with 0 */
169 if (mysql_num_rows(res) != 1) {
170
171 ret=RET_UNAUTHORIZED;
172 goto check_against_db_mysql_free_result;
173 }
174
175 /* got a result, fetch row */
176 if ((row = mysql_fetch_row(res)) == NULL) {
177 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "couldn't fetch row: %s", mysql_error(dbh));
178
179 ret=RET_ERR;
180 goto check_against_db_mysql_free_result;
181 }
182
183 /* check for length of row */
184 if (strlen (row[0]) > MAX_USERNAME_LEN) {
185 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "fetched username from DB, but is longer than max length %d", MAX_USERNAME_LEN);
186
187 ret=RET_ERR;
188 goto check_against_db_mysql_free_result;
189 }
190
191 /* all tests passed, copy content of row[0] to username */
192 strcpy(username, row[0]);
193 ret=RET_AUTHORIZED;
194
195 check_against_db_mysql_free_result:
196 mysql_free_result(res);
197
198 check_against_db_mysql_close:
199 close_db(conf,r,0);
200 return ret;
201 }
202