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  package org.apache.wss4j.stax.setup;
20  
21  import java.nio.charset.StandardCharsets;
22  import java.util.Collections;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import javax.xml.parsers.ParserConfigurationException;
27  import javax.xml.stream.XMLInputFactory;
28  import javax.xml.stream.XMLStreamException;
29  import javax.xml.stream.XMLStreamReader;
30  
31  import org.apache.wss4j.common.ext.WSSecurityException;
32  import org.apache.wss4j.stax.ext.DocumentCreatorImpl;
33  import org.apache.wss4j.stax.ext.WSSConstants;
34  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
35  import org.apache.wss4j.stax.impl.InboundWSSecurityContextImpl;
36  import org.apache.wss4j.stax.impl.WSSecurityStreamReader;
37  import org.apache.wss4j.stax.impl.processor.input.OperationInputProcessor;
38  import org.apache.wss4j.stax.impl.processor.input.SecurityHeaderInputProcessor;
39  import org.apache.wss4j.stax.impl.processor.input.SignatureConfirmationInputProcessor;
40  import org.apache.wss4j.stax.securityEvent.WSSecurityEventConstants;
41  import org.apache.xml.security.exceptions.XMLSecurityException;
42  import org.apache.xml.security.stax.ext.InputProcessor;
43  import org.apache.xml.security.stax.impl.DocumentContextImpl;
44  import org.apache.xml.security.stax.impl.InputProcessorChainImpl;
45  import org.apache.xml.security.stax.impl.processor.input.LogInputProcessor;
46  import org.apache.xml.security.stax.impl.processor.input.XMLEventReaderInputProcessor;
47  import org.apache.xml.security.stax.impl.util.IDGenerator;
48  import org.apache.xml.security.stax.securityEvent.SecurityEvent;
49  import org.apache.xml.security.stax.securityEvent.SecurityEventListener;
50  import org.apache.xml.security.stax.securityEvent.TokenSecurityEvent;
51  import org.apache.xml.security.stax.securityToken.InboundSecurityToken;
52  import org.apache.xml.security.stax.securityToken.SecurityTokenProvider;
53  
54  /**
55   * Inbound Streaming-WebService-Security
56   * An instance of this class can be retrieved over the WSSec class
57   */
58  public class InboundWSSec {
59  
60      protected static final transient org.slf4j.Logger LOG =
61          org.slf4j.LoggerFactory.getLogger(InboundWSSec.class);
62  
63      private static final XMLInputFactory XML_INPUT_FACTORY = XMLInputFactory.newInstance();
64  
65      static {
66          XML_INPUT_FACTORY.setProperty(XMLInputFactory.SUPPORT_DTD, false);
67          XML_INPUT_FACTORY.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
68          try {
69              XML_INPUT_FACTORY.setProperty("org.codehaus.stax2.internNames", true);
70              XML_INPUT_FACTORY.setProperty("org.codehaus.stax2.internNsUris", true);
71              XML_INPUT_FACTORY.setProperty("org.codehaus.stax2.preserveLocation", false);
72          } catch (IllegalArgumentException e) {
73              LOG.debug(e.getMessage(), e);
74              //ignore
75          }
76      }
77  
78      private final WSSSecurityProperties securityProperties;
79      private final boolean initiator;
80      private final boolean returnSecurityError;
81  
82      public InboundWSSec(WSSSecurityProperties securityProperties) {
83          this(securityProperties, false, false);
84      }
85  
86      public InboundWSSec(WSSSecurityProperties securityProperties, boolean initiator,
87                          boolean returnSecurityError) {
88          this.securityProperties = securityProperties;
89          this.initiator = initiator;
90          this.returnSecurityError = returnSecurityError;
91      }
92  
93      /**
94       * Warning:
95       * configure your xmlStreamReader correctly. Otherwise you can create a security hole.
96       * At minimum configure the following properties:
97       * xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
98       * xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
99       * xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, false);
100      * xmlInputFactory.setProperty(WstxInputProperties.P_MIN_TEXT_SEGMENT, new Integer(8192));
101      * <p/>
102      * This method is the entry point for the incoming security-engine.
103      * Hand over the original XMLStreamReader and use the returned one for further processing
104      *
105      * @param xmlStreamReader The original XMLStreamReader
106      * @return A new XMLStreamReader which does transparently the security processing.
107      * @throws XMLStreamException  thrown when a streaming error occurs
108      * @throws WSSecurityException
109      */
110     public XMLStreamReader processInMessage(
111             XMLStreamReader xmlStreamReader) throws XMLStreamException, WSSecurityException {
112         return this.processInMessage(xmlStreamReader, null, (SecurityEventListener)null);
113     }
114 
115     /**
116      * Warning:
117      * configure your xmlStreamReader correctly. Otherwise you can create a security hole.
118      * At minimum configure the following properties:
119      * xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
120      * xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
121      * xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, false);
122      * xmlInputFactory.setProperty(WstxInputProperties.P_MIN_TEXT_SEGMENT, new Integer(8192));
123      * <p/>
124      * This method is the entry point for the incoming security-engine.
125      * Hand over the original XMLStreamReader and use the returned one for further processing
126      *
127      * @param xmlStreamReader       The original XMLStreamReader
128      * @return A new XMLStreamReader which does transparently the security processing.
129      * @throws XMLStreamException  thrown when a streaming error occurs
130      * @throws WSSecurityException
131      */
132     public XMLStreamReader processInMessage(
133             XMLStreamReader xmlStreamReader, List<SecurityEvent> requestSecurityEvents
134     ) throws XMLStreamException, WSSecurityException {
135         return this.processInMessage(xmlStreamReader, requestSecurityEvents, (SecurityEventListener)null);
136     }
137 
138     /**
139      * Warning:
140      * configure your xmlStreamReader correctly. Otherwise you can create a security hole.
141      * At minimum configure the following properties:
142      * xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
143      * xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
144      * xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, false);
145      * xmlInputFactory.setProperty(WstxInputProperties.P_MIN_TEXT_SEGMENT, new Integer(8192));
146      * <p/>
147      * This method is the entry point for the incoming security-engine.
148      * Hand over the original XMLStreamReader and use the returned one for further processing
149      *
150      * @param xmlStreamReader       The original XMLStreamReader
151      * @param securityEventListener A SecurityEventListener to receive security-relevant events.
152      * @return A new XMLStreamReader which does transparently the security processing.
153      * @throws XMLStreamException  thrown when a streaming error occurs
154      * @throws WSSecurityException
155      */
156     public XMLStreamReader processInMessage(
157             XMLStreamReader xmlStreamReader, List<SecurityEvent> requestSecurityEvents,
158             SecurityEventListener securityEventListener) throws XMLStreamException, WSSecurityException {
159         return this.processInMessage(xmlStreamReader, requestSecurityEvents,
160                                      Collections.singletonList(securityEventListener));
161     }
162     /**
163      * Warning:
164      * configure your xmlStreamReader correctly. Otherwise you can create a security hole.
165      * At minimum configure the following properties:
166      * xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
167      * xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
168      * xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, false);
169      * xmlInputFactory.setProperty(WstxInputProperties.P_MIN_TEXT_SEGMENT, new Integer(8192));
170      * <p/>
171      * This method is the entry point for the incoming security-engine.
172      * Hand over the original XMLStreamReader and use the returned one for further processing
173      *
174      * @param xmlStreamReader       The original XMLStreamReader
175      * @param securityEventListeners A list of SecurityEventListeners to receive security-relevant events.
176      * @return A new XMLStreamReader which does transparently the security processing.
177      * @throws XMLStreamException  thrown when a streaming error occurs
178      * @throws WSSecurityException
179      */
180     public XMLStreamReader processInMessage(
181             XMLStreamReader xmlStreamReader, List<SecurityEvent> requestSecurityEvents,
182             List<SecurityEventListener> securityEventListeners) throws XMLStreamException, WSSecurityException {
183 
184         if (requestSecurityEvents == null) {
185             requestSecurityEvents = Collections.emptyList();
186         }
187 
188         final InboundWSSecurityContextImpl securityContextImpl = new InboundWSSecurityContextImpl();
189         securityContextImpl.putList(SecurityEvent.class, requestSecurityEvents);
190         if (securityEventListeners != null) {
191             for (SecurityEventListener securityEventListener : securityEventListeners) {
192                 securityContextImpl.addSecurityEventListener(securityEventListener);
193             }
194         }
195         securityContextImpl.ignoredBSPRules(this.securityProperties.getIgnoredBSPRules());
196         securityContextImpl.setDisableBSPEnforcement(this.securityProperties.isDisableBSPEnforcement());
197         securityContextImpl.setAllowRSA15KeyTransportAlgorithm(this.securityProperties.isAllowRSA15KeyTransportAlgorithm());
198         securityContextImpl.setSoap12(this.securityProperties.isSoap12());
199 
200         if (securityProperties.getDocumentCreator() == null) {
201             try {
202                 securityProperties.setDocumentCreator(new DocumentCreatorImpl());
203             } catch (ParserConfigurationException e) {
204                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, e);
205             }
206         }
207 
208         if (!requestSecurityEvents.isEmpty()) {
209             try {
210                 Iterator<SecurityEvent> securityEventIterator = requestSecurityEvents.iterator();
211                 while (securityEventIterator.hasNext()) {
212                     SecurityEvent securityEvent = securityEventIterator.next();
213                     if (securityEvent instanceof TokenSecurityEvent) {
214                         @SuppressWarnings("unchecked")
215                         final TokenSecurityEvent<? extends InboundSecurityToken> tokenSecurityEvent =
216                                 (TokenSecurityEvent<? extends InboundSecurityToken>)securityEvent;
217 
218                         if (WSSecurityEventConstants.HTTPS_TOKEN.equals(securityEvent.getSecurityEventType())) {
219                             securityContextImpl.registerSecurityEvent(securityEvent);
220                             securityContextImpl.put(WSSConstants.TRANSPORT_SECURITY_ACTIVE, Boolean.TRUE);
221                         }
222 
223                         SecurityTokenProvider<InboundSecurityToken> securityTokenProvider =
224                                 new SecurityTokenProvider<InboundSecurityToken>() {
225 
226                             private String id;
227 
228                             @Override
229                             public InboundSecurityToken getSecurityToken() throws XMLSecurityException {
230                                 return tokenSecurityEvent.getSecurityToken();
231                             }
232 
233                             @Override
234                             public String getId() {
235                                 if (this.id == null) {
236                                     this.id = tokenSecurityEvent.getSecurityToken().getId();
237                                     if (this.id == null) {
238                                         this.id = IDGenerator.generateID(null);
239                                     }
240                                 }
241                                 return this.id;
242                             }
243                         };
244                         securityContextImpl.registerSecurityTokenProvider(securityTokenProvider.getId(), securityTokenProvider);
245                     }
246                 }
247             } catch (XMLSecurityException e) {
248                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, e);
249             }
250         }
251 
252         securityContextImpl.put(WSSConstants.XMLINPUTFACTORY, XML_INPUT_FACTORY);
253 
254         DocumentContextImpl documentContext = new DocumentContextImpl();
255         documentContext.setEncoding(xmlStreamReader.getEncoding() != null ? xmlStreamReader.getEncoding() : StandardCharsets.UTF_8.name());
256         InputProcessorChainImpl inputProcessorChain = new InputProcessorChainImpl(securityContextImpl, documentContext);
257         inputProcessorChain.addProcessor(new XMLEventReaderInputProcessor(securityProperties, xmlStreamReader));
258         inputProcessorChain.addProcessor(new SecurityHeaderInputProcessor(securityProperties));
259         inputProcessorChain.addProcessor(new OperationInputProcessor(securityProperties));
260 
261         if (securityProperties.isEnableSignatureConfirmationVerification()) {
262             inputProcessorChain.addProcessor(new SignatureConfirmationInputProcessor(securityProperties));
263         }
264 
265         if (LOG.isTraceEnabled()) {
266             LogInputProcessor logInputProcessor = new LogInputProcessor(securityProperties);
267             logInputProcessor.addAfterProcessor(SecurityHeaderInputProcessor.class.getName());
268             inputProcessorChain.addProcessor(logInputProcessor);
269         }
270 
271         List<InputProcessor> additionalInputProcessors = securityProperties.getInputProcessorList();
272         if (!additionalInputProcessors.isEmpty()) {
273             Iterator<InputProcessor> inputProcessorIterator = additionalInputProcessors.iterator();
274             while (inputProcessorIterator.hasNext()) {
275                 InputProcessor inputProcessor = inputProcessorIterator.next();
276                 inputProcessorChain.addProcessor(inputProcessor);
277             }
278         }
279 
280         return new WSSecurityStreamReader(inputProcessorChain, securityProperties, initiator, returnSecurityError);
281     }
282 }