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.engine; 21 22 import java.util.Collections; 23 import java.util.LinkedList; 24 import java.util.List; 25 import java.util.Map; 26 27 import javax.security.auth.callback.CallbackHandler; 28 import javax.xml.namespace.QName; 29 30 import org.apache.wss4j.common.bsp.BSPRule; 31 import org.apache.wss4j.common.crypto.Crypto; 32 import org.apache.wss4j.common.ext.WSSecurityException; 33 import org.apache.wss4j.dom.WSConstants; 34 import org.apache.wss4j.dom.WSDocInfo; 35 import org.apache.wss4j.dom.callback.CallbackLookup; 36 import org.apache.wss4j.dom.callback.DOMCallbackLookup; 37 import org.apache.wss4j.dom.handler.RequestData; 38 import org.apache.wss4j.dom.handler.WSHandlerResult; 39 import org.apache.wss4j.dom.processor.Processor; 40 import org.apache.wss4j.dom.saml.DOMSAMLUtil; 41 import org.apache.wss4j.dom.util.WSSecurityUtil; 42 import org.w3c.dom.Document; 43 import org.w3c.dom.Element; 44 import org.w3c.dom.Node; 45 46 /** 47 * WS-Security Engine. 48 */ 49 public class WSSecurityEngine { 50 private static final org.slf4j.Logger LOG = 51 org.slf4j.LoggerFactory.getLogger(WSSecurityEngine.class); 52 53 /** 54 * The WSSConfig instance used by this SecurityEngine to 55 * find Processors for processing security headers 56 */ 57 private WSSConfig wssConfig; 58 private boolean doDebug; 59 private CallbackLookup callbackLookup; 60 61 /** 62 * @return the WSSConfig object set on this instance 63 */ 64 public final WSSConfig 65 getWssConfig() { 66 if (wssConfig == null) { 67 wssConfig = WSSConfig.getNewInstance(); 68 } 69 return wssConfig; 70 } 71 72 /** 73 * @param cfg the WSSConfig instance for this WSSecurityEngine to use 74 * 75 * @return the WSSConfig instance previously set on this 76 * WSSecurityEngine instance 77 */ 78 public final WSSConfig 79 setWssConfig(WSSConfig cfg) { 80 WSSConfig ret = wssConfig; 81 wssConfig = cfg; 82 return ret; 83 } 84 85 /** 86 * Set the CallbackLookup object to use to locate elements 87 * @param callbackLookup the CallbackLookup object to use to locate elements 88 */ 89 public void setCallbackLookup(CallbackLookup callbackLookup) { 90 this.callbackLookup = callbackLookup; 91 } 92 93 /** 94 * Get the CallbackLookup object to use to locate elements 95 * @return the CallbackLookup object to use to locate elements 96 */ 97 public CallbackLookup getCallbackLookup() { 98 return callbackLookup; 99 } 100 101 /** 102 * Process the security header given the soap envelope as W3C document. 103 * <p/> 104 * This is the main entry point to verify or decrypt a SOAP envelope. 105 * First check if a <code>wsse:Security</code> is available with the 106 * defined actor. 107 * 108 * @param doc the SOAP envelope as {@link Document} 109 * @param actor the engine works on behalf of this <code>actor</code>. Refer 110 * to the SOAP specification about <code>actor</code> or <code>role 111 * </code> 112 * @param cb a callback hander to the caller to resolve passwords during 113 * encryption and UsernameToken handling 114 * @param crypto the object that implements the access to the keystore and the 115 * handling of certificates. 116 * @return a WSHandlerResult Object containing the results of processing the security header 117 * @throws WSSecurityException 118 * @see WSSecurityEngine#processSecurityHeader(Element securityHeader, CallbackHandler cb, 119 * Crypto sigVerCrypto, Crypto decCrypto) 120 */ 121 public WSHandlerResult processSecurityHeader( 122 Document doc, 123 String actor, 124 CallbackHandler cb, 125 Crypto crypto 126 ) throws WSSecurityException { 127 return processSecurityHeader(doc, actor, cb, crypto, crypto); 128 } 129 130 /** 131 * Process the security header given the soap envelope as W3C document. 132 * <p/> 133 * This is the main entry point to verify or decrypt a SOAP envelope. 134 * First check if a <code>wsse:Security</code> is available with the 135 * defined actor. 136 * 137 * @param doc the SOAP envelope as {@link Document} 138 * @param actor the engine works on behalf of this <code>actor</code>. Refer 139 * to the SOAP specification about <code>actor</code> or <code>role 140 * </code> 141 * @param cb a callback hander to the caller to resolve passwords during 142 * encryption and UsernameToken handling 143 * @param sigVerCrypto the object that implements the access to the keystore and the 144 * handling of certificates for Signature verification 145 * @param decCrypto the object that implements the access to the keystore and the 146 * handling of certificates for Decryption 147 * @return a WSHandlerResult Object containing the results of processing the security header 148 * @throws WSSecurityException 149 * @see WSSecurityEngine#processSecurityHeader( 150 * Element securityHeader, CallbackHandler cb, Crypto sigVerCrypto, Crypto decCrypto) 151 */ 152 public WSHandlerResult processSecurityHeader( 153 Document doc, 154 String actor, 155 CallbackHandler cb, 156 Crypto sigVerCrypto, 157 Crypto decCrypto 158 ) throws WSSecurityException { 159 LOG.debug("enter processSecurityHeader()"); 160 161 if (actor == null) { 162 actor = ""; 163 } 164 WSHandlerResult wsResult = null; 165 Element elem = WSSecurityUtil.getSecurityHeader(doc, actor); 166 if (elem != null) { 167 LOG.debug("Processing WS-Security header for '{}' actor.", actor); 168 wsResult = processSecurityHeader(elem, actor, cb, sigVerCrypto, decCrypto); 169 } 170 return wsResult; 171 } 172 173 /** 174 * Process the security header given the <code>wsse:Security</code> DOM 175 * Element. 176 * 177 * This function loops over all direct child elements of the 178 * <code>wsse:Security</code> header. If it finds a known element, it 179 * transfers control to the appropriate handling function. The method 180 * processes the known child elements in the same order as they appear in 181 * the <code>wsse:Security</code> element. This is in accordance to the WS 182 * Security specification. <p/> 183 * 184 * Currently the functions can handle the following child elements: 185 * 186 * <ul> 187 * <li>{@link #SIGNATURE <code>ds:Signature</code>}</li> 188 * <li>{@link #ENCRYPTED_KEY <code>xenc:EncryptedKey</code>}</li> 189 * <li>{@link #REFERENCE_LIST <code>xenc:ReferenceList</code>}</li> 190 * <li>{@link #USERNAME_TOKEN <code>wsse:UsernameToken</code>}</li> 191 * <li>{@link #TIMESTAMP <code>wsu:Timestamp</code>}</li> 192 * </ul> 193 * 194 * Note that additional child elements can be processed if appropriate 195 * Processors have been registered with the WSSCondig instance set 196 * on this class. 197 * 198 * @param securityHeader the <code>wsse:Security</code> header element 199 * @param cb a callback hander to the caller to resolve passwords during 200 * encryption and UsernameToken handling 201 * @param sigVerCrypto the object that implements the access to the keystore and the 202 * handling of certificates used for Signature verification 203 * @param decCrypto the object that implements the access to the keystore and the 204 * handling of certificates used for Decryption 205 * @return a WSHandlerResult Object containing the results of processing the security header 206 * @throws WSSecurityException 207 */ 208 public WSHandlerResult processSecurityHeader( 209 Element securityHeader, 210 String actor, 211 CallbackHandler cb, 212 Crypto sigVerCrypto, 213 Crypto decCrypto 214 ) throws WSSecurityException { 215 RequestData data = new RequestData(); 216 data.setActor(actor); 217 data.setWssConfig(getWssConfig()); 218 data.setDecCrypto(decCrypto); 219 data.setSigVerCrypto(sigVerCrypto); 220 data.setCallbackHandler(cb); 221 return processSecurityHeader(securityHeader, data); 222 } 223 224 /** 225 * Process the security header given the soap envelope as W3C document. 226 * <p/> 227 * This is the main entry point to verify or decrypt a SOAP envelope. 228 * First check if a <code>wsse:Security</code> is available with the 229 * defined actor. 230 * 231 * @param doc the SOAP envelope as {@link Document} 232 * @param requestData the RequestData associated with the request. It should 233 * be able to provide the callback handler, cryptos, etc... 234 * as needed by the processing 235 * @return a WSHandlerResult Object containing the results of processing the security header 236 * @throws WSSecurityException 237 */ 238 public WSHandlerResult processSecurityHeader( 239 Document doc, RequestData requestData 240 ) throws WSSecurityException { 241 if (requestData.getActor() == null) { 242 requestData.setActor(""); 243 } 244 String actor = requestData.getActor(); 245 WSHandlerResult wsResult = null; 246 Element elem = WSSecurityUtil.getSecurityHeader(doc, actor); 247 if (elem != null) { 248 if (doDebug) { 249 LOG.debug("Processing WS-Security header for '" + actor + "' actor."); 250 } 251 wsResult = processSecurityHeader(elem, requestData); 252 } 253 return wsResult; 254 } 255 256 /** 257 * Process the security header given the <code>wsse:Security</code> DOM 258 * Element. 259 * 260 * This function loops over all direct child elements of the 261 * <code>wsse:Security</code> header. If it finds a known element, it 262 * transfers control to the appropriate handling function. The method 263 * processes the known child elements in the same order as they appear in 264 * the <code>wsse:Security</code> element. This is in accordance to the WS 265 * Security specification. <p/> 266 * 267 * Currently the functions can handle the following child elements: 268 * 269 * <ul> 270 * <li>{@link #SIGNATURE <code>ds:Signature</code>}</li> 271 * <li>{@link #ENCRYPTED_KEY <code>xenc:EncryptedKey</code>}</li> 272 * <li>{@link #REFERENCE_LIST <code>xenc:ReferenceList</code>}</li> 273 * <li>{@link #USERNAME_TOKEN <code>wsse:UsernameToken</code>}</li> 274 * <li>{@link #TIMESTAMP <code>wsu:Timestamp</code>}</li> 275 * </ul> 276 * 277 * Note that additional child elements can be processed if appropriate 278 * Processors have been registered with the WSSCondig instance set 279 * on this class. 280 * 281 * @param securityHeader the <code>wsse:Security</code> header element 282 * @param requestData the RequestData associated with the request. It should 283 * be able to provide the callback handler, cryptos, etc... 284 * as needed by the processing 285 * @return a WSHandlerResult Object containing the results of processing the security header 286 * @throws WSSecurityException 287 */ 288 public WSHandlerResult processSecurityHeader( 289 Element securityHeader, 290 RequestData requestData 291 ) throws WSSecurityException { 292 if (securityHeader == null) { 293 List<WSSecurityEngineResult> results = Collections.emptyList(); 294 Map<Integer, List<WSSecurityEngineResult>> actionResults = Collections.emptyMap(); 295 return new WSHandlerResult(null, results, actionResults); 296 } 297 298 if (requestData.getWssConfig() == null) { 299 requestData.setWssConfig(getWssConfig()); 300 } 301 302 // 303 // Gather some info about the document to process and store 304 // it for retrieval. Store the implementation of signature crypto 305 // (no need for encryption --- yet) 306 // 307 WSDocInfo wsDocInfo = new WSDocInfo(securityHeader.getOwnerDocument()); 308 CallbackLookup callbackLookupToUse = callbackLookup; 309 if (callbackLookupToUse == null) { 310 callbackLookupToUse = new DOMCallbackLookup(securityHeader.getOwnerDocument()); 311 } 312 wsDocInfo.setCallbackLookup(callbackLookupToUse); 313 wsDocInfo.setCrypto(requestData.getSigVerCrypto()); 314 wsDocInfo.setSecurityHeader(securityHeader); 315 requestData.setWsDocInfo(wsDocInfo); 316 317 final WSSConfig cfg = getWssConfig(); 318 Node node = securityHeader.getFirstChild(); 319 320 List<WSSecurityEngineResult> returnResults = new LinkedList<>(); 321 boolean foundTimestamp = false; 322 while (node != null) { 323 Node nextSibling = node.getNextSibling(); 324 if (Node.ELEMENT_NODE == node.getNodeType()) { 325 QName el = new QName(node.getNamespaceURI(), node.getLocalName()); 326 327 // Check for multiple timestamps 328 if (foundTimestamp && el.equals(WSConstants.TIMESTAMP)) { 329 requestData.getBSPEnforcer().handleBSPRule(BSPRule.R3227); 330 } else if (el.equals(WSConstants.TIMESTAMP)) { 331 foundTimestamp = true; 332 } 333 // 334 // Call the processor for this token. After the processor returns, 335 // store it for later retrieval. The token processor may store some 336 // information about the processed token 337 // 338 Processor p = cfg.getProcessor(el); 339 if (p != null) { 340 List<WSSecurityEngineResult> results = p.handleToken((Element) node, requestData); 341 if (!results.isEmpty()) { 342 returnResults.addAll(0, results); 343 } 344 } else { 345 if (doDebug) { 346 LOG.debug( 347 "Unknown Element: " + node.getLocalName() + " " + node.getNamespaceURI() 348 ); 349 } 350 } 351 } 352 // 353 // If the next sibling is null and the stored next sibling is not null, then we have 354 // encountered an EncryptedData element which was decrypted, and so the next sibling 355 // of the current node is null. In that case, go on to the previously stored next 356 // sibling 357 // 358 if (node.getNextSibling() == null && nextSibling != null 359 && nextSibling.getParentNode() != null) { 360 node = nextSibling; 361 } else { 362 node = node.getNextSibling(); 363 } 364 } 365 366 WSHandlerResult handlerResult = 367 new WSHandlerResult(requestData.getActor(), returnResults, wsDocInfo.getActionResults()); 368 369 // Validate SAML Subject Confirmation requirements 370 if (requestData.isValidateSamlSubjectConfirmation()) { 371 Element bodyElement = callbackLookupToUse.getSOAPBody(); 372 DOMSAMLUtil.validateSAMLResults(handlerResult, requestData.getTlsCerts(), bodyElement); 373 } 374 375 wsDocInfo.clear(); 376 377 return handlerResult; 378 } 379 }