On 04/09/2008 01:46 PM, [EMAIL PROTECTED] wrote:
Author: minfrin
Date: Wed Apr  9 04:46:46 2008
New Revision: 646285

URL: http://svn.apache.org/viewvc?rev=646285&view=rev
Log:
mod_auth_form: Add a module capable of allowing end users to log
in using an HTML form, storing the credentials within mod_session.

Added:
    httpd/httpd/trunk/docs/manual/mod/mod_auth_form.xml   (with props)
    httpd/httpd/trunk/modules/aaa/mod_auth_form.c   (with props)
Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/modules/aaa/config.m4


Added: httpd/httpd/trunk/modules/aaa/mod_auth_form.c
URL: 
http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/aaa/mod_auth_form.c?rev=646285&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/aaa/mod_auth_form.c (added)
+++ httpd/httpd/trunk/modules/aaa/mod_auth_form.c Wed Apr  9 04:46:46 2008

+static int check_site(request_rec * r, const char *site, const char 
*sent_user, const char *sent_hash)
+{
+
+    if (site && sent_user && sent_hash) {
+        const char *hash = ap_md5(r->pool,
+                      (unsigned char *) apr_pstrcat(r->pool, sent_user, ":", 
site, NULL));
+ +// if (APR_SUCCESS == apr_password_validate(apr_pstrcat(r->pool, sent_user, ":", site, NULL),
+//                                                 sent_hash)) {

This is a C++ style comment that breaks with ANSI compilers.
I think it would be better to remove the code or use a #if 0 #endif block.

+        if (!strcmp(sent_hash, hash)) {
+            return OK;
+        }
+        else {
+            return AUTH_USER_NOT_FOUND;
+        }
+    }
+
+    return DECLINED;
+
+}
+

+
+/**
+ * Must we use form authentication? If so, extract the cookie and run
+ * the authnz hooks to determine if the login is valid.
+ *
+ * If the login is not valid, a 401 Not Authorized will be returned. It
+ * is up to the webmaster to ensure this screen displays a suitable login
+ * form to give the user the opportunity to log in.
+ */
+static int authenticate_form_user(request_rec * r)
+{
+    auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
+                                                      &auth_form_module);
+    const char *sent_user = NULL, *sent_pw = NULL, *sent_loc = NULL, 
*sent_method = NULL,
+        *sent_mimetype = NULL, *sent_hash = NULL;
+    const char *current_auth = NULL;
+    apr_status_t res;
+    int rv = HTTP_UNAUTHORIZED;
+    int rv2 = HTTP_UNAUTHORIZED;
+
+    /* Are we configured to be Form auth? */
+    current_auth = ap_auth_type(r);
+    if (!current_auth || strcasecmp(current_auth, "form")) {
+        return DECLINED;
+    }
+
+    /*
+     * XSS security warning: using cookies to store private data only works
+     * when the administrator has full control over the source website. When
+     * in forward-proxy mode, websites are public by definition, and so can
+     * never be secure. Abort the auth attempt in this case.
+     */
+    if (PROXYREQ_PROXY == r->proxyreq) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR,
+                      0, r, LOG_PREFIX "form auth cannot be used for proxy "
+                      "requests due to XSS risk, access denied: %s", r->uri);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* We need an authentication realm. */
+    if (!ap_auth_name(r)) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR,
+                      0, r, LOG_PREFIX "need AuthName: %s", r->uri);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    r->ap_auth_type = (char *) current_auth;
+
+    /* try get the username and password from a session, if present */
+    res = get_session_auth(r, &sent_user, &sent_pw, &sent_hash);
+
+    /* first test whether the site passphrase matches */
+    if (APR_SUCCESS == res && sent_user && sent_hash && sent_pw) {
+        rv = check_site(r, conf->site, sent_user, sent_hash);
+        if (OK == rv) {
+            fake_basic_authentication(r, conf, sent_user, sent_pw);
+            return OK;
+        }
+    }
+
+    /* otherwise test for a normal password match */
+    if (APR_SUCCESS == res && sent_user && sent_pw) {
+        rv = check_auth(r, sent_user, sent_pw);
+        if (OK == rv) {
+            fake_basic_authentication(r, conf, sent_user, sent_pw);
+            return OK;
+        }
+    }
+
+    /*
+     * If we reach this point, the request should fail with access denied,
+     * except for one potential scenario:
+     *
+     * If the request is a POST, and the posted form contains user defined 
fields
+     * for a username and a password, and the username and password are 
correct,
+     * then return the response obtained by a GET to this URL.
+     *
+     * If an additional user defined location field is present in the form,
+     * instead of a GET of the current URL, redirect the browser to the new
+     * location.
+     *
+     * As a further option, if the user defined fields for the type of request,
+     * the mime type of the body of the request, and the body of the request
+     * itself are present, replace this request with a new request of the given
+     * type and with the given body.
+     *
+     * Otherwise access is denied.
+     */
+    if (r->method_number == M_POST) {
+        rv2 = get_form_auth(r, conf->username, conf->password, conf->location,
+                            conf->method, conf->mimetype, conf->body,
+                            &sent_user, &sent_pw, &sent_loc, &sent_method,
+                            &sent_mimetype, conf);
+        if (OK == rv2) {
+            rv = check_auth(r, sent_user, sent_pw);
+            if (OK == rv) {
+                fake_basic_authentication(r, conf, sent_user, sent_pw);
+                set_session_auth(r, sent_user, sent_pw, conf->site);
+            }
+        }
+    }
+
+    /*
+     * did the admin prefer to be redirected to the login page on failure
+     * instead?
+     */
+    if (HTTP_UNAUTHORIZED == rv && conf->loginrequired) {
+        apr_table_set(r->headers_out, "Location", conf->loginrequired);
+        return HTTP_MOVED_PERMANENTLY;
+    }
+
+    /* did the user ask to be redirected on login success? */
+    if (sent_loc) {
+        apr_table_set(r->headers_out, "Location", sent_loc);
+        rv = HTTP_MOVED_PERMANENTLY;
+    }
+
+    /*
+     * If the user has submitted a sent method along with their form, switch
+     * in the redirect handler. The redirect handler will replace the form
+     * login request with the request given inside the login form.
+     */
+    if (OK == rv) {
+        if (sent_method && sent_mimetype && r->kept_body) {
+            r->handler = FORM_REDIRECT_HANDLER;
+        }
+        else if (sent_method || sent_mimetype || r->kept_body) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR,
+             0, r, LOG_PREFIX "the login form must contain a field for all "
+                          "three of the method, mimetype and body for the original 
request "
+                          "to be redirected, reverting to GET: %s", r->uri);
+        }
+    }
+
+    /*
+     * potential security issue: if we return a login to the browser, we must
+     * send a no-store to make sure a well behaved browser will not try and
+     * send the login details a second time if the back button is pressed.
+ * + * if the user has full control over the backend, the
+     * AuthCookieDisableNoStore can be used to turn this off.
+     */
+    if (HTTP_UNAUTHORIZED == rv && !conf->disable_no_store) {
+        apr_table_addn(r->headers_out, "Cache-Control", "no-store");
+        apr_table_addn(r->err_headers_out, "Cache-Control", "no-store");

Isn't it sufficient to add this r->err_headers_out?

+    }
+
+    return rv;
+
+}
+
+/**
+ * Handle a login attempt.
+ *
+ * If the login session is either missing or form authnz is unsuccessful, a
+ * 401 Not Authorized will be returned to the browser. The webmaster
+ * is expected to insert a login form into the 401 Not Authorized
+ * error screen.
+ *
+ * If the webmaster wishes, they can point the form submission at this
+ * handler, which will redirect the user to the correct page on success.
+ * On failure, the 401 Not Authorized error screen will be redisplayed,
+ * where the login attempt can be repeated.
+ *
+ */
+static int authenticate_form_login_handler(request_rec * r)
+{
+
+    auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
+                                                      &auth_form_module);
+
+    const char *sent_user = NULL, *sent_pw = NULL, *sent_loc = NULL;
+    int rv;
+
+    if (strcmp(r->handler, FORM_LOGIN_HANDLER)) {
+        return DECLINED;
+    }
+
+    if (r->method_number != M_POST) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
+          "the " FORM_LOGIN_HANDLER " only supports the POST method for %s",
+                      r->uri);
+        return HTTP_METHOD_NOT_ALLOWED;
+    }
+
+    rv = get_form_auth(r, conf->username, conf->password, conf->location,
+                       NULL, NULL, NULL,
+                       &sent_user, &sent_pw, &sent_loc,
+                       NULL, NULL, conf);
+    if (OK == rv) {
+        rv = check_auth(r, sent_user, sent_pw);
+        if (OK == rv) {
+            set_session_auth(r, sent_user, sent_pw, conf->site);
+            if (sent_loc) {
+                apr_table_set(r->headers_out, "Location", sent_loc);
+                return HTTP_MOVED_PERMANENTLY;

Shouldn't this be HTTP_TEMPORARY_REDIRECT?

+            }
+            if (conf->loginsuccess) {
+                apr_table_set(r->headers_out, "Location", conf->loginsuccess);
+                return HTTP_MOVED_PERMANENTLY;

Shouldn't this be HTTP_TEMPORARY_REDIRECT?

+            }
+            return HTTP_OK;
+        }
+    }
+
+    /* did we prefer to be redirected to the login page on failure instead? */
+    if (HTTP_UNAUTHORIZED == rv && conf->loginrequired) {
+        apr_table_set(r->headers_out, "Location", conf->loginrequired);
+        return HTTP_MOVED_PERMANENTLY;

Shouldn't this be HTTP_TEMPORARY_REDIRECT?

+    }
+
+    return rv;
+
+}
+
+/**
+ * Handle a logout attempt.
+ *
+ * If an attempt is made to access this URL, any username and password
+ * embedded in the session is deleted.
+ *
+ * This has the effect of logging the person out.
+ *
+ * If a logout URI has been specified, this function will create an
+ * internal redirect to this page.
+ */
+static int authenticate_form_logout_handler(request_rec * r)
+{
+
+    auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
+                                                      &auth_form_module);
+
+    if (strcmp(r->handler, FORM_LOGOUT_HANDLER)) {
+        return DECLINED;
+    }
+
+    /* remove the username and password, effectively logging the user out */
+    set_session_auth(r, NULL, NULL, NULL);
+
+    /*
+     * make sure the logout page is never cached - otherwise the logout won't
+     * work!
+     */
+    apr_table_addn(r->headers_out, "Cache-Control", "no-store");
+    apr_table_addn(r->err_headers_out, "Cache-Control", "no-store");

Isn't it sufficient to add this r->headers_out?

+
+    /* if set, internal redirect to the logout page */
+    if (conf->logout) {
+        apr_table_addn(r->headers_out, "Location", conf->logout);
+        return HTTP_TEMPORARY_REDIRECT;
+    }
+
+    return HTTP_OK;
+
+}
+
+/**
+ * Handle a redirect attempt.
+ *
+ * If during a form login, the method, mimetype and request body are
+ * specified, this handler will ensure that this request is included
+ * as an internal redirect.
+ *
+ */
+static int authenticate_form_redirect_handler(request_rec * r)
+{
+
+    request_rec *rr = NULL;
+    const char *sent_method = NULL, *sent_mimetype = NULL;
+
+    if (strcmp(r->handler, FORM_REDIRECT_HANDLER)) {
+        return DECLINED;
+    }
+
+    /* get the method and mimetype from the notes */
+    get_notes_auth(r, NULL, NULL, &sent_method, &sent_mimetype);
+
+    if (r->kept_body && sent_method && sent_mimetype) {
+
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, LOG_PREFIX
+          "internal redirect to method '%s' and body mimetype '%s' for the "
+                      "uri: %s", sent_method, sent_mimetype, r->uri);
+
+        rr = ap_sub_req_method_uri(sent_method, r->uri, r, r->output_filters);
+        r->status = ap_run_sub_req(rr);
+
+    }
+    else {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
+        "internal redirect requested but one or all of method, mimetype or "
+                      "body are NULL: %s", r->uri);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* return the underlying error, or OK on success */
+    return r->status == HTTP_OK || r->status == OK ? OK : r->status;

Why not returning r->status here directly?

Regards

Rüdiger

Reply via email to