*/
/*
- * TODO: add "resync" keyword (like "challenge").
* TODO: all requests (success or fail) should take ~ the same amount of time.
* TODO: x99_pwe: change add_vps to success_vps and fail_vps.
* TODO: add Auth-Type config item if not present?
int fast_sync; /* response-before-challenge mode */
int allow_async; /* C/R mode allowed? */
char *chal_req; /* keyword requesting challenge for fast_sync mode */
+ char *resync_req; /* keyword requesting resync for fast_sync mode */
int ewindow_size; /* sync mode event window size (right side value) */
#if 0
int twindow_min; /* sync mode time window left side */
NULL, "0" },
{ "challenge_req", PW_TYPE_STRING_PTR, offsetof(x99_token_t, chal_req),
NULL, CHALLENGE_REQ },
+ { "resync_req", PW_TYPE_STRING_PTR, offsetof(x99_token_t, resync_req),
+ NULL, RESYNC_REQ },
{ "ewindow_size", PW_TYPE_INTEGER, offsetof(x99_token_t, ewindow_size),
NULL, "0" },
#if 0
x99_user_info_t user_info;
int user_found, auth_type;
int pwattr;
+ int32_t sflags = 0; /* flags for state */
VALUE_PAIR *vp;
/* Early exit if Auth-Type == reject */
if (inst->fast_sync &&
((user_info.card_id & X99_CF_SM) || !user_found)) {
- if (x99_pw_valid(request, pwattr, inst->chal_req, NULL)) {
+ if ((x99_pw_valid(request, pwattr, inst->resync_req, NULL) &&
+ /* Set a bit indicating resync */ (sflags |= htonl(1))) ||
+ x99_pw_valid(request, pwattr, inst->chal_req, NULL)) {
/*
* Generate a challenge if requested. We don't test for card
* support [for async] because it's tricky for unknown users.
} /* if (fast_sync && card supports sync mode) */
gen_challenge:
+ /* Set the resync bit by default if the user can't request it. */
+ if (!inst->fast_sync)
+ sflags |= htonl(1);
+
/* Generate a random challenge. */
if (x99_get_random(rnd_fd, rawchallenge, inst->chal_len) == -1) {
radlog(L_ERR, "rlm_x99_token: autz: failed to obtain random data");
}
now = htonl(now);
- if (x99_gen_state(&state, NULL, challenge, now, hmac_key) != 0) {
+ if (x99_gen_state(&state, NULL, challenge, sflags, now, hmac_key) != 0){
radlog(L_ERR, "rlm_x99_token: autz: failed to generate state");
return RLM_MODULE_FAIL;
}
} else {
+ /* x2 b/c pairmake() string->octet needs even num of digits */
state = rad_malloc(3 + inst->chal_len * 2);
(void) sprintf(state, "0x%s%s", challenge, challenge);
}
x99_user_info_t user_info;
char *username;
int i, failcount, pwattr, rc;
+ int32_t sflags = 0; /* flags from state */
time_t last_auth;
char challenge[MAX_CHALLENGE_LEN + 1];
/* Extend expected length if state should have been protected. */
if (user_info.card_id & X99_CF_AM)
- e_length += 4 + 16; /* time + hmac */
+ e_length += 4 + 4 + 16; /* sflags + time + hmac */
if (vp->length != e_length) {
radlog(L_AUTH, "rlm_x99_token: auth: bad state for [%s]: "
/* Verify the state. */
(void) memset(challenge, 0, sizeof(challenge));
(void) memcpy(challenge, vp->strvalue, inst->chal_len);
- (void) memcpy(&then, vp->strvalue + inst->chal_len, 4);
- if (x99_gen_state(NULL, &state, challenge, then, hmac_key) != 0) {
+ (void) memcpy(&sflags, vp->strvalue + inst->chal_len, 4);
+ (void) memcpy(&then, vp->strvalue + inst->chal_len + 4, 4);
+ if (x99_gen_state(NULL,&state,challenge,sflags,then,hmac_key) != 0){
radlog(L_ERR, "rlm_x99_token: auth: failed to generate state");
return RLM_MODULE_FAIL;
}
"in async mode", username);
}
- /*
- * Resync the card. The sync data doesn't mean anything for
- * async-only cards, but we want the side effects of resetting
- * the failcount and the last auth time. We "fail-out" if we
- * can't do this, because if we can't update the last auth time,
- * we will be open to replay attacks over the lifetime of the
- * State attribute (inst->maxdelay).
- */
rc = RLM_MODULE_OK;
- if (x99_get_sync_data(inst->syncdir, username, user_info.card_id,
- 1, 0, challenge, user_info.keyblock) != 0) {
- radlog(L_ERR, "rlm_x99_token: auth: unable to get "
- "sync data e:%d t:%d for [%s] (for resync)",
- 1, 0, username);
- rc = RLM_MODULE_FAIL;
- } else if (x99_set_sync_data(inst->syncdir, username, challenge,
- user_info.keyblock) != 0) {
- radlog(L_ERR, "rlm_x99_token: auth: unable to set sync "
- "data for [%s] (for resync)", username);
- rc = RLM_MODULE_FAIL;
+ if (ntohl(sflags) & 1) {
+ /*
+ * Resync the card. The sync data doesn't mean anything for
+ * async-only cards, but we want the side effects of resetting
+ * the failcount and the last auth time. We "fail-out" if we
+ * can't do this, because if we can't update the last auth time,
+ * we will be open to replay attacks over the lifetime of the
+ * State attribute (inst->maxdelay).
+ */
+ if (x99_get_sync_data(inst->syncdir, username, user_info.card_id,
+ 1, 0, challenge, user_info.keyblock) != 0) {
+ radlog(L_ERR, "rlm_x99_token: auth: unable to get "
+ "sync data e:%d t:%d for [%s] (for resync)",
+ 1, 0, username);
+ rc = RLM_MODULE_FAIL;
+ } else if (x99_set_sync_data(inst->syncdir, username, challenge,
+ user_info.keyblock) != 0) {
+ radlog(L_ERR, "rlm_x99_token: auth: unable to set sync "
+ "data for [%s] (for resync)", username);
+ rc = RLM_MODULE_FAIL;
+ }
+ } else {
+ /* Just update last_auth, failcount. */
+ if (x99_reset_failcount(inst->syncdir, username) != 0) {
+ radlog(L_ERR, "rlm_x99_token: auth: unable to reset failcount "
+ "for [%s]", username);
+ /* NB: don't fail */
+ }
+ if (x99_upd_last_auth(inst->syncdir, username) != 0) {
+ radlog(L_ERR, "rlm_x99_token: auth: unable to update auth time "
+ "for [%s]", username);
+ rc = RLM_MODULE_FAIL;
+ }
}
goto return_pw_valid;
} /* if (user authenticated async) */
free(inst->syncdir);
free(inst->chal_text);
free(inst->chal_req);
+ free(inst->resync_req);
free(instance);
return 0;
}
* track of all challenges issued over that time interval for
* better protection.
*
- * Our state, then, is (challenge + time + hmac(challenge + time, key)),
+ * Our state, then, is
+ * (challenge + resync + time + hmac(challenge + resync + time, key)),
* where '+' denotes concatentation, 'challenge' is the ASCII octets of
- * the challenge, 'time' is the 32-bit time (LSB if time_t is 64 bits)
+ * the challenge, 'flags' is a 32-bit value that can be used to record
+ * additional info, 'time' is the 32-bit time (LSB if time_t is 64 bits)
* in network byte order, and 'key' is a random key, generated in
* x99_token_init(). This means that only the server which generates
* a challenge can verify it; this should be OK if your NAS's load balance
*/
int
x99_gen_state(char **ascii_state, unsigned char **raw_state,
- const char challenge[MAX_CHALLENGE_LEN + 1], int32_t when,
- const unsigned char key[16])
+ const char challenge[MAX_CHALLENGE_LEN + 1], int32_t flags,
+ int32_t when, const unsigned char key[16])
{
HMAC_CTX hmac_ctx;
unsigned char hmac[MD5_DIGEST_LENGTH];
*/
HMAC_Init(&hmac_ctx, key, sizeof(key), EVP_md5());
HMAC_Update(&hmac_ctx, challenge, strlen(challenge));
+ HMAC_Update(&hmac_ctx, (unsigned char *) &flags, 4);
HMAC_Update(&hmac_ctx, (unsigned char *) &when, 4);
HMAC_Final(&hmac_ctx, hmac, NULL);
HMAC_cleanup(&hmac_ctx);
/* Fill in raw_state if requested. */
if (raw_state) {
- *raw_state = rad_malloc(strlen(challenge) + 4 + sizeof(hmac));
+ *raw_state = rad_malloc(strlen(challenge) + 8 + sizeof(hmac));
p = *raw_state;
(void) memcpy(p, challenge, strlen(challenge));
p += strlen(challenge);
+ (void) memcpy(p, &flags, 4);
+ p += 4;
(void) memcpy(p, &when, 4);
p += 4;
(void) memcpy(p, hmac, sizeof(hmac));
if (ascii_state) {
*ascii_state = rad_malloc(2 + /* "0x" */
strlen(challenge) * 2 + /* challenge */
+ 8 + /* flags */
8 + /* time */
sizeof(hmac) * 2 + /* hmac */
1); /* '\0' */
}
}
- /* Add the time. */
+ /* Add the flags and time. */
{
des_cblock cblock;
- (void) memcpy(cblock, &when, 4);
+ (void) memcpy(cblock, &flags, 4);
+ (void) memcpy(&cblock[4], &when, 4);
x99_keyblock_to_string(p, cblock, x99_hex_conversion);
}
- p += 8; /* discard lower 8 bytes, which are junk */
+ p += 16;
/* Add the hmac. */
x99_keyblock_to_string(p, hmac, x99_hex_conversion);