Apache mod auth form with OTP support as separated field
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) {

