1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "apr_strings.h"
18 #include "apr_lib.h" /* for apr_isspace */
19 #include "apr_base64.h" /* for apr_base64_decode et al */
20 #define APR_WANT_STRFUNC /* for strcasecmp */
21 #include "apr_want.h"
22
23 #include "ap_config.h"
24 #include "httpd.h"
25 #include "http_config.h"
26 #include "http_core.h"
27 #include "http_log.h"
28 #include "http_protocol.h"
29 #include "http_request.h"
30 #include "ap_provider.h"
31 #include "util_md5.h"
32 #include "ap_expr.h"
33
34 #include "mod_auth.h"
35 #include "mod_session.h"
36 #include "mod_request.h"
37
38 #define FORM_LOGIN_HANDLER "form-login-handler"
39 #define FORM_LOGOUT_HANDLER "form-logout-handler"
40 #define FORM_REDIRECT_HANDLER "form-redirect-handler"
41 #define MOD_AUTH_FORM_HASH "site"
42
43 static int (*ap_session_load_fn) (request_rec * r, session_rec ** z) = NULL;
44 static apr_status_t (*ap_session_get_fn)(request_rec * r, session_rec * z,
45 const char *key, const char **value) = NULL;
46 static apr_status_t (*ap_session_set_fn)(request_rec * r, session_rec * z,
47 const char *key, const char *value) = NULL;
48 static void (*ap_request_insert_filter_fn) (request_rec * r) = NULL;
49 static void (*ap_request_remove_filter_fn) (request_rec * r) = NULL;
50
51 typedef struct {
52 authn_provider_list *providers;
53 char *dir;
54 int authoritative;
55 int authoritative_set;
56 const char *site;
57 int site_set;
58 const char *username;
59 int username_set;
60 const char *password;
61 int password_set;
62 apr_size_t form_size;
63 int form_size_set;
64 int fakebasicauth;
65 int fakebasicauth_set;
66 const char *location;
67 int location_set;
68 const char *method;
69 int method_set;
70 const char *mimetype;
71 int mimetype_set;
72 const char *body;
73 int body_set;
74 int disable_no_store;
75 int disable_no_store_set;
76 ap_expr_info_t *loginsuccess;
77 int loginsuccess_set;
78 ap_expr_info_t *loginrequired;
79 int loginrequired_set;
80 ap_expr_info_t *logout;
81 int logout_set;
82 } auth_form_config_rec;
83
create_auth_form_dir_config(apr_pool_t * p,char * d)84 static void *create_auth_form_dir_config(apr_pool_t * p, char *d)
85 {
86 auth_form_config_rec *conf = apr_pcalloc(p, sizeof(*conf));
87
88 conf->dir = d;
89 /* Any failures are fatal. */
90 conf->authoritative = 1;
91
92 /* form size defaults to 8k */
93 conf->form_size = HUGE_STRING_LEN;
94
95 /* default form field names */
96 conf->username = "httpd_username";
97 conf->password = "httpd_password";
98 conf->location = "httpd_location";
99 conf->method = "httpd_method";
100 conf->mimetype = "httpd_mimetype";
101 conf->body = "httpd_body";
102
103 return conf;
104 }
105
merge_auth_form_dir_config(apr_pool_t * p,void * basev,void * addv)106 static void *merge_auth_form_dir_config(apr_pool_t * p, void *basev, void *addv)
107 {
108 auth_form_config_rec *new = (auth_form_config_rec *) apr_pcalloc(p, sizeof(auth_form_config_rec));
109 auth_form_config_rec *add = (auth_form_config_rec *) addv;
110 auth_form_config_rec *base = (auth_form_config_rec *) basev;
111
112 new->providers = !add->providers ? base->providers : add->providers;
113 new->authoritative = (add->authoritative_set == 0) ? base->authoritative : add->authoritative;
114 new->authoritative_set = add->authoritative_set || base->authoritative_set;
115 new->site = (add->site_set == 0) ? base->site : add->site;
116 new->site_set = add->site_set || base->site_set;
117 new->username = (add->username_set == 0) ? base->username : add->username;
118 new->username_set = add->username_set || base->username_set;
119 new->password = (add->password_set == 0) ? base->password : add->password;
120 new->password_set = add->password_set || base->password_set;
121 new->location = (add->location_set == 0) ? base->location : add->location;
122 new->location_set = add->location_set || base->location_set;
123 new->form_size = (add->form_size_set == 0) ? base->form_size : add->form_size;
124 new->form_size_set = add->form_size_set || base->form_size_set;
125 new->fakebasicauth = (add->fakebasicauth_set == 0) ? base->fakebasicauth : add->fakebasicauth;
126 new->fakebasicauth_set = add->fakebasicauth_set || base->fakebasicauth_set;
127 new->method = (add->method_set == 0) ? base->method : add->method;
128 new->method_set = add->method_set || base->method_set;
129 new->mimetype = (add->mimetype_set == 0) ? base->mimetype : add->mimetype;
130 new->mimetype_set = add->mimetype_set || base->mimetype_set;
131 new->body = (add->body_set == 0) ? base->body : add->body;
132 new->body_set = add->body_set || base->body_set;
133 new->disable_no_store = (add->disable_no_store_set == 0) ? base->disable_no_store : add->disable_no_store;
134 new->disable_no_store_set = add->disable_no_store_set || base->disable_no_store_set;
135 new->loginsuccess = (add->loginsuccess_set == 0) ? base->loginsuccess : add->loginsuccess;
136 new->loginsuccess_set = add->loginsuccess_set || base->loginsuccess_set;
137 new->loginrequired = (add->loginrequired_set == 0) ? base->loginrequired : add->loginrequired;
138 new->loginrequired_set = add->loginrequired_set || base->loginrequired_set;
139 new->logout = (add->logout_set == 0) ? base->logout : add->logout;
140 new->logout_set = add->logout_set || base->logout_set;
141
142 return new;
143 }
144
add_authn_provider(cmd_parms * cmd,void * config,const char * arg)145 static const char *add_authn_provider(cmd_parms * cmd, void *config,
146 const char *arg)
147 {
148 auth_form_config_rec *conf = (auth_form_config_rec *) config;
149 authn_provider_list *newp;
150
151 newp = apr_pcalloc(cmd->pool, sizeof(authn_provider_list));
152 newp->provider_name = apr_pstrdup(cmd->pool, arg);
153
154 /* lookup and cache the actual provider now */
155 newp->provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP,
156 newp->provider_name,
157 AUTHN_PROVIDER_VERSION);
158
159 if (newp->provider == NULL) {
160 /*
161 * by the time they use it, the provider should be loaded and
162 * registered with us.
163 */
164 return apr_psprintf(cmd->pool,
165 "Unknown Authn provider: %s",
166 newp->provider_name);
167 }
168
169 if (!newp->provider->check_password) {
170 /* if it doesn't provide the appropriate function, reject it */
171 return apr_psprintf(cmd->pool,
172 "The '%s' Authn provider doesn't support "
173 "Form Authentication", newp->provider_name);
174 }
175
176 if (!ap_session_load_fn || !ap_session_get_fn || !ap_session_set_fn) {
177 ap_session_load_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_load);
178 ap_session_get_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_get);
179 ap_session_set_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_set);
180 if (!ap_session_load_fn || !ap_session_get_fn || !ap_session_set_fn) {
181 return "You must load mod_session to enable the mod_auth_form "
182 "functions";
183 }
184 }
185
186 if (!ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
187 ap_request_insert_filter_fn = APR_RETRIEVE_OPTIONAL_FN(ap_request_insert_filter);
188 ap_request_remove_filter_fn = APR_RETRIEVE_OPTIONAL_FN(ap_request_remove_filter);
189 if (!ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
190 return "You must load mod_request to enable the mod_auth_form "
191 "functions";
192 }
193 }
194
195 /* Add it to the list now. */
196 if (!conf->providers) {
197 conf->providers = newp;
198 }
199 else {
200 authn_provider_list *last = conf->providers;
201
202 while (last->next) {
203 last = last->next;
204 }
205 last->next = newp;
206 }
207
208 return NULL;
209 }
210
211 /**
212 * Sanity check a given string that it exists, is not empty,
213 * and does not contain special characters.
214 */
check_string(cmd_parms * cmd,const char * string)215 static const char *check_string(cmd_parms * cmd, const char *string)
216 {
217 if (!string || !*string || ap_strchr_c(string, '=') || ap_strchr_c(string, '&')) {
218 return apr_pstrcat(cmd->pool, cmd->directive->directive,
219 " cannot be empty, or contain '=' or '&'.",
220 NULL);
221 }
222 return NULL;
223 }
224
set_cookie_form_location(cmd_parms * cmd,void * config,const char * location)225 static const char *set_cookie_form_location(cmd_parms * cmd, void *config, const char *location)
226 {
227 auth_form_config_rec *conf = (auth_form_config_rec *) config;
228 conf->location = location;
229 conf->location_set = 1;
230 return check_string(cmd, location);
231 }
232
set_cookie_form_username(cmd_parms * cmd,void * config,const char * username)233 static const char *set_cookie_form_username(cmd_parms * cmd, void *config, const char *username)
234 {
235 auth_form_config_rec *conf = (auth_form_config_rec *) config;
236 conf->username = username;
237 conf->username_set = 1;
238 return check_string(cmd, username);
239 }
240
set_cookie_form_password(cmd_parms * cmd,void * config,const char * password)241 static const char *set_cookie_form_password(cmd_parms * cmd, void *config, const char *password)
242 {
243 auth_form_config_rec *conf = (auth_form_config_rec *) config;
244 conf->password = password;
245 conf->password_set = 1;
246 return check_string(cmd, password);
247 }
248
set_cookie_form_method(cmd_parms * cmd,void * config,const char * method)249 static const char *set_cookie_form_method(cmd_parms * cmd, void *config, const char *method)
250 {
251 auth_form_config_rec *conf = (auth_form_config_rec *) config;
252 conf->method = method;
253 conf->method_set = 1;
254 return check_string(cmd, method);
255 }
256
set_cookie_form_mimetype(cmd_parms * cmd,void * config,const char * mimetype)257 static const char *set_cookie_form_mimetype(cmd_parms * cmd, void *config, const char *mimetype)
258 {
259 auth_form_config_rec *conf = (auth_form_config_rec *) config;
260 conf->mimetype = mimetype;
261 conf->mimetype_set = 1;
262 return check_string(cmd, mimetype);
263 }
264
set_cookie_form_body(cmd_parms * cmd,void * config,const char * body)265 static const char *set_cookie_form_body(cmd_parms * cmd, void *config, const char *body)
266 {
267 auth_form_config_rec *conf = (auth_form_config_rec *) config;
268 conf->body = body;
269 conf->body_set = 1;
270 return check_string(cmd, body);
271 }
272
set_cookie_form_size(cmd_parms * cmd,void * config,const char * arg)273 static const char *set_cookie_form_size(cmd_parms * cmd, void *config,
274 const char *arg)
275 {
276 auth_form_config_rec *conf = config;
277 apr_off_t size;
278
279 if (APR_SUCCESS != apr_strtoff(&size, arg, NULL, 10)
280 || size < 0 || size > APR_SIZE_MAX) {
281 return "AuthCookieFormSize must be a size in bytes, or zero.";
282 }
283 conf->form_size = (apr_size_t)size;
284 conf->form_size_set = 1;
285
286 return NULL;
287 }
288
set_login_required_location(cmd_parms * cmd,void * config,const char * loginrequired)289 static const char *set_login_required_location(cmd_parms * cmd, void *config, const char *loginrequired)
290 {
291 auth_form_config_rec *conf = (auth_form_config_rec *) config;
292 const char *err;
293
294 conf->loginrequired = ap_expr_parse_cmd(cmd, loginrequired, AP_EXPR_FLAG_STRING_RESULT,
295 &err, NULL);
296 if (err) {
297 return apr_psprintf(cmd->pool,
298 "Could not parse login required expression '%s': %s",
299 loginrequired, err);
300 }
301 conf->loginrequired_set = 1;
302
303 return NULL;
304 }
305
set_login_success_location(cmd_parms * cmd,void * config,const char * loginsuccess)306 static const char *set_login_success_location(cmd_parms * cmd, void *config, const char *loginsuccess)
307 {
308 auth_form_config_rec *conf = (auth_form_config_rec *) config;
309 const char *err;
310
311 conf->loginsuccess = ap_expr_parse_cmd(cmd, loginsuccess, AP_EXPR_FLAG_STRING_RESULT,
312 &err, NULL);
313 if (err) {
314 return apr_psprintf(cmd->pool,
315 "Could not parse login success expression '%s': %s",
316 loginsuccess, err);
317 }
318 conf->loginsuccess_set = 1;
319
320 return NULL;
321 }
322
set_logout_location(cmd_parms * cmd,void * config,const char * logout)323 static const char *set_logout_location(cmd_parms * cmd, void *config, const char *logout)
324 {
325 auth_form_config_rec *conf = (auth_form_config_rec *) config;
326 const char *err;
327
328 conf->logout = ap_expr_parse_cmd(cmd, logout, AP_EXPR_FLAG_STRING_RESULT,
329 &err, NULL);
330 if (err) {
331 return apr_psprintf(cmd->pool,
332 "Could not parse logout required expression '%s': %s",
333 logout, err);
334 }
335 conf->logout_set = 1;
336
337 return NULL;
338 }
339
set_site_passphrase(cmd_parms * cmd,void * config,const char * site)340 static const char *set_site_passphrase(cmd_parms * cmd, void *config, const char *site)
341 {
342 auth_form_config_rec *conf = (auth_form_config_rec *) config;
343 conf->site = site;
344 conf->site_set = 1;
345 return NULL;
346 }
347
set_authoritative(cmd_parms * cmd,void * config,int flag)348 static const char *set_authoritative(cmd_parms * cmd, void *config, int flag)
349 {
350 auth_form_config_rec *conf = (auth_form_config_rec *) config;
351 conf->authoritative = flag;
352 conf->authoritative_set = 1;
353 return NULL;
354 }
355
set_fake_basic_auth(cmd_parms * cmd,void * config,int flag)356 static const char *set_fake_basic_auth(cmd_parms * cmd, void *config, int flag)
357 {
358 auth_form_config_rec *conf = (auth_form_config_rec *) config;
359 conf->fakebasicauth = flag;
360 conf->fakebasicauth_set = 1;
361 return NULL;
362 }
363
set_disable_no_store(cmd_parms * cmd,void * config,int flag)364 static const char *set_disable_no_store(cmd_parms * cmd, void *config, int flag)
365 {
366 auth_form_config_rec *conf = (auth_form_config_rec *) config;
367 conf->disable_no_store = flag;
368 conf->disable_no_store_set = 1;
369 return NULL;
370 }
371
372 static const command_rec auth_form_cmds[] =
373 {
374 AP_INIT_ITERATE("AuthFormProvider", add_authn_provider, NULL, OR_AUTHCFG,
375 "specify the auth providers for a directory or location"),
376 AP_INIT_TAKE1("AuthFormUsername", set_cookie_form_username, NULL, OR_AUTHCFG,
377 "The field of the login form carrying the username"),
378 AP_INIT_TAKE1("AuthFormPassword", set_cookie_form_password, NULL, OR_AUTHCFG,
379 "The field of the login form carrying the password"),
380 AP_INIT_TAKE1("AuthFormLocation", set_cookie_form_location, NULL, OR_AUTHCFG,
381 "The field of the login form carrying the URL to redirect on "
382 "successful login."),
383 AP_INIT_TAKE1("AuthFormMethod", set_cookie_form_method, NULL, OR_AUTHCFG,
384 "The field of the login form carrying the original request method."),
385 AP_INIT_TAKE1("AuthFormMimetype", set_cookie_form_mimetype, NULL, OR_AUTHCFG,
386 "The field of the login form carrying the original request mimetype."),
387 AP_INIT_TAKE1("AuthFormBody", set_cookie_form_body, NULL, OR_AUTHCFG,
388 "The field of the login form carrying the urlencoded original request "
389 "body."),
390 AP_INIT_TAKE1("AuthFormSize", set_cookie_form_size, NULL, ACCESS_CONF,
391 "Maximum size of body parsed by the form parser"),
392 AP_INIT_TAKE1("AuthFormLoginRequiredLocation", set_login_required_location,
393 NULL, OR_AUTHCFG,
394 "If set, redirect the browser to this URL rather than "
395 "return 401 Not Authorized."),
396 AP_INIT_TAKE1("AuthFormLoginSuccessLocation", set_login_success_location,
397 NULL, OR_AUTHCFG,
398 "If set, redirect the browser to this URL when a login "
399 "processed by the login handler is successful."),
400 AP_INIT_TAKE1("AuthFormLogoutLocation", set_logout_location,
401 NULL, OR_AUTHCFG,
402 "The URL of the logout successful page. An attempt to access an "
403 "URL handled by the handler " FORM_LOGOUT_HANDLER " will result "
404 "in an redirect to this page after logout."),
405 AP_INIT_TAKE1("AuthFormSitePassphrase", set_site_passphrase,
406 NULL, OR_AUTHCFG,
407 "If set, use this passphrase to determine whether the user should "
408 "be authenticated. Bypasses the user authentication check on "
409 "every website hit, and is useful for high traffic sites."),
410 AP_INIT_FLAG("AuthFormAuthoritative", set_authoritative,
411 NULL, OR_AUTHCFG,
412 "Set to 'Off' to allow access control to be passed along to "
413 "lower modules if the UserID is not known to this module"),
414 AP_INIT_FLAG("AuthFormFakeBasicAuth", set_fake_basic_auth,
415 NULL, OR_AUTHCFG,
416 "Set to 'On' to pass through authentication to the rest of the "
417 "server as a basic authentication header."),
418 AP_INIT_FLAG("AuthFormDisableNoStore", set_disable_no_store,
419 NULL, OR_AUTHCFG,
420 "Set to 'on' to stop the sending of a Cache-Control no-store header with "
421 "the login screen. This allows the browser to cache the credentials, but "
422 "at the risk of it being possible for the login form to be resubmitted "
423 "and revealed to the backend server through XSS. Use at own risk."),
424 {NULL}
425 };
426
427 module AP_MODULE_DECLARE_DATA auth_form_module;
428
note_cookie_auth_failure(request_rec * r)429 static void note_cookie_auth_failure(request_rec * r)
430 {
431 auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
432 &auth_form_module);
433
434 if (conf->location && ap_strchr_c(conf->location, ':')) {
435 apr_table_setn(r->err_headers_out, "Location", conf->location);
436 }
437 }
438
hook_note_cookie_auth_failure(request_rec * r,const char * auth_type)439 static int hook_note_cookie_auth_failure(request_rec * r,
440 const char *auth_type)
441 {
442 if (strcasecmp(auth_type, "form"))
443 return DECLINED;
444
445 note_cookie_auth_failure(r);
446 return OK;
447 }
448
449 /**
450 * Set the auth username and password into the main request
451 * notes table.
452 */
set_notes_auth(request_rec * r,const char * user,const char * pw,const char * method,const char * mimetype)453 static void set_notes_auth(request_rec * r,
454 const char *user, const char *pw,
455 const char *method, const char *mimetype)
456 {
457 apr_table_t *notes = NULL;
458 const char *authname;
459
460 /* find the main request */
461 while (r->main) {
462 r = r->main;
463 }
464 /* find the first redirect */
465 while (r->prev) {
466 r = r->prev;
467 }
468 notes = r->notes;
469
470 /* have we isolated the user and pw before? */
471 authname = ap_auth_name(r);
472 if (user) {
473 apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-user", NULL), user);
474 }
475 if (pw) {
476 apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-pw", NULL), pw);
477 }
478 if (method) {
479 apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-method", NULL), method);
480 }
481 if (mimetype) {
482 apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-mimetype", NULL), mimetype);
483 }
484
485 }
486
487 /**
488 * Get the auth username and password from the main request
489 * notes table, if present.
490 */
get_notes_auth(request_rec * r,const char ** user,const char ** pw,const char ** method,const char ** mimetype)491 static void get_notes_auth(request_rec *r,
492 const char **user, const char **pw,
493 const char **method, const char **mimetype)
494 {
495 const char *authname;
496 request_rec *m = r;
497
498 /* find the main request */
499 while (m->main) {
500 m = m->main;
501 }
502 /* find the first redirect */
503 while (m->prev) {
504 m = m->prev;
505 }
506
507 /* have we isolated the user and pw before? */
508 authname = ap_auth_name(m);
509 if (user) {
510 *user = (char *) apr_table_get(m->notes, apr_pstrcat(m->pool, authname, "-user", NULL));
511 }
512 if (pw) {
513 *pw = (char *) apr_table_get(m->notes, apr_pstrcat(m->pool, authname, "-pw", NULL));
514 }
515 if (method) {
516 *method = (char *) apr_table_get(m->notes, apr_pstrcat(m->pool, authname, "-method", NULL));
517 }
518 if (mimetype) {
519 *mimetype = (char *) apr_table_get(m->notes, apr_pstrcat(m->pool, authname, "-mimetype", NULL));
520 }
521
522 /* set the user, even though the user is unauthenticated at this point */
523 if (user && *user) {
524 r->user = (char *) *user;
525 }
526
527 ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, r,
528 "from notes: user: %s, pw: %s, method: %s, mimetype: %s",
529 user ? *user : "<null>", pw ? *pw : "<null>",
530 method ? *method : "<null>", mimetype ? *mimetype : "<null>");
531
532 }
533
534 /**
535 * Set the auth username and password into the session.
536 *
537 * If either the username, or the password are NULL, the username
538 * and/or password will be removed from the session.
539 */
set_session_auth(request_rec * r,const char * user,const char * pw,const char * site)540 static apr_status_t set_session_auth(request_rec * r,
541 const char *user, const char *pw, const char *site)
542 {
543 const char *hash = NULL;
544 const char *authname = ap_auth_name(r);
545 session_rec *z = NULL;
546
547 if (site) {
548 hash = ap_md5(r->pool,
549 (unsigned char *) apr_pstrcat(r->pool, user, ":", site, NULL));
550 }
551
552 ap_session_load_fn(r, &z);
553 ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_USER, NULL), user);
554 ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_PW, NULL), pw);
555 ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_AUTH_FORM_HASH, NULL), hash);
556
557 return APR_SUCCESS;
558
559 }
560
561 /**
562 * Get the auth username and password from the main request
563 * notes table, if present.
564 */
get_session_auth(request_rec * r,const char ** user,const char ** pw,const char ** hash)565 static apr_status_t get_session_auth(request_rec * r,
566 const char **user, const char **pw, const char **hash)
567 {
568 const char *authname = ap_auth_name(r);
569 session_rec *z = NULL;
570 ap_session_load_fn(r, &z);
571
572 if (user) {
573 ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_USER, NULL), user);
574 }
575 if (pw) {
576 ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_PW, NULL), pw);
577 }
578 if (hash) {
579 ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_AUTH_FORM_HASH, NULL), hash);
580 }
581
582 /* set the user, even though the user is unauthenticated at this point */
583 if (user && *user) {
584 r->user = (char *) *user;
585 }
586
587 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
588 "from session: " MOD_SESSION_USER ": %s, " MOD_SESSION_PW
589 ": %s, " MOD_AUTH_FORM_HASH ": %s",
590 user ? *user : "<null>", pw ? *pw : "<null>",
591 hash ? *hash : "<null>");
592
593 return APR_SUCCESS;
594
595 }
596
597 /**
598 * Isolate the username and password in a POSTed form with the
599 * username in the "username" field, and the password in the
600 * "password" field.
601 *
602 * If either the username or the password is missing, this
603 * function will return HTTP_UNAUTHORIZED.
604 *
605 * The location field is considered optional, and will be returned
606 * if present.
607 */
get_form_auth(request_rec * r,const char * username,const char * password,const char * location,const char * method,const char * mimetype,const char * body,const char ** sent_user,const char ** sent_pw,const char ** sent_loc,const char ** sent_method,const char ** sent_mimetype,apr_bucket_brigade ** sent_body,auth_form_config_rec * conf)608 static int get_form_auth(request_rec * r,
609 const char *username,
610 const char *password,
611 const char *location,
612 const char *method,
613 const char *mimetype,
614 const char *body,
615 const char **sent_user,
616 const char **sent_pw,
617 const char **sent_loc,
618 const char **sent_method,
619 const char **sent_mimetype,
620 apr_bucket_brigade **sent_body,
621 auth_form_config_rec * conf)
622 {
623 /* sanity check - are we a POST request? */
624
625 /* find the username and password in the form */
626 apr_array_header_t *pairs = NULL;
627 apr_off_t len;
628 apr_size_t size;
629 int res;
630 char *buffer;
631
632 /* have we isolated the user and pw before? */
633 get_notes_auth(r, sent_user, sent_pw, sent_method, sent_mimetype);
634 if (*sent_user && *sent_pw) {
635 return OK;
636 }
637
638 res = ap_parse_form_data(r, NULL, &pairs, -1, conf->form_size);
639 if (res != OK) {
640 return res;
641 }
642 while (pairs && !apr_is_empty_array(pairs)) {
643 ap_form_pair_t *pair = (ap_form_pair_t *) apr_array_pop(pairs);
644 if (username && !strcmp(pair->name, username) && sent_user) {
645 apr_brigade_length(pair->value, 1, &len);
646 size = (apr_size_t) len;
647 buffer = apr_palloc(r->pool, size + 1);
648 apr_brigade_flatten(pair->value, buffer, &size);
649 buffer[len] = 0;
650 *sent_user = buffer;
651 }
652 else if (password && !strcmp(pair->name, password) && sent_pw) {
653 apr_brigade_length(pair->value, 1, &len);
654 size = (apr_size_t) len;
655 buffer = apr_palloc(r->pool, size + 1);
656 apr_brigade_flatten(pair->value, buffer, &size);
657 buffer[len] = 0;
658 *sent_pw = buffer;
659 }
660 else if (location && !strcmp(pair->name, location) && sent_loc) {
661 apr_brigade_length(pair->value, 1, &len);
662 size = (apr_size_t) len;
663 buffer = apr_palloc(r->pool, size + 1);
664 apr_brigade_flatten(pair->value, buffer, &size);
665 buffer[len] = 0;
666 *sent_loc = buffer;
667 }
668 else if (method && !strcmp(pair->name, method) && sent_method) {
669 apr_brigade_length(pair->value, 1, &len);
670 size = (apr_size_t) len;
671 buffer = apr_palloc(r->pool, size + 1);
672 apr_brigade_flatten(pair->value, buffer, &size);
673 buffer[len] = 0;
674 *sent_method = buffer;
675 }
676 else if (mimetype && !strcmp(pair->name, mimetype) && sent_mimetype) {
677 apr_brigade_length(pair->value, 1, &len);
678 size = (apr_size_t) len;
679 buffer = apr_palloc(r->pool, size + 1);
680 apr_brigade_flatten(pair->value, buffer, &size);
681 buffer[len] = 0;
682 *sent_mimetype = buffer;
683 }
684 else if (body && !strcmp(pair->name, body) && sent_body) {
685 *sent_body = pair->value;
686 }
687 }
688
689 /* set the user, even though the user is unauthenticated at this point */
690 if (*sent_user) {
691 r->user = (char *) *sent_user;
692 }
693
694 /* a missing username or missing password means auth denied */
695 if (!sent_user || !*sent_user || !sent_pw || !*sent_pw) {
696 return HTTP_UNAUTHORIZED;
697 }
698
699 /*
700 * save away the username, password, mimetype and method, so that they
701 * are available should the auth need to be run again.
702 */
703 set_notes_auth(r, *sent_user, *sent_pw, sent_method ? *sent_method : NULL,
704 sent_mimetype ? *sent_mimetype : NULL);
705
706 return OK;
707 }
708
709 /* These functions return 0 if client is OK, and proper error status
710 * if not... either HTTP_UNAUTHORIZED, if we made a check, and it failed, or
711 * HTTP_INTERNAL_SERVER_ERROR, if things are so totally confused that we
712 * couldn't figure out how to tell if the client is authorized or not.
713 *
714 * If they return DECLINED, and all other modules also decline, that's
715 * treated by the server core as a configuration error, logged and
716 * reported as such.
717 */
718
719
720 /**
721 * Given a username and site passphrase hash from the session, determine
722 * whether the site passphrase is valid for this session.
723 *
724 * If the site passphrase is NULL, or if the sent_hash is NULL, this
725 * function returns DECLINED.
726 *
727 * If the site passphrase hash does not match the sent hash, this function
728 * returns AUTH_USER_NOT_FOUND.
729 *
730 * On success, returns OK.
731 */
check_site(request_rec * r,const char * site,const char * sent_user,const char * sent_hash)732 static int check_site(request_rec * r, const char *site, const char *sent_user, const char *sent_hash)
733 {
734
735 if (site && sent_user && sent_hash) {
736 const char *hash = ap_md5(r->pool,
737 (unsigned char *) apr_pstrcat(r->pool, sent_user, ":", site, NULL));
738
739 if (!strcmp(sent_hash, hash)) {
740 return OK;
741 }
742 else {
743 return AUTH_USER_NOT_FOUND;
744 }
745 }
746
747 return DECLINED;
748
749 }
750
751 /**
752 * Given a username and password (extracted externally from a cookie), run
753 * the authnz hooks to determine whether this request is authorized.
754 *
755 * Return an HTTP code.
756 */
check_authn(request_rec * r,const char * sent_user,const char * sent_pw)757 static int check_authn(request_rec * r, const char *sent_user, const char *sent_pw)
758 {
759 authn_status auth_result;
760 authn_provider_list *current_provider;
761 auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
762 &auth_form_module);
763
764 current_provider = conf->providers;
765 do {
766 const authn_provider *provider;
767
768 /*
769 * For now, if a provider isn't set, we'll be nice and use the file
770 * provider.
771 */
772 if (!current_provider) {
773 provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP,
774 AUTHN_DEFAULT_PROVIDER,
775 AUTHN_PROVIDER_VERSION);
776
777 if (!provider || !provider->check_password) {
778 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01806)
779 "no authn provider configured");
780 auth_result = AUTH_GENERAL_ERROR;
781 break;
782 }
783 apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, AUTHN_DEFAULT_PROVIDER);
784 }
785 else {
786 provider = current_provider->provider;
787 apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, current_provider->provider_name);
788 }
789
790 if (!sent_user || !sent_pw) {
791 auth_result = AUTH_USER_NOT_FOUND;
792 break;
793 }
794
795 auth_result = provider->check_password(r, sent_user, sent_pw);
796
797 apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE);
798
799 /* Something occured. Stop checking. */
800 if (auth_result != AUTH_USER_NOT_FOUND) {
801 break;
802 }
803
804 /* If we're not really configured for providers, stop now. */
805 if (!conf->providers) {
806 break;
807 }
808
809 current_provider = current_provider->next;
810 } while (current_provider);
811
812 if (auth_result != AUTH_GRANTED) {
813 int return_code;
814
815 /* If we're not authoritative, then any error is ignored. */
816 if (!(conf->authoritative) && auth_result != AUTH_DENIED) {
817 return DECLINED;
818 }
819
820 switch (auth_result) {
821 case AUTH_DENIED:
822 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01807)
823 "user '%s': authentication failure for \"%s\": "
824 "password Mismatch",
825 sent_user, r->uri);
826 return_code = HTTP_UNAUTHORIZED;
827 break;
828 case AUTH_USER_NOT_FOUND:
829 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01808)
830 "user '%s' not found: %s", sent_user, r->uri);
831 return_code = HTTP_UNAUTHORIZED;
832 break;
833 case AUTH_GENERAL_ERROR:
834 default:
835 /*
836 * We'll assume that the module has already said what its error
837 * was in the logs.
838 */
839 return_code = HTTP_INTERNAL_SERVER_ERROR;
840 break;
841 }
842
843 /* If we're returning 403, tell them to try again. */
844 if (return_code == HTTP_UNAUTHORIZED) {
845 note_cookie_auth_failure(r);
846 }
847
848 /* TODO: Flag the user somehow as to the reason for the failure */
849
850 return return_code;
851 }
852
853 return OK;
854
855 }
856
857 /* fake the basic authentication header if configured to do so */
fake_basic_authentication(request_rec * r,auth_form_config_rec * conf,const char * user,const char * pw)858 static void fake_basic_authentication(request_rec *r, auth_form_config_rec *conf,
859 const char *user, const char *pw)
860 {
861 if (conf->fakebasicauth) {
862 char *basic = apr_pstrcat(r->pool, user, ":", pw, NULL);
863 apr_size_t size = (apr_size_t) strlen(basic);
864 char *base64 = apr_palloc(r->pool,
865 apr_base64_encode_len(size + 1) * sizeof(char));
866 apr_base64_encode(base64, basic, size);
867 apr_table_setn(r->headers_in, "Authorization",
868 apr_pstrcat(r->pool, "Basic ", base64, NULL));
869 }
870 }
871
872 /**
873 * Must we use form authentication? If so, extract the cookie and run
874 * the authnz hooks to determine if the login is valid.
875 *
876 * If the login is not valid, a 401 Not Authorized will be returned. It
877 * is up to the webmaster to ensure this screen displays a suitable login
878 * form to give the user the opportunity to log in.
879 */
authenticate_form_authn(request_rec * r)880 static int authenticate_form_authn(request_rec * r)
881 {
882 auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
883 &auth_form_module);
884 const char *sent_user = NULL, *sent_pw = NULL, *sent_hash = NULL;
885 const char *sent_loc = NULL, *sent_method = "GET", *sent_mimetype = NULL;
886 const char *current_auth = NULL;
887 const char *err;
888 apr_status_t res;
889 int rv = HTTP_UNAUTHORIZED;
890
891 /* Are we configured to be Form auth? */
892 current_auth = ap_auth_type(r);
893 if (!current_auth || strcasecmp(current_auth, "form")) {
894 return DECLINED;
895 }
896
897 /*
898 * XSS security warning: using cookies to store private data only works
899 * when the administrator has full control over the source website. When
900 * in forward-proxy mode, websites are public by definition, and so can
901 * never be secure. Abort the auth attempt in this case.
902 */
903 if (PROXYREQ_PROXY == r->proxyreq) {
904 ap_log_rerror(APLOG_MARK, APLOG_ERR,
905 0, r, APLOGNO(01809) "form auth cannot be used for proxy "
906 "requests due to XSS risk, access denied: %s", r->uri);
907 return HTTP_INTERNAL_SERVER_ERROR;
908 }
909
910 /* We need an authentication realm. */
911 if (!ap_auth_name(r)) {
912 ap_log_rerror(APLOG_MARK, APLOG_ERR,
913 0, r, APLOGNO(01810) "need AuthName: %s", r->uri);
914 return HTTP_INTERNAL_SERVER_ERROR;
915 }
916
917 r->ap_auth_type = (char *) current_auth;
918
919 /* try get the username and password from the notes, if present */
920 get_notes_auth(r, &sent_user, &sent_pw, &sent_method, &sent_mimetype);
921 if (!sent_user || !sent_pw || !*sent_user || !*sent_pw) {
922
923 /* otherwise try get the username and password from a session, if present */
924 res = get_session_auth(r, &sent_user, &sent_pw, &sent_hash);
925
926 }
927 else {
928 res = APR_SUCCESS;
929 }
930
931 /* first test whether the site passphrase matches */
932 if (APR_SUCCESS == res && sent_user && sent_hash && sent_pw) {
933 rv = check_site(r, conf->site, sent_user, sent_hash);
934 if (OK == rv) {
935 fake_basic_authentication(r, conf, sent_user, sent_pw);
936 return OK;
937 }
938 }
939
940 /* otherwise test for a normal password match */
941 if (APR_SUCCESS == res && sent_user && sent_pw) {
942 rv = check_authn(r, sent_user, sent_pw);
943 if (OK == rv) {
944 fake_basic_authentication(r, conf, sent_user, sent_pw);
945 return OK;
946 }
947 }
948
949 /*
950 * If we reach this point, the request should fail with access denied,
951 * except for one potential scenario:
952 *
953 * If the request is a POST, and the posted form contains user defined fields
954 * for a username and a password, and the username and password are correct,
955 * then return the response obtained by a GET to this URL.
956 *
957 * If an additional user defined location field is present in the form,
958 * instead of a GET of the current URL, redirect the browser to the new
959 * location.
960 *
961 * As a further option, if the user defined fields for the type of request,
962 * the mime type of the body of the request, and the body of the request
963 * itself are present, replace this request with a new request of the given
964 * type and with the given body.
965 *
966 * Otherwise access is denied.
967 *
968 * Reading the body requires some song and dance, because the input filters
969 * are not yet configured. To work around this problem, we create a
970 * subrequest and use that to create a sane filter stack we can read the
971 * form from.
972 *
973 * The main request is then capped with a kept_body input filter, which has
974 * the effect of guaranteeing the input stack can be safely read a second time.
975 *
976 */
977 if (HTTP_UNAUTHORIZED == rv && r->method_number == M_POST && ap_is_initial_req(r)) {
978 request_rec *rr;
979 apr_bucket_brigade *sent_body = NULL;
980
981 /* create a subrequest of our current uri */
982 rr = ap_sub_req_lookup_uri(r->uri, r, r->input_filters);
983 rr->headers_in = r->headers_in;
984
985 /* run the insert_filters hook on the subrequest to ensure a body read can
986 * be done properly.
987 */
988 ap_run_insert_filter(rr);
989
990 /* parse the form by reading the subrequest */
991 rv = get_form_auth(rr, conf->username, conf->password, conf->location,
992 conf->method, conf->mimetype, conf->body,
993 &sent_user, &sent_pw, &sent_loc, &sent_method,
994 &sent_mimetype, &sent_body, conf);
995
996 /* make sure any user detected within the subrequest is saved back to
997 * the main request.
998 */
999 r->user = apr_pstrdup(r->pool, rr->user);
1000
1001 /* we cannot clean up rr at this point, as memory allocated to rr is
1002 * referenced from the main request. It will be cleaned up when the
1003 * main request is cleaned up.
1004 */
1005
1006 /* insert the kept_body filter on the main request to guarantee the
1007 * input filter stack cannot be read a second time, optionally inject
1008 * a saved body if one was specified in the login form.
1009 */
1010 if (sent_body && sent_mimetype) {
1011 apr_table_set(r->headers_in, "Content-Type", sent_mimetype);
1012 r->kept_body = sent_body;
1013 }
1014 else {
1015 r->kept_body = apr_brigade_create(r->pool, r->connection->bucket_alloc);
1016 }
1017 ap_request_insert_filter_fn(r);
1018
1019 /* did the form ask to change the method? if so, switch in the redirect handler
1020 * to relaunch this request as the subrequest with the new method. If the
1021 * form didn't specify a method, the default value GET will force a redirect.
1022 */
1023 if (sent_method && strcmp(r->method, sent_method)) {
1024 r->handler = FORM_REDIRECT_HANDLER;
1025 }
1026
1027 /* check the authn in the main request, based on the username found */
1028 if (OK == rv) {
1029 rv = check_authn(r, sent_user, sent_pw);
1030 if (OK == rv) {
1031 fake_basic_authentication(r, conf, sent_user, sent_pw);
1032 set_session_auth(r, sent_user, sent_pw, conf->site);
1033 if (sent_loc) {
1034 apr_table_set(r->headers_out, "Location", sent_loc);
1035 return HTTP_MOVED_TEMPORARILY;
1036 }
1037 if (conf->loginsuccess) {
1038 const char *loginsuccess = ap_expr_str_exec(r,
1039 conf->loginsuccess, &err);
1040 if (!err) {
1041 apr_table_set(r->headers_out, "Location", loginsuccess);
1042 return HTTP_MOVED_TEMPORARILY;
1043 }
1044 else {
1045 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02339)
1046 "Can't evaluate login success expression: %s", err);
1047 return HTTP_INTERNAL_SERVER_ERROR;
1048 }
1049 }
1050 }
1051 }
1052
1053 }
1054
1055 /*
1056 * did the admin prefer to be redirected to the login page on failure
1057 * instead?
1058 */
1059 if (HTTP_UNAUTHORIZED == rv && conf->loginrequired) {
1060 const char *loginrequired = ap_expr_str_exec(r,
1061 conf->loginrequired, &err);
1062 if (!err) {
1063 apr_table_set(r->headers_out, "Location", loginrequired);
1064 return HTTP_MOVED_TEMPORARILY;
1065 }
1066 else {
1067 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02340)
1068 "Can't evaluate login required expression: %s", err);
1069 return HTTP_INTERNAL_SERVER_ERROR;
1070 }
1071 }
1072
1073 /* did the user ask to be redirected on login success? */
1074 if (sent_loc) {
1075 apr_table_set(r->headers_out, "Location", sent_loc);
1076 rv = HTTP_MOVED_TEMPORARILY;
1077 }
1078
1079
1080 /*
1081 * potential security issue: if we return a login to the browser, we must
1082 * send a no-store to make sure a well behaved browser will not try and
1083 * send the login details a second time if the back button is pressed.
1084 *
1085 * if the user has full control over the backend, the
1086 * AuthCookieDisableNoStore can be used to turn this off.
1087 */
1088 if (HTTP_UNAUTHORIZED == rv && !conf->disable_no_store) {
1089 apr_table_addn(r->headers_out, "Cache-Control", "no-store");
1090 apr_table_addn(r->err_headers_out, "Cache-Control", "no-store");
1091 }
1092
1093 return rv;
1094
1095 }
1096
1097 /**
1098 * Handle a login attempt.
1099 *
1100 * If the login session is either missing or form authnz is unsuccessful, a
1101 * 401 Not Authorized will be returned to the browser. The webmaster
1102 * is expected to insert a login form into the 401 Not Authorized
1103 * error screen.
1104 *
1105 * If the webmaster wishes, they can point the form submission at this
1106 * handler, which will redirect the user to the correct page on success.
1107 * On failure, the 401 Not Authorized error screen will be redisplayed,
1108 * where the login attempt can be repeated.
1109 *
1110 */
authenticate_form_login_handler(request_rec * r)1111 static int authenticate_form_login_handler(request_rec * r)
1112 {
1113 auth_form_config_rec *conf;
1114 const char *err;
1115
1116 const char *sent_user = NULL, *sent_pw = NULL, *sent_loc = NULL;
1117 int rv;
1118
1119 if (strcmp(r->handler, FORM_LOGIN_HANDLER)) {
1120 return DECLINED;
1121 }
1122
1123 if (r->method_number != M_POST) {
1124 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01811)
1125 "the " FORM_LOGIN_HANDLER " only supports the POST method for %s",
1126 r->uri);
1127 return HTTP_METHOD_NOT_ALLOWED;
1128 }
1129
1130 conf = ap_get_module_config(r->per_dir_config, &auth_form_module);
1131
1132 rv = get_form_auth(r, conf->username, conf->password, conf->location,
1133 NULL, NULL, NULL,
1134 &sent_user, &sent_pw, &sent_loc,
1135 NULL, NULL, NULL, conf);
1136 if (OK == rv) {
1137 rv = check_authn(r, sent_user, sent_pw);
1138 if (OK == rv) {
1139 set_session_auth(r, sent_user, sent_pw, conf->site);
1140 if (sent_loc) {
1141 apr_table_set(r->headers_out, "Location", sent_loc);
1142 return HTTP_MOVED_TEMPORARILY;
1143 }
1144 if (conf->loginsuccess) {
1145 const char *loginsuccess = ap_expr_str_exec(r,
1146 conf->loginsuccess, &err);
1147 if (!err) {
1148 apr_table_set(r->headers_out, "Location", loginsuccess);
1149 return HTTP_MOVED_TEMPORARILY;
1150 }
1151 else {
1152 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02341)
1153 "Can't evaluate login success expression: %s", err);
1154 return HTTP_INTERNAL_SERVER_ERROR;
1155 }
1156 }
1157 return HTTP_OK;
1158 }
1159 }
1160
1161 /* did we prefer to be redirected to the login page on failure instead? */
1162 if (HTTP_UNAUTHORIZED == rv && conf->loginrequired) {
1163 const char *loginrequired = ap_expr_str_exec(r,
1164 conf->loginrequired, &err);
1165 if (!err) {
1166 apr_table_set(r->headers_out, "Location", loginrequired);
1167 return HTTP_MOVED_TEMPORARILY;
1168 }
1169 else {
1170 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02342)
1171 "Can't evaluate login required expression: %s", err);
1172 return HTTP_INTERNAL_SERVER_ERROR;
1173 }
1174 }
1175
1176 return rv;
1177
1178 }
1179
1180 /**
1181 * Handle a logout attempt.
1182 *
1183 * If an attempt is made to access this URL, any username and password
1184 * embedded in the session is deleted.
1185 *
1186 * This has the effect of logging the person out.
1187 *
1188 * If a logout URI has been specified, this function will create an
1189 * internal redirect to this page.
1190 */
authenticate_form_logout_handler(request_rec * r)1191 static int authenticate_form_logout_handler(request_rec * r)
1192 {
1193 auth_form_config_rec *conf;
1194 const char *err;
1195
1196 if (strcmp(r->handler, FORM_LOGOUT_HANDLER)) {
1197 return DECLINED;
1198 }
1199
1200 conf = ap_get_module_config(r->per_dir_config, &auth_form_module);
1201
1202 /* remove the username and password, effectively logging the user out */
1203 set_session_auth(r, NULL, NULL, NULL);
1204
1205 /*
1206 * make sure the logout page is never cached - otherwise the logout won't
1207 * work!
1208 */
1209 apr_table_addn(r->headers_out, "Cache-Control", "no-store");
1210 apr_table_addn(r->err_headers_out, "Cache-Control", "no-store");
1211
1212 /* if set, internal redirect to the logout page */
1213 if (conf->logout) {
1214 const char *logout = ap_expr_str_exec(r,
1215 conf->logout, &err);
1216 if (!err) {
1217 apr_table_addn(r->headers_out, "Location", logout);
1218 return HTTP_TEMPORARY_REDIRECT;
1219 }
1220 else {
1221 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02343)
1222 "Can't evaluate logout expression: %s", err);
1223 return HTTP_INTERNAL_SERVER_ERROR;
1224 }
1225 }
1226
1227 return HTTP_OK;
1228
1229 }
1230
1231 /**
1232 * Handle a redirect attempt.
1233 *
1234 * If during a form login, the method, mimetype and request body are
1235 * specified, this handler will ensure that this request is included
1236 * as an internal redirect.
1237 *
1238 */
authenticate_form_redirect_handler(request_rec * r)1239 static int authenticate_form_redirect_handler(request_rec * r)
1240 {
1241
1242 request_rec *rr = NULL;
1243 const char *sent_method = NULL, *sent_mimetype = NULL;
1244
1245 if (strcmp(r->handler, FORM_REDIRECT_HANDLER)) {
1246 return DECLINED;
1247 }
1248
1249 /* get the method and mimetype from the notes */
1250 get_notes_auth(r, NULL, NULL, &sent_method, &sent_mimetype);
1251
1252 if (r->kept_body && sent_method && sent_mimetype) {
1253
1254 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01812)
1255 "internal redirect to method '%s' and body mimetype '%s' for the "
1256 "uri: %s", sent_method, sent_mimetype, r->uri);
1257
1258 rr = ap_sub_req_method_uri(sent_method, r->uri, r, r->output_filters);
1259 r->status = ap_run_sub_req(rr);
1260
1261 }
1262 else {
1263 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01813)
1264 "internal redirect requested but one or all of method, mimetype or "
1265 "body are NULL: %s", r->uri);
1266 return HTTP_INTERNAL_SERVER_ERROR;
1267 }
1268
1269 /* return the underlying error, or OK on success */
1270 return r->status == HTTP_OK || r->status == OK ? OK : r->status;
1271
1272 }
1273
register_hooks(apr_pool_t * p)1274 static void register_hooks(apr_pool_t * p)
1275 {
1276 #if AP_MODULE_MAGIC_AT_LEAST(20080403,1)
1277 ap_hook_check_authn(authenticate_form_authn, NULL, NULL, APR_HOOK_MIDDLE,
1278 AP_AUTH_INTERNAL_PER_CONF);
1279 #else
1280 ap_hook_check_user_id(authenticate_form_authn, NULL, NULL, APR_HOOK_MIDDLE);
1281 #endif
1282 ap_hook_handler(authenticate_form_login_handler, NULL, NULL, APR_HOOK_MIDDLE);
1283 ap_hook_handler(authenticate_form_logout_handler, NULL, NULL, APR_HOOK_MIDDLE);
1284 ap_hook_handler(authenticate_form_redirect_handler, NULL, NULL, APR_HOOK_MIDDLE);
1285
1286 ap_hook_note_auth_failure(hook_note_cookie_auth_failure, NULL, NULL,
1287 APR_HOOK_MIDDLE);
1288 }
1289
1290 AP_DECLARE_MODULE(auth_form) =
1291 {
1292 STANDARD20_MODULE_STUFF,
1293 create_auth_form_dir_config, /* dir config creater */
1294 merge_auth_form_dir_config, /* dir merger --- default is to override */
1295 NULL, /* server config */
1296 NULL, /* merge server config */
1297 auth_form_cmds, /* command apr_table_t */
1298 register_hooks /* register hooks */
1299 };
1300