Commit 554454bf authored by David Woodhouse's avatar David Woodhouse

Calculate TOTP/HOTP codes for ourselves

No released version of liboath supports SHA256/SHA512. At this point it
looks like more trouble than it's worth, since generating an HMAC is
fairly trivial and we already do it in various places already.

This is simpler than adding conditional support for the new function
that liboath will eventually add to allow us to specify the HMAC
algorithm we want it to use.

(SHA1 only for now; we need to work out how the user asks for it too)
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 3e097d56
......@@ -2591,3 +2591,25 @@ int openconnect_yubikey_chalresp(struct openconnect_info *vpninfo,
return 0;
}
#endif
int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge)
{
int ret;
int hpos;
unsigned char hash[64]; /* Enough for a SHA256 */
hpos = 19;
ret = gnutls_hmac_fast(GNUTLS_MAC_SHA1,
vpninfo->oath_secret,
vpninfo->oath_secret_len,
challenge, 8, hash);
if (ret) {
vpninfo->progress(vpninfo, PRG_ERR,
_("Failed to calculate OATH HMAC: %s\n"),
gnutls_strerror(ret));
return -EINVAL;
}
hpos = hash[hpos] & 15;
return load_be32(&hash[hpos]) & 0x7fffffff;
}
......@@ -239,7 +239,7 @@ int can_gen_totp_code(struct openconnect_info *vpninfo,
} else if (vpninfo->token_tries == 1) {
vpn_progress(vpninfo, PRG_DEBUG,
_("OK to generate NEXT tokencode\n"));
vpninfo->token_time += OATH_TOTP_DEFAULT_TIME_STEP_SIZE;
vpninfo->token_time += 30;
} else {
/* limit the number of retries, to avoid account lockouts */
vpn_progress(vpninfo, PRG_INFO,
......@@ -272,30 +272,41 @@ int can_gen_hotp_code(struct openconnect_info *vpninfo,
return 0;
}
static int gen_hotp(struct openconnect_info *vpninfo, uint64_t data, char *output)
{
uint32_t data_be[2];
int digest;
data_be[0] = htonl(data >> 32);
data_be[1] = htonl(data);
digest = hotp_hmac(vpninfo, data_be);
if (digest < 0)
return digest;
digest %= 1000000;
snprintf(output, 7, "%d", digest);
return 0;
}
int do_gen_totp_code(struct openconnect_info *vpninfo,
struct oc_auth_form *form,
struct oc_form_opt *opt)
{
int oath_err;
char tokencode[7];
uint64_t challenge;
if (!vpninfo->token_time)
vpninfo->token_time = time(NULL);
vpn_progress(vpninfo, PRG_INFO, _("Generating OATH TOTP token code\n"));
oath_err = oath_totp_generate(vpninfo->oath_secret,
vpninfo->oath_secret_len,
vpninfo->token_time,
OATH_TOTP_DEFAULT_TIME_STEP_SIZE,
OATH_TOTP_DEFAULT_START_TIME,
6, tokencode);
if (oath_err != OATH_OK) {
vpn_progress(vpninfo, PRG_ERR,
_("Unable to generate OATH TOTP token code: %s\n"),
oath_strerror(oath_err));
/* XXX: Support non-standard start time and step size */
challenge = vpninfo->token_time / 30;
if (gen_hotp(vpninfo, challenge, tokencode))
return -EIO;
}
vpninfo->token_tries++;
opt->_value = strdup(tokencode);
......@@ -392,7 +403,6 @@ int do_gen_hotp_code(struct openconnect_info *vpninfo,
struct oc_auth_form *form,
struct oc_form_opt *opt)
{
int oath_err;
char tokencode[7];
int ret;
......@@ -405,20 +415,9 @@ int do_gen_hotp_code(struct openconnect_info *vpninfo,
if (ret)
return ret;
}
oath_err = oath_hotp_generate(vpninfo->oath_secret,
vpninfo->oath_secret_len,
vpninfo->token_time,
6, false, OATH_HOTP_DYNAMIC_TRUNCATION,
tokencode);
if (oath_err != OATH_OK) {
vpn_progress(vpninfo, PRG_ERR,
_("Unable to generate OATH HOTP token code: %s\n"),
oath_strerror(oath_err));
if (vpninfo->unlock_token)
vpninfo->unlock_token(vpninfo->tok_cbdata, NULL);
if (gen_hotp(vpninfo, vpninfo->token_time, tokencode))
return -EIO;
}
vpninfo->token_time++;
vpninfo->token_tries++;
opt->_value = strdup(tokencode);
......
......@@ -832,6 +832,7 @@ int openconnect_yubikey_chalresp(struct openconnect_info *vpninfo,
const void *challenge, int chall_len, void *result);
int openconnect_hash_yubikey_password(struct openconnect_info *vpninfo,
const char *password, const void *ident, int id_len);
int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge);
#if defined(OPENCONNECT_OPENSSL)
#define openconnect_https_connected(_v) ((_v)->https_ssl)
#elif defined (OPENCONNECT_GNUTLS)
......
......@@ -1667,3 +1667,20 @@ int openconnect_yubikey_chalresp(struct openconnect_info *vpninfo,
return 0;
}
#endif
int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge)
{
unsigned char hash[64]; /* Enough for a SHA256 */
unsigned int hashlen = sizeof(hash);
if (!HMAC(EVP_sha1(), vpninfo->oath_secret, vpninfo->oath_secret_len,
challenge, 8, hash, &hashlen)) {
vpninfo->progress(vpninfo, PRG_ERR,
_("Failed to calculate OATH HMAC\n"));
openconnect_report_ssl_errors(vpninfo);
return -EINVAL;
}
hashlen = hash[hashlen - 1] & 15;
return load_be32(&hash[hashlen]) & 0x7fffffff;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment