View Javadoc

1   /*
2    * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.common.binding.security;
18  
19  import org.opensaml.common.SAMLObject;
20  import org.opensaml.common.SignableSAMLObject;
21  import org.opensaml.common.binding.SAMLMessageContext;
22  import org.opensaml.security.SAMLSignatureProfileValidator;
23  import org.opensaml.ws.message.MessageContext;
24  import org.opensaml.ws.security.SecurityPolicyException;
25  import org.opensaml.xml.security.trust.TrustEngine;
26  import org.opensaml.xml.signature.Signature;
27  import org.opensaml.xml.validation.ValidationException;
28  import org.opensaml.xml.validation.Validator;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  /**
33   * SAML security policy rule which validates the signature (if present) on the {@link SAMLObject} which represents the
34   * SAML protocol message being processed.
35   * 
36   * <p>
37   * If the message is not an instance of {@link SignableSAMLObject}, then no processing is performed. If signature
38   * validation is successful, and the SAML message context issuer was not previously authenticated, then the context's
39   * issuer authentication state will be set to <code>true</code>.
40   * </p>
41   * 
42   * <p>
43   * If an optional {@link Validator} for {@link Signature} objects is supplied, this validator will be used to validate
44   * the XML Signature element prior to the actual cryptographic validation of the signature. This might for example be
45   * used to enforce certain signature profile requirements or to detect signatures upon which it would be unsafe to
46   * attempt cryptographic processing. When using the single argument constructuor form, the validator will default to
47   * {@link SAMLSignatureProfileValidator}.
48   * </p>
49   */
50  public class SAMLProtocolMessageXMLSignatureSecurityPolicyRule extends BaseSAMLXMLSignatureSecurityPolicyRule {
51  
52      /** Logger. */
53      private final Logger log = LoggerFactory.getLogger(SAMLProtocolMessageXMLSignatureSecurityPolicyRule.class);
54  
55      /** Validator for XML Signature instances. */
56      private Validator<Signature> sigValidator;
57  
58      /**
59       * Constructor.
60       * 
61       * Signature pre-validator defaults to {@link SAMLSignatureProfileValidator}.
62       * 
63       * @param engine Trust engine used to verify the signature
64       */
65      public SAMLProtocolMessageXMLSignatureSecurityPolicyRule(TrustEngine<Signature> engine) {
66          super(engine);
67          sigValidator = new SAMLSignatureProfileValidator();
68      }
69  
70      /**
71       * Constructor.
72       * 
73       * @param engine Trust engine used to verify the signature
74       * @param signatureValidator optional pre-validator used to validate Signature elements prior to the actual
75       *            cryptographic validation operation
76       */
77      public SAMLProtocolMessageXMLSignatureSecurityPolicyRule(TrustEngine<Signature> engine,
78              Validator<Signature> signatureValidator) {
79          super(engine);
80          sigValidator = signatureValidator;
81      }
82  
83      /** {@inheritDoc} */
84      public void evaluate(MessageContext messageContext) throws SecurityPolicyException {
85          if (!(messageContext instanceof SAMLMessageContext)) {
86              log.debug("Invalid message context type, this policy rule only supports SAMLMessageContext");
87              return;
88          }
89  
90          SAMLMessageContext samlMsgCtx = (SAMLMessageContext) messageContext;
91  
92          SAMLObject samlMsg = samlMsgCtx.getInboundSAMLMessage();
93          if (!(samlMsg instanceof SignableSAMLObject)) {
94              log.debug("Extracted SAML message was not a SignableSAMLObject, can not process signature");
95              return;
96          }
97          SignableSAMLObject signableObject = (SignableSAMLObject) samlMsg;
98          if (!signableObject.isSigned()) {
99              log.info("SAML protocol message was not signed, skipping XML signature processing");
100             return;
101         }
102         Signature signature = signableObject.getSignature();
103 
104         performPreValidation(signature);
105 
106         doEvaluate(signature, signableObject, samlMsgCtx);
107     }
108 
109     /**
110      * Perform cryptographic validation and trust evaluation on the Signature token using the configured Signature trust
111      * engine.
112      * 
113      * @param signature the signature which is being evaluated
114      * @param signableObject the signable object which contained the signature
115      * @param samlMsgCtx the SAML message context being processed
116      * @throws SecurityPolicyException thrown if the signature fails validation
117      */
118     protected void doEvaluate(Signature signature, SignableSAMLObject signableObject, SAMLMessageContext samlMsgCtx)
119             throws SecurityPolicyException {
120 
121         String contextIssuer = samlMsgCtx.getInboundMessageIssuer();
122         if (contextIssuer != null) {
123             String msgType = signableObject.getElementQName().toString();
124             log.debug("Attempting to verify signature on signed SAML protocol message using context issuer message type: {}",
125                             msgType);
126 
127             if (evaluate(signature, contextIssuer, samlMsgCtx)) {
128                 log.info("Validation of protocol message signature succeeded, message type: {}", msgType);
129                 if (!samlMsgCtx.isInboundSAMLMessageAuthenticated()) {
130                     log.info("Authentication via protocol message signature succeeded for context issuer entity ID {}",
131                             contextIssuer);
132                     samlMsgCtx.setInboundSAMLMessageAuthenticated(true);
133                 }
134             } else {
135                 log.warn("Validation of protocol message signature failed for context issuer '" + contextIssuer
136                         + "', message type: " + msgType);
137                 throw new SecurityPolicyException("Validation of protocol message signature failed");
138             }
139         } else {
140             log.warn("Context issuer unavailable, can not attempt SAML protocol message signature validation");
141             throw new SecurityPolicyException("Context issuer unavailable, can not validate signature");
142         }
143     }
144 
145     /**
146      * Get the validator used to perform pre-validation on Signature tokens.
147      * 
148      * @return the configured Signature validator, or null
149      */
150     protected Validator<Signature> getSignaturePrevalidator() {
151         return sigValidator;
152     }
153 
154     /**
155      * Perform pre-validation on the Signature token.
156      * 
157      * @param signature the signature to evaluate
158      * @throws SecurityPolicyException thrown if the signature element fails pre-validation
159      */
160     protected void performPreValidation(Signature signature) throws SecurityPolicyException {
161         if (getSignaturePrevalidator() != null) {
162             try {
163                 getSignaturePrevalidator().validate(signature);
164             } catch (ValidationException e) {
165                 log.warn("Protocol message signature failed signature pre-validation", e);
166                 throw new SecurityPolicyException("Protocol message signature failed signature pre-validation", e);
167             }
168         }
169     }
170 }