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