radius: add access-challenge (multifactor) authentication

skip banning each next OTP for modules with allows_retries option:

sec_mod_auth: add check - the repeated password or the password of the
following factor is entered

radius: passwd_count incremention is related to a auth-message change

sec-mod-auth: set more descriptive name for password-retries indicator

Signed-off-by: Alexey Dotsenko <lex@rwx.su>
This commit is contained in:
Alexey Dotsenko
2019-05-29 15:51:09 +03:00
parent 15380220ac
commit 283daffc1a
3 changed files with 61 additions and 3 deletions

View File

@@ -30,6 +30,7 @@
#include "radius.h"
#include "auth/common.h"
#include "str.h"
#include <ccan/hash/hash.h>
#ifdef HAVE_RADIUS
@@ -124,6 +125,7 @@ static int radius_auth_init(void **ctx, void *pool, void *_vctx, const common_au
pctx->pass_msg[0] = 0;
pctx->vctx = vctx;
pctx->passwd_counter = 0;
default_realm = rc_conf_str(pctx->vctx->rh, "default_realm");
@@ -331,6 +333,18 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len)
goto cleanup;
}
if (pctx->state != NULL) {
if (rc_avpair_add(pctx->vctx->rh, &send, PW_STATE, pctx->state, -1, 0) == NULL) {
syslog(LOG_ERR,
"%s:%u: error in constructing radius message for user '%s'", __func__, __LINE__,
pctx->username);
ret = ERR_AUTH_FAIL;
goto cleanup;
}
talloc_free(pctx->state);
pctx->state = NULL;
}
pctx->pass_msg[0] = 0;
ret = rc_aaa(pctx->vctx->rh, pctx->id, send, &recvd, pctx->pass_msg, 1, PW_ACCESS_REQUEST);
@@ -421,12 +435,36 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len)
ret = 0;
goto cleanup;
} else if (ret == CHALLENGE_RC) {
vp = recvd;
while(vp != NULL) {
if (vp->attribute == PW_STATE && vp->type == PW_TYPE_STRING) {
/* State */
if (vp->lvalue > 0)
pctx->state = talloc_strdup(pctx, vp->strvalue);
pctx->id++;
syslog(LOG_DEBUG, "radius-auth: Access-Challenge response stage %u, State %s", pctx->passwd_counter, vp->strvalue);
ret = ERR_AUTH_CONTINUE;
}
vp = vp->next;
}
/* PW_STATE or PW_REPLY_MESSAGE is empty*/
if ((pctx->pass_msg[0] == 0) || (pctx->state == NULL)) {
strlcpy(pctx->pass_msg, pass_msg_failed, sizeof(pctx->pass_msg));
syslog(LOG_ERR, "radius-auth: Access-Challenge with invalid State or Reply-Message");
ret = ERR_AUTH_FAIL;
}
goto cleanup;
} else {
fail:
if (pctx->pass_msg[0] == 0)
strlcpy(pctx->pass_msg, pass_msg_failed, sizeof(pctx->pass_msg));
if (pctx->retries++ < MAX_PASSWORD_TRIES-1) {
if (pctx->retries++ < MAX_PASSWORD_TRIES-1 && pctx->passwd_counter == 0) {
ret = ERR_AUTH_CONTINUE;
goto cleanup;
}
@@ -449,10 +487,23 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len)
static int radius_auth_msg(void *ctx, void *pool, passwd_msg_st *pst)
{
struct radius_ctx_st *pctx = ctx;
size_t prompt_hash = 0;
if (pctx->pass_msg[0] != 0)
pst->msg_str = talloc_strdup(pool, pctx->pass_msg);
if (pctx->state != NULL) {
/* differentiate password prompts, if the hash of the prompt
* is different.
*/
prompt_hash = hash_any(pctx->pass_msg, strlen(pctx->pass_msg), 0);
if (pctx->prev_prompt_hash != prompt_hash)
pctx->passwd_counter++;
pctx->prev_prompt_hash = prompt_hash;
pst->counter = pctx->passwd_counter;
}
/* use default prompt */
return 0;
}

View File

@@ -68,6 +68,9 @@ struct radius_ctx_st {
unsigned id;
struct radius_vhost_ctx *vctx;
char *state;
unsigned passwd_counter;
size_t prev_prompt_hash;
};
extern const struct auth_mod_st radius_auth_funcs;

View File

@@ -328,6 +328,7 @@ int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int resu
{
int ret;
passwd_msg_st pst;
int passwd_retries = 1;
if ((result == ERR_AUTH_CONTINUE || result == 0) && e->module) {
memset(&pst, 0, sizeof(pst));
@@ -338,12 +339,15 @@ int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int resu
return ret;
}
e->msg_str = pst.msg_str;
/* password requested for the next stage in multifactor auth OR password is requested again at the same stage */
passwd_retries = (e->passwd_counter < pst.counter) ? 0 : 1;
e->passwd_counter = pst.counter;
}
if (result == ERR_AUTH_CONTINUE) {
/* if the module allows multiple retries for the password */
if (e->status != PS_AUTH_INIT && e->module && e->module->allows_retries) {
/* if the module allows multiple retries for the password and the password refers to the same stage */
if (e->status != PS_AUTH_INIT && e->module && e->module->allows_retries && passwd_retries == 1) {
sec_mod_add_score_to_ip(sec, e, e->acct_info.remote_ip, e->vhost->perm_config.config->ban_points_wrong_password);
}