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