import from HEAD:
[freeradius.git] / src / modules / rlm_otp / otp_cardops.c
1 /*
2  * otp_cardops.c
3  * $Id$
4  *
5  * Passcode verification functions for otp.
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License as published by
9  *   the Free Software Foundation; either version 2 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  *
22  * Copyright 2002-2005  Google, Inc.
23  * Copyright 2005 TRI-D Systems, Inc.
24  */
25
26 static const char rcsid[] = "$Id$";
27
28 #if defined(__linux__) && !defined(_GNU_SOURCE)
29 #define _GNU_SOURCE     /* RTLD_DEFAULT */
30 #endif
31 #include <dlfcn.h>
32 #include <inttypes.h>
33 #include <limits.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <time.h>
37
38 #include "otp.h"
39 #include "otp_cardops.h"
40
41 /* Global data */
42 cardops_t otp_cardops[OTP_MAX_VENDORS]; /* cardops objects */
43 int otp_num_cardops = 0;                /* number of cardops modules loaded */
44
45
46 /*
47  * Test for passcode validity.
48  *
49  * If challenge is supplied, it is used to generate the card response
50  * against which the passcode will be compared.  If challenge is not
51  * supplied, or if the comparison fails, synchronous responses are
52  * generated and tested.  NOTE: for async authentications, sync mode
53  * responses are still considered valid!  (Assuming module configuration
54  * allows sync mode.)
55  *
56  * If passcode is supplied, a simple string comparison is done, else if
57  * cmp is supplied, it is called to test for validity.  The cmp function
58  * is required for RADIUS, where we might have a CHAP response instead
59  * of the plaintext of the passcode from the user.
60  *
61  * If challenge is supplied, then resync is used to determine if the card
62  * should be resynced or if this is a one-off response.  (If challenge is
63  * not supplied, this is sync mode response and the card is always resynced.)
64  *
65  * Returns one of the OTP_RC_* return codes.  challenge is overwritten.
66  */
67 int
68 otp_pw_valid(const char *username, char *challenge, const char *passcode,
69              int resync, const otp_option_t *opt,
70              cmpfunc_t cmpfunc, void *data,
71              const char *log_prefix)
72 {
73   int   rc, nmatch, i;
74   int   e = 0, t = 0;   /* must initialize for async auth path */
75   int   fc;             /* failcondition */
76
77         /* expected response */
78   char  e_response[OTP_MAX_RESPONSE_LEN + OTP_MAX_PIN_LEN + 1];
79   int   pin_offset = 0; /* pin offset in e_response (prepend or append) */
80
81   otp_card_info_t       card_info  = { .cardops = NULL };
82   otp_user_state_t      user_state = { .locked = 0 };
83
84   time_t now = time(NULL);
85
86   /*
87    * In the Cyclades ACS environment (2.3.0 tested), the runtime linker
88    * apparently does not run static constructors in ELF .ctors sections.
89    * Since that is how we initialize cardops modules, we have an ugly
90    * workaround here.  Our other choice is to implement cardops modules
91    * as full-fledged shared libraries, which is just too much work.
92    */
93   if (otp_num_cardops == 0) {
94     void (*cardops_init)(void);
95
96     /* ctors did not run; execute all known constructors */
97     if ((cardops_init = dlsym(RTLD_DEFAULT, "cryptocard_init")))
98       (*cardops_init)();
99     if ((cardops_init = dlsym(RTLD_DEFAULT, "trid_init")))
100       (*cardops_init)();
101   }
102
103   /* sanity */
104   if (!challenge) {
105     rc = OTP_RC_SERVICE_ERR;
106     goto auth_done_service_err;
107   }
108   if (!passcode && !cmpfunc) {
109     otp_log(OTP_LOG_CRIT, "%s: %s: Can't test passcode validity!",
110             log_prefix, __func__);
111     rc = OTP_RC_SERVICE_ERR;
112     goto auth_done_service_err;
113   }
114
115   /* Look up user info.  (errors are logged by the function) */
116   rc = otp_get_card_info(opt->pwdfile, username, &card_info, log_prefix);
117   if (rc == -1) {
118     rc = OTP_RC_USER_UNKNOWN;
119     goto auth_done_service_err;
120   } else if (rc == -2) {
121     rc = OTP_RC_AUTHINFO_UNAVAIL;
122     goto auth_done_service_err;
123   }
124   card_info.username = username;
125
126   /* Find the correct cardops module. */
127   for (i = 0; otp_cardops[i].prefix; i++) {
128     if (!strncasecmp(otp_cardops[i].prefix, card_info.card,
129                      otp_cardops[i].prefix_len)) {
130       card_info.cardops = &otp_cardops[i];
131       break;
132     }
133   }
134   if (!card_info.cardops) {
135     otp_log(OTP_LOG_ERR, "%s: %s: invalid card type '%s' for [%s]",
136             log_prefix, __func__, card_info.card, username);
137     rc = OTP_RC_SERVICE_ERR;
138     goto auth_done_service_err;
139   }
140
141   /* Convert name to a feature mask once, for fast operations later. */
142   if (card_info.cardops->name2fm(card_info.card, &card_info.featuremask)) {
143     otp_log(OTP_LOG_ERR, "%s: %s: invalid card type '%s' for [%s]",
144             log_prefix, __func__, card_info.card, username);
145     rc = OTP_RC_SERVICE_ERR;
146     goto auth_done_service_err;
147   }
148
149   /* Convert keystring to a keyblock. */
150   if (card_info.cardops->keystring2keyblock(card_info.keystring,
151                                             card_info.keyblock) < 0) {
152     otp_log(OTP_LOG_ERR, "%s: %s: invalid key '%s' for [%s]",
153             log_prefix, __func__, card_info.keystring, username);
154     rc = OTP_RC_SERVICE_ERR;
155     goto auth_done_service_err;
156   }
157
158   /* Adjust e_response for PIN prepend. */
159   if (opt->prepend_pin) {
160     (void) strcpy(e_response, card_info.pin);
161     pin_offset = strlen(e_response);
162   }
163
164   /* Get user state. */
165   if (otp_state_get(opt, username, &user_state, log_prefix) != 0) {
166     otp_log(OTP_LOG_ERR, "%s: %s: unable to get state for [%s]",
167             log_prefix, __func__, username);
168     rc = OTP_RC_SERVICE_ERR;
169     goto auth_done_service_err;
170   }
171   if (user_state.nullstate) {
172     if (card_info.cardops->nullstate(opt, &card_info, &user_state,
173                                      now, log_prefix)) {
174       otp_log(OTP_LOG_ERR, "%s: %s: unable to set null state for [%s]",
175               log_prefix, __func__, username);
176       rc = OTP_RC_SERVICE_ERR;
177       goto auth_done_service_err;
178     }
179   }
180
181   /* Set fc (failcondition). */
182   if (opt->hardfail && user_state.failcount >= (unsigned) opt->hardfail) {
183     /* NOTE: persistent softfail stops working */
184     fc = OTP_FC_FAIL_HARD;
185   } else if (opt->softfail && user_state.authtime == INT32_MAX) {
186     fc = OTP_FC_FAIL_SOFT;
187   } else if (opt->softfail &&
188              user_state.failcount >= (unsigned) opt->softfail) {
189     uint32_t when;
190     int fcount;
191
192     /*
193      * Determine the next time this user can authenticate.
194      *
195      * Once we hit softfail, we introduce a 1m delay before the user
196      * can authenticate.  For each successive failed authentication,
197      * we double the delay time, up to a max of 32 minutes.  While in
198      * the "delay mode" of operation, all authentication ATTEMPTS are
199      * considered failures.  Also, each attempt during the delay period
200      * restarts the clock.
201      *
202      * The advantage of a delay instead of a simple lockout is that an
203      * attacker can't lock out a user as easily; the user need only wait
204      * a bit before he can authenticate.
205      *
206      * Note that if the user waits for the delay period to expire, he
207      * is no longer in softfail and can only use ewindow, not rwindow.
208      */
209     fcount = user_state.failcount - opt->softfail;
210     /*
211      * when and authtime are uint32, but time is saved as int32,
212      * so this can't overflow
213      */
214     when = user_state.authtime +
215            (fcount > 5 ? 32 * 60 : (1 << fcount) * 60);
216     if ((uint32_t) now < when)
217       fc = OTP_FC_FAIL_SOFT;
218     else
219       fc = OTP_FC_FAIL_NONE;
220   } else {
221     fc = OTP_FC_FAIL_NONE;
222   }
223
224 async_response:
225   /*
226    * Test async response.
227    */
228   if (*challenge && (card_info.featuremask & OTP_CF_AM) && opt->allow_async) {
229     ssize_t chal_len;
230
231     /* Perform any site-specific transforms of the challenge. */
232     if (opt->site_transform) {
233       if ((chal_len = otp_challenge_transform(username,
234                                               challenge, opt->chal_len)) < 0) {
235         otp_log(OTP_LOG_ERR, "%s: %s: challenge transform failed for [%s]",
236                 log_prefix, __func__, username);
237         rc = OTP_RC_SERVICE_ERR;
238         goto auth_done_service_err;
239         /* NB: state not updated. */
240       }
241     } else {
242       chal_len = opt->chal_len;
243     }
244
245     /* Calculate the async response. */
246     if (card_info.cardops->response(&card_info, challenge, chal_len,
247                                     &e_response[pin_offset],
248                                     log_prefix) != 0) {
249       otp_log(OTP_LOG_ERR,
250               "%s: %s: unable to calculate async response for [%s], "
251               "to challenge %s", log_prefix, __func__, username, challenge);
252       rc = OTP_RC_SERVICE_ERR;
253       goto auth_done_service_err;
254       /* NB: state not updated. */
255     }
256
257     /* NOTE: We do not display the PIN. */
258     if (opt->debug) {
259       char s[OTP_MAX_CHALLENGE_LEN * 2 + 1];
260
261       otp_log(OTP_LOG_DEBUG,
262               "%s: %s: [%s], async challenge %s, expecting response %s",
263               log_prefix, __func__, username,
264               card_info.cardops->printchallenge(s, challenge, chal_len),
265               &e_response[pin_offset]);
266     }
267
268     /* Add PIN if needed. */
269     if (!opt->prepend_pin)
270       (void) strcat(e_response, card_info.pin);
271
272     /* Test user-supplied passcode. */
273     if (passcode)
274       nmatch = strcmp(passcode, e_response);
275     else
276       nmatch = cmpfunc(data, e_response, log_prefix);
277     if (!nmatch) {
278       if (!opt->allow_async) {
279         otp_log(OTP_LOG_AUTH, "%s: %s: bad async auth for [%s]: "
280                               "valid but disallowed by config",
281                 log_prefix, __func__, username);
282         rc = OTP_RC_AUTH_ERR;
283         goto auth_done;
284       }
285       if (fc == OTP_FC_FAIL_HARD) {
286         otp_log(OTP_LOG_AUTH,
287                 "%s: %s: bad async auth for [%s]: valid but in hardfail "
288                 " (%d/%d failed/max)", log_prefix, __func__, username,
289                 user_state.failcount, opt->hardfail);
290         rc = OTP_RC_MAXTRIES;
291         goto auth_done;
292       }
293       if (fc == OTP_FC_FAIL_SOFT) {
294         otp_log(OTP_LOG_AUTH,
295                 "%s: %s: bad async auth for [%s]: valid but in softfail "
296                 " (%d/%d failed/max)", log_prefix, __func__, username,
297                 user_state.failcount, opt->softfail);
298         rc = OTP_RC_MAXTRIES;
299         goto auth_done;
300       }
301 #ifdef FREERADIUS
302       if ((uint32_t) now - user_state.authtime < (unsigned) opt->chal_delay) {
303         otp_log(OTP_LOG_AUTH,
304                 "%s: %s: bad async auth for [%s]: valid but too soon",
305                 log_prefix, __func__, username);
306         rc = OTP_RC_MAXTRIES;
307         goto auth_done;
308       }
309 #endif
310
311       /* Authenticated in async mode. */
312       rc = OTP_RC_OK;
313       /* special log message for sync users */
314       if (card_info.featuremask & OTP_CF_SM)
315         otp_log(OTP_LOG_INFO, "%s: %s: [%s] authenticated in async mode",
316                 log_prefix, __func__, username);
317       goto auth_done;
318     } /* if (user authenticated async) */
319   } /* if (async mode possible) */
320
321 sync_response:
322   /*
323    * Calculate and test sync responses in the window.  Note that we
324    * always accept a sync response, even if a challenge or resync was
325    * explicitly requested.
326    *
327    * Note that we always start at the t=0 e=0 window position, even
328    * though we may already know a previous authentication is further
329    * ahead in the window (when in softfail).  This has the effect that
330    * an rwindow softfail override can occur in a sequence like 6,3,4.
331    * That is, the user can always move backwards in the window to
332    * restart the rwindow sequence, as long as they don't go beyond
333    * (prior to) the last successful authentication.
334    */
335   if ((card_info.featuremask & OTP_CF_SM) && opt->allow_sync) {
336     int tend, end, ewindow, rwindow;
337
338     /* set ending ewin counter */
339     if (card_info.featuremask & OTP_CF_FRW) {
340       /* force rwindow for e+t cards */
341       rwindow = (card_info.featuremask & OTP_CF_FRW) >> OTP_CF_FRW_SHIFT;
342       ewindow = 0; /* quiet compiler */
343     } else {
344       ewindow = opt->ewindow_size;
345       /* Increase window for softfail+rwindow. */
346       if (opt->rwindow_size && fc == OTP_FC_FAIL_SOFT)
347         rwindow = opt->rwindow_size;
348       else
349         rwindow = 0;
350     }
351     end = rwindow ? rwindow : ewindow;
352
353     /* Setup initial challenge. */
354     (void) memcpy(challenge, user_state.challenge, user_state.clen);
355
356     /* Test each sync response in the window. */
357     tend = card_info.cardops->maxtwin(&card_info, user_state.csd);
358     for (t = 0; t <= tend; ++t) {
359       for (e = 0; e <= end; ++e) {
360         /* Get next challenge. */
361         rc = card_info.cardops->challenge(&card_info, &user_state, challenge,
362                                           now, t, e, log_prefix);
363         if (rc == -1) {
364           otp_log(OTP_LOG_ERR,
365                   "%s: %s: unable to get sync challenge t:%d e:%d for [%s]",
366                   log_prefix, __func__, t, e, username);
367           rc = OTP_RC_SERVICE_ERR;
368           goto auth_done_service_err;
369           /* NB: state not updated. */
370         } else if (rc == -2) {
371           /*
372            * For event synchronous modes, we can never go backwards (the
373            * challenge() method can only walk forward on the event counter),
374            * so there is an implicit guarantee that we'll never get a
375            * response matching an event in the past.
376            *
377            * But for time synchronous modes, challenge() can walk backwards,
378            * in order to accomodate clock drift.  We must never allow a
379            * successful auth for a correct passcode earlier in time than
380            * one already used successfully, so we skip out early here.
381            */
382           if (opt->debug)
383             otp_log(OTP_LOG_DEBUG,
384                     "%s: %s: [%s], sync challenge t:%d e:%d is early",
385                     log_prefix, __func__, username, t, e);
386           continue;
387         }
388
389         /* Calculate sync response. */
390         if (card_info.cardops->response(&card_info, challenge, user_state.clen,
391                                         &e_response[pin_offset],
392                                         log_prefix) != 0) {
393           otp_log(OTP_LOG_ERR,
394                   "%s: %s: unable to calculate sync response "
395                   "t:%d e:%d for [%s], to challenge %s",
396                   log_prefix, __func__, t, e, username, challenge);
397           rc = OTP_RC_SERVICE_ERR;
398           goto auth_done_service_err;
399           /* NB: state not updated. */
400         }
401
402         /* NOTE: We do not display the PIN. */
403         if (opt->debug) {
404           char s[OTP_MAX_CHALLENGE_LEN * 2 + 1];
405
406           otp_log(OTP_LOG_DEBUG, "%s: %s: [%s], sync challenge t:%d e:%d %s, "
407                                  "expecting response %s",
408                   log_prefix, __func__, username, t, e,
409                   card_info.cardops->printchallenge(s, challenge,
410                                                     user_state.clen),
411                   &e_response[pin_offset]);
412         }
413
414         /* Add PIN if needed. */
415         if (!opt->prepend_pin)
416           (void) strcat(e_response, card_info.pin);
417
418         /* Test user-supplied passcode. */
419         if (passcode)
420           nmatch = strcmp(passcode, e_response);
421         else
422           nmatch = cmpfunc(data, e_response, log_prefix);
423         if (!nmatch) {
424           if (fc == OTP_FC_FAIL_HARD) {
425             otp_log(OTP_LOG_AUTH,
426                     "%s: %s: bad sync auth for [%s]: valid but in hardfail "
427                     " (%d/%d failed/max)", log_prefix, __func__, username,
428                     user_state.failcount, opt->hardfail);
429             rc = OTP_RC_MAXTRIES;
430           } else if (fc == OTP_FC_FAIL_SOFT) {
431             /*
432              * rwindow logic
433              */
434             if (!rwindow) {
435               /* rwindow mode not configured */
436               otp_log(OTP_LOG_AUTH,
437                       "%s: %s: bad sync auth for [%s]: valid but in softfail "
438                       " (%d/%d failed/max)", log_prefix, __func__, username,
439                       user_state.failcount, opt->softfail);
440               rc = OTP_RC_MAXTRIES;
441               goto auth_done;
442             }
443
444             /*
445              * User must enter two consecutive correct sync passcodes
446              * for rwindow softfail override.
447              */
448             if (card_info.cardops->isconsecutive(&card_info, &user_state,
449                                                  e, log_prefix)) {
450               /*
451                * This is the 2nd of two consecutive responses, is it
452                * soon enough?  Note that for persistent softfail we can't
453                * enforce rwindow_delay b/c the previous authtime is also
454                * the persistent softfail sentinel.
455                */
456               if (user_state.authtime == INT32_MAX ||
457                   now - user_state.authtime < (unsigned) opt->rwindow_delay) {
458                 otp_log(OTP_LOG_AUTH,
459                         "%s: %s: rwindow softfail override for [%s] at "
460                         "window position t:%d e:%d", log_prefix, __func__,
461                         username, t, e);
462                 rc = OTP_RC_OK;
463                 goto sync_done;
464               } else {
465                 /* consecutive but not soon enough */
466                 otp_log(OTP_LOG_AUTH,
467                         "%s: %s: late override for [%s] at "
468                         "window position t:%d e:%d", log_prefix, __func__,
469                         username, t, e);
470                 rc = OTP_RC_AUTH_ERR;
471               }
472             } /* if (passcode is consecutive */
473
474             /* passcode correct, but not consecutive or not soon enough */
475             if (opt->debug)
476               otp_log(OTP_LOG_DEBUG,
477                       "%s: %s: [%s] rwindow candidate "
478                       "at window position t:%d e:%d", log_prefix, __func__,
479                       username, t, e);
480             rc = OTP_RC_AUTH_ERR;
481
482           } else {
483             /* normal sync mode auth */
484             rc = OTP_RC_OK;
485           } /* else (!hardfail && !softfail) */
486
487 sync_done:
488           /* force resync; this only has an effect if (rc == OTP_RC_OK) */
489           resync = 1;
490           /* update csd (et al.) on successful auth or rwindow candidate */
491           if (card_info.cardops->updatecsd(&user_state, now, t, e, rc) != 0) {
492             otp_log(OTP_LOG_ERR, "%s: %s: unable to update csd for [%s]",
493                     log_prefix, __func__, username);
494             rc = OTP_RC_SERVICE_ERR;
495             goto auth_done_service_err;
496             /* NB: state not updated. */
497           }
498           goto auth_done;
499
500         } /* if (passcode is valid) */
501       } /* for (each slot in the ewindow) */
502     } /* for (each slot in the twindow) */
503   } /* if (sync mode possible) */
504
505   /* Both async and sync mode failed. */
506   rc = OTP_RC_AUTH_ERR;
507
508 auth_done:
509   if (rc == OTP_RC_OK) {
510     if (resync)
511       (void) memcpy(user_state.challenge, challenge, user_state.clen);
512     user_state.failcount   = 0;
513     user_state.authtime    = now;
514   } else {
515     if (++user_state.failcount == UINT_MAX)
516       user_state.failcount--;
517     /* authtime set to INT32_MAX by nullstate(); leave it to force softfail */
518     if (user_state.authtime != INT32_MAX)
519       user_state.authtime = (int32_t) now;      /* cast prevents overflow */
520     /*
521      * Note that we don't update the challenge.  Even for softfail (where
522      * we might have actually had a good auth), we want the challenge
523      * unchanged because we always start our sync loop at e=0 t=0 (and so
524      * challenge must stay as the 0'th challenge regardless of next valid
525      * window position, because the challenge() method can't return
526      * arbitrary event window positions--since we start at e=0 the challenge
527      * must be the 0th challenge, i.e. unchanged).
528      */
529   } /* else (rc != OTP_RC_OK) */
530   user_state.updated = 1;
531
532 auth_done_service_err:  /* exit here for system errors */
533   /*
534    * Release and update state.
535    *
536    * We "fail-out" if we can't do this, because for sync mode the
537    * response can be reused until state data is updated, an obvious
538    * replay attack.
539    *
540    * For async mode with RADIUS, if we can't update the last auth
541    * time, we will be open to a less obvious replay attack over the
542    * lifetime of the State attribute (opt->chal_delay): if someone
543    * that can see RADIUS traffic captures an Access-Request containing
544    * a State attribute, and can cause the NAS to cycle the request id
545    * within opt->chal_delay secs, then they can login to the NAS and
546    * insert the captured State attribute into the new Access-Request,
547    * and we'll give an Access-Accept.
548    */
549   if (user_state.locked) {
550     if (otp_state_put(username, &user_state, log_prefix) != 0) {
551       otp_log(OTP_LOG_ERR, "%s: %s: unable to put state for [%s]",
552               log_prefix, __func__, username);
553       rc = OTP_RC_SERVICE_ERR;  /* no matter what it might have been */
554     }
555   }
556
557   return rc;
558 }