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 */
24 #include "ap_config.h"
26 #include "http_config.h"
27 #include "http_core.h"
29 #include "http_protocol.h"
30 #include "http_request.h"
31 #include "ap_provider.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
;
56 authn_provider_list
*providers
;
59 int authoritative_set
;
69 int fakebasicauth_set
;
79 int disable_no_store_set
;
80 const char *loginsuccess
;
82 const char *loginrequired
;
83 int loginrequired_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
));
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";
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
;
149 static const char *add_authn_provider(cmd_parms
* cmd
, void *config
,
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
,
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 "
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 "
200 /* Add it to the list now. */
201 if (!conf
->providers
) {
202 conf
->providers
= newp
;
205 authn_provider_list
*last
= conf
->providers
;
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 '&'.",
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
;
275 return check_string(cmd
, body
);
278 static const char *set_cookie_form_size(cmd_parms
* cmd
, void *config
,
281 auth_form_config_rec
*conf
= config
;
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;
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;
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;
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;
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
;
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;
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;
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;
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 "
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
,
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
,
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
,
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
,
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
,
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
,
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
,
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."),
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
417 static void note_cookie_auth_failure(request_rec
* r
)
419 auth_form_config_rec
*conf
= ap_get_module_config(r
->per_dir_config
,
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
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 */
442 /* find the first redirect */
448 /* have we isolated the user and pw before? */
449 authname
= ap_auth_name(r
);
451 apr_table_setn(notes
, apr_pstrcat(r
->pool
, authname
, "-user", NULL
), user
);
454 apr_table_setn(notes
, apr_pstrcat(r
->pool
, authname
, "-pw", NULL
), pw
);
457 apr_table_setn(notes
, apr_pstrcat(r
->pool
, authname
, "-method", NULL
), method
);
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 */
479 /* find the first redirect */
484 /* have we isolated the user and pw before? */
485 authname
= ap_auth_name(r
);
487 *user
= (char *) apr_table_get(r
->notes
, apr_pstrcat(r
->pool
, authname
, "-user", NULL
));
490 *pw
= (char *) apr_table_get(r
->notes
, apr_pstrcat(r
->pool
, authname
, "-pw", NULL
));
493 *method
= (char *) apr_table_get(r
->notes
, apr_pstrcat(r
->pool
, authname
, "-method", NULL
));
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
;
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
);
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
);
540 ap_session_get_fn(r
, z
, apr_pstrcat(r
->pool
, authname
, "-" MOD_SESSION_USER
, NULL
), user
);
543 ap_session_get_fn(r
, z
, apr_pstrcat(r
->pool
, authname
, "-" MOD_SESSION_PW
, NULL
), pw
);
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 */
551 r
->user
= (char *) *user
;
559 * Isolate the username and password in a POSTed form with the
560 * username in the "username" field, and the password in the
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
569 static int get_form_auth(request_rec
* r
,
570 const char *username
,
571 const char *password
,
572 const char *location
,
574 const char *mimetype
,
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
;
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
) {
599 res
= ap_parse_request_form_fn(r
, NULL
, &pairs
, -1, conf
->form_size
);
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
);
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
);
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
);
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
);
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
);
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 */
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
);
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
)) {
692 return AUTH_USER_NOT_FOUND
;
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
,
713 current_provider
= conf
->providers
;
715 const authn_provider
*provider
;
718 * For now, if a provider isn't set, we'll be nice and use the file
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
;
732 apr_table_setn(r
->notes
, AUTHN_PROVIDER_NAME_NOTE
, AUTHN_DEFAULT_PROVIDER
);
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
;
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
) {
753 /* If we're not really configured for providers, stop now. */
754 if (!conf
->providers
) {
758 current_provider
= current_provider
->next
;
759 } while (current_provider
);
761 if (auth_result
!= AUTH_GRANTED
) {
764 /* If we're not authoritative, then any error is ignored. */
765 if (!(conf
->authoritative
) && auth_result
!= AUTH_DENIED
) {
769 switch (auth_result
) {
771 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
, LOG_PREFIX
772 "user '%s': authentication failure for \"%s\": "
775 return_code
= HTTP_UNAUTHORIZED
;
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
;
782 case AUTH_GENERAL_ERROR
:
785 * We'll assume that the module has already said what its error
788 return_code
= HTTP_INTERNAL_SERVER_ERROR
;
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 */
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
,
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
;
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")) {
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
);
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
);
883 fake_basic_authentication(r
, conf
, sent_user
, sent_pw
);
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
);
892 fake_basic_authentication(r
, conf
, sent_user
, sent_pw
);
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
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
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
)) {
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
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
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
;
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 */
977 rv
= check_authn(r
, sent_user
, sent_pw
);
979 fake_basic_authentication(r
, conf
, sent_user
, sent_pw
);
980 set_session_auth(r
, sent_user
, sent_pw
, conf
->site
);
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
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? */
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");
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
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
,
1047 const char *sent_user
= NULL
, *sent_pw
= NULL
, *sent_loc
= NULL
;
1050 if (strcmp(r
->handler
, FORM_LOGIN_HANDLER
)) {
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",
1058 return HTTP_METHOD_NOT_ALLOWED
;
1061 rv
= get_form_auth(r
, conf
->username
, conf
->password
, conf
->location
,
1063 &sent_user
, &sent_pw
, &sent_loc
,
1064 NULL
, NULL
, NULL
, conf
);
1066 rv
= check_authn(r
, sent_user
, sent_pw
);
1068 set_session_auth(r
, sent_user
, sent_pw
, conf
->site
);
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
;
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
;
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
,
1108 if (strcmp(r
->handler
, FORM_LOGOUT_HANDLER
)) {
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
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 */
1124 apr_table_addn(r
->headers_out
, "Location", conf
->logout
);
1125 return HTTP_TEMPORARY_REDIRECT
;
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
)) {
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
);
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
);
1181 ap_hook_check_user_id(authenticate_form_authn
, NULL
, NULL
, APR_HOOK_MIDDLE
);
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 */