/**
 * oath_totp_validate:
 * @secret: the shared secret string
 * @secret_length: length of @secret
 * @now: Unix time value to validate TOTP for
 * @time_step_size: time step system parameter (typically 30)
 * @start_offset: Unix time of when to start counting time steps (typically 0)
 * @window: how many OTPs after/before start OTP to test
 * @otp: the OTP to validate.
 *
 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
 *
 * Currently only OTP lengths of 6, 7 or 8 digits are supported.  This
 * restrictions may be lifted in future versions, although some
 * limitations are inherent in the protocol.
 *
 * Returns: Returns absolute value of position in OTP window (zero is
 *   first position), or %OATH_INVALID_OTP if no OTP was found in OTP
 *   window, or an error code.
 *
 * Since: 1.6.0
 **/
int
oath_totp_validate (const char *secret,
		    size_t secret_length,
		    time_t now,
		    unsigned time_step_size,
		    time_t start_offset, size_t window, const char *otp)
{
  return oath_totp_validate2 (secret, secret_length, now, time_step_size,
			      start_offset, window, NULL, otp);
}
Beispiel #2
0
static int
parse_usersfile (const char *username,
		 const char *otp,
		 size_t window,
		 const char *passwd,
		 time_t * last_otp,
		 FILE * infh,
		 char **lineptr, size_t * n, uint64_t * new_moving_factor)
{
  while (getline (lineptr, n, infh) != -1)
    {
      char *saveptr;
      char *p = strtok_r (*lineptr, whitespace, &saveptr);
      unsigned digits, totpstepsize;
      char secret[32];
      size_t secret_length = sizeof (secret);
      uint64_t start_moving_factor = 0;
      int rc;
      char *prev_otp = NULL;

      if (p == NULL)
	continue;

      /* Read token type */
      if (parse_type (p, &digits, &totpstepsize) != 0)
	  continue;

      /* Read username */
      p = strtok_r (NULL, whitespace, &saveptr);
      if (p == NULL || strcmp (p, username) != 0)
	continue;

      /* Read password. */
      p = strtok_r (NULL, whitespace, &saveptr);
      if (passwd)
	{
	  if (p == NULL)
	    continue;
	  if (strcmp (p, "-") == 0)
	    {
	      if (*passwd != '\0')
		return OATH_BAD_PASSWORD;
	    }
	  else if (strcmp (p, passwd) != 0)
	    return OATH_BAD_PASSWORD;
	}

      /* Read key. */
      p = strtok_r (NULL, whitespace, &saveptr);
      if (p == NULL)
	continue;
      rc = oath_hex2bin (p, secret, &secret_length);
      if (rc != OATH_OK)
	return rc;

      /* Read (optional) moving factor. */
      p = strtok_r (NULL, whitespace, &saveptr);
      if (p && *p)
	{
	  char *endptr;
	  unsigned long long int ull = strtoull (p, &endptr, 10);
	  if (endptr && *endptr != '\0')
	    return OATH_INVALID_COUNTER;
	  start_moving_factor = ull;
	}

      /* Read (optional) last OTP */
      prev_otp = strtok_r (NULL, whitespace, &saveptr);

      /* Read (optional) last_otp */
      p = strtok_r (NULL, whitespace, &saveptr);
      if (p)
	{
	  struct tm tm;
	  char *ts;

	  ts = strptime (p, TIME_FORMAT_STRING, &tm);
	  if (ts == NULL || *ts != '\0')
	    return OATH_INVALID_TIMESTAMP;
	  tm.tm_isdst = -1;
	  if (last_otp)
	    {
	      *last_otp = mktime (&tm);
	      if (*last_otp == (time_t) - 1)
		return OATH_INVALID_TIMESTAMP;
	    }
	}

      if (prev_otp && strcmp (prev_otp, otp) == 0)
	return OATH_REPLAYED_OTP;

      if (totpstepsize == 0)
	rc = oath_hotp_validate (secret, secret_length,
				 start_moving_factor, window, otp);
      else if (prev_otp)
	{
	  int prev_otp_pos, this_otp_pos, tmprc;
	  rc = oath_totp_validate2 (secret, secret_length,
				    time (NULL), totpstepsize, 0, window,
				    &this_otp_pos, otp);
	  if (rc < 0)
	    return rc;
	  tmprc = oath_totp_validate2 (secret, secret_length,
				       time (NULL), totpstepsize, 0, window,
				       &prev_otp_pos, prev_otp);
	  if (tmprc >= 0 && prev_otp_pos >= this_otp_pos)
	    return OATH_REPLAYED_OTP;
	}
      else
	rc = oath_totp_validate (secret, secret_length,
				 time (NULL), totpstepsize, 0, window, otp);
      if (rc < 0)
	return rc;
      *new_moving_factor = start_moving_factor + rc;
      return OATH_OK;
    }

  return OATH_UNKNOWN_USER;
}