/* * Password Management Servlets (PWM) * http://www.pwm-project.org * * Copyright (c) 2006-2009 Novell, Inc. * Copyright (c) 2009-2017 The PWM Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package password.pwm.health; import password.pwm.AppProperty; import password.pwm.PwmApplication; import password.pwm.PwmConstants; import password.pwm.config.ActionConfiguration; import password.pwm.config.Configuration; import password.pwm.config.PwmSetting; import password.pwm.config.PwmSettingSyntax; import password.pwm.config.profile.LdapProfile; import password.pwm.config.stored.StoredConfigReference; import password.pwm.config.stored.StoredConfigurationImpl; import password.pwm.config.stored.StoredConfigurationUtil; import password.pwm.config.value.ActionValue; import password.pwm.error.ErrorInformation; import password.pwm.error.PwmError; import password.pwm.error.PwmOperationalException; import password.pwm.error.PwmUnrecoverableException; import password.pwm.util.java.JavaHelper; import password.pwm.util.java.TimeDuration; import password.pwm.util.logging.PwmLogger; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; public class CertificateChecker implements HealthChecker { private static final PwmLogger LOGGER = PwmLogger.forClass(CertificateChecker.class); @Override public List<HealthRecord> doHealthCheck(final PwmApplication pwmApplication) { final List<HealthRecord> records = new ArrayList<>(); records.addAll(doHealthCheck(pwmApplication.getConfig())); try { records.addAll(doActionHealthCheck(pwmApplication.getConfig())); } catch (PwmUnrecoverableException e) { LOGGER.error("error while checking action certificates: " + e.getMessage(),e); } return records; } private static List<HealthRecord> doHealthCheck(final Configuration configuration) { final List<HealthRecord> returnList = new ArrayList<>(); for (final PwmSetting setting : PwmSetting.values()) { if (setting.getSyntax() == PwmSettingSyntax.X509CERT && !setting.getCategory().hasProfiles()) { if (setting != PwmSetting.LDAP_SERVER_CERTS) { final X509Certificate[] certs = configuration.readSettingAsCertificate(setting); returnList.addAll(doHealthCheck(configuration,setting,null,certs)); } } } for (final LdapProfile ldapProfile : configuration.getLdapProfiles().values()) { final X509Certificate[] certificates = configuration.getLdapProfiles().get(ldapProfile.getIdentifier()).readSettingAsCertificate(PwmSetting.LDAP_SERVER_CERTS); returnList.addAll(doHealthCheck(configuration,PwmSetting.LDAP_SERVER_CERTS,ldapProfile.getIdentifier(),certificates)); } return Collections.unmodifiableList(returnList); } private static List<HealthRecord> doActionHealthCheck(final Configuration configuration) throws PwmUnrecoverableException { final StoredConfigurationImpl storedConfiguration = configuration.getStoredConfiguration(); final List<HealthRecord> returnList = new ArrayList<>(); final List<StoredConfigReference> modifiedReferences = StoredConfigurationUtil.modifiedSettings(storedConfiguration); for (final StoredConfigReference storedConfigReference : modifiedReferences) { if (storedConfigReference.getRecordType() == StoredConfigReference.RecordType.SETTING) { final PwmSetting pwmSetting = PwmSetting.forKey(storedConfigReference.getRecordID()); if (pwmSetting != null && pwmSetting.getSyntax() == PwmSettingSyntax.ACTION) { final ActionValue value = (ActionValue)storedConfiguration.readSetting(pwmSetting, storedConfigReference.getProfileID()); for (final ActionConfiguration actionConfiguration : value.toNativeObject()) { final X509Certificate[] certificates = actionConfiguration.getCertificates(); returnList.addAll(doHealthCheck(configuration, pwmSetting, storedConfigReference.getProfileID(), certificates)); } } } } return Collections.unmodifiableList(returnList); } private static List<HealthRecord> doHealthCheck(final Configuration configuration, final PwmSetting setting, final String profileID, final X509Certificate[] certificates) { final long warnDurationMs = 1000 * Long.parseLong(configuration.readAppProperty(AppProperty.HEALTH_CERTIFICATE_WARN_SECONDS)); if (certificates != null) { final List<HealthRecord> returnList = new ArrayList<>(); for (final X509Certificate certificate : certificates) { try { checkCertificate(certificate, warnDurationMs); return Collections.emptyList(); } catch (PwmOperationalException e) { final String errorDetail = e.getErrorInformation().getDetailedErrorMsg(); final HealthRecord record = HealthRecord.forMessage(HealthMessage.Config_Certificate, setting.toMenuLocationDebug(profileID,PwmConstants.DEFAULT_LOCALE), errorDetail ); return Collections.singletonList(record); } } return returnList; } return Collections.emptyList(); } public static void checkCertificate(final X509Certificate certificate, final long warnDurationMs) throws PwmOperationalException { if (certificate == null) { return; } try { certificate.checkValidity(); } catch (CertificateException e) { final StringBuilder errorMsg = new StringBuilder(); errorMsg.append("certificate for subject "); errorMsg.append(certificate.getSubjectDN().getName()); errorMsg.append(" is not valid: "); errorMsg.append(e.getMessage()); final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_CERTIFICATE_ERROR, errorMsg.toString(), new String[]{errorMsg.toString()}); throw new PwmOperationalException(errorInformation); } final Date expireDate = certificate.getNotAfter(); final TimeDuration durationUntilExpire = TimeDuration.fromCurrent(expireDate); if (durationUntilExpire.isShorterThan(warnDurationMs)) { final StringBuilder errorMsg = new StringBuilder(); errorMsg.append("certificate for subject "); errorMsg.append(certificate.getSubjectDN().getName()); errorMsg.append(" will expire on: "); errorMsg.append(JavaHelper.toIsoDate(expireDate)); errorMsg.append(" (").append(durationUntilExpire.asCompactString()).append(" from now)"); final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_CERTIFICATE_ERROR, errorMsg.toString(), new String[]{errorMsg.toString()}); throw new PwmOperationalException(errorInformation); } } }