Made it 2.0.0, and removed the changes that are in 1.1.x, as
[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 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 <freeradius-devel/modules.h>
53 #endif
54
55 static const char rcsid[] = "$Id$";
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->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->vp_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->vp_strvalue, inst->chal_len);
506         (void) memcpy(&sflags, vp->vp_strvalue + inst->chal_len, 4);
507         (void) memcpy(&then, vp->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->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   RLM_MODULE_INIT,
579   "otp",
580   RLM_TYPE_THREAD_SAFE,         /* type */
581   otp_instantiate,              /* instantiation */
582   otp_detach,                   /* detach */
583   {
584     otp_authenticate,           /* authentication */
585     otp_authorize,              /* authorization */
586     NULL,                       /* preaccounting */
587     NULL,                       /* accounting */
588     NULL,                       /* checksimul */
589     NULL,                       /* pre-proxy */
590     NULL,                       /* post-proxy */
591     NULL                        /* post-auth */
592   },
593 };