~ubuntu-branches/ubuntu/dapper/freeradius/dapper-updates

« back to all changes in this revision

Viewing changes to src/modules/rlm_x99_token/x99_rlm.c

  • Committer: Bazaar Package Importer
  • Author(s): Paul Hampson
  • Date: 2006-01-15 13:34:13 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20060115133413-92geefww41y7hqi8
Tags: 1.1.0-1
* ReDebianise upstream tarball:
  - Deleted RFCs: 2243 2289 2433 2548 2618 2619 2620 2621 2716 2759 2809 2865
                  2866 2867 2868 2869 2882 2924 3162 3575 3576 3579 3580
                  draft-kamath-pppext-eap-mschapv2-00

* New FreeRADIUS modules marked stable by new upstream release
  - rlm_perl
  - rlm_sqlcounter
  - rlm_sql_log + radsqlrelay
  - rlm_otp (formerly rlm_x99_token, not built as it depends on OpenSSL)

* Remove upstream-integrated patches:
  - 02_EAP-SIM_doesnt_need_openssl
  - 03_X99_is_not_stable
  - 07_manpage_fixups
  - 09_use_crypth_if_we_have_it
  - 10_escape_entire_ldap_string
  - 11_dont_xlat_possibly_bad_usernames_in_bad_accounting_packets
  - 12_dialup_admin_various_fixes

* More dialup-admin fixes from Arve Seljebu
  - Fix redirects in dialup-admin pages on servers with
    register_globals turned off.
    Closes: #333704
  - HTTP form fields will always fail is_int, use in_numeric instead
    Closes: #335149
  - Created 12_more_dialup_admin_various_fixes

* Update to Policy 3.6.2.0
* Upgrade Debhelper support to V5
* Don't install the .in files with the examples
* Prefer libmysqlclient15-dev
  Closes: #343779
* Shared secrets can only be 31 characters long, note this in clients.conf
  - Created 02_document_actual_shared_secret_maximum_length
  Closes: 344606
* Added support for lsb-init functions

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * x99_rlm.c
3
 
 * $Id: x99_rlm.c,v 1.39.2.1 2004/11/03 17:28:25 aland Exp $
4
 
 *
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.
9
 
 *
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.
14
 
 *
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
18
 
 *
19
 
 * Copyright 2000,2001,2002  The FreeRADIUS server project
20
 
 * Copyright 2001,2002  Google, Inc.
21
 
 */
22
 
 
23
 
/*
24
 
 * STRONG WARNING SECTION:
25
 
 *
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>.
30
 
 *
31
 
 * Please read the accompanying docs.
32
 
 */
33
 
 
34
 
/*
35
 
 * TODO: support setting multiple auth-types in authorize()
36
 
 * TODO: support soft PIN? ???
37
 
 * TODO: support other than ILP32 (for State)
38
 
 */
39
 
 
40
 
 
41
 
#include <stdlib.h>
42
 
#include <string.h>
43
 
#include <sys/types.h>
44
 
#include <sys/stat.h>
45
 
#include <unistd.h>
46
 
#include <fcntl.h>
47
 
#include <time.h>
48
 
#include <netinet/in.h> /* htonl() */
49
 
 
50
 
#ifdef FREERADIUS
51
 
#include "radiusd.h"
52
 
#include "modules.h"
53
 
#endif
54
 
#include "x99.h"
55
 
 
56
 
static const char rcsid[] = "$Id: x99_rlm.c,v 1.39.2.1 2004/11/03 17:28:25 aland Exp $";
57
 
 
58
 
/* Global data */
59
 
static int rnd_fd;                      /* fd for random device           */
60
 
static unsigned char hmac_key[16];      /* to protect State attribute     */
61
 
 
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),
65
 
      NULL, PWDFILE },
66
 
    { "syncdir", PW_TYPE_STRING_PTR, offsetof(x99_token_t, syncdir),
67
 
      NULL, 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),
71
 
      NULL, "6" },
72
 
    { "challenge_delay", PW_TYPE_INTEGER, offsetof(x99_token_t, chal_delay),
73
 
      NULL, "30" },
74
 
    { "softfail", PW_TYPE_INTEGER, offsetof(x99_token_t, softfail),
75
 
      NULL, "5" },
76
 
    { "hardfail", PW_TYPE_INTEGER, offsetof(x99_token_t, hardfail),
77
 
      NULL, "0" },
78
 
    { "allow_sync", PW_TYPE_BOOLEAN, offsetof(x99_token_t, allow_sync),
79
 
      NULL, "yes" },
80
 
    { "fast_sync", PW_TYPE_BOOLEAN, offsetof(x99_token_t, fast_sync),
81
 
      NULL, "yes" },
82
 
    { "allow_async", PW_TYPE_BOOLEAN, offsetof(x99_token_t, allow_async),
83
 
      NULL, "no" },
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),
87
 
      NULL, RESYNC_REQ },
88
 
    { "ewindow_size", PW_TYPE_INTEGER, offsetof(x99_token_t, ewindow_size),
89
 
      NULL, "0" },
90
 
    { "ewindow2_size", PW_TYPE_INTEGER, offsetof(x99_token_t, ewindow2_size),
91
 
      NULL, "0" },
92
 
    { "ewindow2_delay", PW_TYPE_INTEGER, offsetof(x99_token_t, ewindow2_delay),
93
 
      NULL, "60" },
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" },
102
 
#if 0
103
 
    { "twindow_min", PW_TYPE_INTEGER, offsetof(x99_token_t, twindow_min),
104
 
      NULL, "0" },
105
 
    { "twindow_max", PW_TYPE_INTEGER, offsetof(x99_token_t, twindow_max),
106
 
      NULL, "0" },
107
 
#endif
108
 
 
109
 
    { NULL, -1, 0, NULL, NULL }         /* end the list */
110
 
};
111
 
 
112
 
 
113
 
/* per-module initialization */
114
 
static int
115
 
x99_token_init(void)
116
 
{
117
 
    if ((rnd_fd = open(DEVURANDOM, O_RDONLY)) == -1) {
118
 
        x99_log(X99_LOG_ERR, "init: error opening %s: %s", DEVURANDOM,
119
 
                strerror(errno));
120
 
        return -1;
121
 
    }
122
 
 
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");
126
 
        return -1;
127
 
    }
128
 
 
129
 
    /* Initialize the passcode encoding/checking functions. */
130
 
    x99_pwe_init();
131
 
 
132
 
    return 0;
133
 
}
134
 
 
135
 
 
136
 
/* per-instance initialization */
137
 
static int
138
 
x99_token_instantiate(CONF_SECTION *conf, void **instance)
139
 
{
140
 
    x99_token_t *data;
141
 
    char *p;
142
 
    struct stat st;
143
 
 
144
 
    /* Set up a storage area for instance data. */
145
 
    data = rad_malloc(sizeof(*data));
146
 
    if (!data)
147
 
        return -1;
148
 
    memset(data, 0, sizeof(*data));
149
 
 
150
 
    /* If the configuration parameters can't be parsed, then fail. */
151
 
    if (cf_section_parse(conf, data, module_config) < 0) {
152
 
        free(data);
153
 
        return -1;
154
 
    }
155
 
 
156
 
    /* Verify ranges for those vars that are limited. */
157
 
    if ((data->chal_len < 5) || (data->chal_len > MAX_CHALLENGE_LEN)) {
158
 
        data->chal_len = 6;
159
 
        x99_log(X99_LOG_ERR,
160
 
                "invalid challenge_length, range 5-%d, using default of 6",
161
 
                MAX_CHALLENGE_LEN);
162
 
 
163
 
    }
164
 
 
165
 
    /* Enforce a single "%" sequence, which must be "%s" */
166
 
    p = strchr(data->chal_prompt, '%');
167
 
    if ((p == NULL) || (p != strrchr(data->chal_prompt, '%')) ||
168
 
        strncmp(p,"%s",2)){
169
 
        free(data->chal_prompt);
170
 
        data->chal_prompt = strdup(CHALLENGE_PROMPT);
171
 
        x99_log(X99_LOG_ERR,
172
 
                "invalid challenge_prompt, using default of \"%s\"",
173
 
                CHALLENGE_PROMPT);
174
 
    }
175
 
 
176
 
    if (data->softfail < 0) {
177
 
        data->softfail = 5;
178
 
        x99_log(X99_LOG_ERR, "softfail must be at least 1 "
179
 
                "(or 0 == infinite), using default of 5");
180
 
    }
181
 
 
182
 
    if (data->hardfail < 0) {
183
 
        data->hardfail = 0;
184
 
        x99_log(X99_LOG_ERR, "hardfail must be at least 1 "
185
 
                "(or 0 == infinite), using default of 0");
186
 
    }
187
 
 
188
 
    if (data->fast_sync && !data->allow_sync) {
189
 
        data->fast_sync = 0;
190
 
        x99_log(X99_LOG_INFO,
191
 
                "fast_sync is yes, but allow_sync is no; disabling fast_sync");
192
 
    }
193
 
 
194
 
    if (!data->allow_sync && !data->allow_async) {
195
 
        x99_log(X99_LOG_ERR,
196
 
                "at least one of {allow_async, allow_sync} must be set");
197
 
        free(data);
198
 
        return -1;
199
 
    }
200
 
 
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",
204
 
                MAX_EWINDOW_SIZE);
205
 
    }
206
 
 
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");
211
 
    }
212
 
 
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");
217
 
    }
218
 
 
219
 
    if ((data->mschapv2_mppe_policy > 2) || (data->mschapv2_mppe_policy < 0)) {
220
 
        data->mschapv2_mppe_policy = 2;
221
 
        x99_log(X99_LOG_ERR,
222
 
                "invalid value for mschapv2_mppe, using default of 2");
223
 
    }
224
 
 
225
 
    if ((data->mschapv2_mppe_types > 2) || (data->mschapv2_mppe_types < 0)) {
226
 
        data->mschapv2_mppe_types = 2;
227
 
        x99_log(X99_LOG_ERR,
228
 
                "invalid value for mschapv2_mppe_bits, using default of 2");
229
 
    }
230
 
 
231
 
    if ((data->mschap_mppe_policy > 2) || (data->mschap_mppe_policy < 0)) {
232
 
        data->mschap_mppe_policy = 2;
233
 
        x99_log(X99_LOG_ERR,
234
 
                "invalid value for mschap_mppe, using default of 2");
235
 
    }
236
 
 
237
 
    if (data->mschap_mppe_types != 2) {
238
 
        data->mschap_mppe_types = 2;
239
 
        x99_log(X99_LOG_ERR,
240
 
                "invalid value for mschap_mppe_bits, using default of 2");
241
 
    }
242
 
 
243
 
#if 0
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",
247
 
                MAX_TWINDOW_SIZE);
248
 
    }
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;
252
 
        x99_log(X99_LOG_ERR,
253
 
                "invalid values for time window, using default of 0");
254
 
    }
255
 
#endif
256
 
 
257
 
    if (stat(data->syncdir, &st) != 0) {
258
 
        x99_log(X99_LOG_ERR, "syncdir %s error: %s",
259
 
                data->syncdir, strerror(errno));
260
 
        free(data);
261
 
        return -1;
262
 
    }
263
 
    if (st.st_mode != (S_IFDIR|S_IRWXU)) {
264
 
        x99_log(X99_LOG_ERR, "syncdir %s has loose permissions", data->syncdir);
265
 
        free(data);
266
 
        return -1;
267
 
    }
268
 
 
269
 
    /* Set the instance name (for use with authorize()) */
270
 
    data->name = cf_section_name2(conf);
271
 
    if (!data->name)
272
 
        data->name = cf_section_name1(conf);
273
 
    if (!data->name) {
274
 
        x99_log(X99_LOG_ERR, "no instance name (this can't happen)");
275
 
        free(data);
276
 
        return -1;
277
 
    }
278
 
 
279
 
    *instance = data;
280
 
    return 0;
281
 
}
282
 
 
283
 
 
284
 
/* Generate a challenge to be presented to the user. */
285
 
static int
286
 
x99_token_authorize(void *instance, REQUEST *request)
287
 
{
288
 
    x99_token_t *inst = (x99_token_t *) instance;
289
 
 
290
 
    char challenge[MAX_CHALLENGE_LEN + 1];      /* +1 for '\0' terminator */
291
 
    char *state;
292
 
    int rc;
293
 
 
294
 
    x99_user_info_t user_info;
295
 
    int user_found, auth_type_found;
296
 
    int pwattr;
297
 
    int32_t sflags = 0; /* flags for state */
298
 
    VALUE_PAIR *vp;
299
 
 
300
 
    /* Early exit if Auth-Type != inst->name */
301
 
    auth_type_found = 0;
302
 
    if ((vp = pairfind(request->config_items, PW_AUTHTYPE)) != NULL) {
303
 
        auth_type_found = 1;
304
 
        if (strcmp(vp->strvalue, inst->name)) {
305
 
            return RLM_MODULE_NOOP;
306
 
        }
307
 
    }
308
 
 
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;
313
 
    }
314
 
 
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;
320
 
    }
321
 
 
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;
326
 
    }
327
 
 
328
 
    /* Look up the user's info. */
329
 
    user_found = 1;
330
 
    if ((rc = x99_get_user_info(inst->pwdfile, request->username->strvalue,
331
 
                                &user_info)) == -2) {
332
 
#if 0
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);
336
 
#endif
337
 
        return RLM_MODULE_FAIL;
338
 
    }
339
 
    if (rc == -1) {
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 */
343
 
        user_found = 0;
344
 
    }
345
 
 
346
 
    /* fast_sync mode (challenge only if requested) */
347
 
    if (inst->fast_sync &&
348
 
        ((user_info.card_id & X99_CF_SM) || !user_found)) {
349
 
 
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)) {
353
 
            /*
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.
362
 
             */
363
 
            DEBUG("rlm_x99_token: autz: fast_sync challenge requested");
364
 
            goto gen_challenge;
365
 
 
366
 
        } else {
367
 
            /*
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.
371
 
             */
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;
375
 
            }
376
 
            pairadd(&request->config_items, vp);
377
 
            DEBUG("rlm_x99_token: autz: using fast_sync");
378
 
 
379
 
            if (!auth_type_found)
380
 
                pairadd(&request->config_items,
381
 
                        pairmake("Auth-Type", "x99_token", T_OP_EQ));
382
 
            return RLM_MODULE_OK;
383
 
 
384
 
        }
385
 
    } /* if (fast_sync && card supports sync mode) */
386
 
 
387
 
gen_challenge:
388
 
    /* Set the resync bit by default if the user can't request it. */
389
 
    if (!inst->fast_sync)
390
 
        sflags |= htonl(1);
391
 
 
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;
396
 
    }
397
 
 
398
 
    /*
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().
406
 
     */
407
 
    if (user_info.card_id & X99_CF_AM) {
408
 
        time_t now = time(NULL);
409
 
 
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;
413
 
        }
414
 
        now = htonl(now);
415
 
 
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;
419
 
        }
420
 
    } else {
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);
424
 
    }
425
 
    pairadd(&request->reply->vps, pairmake("State", state, T_OP_EQ));
426
 
    free(state);
427
 
 
428
 
    /* Add the challenge to the reply. */
429
 
    {
430
 
        char *u_challenge;      /* challenge with addt'l presentation text */
431
 
 
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));
436
 
        free(u_challenge);
437
 
    }
438
 
 
439
 
    /*
440
 
     * Mark the packet as an Access-Challenge packet.
441
 
     * The server will take care of sending it to the user.
442
 
     */
443
 
    request->reply->code = PW_ACCESS_CHALLENGE;
444
 
    DEBUG("rlm_x99_token: Sending Access-Challenge.");
445
 
 
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;
451
 
}
452
 
 
453
 
 
454
 
/* Verify the response entered by the user. */
455
 
static int
456
 
x99_token_authenticate(void *instance, REQUEST *request)
457
 
{
458
 
    x99_token_t *inst = (x99_token_t *) instance;
459
 
 
460
 
    x99_user_info_t user_info;
461
 
    char *username;
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 */
466
 
 
467
 
    char challenge[MAX_CHALLENGE_LEN + 1];
468
 
    char e_response[9];         /* expected response */
469
 
    VALUE_PAIR *add_vps = NULL;
470
 
 
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;
476
 
    }
477
 
    username = request->username->strvalue;
478
 
 
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;
483
 
    }
484
 
 
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));
490
 
 
491
 
    /* Look up the user's info. */
492
 
    if (x99_get_user_info(inst->pwdfile, username, &user_info) != 0) {
493
 
#if 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);
496
 
#endif
497
 
        return RLM_MODULE_REJECT;
498
 
    }
499
 
 
500
 
    /* Retrieve the challenge (from State attribute), unless (fast_sync). */
501
 
    if (pairfind(request->config_items, PW_X99_FAST) == NULL) {
502
 
        VALUE_PAIR      *vp;
503
 
        unsigned char   *state;
504
 
        time_t          then;
505
 
 
506
 
        if ((vp = pairfind(request->packet->vps, PW_STATE)) != NULL) {
507
 
            int e_length = inst->chal_len;
508
 
 
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 */
512
 
 
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;
517
 
            }
518
 
 
519
 
            /* Fast path if we didn't protect the state. */
520
 
            if (!(user_info.card_id & X99_CF_AM))
521
 
                goto good_state;
522
 
 
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;
531
 
            }
532
 
            if (memcmp(state, vp->strvalue, vp->length)) {
533
 
                x99_log(X99_LOG_AUTH,
534
 
                        "auth: bad state for [%s]: hmac", username);
535
 
                free(state);
536
 
                return RLM_MODULE_REJECT;
537
 
            }
538
 
            free(state);
539
 
 
540
 
            /* State is valid, but check expiry. */
541
 
            then = ntohl(then);
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;
546
 
            }
547
 
        } else {
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?)",
551
 
                    username);
552
 
            return RLM_MODULE_FAIL;
553
 
        }
554
 
    } /* if (!fast_sync) */
555
 
 
556
 
good_state:
557
 
            /* State is good! */
558
 
 
559
 
    /* Get the time of the last authentication. */
560
 
    if (x99_get_last_auth(inst->syncdir, username, &last_auth) != 0) {
561
 
        x99_log(X99_LOG_ERR,
562
 
                "auth: unable to get last auth time for [%s]", username);
563
 
        return RLM_MODULE_FAIL;
564
 
    }
565
 
 
566
 
    /* Check failure count. */
567
 
    fc = x99_check_failcount(username, inst);
568
 
    if ((fc == FAIL_ERR) || (fc == FAIL_HARD))
569
 
        return RLM_MODULE_USERLOCK;
570
 
 
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;
575
 
 
576
 
        if (!pairfind(request->config_items, PW_X99_FAST)) {
577
 
            /*
578
 
             * ewindow2 softfail override requires two consecutive sync
579
 
             * responses.  Fail, and record that this was async.
580
 
             */
581
 
            if (x99_set_last_auth_pos(inst->syncdir, username, 0))
582
 
                x99_log(X99_LOG_ERR,
583
 
                        "auth: failed to record last auth pos for [%s]",
584
 
                        username);
585
 
            return RLM_MODULE_USERLOCK;
586
 
        }
587
 
 
588
 
        /* We're now in "ewindow2 mode" ... subsequent logic must test fc */
589
 
        goto sync_response;
590
 
    }
591
 
 
592
 
    /*
593
 
     * Don't bother to check async response if either
594
 
     * - the card doesn't support it, or
595
 
     * - we're doing fast_sync.
596
 
     */
597
 
    if (!(user_info.card_id & X99_CF_AM) ||
598
 
        pairfind(request->config_items, PW_X99_FAST)) {
599
 
        goto sync_response;
600
 
    }
601
 
 
602
 
    /* Perform any site-specific transforms of the challenge. */
603
 
    if (x99_challenge_transform(username, challenge) != 0) {
604
 
        x99_log(X99_LOG_ERR,
605
 
                "auth: challenge transform failed for [%s]", username);
606
 
        return RLM_MODULE_FAIL;
607
 
        /* NB: last_auth, failcount not updated. */
608
 
    }
609
 
 
610
 
    /* Calculate and test the async response. */
611
 
    if (x99_response(challenge, e_response, user_info.card_id,
612
 
                     user_info.keyblock) != 0) {
613
 
        x99_log(X99_LOG_ERR,
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. */
618
 
    }
619
 
    DEBUG("rlm_x99_token: auth: [%s], async challenge %s, "
620
 
          "expecting response %s", username, challenge, e_response);
621
 
 
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. */
630
 
        }
631
 
 
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. */
639
 
        }
640
 
 
641
 
        if (user_info.card_id & X99_CF_SM) {
642
 
            x99_log(X99_LOG_INFO,
643
 
                    "auth: [%s] authenticated in async mode", username);
644
 
        }
645
 
 
646
 
        rc = RLM_MODULE_OK;
647
 
        if (ntohl(sflags) & 1) {
648
 
            /*
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).
655
 
             */
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) {
663
 
                x99_log(X99_LOG_ERR,
664
 
                        "auth: unable to set sync data for [%s] (for resync)",
665
 
                        username);
666
 
                rc = RLM_MODULE_FAIL;
667
 
            }
668
 
        } else {
669
 
            /* Just update failcount, last_auth, auth_pos. */
670
 
            if (x99_reset_failcount(inst->syncdir, username) != 0) {
671
 
                x99_log(X99_LOG_ERR,
672
 
                        "auth: unable to reset failcount for [%s]", username);
673
 
                rc = RLM_MODULE_FAIL;
674
 
            }
675
 
        }
676
 
        goto return_pw_valid;
677
 
    } /* if (user authenticated async) */
678
 
 
679
 
sync_response:
680
 
    /*
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.
684
 
     */
685
 
    if ((user_info.card_id & X99_CF_SM) && inst->allow_sync) {
686
 
        int start = 0, end = inst->ewindow_size;
687
 
 
688
 
        /*
689
 
         * Tweak start,end for ewindow2_size logic.
690
 
         *
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
698
 
         * sequence).
699
 
         *
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.
703
 
         *
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).
710
 
         */
711
 
        if (fc == FAIL_SOFT) {
712
 
            start = x99_get_last_auth_pos(inst->syncdir, username);
713
 
            end = inst->ewindow2_size;
714
 
        }
715
 
 
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) {
721
 
                x99_log(X99_LOG_ERR,
722
 
                        "auth: unable to get sync data e:%d t:%d for [%s]",
723
 
                        i, 0, username);
724
 
                rc = RLM_MODULE_FAIL;
725
 
                goto return_pw_valid;
726
 
                /* NB: last_auth, failcount not updated. */
727
 
            }
728
 
 
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. */
738
 
            }
739
 
            DEBUG("rlm_x99_token: auth: [%s], sync challenge %d %s, "
740
 
                  "expecting response %s", username, i, challenge, e_response);
741
 
 
742
 
            /* Test user-supplied passcode. */
743
 
            if (x99_pw_valid(request, inst, pwattr, e_response, &add_vps)) {
744
 
                /*
745
 
                 * Yay!  User authenticated via sync mode.  Resync.
746
 
                 */
747
 
                rc = RLM_MODULE_OK;
748
 
 
749
 
                /*
750
 
                 * ewindow2_size logic
751
 
                 */
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);
761
 
                    } else {
762
 
                        /* correct, but not consecutive or not soon enough */
763
 
                        DEBUG("rlm_x99_token: auth: [%s] ewindow2 candidate "
764
 
                              "at position %i", username, i);
765
 
                        auth_pos = i;
766
 
                        rc = RLM_MODULE_REJECT;
767
 
                        break;
768
 
                    }
769
 
                }
770
 
 
771
 
                /*
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)
776
 
                 */
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) {
784
 
                    x99_log(X99_LOG_ERR,
785
 
                            "auth: unable to set sync data for [%s] "
786
 
                            "(for resync)", username);
787
 
                    rc = RLM_MODULE_FAIL;
788
 
                }
789
 
                goto return_pw_valid;
790
 
 
791
 
            } /* if (passcode is valid) */
792
 
        } /* for (each slot in the window) */
793
 
    } /* if (card is in sync mode and sync mode allowed) */
794
 
 
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)) {
798
 
        x99_log(X99_LOG_ERR,
799
 
                "auth: unable to increment failure count for user [%s]",
800
 
                username);
801
 
    }
802
 
    if (x99_set_last_auth_pos(inst->syncdir, username, auth_pos)) {
803
 
        x99_log(X99_LOG_ERR,
804
 
                "auth: unable to set ewindow2 position for user [%s]",
805
 
                username);
806
 
    }
807
 
    return RLM_MODULE_REJECT;
808
 
 
809
 
    /* Must exit here after a successful return from x99_pw_valid(). */
810
 
return_pw_valid:
811
 
 
812
 
    /* Handle any vps returned from x99_pw_valid(). */
813
 
    if (rc == RLM_MODULE_OK) {
814
 
        pairadd(&request->reply->vps, add_vps);
815
 
    } else {
816
 
        pairfree(&add_vps);
817
 
    }
818
 
    return rc;
819
 
}
820
 
 
821
 
 
822
 
/* per-instance destruction */
823
 
static int
824
 
x99_token_detach(void *instance)
825
 
{
826
 
    x99_token_t *inst = (x99_token_t *) instance;
827
 
 
828
 
    free(inst->pwdfile);
829
 
    free(inst->syncdir);
830
 
    free(inst->chal_prompt);
831
 
    free(inst->chal_req);
832
 
    free(inst->resync_req);
833
 
    free(instance);
834
 
    return 0;
835
 
}
836
 
 
837
 
 
838
 
/* per-module destruction */
839
 
static int
840
 
x99_token_destroy(void)
841
 
{
842
 
    (void) memset(hmac_key, 0, sizeof(hmac_key));
843
 
    (void) close(rnd_fd);
844
 
    return 0;
845
 
}
846
 
 
847
 
/*
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.
852
 
 */
853
 
module_t rlm_x99_token = {
854
 
        "x99_token",
855
 
        RLM_TYPE_THREAD_SAFE,           /* type */
856
 
        x99_token_init,                 /* initialization */
857
 
        x99_token_instantiate,          /* instantiation */
858
 
        {
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 */
866
 
                NULL                    /* post-auth */
867
 
        },
868
 
        x99_token_detach,               /* detach */
869
 
        x99_token_destroy,              /* destroy */
870
 
};