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 * Version history:
21 * Version 0.2 - 28.07.2004 - first release - Thimo Eichstaedt <apache-mod@digithi.de>
22 * Version 0.3 - 03.08.2004 - code fixup - Thimo Eichstaedt <apache-mod@digithi.de>
23 * Version 0.4 - 05.04.2005 - bug fixes and redirect modified - Thimo Eichstaedt <apache-mod@digithi.de>
24 * Version 0.5 - 01.06.2005 - mysql_close() fix - Thimo Eichstaedt <apache-mod@digithi.de>
25 * Version 0.6 - 20.12.2005 - mysql_otions() added, mysql query modification - Thimo Eichstaedt <apache.mod@digithi.de>
26 * Version 0.7 - 04.01.2006 - load return codes with default values - Thimo Eichstaedt <apache-mod@digithi.de>
27 * Version 0.8 - 26.03.2007 - code cleanup, small memory leak fixed - Thimo Eichstaedt <apache-mod@digithi.de>
28 * Version 0.9 - 23.04.2007 - code/documentation cleanup, persistent database connections added, rename of config vars - Thimo Eichstaedt <apache-mod@digithi.de>
29 * Version 1.0 - 18.06.2009 - AuthCookieSQL_AdditionalSQL added, minor code cleanup - Thimo Eichstaedt <apache-mod@digithi.de>
30 *
31 */
32
33 /********************************************************************************
34 * includes *
35 ********************************************************************************/
36
37 #include "apr_strings.h"
38 #include "httpd.h"
39 #include "http_config.h"
40 #include "http_core.h"
41 #include "http_request.h"
42 #include "http_log.h"
43 #include "mod_auth_cookie_sql2.h"
44
45 #include <time.h> /* for time */
46 #include <string.h> /* for strncmp */
47
48 module AP_MODULE_DECLARE_DATA MODULE_NAME_module;
49
50 /********************************************************************************
51 * public function declarations *
52 ********************************************************************************/
53 static int auth_cookie_sql2_authenticate_user (request_rec *);
54 static void *auth_cookie_sql2_create_auth_dir_config(apr_pool_t *, char *);
55
56 /********************************************************************************
57 * local function declarations *
58 ********************************************************************************/
59 static int check_valid_cookie(request_rec *, auth_cookie_sql2_config_rec *);
60 static int do_redirect(request_rec *);
61
62 /********************************************************************************
63 * local variables *
64 ********************************************************************************/
65 static auth_cookie_sql2_config_rec default_config_rec = {
66 0, /* are we activated ? */
67 NULL, /* Cookie Name */
68 NULL, /* Database host */
69 NULL, /* Database user */
70 NULL, /* Database password */
71 NULL, /* Database name */
72 NULL, /* Database table */
73 0, /* Persistent connection */
74 NULL, /* Database username field */
75 NULL, /* Database sessname field */
76 NULL, /* Database sessval field */
77 NULL, /* Database expiry information field */
78 NULL, /* Database remote ip information field */
79 NULL, /* SQL addon */
80 NULL, /* goto URL if failure */
81 };
82
83 /********************************************************************************
84 * functions *
85 ********************************************************************************/
86
auth_cookie_sql2_create_auth_dir_config(apr_pool_t * p,char * d)87 static void *auth_cookie_sql2_create_auth_dir_config(apr_pool_t *p, char *d) {
88 auth_cookie_sql2_config_rec *conf = apr_pcalloc(p, sizeof(*conf));
89
90 if (conf) {
91 // Set default values
92 *conf=default_config_rec;
93 }
94 return conf;
95 }
96
97 /* this function is called when the child or module is cleared */
auth_cookie_sql2_child_exit(void * data)98 static apr_status_t auth_cookie_sql2_child_exit(void *data) {
99 #ifdef AUTH_COOKIE_SQL2_DEBUG
100 server_rec *s = (server_rec *) data;
101 #endif
102
103 #ifdef AUTH_COOKIE_SQL2_DEBUG
104 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, s, ERRTAG "child exit called.");
105 #endif
106
107 close_db(NULL, NULL, 1);
108 return APR_SUCCESS;
109 }
110
111 /* this function is called the the module is initialized by apache */
auth_cookie_sql2_child_init(apr_pool_t * p,server_rec * s)112 static void auth_cookie_sql2_child_init(apr_pool_t *p, server_rec *s) {
113
114 #ifdef AUTH_COOKIE_SQL2_DEBUG
115 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, s, ERRTAG "child init called.");
116 #endif
117
118 apr_pool_cleanup_register(p,
119 s,
120 auth_cookie_sql2_child_exit,
121 auth_cookie_sql2_child_exit
122 );
123 }
124
125 /* send a redirect to the browser */
do_redirect(request_rec * r)126 static int do_redirect(request_rec *r) {
127 auth_cookie_sql2_config_rec *conf = ap_get_module_config(r->per_dir_config, &MODULE_NAME_module);
128 char *redirect = apr_psprintf(r->pool, "%s?r=%s", conf->failureurl, r->uri);
129
130 if (redirect) {
131 apr_table_setn(r->headers_out, "Location", redirect);
132 return HTTP_MOVED_TEMPORARILY;
133 } else {
134 return HTTP_INTERNAL_SERVER_ERROR;
135 }
136 }
137
138 /* check for a valid cookie */
check_valid_cookie(request_rec * r,auth_cookie_sql2_config_rec * conf)139 static int check_valid_cookie(request_rec *r, auth_cookie_sql2_config_rec *conf) {
140 const char *cookieptr;
141 char *cookies, *value;
142 char username[MAX_USERNAME_LEN + 1];
143 time_t tc;
144 int db_ret=RET_UNAUTHORIZED; //default: unauthorized
145
146 // grab pointer to cookie informations
147 if ((cookieptr = apr_table_get(r->headers_in, "Cookie")) == NULL) {
148
149 // no cookies found at all, so return with failure/unauthorized
150 if (conf->failureurl) {
151 return do_redirect(r);
152 } else {
153 return HTTP_UNAUTHORIZED;
154 }
155 }
156
157 // make a copy of cookies, so we can do what we want (strtok, ...)
158 if ((cookies = apr_palloc(r->pool, strlen(cookieptr) + 2)) == NULL) {
159 // error
160 return HTTP_INTERNAL_SERVER_ERROR;
161 }
162 strcpy (cookies, cookieptr);
163 cookies[0 + strlen(cookieptr)] = ';';
164 cookies[1 + strlen(cookieptr)] = '\0';
165
166 // fetch current time;
167 tc = time(NULL);
168
169 // now check for cookies
170 if (conf->cookiename) {
171 // a cookiename is given, we are searching only for THIS cookie in the DB
172
173 // find cookie we are searching for
174 if ((cookies = strstr(cookies, conf->cookiename)) != NULL) {
175 // found cookie
176
177 if ((value = strchr (cookies, '=')) != NULL) {
178 // found beginning of cookieval
179 if ((value = strtok(value+1, " ;\n\r\t\f")) != NULL) {
180
181 #ifdef AUTH_COOKIE_SQL2_DEBUG
182 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "valid cookie found, data: %s", cookies);
183 #endif
184
185 #if MODULE_MAGIC_NUMBER_MAJOR >= 20111130
186 db_ret=check_against_db(conf, r, conf->cookiename, value, username,r->useragent_ip, conf->sql_addon, tc);
187 #else
188 db_ret=check_against_db(conf, r, conf->cookiename, value, username,r->connection->remote_ip, conf->sql_addon, tc);
189 #endif
190 }
191 }
192 }
193 } else {
194 // no cookiename is given, lets try all cookies the client gave us
195 for (cookies = strtok(cookies, " ;\n\r\t\f"); (cookies); cookies = strtok(NULL, " ;\n\r\t\f")) {
196
197 /* content of cookies should now be like "aaa=bbb"
198 check this and, if not, continue search for a valid cookie */
199 if ((value = strchr (cookies, '=')) == NULL) {
200 continue;
201 }
202
203 #ifdef AUTH_COOKIE_SQL2_DEBUG
204 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "testing against valid cookie: %s", cookies);
205 #endif
206
207 /* modify aaa=bbb\0 to aaa\0bbb\0, so cookie contains aaa\0
208 and value contains bbb\0 */
209 *value = '\0';
210 value++;
211
212 if ((db_ret=check_against_db(conf, r, cookies, value, username,
213 #if MODULE_MAGIC_NUMBER_MAJOR >= 20111130
214 r->useragent_ip,
215 #else
216 r->connection->remote_ip,
217 #endif
218 conf->sql_addon, tc)) == RET_AUTHORIZED) {
219 // found valid cookie
220
221 #ifdef AUTH_COOKIE_SQL2_DEBUG
222 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "valid cookie found");
223 #endif
224
225 break;
226 }
227 }
228 }
229
230 // handle return codes
231 switch (db_ret) {
232 case RET_AUTHORIZED:
233 // valid cookie found
234 r->user = apr_pstrdup(r->pool,username);
235 r->ap_auth_type="Cookie";
236 return OK;
237 break;
238 case RET_UNAUTHORIZED:
239 // no valid information in database found
240 if (conf->failureurl) {
241 return do_redirect(r);
242 } else {
243 return HTTP_UNAUTHORIZED;
244 }
245 break;
246 default:
247 return DECLINED;
248 break;
249 }
250 }
251
252 /* try to authenticate the user */
auth_cookie_sql2_authenticate_user(request_rec * r)253 static int auth_cookie_sql2_authenticate_user (request_rec *r) {
254 auth_cookie_sql2_config_rec *conf = ap_get_module_config(r->per_dir_config, &MODULE_NAME_module);
255
256 if (! conf->activated) {
257 // not active
258 return DECLINED;
259 }
260
261 if ( !conf->dbhost || !conf->dbuser || !conf->dbpassword || !conf->dbname || !conf->dbtable ) {
262 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "please check database connect information, some are missing");
263 return DECLINED;
264 }
265
266 if ( !conf->dbusername_field || !conf->dbsessname_field || !conf->dbsessval_field) {
267 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "please check database field names, some are missing");
268 return DECLINED;
269 }
270
271 #ifdef AUTH_COOKIE_SQL2_DEBUG
272 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "module started.");
273 #endif
274
275 return check_valid_cookie(r,conf);
276
277 }
278
279
280 /* Module data */
281 static const command_rec auth_commands[] = {
282
283 AP_INIT_FLAG("AuthCookieSql", ap_set_flag_slot,
284 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, activated),
285 WHERE_ALLOWED, "Set to on to activate Cookie Auth"),
286
287
288 AP_INIT_TAKE1("AuthCookieSql_CookieName", ap_set_string_slot,
289 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, cookiename),
290 WHERE_ALLOWED, "Name of the cookie"),
291
292 AP_INIT_TAKE1("AuthCookieSql_DBHost", ap_set_string_slot,
293 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, dbhost),
294 WHERE_ALLOWED, "Host on which DB server resides"),
295
296 AP_INIT_TAKE1("AuthCookieSql_DBUser", ap_set_string_slot,
297 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, dbuser),
298 WHERE_ALLOWED, "Username for DB access"),
299
300 AP_INIT_TAKE1("AuthCookieSql_DBPassword", ap_set_string_slot,
301 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, dbpassword),
302 WHERE_ALLOWED, "Password for DB access"),
303
304 AP_INIT_TAKE1("AuthCookieSql_DBName", ap_set_string_slot,
305 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, dbname),
306 WHERE_ALLOWED, "Database name"),
307
308 AP_INIT_TAKE1("AuthCookieSql_DBTable", ap_set_string_slot,
309 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, dbtable),
310 WHERE_ALLOWED, "Table name in database"),
311
312 AP_INIT_FLAG("AuthCookieSql_DBPersistent", ap_set_flag_slot,
313 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, dbpersistent),
314 WHERE_ALLOWED, "If enabled connection to database is held open during requests"),
315
316 AP_INIT_TAKE1("AuthCookieSql_UsernameField", ap_set_string_slot,
317 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, dbusername_field),
318 WHERE_ALLOWED, "Field name in database where username is hold"),
319
320 AP_INIT_TAKE1("AuthCookieSql_SessnameField", ap_set_string_slot,
321 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, dbsessname_field),
322 WHERE_ALLOWED, "Field name in database where session name is hold"),
323
324 AP_INIT_TAKE1("AuthCookieSql_SessvalField", ap_set_string_slot,
325 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, dbsessval_field),
326 WHERE_ALLOWED, "Field name in database where session key is hold"),
327
328 AP_INIT_TAKE1("AuthCookieSql_ExpiryField", ap_set_string_slot,
329 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, dbexpiry_field),
330 WHERE_ALLOWED, "Field name in database where expiry information is hold"),
331
332 AP_INIT_TAKE1("AuthCookieSql_RemoteIPField", ap_set_string_slot,
333 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, dbremoteip_field),
334 WHERE_ALLOWED, "Field name in database where remote ip information is hold"),
335
336 AP_INIT_TAKE1("AuthCookieSql_AdditionalSql", ap_set_string_slot,
337 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, sql_addon),
338 WHERE_ALLOWED, "SQL clause which is appended after the SQL statement"),
339
340 AP_INIT_TAKE1("AuthCookieSql_FailureURL", ap_set_string_slot,
341 (void *)APR_OFFSETOF(auth_cookie_sql2_config_rec, failureurl),
342 WHERE_ALLOWED, "URL where to go to when auth not successful"),
343
344 { NULL }
345 };
346
347 /* register hooks at the apache server */
auth_cookie_sql2_register_hooks(apr_pool_t * p)348 static void auth_cookie_sql2_register_hooks(apr_pool_t *p) {
349
350 /* Hook in and great, we are the first :) */
351 ap_hook_check_user_id(auth_cookie_sql2_authenticate_user,NULL,NULL,APR_HOOK_FIRST);
352
353 /* hook in for initialisation */
354 ap_hook_child_init(auth_cookie_sql2_child_init, NULL, NULL, APR_HOOK_MIDDLE);
355 }
356
357 /* module data */
358 module AP_MODULE_DECLARE_DATA MODULE_NAME_module = {
359 STANDARD20_MODULE_STUFF,
360 auth_cookie_sql2_create_auth_dir_config, /* per-directory config creater */
361 NULL, /* dir merger --- default is to override */
362 NULL, /* server config creator */
363 NULL, /* server config merger */
364 auth_commands, /* command table */
365 auth_cookie_sql2_register_hooks /* set up other request processing hooks */
366 };
367