3
* $Id: x99_rlm.c,v 1.39.2.1 2004/11/03 17:28:25 aland Exp $
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: x99_rlm.c,v 1.39.2.1 2004/11/03 17:28:25 aland Exp $";
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 */