*/
#include <string.h>
+#include <time.h>
#include "../otp.h"
#include "../otp_cardops.h"
__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))
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 = {
.nullstate = cryptocard_nullstate,
.challenge = cryptocard_challenge,
.response = cryptocard_response,
- .updatecsd = cryptocard_updatecsd
+ .updatecsd = cryptocard_updatecsd,
+ .nexttwin = cryptocard_nexttwin,
+ .twin2authtime = cryptocard_twin2authtime
};
/* constructor */
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))
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 */
*
* Copyright 2002-2005 Google, Inc.
* Copyright 2005 TRI-D Systems, Inc.
- *
*/
static const char rcsid[] = "$Id$";
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;
/* 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) {
" (%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
} 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;
} /* 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]",
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)",
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
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];
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;
}
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';