/** * 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); }
/** * 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); }
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; }