View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.wss4j.dom.str;
21  
22  import java.util.List;
23  
24  import javax.security.auth.callback.Callback;
25  import javax.security.auth.callback.CallbackHandler;
26  
27  import org.apache.wss4j.common.bsp.BSPEnforcer;
28  import org.apache.wss4j.common.bsp.BSPRule;
29  import org.apache.wss4j.common.ext.WSPasswordCallback;
30  import org.apache.wss4j.common.ext.WSSecurityException;
31  import org.apache.wss4j.common.saml.SamlAssertionWrapper;
32  import org.apache.wss4j.common.token.BinarySecurity;
33  import org.apache.wss4j.common.token.PKIPathSecurity;
34  import org.apache.wss4j.common.token.SecurityTokenReference;
35  import org.apache.wss4j.common.token.X509Security;
36  import org.apache.wss4j.common.util.XMLUtils;
37  import org.apache.wss4j.dom.WSConstants;
38  import org.apache.wss4j.dom.WSDocInfo;
39  import org.apache.wss4j.dom.callback.CallbackLookup;
40  import org.apache.wss4j.dom.callback.DOMCallbackLookup;
41  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
42  import org.apache.wss4j.dom.handler.RequestData;
43  import org.apache.wss4j.dom.message.token.KerberosSecurity;
44  import org.apache.wss4j.dom.processor.Processor;
45  import org.w3c.dom.Document;
46  import org.w3c.dom.Element;
47  
48  /**
49   * Some utilities for the STRParsers.
50   */
51  public final class STRParserUtil {
52  
53      private static final org.slf4j.Logger LOG =
54          org.slf4j.LoggerFactory.getLogger(STRParserUtil.class);
55  
56      private STRParserUtil() {
57          // complete
58      }
59  
60      /**
61       * Get an SamlAssertionWrapper object from parsing a SecurityTokenReference that uses
62       * a KeyIdentifier that points to a SAML Assertion.
63       *
64       * @param secRef the SecurityTokenReference to the SAML Assertion
65       * @param strElement The SecurityTokenReference DOM element
66       * @param request The RequestData instance used to obtain configuration
67       * @return an SamlAssertionWrapper object
68       * @throws WSSecurityException
69       */
70      public static SamlAssertionWrapper getAssertionFromKeyIdentifier(
71          SecurityTokenReference secRef,
72          Element strElement,
73          RequestData request
74      ) throws WSSecurityException {
75          String keyIdentifierValue = secRef.getKeyIdentifierValue();
76          String type = secRef.getKeyIdentifierValueType();
77          WSSecurityEngineResult result = request.getWsDocInfo().getResult(keyIdentifierValue);
78  
79          SamlAssertionWrapper samlAssertion = null;
80          Element token = null;
81          if (result != null) {
82              samlAssertion =
83                  (SamlAssertionWrapper)result.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
84              return samlAssertion;
85          } else {
86              token =
87                  findProcessedTokenElement(
88                      strElement.getOwnerDocument(), request.getWsDocInfo(), request.getCallbackHandler(),
89                      keyIdentifierValue, type
90                  );
91              if (token != null) {
92                  if (!"Assertion".equals(token.getLocalName())) {
93                      throw new WSSecurityException(
94                          WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"
95                      );
96                  }
97                  return new SamlAssertionWrapper(token);
98              }
99              token =
100                 findUnprocessedTokenElement(
101                     strElement.getOwnerDocument(), request.getWsDocInfo(), keyIdentifierValue, type
102                 );
103 
104             if (token == null || !"Assertion".equals(token.getLocalName())) {
105                 throw new WSSecurityException(
106                     WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"
107                 );
108             }
109             Processor proc = request.getWssConfig().getProcessor(WSConstants.SAML_TOKEN);
110             List<WSSecurityEngineResult> samlResult = proc.handleToken(token, request);
111             return
112                 (SamlAssertionWrapper)samlResult.get(0).get(
113                     WSSecurityEngineResult.TAG_SAML_ASSERTION
114                 );
115         }
116     }
117 
118 
119     /**
120      * Check that the BinarySecurityToken referenced by the SecurityTokenReference argument
121      * is BSP compliant.
122      * @param secRef The SecurityTokenReference to the BinarySecurityToken
123      * @param token The BinarySecurityToken
124      * @param bspEnforcer a BSPEnforcer instance to enforce BSP rules
125      * @throws WSSecurityException
126      */
127     public static void checkBinarySecurityBSPCompliance(
128         SecurityTokenReference secRef,
129         BinarySecurity token,
130         BSPEnforcer bspEnforcer
131     ) throws WSSecurityException {
132         if (secRef.containsReference()) {
133             // Check the ValueType attributes
134             String valueType = secRef.getReference().getValueType();
135             if (token instanceof X509Security && !X509Security.X509_V3_TYPE.equals(valueType)
136                 || token instanceof PKIPathSecurity && !PKIPathSecurity.PKI_TYPE.equals(valueType)
137                 || token instanceof KerberosSecurity
138                         && !(valueType == null || valueType.length() == 0)
139                         && !WSConstants.WSS_GSS_KRB_V5_AP_REQ.equals(valueType)) {
140                 bspEnforcer.handleBSPRule(BSPRule.R3058);
141             }
142         } else if (secRef.containsKeyIdentifier()) {
143             String valueType = secRef.getKeyIdentifierValueType();
144             if (!SecurityTokenReference.SKI_URI.equals(valueType)
145                 && !SecurityTokenReference.THUMB_URI.equals(valueType)
146                 && !WSConstants.WSS_KRB_KI_VALUE_TYPE.equals(valueType)
147                 && !X509Security.X509_V3_TYPE.equals(valueType)) {
148                 bspEnforcer.handleBSPRule(BSPRule.R3063);
149             }
150         }
151 
152         // Check TokenType attributes
153         if (token instanceof PKIPathSecurity) {
154             String tokenType = secRef.getTokenType();
155             if (!PKIPathSecurity.PKI_TYPE.equals(tokenType)) {
156                 bspEnforcer.handleBSPRule(BSPRule.R5215);
157             }
158         }
159     }
160 
161     /**
162      * Check that the EncryptedKey referenced by the SecurityTokenReference argument
163      * is BSP compliant.
164      * @param secRef The SecurityTokenReference to the BinarySecurityToken
165      * @param bspEnforcer a BSPEnforcer instance to enforce BSP rules
166      * @throws WSSecurityException
167      */
168     public static void checkEncryptedKeyBSPCompliance(
169         SecurityTokenReference secRef, BSPEnforcer bspEnforcer
170     ) throws WSSecurityException {
171         if (secRef.containsKeyIdentifier()) {
172             String valueType = secRef.getKeyIdentifierValueType();
173             if (!SecurityTokenReference.ENC_KEY_SHA1_URI.equals(valueType)) {
174                 bspEnforcer.handleBSPRule(BSPRule.R3063);
175             }
176         }
177 
178         String tokenType = secRef.getTokenType();
179         if (!WSConstants.WSS_ENC_KEY_VALUE_TYPE.equals(tokenType)) {
180             bspEnforcer.handleBSPRule(BSPRule.R5215);
181         }
182     }
183 
184     /**
185      * Check that the SAML token referenced by the SecurityTokenReference argument
186      * is BSP compliant.
187      * @param secRef The SecurityTokenReference to the SAML token
188      * @param samlAssertion The SAML Token SamlAssertionWrapper object
189      * @param bspEnforcer a BSPEnforcer instance to enforce BSP rules
190      * @throws WSSecurityException
191      */
192     public static void checkSamlTokenBSPCompliance(
193         SecurityTokenReference secRef,
194         SamlAssertionWrapper samlAssertion,
195         BSPEnforcer bspEnforcer
196     ) throws WSSecurityException {
197         // Check the KeyIdentifier ValueType attributes
198         if (secRef.containsKeyIdentifier()) {
199             String valueType = secRef.getKeyIdentifierValueType();
200             if (samlAssertion.getSaml1() != null
201                 && !WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(valueType)) {
202                 bspEnforcer.handleBSPRule(BSPRule.R6603);
203             }
204             if (samlAssertion.getSaml2() != null
205                 && !WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(valueType)) {
206                 bspEnforcer.handleBSPRule(BSPRule.R6616);
207             }
208             String encoding = secRef.getKeyIdentifierEncodingType();
209             if (encoding != null && encoding.length() != 0) {
210                 bspEnforcer.handleBSPRule(BSPRule.R6604);
211             }
212         }
213 
214         // Check the TokenType attribute
215         String tokenType = secRef.getTokenType();
216         if (samlAssertion.getSaml1() != null && !WSConstants.WSS_SAML_TOKEN_TYPE.equals(tokenType)) {
217             bspEnforcer.handleBSPRule(BSPRule.R6611);
218         }
219         if (samlAssertion.getSaml2() != null && !WSConstants.WSS_SAML2_TOKEN_TYPE.equals(tokenType)) {
220             bspEnforcer.handleBSPRule(BSPRule.R6617);
221         }
222 
223         // Check the ValueType attribute of the Reference for SAML2
224         if (samlAssertion.getSaml2() != null && secRef.containsReference()) {
225             String valueType = secRef.getReference().getValueType();
226             if (valueType != null && valueType.length() != 0) {
227                 bspEnforcer.handleBSPRule(BSPRule.R6614);
228             }
229         }
230     }
231 
232     /**
233      * Check that the Username token referenced by the SecurityTokenReference argument
234      * is BSP compliant.
235      * @param secRef The SecurityTokenReference to the Username token
236      * @param bspEnforcer a BSPEnforcer instance to enforce BSP rules
237      * @throws WSSecurityException
238      */
239     public static void checkUsernameTokenBSPCompliance(
240         SecurityTokenReference secRef, BSPEnforcer bspEnforcer
241     ) throws WSSecurityException {
242         if (!secRef.containsReference()) {
243             // BSP does not permit using a KeyIdentifier to refer to a U/T
244             bspEnforcer.handleBSPRule(BSPRule.R4215);
245         }
246 
247         if (secRef.getReference() != null) {
248             String valueType = secRef.getReference().getValueType();
249             if (!WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE.equals(valueType)) {
250                 bspEnforcer.handleBSPRule(BSPRule.R4214);
251             }
252         }
253     }
254 
255     /**
256      * Get the Secret Key from a CallbackHandler
257      * @param id The id of the element
258      * @param type The type of the element (may be null)
259      * @param identifier The WSPasswordCallback usage identifier
260      * @poaram data The RequestData Object
261      * @return A Secret Key
262      * @throws WSSecurityException
263      */
264     public static byte[] getSecretKeyFromToken(
265         String id,
266         String type,
267         int identifier,
268         RequestData data
269     ) throws WSSecurityException {
270         String uri = XMLUtils.getIDFromReference(id);
271         WSPasswordCallback pwcb =
272             new WSPasswordCallback(uri, null, type, identifier);
273         try {
274             Callback[] callbacks = new Callback[]{pwcb};
275             if (data.getCallbackHandler() != null) {
276                 data.getCallbackHandler().handle(callbacks);
277                 return pwcb.getKey();
278             }
279         } catch (Exception e) {
280             throw new WSSecurityException(
281                 WSSecurityException.ErrorCode.FAILURE, e,
282                 "noPassword", new Object[] {uri});
283         }
284 
285         return new byte[0];
286     }
287 
288     public static Element getTokenElement(
289         Document doc, WSDocInfo docInfo, CallbackHandler cb,
290         String uri, String valueType
291     ) throws WSSecurityException {
292         LOG.debug("Token reference uri: {}", uri);
293         LOG.debug("Token reference ValueType: {}", valueType);
294 
295         if (uri == null) {
296             throw new WSSecurityException(
297                 WSSecurityException.ErrorCode.INVALID_SECURITY, "badReferenceURI"
298             );
299         }
300 
301         Element tokElement =
302             findProcessedTokenElement(doc, docInfo, cb, uri, valueType);
303         if (tokElement == null) {
304             tokElement = findUnprocessedTokenElement(doc, docInfo, uri, valueType);
305         }
306 
307         if (tokElement == null) {
308             throw new WSSecurityException(
309                 WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE,
310                 "noToken", new Object[] {uri});
311         }
312         return tokElement;
313     }
314 
315     /**
316      * Find a token that has not been processed already - in other words, it searches for
317      * the element, rather than trying to access previous results to find the element
318      * @param doc Parent Document
319      * @param docInfo WSDocInfo instance
320      * @param uri URI of the element
321      * @param type Type of the element
322      * @return A DOM element
323      * @throws WSSecurityException
324      */
325     public static Element findUnprocessedTokenElement(
326         Document doc,
327         WSDocInfo docInfo,
328         String uri,
329         String type
330     ) throws WSSecurityException {
331         String id = XMLUtils.getIDFromReference(uri);
332         //
333         // Delegate finding the element to the CallbackLookup instance
334         //
335         CallbackLookup callbackLookup = null;
336         if (docInfo != null) {
337             callbackLookup = docInfo.getCallbackLookup();
338         }
339         if (callbackLookup == null) {
340             callbackLookup = new DOMCallbackLookup(doc);
341         }
342         return callbackLookup.getElement(id, type, true);
343     }
344 
345     /**
346      * Find a token that has been processed already - in other words, it access previous
347      * results to find the element, rather than conducting a general search
348      * @param doc Parent Document
349      * @param docInfo WSDocInfo instance
350      * @param cb CallbackHandler instance
351      * @param uri URI of the element
352      * @param type Type of the element
353      * @return A DOM element
354      * @throws WSSecurityException
355      */
356     public static Element findProcessedTokenElement(
357         Document doc,
358         WSDocInfo docInfo,
359         CallbackHandler cb,
360         String uri,
361         String type
362     ) throws WSSecurityException {
363         String id = XMLUtils.getIDFromReference(uri);
364         //
365         // Try to find it from the WSDocInfo instance first
366         //
367         if (docInfo != null) {
368             Element token = docInfo.getTokenElement(id);
369             if (token != null) {
370                 return token;
371             }
372         }
373 
374         //
375         // Try to find a custom token
376         //
377         if (cb != null && (WSConstants.WSC_SCT.equals(type)
378             || WSConstants.WSC_SCT_05_12.equals(type)
379             || WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(type)
380             || WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(type)
381             || KerberosSecurity.isKerberosToken(type))) {
382             //try to find a custom token
383             WSPasswordCallback pwcb =
384                 new WSPasswordCallback(id, WSPasswordCallback.CUSTOM_TOKEN);
385             try {
386                 cb.handle(new Callback[]{pwcb});
387                 Element assertionElem = pwcb.getCustomToken();
388                 if (assertionElem != null) {
389                     return (Element)doc.importNode(assertionElem, true);
390                 }
391             } catch (Exception e) {
392                 LOG.debug(e.getMessage(), e);
393                 // Consume this failure
394             }
395         }
396         return null;
397     }
398 
399 }