5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 * Copyright 2000,2001,2002 The FreeRADIUS server project
20 * Copyright 2001,2002 Google, Inc.
24 * STRONG WARNING SECTION:
26 * ANSI X9.9 has been withdrawn as a standard, due to the weakness of DES.
27 * An attacker can learn the token's secret by observing two
28 * challenge/response pairs. See ANSI document X9 TG-24-1999
29 * <URL:http://www.x9.org/TG24_1999.pdf>.
31 * Please read the accompanying docs.
35 * TODO: support setting multiple auth-types in authorize()
36 * TODO: support soft PIN? ???
37 * TODO: support other than ILP32 (for State)
43 #include <sys/types.h>
48 #include <netinet/in.h> /* htonl() */
56 static const char rcsid[] = "$Id$";
59 static int rnd_fd; /* fd for random device */
60 static unsigned char hmac_key[16]; /* to protect State attribute */
62 /* A mapping of configuration file names to internal variables. */
63 static CONF_PARSER module_config[] = {
64 { "pwdfile", PW_TYPE_STRING_PTR, offsetof(x99_token_t, pwdfile),
66 { "syncdir", PW_TYPE_STRING_PTR, offsetof(x99_token_t, syncdir),
68 { "challenge_prompt", PW_TYPE_STRING_PTR, offsetof(x99_token_t,chal_prompt),
69 NULL, CHALLENGE_PROMPT },
70 { "challenge_length", PW_TYPE_INTEGER, offsetof(x99_token_t, chal_len),
72 { "challenge_delay", PW_TYPE_INTEGER, offsetof(x99_token_t, chal_delay),
74 { "softfail", PW_TYPE_INTEGER, offsetof(x99_token_t, softfail),
76 { "hardfail", PW_TYPE_INTEGER, offsetof(x99_token_t, hardfail),
78 { "allow_sync", PW_TYPE_BOOLEAN, offsetof(x99_token_t, allow_sync),
80 { "fast_sync", PW_TYPE_BOOLEAN, offsetof(x99_token_t, fast_sync),
82 { "allow_async", PW_TYPE_BOOLEAN, offsetof(x99_token_t, allow_async),
84 { "challenge_req", PW_TYPE_STRING_PTR, offsetof(x99_token_t, chal_req),
85 NULL, CHALLENGE_REQ },
86 { "resync_req", PW_TYPE_STRING_PTR, offsetof(x99_token_t, resync_req),
88 { "ewindow_size", PW_TYPE_INTEGER, offsetof(x99_token_t, ewindow_size),
90 { "ewindow2_size", PW_TYPE_INTEGER, offsetof(x99_token_t, ewindow2_size),
92 { "ewindow2_delay", PW_TYPE_INTEGER, offsetof(x99_token_t, ewindow2_delay),
94 { "mschapv2_mppe", PW_TYPE_INTEGER,
95 offsetof(x99_token_t, mschapv2_mppe_policy), NULL, "2" },
96 { "mschapv2_mppe_bits", PW_TYPE_INTEGER,
97 offsetof(x99_token_t, mschapv2_mppe_types), NULL, "2" },
98 { "mschap_mppe", PW_TYPE_INTEGER,
99 offsetof(x99_token_t, mschap_mppe_policy), NULL, "2" },
100 { "mschap_mppe_bits", PW_TYPE_INTEGER,
101 offsetof(x99_token_t, mschap_mppe_types), NULL, "2" },
103 { "twindow_min", PW_TYPE_INTEGER, offsetof(x99_token_t, twindow_min),
105 { "twindow_max", PW_TYPE_INTEGER, offsetof(x99_token_t, twindow_max),
109 { NULL, -1, 0, NULL, NULL } /* end the list */
113 /* per-module initialization */
117 if ((rnd_fd = open(DEVURANDOM, O_RDONLY)) == -1) {
118 x99_log(X99_LOG_ERR, "init: error opening %s: %s", DEVURANDOM,
123 /* Generate a random key, used to protect the State attribute. */
124 if (x99_get_random(rnd_fd, hmac_key, sizeof(hmac_key)) == -1) {
125 x99_log(X99_LOG_ERR, "init: failed to obtain random data for hmac_key");
129 /* Initialize the passcode encoding/checking functions. */
136 /* per-instance initialization */
138 x99_token_instantiate(CONF_SECTION *conf, void **instance)
144 /* Set up a storage area for instance data. */
145 data = rad_malloc(sizeof(*data));
148 memset(data, 0, sizeof(*data));
150 /* If the configuration parameters can't be parsed, then fail. */
151 if (cf_section_parse(conf, data, module_config) < 0) {
156 /* Verify ranges for those vars that are limited. */
157 if ((data->chal_len < 5) || (data->chal_len > MAX_CHALLENGE_LEN)) {
160 "invalid challenge_length, range 5-%d, using default of 6",
165 /* Enforce a single "%" sequence, which must be "%s" */
166 p = strchr(data->chal_prompt, '%');
167 if ((p == NULL) || (p != strrchr(data->chal_prompt, '%')) ||
169 free(data->chal_prompt);
170 data->chal_prompt = strdup(CHALLENGE_PROMPT);
172 "invalid challenge_prompt, using default of \"%s\"",
176 if (data->softfail < 0) {
178 x99_log(X99_LOG_ERR, "softfail must be at least 1 "
179 "(or 0 == infinite), using default of 5");
182 if (data->hardfail < 0) {
184 x99_log(X99_LOG_ERR, "hardfail must be at least 1 "
185 "(or 0 == infinite), using default of 0");
188 if (data->fast_sync && !data->allow_sync) {
190 x99_log(X99_LOG_INFO,
191 "fast_sync is yes, but allow_sync is no; disabling fast_sync");
194 if (!data->allow_sync && !data->allow_async) {
196 "at least one of {allow_async, allow_sync} must be set");
201 if ((data->ewindow_size > MAX_EWINDOW_SIZE) || (data->ewindow_size < 0)) {
202 data->ewindow_size = 0;
203 x99_log(X99_LOG_ERR, "max ewindow_size is %d, using default of 0",
207 if (data->ewindow2_size && (data->ewindow2_size < data->ewindow_size)) {
208 data->ewindow2_size = 0;
209 x99_log(X99_LOG_ERR, "ewindow2_size must be at least as large as "
210 "ewindow_size, using default of 0");
213 if (data->ewindow2_size && !data->ewindow2_delay) {
214 data->ewindow2_size = 0;
215 x99_log(X99_LOG_ERR, "ewindow2_size is non-zero, "
216 "but ewindow2_delay is zero; disabling ewindow2");
219 if ((data->mschapv2_mppe_policy > 2) || (data->mschapv2_mppe_policy < 0)) {
220 data->mschapv2_mppe_policy = 2;
222 "invalid value for mschapv2_mppe, using default of 2");
225 if ((data->mschapv2_mppe_types > 2) || (data->mschapv2_mppe_types < 0)) {
226 data->mschapv2_mppe_types = 2;
228 "invalid value for mschapv2_mppe_bits, using default of 2");
231 if ((data->mschap_mppe_policy > 2) || (data->mschap_mppe_policy < 0)) {
232 data->mschap_mppe_policy = 2;
234 "invalid value for mschap_mppe, using default of 2");
237 if (data->mschap_mppe_types != 2) {
238 data->mschap_mppe_types = 2;
240 "invalid value for mschap_mppe_bits, using default of 2");
244 if (data->twindow_max - data->twindow_min > MAX_TWINDOW_SIZE) {
245 data->twindow_min = data->twindow_max = 0;
246 x99_log(X99_LOG_ERR, "max time window size is %d, using default of 0",
249 if ((data->twindow_min > 0) || (data->twindow_max < 0) ||
250 (data->twindow_max < data->twindow_min)) {
251 data->twindow_min = data->twindow_max = 0;
253 "invalid values for time window, using default of 0");
257 if (stat(data->syncdir, &st) != 0) {
258 x99_log(X99_LOG_ERR, "syncdir %s error: %s",
259 data->syncdir, strerror(errno));
263 if (st.st_mode != (S_IFDIR|S_IRWXU)) {
264 x99_log(X99_LOG_ERR, "syncdir %s has loose permissions", data->syncdir);
269 /* Set the instance name (for use with authorize()) */
270 data->name = cf_section_name2(conf);
272 data->name = cf_section_name1(conf);
274 x99_log(X99_LOG_ERR, "no instance name (this can't happen)");
284 /* Generate a challenge to be presented to the user. */
286 x99_token_authorize(void *instance, REQUEST *request)
288 x99_token_t *inst = (x99_token_t *) instance;
290 char challenge[MAX_CHALLENGE_LEN + 1]; /* +1 for '\0' terminator */
294 x99_user_info_t user_info;
295 int user_found, auth_type_found;
297 int32_t sflags = 0; /* flags for state */
300 /* Early exit if Auth-Type != inst->name */
302 if ((vp = pairfind(request->config_items, PW_AUTHTYPE)) != NULL) {
304 if (strcmp(vp->strvalue, inst->name)) {
305 return RLM_MODULE_NOOP;
309 /* The State attribute will be present if this is a response. */
310 if (pairfind(request->packet->vps, PW_STATE) != NULL) {
311 DEBUG("rlm_x99_token: autz: Found response to access challenge");
312 return RLM_MODULE_OK;
315 /* User-Name attribute required. */
316 if (!request->username) {
317 x99_log(X99_LOG_AUTH,
318 "autz: Attribute \"User-Name\" required for authentication.");
319 return RLM_MODULE_INVALID;
322 if ((pwattr = x99_pw_present(request)) == 0) {
323 x99_log(X99_LOG_AUTH, "autz: Attribute \"User-Password\" "
324 "or equivalent required for authentication.");
325 return RLM_MODULE_INVALID;
328 /* Look up the user's info. */
330 if ((rc = x99_get_user_info(inst->pwdfile, request->username->strvalue,
331 &user_info)) == -2) {
333 /* x99_get_user_info() logs a more useful message, this is noisy. */
334 x99_log(X99_LOG_ERR, "autz: error reading user [%s] info",
335 request->username->strvalue);
337 return RLM_MODULE_FAIL;
340 x99_log(X99_LOG_AUTH, "autz: user [%s] not found in %s",
341 request->username->strvalue, inst->pwdfile);
342 memset(&user_info, 0, sizeof(user_info)); /* X99_CF_NONE */
346 /* fast_sync mode (challenge only if requested) */
347 if (inst->fast_sync &&
348 ((user_info.card_id & X99_CF_SM) || !user_found)) {
350 if ((x99_pw_valid(request, inst, pwattr, inst->resync_req, NULL) &&
351 /* Set a bit indicating resync */ (sflags |= htonl(1))) ||
352 x99_pw_valid(request, inst, pwattr, inst->chal_req, NULL)) {
354 * Generate a challenge if requested. We don't test for card
355 * support [for async] because it's tricky for unknown users.
356 * Some configurations would have a problem where known users
357 * cannot request a challenge, but unknown users can. This
358 * reveals information. The easiest fix seems to be to always
359 * hand out a challenge on request.
360 * We also don't test if the server allows async mode, this
361 * would also reveal information.
363 DEBUG("rlm_x99_token: autz: fast_sync challenge requested");
368 * Otherwise, this is the token sync response. Signal
369 * the authenticate code to ignore State. We don't need
370 * to set a value, /existence/ of the vp is the signal.
372 if ((vp = paircreate(PW_X99_FAST, PW_TYPE_INTEGER)) == NULL) {
373 x99_log(X99_LOG_CRIT, "autz: no memory");
374 return RLM_MODULE_FAIL;
376 pairadd(&request->config_items, vp);
377 DEBUG("rlm_x99_token: autz: using fast_sync");
379 if (!auth_type_found)
380 pairadd(&request->config_items,
381 pairmake("Auth-Type", "x99_token", T_OP_EQ));
382 return RLM_MODULE_OK;
385 } /* if (fast_sync && card supports sync mode) */
388 /* Set the resync bit by default if the user can't request it. */
389 if (!inst->fast_sync)
392 /* Generate a random challenge. */
393 if (x99_get_challenge(rnd_fd, challenge, inst->chal_len) == -1) {
394 x99_log(X99_LOG_ERR, "autz: failed to obtain random challenge");
395 return RLM_MODULE_FAIL;
399 * Create the State attribute, which will be returned to us along with
400 * the response. We will need this to verify the response. Create
401 * a strong state if the user will be able use this with their token.
402 * Otherwise, we discard it anyway, so don't "waste" time with hmac.
403 * We also don't do the hmac if the user wasn't found (mask won't match).
404 * We always create at least a trivial state, so x99_token_authorize()
405 * can easily pass on to x99_token_authenticate().
407 if (user_info.card_id & X99_CF_AM) {
408 time_t now = time(NULL);
410 if (sizeof(now) != 4 || sizeof(long) != 4) {
411 x99_log(X99_LOG_ERR, "autz: only ILP32 arch is supported");
412 return RLM_MODULE_FAIL;
416 if (x99_gen_state(&state, NULL, challenge, sflags, now, hmac_key) != 0){
417 x99_log(X99_LOG_ERR, "autz: failed to generate state");
418 return RLM_MODULE_FAIL;
421 /* x2 b/c pairmake() string->octet needs even num of digits */
422 state = rad_malloc(3 + inst->chal_len * 2);
423 (void) sprintf(state, "0x%s%s", challenge, challenge);
425 pairadd(&request->reply->vps, pairmake("State", state, T_OP_EQ));
428 /* Add the challenge to the reply. */
430 char *u_challenge; /* challenge with addt'l presentation text */
432 u_challenge = rad_malloc(strlen(inst->chal_prompt)+MAX_CHALLENGE_LEN+1);
433 (void) sprintf(u_challenge, inst->chal_prompt, challenge);
434 pairadd(&request->reply->vps,
435 pairmake("Reply-Message", u_challenge, T_OP_EQ));
440 * Mark the packet as an Access-Challenge packet.
441 * The server will take care of sending it to the user.
443 request->reply->code = PW_ACCESS_CHALLENGE;
444 DEBUG("rlm_x99_token: Sending Access-Challenge.");
446 /* TODO: support config-specific auth-type */
447 if (!auth_type_found)
448 pairadd(&request->config_items,
449 pairmake("Auth-Type", "x99_token", T_OP_EQ));
450 return RLM_MODULE_HANDLED;
454 /* Verify the response entered by the user. */
456 x99_token_authenticate(void *instance, REQUEST *request)
458 x99_token_t *inst = (x99_token_t *) instance;
460 x99_user_info_t user_info;
462 int i, pwattr, rc, fc;
463 int32_t sflags = 0; /* flags from state */
464 time_t last_auth; /* time of last authentication */
465 unsigned auth_pos = 0; /* window position of last authentication */
467 char challenge[MAX_CHALLENGE_LEN + 1];
468 char e_response[9]; /* expected response */
469 VALUE_PAIR *add_vps = NULL;
471 /* User-Name attribute required. */
472 if (!request->username) {
473 x99_log(X99_LOG_AUTH,
474 "auth: Attribute \"User-Name\" required for authentication.");
475 return RLM_MODULE_INVALID;
477 username = request->username->strvalue;
479 if ((pwattr = x99_pw_present(request)) == 0) {
480 x99_log(X99_LOG_AUTH, "auth: Attribute \"User-Password\" "
481 "or equivalent required for authentication.");
482 return RLM_MODULE_INVALID;
485 /* Add a message to the auth log. */
486 pairadd(&request->packet->vps, pairmake("Module-Failure-Message",
487 X99_MODULE_NAME, T_OP_EQ));
488 pairadd(&request->packet->vps, pairmake("Module-Success-Message",
489 X99_MODULE_NAME, T_OP_EQ));
491 /* Look up the user's info. */
492 if (x99_get_user_info(inst->pwdfile, username, &user_info) != 0) {
494 /* x99_get_user_info() logs a more useful message, this is noisy. */
495 x99_log(X99_LOG_AUTH, "auth: error reading user [%s] info", username);
497 return RLM_MODULE_REJECT;
500 /* Retrieve the challenge (from State attribute), unless (fast_sync). */
501 if (pairfind(request->config_items, PW_X99_FAST) == NULL) {
503 unsigned char *state;
506 if ((vp = pairfind(request->packet->vps, PW_STATE)) != NULL) {
507 int e_length = inst->chal_len;
509 /* Extend expected length if state should have been protected. */
510 if (user_info.card_id & X99_CF_AM)
511 e_length += 4 + 4 + 16; /* sflags + time + hmac */
513 if (vp->length != e_length) {
514 x99_log(X99_LOG_AUTH,
515 "auth: bad state for [%s]: length", username);
516 return RLM_MODULE_INVALID;
519 /* Fast path if we didn't protect the state. */
520 if (!(user_info.card_id & X99_CF_AM))
523 /* Verify the state. */
524 (void) memset(challenge, 0, sizeof(challenge));
525 (void) memcpy(challenge, vp->strvalue, inst->chal_len);
526 (void) memcpy(&sflags, vp->strvalue + inst->chal_len, 4);
527 (void) memcpy(&then, vp->strvalue + inst->chal_len + 4, 4);
528 if (x99_gen_state(NULL,&state,challenge,sflags,then,hmac_key) != 0){
529 x99_log(X99_LOG_ERR, "auth: failed to generate state");
530 return RLM_MODULE_FAIL;
532 if (memcmp(state, vp->strvalue, vp->length)) {
533 x99_log(X99_LOG_AUTH,
534 "auth: bad state for [%s]: hmac", username);
536 return RLM_MODULE_REJECT;
540 /* State is valid, but check expiry. */
542 if (time(NULL) - then > inst->chal_delay) {
543 x99_log(X99_LOG_AUTH,
544 "auth: bad state for [%s]: expired", username);
545 return RLM_MODULE_REJECT;
548 /* This should only happen if the authorize code didn't run. */
549 x99_log(X99_LOG_ERR, "auth: bad state for [%s]: missing "
550 "(is x99_token listed in radiusd.conf's authorize stanza?)",
552 return RLM_MODULE_FAIL;
554 } /* if (!fast_sync) */
559 /* Get the time of the last authentication. */
560 if (x99_get_last_auth(inst->syncdir, username, &last_auth) != 0) {
562 "auth: unable to get last auth time for [%s]", username);
563 return RLM_MODULE_FAIL;
566 /* Check failure count. */
567 fc = x99_check_failcount(username, inst);
568 if ((fc == FAIL_ERR) || (fc == FAIL_HARD))
569 return RLM_MODULE_USERLOCK;
571 /* Some checks for ewindow2_size logic. */
572 if (fc == FAIL_SOFT) {
573 if (!inst->ewindow2_size) /* no auto-resync */
574 return RLM_MODULE_USERLOCK;
576 if (!pairfind(request->config_items, PW_X99_FAST)) {
578 * ewindow2 softfail override requires two consecutive sync
579 * responses. Fail, and record that this was async.
581 if (x99_set_last_auth_pos(inst->syncdir, username, 0))
583 "auth: failed to record last auth pos for [%s]",
585 return RLM_MODULE_USERLOCK;
588 /* We're now in "ewindow2 mode" ... subsequent logic must test fc */
593 * Don't bother to check async response if either
594 * - the card doesn't support it, or
595 * - we're doing fast_sync.
597 if (!(user_info.card_id & X99_CF_AM) ||
598 pairfind(request->config_items, PW_X99_FAST)) {
602 /* Perform any site-specific transforms of the challenge. */
603 if (x99_challenge_transform(username, challenge) != 0) {
605 "auth: challenge transform failed for [%s]", username);
606 return RLM_MODULE_FAIL;
607 /* NB: last_auth, failcount not updated. */
610 /* Calculate and test the async response. */
611 if (x99_response(challenge, e_response, user_info.card_id,
612 user_info.keyblock) != 0) {
614 "auth: unable to calculate async response for [%s], "
615 "to challenge %s", username, challenge);
616 return RLM_MODULE_FAIL;
617 /* NB: last_auth, failcount not updated. */
619 DEBUG("rlm_x99_token: auth: [%s], async challenge %s, "
620 "expecting response %s", username, challenge, e_response);
622 if (x99_pw_valid(request, inst, pwattr, e_response, &add_vps)) {
623 /* Password matches. Is this allowed? */
624 if (!inst->allow_async) {
625 x99_log(X99_LOG_AUTH,
626 "auth: bad async for [%s]: disallowed by config", username);
627 rc = RLM_MODULE_REJECT;
628 goto return_pw_valid;
629 /* NB: last_auth, failcount not updated. */
632 /* Make sure this isn't a replay by forcing a delay. */
633 if (time(NULL) - last_auth < inst->chal_delay) {
634 x99_log(X99_LOG_AUTH,
635 "auth: bad async for [%s]: too soon", username);
636 rc = RLM_MODULE_REJECT;
637 goto return_pw_valid;
638 /* NB: last_auth, failcount not updated. */
641 if (user_info.card_id & X99_CF_SM) {
642 x99_log(X99_LOG_INFO,
643 "auth: [%s] authenticated in async mode", username);
647 if (ntohl(sflags) & 1) {
649 * Resync the card. The sync data doesn't mean anything for
650 * async-only cards, but we want the side effects of resetting
651 * the failcount and the last auth time. We "fail-out" if we
652 * can't do this, because if we can't update the last auth time,
653 * we will be open to replay attacks over the lifetime of the
654 * State attribute (inst->chal_delay).
656 if (x99_get_sync_data(inst->syncdir, username, user_info.card_id,
657 1, 0, challenge, user_info.keyblock) != 0) {
658 x99_log(X99_LOG_ERR, "auth: unable to get sync data "
659 "e:%d t:%d for [%s] (for resync)", 1, 0, username);
660 rc = RLM_MODULE_FAIL;
661 } else if (x99_set_sync_data(inst->syncdir, username, challenge,
662 user_info.keyblock) != 0) {
664 "auth: unable to set sync data for [%s] (for resync)",
666 rc = RLM_MODULE_FAIL;
669 /* Just update failcount, last_auth, auth_pos. */
670 if (x99_reset_failcount(inst->syncdir, username) != 0) {
672 "auth: unable to reset failcount for [%s]", username);
673 rc = RLM_MODULE_FAIL;
676 goto return_pw_valid;
677 } /* if (user authenticated async) */
681 * Calculate and test sync responses in the window.
682 * Note that we always accept a sync response, even
683 * if a challenge or resync was explicitly requested.
685 if ((user_info.card_id & X99_CF_SM) && inst->allow_sync) {
686 int start = 0, end = inst->ewindow_size;
689 * Tweak start,end for ewindow2_size logic.
691 * If user is in softfail, and their last response was correct,
692 * start at that response. We used to start at the NEXT
693 * response (the one that will let them in), but the MS Windows
694 * "incorrect password" dialog is confusing and users end up
695 * reusing the same password twice; this has the effect that
696 * ewindow2 doesn't work at all for them (they enter 1,1,2,2,3,3;
697 * the 1,2 or 2,3 wouldn't work since the repeat would reset the
700 * The response sequence 6,5,6 won't work (but 6,5,6,7 will).
701 * That's OK; we want to optimize for the 6,7 sequence. The user
702 * can't generate the 6,5 sequence from the token anyway.
704 * If the user starts at the left edge of the window (0,1,2) they
705 * have to enter three responses. We don't accept the zeroeth
706 * response as part of the sequence because we can't differentiate
707 * between a correct entry of the zeroeth response (which stores
708 * 0 as the last_auth_pos) and an incorrect entry (which "resets"
709 * the last_auth_pos to 0).
711 if (fc == FAIL_SOFT) {
712 start = x99_get_last_auth_pos(inst->syncdir, username);
713 end = inst->ewindow2_size;
716 challenge[0] = '\0'; /* initialize for x99_get_sync_data() */
717 for (i = start; i <= end; ++i) {
718 /* Get sync challenge and key. */
719 if (x99_get_sync_data(inst->syncdir, username, user_info.card_id,
720 i, 0, challenge, user_info.keyblock) != 0) {
722 "auth: unable to get sync data e:%d t:%d for [%s]",
724 rc = RLM_MODULE_FAIL;
725 goto return_pw_valid;
726 /* NB: last_auth, failcount not updated. */
729 /* Calculate sync response. */
730 if (x99_response(challenge, e_response, user_info.card_id,
731 user_info.keyblock) != 0) {
732 x99_log(X99_LOG_ERR, "auth: unable to calculate sync response "
733 "e:%d t:%d for [%s], to challenge %s",
734 i, 0, username, challenge);
735 rc = RLM_MODULE_FAIL;
736 goto return_pw_valid;
737 /* NB: last_auth, failcount not updated. */
739 DEBUG("rlm_x99_token: auth: [%s], sync challenge %d %s, "
740 "expecting response %s", username, i, challenge, e_response);
742 /* Test user-supplied passcode. */
743 if (x99_pw_valid(request, inst, pwattr, e_response, &add_vps)) {
745 * Yay! User authenticated via sync mode. Resync.
750 * ewindow2_size logic
752 if (fc == FAIL_SOFT) {
753 /* User must authenticate twice in a row, ... */
754 if (start && (i == start + 1) &&
755 /* ... within ewindow2_delay seconds. */
756 (time(NULL) - last_auth < inst->ewindow2_delay)) {
757 /* This is the 2nd of two consecutive responses. */
758 x99_log(X99_LOG_AUTH,
759 "auth: ewindow2 softfail override for [%s] at "
760 "window position %d", username, i);
762 /* correct, but not consecutive or not soon enough */
763 DEBUG("rlm_x99_token: auth: [%s] ewindow2 candidate "
764 "at position %i", username, i);
766 rc = RLM_MODULE_REJECT;
772 * The same failure/replay issue applies here as in the
773 * identical code block in the async section above, with
774 * the additional problem that a response can be reused
775 * indefinitely! (until the sync data is updated)
777 if (x99_get_sync_data(inst->syncdir,username,user_info.card_id,
778 1, 0, challenge,user_info.keyblock) != 0){
779 x99_log(X99_LOG_ERR, "auth: unable to get sync data "
780 "e:%d t:%d for [%s] (for resync)", 1, 0, username);
781 rc = RLM_MODULE_FAIL;
782 } else if (x99_set_sync_data(inst->syncdir, username, challenge,
783 user_info.keyblock) != 0) {
785 "auth: unable to set sync data for [%s] "
786 "(for resync)", username);
787 rc = RLM_MODULE_FAIL;
789 goto return_pw_valid;
791 } /* if (passcode is valid) */
792 } /* for (each slot in the window) */
793 } /* if (card is in sync mode and sync mode allowed) */
795 /* Both async and sync mode failed. */
796 if ((fc != FAIL_SOFT) /* !already incremented by x99_check_failcount() */ &&
797 (x99_incr_failcount(inst->syncdir, username) != 0)) {
799 "auth: unable to increment failure count for user [%s]",
802 if (x99_set_last_auth_pos(inst->syncdir, username, auth_pos)) {
804 "auth: unable to set ewindow2 position for user [%s]",
807 return RLM_MODULE_REJECT;
809 /* Must exit here after a successful return from x99_pw_valid(). */
812 /* Handle any vps returned from x99_pw_valid(). */
813 if (rc == RLM_MODULE_OK) {
814 pairadd(&request->reply->vps, add_vps);
822 /* per-instance destruction */
824 x99_token_detach(void *instance)
826 x99_token_t *inst = (x99_token_t *) instance;
830 free(inst->chal_prompt);
831 free(inst->chal_req);
832 free(inst->resync_req);
838 /* per-module destruction */
840 x99_token_destroy(void)
842 (void) memset(hmac_key, 0, sizeof(hmac_key));
843 (void) close(rnd_fd);
848 * If the module needs to temporarily modify it's instantiation
849 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
850 * The server will then take care of ensuring that the module
851 * is single-threaded.
853 module_t rlm_x99_token = {
855 RLM_TYPE_THREAD_SAFE, /* type */
856 x99_token_init, /* initialization */
857 x99_token_instantiate, /* instantiation */
859 x99_token_authenticate, /* authentication */
860 x99_token_authorize, /* authorization */
861 NULL, /* preaccounting */
862 NULL, /* accounting */
863 NULL, /* checksimul */
864 NULL, /* pre-proxy */
865 NULL, /* post-proxy */
868 x99_token_detach, /* detach */
869 x99_token_destroy, /* destroy */