Пример #1
0
/**
 * oath_totp_validate2:
 * @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_pos: output search position in search window (may be NULL).
 * @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.10.0
 **/
int
oath_totp_validate2 (const char *secret,
		     size_t secret_length,
		     time_t now,
		     unsigned time_step_size,
		     time_t start_offset,
		     size_t window, int *otp_pos, const char *otp)
{
  return oath_totp_validate2_callback (secret, secret_length, now,
				       time_step_size, start_offset,
				       strlen (otp), window, otp_pos,
				       _oath_strcmp_callback, (void *) otp);
}
Пример #2
0
/**
 * oath_totp_validate_callback:
 * @secret: the shared secret string
 * @secret_length: length of @secret
 * @now: Unix time value to compute 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 start counter to test
 * @digits: number of requested digits in the OTP
 * @strcmp_otp: function pointer to a strcmp-like function.
 * @strcmp_handle: caller handle to be passed on to @strcmp_otp.
 *
 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
 *
 * Validation is implemented by generating a number of potential OTPs
 * and performing a call to the @strcmp_otp function, to compare the
 * potential OTP against the given @otp.  It has the following
 * prototype:
 *
 * int (*oath_validate_strcmp_function) (void *handle, const char *test_otp);
 *
 * The function should be similar to strcmp in that it return 0 only
 * on matches.  It differs by permitting use of negative return codes
 * as indication of internal failures in the callback.  Positive
 * values indicate OTP mismatch.
 *
 * This callback interface is useful when you cannot compare OTPs
 * directly using normal strcmp, but instead for example only have a
 * hashed OTP.  You would then typically pass in the hashed OTP in the
 * @strcmp_handle and let your implementation of @strcmp_otp hash the
 * test_otp OTP using the same hash, and then compare the results.
 *
 * 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 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_callback (const char *secret,
			     size_t secret_length,
			     time_t now,
			     unsigned time_step_size,
			     time_t start_offset,
			     unsigned digits,
			     size_t window,
			     oath_validate_strcmp_function strcmp_otp,
			     void *strcmp_handle)
{
  return oath_totp_validate2_callback (secret, secret_length, now,
				       time_step_size, start_offset,
				       digits, window, NULL,
				       strcmp_otp, strcmp_handle);
}
Пример #3
0
dynalogin_result_t dynalogin_authenticate_internal
	(dynalogin_session_t *h, const dynalogin_userid_t userid,
			dynalogin_scheme_t scheme,
			struct oath_callback_pvt_t *pvt,
			oath_validate_strcmp_function strcmp_otp)
{
	int rc;
	dynalogin_user_data_t *ud;
	dynalogin_result_t res;
	time_t now;
	dynalogin_counter_t _now, next_counter;
	int fail_inc = 1, totp_offset;

	if(h == NULL || userid == NULL || pvt == NULL)
		return DYNALOGIN_ERROR;

	h->datasource->user_fetch(&ud, userid, h->pool);
	if(ud == NULL)
	{
		syslog(LOG_ERR, "userid not found: %s", userid);
		return DYNALOGIN_DENY;
	}

	if(ud->scheme != scheme)
	{
		syslog(LOG_ERR, "scheme mismatch for user %s (expected %s)",
				userid, get_scheme_name(ud->scheme));
		return DYNALOGIN_DENY;
	}

	if(ud->locked != 0)
	{
		syslog(LOG_ERR, "account locked: %s", userid);
		return DYNALOGIN_DENY;
	}

	pvt->password = ud->password;

	if(apr_pool_create(&(pvt->pool), h->pool) != APR_SUCCESS)
	{
		return DYNALOGIN_ERROR;
	}

	switch(scheme)
	{
	case HOTP:
		rc = oath_hotp_validate_callback (
				ud->secret,
				strlen(ud->secret),
				ud->counter,
				h->hotp_window, h->hotp_digits,
				strcmp_otp,
				pvt);
		next_counter = ud->counter + (rc + 1);
		break;
	case TOTP:
		time(&now);
		rc = oath_totp_validate2_callback (
				ud->secret,
				strlen(ud->secret),
				now,
				h->totp_x,
				h->totp_t0,
				h->totp_digits,
				h->totp_window,
				&totp_offset,
				strcmp_otp,
				pvt);

		/* we use totp_offset here because the rc from
		   oath_totp_validate2_callback is a negative value in case
		   of error, but negative offsets are valid with TOTP */
		_now = (now - h->totp_t0) / h->totp_x;
		if((_now + totp_offset) >= ud->counter)
		{
			next_counter = _now + totp_offset + 1;
		}
		else
		{
			fail_inc = 0;
			syslog(LOG_WARNING, "Token replay detected, denying authentication");
			rc = OATH_REPLAYED_OTP;
		}
		/* totp_offset only contains a valid value if the OTP was 
		   inside the specified window - in that case the rc is the 
		   absolute offset */
		if(rc>=0 && totp_offset < 0) 
		{
		    syslog(LOG_WARNING, "TOTP validation returned offset %d (~%d seconds behind)",totp_offset,rc*h->totp_x);
		}
		else if(rc > 0)
		{
		    syslog(LOG_WARNING, "TOTP validation returned offset %d (~%d seconds ahead)",totp_offset,rc*h->totp_x);
		}
		break;
	default:
		syslog(LOG_ERR, "unsupported scheme");
		fail_inc = 0;
		rc = -1;
	}
	apr_pool_destroy(pvt->pool);
	if(rc < 0)
	{
		if(ud->failure_count > 3)
        	{
        		ud->locked = 1;
        		res = DYNALOGIN_DENY;
        	}
        	else
        	{
        		ud->failure_count += fail_inc;
        		res = DYNALOGIN_DENY;
        	}
	}
	else
	{
		ud->counter = next_counter;
		ud->failure_count = 0;
		time(&ud->last_success);
		ud->last_code = pvt->validated_code;
		res = DYNALOGIN_SUCCESS;
	}
	time(&ud->last_attempt);
	h->datasource->user_update(ud, h->pool);
	return res;
}