~ubuntu-branches/ubuntu/natty/freeradius/natty-updates

« back to all changes in this revision

Viewing changes to src/modules/rlm_otp/otp_rlm.c

  • Committer: Bazaar Package Importer
  • Author(s): Paul Hampson
  • Date: 2006-01-15 13:34:13 UTC
  • mto: (3.1.3 dapper) (4.1.3 sid) (1.1.14 upstream)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20060115133413-zo1dslttvdoalqym
Tags: upstream-1.1.0
ImportĀ upstreamĀ versionĀ 1.1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * otp_rlm.c
 
3
 * $Id: otp_rlm.c,v 1.19.2.2 2005/12/08 02:07:32 fcusack 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
 * Copyright 2005 TRI-D Systems, Inc.
 
22
 */
 
23
 
 
24
/*
 
25
 * STRONG WARNING SECTION:
 
26
 *
 
27
 * ANSI X9.9 has been withdrawn as a standard, due to the weakness of DES.
 
28
 * An attacker can learn the token's secret by observing two
 
29
 * challenge/response pairs.  See ANSI document X9 TG-24-1999
 
30
 * <URL:http://www.x9.org/docs/TG24_1999.pdf>.
 
31
 *
 
32
 * Please read the accompanying docs.
 
33
 */
 
34
 
 
35
/*
 
36
 * TODO: support setting multiple auth-types in authorize()
 
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(), ntohl() */
 
49
 
 
50
#include "otp.h"
 
51
#ifdef FREERADIUS
 
52
#include <modules.h>
 
53
#endif
 
54
 
 
55
static const char rcsid[] = "$Id: otp_rlm.c,v 1.19.2.2 2005/12/08 02:07:32 fcusack Exp $";
 
56
 
 
57
/* Global data */
 
58
static unsigned char hmac_key[16];      /* to protect State attribute     */
 
59
static int ninstance = 0;               /* #instances, for global init    */
 
60
 
 
61
/* A mapping of configuration file names to internal variables. */
 
62
static const CONF_PARSER module_config[] = {
 
63
  { "pwdfile", PW_TYPE_STRING_PTR, offsetof(otp_option_t, pwdfile),
 
64
    NULL, OTP_PWDFILE },
 
65
  { "lsmd_rp", PW_TYPE_STRING_PTR, offsetof(otp_option_t, lsmd_rp),
 
66
    NULL, OTP_LSMD_RP },
 
67
  { "challenge_prompt", PW_TYPE_STRING_PTR,offsetof(otp_option_t, chal_prompt),
 
68
    NULL, OTP_CHALLENGE_PROMPT },
 
69
  { "challenge_length", PW_TYPE_INTEGER, offsetof(otp_option_t, chal_len),
 
70
    NULL, "6" },
 
71
  { "challenge_delay", PW_TYPE_INTEGER, offsetof(otp_option_t, chal_delay),
 
72
    NULL, "30" },
 
73
  { "softfail", PW_TYPE_INTEGER, offsetof(otp_option_t, softfail),
 
74
    NULL, "5" },
 
75
  { "hardfail", PW_TYPE_INTEGER, offsetof(otp_option_t, hardfail),
 
76
    NULL, "0" },
 
77
  { "allow_sync", PW_TYPE_BOOLEAN, offsetof(otp_option_t, allow_sync),
 
78
    NULL, "yes" },
 
79
  { "fast_sync", PW_TYPE_BOOLEAN, offsetof(otp_option_t, fast_sync),
 
80
    NULL, "yes" },
 
81
  { "allow_async", PW_TYPE_BOOLEAN, offsetof(otp_option_t, allow_async),
 
82
    NULL, "no" },
 
83
  { "challenge_req", PW_TYPE_STRING_PTR, offsetof(otp_option_t, chal_req),
 
84
    NULL, OTP_CHALLENGE_REQ },
 
85
  { "resync_req", PW_TYPE_STRING_PTR, offsetof(otp_option_t, resync_req),
 
86
    NULL, OTP_RESYNC_REQ },
 
87
  { "prepend_pin", PW_TYPE_BOOLEAN, offsetof(otp_option_t, prepend_pin),
 
88
    NULL, "yes" },
 
89
  { "ewindow_size", PW_TYPE_INTEGER, offsetof(otp_option_t, ewindow_size),
 
90
    NULL, "0" },
 
91
  { "rwindow_size", PW_TYPE_INTEGER, offsetof(otp_option_t, rwindow_size),
 
92
    NULL, "0" },
 
93
  { "rwindow_delay", PW_TYPE_INTEGER, offsetof(otp_option_t, rwindow_delay),
 
94
    NULL, "60" },
 
95
  { "mschapv2_mppe", PW_TYPE_INTEGER,
 
96
    offsetof(otp_option_t, mschapv2_mppe_policy), NULL, "2" },
 
97
  { "mschapv2_mppe_bits", PW_TYPE_INTEGER,
 
98
    offsetof(otp_option_t, mschapv2_mppe_types), NULL, "2" },
 
99
  { "mschap_mppe", PW_TYPE_INTEGER,
 
100
    offsetof(otp_option_t, mschap_mppe_policy), NULL, "2" },
 
101
  { "mschap_mppe_bits", PW_TYPE_INTEGER,
 
102
    offsetof(otp_option_t, mschap_mppe_types), NULL, "2" },
 
103
 
 
104
  { "debug", PW_TYPE_BOOLEAN, offsetof(otp_option_t, debug),
 
105
    NULL, "no" },
 
106
 
 
107
  { NULL, -1, 0, NULL, NULL }           /* end the list */
 
108
};
 
109
 
 
110
 
 
111
/* transform otp_pw_valid() return code into an rlm return code */
 
112
static int
 
113
otprc2rlmrc(int rc)
 
114
{
 
115
  switch (rc) {
 
116
    case OTP_RC_OK:                     return RLM_MODULE_OK;
 
117
    case OTP_RC_USER_UNKNOWN:           return RLM_MODULE_REJECT;
 
118
    case OTP_RC_AUTHINFO_UNAVAIL:       return RLM_MODULE_REJECT;
 
119
    case OTP_RC_AUTH_ERR:               return RLM_MODULE_REJECT;
 
120
    case OTP_RC_MAXTRIES:               return RLM_MODULE_USERLOCK;
 
121
    case OTP_RC_SERVICE_ERR:            return RLM_MODULE_FAIL;
 
122
    default:                            return RLM_MODULE_FAIL;
 
123
  }
 
124
}
 
125
 
 
126
 
 
127
/* per-instance initialization */
 
128
static int
 
129
otp_instantiate(CONF_SECTION *conf, void **instance)
 
130
{
 
131
  const char *log_prefix = OTP_MODULE_NAME;
 
132
  otp_option_t *opt;
 
133
  char *p;
 
134
 
 
135
  /* Set up a storage area for instance data. */
 
136
  opt = rad_malloc(sizeof(*opt));
 
137
  (void) memset(opt, 0, sizeof(*opt));
 
138
 
 
139
  /* If the configuration parameters can't be parsed, then fail. */
 
140
  if (cf_section_parse(conf, opt, module_config) < 0) {
 
141
    free(opt);
 
142
    return -1;
 
143
  }
 
144
 
 
145
  /* Onetime initialization. */
 
146
  if (!ninstance) {
 
147
    /* Generate a random key, used to protect the State attribute. */
 
148
    if (otp_get_random(-1, hmac_key, sizeof(hmac_key), log_prefix) == -1) {
 
149
      otp_log(OTP_LOG_ERR, "%s: %s: failed to obtain random data for hmac_key",
 
150
              log_prefix, __func__);
 
151
      free(opt);
 
152
      return -1;
 
153
    }
 
154
 
 
155
    /* Initialize the passcode encoding/checking functions. */
 
156
    otp_pwe_init();
 
157
 
 
158
    /*
 
159
     * Don't do this again.
 
160
     * Only the main thread instantiates and detaches instances,
 
161
     * so this does not need mutex protection.
 
162
     */
 
163
    ninstance++;
 
164
  }
 
165
 
 
166
  /* Verify ranges for those vars that are limited. */
 
167
  if ((opt->chal_len < 5) || (opt->chal_len > OTP_MAX_CHALLENGE_LEN)) {
 
168
    opt->chal_len = 6;
 
169
    otp_log(OTP_LOG_ERR,
 
170
            "%s: %s: invalid challenge_length, range 5-%d, using default of 6",
 
171
            log_prefix, __func__, OTP_MAX_CHALLENGE_LEN);
 
172
  }
 
173
 
 
174
  /* Enforce a single "%" sequence, which must be "%s" */
 
175
  p = strchr(opt->chal_prompt, '%');
 
176
  if ((p == NULL) || (p != strrchr(opt->chal_prompt, '%')) ||
 
177
      strncmp(p,"%s",2)) {
 
178
    free(opt->chal_prompt);
 
179
    opt->chal_prompt = strdup(OTP_CHALLENGE_PROMPT);
 
180
    otp_log(OTP_LOG_ERR,
 
181
            "%s: %s: invalid challenge_prompt, using default of \"%s\"",
 
182
            log_prefix, __func__, OTP_CHALLENGE_PROMPT);
 
183
  }
 
184
 
 
185
  if (opt->softfail < 0) {
 
186
    opt->softfail = 5;
 
187
    otp_log(OTP_LOG_ERR, "%s: %s: softfail must be at least 1 "
 
188
                         "(or 0 == infinite), using default of 5",
 
189
            log_prefix, __func__);
 
190
  }
 
191
 
 
192
  if (opt->hardfail < 0) {
 
193
    opt->hardfail = 0;
 
194
    otp_log(OTP_LOG_ERR, "%s: %s: hardfail must be at least 1 "
 
195
                         "(or 0 == infinite), using default of 0",
 
196
            log_prefix, __func__);
 
197
  }
 
198
 
 
199
  if (!opt->hardfail && opt->hardfail <= opt->softfail) {
 
200
    /*
 
201
     * This is noise if the admin leaves softfail alone, so it gets
 
202
     * the default value of 5, and sets hardfail <= to that ... but
 
203
     * in practice that will never happen.  Anyway, it is easily
 
204
     * overcome with a softfail setting of 0.
 
205
     *
 
206
     * This is because we can't tell the difference between a default
 
207
     * [softfail] value and an admin-configured one.
 
208
     */
 
209
    otp_log(OTP_LOG_ERR, "%s: %s: hardfail (%d) is less than softfail (%d), "
 
210
                         "effectively disabling softfail",
 
211
            log_prefix, __func__, opt->hardfail, opt->softfail);
 
212
  }
 
213
 
 
214
  if (opt->fast_sync && !opt->allow_sync) {
 
215
    opt->fast_sync = 0;
 
216
    otp_log(OTP_LOG_ERR, "%s: %s: fast_sync is yes, but allow_sync is no; "
 
217
                         "disabling fast_sync",
 
218
            log_prefix, __func__);
 
219
  }
 
220
 
 
221
  if (!opt->allow_sync && !opt->allow_async) {
 
222
    otp_log(OTP_LOG_ERR,
 
223
            "%s: %s: at least one of {allow_async, allow_sync} must be set",
 
224
            log_prefix, __func__);
 
225
    free(opt);
 
226
    return -1;
 
227
  }
 
228
 
 
229
  if ((opt->ewindow_size > OTP_MAX_EWINDOW_SIZE) ||
 
230
    (opt->ewindow_size < 0)) {
 
231
    opt->ewindow_size = 0;
 
232
    otp_log(OTP_LOG_ERR, "%s: %s: max ewindow_size is %d, using default of 0",
 
233
            log_prefix, __func__, OTP_MAX_EWINDOW_SIZE);
 
234
  }
 
235
 
 
236
  if (opt->rwindow_size && (opt->rwindow_size < opt->ewindow_size)) {
 
237
    opt->rwindow_size = 0;
 
238
    otp_log(OTP_LOG_ERR, "%s: %s: rwindow_size must be at least as large as "
 
239
                         "ewindow_size, using default of 0",
 
240
            log_prefix, __func__);
 
241
  }
 
242
 
 
243
  if (opt->rwindow_size && !opt->rwindow_delay) {
 
244
    opt->rwindow_size = 0;
 
245
    otp_log(OTP_LOG_ERR, "%s: %s: rwindow_size is non-zero, "
 
246
                         "but rwindow_delay is zero; disabling rwindow",
 
247
            log_prefix, __func__);
 
248
  }
 
249
 
 
250
  if ((opt->mschapv2_mppe_policy > 2) || (opt->mschapv2_mppe_policy < 0)) {
 
251
    opt->mschapv2_mppe_policy = 2;
 
252
    otp_log(OTP_LOG_ERR,
 
253
            "%s: %s: invalid value for mschapv2_mppe, using default of 2",
 
254
            log_prefix, __func__);
 
255
  }
 
256
 
 
257
  if ((opt->mschapv2_mppe_types > 2) || (opt->mschapv2_mppe_types < 0)) {
 
258
    opt->mschapv2_mppe_types = 2;
 
259
    otp_log(OTP_LOG_ERR,
 
260
            "%s: %s: invalid value for mschapv2_mppe_bits, using default of 2",
 
261
            log_prefix, __func__);
 
262
  }
 
263
 
 
264
  if ((opt->mschap_mppe_policy > 2) || (opt->mschap_mppe_policy < 0)) {
 
265
    opt->mschap_mppe_policy = 2;
 
266
    otp_log(OTP_LOG_ERR,
 
267
            "%s: %s: invalid value for mschap_mppe, using default of 2",
 
268
            log_prefix, __func__);
 
269
  }
 
270
 
 
271
  if (opt->mschap_mppe_types != 2) {
 
272
    opt->mschap_mppe_types = 2;
 
273
    otp_log(OTP_LOG_ERR,
 
274
            "%s: %s: invalid value for mschap_mppe_bits, using default of 2",
 
275
            log_prefix, __func__);
 
276
  }
 
277
 
 
278
  /* set the instance name (for use with authorize()) */
 
279
  opt->name = cf_section_name2(conf);
 
280
  if (!opt->name)
 
281
    opt->name = cf_section_name1(conf);
 
282
  if (!opt->name) {
 
283
    otp_log(OTP_LOG_CRIT, "%s: %s: no instance name (this can't happen)",
 
284
            log_prefix, __func__);
 
285
    free(opt);
 
286
    return -1;
 
287
  }
 
288
 
 
289
  /* set debug opt for portable debug output (see DEBUG definition) */
 
290
  if (debug_flag)
 
291
    opt->debug = 1;
 
292
 
 
293
  *instance = opt;
 
294
  return 0;
 
295
}
 
296
 
 
297
 
 
298
/* Generate a challenge to be presented to the user. */
 
299
static int
 
300
otp_authorize(void *instance, REQUEST *request)
 
301
{
 
302
  otp_option_t *inst = (otp_option_t *) instance;
 
303
  const char *log_prefix = OTP_MODULE_NAME;
 
304
 
 
305
  char challenge[OTP_MAX_CHALLENGE_LEN + 1];    /* +1 for '\0' terminator */
 
306
  char *state;
 
307
  int auth_type_found;
 
308
  int32_t sflags = 0; /* flags for state */
 
309
 
 
310
  struct otp_pwe_cmp_t data = {
 
311
    .request            = request,
 
312
    .inst               = inst,
 
313
    .returned_vps       = NULL
 
314
  };
 
315
 
 
316
  /* Early exit if Auth-Type != inst->name */
 
317
  {
 
318
    VALUE_PAIR *vp;
 
319
 
 
320
    auth_type_found = 0;
 
321
    if ((vp = pairfind(request->config_items, PW_AUTHTYPE)) != NULL) {
 
322
      auth_type_found = 1;
 
323
      if (strcmp(vp->strvalue, inst->name))
 
324
        return RLM_MODULE_NOOP;
 
325
    }
 
326
  }
 
327
 
 
328
  /* The State attribute will be present if this is a response. */
 
329
  if (pairfind(request->packet->vps, PW_STATE) != NULL) {
 
330
    DEBUG("rlm_otp: autz: Found response to Access-Challenge");
 
331
    return RLM_MODULE_OK;
 
332
  }
 
333
 
 
334
  /* User-Name attribute required. */
 
335
  if (!request->username) {
 
336
    otp_log(OTP_LOG_AUTH,
 
337
            "%s: %s: Attribute \"User-Name\" required for authentication.",
 
338
            log_prefix, __func__);
 
339
    return RLM_MODULE_INVALID;
 
340
  }
 
341
 
 
342
  if ((data.pwattr = otp_pwe_present(request, log_prefix)) == 0) {
 
343
    otp_log(OTP_LOG_AUTH, "%s: %s: Attribute \"User-Password\" "
 
344
                          "or equivalent required for authentication.",
 
345
            log_prefix, __func__);
 
346
    return RLM_MODULE_INVALID;
 
347
  }
 
348
 
 
349
  /* fast_sync mode (challenge only if requested) */
 
350
  if (inst->fast_sync) {
 
351
    if ((!otp_pwe_cmp(&data, inst->resync_req, log_prefix) &&
 
352
        /* Set a bit indicating resync */ (sflags |= htonl(1))) ||
 
353
        !otp_pwe_cmp(&data, inst->chal_req, log_prefix)) {
 
354
      /*
 
355
       * Generate a challenge if requested.  Note that we do this
 
356
       * even if configuration doesn't allow async mode.
 
357
       */
 
358
      DEBUG("rlm_otp: autz: fast_sync challenge requested");
 
359
      goto gen_challenge;
 
360
 
 
361
    } else {
 
362
      /* Otherwise, this is the token sync response. */
 
363
      if (!auth_type_found)
 
364
        pairadd(&request->config_items, pairmake("Auth-Type", "otp", T_OP_EQ));
 
365
        return RLM_MODULE_OK;
 
366
 
 
367
    }
 
368
  } /* if (fast_sync && card supports sync mode) */
 
369
 
 
370
gen_challenge:
 
371
  /* Set the resync bit by default if the user can't choose. */
 
372
  if (!inst->fast_sync)
 
373
    sflags |= htonl(1);
 
374
 
 
375
  /* Generate a random challenge. */
 
376
  if (otp_async_challenge(-1, challenge, inst->chal_len, log_prefix) == -1) {
 
377
    otp_log(OTP_LOG_ERR, "%s: %s: failed to obtain random challenge",
 
378
            log_prefix, __func__);
 
379
    return RLM_MODULE_FAIL;
 
380
  }
 
381
 
 
382
  /*
 
383
   * Create the State attribute, which will be returned to us along with
 
384
   * the response.  We will need this to verify the response.  It must
 
385
   * be hmac protected to prevent insertion of arbitrary State by an
 
386
   * inside attacker.  If we won't actually use the State (server config
 
387
   * doesn't allow async), we just use a trivial State.  We always create
 
388
   * at least a trivial State, so otp_authorize() can quickly pass on to
 
389
   * otp_authenticate().
 
390
   */
 
391
  if (inst->allow_async) {
 
392
    time_t now = time(NULL);
 
393
 
 
394
    if (sizeof(now) != 4 || sizeof(long) != 4) {
 
395
      otp_log(OTP_LOG_ERR, "%s: %s: only ILP32 arch is supported",
 
396
              log_prefix, __func__);
 
397
      return RLM_MODULE_FAIL;
 
398
    }
 
399
    now = htonl(now);
 
400
 
 
401
    if (otp_gen_state(&state, NULL, challenge, inst->chal_len, sflags,
 
402
                      now, hmac_key) != 0) {
 
403
      otp_log(OTP_LOG_ERR, "%s: %s: failed to generate state",
 
404
              log_prefix, __func__);
 
405
      return RLM_MODULE_FAIL;
 
406
    }
 
407
  } else {
 
408
    state = rad_malloc(5);
 
409
    (void) sprintf(state, "0x00");
 
410
  }
 
411
  pairadd(&request->reply->vps, pairmake("State", state, T_OP_EQ));
 
412
  free(state);
 
413
 
 
414
  /* Add the challenge to the reply. */
 
415
  {
 
416
    char *u_challenge;  /* challenge with addt'l presentation text */
 
417
 
 
418
    u_challenge = rad_malloc(strlen(inst->chal_prompt) +
 
419
                             OTP_MAX_CHALLENGE_LEN + 1);
 
420
/* XXX */
 
421
    (void) sprintf(u_challenge, inst->chal_prompt, challenge);
 
422
    pairadd(&request->reply->vps,
 
423
            pairmake("Reply-Message", u_challenge, T_OP_EQ));
 
424
    free(u_challenge);
 
425
  }
 
426
 
 
427
  /*
 
428
   * Mark the packet as an Access-Challenge packet.
 
429
   * The server will take care of sending it to the user.
 
430
   */
 
431
  request->reply->code = PW_ACCESS_CHALLENGE;
 
432
  DEBUG("rlm_otp: Sending Access-Challenge.");
 
433
 
 
434
  /* TODO: support config-specific auth-type */
 
435
  if (!auth_type_found)
 
436
    pairadd(&request->config_items, pairmake("Auth-Type", "otp", T_OP_EQ));
 
437
  return RLM_MODULE_HANDLED;
 
438
}
 
439
 
 
440
 
 
441
/* Verify the response entered by the user. */
 
442
static int
 
443
otp_authenticate(void *instance, REQUEST *request)
 
444
{
 
445
  otp_option_t *inst = (otp_option_t *) instance;
 
446
  const char *log_prefix = OTP_MODULE_NAME;
 
447
 
 
448
  char *username;
 
449
  int rc;
 
450
  int resync = 0;       /* resync flag for async mode */
 
451
 
 
452
  unsigned char challenge[OTP_MAX_CHALLENGE_LEN];       /* cf. authorize() */
 
453
  VALUE_PAIR *add_vps = NULL;
 
454
 
 
455
  struct otp_pwe_cmp_t data = {
 
456
    .request            = request,
 
457
    .inst               = inst,
 
458
    .returned_vps       = &add_vps
 
459
  };
 
460
 
 
461
  /* User-Name attribute required. */
 
462
  if (!request->username) {
 
463
    otp_log(OTP_LOG_AUTH,
 
464
            "%s: %s: Attribute \"User-Name\" required for authentication.",
 
465
            log_prefix, __func__);
 
466
    return RLM_MODULE_INVALID;
 
467
  }
 
468
  username = request->username->strvalue;
 
469
 
 
470
  if ((data.pwattr = otp_pwe_present(request, log_prefix)) == 0) {
 
471
    otp_log(OTP_LOG_AUTH, "%s: %s: Attribute \"User-Password\" "
 
472
                          "or equivalent required for authentication.",
 
473
            log_prefix, __func__);
 
474
    return RLM_MODULE_INVALID;
 
475
  }
 
476
 
 
477
  /* Add a message to the auth log. */
 
478
  pairadd(&request->packet->vps, pairmake("Module-Failure-Message",
 
479
                                          OTP_MODULE_NAME, T_OP_EQ));
 
480
  pairadd(&request->packet->vps, pairmake("Module-Success-Message",
 
481
                                          OTP_MODULE_NAME, T_OP_EQ));
 
482
 
 
483
  /* Retrieve the challenge (from State attribute). */
 
484
  {
 
485
    VALUE_PAIR  *vp;
 
486
    unsigned char       *state;
 
487
    int32_t             sflags = 0;     /* state flags */
 
488
    int32_t             then;           /* state timestamp */
 
489
 
 
490
    if ((vp = pairfind(request->packet->vps, PW_STATE)) != NULL) {
 
491
      int e_length = inst->chal_len;
 
492
 
 
493
      /* Extend expected length if state should have been protected. */
 
494
      if (inst->allow_async)
 
495
        e_length += 4 + 4 + 16; /* sflags + time + hmac */
 
496
 
 
497
      if (vp->length != e_length) {
 
498
        otp_log(OTP_LOG_AUTH, "%s: %s: bad state for [%s]: length",
 
499
                log_prefix, __func__, username);
 
500
        return RLM_MODULE_INVALID;
 
501
      }
 
502
 
 
503
      if (inst->allow_async) {
 
504
        /* Verify the state. */
 
505
        (void) memcpy(challenge, vp->strvalue, inst->chal_len);
 
506
        (void) memcpy(&sflags, vp->strvalue + inst->chal_len, 4);
 
507
        (void) memcpy(&then, vp->strvalue + inst->chal_len + 4, 4);
 
508
        if (otp_gen_state(NULL, &state, challenge, inst->chal_len,
 
509
                          sflags, then, hmac_key) != 0) {
 
510
          otp_log(OTP_LOG_ERR, "%s: %s: failed to generate state",
 
511
                  log_prefix, __func__);
 
512
          return RLM_MODULE_FAIL;
 
513
        }
 
514
        if (memcmp(state, vp->strvalue, vp->length)) {
 
515
          otp_log(OTP_LOG_AUTH, "%s: %s: bad state for [%s]: hmac",
 
516
                  log_prefix, __func__, username);
 
517
          free(state);
 
518
          return RLM_MODULE_REJECT;
 
519
        }
 
520
        free(state);
 
521
 
 
522
        /* State is valid, but check expiry. */
 
523
        then = ntohl(then);
 
524
        if (time(NULL) - then > inst->chal_delay) {
 
525
          otp_log(OTP_LOG_AUTH, "%s: %s: bad state for [%s]: expired",
 
526
                  log_prefix, __func__, username);
 
527
          return RLM_MODULE_REJECT;
 
528
        }
 
529
        resync = ntohl(sflags) & 1;
 
530
      } /* if (State should have been protected) */
 
531
    } /* if (State present) */
 
532
  } /* code block */
 
533
 
 
534
  /* do it */
 
535
  rc = otprc2rlmrc(otp_pw_valid(username, challenge, NULL, resync, inst,
 
536
                                otp_pwe_cmp, &data, log_prefix));
 
537
 
 
538
  /* Handle any vps returned from otp_pwe_cmp(). */
 
539
  if (rc == RLM_MODULE_OK) {
 
540
    pairadd(&request->reply->vps, add_vps);
 
541
  } else {
 
542
    pairfree(&add_vps);
 
543
  }
 
544
  return rc;
 
545
}
 
546
 
 
547
 
 
548
/* per-instance destruction */
 
549
static int
 
550
otp_detach(void *instance)
 
551
{
 
552
  otp_option_t *inst = (otp_option_t *) instance;
 
553
 
 
554
  free(inst->pwdfile);
 
555
  free(inst->lsmd_rp);
 
556
  free(inst->chal_prompt);
 
557
  free(inst->chal_req);
 
558
  free(inst->resync_req);
 
559
  free(instance);
 
560
  /*
 
561
   * Only the main thread instantiates and detaches instances,
 
562
   * so this does not need mutex protection.
 
563
   */
 
564
  if (--ninstance == 0)
 
565
    memset(hmac_key, 0, sizeof(hmac_key));
 
566
 
 
567
  return 0;
 
568
}
 
569
 
 
570
 
 
571
/*
 
572
 *      If the module needs to temporarily modify it's instantiation
 
573
 *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
 
574
 *      The server will then take care of ensuring that the module
 
575
 *      is single-threaded.
 
576
 */
 
577
module_t rlm_otp = {
 
578
  "otp",
 
579
  RLM_TYPE_THREAD_SAFE,         /* type */
 
580
  NULL,                         /* initialization */
 
581
  otp_instantiate,              /* instantiation */
 
582
  {
 
583
    otp_authenticate,           /* authentication */
 
584
    otp_authorize,              /* authorization */
 
585
    NULL,                       /* preaccounting */
 
586
    NULL,                       /* accounting */
 
587
    NULL,                       /* checksimul */
 
588
    NULL,                       /* pre-proxy */
 
589
    NULL,                       /* post-proxy */
 
590
    NULL                        /* post-auth */
 
591
  },
 
592
  otp_detach,                   /* detach */
 
593
  NULL,                         /* destroy */
 
594
};