Apache mod auth form with OTP support as separated field

Posted: 9th febbraio 2018 by luca in nerd

In questi giorni ho avuto la necessità di modificare il modulo di autenticazione mod_auth_form di apache per aggiungere il supporto dell’OTP come campo separato e non per forza accodato alla password (questo perchè ho la necessità di salvare separatamente i due campi per successivi utilizzi in backend).

Con questa piccola patch si ottengono due nuovi parametri di configurazione inerenti a mod_auth_form, ovvero:

AuthFormOtp = Permette di impostare il nome del campo form che cotiene il campo OTP (come per AuthFormPassword) [default httpd_otp]
AuthFormOtpReuse = se impostato ad On rimanda il token otp accodato alla password al provider di autenticazione scelto anche quando tutte le credenziali arrivano dalla sessione (cookie nel nostro caso) [default off]

La logica di funzionamento è molto semplice; se nella POST il campo httpd_otp NON esiste il modulo funziona in maniera tradizionale, invece se questo campo è presente allora la password e l’otp vengono uniti in una unica stringa ed inviati al sottostante provider autenticazione scelto.
Il salvataggio dei dati nella sessione avviene mantendendo separati user password e otp.

Attenzione, l’otp e la password vengono uniti SOLO nel caso di una autenticazione proveniente da form (quindi una post http), se la ri-autenticazione sta avvenendo mediante la sessione (es: cookie) l’otp viene ignorato perchè, verosimilmente, sarà scaduto… questo comportamento però modificabile tramite l’apposita riga di configurazione AuthFormOtpReuse.

Ecco la patch:

--- mod_auth_form.c.orig	2014-06-03 14:14:22.000000000 +0200
+++ mod_auth_form.c	2018-02-09 10:11:56.274877918 +0100
@@ -40,6 +40,8 @@
 #define FORM_REDIRECT_HANDLER "form-redirect-handler"
 #define MOD_AUTH_FORM_HASH "site"
 
+#define MOD_SESSION_OTP "otp"
+
 static int (*ap_session_load_fn) (request_rec * r, session_rec ** z) = NULL;
 static apr_status_t (*ap_session_get_fn)(request_rec * r, session_rec * z,
         const char *key, const char **value) = NULL;
@@ -59,10 +61,14 @@
     int username_set;
     const char *password;
     int password_set;
+    const char *otp;
+    int otp_set;
     apr_size_t form_size;
     int form_size_set;
     int fakebasicauth;
     int fakebasicauth_set;
+    int otpreuse;
+    int otpreuse_set;
     const char *location;
     int location_set;
     const char *method;
@@ -95,6 +101,7 @@
     /* default form field names */
     conf->username = "httpd_username";
     conf->password = "httpd_password";
+    conf->otp = "httpd_otp";
     conf->location = "httpd_location";
     conf->method = "httpd_method";
     conf->mimetype = "httpd_mimetype";
@@ -118,12 +125,16 @@
     new->username_set = add->username_set || base->username_set;
     new->password = (add->password_set == 0) ? base->password : add->password;
     new->password_set = add->password_set || base->password_set;
+    new->otp = (add->otp_set == 0) ? base->otp : add->otp;
+    new->otp_set = add->otp_set || base->otp_set;
     new->location = (add->location_set == 0) ? base->location : add->location;
     new->location_set = add->location_set || base->location_set;
     new->form_size = (add->form_size_set == 0) ? base->form_size : add->form_size;
     new->form_size_set = add->form_size_set || base->form_size_set;
     new->fakebasicauth = (add->fakebasicauth_set == 0) ? base->fakebasicauth : add->fakebasicauth;
     new->fakebasicauth_set = add->fakebasicauth_set || base->fakebasicauth_set;
+    new->otpreuse = (add->otpreuse_set == 0) ? base->otpreuse : add->otpreuse;
+    new->otpreuse_set = add->otpreuse_set || base->otpreuse_set;
     new->method = (add->method_set == 0) ? base->method : add->method;
     new->method_set = add->method_set || base->method_set;
     new->mimetype = (add->mimetype_set == 0) ? base->mimetype : add->mimetype;
@@ -227,6 +238,14 @@
     return check_string(cmd, password);
 }
 
+static const char *set_cookie_form_otp(cmd_parms * cmd, void *config, const char *otp)
+{
+    auth_form_config_rec *conf = (auth_form_config_rec *) config;
+    conf->otp = otp;
+    conf->otp_set = 1;
+    return check_string(cmd, otp);
+}
+
 static const char *set_cookie_form_method(cmd_parms * cmd, void *config, const char *method)
 {
     auth_form_config_rec *conf = (auth_form_config_rec *) config;
@@ -342,6 +361,14 @@
     return NULL;
 }
 
+static const char *set_disable_otp_reuse(cmd_parms * cmd, void *config, int flag)
+{
+    auth_form_config_rec *conf = (auth_form_config_rec *) config;
+    conf->otpreuse = flag;
+    conf->otpreuse_set = 1;
+    return NULL;
+}
+
 static const char *set_disable_no_store(cmd_parms * cmd, void *config, int flag)
 {
     auth_form_config_rec *conf = (auth_form_config_rec *) config;
@@ -358,6 +385,8 @@
                   "The field of the login form carrying the username"),
     AP_INIT_TAKE1("AuthFormPassword", set_cookie_form_password, NULL, OR_AUTHCFG,
                   "The field of the login form carrying the password"),
+    AP_INIT_TAKE1("AuthFormOtp", set_cookie_form_otp, NULL, OR_AUTHCFG,
+                  "The field of the login form carrying the OTP"),
     AP_INIT_TAKE1("AuthFormLocation", set_cookie_form_location, NULL, OR_AUTHCFG,
                   "The field of the login form carrying the URL to redirect on "
                   "successful login."),
@@ -396,6 +425,10 @@
                  NULL, OR_AUTHCFG,
                  "Set to 'On' to pass through authentication to the rest of the "
                  "server as a basic authentication header."),
+    AP_INIT_FLAG("AuthFormOtpReuse", set_disable_otp_reuse,
+                 NULL, OR_AUTHCFG,
+                 "Set to 'On' for reuse OTP number from cookie and send'it to "
+                 "to authentication provider."),
     AP_INIT_FLAG("AuthFormDisableNoStore", set_disable_no_store,
                  NULL, OR_AUTHCFG,
                  "Set to 'on' to stop the sending of a Cache-Control no-store header with "
@@ -432,7 +465,7 @@
  * notes table.
  */
 static void set_notes_auth(request_rec * r,
-                                const char *user, const char *pw,
+                                const char *user, const char *pw, const char *otp,
                                 const char *method, const char *mimetype)
 {
     apr_table_t *notes = NULL;
@@ -456,6 +489,9 @@
     if (pw) {
         apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-pw", NULL), pw);
     }
+    if (otp) {
+        apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-otp", NULL), otp);
+    }
     if (method) {
         apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-method", NULL), method);
     }
@@ -471,7 +507,7 @@
  */
 static void get_notes_auth(request_rec *r,
                            const char **user, const char **pw,
-                           const char **method, const char **mimetype)
+                           const char **method, const char **mimetype, const char **otp)
 {
     const char *authname;
     request_rec *m = r;
@@ -493,6 +529,9 @@
     if (pw) {
         *pw = (char *) apr_table_get(m->notes, apr_pstrcat(m->pool, authname, "-pw", NULL));
     }
+    if (otp) {
+        *otp = (char *) apr_table_get(m->notes, apr_pstrcat(m->pool, authname, "-otp", NULL));
+    }
     if (method) {
         *method = (char *) apr_table_get(m->notes, apr_pstrcat(m->pool, authname, "-method", NULL));
     }
@@ -506,9 +545,9 @@
     }
 
     ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
-                  "from notes: user: %s, pw: %s, method: %s, mimetype: %s",
-                  user ? *user : "", pw ? *pw : "",
-                  method ? *method : "", mimetype ? *mimetype : "");
+                  "from notes: user: %s, pw: %s, method: %s, mimetype: %s, otp: %s",
+                  user ? *user : "", pw ? *pw : "", 
+                  method ? *method : "", mimetype ? *mimetype : "",otp ? *otp : "");
 
 }
 
@@ -519,7 +558,7 @@
  * and/or password will be removed from the session.
  */
 static apr_status_t set_session_auth(request_rec * r,
-                                     const char *user, const char *pw, const char *site)
+                                     const char *user, const char *pw, const char *site, const char *otp)
 {
     const char *hash = NULL;
     const char *authname = ap_auth_name(r);
@@ -533,6 +572,7 @@
     ap_session_load_fn(r, &z);
     ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_USER, NULL), user);
     ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_PW, NULL), pw);
+    ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_OTP, NULL), otp);
     ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_AUTH_FORM_HASH, NULL), hash);
 
     return APR_SUCCESS;
@@ -544,10 +584,12 @@
  * notes table, if present.
  */
 static apr_status_t get_session_auth(request_rec * r,
-                                     const char **user, const char **pw, const char **hash)
+                                     const char **user, const char **pw, const char **hash, const char **otp)
 {
     const char *authname = ap_auth_name(r);
     session_rec *z = NULL;
+    auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
+                                                      &auth_form_module);
 
     ap_session_load_fn(r, &z);
 
@@ -557,6 +599,11 @@
     if (pw) {
         ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_PW, NULL), pw);
     }
+    if (conf->otpreuse) {
+	    if (otp) {
+	        ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_OTP, NULL), otp);
+	    }
+    }
     if (hash) {
         ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_AUTH_FORM_HASH, NULL), hash);
     }
@@ -568,9 +615,10 @@
 
     ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
                   "from session: " MOD_SESSION_USER ": %s, " MOD_SESSION_PW
-                  ": %s, " MOD_AUTH_FORM_HASH ": %s",
+                  ": %s, " MOD_AUTH_FORM_HASH ": %s, " MOD_SESSION_OTP ": %s (reuse %s)" ,
                   user ? *user : "", pw ? *pw : "",
-                  hash ? *hash : "");
+                  hash ? *hash : "", otp ? *otp : "",
+                  conf->otpreuse ? "On" : "Off");
 
     return APR_SUCCESS;
 
@@ -590,12 +638,14 @@
 static int get_form_auth(request_rec * r,
                              const char *username,
                              const char *password,
+                             const char *otp,
                              const char *location,
                              const char *method,
                              const char *mimetype,
                              const char *body,
                              const char **sent_user,
                              const char **sent_pw,
+                             const char **sent_otp,
                              const char **sent_loc,
                              const char **sent_method,
                              const char **sent_mimetype,
@@ -612,7 +662,7 @@
     char *buffer;
 
     /* have we isolated the user and pw before? */
-    get_notes_auth(r, sent_user, sent_pw, sent_method, sent_mimetype);
+    get_notes_auth(r, sent_user, sent_pw, sent_method, sent_mimetype, sent_otp);
     if (*sent_user && *sent_pw) {
         return OK;
     }
@@ -639,6 +689,14 @@
             buffer[len] = 0;
             *sent_pw = buffer;
         }
+        else if (otp && !strcmp(pair->name, otp) && sent_otp) {
+            apr_brigade_length(pair->value, 1, &len);
+            size = (apr_size_t) len;
+            buffer = apr_palloc(r->pool, size + 1);
+            apr_brigade_flatten(pair->value, buffer, &size);
+            buffer[len] = 0;
+            *sent_otp = buffer;
+        }
         else if (location && !strcmp(pair->name, location) && sent_loc) {
             apr_brigade_length(pair->value, 1, &len);
             size = (apr_size_t) len;
@@ -669,11 +727,12 @@
     }
 
     ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
-                  "from form: user: %s, pw: %s, method: %s, mimetype: %s, location: %s",
+                  "from form: user: %s, pw: %s, method: %s, mimetype: %s, location: %s, otp: %s",
                   sent_user ? *sent_user : "", sent_pw ? *sent_pw : "",
                   sent_method ? *sent_method : "",
                   sent_mimetype ? *sent_mimetype : "",
-                  sent_loc ? *sent_loc : "");
+                  sent_loc ? *sent_loc : "",
+		  sent_otp ? *sent_otp : "");
 
     /* set the user, even though the user is unauthenticated at this point */
     if (sent_user && *sent_user) {
@@ -702,7 +761,7 @@
      * save away the username, password, mimetype and method, so that they
      * are available should the auth need to be run again.
      */
-    set_notes_auth(r, *sent_user, *sent_pw, sent_method ? *sent_method : NULL,
+    set_notes_auth(r, *sent_user, *sent_pw, *sent_otp ? *sent_otp : NULL, sent_method ? *sent_method : NULL,
                    sent_mimetype ? *sent_mimetype : NULL);
 
     return OK;
@@ -756,12 +815,20 @@
  *
  * Return an HTTP code.
  */
-static int check_authn(request_rec * r, const char *sent_user, const char *sent_pw)
+static int check_authn(request_rec * r, const char *sent_user, const char *sent_pw, const char *sent_otp)
 {
     authn_status auth_result;
     authn_provider_list *current_provider;
     auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
                                                       &auth_form_module);
+    char *sent_secret = NULL;
+
+    // -lm
+    if (sent_otp) {
+	sent_secret = apr_pstrcat(r->pool, sent_pw, sent_otp, NULL);
+    } else {
+	sent_secret = apr_pstrcat(r->pool, sent_pw, NULL);
+    }
 
     current_provider = conf->providers;
     do {
@@ -794,7 +861,12 @@
             break;
         }
 
-        auth_result = provider->check_password(r, sent_user, sent_pw);
+	ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+                  "provider->check_password - sent_user : %s, sent_secret: %s (pw: %s - otp: %s)", 
+		  sent_user, sent_secret, sent_pw, sent_otp);
+
+        auth_result = provider->check_password(r, sent_user, sent_secret);
+        //auth_result = provider->check_password(r, sent_user, sent_pw);
 
         apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE);
 
@@ -883,7 +955,7 @@
 {
     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_hash = NULL;
+    const char *sent_user = NULL, *sent_pw = NULL, *sent_hash = NULL, *sent_otp = NULL;
     const char *sent_loc = NULL, *sent_method = "GET", *sent_mimetype = NULL;
     const char *current_auth = NULL;
     const char *err;
@@ -919,11 +991,11 @@
     r->ap_auth_type = (char *) current_auth;
 
     /* try get the username and password from the notes, if present */
-    get_notes_auth(r, &sent_user, &sent_pw, &sent_method, &sent_mimetype);
+    get_notes_auth(r, &sent_user, &sent_pw, &sent_method, &sent_mimetype, &sent_otp);
     if (!sent_user || !sent_pw || !*sent_user || !*sent_pw) {
 
         /* otherwise try get the username and password from a session, if present */
-        res = get_session_auth(r, &sent_user, &sent_pw, &sent_hash);
+        res = get_session_auth(r, &sent_user, &sent_pw, &sent_hash, &sent_otp);
 
     }
     else {
@@ -941,7 +1013,7 @@
 
     /* otherwise test for a normal password match */
     if (APR_SUCCESS == res && sent_user && sent_pw) {
-        rv = check_authn(r, sent_user, sent_pw);
+        rv = check_authn(r, sent_user, sent_pw, sent_otp);
         if (OK == rv) {
             fake_basic_authentication(r, conf, sent_user, sent_pw);
             return OK;
@@ -990,9 +1062,9 @@
         ap_run_insert_filter(rr);
 
         /* parse the form by reading the subrequest */
-        rv = get_form_auth(rr, conf->username, conf->password, conf->location,
+        rv = get_form_auth(rr, conf->username, conf->password, conf->otp, conf->location,
                            conf->method, conf->mimetype, conf->body,
-                           &sent_user, &sent_pw, &sent_loc, &sent_method,
+                           &sent_user, &sent_pw, &sent_otp, &sent_loc, &sent_method,
                            &sent_mimetype, &sent_body, conf);
 
         /* make sure any user detected within the subrequest is saved back to
@@ -1028,10 +1100,10 @@
 
         /* check the authn in the main request, based on the username found */
         if (OK == rv) {
-            rv = check_authn(r, sent_user, sent_pw);
+            rv = check_authn(r, sent_user, sent_pw, sent_otp);
             if (OK == rv) {
                 fake_basic_authentication(r, conf, sent_user, sent_pw);
-                set_session_auth(r, sent_user, sent_pw, conf->site);
+                set_session_auth(r, sent_user, sent_pw, conf->site, sent_otp);
                 if (sent_loc) {
                     apr_table_set(r->headers_out, "Location", sent_loc);
                     return HTTP_MOVED_TEMPORARILY;
@@ -1115,7 +1187,7 @@
     auth_form_config_rec *conf;
     const char *err;
 
-    const char *sent_user = NULL, *sent_pw = NULL, *sent_loc = NULL;
+    const char *sent_user = NULL, *sent_pw = NULL, *sent_otp = NULL, *sent_loc = NULL;
     int rv;
 
     if (strcmp(r->handler, FORM_LOGIN_HANDLER)) {
@@ -1131,14 +1203,14 @@
 
     conf = ap_get_module_config(r->per_dir_config, &auth_form_module);
 
-    rv = get_form_auth(r, conf->username, conf->password, conf->location,
+    rv = get_form_auth(r, conf->username, conf->password, conf->otp, conf->location,
                        NULL, NULL, NULL,
-                       &sent_user, &sent_pw, &sent_loc,
+                       &sent_user, &sent_pw, &sent_otp, &sent_loc,
                        NULL, NULL, NULL, conf);
     if (OK == rv) {
-        rv = check_authn(r, sent_user, sent_pw);
+        rv = check_authn(r, sent_user, sent_pw, sent_otp);
         if (OK == rv) {
-            set_session_auth(r, sent_user, sent_pw, conf->site);
+            set_session_auth(r, sent_user, sent_pw, conf->site, sent_otp);
             if (sent_loc) {
                 apr_table_set(r->headers_out, "Location", sent_loc);
                 return HTTP_MOVED_TEMPORARILY;
@@ -1202,7 +1274,7 @@
     conf = ap_get_module_config(r->per_dir_config, &auth_form_module);
 
     /* remove the username and password, effectively logging the user out */
-    set_session_auth(r, NULL, NULL, NULL);
+    set_session_auth(r, NULL, NULL, NULL, NULL);
 
     /*
      * make sure the logout page is never cached - otherwise the logout won't
@@ -1249,7 +1321,7 @@
     }
 
     /* get the method and mimetype from the notes */
-    get_notes_auth(r, NULL, NULL, &sent_method, &sent_mimetype);
+    get_notes_auth(r, NULL, NULL, &sent_method, &sent_mimetype, NULL);
 
     if (r->kept_body && sent_method && sent_mimetype) {