- Add nexttwin() cardops method, for help with evaluating
authorfcusack <fcusack>
Thu, 29 Sep 2005 04:55:10 +0000 (04:55 +0000)
committerfcusack <fcusack>
Thu, 29 Sep 2005 04:55:10 +0000 (04:55 +0000)
          consecutive passwords.  Each cardops module has to implement its
          own method of "walking" the time (twin) counter, for now anyway.
        - Add twin2authtime() cardops method, for help ensuring that a
          passcode in the past (negative twin) is not earlier than the
          most recently seen passcode.  Remove comment insisting that
          the challenge() or response() method needed to implement this;
          that would have been awkward.
        - Add minauthtime state field, to support twin2authtime() change.
        - Change authtime state field to %x from %d.
        - Leave state at version 3.
        - Add 'when' arg to challenge() cardops method, to ensure that
          sucessive challenges (as twin changes) are relative to the
          same t=0 time.

src/modules/rlm_otp/cardops/cryptocard.c
src/modules/rlm_otp/cardops/cryptocard.h
src/modules/rlm_otp/otp.h
src/modules/rlm_otp/otp_cardops.c
src/modules/rlm_otp/otp_cardops.h
src/modules/rlm_otp/otp_state.c

index 2238c53..cd9d85e 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 #include <string.h>
+#include <time.h>
 
 #include "../otp.h"
 #include "../otp_cardops.h"
@@ -117,6 +118,10 @@ cryptocard_challenge(const otp_user_info_t *user_info,
 __attribute__ ((unused))
 #endif
                      const char csd[OTP_MAX_CSD_LEN + 1],
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+                    time_t when,
                      char challenge[OTP_MAX_CHALLENGE_LEN + 1],
 #ifdef __GNUC__
 __attribute__ ((unused))
@@ -223,6 +228,40 @@ __attribute__ ((unused))
   return 0;
 }
 
+/* no twin so just return 0 */
+static int
+cryptocard_nexttwin(
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+                    int twin)
+{
+  return 0;
+}
+
+/* no twin so just return success */
+static time_t
+cryptocard_twin2authtime(
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+                         const char csd[OTP_MAX_CSD_LEN + 1],
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+                         time_t when,
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+                         int twin,
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+                         const char *log_prefix)
+{
+  return 0;
+}
+
 
 /* cardops instance */
 static cardops_t cryptocard_cardops = {
@@ -234,7 +273,9 @@ static cardops_t cryptocard_cardops = {
   .nullstate           = cryptocard_nullstate,
   .challenge           = cryptocard_challenge,
   .response            = cryptocard_response,
-  .updatecsd           = cryptocard_updatecsd
+  .updatecsd           = cryptocard_updatecsd,
+  .nexttwin            = cryptocard_nexttwin,
+  .twin2authtime       = cryptocard_twin2authtime
 };
 
 /* constructor */
index 10d81c8..687fd7a 100644 (file)
@@ -43,13 +43,17 @@ static int cryptocard_keystring2keyblock(const char *, unsigned char []);
 static int cryptocard_nullstate(const otp_option_t *, const otp_user_info_t *,
                                 otp_user_state_t *, const char *);
 static int cryptocard_challenge(const otp_user_info_t *,
-                                const char [OTP_MAX_CSD_LEN + 1],
+                                const char [OTP_MAX_CSD_LEN + 1], time_t,
                                 char [OTP_MAX_CHALLENGE_LEN + 1],
                                 unsigned, const char *);
 static int cryptocard_response(otp_user_info_t *, char *, const char *,
-                               char [OTP_MAX_RESPONSE_LEN + 1], const char *);
+                               char [OTP_MAX_RESPONSE_LEN + 1],
+                               const char *);
 static int cryptocard_updatecsd(const otp_user_info_t *, otp_user_state_t *,
                                 const char *);
+static int cryptocard_nexttwin(int);
+static time_t cryptocard_twin2authtime(const char [OTP_MAX_CSD_LEN + 1],
+                                       time_t, int, const char *);
 
 #ifdef __GNUC__
 __attribute__ ((constructor))
index 4bda340..4012ff1 100644 (file)
@@ -147,6 +147,7 @@ typedef struct otp_user_state_t {
   int          authewin;               /* ewindow position for softfail  */
   int          authtwin;               /* twindow position for softfail  */
   uint32_t     authtime;               /* time of last auth              */
+  uint32_t     minauthtime;            /* minimum authtime               */
 } otp_user_state_t;
 
 /* fc (failcondition) shortcuts */
index 0af82e1..65cf720 100644 (file)
@@ -21,7 +21,6 @@
  *
  * Copyright 2002-2005  Google, Inc.
  * Copyright 2005 TRI-D Systems, Inc.
- *
  */
 
 static const char rcsid[] = "$Id$";
@@ -61,7 +60,7 @@ isconsecutive(const otp_option_t *opt, const otp_user_info_t *user_info,
   if (frw && user_state->authewin == frw) {
     /* ewin already maxed, reset and advance twin */
     nextewin = 0;
-    nexttwin = user_state->authtwin + 1;
+    nexttwin = user_info->cardops->nexttwin(user_state->authtwin);
   } else {
     nextewin = user_state->authewin + 1;
     nexttwin = user_state->authtwin;
@@ -369,21 +368,7 @@ sync_response:
     /* Test each sync response in the window. */
     for (t = 0; t <= (user_info.featuremask & OTP_CF_TW) * 2; ++t) {
       for (e = 0; e <= end; ++e) {
-        /*
-         * Calculate sync response.
-         *
-         * For event synchronous modes, we can never go backwards (the
-         * challenge() method can only walk forwards), so there is an
-         * implicit guarantee that we'll never get a response matching
-         * an event in the past.
-         *
-         * But for time synchronous modes, challenge() can walk backwards,
-         * in order to accomodate clock drift.  The cardops object must
-         * not return a valid response for a time earlier than one at
-         * which the user already authenticated.  This can be done either
-         * with challenge() (never return a challenge that is too early)
-         * or response() (never validate a challenge that is too early).
-         */
+        /* Calculate sync response. */
         if (user_info.cardops->response(&user_info, csd, challenge,
                                         &e_response[pin_offset],
                                         log_prefix) != 0) {
@@ -424,6 +409,27 @@ sync_response:
                     " (%d/%d failed/max)", log_prefix, username,
                     user_state.failcount, opt->hardfail);
             rc = OTP_RC_MAXTRIES;
+          } else if (user_info.cardops->twin2authtime(csd, now, t, log_prefix)<
+                     user_state.minauthtime) {
+            /*
+             * For event synchronous modes, we can never go backwards (the
+             * challenge() method can only walk forwards), so there is an
+             * implicit guarantee that we'll never get a response matching
+             * an event in the past.
+             *
+             * But for time synchronous modes, challenge() can walk backwards,
+             * in order to accomodate clock drift.  We must never allow a
+             * successful auth for a correct passcode earlier in time than
+             * one already used successfully.
+             *
+             * We might move this test to before the response() call,
+             * to avoid the crypto.  We lose the report about old
+             * passcodes, but this is the case anyway for event sync.
+             */
+            otp_log(OTP_LOG_AUTH,
+                    "%s: bad sync auth for [%s]: valid but in the past",
+                    log_prefix, username);
+            rc = OTP_RC_AUTH_ERR;
           } else if (fc == OTP_FC_FAIL_SOFT) {
             /*
              * rwindow logic
@@ -467,7 +473,7 @@ sync_response:
           } else {
             /* normal sync mode auth */
             rc = OTP_RC_OK;
-          } /* else (!hardfail && !softfail) */
+          } /* else (!hardfail && !too_early && !softfail) */
 
           /* force resync; this only has an effect if (rc == OTP_RC_OK) */
           resync = 1;
@@ -486,7 +492,7 @@ sync_response:
         } /* if (passcode is valid) */
 
         /* Get next challenge (extra work at end of failing loop;TODO: fix). */
-        if (user_info.cardops->challenge(&user_info, csd, challenge,
+        if (user_info.cardops->challenge(&user_info, csd, now, challenge,
                                          t, log_prefix) != 0) {
           otp_log(OTP_LOG_ERR,
                   "%s: unable to get sync challenge t:%d e:%d for [%s]",
@@ -506,7 +512,7 @@ auth_done:
   if (rc == OTP_RC_OK) {
     if (resync) {
       /* Resync the card (update the challenge). */
-      if (user_info.cardops->challenge(&user_info, csd, challenge,
+      if (user_info.cardops->challenge(&user_info, csd, now, challenge,
                                        t, log_prefix) != 0) {
         otp_log(OTP_LOG_ERR, "%s: unable to get sync challenge "
                              "t:%d e:%d for [%s] (for resync)",
@@ -521,6 +527,9 @@ auth_done:
     user_state.authewin  = 0;
     user_state.authtwin  = 0;
     user_state.authtime  = (int32_t) now;      /* cast prevents overflow */
+    /* NOTE: csd is the value before calling updatecsd() */
+    user_state.minauthtime =
+      (int32_t) user_info.cardops->twin2authtime(csd, now, t, log_prefix);
   } else {
     /*
      * Note that we initialized auth[et]win to -2 to accomodate rwindow test
index bc73bb5..a8dfcfc 100644 (file)
@@ -62,11 +62,13 @@ typedef struct cardops_t {
   int (*keystring2keyblock)(const char *, unsigned char []);
   int (*nullstate)(const otp_option_t *, const otp_user_info_t *,
                    otp_user_state_t *, const char *);
-  int (*challenge)(const otp_user_info_t *, const char [], char [], unsigned,
-                   const char *);
+  int (*challenge)(const otp_user_info_t *, const char [], time_t, char [],
+                   unsigned, const char *);
   int (*response)(otp_user_info_t *, char *, const char *,
                   char [OTP_MAX_RESPONSE_LEN + 1], const char *);
   int (*updatecsd)(const otp_user_info_t *, otp_user_state_t *, const char *);
+  int (*nexttwin)(int);
+  int32_t (*twin2authtime)(const char [], time_t, int, const char *);
 } cardops_t;
 #define OTP_MAX_VENDORS 16
 extern cardops_t otp_cardops[OTP_MAX_VENDORS];
index 506c5c1..e3f8c97 100644 (file)
@@ -339,12 +339,28 @@ otp_state_parse(const char *buf, size_t buflen, const char *username,
     return -1;
   }
   *q++ = '\0';
-  if (sscanf(p, "%" SCNu32, &user_state->authtime) != 1) {
+  if (sscanf(p, "%" SCNx32, &user_state->authtime) != 1) {
     otp_log(OTP_LOG_ERR, "%s: state data invalid authtime for [%s]",
             log_prefix, username);
     (void) otp_state_put(username, user_state, log_prefix);
     return -1;
   }
+  p = q;       /* minauthtime */
+
+  /* extract minauthtime */
+  if ((q = strchr(p, ':')) == NULL) {
+    otp_log(OTP_LOG_ERR, "%s: state data invalid minauthtime for [%s]",
+            log_prefix, username);
+    (void) otp_state_put(username, user_state, log_prefix);
+    return -1;
+  }
+  *q++ = '\0';
+  if (sscanf(p, "%" SCNx32, &user_state->minauthtime) != 1) {
+    otp_log(OTP_LOG_ERR, "%s: state data invalid minauthtime for [%s]",
+            log_prefix, username);
+    (void) otp_state_put(username, user_state, log_prefix);
+    return -1;
+  }
 
   return 0;
 }
@@ -364,11 +380,12 @@ otp_state_unparse(char *buf, size_t buflen, const char *username,
     return -1;
 
   if (user_state->updated)
-    (void) snprintf(buf, buflen, "P %s 3:%s:%s:%s:%u:%d:%d:%" PRIu32 ":",
+    (void) snprintf(buf, buflen, "P %s 3:%s:%s:%s:%u:%d:%d:%" PRIx32 ":"
+                                 "%" PRIx32 ":",
                     username, username, user_state->challenge, user_state->csd,
                     user_state->failcount,
                     user_state->authewin, user_state->authtwin,
-                    user_state->authtime);
+                    user_state->authtime, user_state->minauthtime);
   else
     (void) snprintf(buf, buflen, "P %s", username);
   buf[buflen - 1] = '\0';