/** * Copyright (c) 2015-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.model.impl.security; import javax.xml.datatype.Duration; import javax.xml.soap.SOAPMessage; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor; import org.apache.wss4j.common.ext.WSSecurityException; import org.apache.wss4j.dom.util.WSSecurityUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.evolveum.midpoint.audit.api.AuditEventRecord; import com.evolveum.midpoint.audit.api.AuditEventStage; import com.evolveum.midpoint.audit.api.AuditEventType; import com.evolveum.midpoint.audit.api.AuditService; import com.evolveum.midpoint.model.impl.ModelObjectResolver; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.result.OperationResultStatus; import com.evolveum.midpoint.security.api.ConnectionEnvironment; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.task.api.TaskManager; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.NonceCredentialsPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordCredentialsPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordLifeTimeType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SecurityPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SecurityQuestionsCredentialsPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ValuePolicyType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; /** * @author semancik * */ @Component public class SecurityHelper { private static final Trace LOGGER = TraceManager.getTrace(SecurityHelper.class); public static final String CONTEXTUAL_PROPERTY_AUDITED_NAME = SecurityHelper.class.getName() + ".audited"; @Autowired private TaskManager taskManager; @Autowired private AuditService auditService; @Autowired(required = true) private ModelObjectResolver objectResolver; public void auditLoginSuccess(@NotNull UserType user, @NotNull ConnectionEnvironment connEnv) { auditLogin(user.getName().getOrig(), user, connEnv, OperationResultStatus.SUCCESS, null); } public void auditLoginFailure(@Nullable String username, @Nullable UserType user, @NotNull ConnectionEnvironment connEnv, String message) { auditLogin(username, user, connEnv, OperationResultStatus.FATAL_ERROR, message); } private void auditLogin(@Nullable String username, @Nullable UserType user, @NotNull ConnectionEnvironment connEnv, @NotNull OperationResultStatus status, @Nullable String message) { Task task = taskManager.createTaskInstance(); task.setChannel(connEnv.getChannel()); LOGGER.debug("Login {} username={}, channel={}: {}", status == OperationResultStatus.SUCCESS ? "success" : "failure", username, connEnv.getChannel(), message); AuditEventRecord record = new AuditEventRecord(AuditEventType.CREATE_SESSION, AuditEventStage.REQUEST); record.setParameter(username); if (user != null) { record.setInitiator(user.asPrismObject()); } record.setChannel(connEnv.getChannel()); record.setTimestamp(System.currentTimeMillis()); record.setOutcome(status); record.setMessage(message); record.setSessionIdentifier(connEnv.getSessionId()); auditService.audit(record, task); } public void auditLogout(ConnectionEnvironment connEnv, Task task) { AuditEventRecord record = new AuditEventRecord(AuditEventType.TERMINATE_SESSION, AuditEventStage.REQUEST); PrismObject<UserType> owner = task.getOwner(); if (owner != null) { record.setInitiator(owner); PolyStringType name = owner.asObjectable().getName(); if (name != null) { record.setParameter(name.getOrig()); } } record.setChannel(connEnv.getChannel()); record.setTimestamp(System.currentTimeMillis()); record.setSessionIdentifier(connEnv.getSessionId()); record.setOutcome(OperationResultStatus.SUCCESS); auditService.audit(record, task); } public String getUsernameFromMessage(SOAPMessage saajSoapMessage) throws WSSecurityException { if (saajSoapMessage == null) { return null; } Element securityHeader = WSSecurityUtil.getSecurityHeader(saajSoapMessage.getSOAPPart(), ""); return getUsernameFromSecurityHeader(securityHeader); } private String getUsernameFromSecurityHeader(Element securityHeader) { if (securityHeader == null) { return null; } String username = ""; NodeList list = securityHeader.getChildNodes(); int len = list.getLength(); Node elem; for (int i = 0; i < len; i++) { elem = list.item(i); if (elem.getNodeType() != Node.ELEMENT_NODE) { continue; } if ("UsernameToken".equals(elem.getLocalName())) { NodeList nodes = elem.getChildNodes(); int len2 = nodes.getLength(); for (int j = 0; j < len2; j++) { Node elem2 = nodes.item(j); if ("Username".equals(elem2.getLocalName())) { username = elem2.getTextContent(); } } } } return username; } public SOAPMessage getSOAPMessage(SoapMessage msg) { SAAJInInterceptor.INSTANCE.handleMessage(msg); return msg.getContent(SOAPMessage.class); } /** * Returns security policy applicable for the specified user. It looks for organization and global policies and takes into account * deprecated properties and password policy references. The resulting security policy has all the (non-deprecated) properties set. * If there is also referenced value policy, it is will be stored as "object" in the value policy reference inside the * returned security policy. */ public <F extends FocusType> SecurityPolicyType locateSecurityPolicy(PrismObject<F> user, PrismObject<SystemConfigurationType> systemConfiguration, Task task, OperationResult result) throws SchemaException { PrismObject<SecurityPolicyType> orgSecurityPolicy = objectResolver.searchOrgTreeWidthFirstReference(user, o -> o.asObjectable().getSecurityPolicyRef(), "security policy", task, result); LOGGER.trace("Found organization security policy: {}", orgSecurityPolicy); if (orgSecurityPolicy != null) { SecurityPolicyType orgSecurityPolicyType = orgSecurityPolicy.asObjectable(); postProcessSecurityPolicy(orgSecurityPolicyType, task, result); traceSecurityPolicy(orgSecurityPolicyType, user); return orgSecurityPolicyType; } if (systemConfiguration != null) { ObjectReferenceType globalSecurityPolicyRef = systemConfiguration.asObjectable().getGlobalSecurityPolicyRef(); if (globalSecurityPolicyRef != null) { try { SecurityPolicyType globalSecurityPolicyType = objectResolver.resolve(globalSecurityPolicyRef, SecurityPolicyType.class, null, "global security policy reference in system configuration", task, result); LOGGER.trace("Using global security policy: {}", globalSecurityPolicyType); postProcessSecurityPolicy(globalSecurityPolicyType, task, result); traceSecurityPolicy(globalSecurityPolicyType, user); return globalSecurityPolicyType; } catch (ObjectNotFoundException | SchemaException e) { LOGGER.error(e.getMessage(), e); traceSecurityPolicy(null, user); return null; } } } // DEPRECATED, legacy PrismObject<ValuePolicyType> orgPasswordPolicy = objectResolver.searchOrgTreeWidthFirstReference(user, o -> o.asObjectable().getPasswordPolicyRef(), "security policy", task, result); LOGGER.trace("Found organization password policy: {}", orgPasswordPolicy); if (orgPasswordPolicy != null) { SecurityPolicyType policy = postProcessPasswordPolicy(orgPasswordPolicy.asObjectable()); traceSecurityPolicy(policy, user); return policy; } if (systemConfiguration != null) { ObjectReferenceType globalPasswordPolicyRef = systemConfiguration.asObjectable().getGlobalPasswordPolicyRef(); if (globalPasswordPolicyRef != null) { try { ValuePolicyType globalPasswordPolicyType = objectResolver.resolve(globalPasswordPolicyRef, ValuePolicyType.class, null, "global security policy reference in system configuration", task, result); LOGGER.trace("Using global password policy: {}", globalPasswordPolicyType); SecurityPolicyType policy = postProcessPasswordPolicy(globalPasswordPolicyType); traceSecurityPolicy(policy, user); return policy; } catch (ObjectNotFoundException | SchemaException e) { LOGGER.error(e.getMessage(), e); traceSecurityPolicy(null, user); return null; } } } return null; } private <F extends FocusType> void traceSecurityPolicy(SecurityPolicyType securityPolicyType, PrismObject<F> user) { if (LOGGER.isTraceEnabled()) { if (securityPolicyType == null) { LOGGER.trace("Located security policy for {}: null", user); } else { LOGGER.trace("Located security policy for {}:\n{}", user, securityPolicyType.asPrismObject().debugDump(1)); } } } private void postProcessSecurityPolicy(SecurityPolicyType securityPolicyType, Task task, OperationResult result) { CredentialsPolicyType creds = securityPolicyType.getCredentials(); if (creds != null) { PasswordCredentialsPolicyType passwd = creds.getPassword(); if (passwd != null) { postProcessPasswordCredentialPolicy(securityPolicyType, passwd, task, result); } for (NonceCredentialsPolicyType nonce: creds.getNonce()) { postProcessCredentialPolicy(securityPolicyType, nonce, "nonce credential policy", task, result); } SecurityQuestionsCredentialsPolicyType securityQuestions = creds.getSecurityQuestions(); if (securityQuestions != null) { postProcessCredentialPolicy(securityPolicyType, securityQuestions, "security questions credential policy", task, result); } } } private void postProcessPasswordCredentialPolicy(SecurityPolicyType securityPolicyType, PasswordCredentialsPolicyType passwd, Task task, OperationResult result) { // Deprecated settings Integer passwordHistoryLength = passwd.getPasswordHistoryLength(); if (passwordHistoryLength != null && passwd.getHistoryLength() == null) { passwd.setHistoryLength(passwordHistoryLength); } ObjectReferenceType passwordPolicyRef = passwd.getPasswordPolicyRef(); if (passwordPolicyRef != null && passwd.getValuePolicyRef() == null) { passwd.setValuePolicyRef(passwordPolicyRef.clone()); } ValuePolicyType valuePolicyType = postProcessCredentialPolicy(securityPolicyType, passwd, "password credential policy", task, result); if (valuePolicyType != null) { setDeprecatedPasswordPolicyProperties(valuePolicyType, passwd); } } private ValuePolicyType postProcessCredentialPolicy(SecurityPolicyType securityPolicyType, CredentialPolicyType credPolicy, String credShortDesc, Task task, OperationResult result) { ObjectReferenceType valuePolicyRef = credPolicy.getValuePolicyRef(); if (valuePolicyRef == null) { return null; } ValuePolicyType valuePolicyType; try { valuePolicyType = objectResolver.resolve(valuePolicyRef, ValuePolicyType.class, null, credShortDesc + " in " + securityPolicyType, task, result); } catch (ObjectNotFoundException | SchemaException e) { LOGGER.warn("{} {} referenced from {} was not found", credShortDesc, valuePolicyRef.getOid(), securityPolicyType); return null; } valuePolicyRef.asReferenceValue().setObject(valuePolicyType.asPrismObject()); return valuePolicyType; } private SecurityPolicyType postProcessPasswordPolicy(ValuePolicyType passwordPolicyType) { SecurityPolicyType securityPolicyType = new SecurityPolicyType(); CredentialsPolicyType creds = new CredentialsPolicyType(); PasswordCredentialsPolicyType passwd = new PasswordCredentialsPolicyType(); ObjectReferenceType passwordPolicyRef = new ObjectReferenceType(); passwordPolicyRef.asReferenceValue().setObject(passwordPolicyType.asPrismObject()); passwd.setValuePolicyRef(passwordPolicyRef); creds.setPassword(passwd); securityPolicyType.setCredentials(creds); setDeprecatedPasswordPolicyProperties(passwordPolicyType, passwd); return securityPolicyType; } private void setDeprecatedPasswordPolicyProperties(ValuePolicyType passwordPolicyType, PasswordCredentialsPolicyType passwd) { PasswordLifeTimeType lifetime = passwordPolicyType.getLifetime(); if (lifetime != null) { Integer expiration = lifetime.getExpiration(); if (expiration != null && expiration != 0 && passwd.getMaxAge() == null) { passwd.setMaxAge(daysToDuration(expiration)); } Integer minPasswordAge = lifetime.getMinPasswordAge(); if (minPasswordAge != null && minPasswordAge != 0 && passwd.getMinAge() == null) { passwd.setMinAge(daysToDuration(minPasswordAge)); } Integer passwordHistoryLength = lifetime.getPasswordHistoryLength(); if (passwordHistoryLength != null && passwd.getHistoryLength() == null) { passwd.setHistoryLength(passwordHistoryLength); } } String minOccurs = passwordPolicyType.getMinOccurs(); if (minOccurs != null && passwd.getMinOccurs() == null) { passwd.setMinOccurs(minOccurs); } } private Duration daysToDuration(int days) { return XmlTypeConverter.createDuration(days * 1000 * 60 * 60 * 24); } }