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) {