mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 16:57:00 +08:00
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:
@@ -30,6 +30,7 @@
|
|||||||
#include "radius.h"
|
#include "radius.h"
|
||||||
#include "auth/common.h"
|
#include "auth/common.h"
|
||||||
#include "str.h"
|
#include "str.h"
|
||||||
|
#include <ccan/hash/hash.h>
|
||||||
|
|
||||||
#ifdef HAVE_RADIUS
|
#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->pass_msg[0] = 0;
|
||||||
pctx->vctx = vctx;
|
pctx->vctx = vctx;
|
||||||
|
pctx->passwd_counter = 0;
|
||||||
|
|
||||||
default_realm = rc_conf_str(pctx->vctx->rh, "default_realm");
|
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;
|
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;
|
pctx->pass_msg[0] = 0;
|
||||||
ret = rc_aaa(pctx->vctx->rh, pctx->id, send, &recvd, pctx->pass_msg, 1, PW_ACCESS_REQUEST);
|
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;
|
ret = 0;
|
||||||
goto cleanup;
|
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 {
|
} else {
|
||||||
fail:
|
fail:
|
||||||
if (pctx->pass_msg[0] == 0)
|
if (pctx->pass_msg[0] == 0)
|
||||||
strlcpy(pctx->pass_msg, pass_msg_failed, sizeof(pctx->pass_msg));
|
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;
|
ret = ERR_AUTH_CONTINUE;
|
||||||
goto cleanup;
|
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)
|
static int radius_auth_msg(void *ctx, void *pool, passwd_msg_st *pst)
|
||||||
{
|
{
|
||||||
struct radius_ctx_st *pctx = ctx;
|
struct radius_ctx_st *pctx = ctx;
|
||||||
|
size_t prompt_hash = 0;
|
||||||
|
|
||||||
if (pctx->pass_msg[0] != 0)
|
if (pctx->pass_msg[0] != 0)
|
||||||
pst->msg_str = talloc_strdup(pool, pctx->pass_msg);
|
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 */
|
/* use default prompt */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ struct radius_ctx_st {
|
|||||||
unsigned id;
|
unsigned id;
|
||||||
|
|
||||||
struct radius_vhost_ctx *vctx;
|
struct radius_vhost_ctx *vctx;
|
||||||
|
char *state;
|
||||||
|
unsigned passwd_counter;
|
||||||
|
size_t prev_prompt_hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct auth_mod_st radius_auth_funcs;
|
extern const struct auth_mod_st radius_auth_funcs;
|
||||||
|
|||||||
@@ -328,6 +328,7 @@ int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int resu
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
passwd_msg_st pst;
|
passwd_msg_st pst;
|
||||||
|
int passwd_retries = 1;
|
||||||
|
|
||||||
if ((result == ERR_AUTH_CONTINUE || result == 0) && e->module) {
|
if ((result == ERR_AUTH_CONTINUE || result == 0) && e->module) {
|
||||||
memset(&pst, 0, sizeof(pst));
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
e->msg_str = pst.msg_str;
|
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;
|
e->passwd_counter = pst.counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == ERR_AUTH_CONTINUE) {
|
if (result == ERR_AUTH_CONTINUE) {
|
||||||
/* if the module allows multiple retries for the password */
|
/* 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) {
|
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);
|
sec_mod_add_score_to_ip(sec, e, e->acct_info.remote_ip, e->vhost->perm_config.config->ban_points_wrong_password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user