diff --git a/modules/pam_oath/oath.h b/modules/pam_oath/oath.h index ffd28c0..f19849d 100644 --- a/modules/pam_oath/oath.h +++ b/modules/pam_oath/oath.h @@ -101,7 +101,18 @@ struct oath_key *oath_key_from_uri(const char *); struct oath_key *oath_key_from_file(const char *); char *oath_key_to_uri(const struct oath_key *); +#define DUMMY_LABEL ("oath-dummy-key") +#define DUMMY_LABELLEN (sizeof DUMMY_LABEL) +#define DUMMY_KEYLEN 80 + +struct oath_key *oath_dummy_key(enum oath_mode, enum oath_hash, unsigned int); + unsigned int oath_hotp(const uint8_t *, size_t, uint64_t, unsigned int); +int oath_hotp_current(struct oath_key *); +int oath_hotp_match(struct oath_key *, unsigned int, int); + unsigned int oath_totp(const uint8_t *, size_t, unsigned int); +int oath_totp_match(const struct oath_key *, unsigned int, int); +unsigned int oath_totp_current(const struct oath_key *); #endif diff --git a/modules/pam_oath/oath_hotp.c b/modules/pam_oath/oath_hotp.c index 741719e..e247f78 100644 --- a/modules/pam_oath/oath_hotp.c +++ b/modules/pam_oath/oath_hotp.c @@ -37,6 +37,7 @@ #include #include +#include #include "oath.h" @@ -87,3 +88,52 @@ oath_hotp(const uint8_t *K, size_t Klen, uint64_t seq, unsigned int Digit) D = Snum % mod; return (D); } + +/* + * Computes the current code for the given key and advances the counter. + */ +int +oath_hotp_current(struct oath_key *k) +{ + unsigned int code; + + if (k == NULL) + return (-1); + if (k->mode != om_hotp) + return (-1); + if (k->counter == UINT64_MAX) + return (-1); + code = oath_hotp(k->key, k->keylen, k->counter, k->digits); + k->counter += 1; + return (code); +} + +/* + * Compares the code provided by the user with expected values within a + * given window. Returns 1 if there was a match, 0 if not, and -1 if an + * error occurred. + */ +int +oath_hotp_match(struct oath_key *k, unsigned int response, int window) +{ + unsigned int code; + int dummy; + + if (k == NULL) + return (-1); + if (window < 1) + return (-1); + if (k->mode != om_hotp) + return (-1); + if (k->counter >= UINT64_MAX - window) + return (-1); + dummy = (memcmp(k->label, DUMMY_LABEL, DUMMY_LABELLEN) == 0); + for (int i = 0; i < window; ++i) { + code = oath_hotp(k->key, k->keylen, k->counter + i, k->digits); + if (code == response && !dummy) { + k->counter = k->counter + i; + return (1); + } + } + return (0); +} diff --git a/modules/pam_oath/oath_key.c b/modules/pam_oath/oath_key.c index eccab9d..43a714a 100644 --- a/modules/pam_oath/oath_key.c +++ b/modules/pam_oath/oath_key.c @@ -112,10 +112,8 @@ oath_key_from_uri(const char *uri) if ((q = strchr(p, '/')) == NULL) goto invalid; if (strlcmp("hotp", p, q - p) == 0) { - openpam_log(PAM_LOG_DEBUG, "OATH mode: HOTP"); key->mode = om_hotp; } else if (strlcmp("totp", p, q - p) == 0) { - openpam_log(PAM_LOG_DEBUG, "OATH mode: TOTP"); key->mode = om_totp; } else { goto invalid; @@ -132,7 +130,7 @@ oath_key_from_uri(const char *uri) p = q + 1; /* extract parameters */ - key->counter = UINTMAX_MAX; + key->counter = UINT64_MAX; while (*p != '\0') { if ((q = strchr(p, '=')) == NULL) goto invalid; @@ -152,6 +150,7 @@ oath_key_from_uri(const char *uri) base32_declen(r - q) > OATH_MAX_KEYLEN) goto invalid; key->key = key->data + key->labellen; + key->keylen = key->datalen - key->labellen; if (base32_dec(q, r - q, key->key, &key->keylen) != 0) goto invalid; if (base32_enclen(key->keylen) != (size_t)(r - q)) @@ -179,11 +178,11 @@ oath_key_from_uri(const char *uri) goto invalid; key->digits = *q - '0'; } else if (strlcmp("counter=", p, q - p) == 0) { - if (key->counter != UINTMAX_MAX) + if (key->counter != UINT64_MAX) /* dupe */ goto invalid; n = strtoumax(q, &e, 10); - if (e != r || n >= UINTMAX_MAX) + if (e != r || n >= UINT64_MAX) goto invalid; key->counter = (uint64_t)n; } else if (strlcmp("period=", p, q - p) == 0) { @@ -226,6 +225,7 @@ oath_key_from_uri(const char *uri) key->digits = 6; if (key->keylen == 0) goto invalid; + return (key); invalid: openpam_log(PAM_LOG_NOTICE, "invalid OATH URI: %s", uri); @@ -295,8 +295,8 @@ oath_key_to_uri(const struct oath_key *key) } /* compute length of base32-encoded key and append it */ - kslen = base32_enclen(key->keylen); - if ((tmp = realloc(uri, urilen + kslen + 1)) == NULL) { + kslen = base32_enclen(key->keylen) + 1; + if ((tmp = realloc(uri, urilen + kslen)) == NULL) { free(uri); return (NULL); } @@ -308,3 +308,22 @@ oath_key_to_uri(const struct oath_key *key) return (uri); } + +struct oath_key * +oath_dummy_key(enum oath_mode mode, enum oath_hash hash, unsigned int digits) +{ + struct oath_key *key; + + if ((key = oath_key_alloc(DUMMY_LABELLEN + DUMMY_KEYLEN)) == NULL) + return (NULL); + key->mode = mode; + key->digits = digits; + key->counter = 0; + key->timestep = 30; + key->hash = hash; + key->label = (char *)key->data; + memcpy(key->label, DUMMY_LABEL, DUMMY_LABELLEN); + key->key = key->data + DUMMY_LABELLEN; + key->keylen = DUMMY_KEYLEN; + return (key); +} diff --git a/modules/pam_oath/oath_totp.c b/modules/pam_oath/oath_totp.c index 5cfc6b6..f499967 100644 --- a/modules/pam_oath/oath_totp.c +++ b/modules/pam_oath/oath_totp.c @@ -34,6 +34,7 @@ #endif #include +#include #include #include "oath.h" @@ -48,3 +49,45 @@ oath_totp(const uint8_t *K, size_t Klen, unsigned int Digit) time(&now); return (oath_hotp(K, Klen, now / TOTP_TIME_STEP, Digit)); } + +unsigned int +oath_totp_current(const struct oath_key *k) +{ + unsigned int code; + uint64_t seq; + + if (k == NULL) + return (-1); + if (k->mode != om_totp) + return (-1); + if (k->timestep == 0) + return (-1); + seq = time(NULL) / k->timestep; + code = oath_hotp(k->key, k->keylen, seq, k->digits); + return (code); +} + +int +oath_totp_match(const struct oath_key *k, unsigned int response, int window) +{ + unsigned int code; + uint64_t seq; + int dummy; + + if (k == NULL) + return (-1); + if (window < 1) + return (-1); + if (k->mode != om_totp) + return (-1); + if (k->timestep == 0) + return (-1); + seq = time(NULL) / k->timestep; + dummy = (memcmp(k->label, DUMMY_LABEL, DUMMY_LABELLEN) == 0); + for (int i = -window; i <= window; ++i) { + code = oath_hotp(k->key, k->keylen, seq + i, k->digits); + if (code == response && !dummy) + return (1); + } + return (0); +}