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