From a68f2772aeaf89cceecc60df6c1a9df7d7de4b37 Mon Sep 17 00:00:00 2001 From: Francis Augusto Medeiros Date: Thu, 10 Nov 2022 09:26:57 +0100 Subject: [PATCH] Adding challenge customization option --- USAGE | 6 ++++++ src/pam_radius_auth.c | 33 +++++++++++++++++++++++++++++---- src/pam_radius_auth.h | 5 +++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/USAGE b/USAGE index 111c05f..f6bf64f 100644 --- a/USAGE +++ b/USAGE @@ -96,6 +96,12 @@ force_prompt - Request a new password and not using the previously entered password. This useful for multi-factor authentication when used with a Token. +challenge=string - Specifies a custom challenge prompt. This will substitute + any challenge prompt inside the Reply-Message attribute of a + Access-Challenge request to a custom prompt. Use + challenge="Enter your TOTP: " (or some other relevant string) + in this situation. + max_challenge=# - configure maximum number of challenges that a server may request. This is a workaround for broken servers and disabled by default. diff --git a/src/pam_radius_auth.c b/src/pam_radius_auth.c index 72f089e..dd5bcaa 100644 --- a/src/pam_radius_auth.c +++ b/src/pam_radius_auth.c @@ -179,6 +179,20 @@ static int _pam_parse(int argc, CONST char **argv, radius_conf_t *conf) snprintf(conf->prompt, MAXPROMPT, "%s: ", (arg+7)); } + } else if (!strncmp(arg, "challenge=", 10)) { + if (!strncmp(conf->challenge, (arg+10), MAXCHALLENGE)) { + _pam_log(LOG_WARNING, "ignoring duplicate '%s'", arg); + } else { + /* truncate excessive challenge prompts to (MAXCHALLENGE - 3) length */ + if (strlen((arg+10)) >= (MAXCHALLENGE - 3)) { + *((arg + 10) + (MAXCHALLENGE - 3)) = '\0'; + } + + /* set the new challenge prompt */ + memset(conf->challenge, 0, sizeof(conf->challenge)); + snprintf(conf->challenge, MAXCHALLENGE, "%s: ", (arg+10)); + } + } else if (!strcmp(arg, "force_prompt")) { conf->force_prompt = TRUE; @@ -196,13 +210,14 @@ static int _pam_parse(int argc, CONST char **argv, radius_conf_t *conf) } } + if (conf->debug) { #define print_bool(cond) (cond) ? "yes" : "no" #define print_string(cond) (cond) ? cond : "" _pam_log(LOG_DEBUG, "DEBUG: conf='%s' use_first_pass=%s try_first_pass=%s skip_passwd=%s retry=%d " \ "localifdown=%s client_id='%s' accounting_bug=%s ruser=%s prompt='%s' force_prompt=%s "\ - "prompt_attribute=%s max_challenge=%d privilege_level=%s", + "prompt_attribute=%s challenge=%s max_challenge=%d privilege_level=%s", conf->conf_file, print_bool(ctrl & PAM_USE_FIRST_PASS), print_bool(ctrl & PAM_TRY_FIRST_PASS), @@ -215,6 +230,7 @@ static int _pam_parse(int argc, CONST char **argv, radius_conf_t *conf) conf->prompt, print_bool(conf->force_prompt), print_bool(conf->prompt_attribute), + conf->challenge, conf->max_challenge, print_bool(conf->privilege_level) ); @@ -1282,6 +1298,8 @@ static int rad_converse(pam_handle_t *pamh, int msg_style, const char *message, return retval; \ } + + PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, UNUSED int flags, int argc, CONST char **argv) { CONST char *user = NULL; @@ -1323,6 +1341,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, UNUSED int flags, int arg return PAM_USER_UNKNOWN; } + DPRINT(LOG_DEBUG, "Got user name: '%s'", user); if (ctrl & PAM_RUSER_ARG) { @@ -1358,6 +1377,8 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, UNUSED int flags, int arg #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) { goto do_next; } + + /* build and initialize the RADIUS packet */ request->code = PW_AUTHENTICATION_REQUEST; get_random_vector(request->vector); @@ -1444,9 +1465,13 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, UNUSED int flags, int arg retval = PAM_AUTHINFO_UNAVAIL; goto do_next; } - - memcpy(challenge, a_reply->data, a_reply->length - 2); - challenge[a_reply->length - 2] = 0; + if (strlen(config.challenge) != 0){ + memcpy(challenge,config.challenge,sizeof(config.challenge)); + challenge[strlen(config.challenge)] = 0; + } else { + memcpy(challenge, a_reply->data, a_reply->length - 2); + challenge[a_reply->length - 2] = 0; + } /* It's full challenge-response, default to echo on, unless the server wants it off */ prompt = PAM_PROMPT_ECHO_ON; diff --git a/src/pam_radius_auth.h b/src/pam_radius_auth.h index 44ee566..b706095 100644 --- a/src/pam_radius_auth.h +++ b/src/pam_radius_auth.h @@ -58,6 +58,9 @@ #define MAXPROMPT 33 /* max prompt length, including '\0' */ #define DEFAULT_PROMPT "Password" /* default prompt, without the ': ' */ +/* Custom challenge length */ +#define MAXCHALLENGE 90 + /************************************************************************* * Platform specific defines @@ -100,6 +103,7 @@ #define PAM_RUSER_ARG 16 + /* buffer size for IP address in string form */ #define MAX_IP_LEN 16 @@ -185,6 +189,7 @@ typedef struct radius_conf_t { int debug; CONST char *conf_file; char prompt[MAXPROMPT]; + char challenge[MAXCHALLENGE]; int prompt_attribute; int privilege_level; } radius_conf_t;