diff options
Diffstat (limited to '')
-rw-r--r-- | src/modules/rlm_yubikey/decrypt.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/src/modules/rlm_yubikey/decrypt.c b/src/modules/rlm_yubikey/decrypt.c new file mode 100644 index 0000000..2bf4543 --- /dev/null +++ b/src/modules/rlm_yubikey/decrypt.c @@ -0,0 +1,137 @@ +/** + * $Id$ + * @file decrypt.c + * @brief Authentication for yubikey OTP tokens using the yubikey library. + * + * @author Arran Cudbard-Bell <a.cudbardb@networkradius.com> + * @copyright 2013 The FreeRADIUS server project + * @copyright 2013 Network RADIUS <info@networkradius.com> + */ +#include "rlm_yubikey.h" + +#ifdef HAVE_YUBIKEY +/** Decrypt a Yubikey OTP AES block + * + * @param inst Module configuration. + * @param request The current request. + * @param passcode string to decrypt. + * @return one of the RLM_RCODE_* constants. + */ +rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char const *passcode) +{ + uint32_t counter; + yubikey_token_st token; + + DICT_ATTR const *da; + + char private_id[(YUBIKEY_UID_SIZE * 2) + 1]; + VALUE_PAIR *key, *vp; + + da = dict_attrbyname("Yubikey-Key"); + if (!da) { + REDEBUG("Dictionary missing entry for 'Yubikey-Key'"); + return RLM_MODULE_FAIL; + } + + key = fr_pair_find_by_da(request->config, da, TAG_ANY); + if (!key) { + REDEBUG("Yubikey-Key attribute not found in control list, can't decrypt OTP data"); + return RLM_MODULE_INVALID; + } + + if (key->vp_length != YUBIKEY_KEY_SIZE) { + REDEBUG("Yubikey-Key length incorrect, expected %u got %zu", YUBIKEY_KEY_SIZE, key->vp_length); + return RLM_MODULE_INVALID; + } + + yubikey_parse((uint8_t const *) passcode + inst->id_len, key->vp_octets, &token); + + /* + * Apparently this just uses byte offsets... + */ + if (!yubikey_crc_ok_p((uint8_t *) &token)) { + REDEBUG("Decrypting OTP token data failed, rejecting"); + return RLM_MODULE_REJECT; + } + + RDEBUG("Token data decrypted successfully"); + + if (RDEBUG_ENABLED2) { + (void) fr_bin2hex((char *) &private_id, (uint8_t*) &token.uid, YUBIKEY_UID_SIZE); + RDEBUG2("Private ID : 0x%s", private_id); + RDEBUG2("Session counter : %u", yubikey_counter(token.ctr)); + RDEBUG2("# used in session : %u", token.use); + RDEBUG2("Token timestamp : %u", + (token.tstph << 16) | token.tstpl); + RDEBUG2("Random data : %u", token.rnd); + RDEBUG2("CRC data : 0x%x", token.crc); + } + + /* + * Private ID used for validation purposes + */ + vp = fr_pair_make(request->packet, &request->packet->vps, "Yubikey-Private-ID", NULL, T_OP_SET); + if (!vp) { + REDEBUG("Failed creating Yubikey-Private-ID"); + + return RLM_MODULE_FAIL; + } + fr_pair_value_memcpy(vp, token.uid, YUBIKEY_UID_SIZE); + + /* + * Token timestamp + */ + vp = fr_pair_make(request->packet, &request->packet->vps, "Yubikey-Timestamp", NULL, T_OP_SET); + if (!vp) { + REDEBUG("Failed creating Yubikey-Timestamp"); + + return RLM_MODULE_FAIL; + } + vp->vp_integer = (token.tstph << 16) | token.tstpl; + vp->vp_length = 4; + + /* + * Token random + */ + vp = fr_pair_make(request->packet, &request->packet->vps, "Yubikey-Random", NULL, T_OP_SET); + if (!vp) { + REDEBUG("Failed creating Yubikey-Random"); + + return RLM_MODULE_FAIL; + } + vp->vp_integer = token.rnd; + vp->vp_length = 4; + + /* + * Combine the two counter fields together so we can do + * replay attack checks. + */ + counter = (yubikey_counter(token.ctr) << 8) | token.use; + + vp = fr_pair_make(request->packet, &request->packet->vps, "Yubikey-Counter", NULL, T_OP_SET); + if (!vp) { + REDEBUG("Failed creating Yubikey-Counter"); + + return RLM_MODULE_FAIL; + } + vp->vp_integer = counter; + vp->vp_length = 4; + + /* + * Now we check for replay attacks + */ + vp = fr_pair_find_by_da(request->config, vp->da, TAG_ANY); + if (!vp) { + RWDEBUG("Yubikey-Counter not found in control list, skipping replay attack checks"); + return RLM_MODULE_OK; + } + + if (counter <= vp->vp_integer) { + REDEBUG("Replay attack detected! Counter value %u, is lt or eq to last known counter value %u", + counter, vp->vp_integer); + return RLM_MODULE_REJECT; + } + + return RLM_MODULE_OK; +} +#endif |