📄 x99_rlm.c
字号:
} /* * Mark the packet as an Access-Challenge packet. * The server will take care of sending it to the user. */ request->reply->code = PW_ACCESS_CHALLENGE; DEBUG("rlm_x99_token: Sending Access-Challenge."); /* TODO: support config-specific auth-type */ if (!auth_type_found) pairadd(&request->config_items, pairmake("Auth-Type", "x99_token", T_OP_EQ)); return RLM_MODULE_HANDLED;}/* Verify the response entered by the user. */static intx99_token_authenticate(void *instance, REQUEST *request){ x99_token_t *inst = (x99_token_t *) instance; x99_user_info_t user_info; char *username; int i, pwattr, rc, fc; int32_t sflags = 0; /* flags from state */ time_t last_auth; /* time of last authentication */ unsigned auth_pos = 0; /* window position of last authentication */ char challenge[MAX_CHALLENGE_LEN + 1]; char e_response[9]; /* expected response */ VALUE_PAIR *add_vps = NULL; /* User-Name attribute required. */ if (!request->username) { x99_log(X99_LOG_AUTH, "auth: Attribute \"User-Name\" required for authentication."); return RLM_MODULE_INVALID; } username = request->username->strvalue; if ((pwattr = x99_pw_present(request)) == 0) { x99_log(X99_LOG_AUTH, "auth: Attribute \"User-Password\" " "or equivalent required for authentication."); return RLM_MODULE_INVALID; } /* Add a message to the auth log. */ pairadd(&request->packet->vps, pairmake("Module-Failure-Message", X99_MODULE_NAME, T_OP_EQ)); pairadd(&request->packet->vps, pairmake("Module-Success-Message", X99_MODULE_NAME, T_OP_EQ)); /* Look up the user's info. */ if (x99_get_user_info(inst->pwdfile, username, &user_info) != 0) {#if 0 /* x99_get_user_info() logs a more useful message, this is noisy. */ x99_log(X99_LOG_AUTH, "auth: error reading user [%s] info", username);#endif return RLM_MODULE_REJECT; } /* Retrieve the challenge (from State attribute), unless (fast_sync). */ if (pairfind(request->config_items, PW_X99_FAST) == NULL) { VALUE_PAIR *vp; unsigned char *state; time_t then; if ((vp = pairfind(request->packet->vps, PW_STATE)) != NULL) { int e_length = inst->chal_len; /* Extend expected length if state should have been protected. */ if (user_info.card_id & X99_CF_AM) e_length += 4 + 4 + 16; /* sflags + time + hmac */ if (vp->length != e_length) { x99_log(X99_LOG_AUTH, "auth: bad state for [%s]: length", username); return RLM_MODULE_INVALID; } /* Fast path if we didn't protect the state. */ if (!(user_info.card_id & X99_CF_AM)) goto good_state; /* Verify the state. */ (void) memset(challenge, 0, sizeof(challenge)); (void) memcpy(challenge, vp->strvalue, inst->chal_len); (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){ x99_log(X99_LOG_ERR, "auth: failed to generate state"); return RLM_MODULE_FAIL; } if (memcmp(state, vp->strvalue, vp->length)) { x99_log(X99_LOG_AUTH, "auth: bad state for [%s]: hmac", username); free(state); return RLM_MODULE_REJECT; } free(state); /* State is valid, but check expiry. */ then = ntohl(then); if (time(NULL) - then > inst->chal_delay) { x99_log(X99_LOG_AUTH, "auth: bad state for [%s]: expired", username); return RLM_MODULE_REJECT; } } else { /* This should only happen if the authorize code didn't run. */ x99_log(X99_LOG_ERR, "auth: bad state for [%s]: missing " "(is x99_token listed in radiusd.conf's authorize stanza?)", username); return RLM_MODULE_FAIL; } } /* if (!fast_sync) */good_state: /* State is good! */ /* Get the time of the last authentication. */ if (x99_get_last_auth(inst->syncdir, username, &last_auth) != 0) { x99_log(X99_LOG_ERR, "auth: unable to get last auth time for [%s]", username); return RLM_MODULE_FAIL; } /* Check failure count. */ fc = x99_check_failcount(username, inst); if ((fc == FAIL_ERR) || (fc == FAIL_HARD)) return RLM_MODULE_USERLOCK; /* Some checks for ewindow2_size logic. */ if (fc == FAIL_SOFT) { if (!inst->ewindow2_size) /* no auto-resync */ return RLM_MODULE_USERLOCK; if (!pairfind(request->config_items, PW_X99_FAST)) { /* * ewindow2 softfail override requires two consecutive sync * responses. Fail, and record that this was async. */ if (x99_set_last_auth_pos(inst->syncdir, username, 0)) x99_log(X99_LOG_ERR, "auth: failed to record last auth pos for [%s]", username); return RLM_MODULE_USERLOCK; } /* We're now in "ewindow2 mode" ... subsequent logic must test fc */ goto sync_response; } /* * Don't bother to check async response if either * - the card doesn't support it, or * - we're doing fast_sync. */ if (!(user_info.card_id & X99_CF_AM) || pairfind(request->config_items, PW_X99_FAST)) { goto sync_response; } /* Perform any site-specific transforms of the challenge. */ if (x99_challenge_transform(username, challenge) != 0) { x99_log(X99_LOG_ERR, "auth: challenge transform failed for [%s]", username); return RLM_MODULE_FAIL; /* NB: last_auth, failcount not updated. */ } /* Calculate and test the async response. */ if (x99_response(challenge, e_response, user_info.card_id, user_info.keyblock) != 0) { x99_log(X99_LOG_ERR, "auth: unable to calculate async response for [%s], " "to challenge %s", username, challenge); return RLM_MODULE_FAIL; /* NB: last_auth, failcount not updated. */ } DEBUG("rlm_x99_token: auth: [%s], async challenge %s, " "expecting response %s", username, challenge, e_response); if (x99_pw_valid(request, inst, pwattr, e_response, &add_vps)) { /* Password matches. Is this allowed? */ if (!inst->allow_async) { x99_log(X99_LOG_AUTH, "auth: bad async for [%s]: disallowed by config", username); rc = RLM_MODULE_REJECT; goto return_pw_valid; /* NB: last_auth, failcount not updated. */ } /* Make sure this isn't a replay by forcing a delay. */ if (time(NULL) - last_auth < inst->chal_delay) { x99_log(X99_LOG_AUTH, "auth: bad async for [%s]: too soon", username); rc = RLM_MODULE_REJECT; goto return_pw_valid; /* NB: last_auth, failcount not updated. */ } if (user_info.card_id & X99_CF_SM) { x99_log(X99_LOG_INFO, "auth: [%s] authenticated in async mode", username); } rc = RLM_MODULE_OK; 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->chal_delay). */ if (x99_get_sync_data(inst->syncdir, username, user_info.card_id, 1, 0, challenge, user_info.keyblock) != 0) { x99_log(X99_LOG_ERR, "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) { x99_log(X99_LOG_ERR, "auth: unable to set sync data for [%s] (for resync)", username); rc = RLM_MODULE_FAIL; } } else { /* Just update failcount, last_auth, auth_pos. */ if (x99_reset_failcount(inst->syncdir, username) != 0) { x99_log(X99_LOG_ERR, "auth: unable to reset failcount for [%s]", username); rc = RLM_MODULE_FAIL; } } goto return_pw_valid; } /* if (user authenticated async) */sync_response: /* * Calculate and test sync responses in the window. * Note that we always accept a sync response, even * if a challenge or resync was explicitly requested. */ if ((user_info.card_id & X99_CF_SM) && inst->allow_sync) { int start = 0, end = inst->ewindow_size; /* * Tweak start,end for ewindow2_size logic. * * If user is in softfail, and their last response was correct, * start at that response. We used to start at the NEXT * response (the one that will let them in), but the MS Windows * "incorrect password" dialog is confusing and users end up * reusing the same password twice; this has the effect that * ewindow2 doesn't work at all for them (they enter 1,1,2,2,3,3; * the 1,2 or 2,3 wouldn't work since the repeat would reset the * sequence). * * The response sequence 6,5,6 won't work (but 6,5,6,7 will). * That's OK; we want to optimize for the 6,7 sequence. The user * can't generate the 6,5 sequence from the token anyway. * * If the user starts at the left edge of the window (0,1,2) they * have to enter three responses. We don't accept the zeroeth * response as part of the sequence because we can't differentiate * between a correct entry of the zeroeth response (which stores * 0 as the last_auth_pos) and an incorrect entry (which "resets" * the last_auth_pos to 0). */ if (fc == FAIL_SOFT) { start = x99_get_last_auth_pos(inst->syncdir, username); end = inst->ewindow2_size; } challenge[0] = '\0'; /* initialize for x99_get_sync_data() */ for (i = start; i <= end; ++i) { /* Get sync challenge and key. */ if (x99_get_sync_data(inst->syncdir, username, user_info.card_id, i, 0, challenge, user_info.keyblock) != 0) { x99_log(X99_LOG_ERR, "auth: unable to get sync data e:%d t:%d for [%s]", i, 0, username); rc = RLM_MODULE_FAIL; goto return_pw_valid; /* NB: last_auth, failcount not updated. */ } /* Calculate sync response. */ if (x99_response(challenge, e_response, user_info.card_id, user_info.keyblock) != 0) { x99_log(X99_LOG_ERR, "auth: unable to calculate sync response " "e:%d t:%d for [%s], to challenge %s", i, 0, username, challenge); rc = RLM_MODULE_FAIL; goto return_pw_valid; /* NB: last_auth, failcount not updated. */ } DEBUG("rlm_x99_token: auth: [%s], sync challenge %d %s, " "expecting response %s", username, i, challenge, e_response); /* Test user-supplied passcode. */ if (x99_pw_valid(request, inst, pwattr, e_response, &add_vps)) { /* * Yay! User authenticated via sync mode. Resync. */ rc = RLM_MODULE_OK; /* * ewindow2_size logic */ if (fc == FAIL_SOFT) { /* User must authenticate twice in a row, ... */ if (start && (i == start + 1) && /* ... within ewindow2_delay seconds. */ (time(NULL) - last_auth < inst->ewindow2_delay)) { /* This is the 2nd of two consecutive responses. */ x99_log(X99_LOG_AUTH, "auth: ewindow2 softfail override for [%s] at " "window position %d", username, i); } else { /* correct, but not consecutive or not soon enough */ DEBUG("rlm_x99_token: auth: [%s] ewindow2 candidate " "at position %i", username, i); auth_pos = i; rc = RLM_MODULE_REJECT; break; } } /* * The same failure/replay issue applies here as in the * identical code block in the async section above, with * the additional problem that a response can be reused * indefinitely! (until the sync data is updated) */ if (x99_get_sync_data(inst->syncdir,username,user_info.card_id, 1, 0, challenge,user_info.keyblock) != 0){ x99_log(X99_LOG_ERR, "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) { x99_log(X99_LOG_ERR, "auth: unable to set sync data for [%s] " "(for resync)", username); rc = RLM_MODULE_FAIL; } goto return_pw_valid; } /* if (passcode is valid) */ } /* for (each slot in the window) */ } /* if (card is in sync mode and sync mode allowed) */ /* Both async and sync mode failed. */ if ((fc != FAIL_SOFT) /* !already incremented by x99_check_failcount() */ && (x99_incr_failcount(inst->syncdir, username) != 0)) { x99_log(X99_LOG_ERR, "auth: unable to increment failure count for user [%s]", username); } if (x99_set_last_auth_pos(inst->syncdir, username, auth_pos)) { x99_log(X99_LOG_ERR, "auth: unable to set ewindow2 position for user [%s]", username); } return RLM_MODULE_REJECT; /* Must exit here after a successful return from x99_pw_valid(). */return_pw_valid: /* Handle any vps returned from x99_pw_valid(). */ if (rc == RLM_MODULE_OK) { pairadd(&request->reply->vps, add_vps); } else { pairfree(&add_vps); } return rc;}/* per-instance destruction */static intx99_token_detach(void *instance){ x99_token_t *inst = (x99_token_t *) instance; free(inst->pwdfile); free(inst->syncdir); free(inst->chal_prompt); free(inst->chal_req); free(inst->resync_req); free(instance); return 0;}/* per-module destruction */static intx99_token_destroy(void){ (void) memset(hmac_key, 0, sizeof(hmac_key)); (void) close(rnd_fd); return 0;}/* * If the module needs to temporarily modify it's instantiation * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE. * The server will then take care of ensuring that the module * is single-threaded. */module_t rlm_x99_token = { "x99_token", RLM_TYPE_THREAD_SAFE, /* type */ x99_token_init, /* initialization */ x99_token_instantiate, /* instantiation */ { x99_token_authenticate, /* authentication */ x99_token_authorize, /* authorization */ NULL, /* preaccounting */ NULL, /* accounting */ NULL, /* checksimul */ NULL, /* pre-proxy */ NULL, /* post-proxy */ NULL /* post-auth */ }, x99_token_detach, /* detach */ x99_token_destroy, /* destroy */};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -