Search in sources :

Example 1 with OTPUserRecord

use of password.pwm.util.operations.otp.OTPUserRecord in project pwm by pwm-project.

the class UserInfoReader method isRequiresOtpConfig.

@Override
public boolean isRequiresOtpConfig() throws PwmUnrecoverableException {
    LOGGER.trace(sessionLabel, "checkOtp: beginning process to check if user OTP setup is required");
    SetupOtpProfile setupOtpProfile = null;
    final Map<ProfileType, String> profileIDs = selfCachedReference.getProfileIDs();
    if (profileIDs.containsKey(ProfileType.UpdateAttributes)) {
        setupOtpProfile = pwmApplication.getConfig().getSetupOTPProfiles().get(profileIDs.get(ProfileType.SetupOTPProfile));
    }
    if (setupOtpProfile == null) {
        LOGGER.trace(sessionLabel, "checkOtp: no otp setup profile assigned, user OTP setup is not required");
        return false;
    }
    if (!setupOtpProfile.readSettingAsBoolean(PwmSetting.OTP_ALLOW_SETUP)) {
        LOGGER.trace(sessionLabel, "checkOtp: OTP allow setup is not enabled");
        return false;
    }
    final ForceSetupPolicy policy = setupOtpProfile.readSettingAsEnum(PwmSetting.OTP_FORCE_SETUP, ForceSetupPolicy.class);
    if (policy == ForceSetupPolicy.SKIP) {
        LOGGER.trace(sessionLabel, "checkOtp: OTP force setup policy is set to SKIP, user OTP setup is not required");
        return false;
    }
    final OTPUserRecord otpUserRecord = selfCachedReference.getOtpUserRecord();
    final boolean hasStoredOtp = otpUserRecord != null && otpUserRecord.getSecret() != null;
    if (hasStoredOtp) {
        LOGGER.trace(sessionLabel, "checkOtp: user has existing valid otp record, user OTP setup is not required");
        return false;
    }
    // hasStoredOtp is always true at this point, so if forced then update needed
    LOGGER.debug(sessionLabel, "checkOtp: user does not have existing valid otp record, user OTP setup is required");
    return policy == ForceSetupPolicy.FORCE || policy == ForceSetupPolicy.FORCE_ALLOW_SKIP;
}
Also used : ProfileType(password.pwm.config.profile.ProfileType) SetupOtpProfile(password.pwm.config.profile.SetupOtpProfile) ForceSetupPolicy(password.pwm.config.option.ForceSetupPolicy) OTPUserRecord(password.pwm.util.operations.otp.OTPUserRecord)

Example 2 with OTPUserRecord

use of password.pwm.util.operations.otp.OTPUserRecord in project pwm by pwm-project.

the class HelpdeskServlet method restValidateOtpCodeRequest.

@ActionHandler(action = "validateOtpCode")
private ProcessStatus restValidateOtpCodeRequest(final PwmRequest pwmRequest) throws IOException, PwmUnrecoverableException, ServletException, ChaiUnavailableException {
    final HelpdeskProfile helpdeskProfile = getHelpdeskProfile(pwmRequest);
    final Instant startTime = Instant.now();
    final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = JsonUtil.deserialize(pwmRequest.readRequestBodyAsString(), HelpdeskVerificationRequestBean.class);
    final String userKey = helpdeskVerificationRequestBean.getUserKey();
    if (userKey == null || userKey.isEmpty()) {
        final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, "userKey parameter is missing");
        pwmRequest.respondWithError(errorInformation, false);
        return ProcessStatus.Halt;
    }
    final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
    if (!helpdeskProfile.readOptionalVerificationMethods().contains(IdentityVerificationMethod.OTP)) {
        final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "password otp verification request, but otp verify is not enabled");
        LOGGER.error(pwmRequest, errorInformation);
        pwmRequest.respondWithError(errorInformation);
        return ProcessStatus.Halt;
    }
    final String code = helpdeskVerificationRequestBean.getCode();
    final OTPUserRecord otpUserRecord = pwmRequest.getPwmApplication().getOtpService().readOTPUserConfiguration(pwmRequest.getSessionLabel(), userIdentity);
    try {
        final boolean passed = pwmRequest.getPwmApplication().getOtpService().validateToken(pwmRequest.getSessionLabel(), userIdentity, otpUserRecord, code, false);
        final HelpdeskVerificationStateBean verificationStateBean = HelpdeskVerificationStateBean.fromClientString(pwmRequest, helpdeskVerificationRequestBean.getVerificationState());
        if (passed) {
            final PwmSession pwmSession = pwmRequest.getPwmSession();
            final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(AuditEvent.HELPDESK_VERIFY_OTP, pwmSession.getUserInfo().getUserIdentity(), null, userIdentity, pwmSession.getSessionStateBean().getSrcAddress(), pwmSession.getSessionStateBean().getSrcHostname());
            pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
            StatisticsManager.incrementStat(pwmRequest, Statistic.HELPDESK_VERIFY_OTP);
            verificationStateBean.addRecord(userIdentity, IdentityVerificationMethod.OTP);
        } else {
            final PwmSession pwmSession = pwmRequest.getPwmSession();
            final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(AuditEvent.HELPDESK_VERIFY_OTP_INCORRECT, pwmSession.getUserInfo().getUserIdentity(), null, userIdentity, pwmSession.getSessionStateBean().getSrcAddress(), pwmSession.getSessionStateBean().getSrcHostname());
            pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
        }
        // add a delay to prevent continuous checks
        final long delayMs = Long.parseLong(pwmRequest.getConfig().readAppProperty(AppProperty.HELPDESK_VERIFICATION_INVALID_DELAY_MS));
        while (TimeDuration.fromCurrent(startTime).isShorterThan(delayMs)) {
            JavaHelper.pause(100);
        }
        final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
        final RestResultBean restResultBean = RestResultBean.withData(responseBean);
        pwmRequest.outputJsonResult(restResultBean);
    } catch (PwmOperationalException e) {
        pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(), pwmRequest));
    }
    return ProcessStatus.Halt;
}
Also used : Instant(java.time.Instant) UserIdentity(password.pwm.bean.UserIdentity) HelpdeskProfile(password.pwm.config.profile.HelpdeskProfile) HelpdeskAuditRecord(password.pwm.svc.event.HelpdeskAuditRecord) PwmOperationalException(password.pwm.error.PwmOperationalException) ErrorInformation(password.pwm.error.ErrorInformation) AuditRecordFactory(password.pwm.svc.event.AuditRecordFactory) PwmSession(password.pwm.http.PwmSession) OTPUserRecord(password.pwm.util.operations.otp.OTPUserRecord) RestResultBean(password.pwm.ws.server.RestResultBean)

Example 3 with OTPUserRecord

use of password.pwm.util.operations.otp.OTPUserRecord in project pwm by pwm-project.

the class OtpService method readOTPUserConfiguration.

public OTPUserRecord readOTPUserConfiguration(final SessionLabel sessionLabel, final UserIdentity userIdentity) throws PwmUnrecoverableException, ChaiUnavailableException {
    OTPUserRecord otpConfig = null;
    final Configuration config = pwmApplication.getConfig();
    final Date methodStartTime = new Date();
    final List<DataStorageMethod> otpSecretStorageLocations = config.getOtpSecretStorageLocations(PwmSetting.OTP_SECRET_READ_PREFERENCE);
    if (otpSecretStorageLocations != null) {
        final String userGUID = readGuidIfNeeded(pwmApplication, sessionLabel, otpSecretStorageLocations, userIdentity);
        final Iterator<DataStorageMethod> locationIterator = otpSecretStorageLocations.iterator();
        while (otpConfig == null && locationIterator.hasNext()) {
            final DataStorageMethod location = locationIterator.next();
            final OtpOperator operator = operatorMap.get(location);
            if (operator != null) {
                try {
                    otpConfig = operator.readOtpUserConfiguration(userIdentity, userGUID);
                } catch (Exception e) {
                    LOGGER.error(sessionLabel, "unexpected error reading stored otp configuration from " + location + " for user " + userIdentity + ", error: " + e.getMessage());
                }
            } else {
                LOGGER.warn(sessionLabel, String.format("storage location %s not implemented", location.toString()));
            }
        }
    }
    LOGGER.trace(sessionLabel, "readOTPUserConfiguration completed in " + TimeDuration.fromCurrent(methodStartTime).asCompactString() + (otpConfig == null ? ", no otp record found" : ", recordType=" + otpConfig.getType() + ", identifier=" + otpConfig.getIdentifier() + ", timestamp=" + JavaHelper.toIsoDate(otpConfig.getTimestamp())));
    return otpConfig;
}
Also used : OtpOperator(password.pwm.util.operations.otp.OtpOperator) LocalDbOtpOperator(password.pwm.util.operations.otp.LocalDbOtpOperator) LdapOtpOperator(password.pwm.util.operations.otp.LdapOtpOperator) DbOtpOperator(password.pwm.util.operations.otp.DbOtpOperator) Configuration(password.pwm.config.Configuration) DataStorageMethod(password.pwm.config.option.DataStorageMethod) OTPUserRecord(password.pwm.util.operations.otp.OTPUserRecord) Date(java.util.Date) PwmUnrecoverableException(password.pwm.error.PwmUnrecoverableException) PwmException(password.pwm.error.PwmException) PwmOperationalException(password.pwm.error.PwmOperationalException) IOException(java.io.IOException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) ChaiUnavailableException(com.novell.ldapchai.exception.ChaiUnavailableException)

Example 4 with OTPUserRecord

use of password.pwm.util.operations.otp.OTPUserRecord in project pwm by pwm-project.

the class OtpService method validateToken.

public boolean validateToken(final SessionLabel sessionLabel, final UserIdentity userIdentity, final OTPUserRecord otpUserRecord, final String userInput, final boolean allowRecoveryCodes) throws PwmOperationalException, PwmUnrecoverableException {
    boolean otpCorrect = false;
    try {
        final Base32 base32 = new Base32();
        final byte[] rawSecret = base32.decode(otpUserRecord.getSecret());
        final Mac mac = Mac.getInstance("HMACSHA1");
        mac.init(new SecretKeySpec(rawSecret, ""));
        final PasscodeGenerator generator = new PasscodeGenerator(mac, settings.getOtpTokenLength(), settings.getTotpIntervalSeconds());
        switch(otpUserRecord.getType()) {
            case TOTP:
                otpCorrect = generator.verifyTimeoutCode(userInput, settings.getTotpPastIntervals(), settings.getTotpFutureIntervals());
                break;
            default:
                throw new UnsupportedOperationException("OTP type not supported: " + otpUserRecord.getType());
        }
    } catch (Exception e) {
        LOGGER.error(sessionLabel, "error checking otp secret: " + e.getMessage());
    }
    if (!otpCorrect && allowRecoveryCodes && otpUserRecord.getRecoveryCodes() != null && otpUserRecord.getRecoveryInfo() != null) {
        final OTPUserRecord.RecoveryInfo recoveryInfo = otpUserRecord.getRecoveryInfo();
        final String userHashedInput = doRecoveryHash(userInput, recoveryInfo);
        for (final OTPUserRecord.RecoveryCode code : otpUserRecord.getRecoveryCodes()) {
            if (code.getHashCode().equals(userInput) || code.getHashCode().equals(userHashedInput)) {
                if (code.isUsed()) {
                    throw new PwmOperationalException(PwmError.ERROR_OTP_RECOVERY_USED, "recovery code has been previously used");
                }
                code.setUsed(true);
                try {
                    pwmApplication.getOtpService().writeOTPUserConfiguration(null, userIdentity, otpUserRecord);
                } catch (ChaiUnavailableException e) {
                    throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_WRITING_OTP_SECRET, e.getMessage()));
                }
                otpCorrect = true;
            }
        }
    }
    return otpCorrect;
}
Also used : ChaiUnavailableException(com.novell.ldapchai.exception.ChaiUnavailableException) PwmUnrecoverableException(password.pwm.error.PwmUnrecoverableException) Mac(javax.crypto.Mac) PasscodeGenerator(password.pwm.util.operations.otp.PasscodeGenerator) PwmUnrecoverableException(password.pwm.error.PwmUnrecoverableException) PwmException(password.pwm.error.PwmException) PwmOperationalException(password.pwm.error.PwmOperationalException) IOException(java.io.IOException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) ChaiUnavailableException(com.novell.ldapchai.exception.ChaiUnavailableException) PwmOperationalException(password.pwm.error.PwmOperationalException) ErrorInformation(password.pwm.error.ErrorInformation) SecretKeySpec(javax.crypto.spec.SecretKeySpec) Base32(org.apache.commons.codec.binary.Base32) OTPUserRecord(password.pwm.util.operations.otp.OTPUserRecord)

Example 5 with OTPUserRecord

use of password.pwm.util.operations.otp.OTPUserRecord in project pwm by pwm-project.

the class RestVerifyOtpServer method doSetOtpDataJson.

@RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.json, produces = HttpContentType.json)
public RestResultBean doSetOtpDataJson(final RestRequest restRequest) throws IOException, PwmUnrecoverableException {
    final RestVerifyOtpServer.JsonPutOtpInput jsonInput;
    {
        final RestVerifyOtpServer.JsonPutOtpInput jsonBody = RestUtility.deserializeJsonBody(restRequest, RestVerifyOtpServer.JsonPutOtpInput.class, RestUtility.Flag.AllowNullReturn);
        jsonInput = new RestVerifyOtpServer.JsonPutOtpInput(RestUtility.readValueFromJsonAndParam(jsonBody == null ? null : jsonBody.getToken(), restRequest.readParameterAsString("token"), "token"), RestUtility.readValueFromJsonAndParam(jsonBody == null ? null : jsonBody.getUsername(), restRequest.readParameterAsString("username"), "username"));
    }
    final TargetUserIdentity targetUserIdentity = RestUtility.resolveRequestedUsername(restRequest, jsonInput.getUsername());
    try {
        final OtpService otpService = restRequest.getPwmApplication().getOtpService();
        final OTPUserRecord otpUserRecord = otpService.readOTPUserConfiguration(restRequest.getSessionLabel(), targetUserIdentity.getUserIdentity());
        final boolean verified = otpUserRecord != null && otpService.validateToken(restRequest.getSessionLabel(), targetUserIdentity.getUserIdentity(), otpUserRecord, jsonInput.getToken(), false);
        StatisticsManager.incrementStat(restRequest.getPwmApplication(), Statistic.REST_VERIFYOTP);
        return RestResultBean.forSuccessMessage(verified, restRequest, Message.Success_Unknown);
    } catch (ChaiUnavailableException e) {
        throw PwmUnrecoverableException.fromChaiException(e);
    } catch (PwmOperationalException e) {
        final String errorMsg = "unexpected error reading json input: " + e.getMessage();
        final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
        return RestResultBean.fromError(restRequest, errorInformation);
    }
}
Also used : ErrorInformation(password.pwm.error.ErrorInformation) ChaiUnavailableException(com.novell.ldapchai.exception.ChaiUnavailableException) OtpService(password.pwm.util.operations.OtpService) OTPUserRecord(password.pwm.util.operations.otp.OTPUserRecord) PwmOperationalException(password.pwm.error.PwmOperationalException) RestMethodHandler(password.pwm.ws.server.RestMethodHandler)

Aggregations

OTPUserRecord (password.pwm.util.operations.otp.OTPUserRecord)8 PwmOperationalException (password.pwm.error.PwmOperationalException)7 ErrorInformation (password.pwm.error.ErrorInformation)6 ChaiUnavailableException (com.novell.ldapchai.exception.ChaiUnavailableException)4 IOException (java.io.IOException)3 PwmException (password.pwm.error.PwmException)3 PwmUnrecoverableException (password.pwm.error.PwmUnrecoverableException)3 PwmSession (password.pwm.http.PwmSession)3 OtpService (password.pwm.util.operations.OtpService)3 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)2 PwmApplication (password.pwm.PwmApplication)2 UserIdentity (password.pwm.bean.UserIdentity)2 Configuration (password.pwm.config.Configuration)2 SetupOtpProfile (password.pwm.config.profile.SetupOtpProfile)2 RestResultBean (password.pwm.ws.server.RestResultBean)2 Instant (java.time.Instant)1 Date (java.util.Date)1 Mac (javax.crypto.Mac)1 SecretKeySpec (javax.crypto.spec.SecretKeySpec)1 ServletException (javax.servlet.ServletException)1