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.policy.stax.enforcer;
20  
21  import org.apache.wss4j.common.ext.WSSecurityException;
22  import org.apache.wss4j.common.WSSPolicyException;
23  import org.apache.wss4j.stax.ext.WSSConstants;
24  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
25  import org.apache.wss4j.stax.impl.processor.input.SecurityHeaderInputProcessor;
26  import org.apache.wss4j.stax.securityEvent.EncryptedPartSecurityEvent;
27  import org.apache.wss4j.stax.securityEvent.RequiredElementSecurityEvent;
28  import org.apache.wss4j.stax.securityEvent.RequiredPartSecurityEvent;
29  import org.apache.wss4j.stax.securityEvent.SignedPartSecurityEvent;
30  import org.apache.wss4j.stax.utils.WSSUtils;
31  import org.apache.xml.security.exceptions.XMLSecurityException;
32  import org.apache.xml.security.stax.ext.*;
33  import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
34  import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
35  import org.apache.xml.security.stax.securityEvent.ContentEncryptedElementSecurityEvent;
36  import org.apache.xml.security.stax.securityEvent.EncryptedElementSecurityEvent;
37  import org.apache.xml.security.stax.securityEvent.SignedElementSecurityEvent;
38  
39  import javax.xml.namespace.QName;
40  import javax.xml.stream.XMLStreamConstants;
41  import javax.xml.stream.XMLStreamException;
42  import javax.xml.stream.events.Attribute;
43  
44  import java.util.List;
45  
46  /**
47   * Processor to generate SecurityEvents regarding not secured elements
48   */
49  public class PolicyInputProcessor extends AbstractInputProcessor {
50  
51      private final PolicyEnforcer policyEnforcer;
52      private boolean initDone = false;
53      private boolean transportSecurityActive = false;
54  
55      public PolicyInputProcessor(PolicyEnforcer policyEnforcer, XMLSecurityProperties securityProperties) {
56          super(securityProperties);
57          this.setPhase(WSSConstants.Phase.POSTPROCESSING);
58          this.addBeforeProcessor(SecurityHeaderInputProcessor.class.getName());
59          this.policyEnforcer = policyEnforcer;
60      }
61  
62      @Override
63      public XMLSecEvent processHeaderEvent(InputProcessorChain inputProcessorChain) throws XMLStreamException, XMLSecurityException {
64          XMLSecEvent xmlSecEvent = inputProcessorChain.processHeaderEvent();
65          //test if non encrypted element have to be encrypted per policy
66          init(inputProcessorChain);
67          //if transport security is active, every element is encrypted/signed
68          //WSP1.3, 4.2.1 EncryptedParts Assertion
69          List<QName> elementPath = null;
70          if (!transportSecurityActive) {
71              elementPath = xmlSecEvent.getElementPath();
72              if (!inputProcessorChain.getDocumentContext().isInEncryptedContent()
73                      && WSSUtils.isInSecurityHeader(xmlSecEvent, elementPath,
74                      ((WSSSecurityProperties) getSecurityProperties()).getActor())) {
75                  testEncryptionPolicy(xmlSecEvent, elementPath);
76              }
77          }
78          if (xmlSecEvent.getEventType() == XMLStreamConstants.START_ELEMENT) {
79              XMLSecStartElement xmlSecStartElement = xmlSecEvent.asStartElement();
80              if (elementPath == null) {
81                  elementPath = xmlSecStartElement.getElementPath();
82              }
83              final int documentLevel = elementPath.size();
84              if (documentLevel == 3 && WSSUtils.isInSOAPHeader(elementPath)) {
85  
86                  RequiredPartSecurityEvent requiredPartSecurityEvent = new RequiredPartSecurityEvent();
87                  requiredPartSecurityEvent.setElementPath(elementPath);
88                  policyEnforcer.registerSecurityEvent(requiredPartSecurityEvent);
89                  RequiredElementSecurityEvent requiredElementSecurityEvent = new RequiredElementSecurityEvent();
90                  requiredElementSecurityEvent.setElementPath(elementPath);
91                  policyEnforcer.registerSecurityEvent(requiredElementSecurityEvent);
92              } else if (documentLevel > 3) {
93                  //test for required elements
94                  RequiredElementSecurityEvent requiredElementSecurityEvent = new RequiredElementSecurityEvent();
95                  requiredElementSecurityEvent.setElementPath(elementPath);
96                  policyEnforcer.registerSecurityEvent(requiredElementSecurityEvent);
97              }
98          }
99          return xmlSecEvent;
100     }
101 
102     @Override
103     public XMLSecEvent processEvent(InputProcessorChain inputProcessorChain) throws XMLStreamException, XMLSecurityException {
104         XMLSecEvent xmlSecEvent = inputProcessorChain.processEvent();
105 
106         List<QName> elementPath = null;
107         if (XMLStreamConstants.START_ELEMENT == xmlSecEvent.getEventType()) {
108             XMLSecStartElement xmlSecStartElement = xmlSecEvent.asStartElement();
109             int documentLevel = xmlSecStartElement.getDocumentLevel();
110             //test for required elements
111             if (documentLevel > 3) {
112                 RequiredElementSecurityEvent requiredElementSecurityEvent = new RequiredElementSecurityEvent();
113                 elementPath = xmlSecStartElement.getElementPath();
114                 requiredElementSecurityEvent.setElementPath(elementPath);
115                 policyEnforcer.registerSecurityEvent(requiredElementSecurityEvent);
116             }
117         }
118 
119         //if transport security is active, every element is encrypted/signed
120         //WSP1.3, 4.2.1 EncryptedParts Assertion
121         //test if non encrypted element have to be encrypted per policy
122         if (!transportSecurityActive) {
123             final DocumentContext documentContext = inputProcessorChain.getDocumentContext();
124             final boolean inEncryptedContent = documentContext.isInEncryptedContent();
125             final boolean inSignedContent = documentContext.isInSignedContent();
126             if (!inEncryptedContent || !inSignedContent) {
127                 if (elementPath == null) {
128                     elementPath = xmlSecEvent.getElementPath();
129                 }
130                 if (!inEncryptedContent
131                         && !WSSUtils.isInSecurityHeader(xmlSecEvent, elementPath,
132                         ((WSSSecurityProperties) getSecurityProperties()).getActor())) {
133                     testEncryptionPolicy(xmlSecEvent, elementPath);
134                 }
135 
136                 //WSP1.3, 4.1.1 SignedParts Assertion
137                 //test if non signed element have to be signed per policy
138                 if (!inSignedContent) {
139                     testSignaturePolicy(xmlSecEvent, elementPath);
140                 }
141             }
142         }
143         return xmlSecEvent;
144     }
145 
146     @Override
147     public void doFinal(InputProcessorChain inputProcessorChain) throws XMLStreamException, XMLSecurityException {
148         super.doFinal(inputProcessorChain);
149 
150         try {
151             policyEnforcer.doFinal();
152         } catch (WSSPolicyException e) {
153             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
154         }
155     }
156 
157     private void testSignaturePolicy(XMLSecEvent xmlSecEvent, List<QName> elementPath) throws WSSecurityException {
158         if (xmlSecEvent.getEventType() == XMLStreamConstants.START_ELEMENT) {
159             final int documentLevel = elementPath.size();
160             if (documentLevel == 3 && WSSUtils.isInSOAPHeader(elementPath)) {
161                 SignedPartSecurityEvent signedPartSecurityEvent = new SignedPartSecurityEvent(null, false, null);
162                 signedPartSecurityEvent.setElementPath(elementPath);
163                 policyEnforcer.registerSecurityEvent(signedPartSecurityEvent);
164             } else if (documentLevel == 2 && WSSUtils.isInSOAPBody(elementPath)) {
165                 SignedPartSecurityEvent signedPartSecurityEvent = new SignedPartSecurityEvent(null, false, null);
166                 signedPartSecurityEvent.setElementPath(elementPath);
167                 policyEnforcer.registerSecurityEvent(signedPartSecurityEvent);
168             } else if (documentLevel > 3) {
169                 SignedElementSecurityEvent signedElementSecurityEvent = new SignedElementSecurityEvent(null, false, null);
170                 signedElementSecurityEvent.setElementPath(elementPath);
171                 policyEnforcer.registerSecurityEvent(signedElementSecurityEvent);
172             }
173         }
174     }
175 
176     private void testEncryptionPolicy(XMLSecEvent xmlSecEvent, List<QName> elementPath) throws WSSecurityException {
177         //the following events are only interesting for policy verification. So call directly the policyEnforcer for these
178         switch (xmlSecEvent.getEventType()) {
179             case XMLStreamConstants.START_ELEMENT:
180                 final int documentLevel = elementPath.size();
181                 if (documentLevel == 3 && WSSUtils.isInSOAPHeader(elementPath)) {
182 
183                     EncryptedPartSecurityEvent encryptedPartSecurityEvent
184                             = new EncryptedPartSecurityEvent(null, false, null);
185                     encryptedPartSecurityEvent.setElementPath(elementPath);
186                     policyEnforcer.registerSecurityEvent(encryptedPartSecurityEvent);
187                 } else if (documentLevel == 3 && WSSUtils.isInSOAPBody(elementPath)) {
188                     //the body element has documentLevel 2 but we have to use 3 because
189                     //the body element itself is never encrypted but child elements are. So we
190                     //test for the body child element.
191 
192                     EncryptedPartSecurityEvent encryptedPartSecurityEvent
193                             = new EncryptedPartSecurityEvent(null, false, null);
194                     encryptedPartSecurityEvent.setElementPath(elementPath);
195                     policyEnforcer.registerSecurityEvent(encryptedPartSecurityEvent);
196                 } else if (documentLevel > 3) {
197 
198                     EncryptedElementSecurityEvent encryptedElementSecurityEvent
199                             = new EncryptedElementSecurityEvent(null, false, null);
200                     encryptedElementSecurityEvent.setCorrelationID(getId(xmlSecEvent));
201                     encryptedElementSecurityEvent.setElementPath(elementPath);
202                     policyEnforcer.registerSecurityEvent(encryptedElementSecurityEvent);
203 
204                     //... or it could be a contentEncryption too...
205                     ContentEncryptedElementSecurityEvent contentEncryptedElementSecurityEvent
206                             = new ContentEncryptedElementSecurityEvent(null, false, null);
207                     contentEncryptedElementSecurityEvent.setElementPath(xmlSecEvent.getParentXMLSecStartElement().getElementPath());
208                     policyEnforcer.registerSecurityEvent(contentEncryptedElementSecurityEvent);
209                 }
210                 break;
211             case XMLStreamConstants.CHARACTERS:
212             case XMLStreamConstants.ENTITY_REFERENCE:
213             case XMLStreamConstants.PROCESSING_INSTRUCTION:
214                 //can only be a content encryption
215                 ContentEncryptedElementSecurityEvent contentEncryptedElementSecurityEvent
216                         = new ContentEncryptedElementSecurityEvent(null, false, null);
217                 contentEncryptedElementSecurityEvent.setElementPath(xmlSecEvent.getElementPath());
218                 policyEnforcer.registerSecurityEvent(contentEncryptedElementSecurityEvent);
219                 break;
220         }
221     }
222 
223     protected String getId(XMLSecEvent xmlSecEvent) {
224         XMLSecStartElement xmlSecStartElement = xmlSecEvent.asStartElement();
225         if (!xmlSecStartElement.getOnElementDeclaredAttributes().isEmpty()) {
226             Attribute attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_WSU_ID);
227             if (attribute != null) {
228                 return attribute.getValue();
229             }
230             attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_NULL_Id);
231             if (attribute != null) {
232                 return attribute.getValue();
233             }
234             attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_NULL_ID);
235             if (attribute != null) {
236                 return attribute.getValue();
237             }
238             attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_NULL_ASSERTION_ID);
239             if (attribute != null) {
240                 return attribute.getValue();
241             }
242         }
243         return null;
244     }
245 
246     protected void init(InputProcessorChain inputProcessorChain) {
247         if (!this.initDone) {
248             this.initDone = true;
249             this.transportSecurityActive =
250                 Boolean.TRUE.equals(inputProcessorChain.getSecurityContext().get(WSSConstants.TRANSPORT_SECURITY_ACTIVE));
251             inputProcessorChain.getSecurityContext().put(WSSConstants.PROP_ALLOW_RSA15_KEYTRANSPORT_ALGORITHM, Boolean.TRUE);
252             inputProcessorChain.getSecurityContext().put(WSSConstants.PROP_ALLOW_USERNAMETOKEN_NOPASSWORD, Boolean.TRUE.toString());
253         }
254     }
255 }