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.assertionStates;
20  
21  import org.apache.neethi.Assertion;
22  import org.apache.wss4j.policy.AssertionState;
23  import org.apache.wss4j.policy.SPConstants;
24  import org.apache.wss4j.common.WSSPolicyException;
25  import org.apache.wss4j.policy.model.AbstractSymmetricAsymmetricBinding;
26  import org.apache.wss4j.policy.stax.Assertable;
27  import org.apache.wss4j.policy.stax.DummyPolicyAsserter;
28  import org.apache.wss4j.policy.stax.PolicyAsserter;
29  import org.apache.wss4j.stax.ext.WSSConstants;
30  import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
31  import org.apache.wss4j.stax.utils.WSSUtils;
32  import org.apache.wss4j.stax.securityEvent.WSSecurityEventConstants;
33  import org.apache.xml.security.exceptions.XMLSecurityException;
34  import org.apache.xml.security.stax.securityEvent.SecurityEvent;
35  import org.apache.xml.security.stax.securityEvent.SecurityEventConstants;
36  import org.apache.xml.security.stax.securityEvent.SignedElementSecurityEvent;
37  import org.apache.xml.security.stax.securityEvent.TokenSecurityEvent;
38  import org.apache.xml.security.stax.securityToken.InboundSecurityToken;
39  import org.apache.xml.security.stax.securityToken.SecurityToken;
40  
41  import javax.xml.namespace.QName;
42  
43  import java.util.ArrayList;
44  import java.util.LinkedList;
45  import java.util.List;
46  
47  /**
48   * WSP1.3, 6.5 Token Protection Property
49   */
50  public class TokenProtectionAssertionState extends AssertionState implements Assertable {
51  
52      private final List<SignedElementSecurityEvent> signedElementEvents = new ArrayList<>();
53      private final List<TokenSecurityEvent<? extends SecurityToken>> tokenSecurityEvents = new ArrayList<>();
54      private PolicyAsserter policyAsserter;
55      private final boolean soap12;
56  
57      public TokenProtectionAssertionState(Assertion assertion,
58                                           PolicyAsserter policyAsserter,
59                                           boolean initialAssertionState,
60                                           boolean soap12) {
61          super(assertion, initialAssertionState);
62  
63          this.policyAsserter = policyAsserter;
64          if (this.policyAsserter == null) {
65              this.policyAsserter = new DummyPolicyAsserter();
66          }
67  
68          if (initialAssertionState) {
69              String namespace = getAssertion().getName().getNamespaceURI();
70              policyAsserter.assertPolicy(new QName(namespace, SPConstants.PROTECT_TOKENS));
71          }
72  
73          this.soap12 = soap12;
74      }
75  
76      @Override
77      public SecurityEventConstants.Event[] getSecurityEventType() {
78          return new SecurityEventConstants.Event[]{
79                  SecurityEventConstants.SignedElement,
80                  WSSecurityEventConstants.EncryptedKeyToken,
81                  WSSecurityEventConstants.ISSUED_TOKEN,
82                  WSSecurityEventConstants.KERBEROS_TOKEN,
83                  SecurityEventConstants.KeyValueToken,
84                  WSSecurityEventConstants.REL_TOKEN,
85                  WSSecurityEventConstants.SAML_TOKEN,
86                  WSSecurityEventConstants.SECURITY_CONTEXT_TOKEN,
87                  WSSecurityEventConstants.USERNAME_TOKEN,
88                  SecurityEventConstants.X509Token,
89                  WSSecurityEventConstants.OPERATION,
90          };
91      }
92  
93      @Override
94      public boolean assertEvent(SecurityEvent securityEvent) throws WSSPolicyException, XMLSecurityException {
95  
96          AbstractSymmetricAsymmetricBinding abstractSymmetricAsymmetricBinding = (AbstractSymmetricAsymmetricBinding) getAssertion();
97          boolean protectTokens = abstractSymmetricAsymmetricBinding.isProtectTokens();
98          String namespace = getAssertion().getName().getNamespaceURI();
99  
100         if (securityEvent instanceof SignedElementSecurityEvent) {
101             SignedElementSecurityEvent signedElementSecurityEvent = (SignedElementSecurityEvent) securityEvent;
102             if (signedElementSecurityEvent.isSigned()) {
103                 signedElementEvents.add(signedElementSecurityEvent);
104             }
105         } else if (securityEvent instanceof TokenSecurityEvent) {
106             @SuppressWarnings("unchecked")
107             TokenSecurityEvent<? extends SecurityToken> tokenSecurityEvent
108                 = (TokenSecurityEvent<? extends SecurityToken>) securityEvent;
109             tokenSecurityEvents.add(tokenSecurityEvent);
110         } else { //Operation
111             for (int i = 0; i < tokenSecurityEvents.size(); i++) {
112                 TokenSecurityEvent<? extends SecurityToken> tokenSecurityEvent = tokenSecurityEvents.get(i);
113 
114                 SecurityToken securityToken = getEffectiveSignatureToken(tokenSecurityEvent.getSecurityToken());
115 
116                 //a token can only be signed if it is included in the message:
117                 if (((InboundSecurityToken)securityToken).isIncludedInMessage() && isSignatureToken(securityToken)) {
118                     //[WSP1.3_8.9]
119                     boolean signsItsSignatureToken = signsItsSignatureToken(securityToken);
120                     if (protectTokens && !signsItsSignatureToken) {
121                         setAsserted(false);
122                         setErrorMessage("Token " + WSSUtils.pathAsString(((InboundSecurityToken)securityToken).getElementPath())
123                             + " must be signed by its signature.");
124                         policyAsserter.unassertPolicy(new QName(namespace, SPConstants.PROTECT_TOKENS),
125                                                       getErrorMessage());
126                         return false;
127                     } else if (!protectTokens && signsItsSignatureToken) {
128                         setAsserted(false);
129                         setErrorMessage("Token " + WSSUtils.pathAsString(((InboundSecurityToken)securityToken).getElementPath())
130                             + " must not be signed by its signature.");
131                         policyAsserter.unassertPolicy(new QName(namespace, SPConstants.PROTECT_TOKENS),
132                                                       getErrorMessage());
133                         return false;
134                     }
135                 }
136 
137                 if (isEndorsingToken(securityToken) && !signsMainSignature(securityToken)) {
138                     //[WSP1.3_8.9b]
139                     setAsserted(false);
140                     setErrorMessage("Token " + WSSUtils.pathAsString(((InboundSecurityToken)securityToken).getElementPath())
141                         + " must sign the main signature.");
142                     policyAsserter.unassertPolicy(new QName(namespace, SPConstants.PROTECT_TOKENS),
143                                                   getErrorMessage());
144                     return false;
145                 }
146 
147                 if (isMainSignatureToken(securityToken)
148                         && !signsSignedSupportingTokens(securityToken)) {
149                     setAsserted(false);
150                     setErrorMessage("Main signature must sign the Signed*Supporting-Tokens.");
151                     policyAsserter.unassertPolicy(new QName(namespace, SPConstants.PROTECT_TOKENS),
152                                                   getErrorMessage());
153                     return false;
154                 }
155             }
156         }
157 
158         policyAsserter.assertPolicy(new QName(namespace, SPConstants.PROTECT_TOKENS));
159         return true;
160     }
161 
162     private boolean isSignatureToken(SecurityToken securityToken) {
163         List<WSSecurityTokenConstants.TokenUsage> tokenUsages = securityToken.getTokenUsages();
164         for (int i = 0; i < tokenUsages.size(); i++) {
165             WSSecurityTokenConstants.TokenUsage tokenUsage = tokenUsages.get(i);
166             if (WSSecurityTokenConstants.TokenUsage_Signature.equals(tokenUsage)
167                     || WSSecurityTokenConstants.TOKENUSAGE_MAIN_SIGNATURE.equals(tokenUsage)
168                     || tokenUsage.getName().contains("Endorsing")) {
169                 return true;
170             }
171         }
172         return false;
173     }
174 
175     private boolean isEndorsingToken(SecurityToken securityToken) throws XMLSecurityException {
176         SecurityToken rootToken = WSSUtils.getRootToken(securityToken);
177         List<WSSecurityTokenConstants.TokenUsage> tokenUsages = rootToken.getTokenUsages();
178         for (int i = 0; i < tokenUsages.size(); i++) {
179             WSSecurityTokenConstants.TokenUsage tokenUsage = tokenUsages.get(i);
180             if (tokenUsage.getName().contains("Endorsing")) {
181                 return true;
182             }
183         }
184         return false;
185     }
186 
187     private boolean isSignedSupportingToken(SecurityToken securityToken) throws XMLSecurityException {
188         SecurityToken rootToken = WSSUtils.getRootToken(securityToken);
189         List<WSSecurityTokenConstants.TokenUsage> tokenUsages = rootToken.getTokenUsages();
190         for (int i = 0; i < tokenUsages.size(); i++) {
191             WSSecurityTokenConstants.TokenUsage tokenUsage = tokenUsages.get(i);
192             if (tokenUsage.getName().contains("Signed")) {
193                 return true;
194             }
195         }
196         return false;
197     }
198 
199     private boolean isMainSignatureToken(SecurityToken securityToken) throws XMLSecurityException {
200         SecurityToken rootToken = WSSUtils.getRootToken(securityToken);
201         List<WSSecurityTokenConstants.TokenUsage> tokenUsages = rootToken.getTokenUsages();
202         return tokenUsages.contains(WSSecurityTokenConstants.TOKENUSAGE_MAIN_SIGNATURE);
203     }
204 
205     private boolean signsMainSignature(SecurityToken securityToken) throws XMLSecurityException {
206 
207         List<QName> signaturePath = new LinkedList<>();
208         if (soap12) {
209             signaturePath.addAll(WSSConstants.SOAP_12_WSSE_SECURITY_HEADER_PATH);
210         } else {
211             signaturePath.addAll(WSSConstants.SOAP_11_WSSE_SECURITY_HEADER_PATH);
212         }
213         signaturePath.add(WSSConstants.TAG_dsig_Signature);
214 
215         for (int i = 0; i < signedElementEvents.size(); i++) {
216             SignedElementSecurityEvent signedElementSecurityEvent = signedElementEvents.get(i);
217             if (WSSUtils.pathMatches(signedElementSecurityEvent.getElementPath(), signaturePath)) {
218                 SecurityToken signingSecurityToken = getEffectiveSignatureToken(signedElementSecurityEvent.getSecurityToken());
219                 //todo ATM me just check if the token signs a signature but we don't know if it's the main signature
220                 if (signingSecurityToken != null && signingSecurityToken.getId().equals(securityToken.getId())) {
221                     return true;
222                 }
223             }
224         }
225         return false;
226     }
227 
228     private boolean signsItsSignatureToken(SecurityToken securityToken) throws XMLSecurityException {
229         for (int i = 0; i < signedElementEvents.size(); i++) {
230             SignedElementSecurityEvent signedElementSecurityEvent = signedElementEvents.get(i);
231             if (WSSUtils.pathMatches(signedElementSecurityEvent.getElementPath(),
232                                      ((InboundSecurityToken)securityToken).getElementPath())) {
233 
234                 SecurityToken signingSecurityToken = signedElementSecurityEvent.getSecurityToken();
235                 signingSecurityToken = getEffectiveSignatureToken(signingSecurityToken);
236 
237                 if (signingSecurityToken.getId().equals(securityToken.getId())) {
238                     //ok we've found the correlating signedElementSecurityEvent. Now we have to find the Token that
239                     //is covered by this signedElementSecurityEvent:
240                     for (int j = 0; j < tokenSecurityEvents.size(); j++) {
241                         TokenSecurityEvent<? extends SecurityToken> tokenSecurityEvent = tokenSecurityEvents.get(j);
242                         SecurityToken st = getEffectiveSignatureToken(tokenSecurityEvent.getSecurityToken());
243 
244                         if (signedElementSecurityEvent.getXmlSecEvent() == ((InboundSecurityToken)st).getXMLSecEvent()) {
245                             //...and we got the covered token
246                             //next we have to see if the token is the same:
247                             if (st.getId().equals(securityToken.getId())) { //NOPMD
248                                 return true;
249                             }
250                         }
251                     }
252                 }
253             }
254         }
255         return false;
256     }
257 
258     private boolean signsSignedSupportingTokens(SecurityToken securityToken) throws XMLSecurityException {
259 
260         List<SecurityToken> signedSupportingTokens = new LinkedList<>();
261         List<SignedElementSecurityEvent> signedElements = new LinkedList<>();
262 
263         for (int i = 0; i < tokenSecurityEvents.size(); i++) {
264             TokenSecurityEvent<? extends SecurityToken> tokenSecurityEvent = tokenSecurityEvents.get(i);
265             SecurityToken supportingToken = tokenSecurityEvent.getSecurityToken();
266             if (isSignedSupportingToken(supportingToken)) {
267                 if (signedSupportingTokens.contains(supportingToken)) {
268                     continue;
269                 }
270                 signedSupportingTokens.add(supportingToken);
271                 List<QName> elementPath = ((InboundSecurityToken)supportingToken).getElementPath();
272 
273                 boolean found = false;
274                 for (int j = 0; j < signedElementEvents.size(); j++) {
275                     SignedElementSecurityEvent signedElementSecurityEvent = signedElementEvents.get(j);
276                     if (WSSUtils.pathMatches(signedElementSecurityEvent.getElementPath(), elementPath)) {
277                         SecurityToken elementSignatureToken = getEffectiveSignatureToken(signedElementSecurityEvent.getSecurityToken());
278 
279                         if (elementSignatureToken != null && elementSignatureToken.getId().equals(securityToken.getId())) {
280                             if (!signedElements.contains(signedElementSecurityEvent)) {
281                                 signedElements.add(signedElementSecurityEvent);
282                             }
283                             found = true;
284                         }
285                     }
286                 }
287                 if (!found) {
288                     return false;
289                 }
290             }
291         }
292         return signedSupportingTokens.size() <= signedElements.size();
293     }
294 
295     private SecurityToken getEffectiveSignatureToken(SecurityToken securityToken) throws XMLSecurityException {
296         SecurityToken tmp = WSSUtils.getRootToken(securityToken);
297         List<? extends SecurityToken> wrappedTokens = tmp.getWrappedTokens();
298         for (int i = 0; i < wrappedTokens.size(); i++) {
299             SecurityToken token = wrappedTokens.get(i);
300             if (isSignatureToken(token)) {
301                 //WSP 1.3, 6.5 [Token Protection] Property: Note that in cases where derived keys are used
302                 //the 'main' token, and NOT the derived key token, is covered by the signature.
303                 if (WSSecurityTokenConstants.DerivedKeyToken.equals(token.getTokenType())) {
304                     return tmp;
305                 }
306                 tmp = token;
307             }
308         }
309         return tmp;
310     }
311 }