Moved apache code into a folder to help prepare for packaging where we dont want...
[httpd-crcsyncproxy.git] / apache / modules / aaa / mod_auth_form.c
bloba56b87f66261b33a37e37feab9bfdc9fcc658f13
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "apr_strings.h"
18 #include "apr_md5.h" /* for apr_password_validate */
19 #include "apr_lib.h" /* for apr_isspace */
20 #include "apr_base64.h" /* for apr_base64_decode et al */
21 #define APR_WANT_STRFUNC /* for strcasecmp */
22 #include "apr_want.h"
24 #include "ap_config.h"
25 #include "httpd.h"
26 #include "http_config.h"
27 #include "http_core.h"
28 #include "http_log.h"
29 #include "http_protocol.h"
30 #include "http_request.h"
31 #include "ap_provider.h"
32 #include "util_md5.h"
34 #include "mod_auth.h"
35 #include "mod_session.h"
36 #include "mod_request.h"
38 #define LOG_PREFIX "mod_auth_form: "
39 #define FORM_LOGIN_HANDLER "form-login-handler"
40 #define FORM_LOGOUT_HANDLER "form-logout-handler"
41 #define FORM_REDIRECT_HANDLER "form-redirect-handler"
42 #define MOD_AUTH_FORM_HASH "site"
44 static int (*ap_session_load_fn) (request_rec * r, session_rec ** z) = NULL;
45 static void (*ap_session_get_fn) (request_rec * r, session_rec * z,
46 const char *key, const char **value) = NULL;
47 static void (*ap_session_set_fn) (request_rec * r, session_rec * z,
48 const char *key, const char *value) = NULL;
49 static int (*ap_parse_request_form_fn) (request_rec * r, ap_filter_t *f,
50 apr_array_header_t ** ptr,
51 apr_size_t num, apr_size_t size) = NULL;
52 static void (*ap_request_insert_filter_fn) (request_rec * r) = NULL;
53 static void (*ap_request_remove_filter_fn) (request_rec * r) = NULL;
55 typedef struct {
56 authn_provider_list *providers;
57 char *dir;
58 int authoritative;
59 int authoritative_set;
60 const char *site;
61 int site_set;
62 const char *username;
63 int username_set;
64 const char *password;
65 int password_set;
66 apr_size_t form_size;
67 int form_size_set;
68 int fakebasicauth;
69 int fakebasicauth_set;
70 const char *location;
71 int location_set;
72 const char *method;
73 int method_set;
74 const char *mimetype;
75 int mimetype_set;
76 const char *body;
77 int body_set;
78 int disable_no_store;
79 int disable_no_store_set;
80 const char *loginsuccess;
81 int loginsuccess_set;
82 const char *loginrequired;
83 int loginrequired_set;
84 const char *logout;
85 int logout_set;
86 } auth_form_config_rec;
88 static void *create_auth_form_dir_config(apr_pool_t * p, char *d)
90 auth_form_config_rec *conf = apr_pcalloc(p, sizeof(*conf));
92 conf->dir = d;
93 /* Any failures are fatal. */
94 conf->authoritative = 1;
96 /* form size defaults to 8k */
97 conf->form_size = HUGE_STRING_LEN;
99 /* default form field names */
100 conf->username = "httpd_username";
101 conf->password = "httpd_password";
102 conf->location = "httpd_location";
103 conf->method = "httpd_method";
104 conf->mimetype = "httpd_mimetype";
105 conf->body = "httpd_body";
107 return conf;
110 static void *merge_auth_form_dir_config(apr_pool_t * p, void *basev, void *addv)
112 auth_form_config_rec *new = (auth_form_config_rec *) apr_pcalloc(p, sizeof(auth_form_config_rec));
113 auth_form_config_rec *add = (auth_form_config_rec *) addv;
114 auth_form_config_rec *base = (auth_form_config_rec *) basev;
116 new->providers = !add->providers ? base->providers : add->providers;
117 new->authoritative = (add->authoritative_set == 0) ? base->authoritative : add->authoritative;
118 new->authoritative_set = add->authoritative_set || base->authoritative_set;
119 new->site = (add->site_set == 0) ? base->site : add->site;
120 new->site_set = add->site_set || base->site_set;
121 new->username = (add->username_set == 0) ? base->username : add->username;
122 new->username_set = add->username_set || base->username_set;
123 new->password = (add->password_set == 0) ? base->password : add->password;
124 new->password_set = add->password_set || base->password_set;
125 new->location = (add->location_set == 0) ? base->location : add->location;
126 new->location_set = add->location_set || base->location_set;
127 new->form_size = (add->form_size_set == 0) ? base->form_size : add->form_size;
128 new->form_size_set = add->form_size_set || base->form_size_set;
129 new->fakebasicauth = (add->fakebasicauth_set == 0) ? base->fakebasicauth : add->fakebasicauth;
130 new->fakebasicauth_set = add->fakebasicauth_set || base->fakebasicauth_set;
131 new->method = (add->method_set == 0) ? base->method : add->method;
132 new->method_set = add->method_set || base->method_set;
133 new->mimetype = (add->mimetype_set == 0) ? base->mimetype : add->mimetype;
134 new->mimetype_set = add->mimetype_set || base->mimetype_set;
135 new->body = (add->body_set == 0) ? base->body : add->body;
136 new->body_set = add->body_set || base->body_set;
137 new->disable_no_store = (add->disable_no_store_set == 0) ? base->disable_no_store : add->disable_no_store;
138 new->disable_no_store_set = add->disable_no_store_set || base->disable_no_store_set;
139 new->loginsuccess = (add->loginsuccess_set == 0) ? base->loginsuccess : add->loginsuccess;
140 new->loginsuccess_set = add->loginsuccess_set || base->loginsuccess_set;
141 new->loginrequired = (add->loginrequired_set == 0) ? base->loginrequired : add->loginrequired;
142 new->loginrequired_set = add->loginrequired_set || base->loginrequired_set;
143 new->logout = (add->logout_set == 0) ? base->logout : add->logout;
144 new->logout_set = add->logout_set || base->logout_set;
146 return new;
149 static const char *add_authn_provider(cmd_parms * cmd, void *config,
150 const char *arg)
152 auth_form_config_rec *conf = (auth_form_config_rec *) config;
153 authn_provider_list *newp;
155 newp = apr_pcalloc(cmd->pool, sizeof(authn_provider_list));
156 newp->provider_name = apr_pstrdup(cmd->pool, arg);
158 /* lookup and cache the actual provider now */
159 newp->provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP,
160 newp->provider_name,
161 AUTHN_PROVIDER_VERSION);
163 if (newp->provider == NULL) {
165 * by the time they use it, the provider should be loaded and
166 * registered with us.
168 return apr_psprintf(cmd->pool,
169 "Unknown Authn provider: %s",
170 newp->provider_name);
173 if (!newp->provider->check_password) {
174 /* if it doesn't provide the appropriate function, reject it */
175 return apr_psprintf(cmd->pool,
176 "The '%s' Authn provider doesn't support "
177 "Form Authentication", newp->provider_name);
180 if (!ap_session_load_fn || !ap_session_get_fn || !ap_session_set_fn) {
181 ap_session_load_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_load);
182 ap_session_get_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_get);
183 ap_session_set_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_set);
184 if (!ap_session_load_fn || !ap_session_get_fn || !ap_session_set_fn) {
185 return "You must load mod_session to enable the mod_auth_form "
186 "functions";
190 if (!ap_parse_request_form_fn || !ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
191 ap_parse_request_form_fn = APR_RETRIEVE_OPTIONAL_FN(ap_parse_request_form);
192 ap_request_insert_filter_fn = APR_RETRIEVE_OPTIONAL_FN(ap_request_insert_filter);
193 ap_request_remove_filter_fn = APR_RETRIEVE_OPTIONAL_FN(ap_request_remove_filter);
194 if (!ap_parse_request_form_fn || !ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
195 return "You must load mod_request to enable the mod_auth_form "
196 "functions";
200 /* Add it to the list now. */
201 if (!conf->providers) {
202 conf->providers = newp;
204 else {
205 authn_provider_list *last = conf->providers;
207 while (last->next) {
208 last = last->next;
210 last->next = newp;
213 return NULL;
217 * Sanity check a given string that it exists, is not empty,
218 * and does not contain special characters.
220 static const char *check_string(cmd_parms * cmd, const char *string)
222 if (!string || !*string || ap_strchr_c(string, '=') || ap_strchr_c(string, '&')) {
223 return apr_pstrcat(cmd->pool, cmd->directive->directive,
224 " cannot be empty, or contain '=' or '&'.",
225 NULL);
227 return NULL;
230 static const char *set_cookie_form_location(cmd_parms * cmd, void *config, const char *location)
232 auth_form_config_rec *conf = (auth_form_config_rec *) config;
233 conf->location = location;
234 conf->location_set = 1;
235 return check_string(cmd, location);
238 static const char *set_cookie_form_username(cmd_parms * cmd, void *config, const char *username)
240 auth_form_config_rec *conf = (auth_form_config_rec *) config;
241 conf->username = username;
242 conf->username_set = 1;
243 return check_string(cmd, username);
246 static const char *set_cookie_form_password(cmd_parms * cmd, void *config, const char *password)
248 auth_form_config_rec *conf = (auth_form_config_rec *) config;
249 conf->password = password;
250 conf->password_set = 1;
251 return check_string(cmd, password);
254 static const char *set_cookie_form_method(cmd_parms * cmd, void *config, const char *method)
256 auth_form_config_rec *conf = (auth_form_config_rec *) config;
257 conf->method = method;
258 conf->method_set = 1;
259 return check_string(cmd, method);
262 static const char *set_cookie_form_mimetype(cmd_parms * cmd, void *config, const char *mimetype)
264 auth_form_config_rec *conf = (auth_form_config_rec *) config;
265 conf->mimetype = mimetype;
266 conf->mimetype_set = 1;
267 return check_string(cmd, mimetype);
270 static const char *set_cookie_form_body(cmd_parms * cmd, void *config, const char *body)
272 auth_form_config_rec *conf = (auth_form_config_rec *) config;
273 conf->body = body;
274 conf->body_set = 1;
275 return check_string(cmd, body);
278 static const char *set_cookie_form_size(cmd_parms * cmd, void *config,
279 const char *arg)
281 auth_form_config_rec *conf = config;
282 apr_off_t size;
284 if (APR_SUCCESS != apr_strtoff(&size, arg, NULL, 0)
285 || size < 0 || size > APR_SIZE_MAX) {
286 return "AuthCookieFormSize must be a size in bytes, or zero.";
288 conf->form_size = (apr_size_t)size;
289 conf->form_size_set = 1;
291 return NULL;
294 static const char *set_login_required_location(cmd_parms * cmd, void *config, const char *loginrequired)
296 auth_form_config_rec *conf = (auth_form_config_rec *) config;
297 conf->loginrequired = loginrequired;
298 conf->loginrequired_set = 1;
299 return NULL;
302 static const char *set_login_success_location(cmd_parms * cmd, void *config, const char *loginsuccess)
304 auth_form_config_rec *conf = (auth_form_config_rec *) config;
305 conf->loginsuccess = loginsuccess;
306 conf->loginsuccess_set = 1;
307 return NULL;
310 static const char *set_logout_location(cmd_parms * cmd, void *config, const char *logout)
312 auth_form_config_rec *conf = (auth_form_config_rec *) config;
313 conf->logout = logout;
314 conf->logout_set = 1;
315 return NULL;
318 static const char *set_site_passphrase(cmd_parms * cmd, void *config, const char *site)
320 auth_form_config_rec *conf = (auth_form_config_rec *) config;
321 conf->site = site;
322 conf->site_set = 1;
323 return NULL;
326 static const char *set_authoritative(cmd_parms * cmd, void *config, int flag)
328 auth_form_config_rec *conf = (auth_form_config_rec *) config;
329 conf->authoritative = flag;
330 conf->authoritative_set = 1;
331 return NULL;
334 static const char *set_fake_basic_auth(cmd_parms * cmd, void *config, int flag)
336 auth_form_config_rec *conf = (auth_form_config_rec *) config;
337 conf->fakebasicauth = flag;
338 conf->fakebasicauth_set = 1;
339 return NULL;
342 static const char *set_disable_no_store(cmd_parms * cmd, void *config, int flag)
344 auth_form_config_rec *conf = (auth_form_config_rec *) config;
345 conf->disable_no_store = flag;
346 conf->disable_no_store_set = 1;
347 return NULL;
350 static const command_rec auth_form_cmds[] =
352 AP_INIT_ITERATE("AuthFormProvider", add_authn_provider, NULL, OR_AUTHCFG,
353 "specify the auth providers for a directory or location"),
354 AP_INIT_TAKE1("AuthFormUsername", set_cookie_form_username, NULL, OR_AUTHCFG,
355 "The field of the login form carrying the username"),
356 AP_INIT_TAKE1("AuthFormPassword", set_cookie_form_password, NULL, OR_AUTHCFG,
357 "The field of the login form carrying the password"),
358 AP_INIT_TAKE1("AuthFormLocation", set_cookie_form_location, NULL, OR_AUTHCFG,
359 "The field of the login form carrying the URL to redirect on "
360 "successful login."),
361 AP_INIT_TAKE1("AuthFormMethod", set_cookie_form_method, NULL, OR_AUTHCFG,
362 "The field of the login form carrying the original request method."),
363 AP_INIT_TAKE1("AuthFormMimetype", set_cookie_form_mimetype, NULL, OR_AUTHCFG,
364 "The field of the login form carrying the original request mimetype."),
365 AP_INIT_TAKE1("AuthFormBody", set_cookie_form_body, NULL, OR_AUTHCFG,
366 "The field of the login form carrying the urlencoded original request "
367 "body."),
368 AP_INIT_TAKE1("AuthFormSize", set_cookie_form_size, NULL, ACCESS_CONF,
369 "Maximum size of body parsed by the form parser"),
370 AP_INIT_TAKE1("AuthFormLoginRequiredLocation", set_login_required_location,
371 NULL, OR_AUTHCFG,
372 "If set, redirect the browser to this URL rather than "
373 "return 401 Not Authorized."),
374 AP_INIT_TAKE1("AuthFormLoginSuccessLocation", set_login_success_location,
375 NULL, OR_AUTHCFG,
376 "If set, redirect the browser to this URL when a login "
377 "processed by the login handler is successful."),
378 AP_INIT_TAKE1("AuthFormLogoutLocation", set_logout_location,
379 NULL, OR_AUTHCFG,
380 "The URL of the logout successful page. An attempt to access an "
381 "URL handled by the handler " FORM_LOGOUT_HANDLER " will result "
382 "in an redirect to this page after logout."),
383 AP_INIT_TAKE1("AuthFormSitePassphrase", set_site_passphrase,
384 NULL, OR_AUTHCFG,
385 "If set, use this passphrase to determine whether the user should "
386 "be authenticated. Bypasses the user authentication check on "
387 "every website hit, and is useful for high traffic sites."),
388 AP_INIT_FLAG("AuthFormAuthoritative", set_authoritative,
389 NULL, OR_AUTHCFG,
390 "Set to 'Off' to allow access control to be passed along to "
391 "lower modules if the UserID is not known to this module"),
392 AP_INIT_FLAG("AuthFormFakeBasicAuth", set_fake_basic_auth,
393 NULL, OR_AUTHCFG,
394 "Set to 'On' to pass through authentication to the rest of the "
395 "server as a basic authentication header."),
396 AP_INIT_FLAG("AuthFormDisableNoStore", set_disable_no_store,
397 NULL, OR_AUTHCFG,
398 "Set to 'on' to stop the sending of a Cache-Control no-store header with "
399 "the login screen. This allows the browser to cache the credentials, but "
400 "at the risk of it being possible for the login form to be resubmitted "
401 "and revealed to the backend server through XSS. Use at own risk."),
402 {NULL}
405 module AP_MODULE_DECLARE_DATA auth_form_module;
407 /* These functions return 0 if client is OK, and proper error status
408 * if not... either HTTP_UNAUTHORIZED, if we made a check, and it failed, or
409 * HTTP_INTERNAL_SERVER_ERROR, if things are so totally confused that we
410 * couldn't figure out how to tell if the client is authorized or not.
412 * If they return DECLINED, and all other modules also decline, that's
413 * treated by the server core as a configuration error, logged and
414 * reported as such.
417 static void note_cookie_auth_failure(request_rec * r)
419 auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
420 &auth_form_module);
422 if (conf->location && ap_strchr_c(conf->location, ':')) {
423 apr_table_setn(r->err_headers_out, "Location", conf->location);
428 * Set the auth username and password into the main request
429 * notes table.
431 static void set_notes_auth(request_rec * r,
432 const char *user, const char *pw,
433 const char *method, const char *mimetype)
435 apr_table_t *notes = NULL;
436 const char *authname;
438 /* find the main request */
439 while (r->main) {
440 r = r->main;
442 /* find the first redirect */
443 while (r->prev) {
444 r = r->prev;
446 notes = r->notes;
448 /* have we isolated the user and pw before? */
449 authname = ap_auth_name(r);
450 if (user) {
451 apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-user", NULL), user);
453 if (pw) {
454 apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-pw", NULL), pw);
456 if (method) {
457 apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-method", NULL), method);
459 if (mimetype) {
460 apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-mimetype", NULL), mimetype);
466 * Get the auth username and password from the main request
467 * notes table, if present.
469 static void get_notes_auth(request_rec * r,
470 const char **user, const char **pw,
471 const char **method, const char **mimetype)
473 const char *authname;
475 /* find the main request */
476 while (r->main) {
477 r = r->main;
479 /* find the first redirect */
480 while (r->prev) {
481 r = r->prev;
484 /* have we isolated the user and pw before? */
485 authname = ap_auth_name(r);
486 if (user) {
487 *user = (char *) apr_table_get(r->notes, apr_pstrcat(r->pool, authname, "-user", NULL));
489 if (pw) {
490 *pw = (char *) apr_table_get(r->notes, apr_pstrcat(r->pool, authname, "-pw", NULL));
492 if (method) {
493 *method = (char *) apr_table_get(r->notes, apr_pstrcat(r->pool, authname, "-method", NULL));
495 if (mimetype) {
496 *mimetype = (char *) apr_table_get(r->notes, apr_pstrcat(r->pool, authname, "-mimetype", NULL));
502 * Set the auth username and password into the session.
504 * If either the username, or the password are NULL, the username
505 * and/or password will be removed from the session.
507 static apr_status_t set_session_auth(request_rec * r,
508 const char *user, const char *pw, const char *site)
510 const char *hash = NULL;
511 const char *authname = ap_auth_name(r);
512 session_rec *z = NULL;
514 if (site) {
515 hash = ap_md5(r->pool,
516 (unsigned char *) apr_pstrcat(r->pool, user, ":", site, NULL));
519 ap_session_load_fn(r, &z);
520 ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_USER, NULL), user);
521 ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_PW, NULL), pw);
522 ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_AUTH_FORM_HASH, NULL), hash);
524 return APR_SUCCESS;
529 * Get the auth username and password from the main request
530 * notes table, if present.
532 static apr_status_t get_session_auth(request_rec * r,
533 const char **user, const char **pw, const char **hash)
535 const char *authname = ap_auth_name(r);
536 session_rec *z = NULL;
537 ap_session_load_fn(r, &z);
539 if (user) {
540 ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_USER, NULL), user);
542 if (pw) {
543 ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_PW, NULL), pw);
545 if (hash) {
546 ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_AUTH_FORM_HASH, NULL), hash);
549 /* set the user, even though the user is unauthenticated at this point */
550 if (user && *user) {
551 r->user = (char *) *user;
554 return APR_SUCCESS;
559 * Isolate the username and password in a POSTed form with the
560 * username in the "username" field, and the password in the
561 * "password" field.
563 * If either the username or the password is missing, this
564 * function will return HTTP_UNAUTHORIZED.
566 * The location field is considered optional, and will be returned
567 * if present.
569 static int get_form_auth(request_rec * r,
570 const char *username,
571 const char *password,
572 const char *location,
573 const char *method,
574 const char *mimetype,
575 const char *body,
576 const char **sent_user,
577 const char **sent_pw,
578 const char **sent_loc,
579 const char **sent_method,
580 const char **sent_mimetype,
581 apr_bucket_brigade **sent_body,
582 auth_form_config_rec * conf)
584 /* sanity check - are we a POST request? */
586 /* find the username and password in the form */
587 apr_array_header_t *pairs = NULL;
588 apr_off_t len;
589 apr_size_t size;
590 int res;
591 char *buffer;
593 /* have we isolated the user and pw before? */
594 get_notes_auth(r, sent_user, sent_pw, sent_method, sent_mimetype);
595 if (*sent_user && *sent_pw) {
596 return OK;
599 res = ap_parse_request_form_fn(r, NULL, &pairs, -1, conf->form_size);
600 if (res != OK) {
601 return res;
603 while (pairs && !apr_is_empty_array(pairs)) {
604 ap_form_pair_t *pair = (ap_form_pair_t *) apr_array_pop(pairs);
605 if (username && !strcmp(pair->name, username) && sent_user) {
606 apr_brigade_length(pair->value, 1, &len);
607 size = (apr_size_t) len;
608 buffer = apr_palloc(r->pool, size + 1);
609 apr_brigade_flatten(pair->value, buffer, &size);
610 buffer[len] = 0;
611 *sent_user = buffer;
613 else if (password && !strcmp(pair->name, password) && sent_pw) {
614 apr_brigade_length(pair->value, 1, &len);
615 size = (apr_size_t) len;
616 buffer = apr_palloc(r->pool, size + 1);
617 apr_brigade_flatten(pair->value, buffer, &size);
618 buffer[len] = 0;
619 *sent_pw = buffer;
621 else if (location && !strcmp(pair->name, location) && sent_loc) {
622 apr_brigade_length(pair->value, 1, &len);
623 size = (apr_size_t) len;
624 buffer = apr_palloc(r->pool, size + 1);
625 apr_brigade_flatten(pair->value, buffer, &size);
626 buffer[len] = 0;
627 *sent_loc = buffer;
629 else if (method && !strcmp(pair->name, method) && sent_method) {
630 apr_brigade_length(pair->value, 1, &len);
631 size = (apr_size_t) len;
632 buffer = apr_palloc(r->pool, size + 1);
633 apr_brigade_flatten(pair->value, buffer, &size);
634 buffer[len] = 0;
635 *sent_method = buffer;
637 else if (mimetype && !strcmp(pair->name, mimetype) && sent_mimetype) {
638 apr_brigade_length(pair->value, 1, &len);
639 size = (apr_size_t) len;
640 buffer = apr_palloc(r->pool, size + 1);
641 apr_brigade_flatten(pair->value, buffer, &size);
642 buffer[len] = 0;
643 *sent_mimetype = buffer;
645 else if (body && !strcmp(pair->name, body) && sent_body) {
646 *sent_body = pair->value;
650 /* set the user, even though the user is unauthenticated at this point */
651 if (*sent_user) {
652 r->user = (char *) *sent_user;
655 /* a missing username or missing password means auth denied */
656 if (!sent_user || !*sent_user || !sent_pw || !*sent_pw) {
657 return HTTP_UNAUTHORIZED;
661 * save away the username, password, mimetype and method, so that they
662 * are available should the auth need to be run again.
664 set_notes_auth(r, *sent_user, *sent_pw, *sent_method, *sent_mimetype);
666 return OK;
670 * Given a username and site passphrase hash from the session, determine
671 * whether the site passphrase is valid for this session.
673 * If the site passphrase is NULL, or if the sent_hash is NULL, this
674 * function returns DECLINED.
676 * If the site passphrase hash does not match the sent hash, this function
677 * returns AUTH_USER_NOT_FOUND.
679 * On success, returns OK.
681 static int check_site(request_rec * r, const char *site, const char *sent_user, const char *sent_hash)
684 if (site && sent_user && sent_hash) {
685 const char *hash = ap_md5(r->pool,
686 (unsigned char *) apr_pstrcat(r->pool, sent_user, ":", site, NULL));
688 if (!strcmp(sent_hash, hash)) {
689 return OK;
691 else {
692 return AUTH_USER_NOT_FOUND;
696 return DECLINED;
701 * Given a username and password (extracted externally from a cookie), run
702 * the authnz hooks to determine whether this request is authorized.
704 * Return an HTTP code.
706 static int check_authn(request_rec * r, const char *sent_user, const char *sent_pw)
708 authn_status auth_result;
709 authn_provider_list *current_provider;
710 auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
711 &auth_form_module);
713 current_provider = conf->providers;
714 do {
715 const authn_provider *provider;
718 * For now, if a provider isn't set, we'll be nice and use the file
719 * provider.
721 if (!current_provider) {
722 provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP,
723 AUTHN_DEFAULT_PROVIDER,
724 AUTHN_PROVIDER_VERSION);
726 if (!provider || !provider->check_password) {
727 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
728 "no authn provider configured");
729 auth_result = AUTH_GENERAL_ERROR;
730 break;
732 apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, AUTHN_DEFAULT_PROVIDER);
734 else {
735 provider = current_provider->provider;
736 apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, current_provider->provider_name);
739 if (!sent_user || !sent_pw) {
740 auth_result = AUTH_USER_NOT_FOUND;
741 break;
744 auth_result = provider->check_password(r, sent_user, sent_pw);
746 apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE);
748 /* Something occured. Stop checking. */
749 if (auth_result != AUTH_USER_NOT_FOUND) {
750 break;
753 /* If we're not really configured for providers, stop now. */
754 if (!conf->providers) {
755 break;
758 current_provider = current_provider->next;
759 } while (current_provider);
761 if (auth_result != AUTH_GRANTED) {
762 int return_code;
764 /* If we're not authoritative, then any error is ignored. */
765 if (!(conf->authoritative) && auth_result != AUTH_DENIED) {
766 return DECLINED;
769 switch (auth_result) {
770 case AUTH_DENIED:
771 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
772 "user '%s': authentication failure for \"%s\": "
773 "password Mismatch",
774 sent_user, r->uri);
775 return_code = HTTP_UNAUTHORIZED;
776 break;
777 case AUTH_USER_NOT_FOUND:
778 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
779 "user '%s' not found: %s", sent_user, r->uri);
780 return_code = HTTP_UNAUTHORIZED;
781 break;
782 case AUTH_GENERAL_ERROR:
783 default:
785 * We'll assume that the module has already said what its error
786 * was in the logs.
788 return_code = HTTP_INTERNAL_SERVER_ERROR;
789 break;
792 /* If we're returning 403, tell them to try again. */
793 if (return_code == HTTP_UNAUTHORIZED) {
794 note_cookie_auth_failure(r);
797 /* TODO: Flag the user somehow as to the reason for the failure */
799 return return_code;
802 return OK;
806 /* fake the basic authentication header if configured to do so */
807 static void fake_basic_authentication(request_rec *r, auth_form_config_rec *conf,
808 const char *user, const char *pw)
810 if (conf->fakebasicauth) {
811 char *basic = apr_pstrcat(r->pool, user, ":", pw, NULL);
812 apr_size_t size = (apr_size_t) strlen(basic);
813 char *base64 = apr_palloc(r->pool,
814 apr_base64_encode_len(size + 1) * sizeof(char));
815 apr_base64_encode(base64, basic, size);
816 apr_table_set(r->headers_in, "Authorization",
817 apr_pstrcat(r->pool, "Basic ", base64, NULL));
822 * Must we use form authentication? If so, extract the cookie and run
823 * the authnz hooks to determine if the login is valid.
825 * If the login is not valid, a 401 Not Authorized will be returned. It
826 * is up to the webmaster to ensure this screen displays a suitable login
827 * form to give the user the opportunity to log in.
829 static int authenticate_form_authn(request_rec * r)
831 auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
832 &auth_form_module);
833 const char *sent_user = NULL, *sent_pw = NULL, *sent_hash = NULL;
834 const char *sent_loc = NULL, *sent_method = "GET", *sent_mimetype = NULL;
835 const char *current_auth = NULL;
836 apr_status_t res;
837 int rv = HTTP_UNAUTHORIZED;
839 /* Are we configured to be Form auth? */
840 current_auth = ap_auth_type(r);
841 if (!current_auth || strcasecmp(current_auth, "form")) {
842 return DECLINED;
846 * XSS security warning: using cookies to store private data only works
847 * when the administrator has full control over the source website. When
848 * in forward-proxy mode, websites are public by definition, and so can
849 * never be secure. Abort the auth attempt in this case.
851 if (PROXYREQ_PROXY == r->proxyreq) {
852 ap_log_rerror(APLOG_MARK, APLOG_ERR,
853 0, r, LOG_PREFIX "form auth cannot be used for proxy "
854 "requests due to XSS risk, access denied: %s", r->uri);
855 return HTTP_INTERNAL_SERVER_ERROR;
858 /* We need an authentication realm. */
859 if (!ap_auth_name(r)) {
860 ap_log_rerror(APLOG_MARK, APLOG_ERR,
861 0, r, LOG_PREFIX "need AuthName: %s", r->uri);
862 return HTTP_INTERNAL_SERVER_ERROR;
865 r->ap_auth_type = (char *) current_auth;
867 /* try get the username and password from the notes, if present */
868 get_notes_auth(r, &sent_user, &sent_pw, &sent_method, &sent_mimetype);
869 if (!sent_user || !sent_pw || !*sent_user || !*sent_pw) {
871 /* otherwise try get the username and password from a session, if present */
872 res = get_session_auth(r, &sent_user, &sent_pw, &sent_hash);
875 else {
876 res = APR_SUCCESS;
879 /* first test whether the site passphrase matches */
880 if (APR_SUCCESS == res && sent_user && sent_hash && sent_pw) {
881 rv = check_site(r, conf->site, sent_user, sent_hash);
882 if (OK == rv) {
883 fake_basic_authentication(r, conf, sent_user, sent_pw);
884 return OK;
888 /* otherwise test for a normal password match */
889 if (APR_SUCCESS == res && sent_user && sent_pw) {
890 rv = check_authn(r, sent_user, sent_pw);
891 if (OK == rv) {
892 fake_basic_authentication(r, conf, sent_user, sent_pw);
893 return OK;
898 * If we reach this point, the request should fail with access denied,
899 * except for one potential scenario:
901 * If the request is a POST, and the posted form contains user defined fields
902 * for a username and a password, and the username and password are correct,
903 * then return the response obtained by a GET to this URL.
905 * If an additional user defined location field is present in the form,
906 * instead of a GET of the current URL, redirect the browser to the new
907 * location.
909 * As a further option, if the user defined fields for the type of request,
910 * the mime type of the body of the request, and the body of the request
911 * itself are present, replace this request with a new request of the given
912 * type and with the given body.
914 * Otherwise access is denied.
916 * Reading the body requires some song and dance, because the input filters
917 * are not yet configured. To work around this problem, we create a
918 * subrequest and use that to create a sane filter stack we can read the
919 * form from.
921 * The main request is then capped with a kept_body input filter, which has
922 * the effect of guaranteeing the input stack can be safely read a second time.
925 if (HTTP_UNAUTHORIZED == rv && r->method_number == M_POST && ap_is_initial_req(r)) {
926 request_rec *rr;
927 apr_bucket_brigade *sent_body = NULL;
929 /* create a subrequest of our current uri */
930 rr = ap_sub_req_lookup_uri(r->uri, r, r->input_filters);
931 rr->headers_in = r->headers_in;
933 /* run the insert_filters hook on the subrequest to ensure a body read can
934 * be done properly.
936 ap_run_insert_filter(rr);
938 /* parse the form by reading the subrequest */
939 rv = get_form_auth(rr, conf->username, conf->password, conf->location,
940 conf->method, conf->mimetype, conf->body,
941 &sent_user, &sent_pw, &sent_loc, &sent_method,
942 &sent_mimetype, &sent_body, conf);
944 /* make sure any user detected within the subrequest is saved back to
945 * the main request.
947 r->user = apr_pstrdup(r->pool, rr->user);
949 /* we cannot clean up rr at this point, as memory allocated to rr is
950 * referenced from the main request. It will be cleaned up when the
951 * main request is cleaned up.
954 /* insert the kept_body filter on the main request to guarantee the
955 * input filter stack cannot be read a second time, optionally inject
956 * a saved body if one was specified in the login form.
958 if (sent_body && sent_mimetype) {
959 apr_table_set(r->headers_in, "Content-Type", sent_mimetype);
960 r->kept_body = sent_body;
962 else {
963 r->kept_body = apr_brigade_create(r->pool, r->connection->bucket_alloc);
965 ap_request_insert_filter_fn(r);
967 /* did the form ask to change the method? if so, switch in the redirect handler
968 * to relaunch this request as the subrequest with the new method. If the
969 * form didn't specify a method, the default value GET will force a redirect.
971 if (sent_method && strcmp(r->method, sent_method)) {
972 r->handler = FORM_REDIRECT_HANDLER;
975 /* check the authn in the main request, based on the username found */
976 if (OK == rv) {
977 rv = check_authn(r, sent_user, sent_pw);
978 if (OK == rv) {
979 fake_basic_authentication(r, conf, sent_user, sent_pw);
980 set_session_auth(r, sent_user, sent_pw, conf->site);
981 if (sent_loc) {
982 apr_table_set(r->headers_out, "Location", sent_loc);
983 return HTTP_MOVED_PERMANENTLY;
985 if (conf->loginsuccess) {
986 apr_table_set(r->headers_out, "Location", conf->loginsuccess);
987 return HTTP_MOVED_PERMANENTLY;
995 * did the admin prefer to be redirected to the login page on failure
996 * instead?
998 if (HTTP_UNAUTHORIZED == rv && conf->loginrequired) {
999 apr_table_set(r->headers_out, "Location", conf->loginrequired);
1000 return HTTP_MOVED_PERMANENTLY;
1003 /* did the user ask to be redirected on login success? */
1004 if (sent_loc) {
1005 apr_table_set(r->headers_out, "Location", sent_loc);
1006 rv = HTTP_MOVED_PERMANENTLY;
1011 * potential security issue: if we return a login to the browser, we must
1012 * send a no-store to make sure a well behaved browser will not try and
1013 * send the login details a second time if the back button is pressed.
1015 * if the user has full control over the backend, the
1016 * AuthCookieDisableNoStore can be used to turn this off.
1018 if (HTTP_UNAUTHORIZED == rv && !conf->disable_no_store) {
1019 apr_table_addn(r->headers_out, "Cache-Control", "no-store");
1020 apr_table_addn(r->err_headers_out, "Cache-Control", "no-store");
1023 return rv;
1028 * Handle a login attempt.
1030 * If the login session is either missing or form authnz is unsuccessful, a
1031 * 401 Not Authorized will be returned to the browser. The webmaster
1032 * is expected to insert a login form into the 401 Not Authorized
1033 * error screen.
1035 * If the webmaster wishes, they can point the form submission at this
1036 * handler, which will redirect the user to the correct page on success.
1037 * On failure, the 401 Not Authorized error screen will be redisplayed,
1038 * where the login attempt can be repeated.
1041 static int authenticate_form_login_handler(request_rec * r)
1044 auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
1045 &auth_form_module);
1047 const char *sent_user = NULL, *sent_pw = NULL, *sent_loc = NULL;
1048 int rv;
1050 if (strcmp(r->handler, FORM_LOGIN_HANDLER)) {
1051 return DECLINED;
1054 if (r->method_number != M_POST) {
1055 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
1056 "the " FORM_LOGIN_HANDLER " only supports the POST method for %s",
1057 r->uri);
1058 return HTTP_METHOD_NOT_ALLOWED;
1061 rv = get_form_auth(r, conf->username, conf->password, conf->location,
1062 NULL, NULL, NULL,
1063 &sent_user, &sent_pw, &sent_loc,
1064 NULL, NULL, NULL, conf);
1065 if (OK == rv) {
1066 rv = check_authn(r, sent_user, sent_pw);
1067 if (OK == rv) {
1068 set_session_auth(r, sent_user, sent_pw, conf->site);
1069 if (sent_loc) {
1070 apr_table_set(r->headers_out, "Location", sent_loc);
1071 return HTTP_MOVED_PERMANENTLY;
1073 if (conf->loginsuccess) {
1074 apr_table_set(r->headers_out, "Location", conf->loginsuccess);
1075 return HTTP_MOVED_PERMANENTLY;
1077 return HTTP_OK;
1081 /* did we prefer to be redirected to the login page on failure instead? */
1082 if (HTTP_UNAUTHORIZED == rv && conf->loginrequired) {
1083 apr_table_set(r->headers_out, "Location", conf->loginrequired);
1084 return HTTP_MOVED_PERMANENTLY;
1087 return rv;
1092 * Handle a logout attempt.
1094 * If an attempt is made to access this URL, any username and password
1095 * embedded in the session is deleted.
1097 * This has the effect of logging the person out.
1099 * If a logout URI has been specified, this function will create an
1100 * internal redirect to this page.
1102 static int authenticate_form_logout_handler(request_rec * r)
1105 auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
1106 &auth_form_module);
1108 if (strcmp(r->handler, FORM_LOGOUT_HANDLER)) {
1109 return DECLINED;
1112 /* remove the username and password, effectively logging the user out */
1113 set_session_auth(r, NULL, NULL, NULL);
1116 * make sure the logout page is never cached - otherwise the logout won't
1117 * work!
1119 apr_table_addn(r->headers_out, "Cache-Control", "no-store");
1120 apr_table_addn(r->err_headers_out, "Cache-Control", "no-store");
1122 /* if set, internal redirect to the logout page */
1123 if (conf->logout) {
1124 apr_table_addn(r->headers_out, "Location", conf->logout);
1125 return HTTP_TEMPORARY_REDIRECT;
1128 return HTTP_OK;
1133 * Handle a redirect attempt.
1135 * If during a form login, the method, mimetype and request body are
1136 * specified, this handler will ensure that this request is included
1137 * as an internal redirect.
1140 static int authenticate_form_redirect_handler(request_rec * r)
1143 request_rec *rr = NULL;
1144 const char *sent_method = NULL, *sent_mimetype = NULL;
1146 if (strcmp(r->handler, FORM_REDIRECT_HANDLER)) {
1147 return DECLINED;
1150 /* get the method and mimetype from the notes */
1151 get_notes_auth(r, NULL, NULL, &sent_method, &sent_mimetype);
1153 if (r->kept_body && sent_method && sent_mimetype) {
1155 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, LOG_PREFIX
1156 "internal redirect to method '%s' and body mimetype '%s' for the "
1157 "uri: %s", sent_method, sent_mimetype, r->uri);
1159 rr = ap_sub_req_method_uri(sent_method, r->uri, r, r->output_filters);
1160 r->status = ap_run_sub_req(rr);
1163 else {
1164 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
1165 "internal redirect requested but one or all of method, mimetype or "
1166 "body are NULL: %s", r->uri);
1167 return HTTP_INTERNAL_SERVER_ERROR;
1170 /* return the underlying error, or OK on success */
1171 return r->status == HTTP_OK || r->status == OK ? OK : r->status;
1175 static void register_hooks(apr_pool_t * p)
1177 #if AP_MODULE_MAGIC_AT_LEAST(20080403,1)
1178 ap_hook_check_authn(authenticate_form_authn, NULL, NULL, APR_HOOK_MIDDLE,
1179 AP_AUTH_INTERNAL_PER_CONF);
1180 #else
1181 ap_hook_check_user_id(authenticate_form_authn, NULL, NULL, APR_HOOK_MIDDLE);
1182 #endif
1183 ap_hook_handler(authenticate_form_login_handler, NULL, NULL, APR_HOOK_MIDDLE);
1184 ap_hook_handler(authenticate_form_logout_handler, NULL, NULL, APR_HOOK_MIDDLE);
1185 ap_hook_handler(authenticate_form_redirect_handler, NULL, NULL, APR_HOOK_MIDDLE);
1188 module AP_MODULE_DECLARE_DATA auth_form_module =
1190 STANDARD20_MODULE_STUFF,
1191 create_auth_form_dir_config, /* dir config creater */
1192 merge_auth_form_dir_config, /* dir merger --- default is to override */
1193 NULL, /* server config */
1194 NULL, /* merge server config */
1195 auth_form_cmds, /* command apr_table_t */
1196 register_hooks /* register hooks */