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.impl.processor.input;
20  
21  import org.apache.wss4j.binding.wss10.EncodedString;
22  import org.apache.wss4j.binding.wss10.PasswordString;
23  import org.apache.wss4j.binding.wss10.UsernameTokenType;
24  import org.apache.wss4j.binding.wsu10.AttributedDateTime;
25  import org.apache.wss4j.common.bsp.BSPRule;
26  import org.apache.wss4j.common.cache.ReplayCache;
27  import org.apache.wss4j.common.ext.WSSecurityException;
28  import org.apache.wss4j.common.util.DateUtil;
29  import org.apache.wss4j.stax.ext.WSInboundSecurityContext;
30  import org.apache.wss4j.stax.ext.WSSConstants;
31  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
32  import org.apache.wss4j.stax.securityToken.UsernameSecurityToken;
33  import org.apache.wss4j.stax.securityEvent.UsernameTokenSecurityEvent;
34  import org.apache.wss4j.stax.validate.TokenContext;
35  import org.apache.wss4j.stax.validate.UsernameTokenValidator;
36  import org.apache.wss4j.stax.validate.UsernameTokenValidatorImpl;
37  import org.apache.xml.security.exceptions.XMLSecurityException;
38  import org.apache.xml.security.stax.ext.*;
39  import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
40  import org.apache.xml.security.stax.impl.util.IDGenerator;
41  import org.apache.xml.security.stax.securityToken.InboundSecurityToken;
42  import org.apache.xml.security.stax.securityToken.SecurityTokenProvider;
43  
44  import jakarta.xml.bind.JAXBElement;
45  import javax.xml.namespace.QName;
46  import javax.xml.stream.XMLStreamConstants;
47  
48  import java.time.Instant;
49  import java.time.ZonedDateTime;
50  import java.time.format.DateTimeParseException;
51  import java.util.Deque;
52  import java.util.List;
53  
54  /**
55   * Processor for the UsernameToken XML Structure
56   */
57  public class UsernameTokenInputHandler extends AbstractInputSecurityHeaderHandler {
58  
59      @Override
60      public void handle(final InputProcessorChain inputProcessorChain, final XMLSecurityProperties securityProperties,
61                         Deque<XMLSecEvent> eventQueue, Integer index) throws XMLSecurityException {
62  
63          @SuppressWarnings("unchecked")
64          final UsernameTokenType usernameTokenType =
65                  ((JAXBElement<UsernameTokenType>) parseStructure(eventQueue, index, securityProperties)).getValue();
66  
67          final List<XMLSecEvent> xmlSecEvents = getResponsibleXMLSecEvents(eventQueue, index);
68  
69          checkBSPCompliance(inputProcessorChain, usernameTokenType, xmlSecEvents);
70  
71          if (usernameTokenType.getId() == null) {
72              usernameTokenType.setId(IDGenerator.generateID(null));
73          }
74  
75          // Verify Created
76          final WSSSecurityProperties wssSecurityProperties = (WSSSecurityProperties) securityProperties;
77          Instant created = verifyCreated(wssSecurityProperties, usernameTokenType);
78  
79          ReplayCache replayCache = wssSecurityProperties.getNonceReplayCache();  //NOPMD
80          final EncodedString encodedNonce =
81                  XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSSE_NONCE);
82          if (encodedNonce != null && replayCache != null) {
83              // Check for replay attacks
84              String nonce = encodedNonce.getValue();
85              if (replayCache.contains(nonce)) {
86                  throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
87              }
88  
89              // If no Created, then just cache for the default time
90              // Otherwise, cache for the configured TTL of the UsernameToken Created time, as any
91              // older token will just get rejected anyway
92              int utTTL = wssSecurityProperties.getUtTTL();
93              if (created == null || utTTL <= 0) {
94                  replayCache.add(nonce);
95              } else {
96                  replayCache.add(nonce, Instant.now().plusSeconds(utTTL));
97              }
98          }
99  
100         final WSInboundSecurityContext wsInboundSecurityContext =
101             (WSInboundSecurityContext) inputProcessorChain.getSecurityContext();
102         final List<QName> elementPath = getElementPath(eventQueue);
103 
104         final TokenContext tokenContext =
105             new TokenContext(wssSecurityProperties, wsInboundSecurityContext, xmlSecEvents, elementPath);
106 
107         UsernameTokenValidator usernameTokenValidator =
108                 wssSecurityProperties.getValidator(WSSConstants.TAG_WSSE_USERNAME_TOKEN);
109         if (usernameTokenValidator == null) {
110             usernameTokenValidator = new UsernameTokenValidatorImpl();
111         }
112         final UsernameSecurityToken usernameSecurityToken =
113                 usernameTokenValidator.validate(usernameTokenType, tokenContext);
114 
115         SecurityTokenProvider<InboundSecurityToken> securityTokenProvider =
116                 new SecurityTokenProvider<InboundSecurityToken>() {
117 
118             @Override
119             public InboundSecurityToken getSecurityToken() throws XMLSecurityException {
120                 return (InboundSecurityToken)usernameSecurityToken;
121             }
122 
123             @Override
124             public String getId() {
125                 return usernameTokenType.getId();
126             }
127         };
128         inputProcessorChain.getSecurityContext().registerSecurityTokenProvider(usernameTokenType.getId(), securityTokenProvider);
129 
130         //fire a tokenSecurityEvent
131         UsernameTokenSecurityEvent usernameTokenSecurityEvent = new UsernameTokenSecurityEvent();
132         usernameTokenSecurityEvent.setSecurityToken((UsernameSecurityToken)securityTokenProvider.getSecurityToken());
133         // usernameTokenSecurityEvent.setUsernameTokenProfile(WSSConstants.NS_USERNAMETOKEN_PROFILE11);
134         usernameTokenSecurityEvent.setCorrelationID(usernameTokenType.getId());
135         inputProcessorChain.getSecurityContext().registerSecurityEvent(usernameTokenSecurityEvent);
136     }
137 
138     private void checkBSPCompliance(InputProcessorChain inputProcessorChain, UsernameTokenType usernameTokenType,
139                                     List<XMLSecEvent> xmlSecEvents) throws WSSecurityException {
140 
141         final WSInboundSecurityContext securityContext = (WSInboundSecurityContext) inputProcessorChain.getSecurityContext();
142         if (usernameTokenType.getAny() == null) {
143             securityContext.handleBSPRule(BSPRule.R3031);
144         }
145 
146         int passwordIndex = -1;
147         int createdIndex = -1;
148         int nonceIndex = -1;
149         for (int i = 0; i < xmlSecEvents.size(); i++) {
150             XMLSecEvent xmlSecEvent = xmlSecEvents.get(i);
151             if (xmlSecEvent.getEventType() == XMLStreamConstants.START_ELEMENT) {
152                 if (xmlSecEvent.asStartElement().getName().equals(WSSConstants.TAG_WSSE_USERNAME_TOKEN)) {
153                     continue;
154                 } else if (xmlSecEvent.asStartElement().getName().equals(WSSConstants.TAG_WSSE_PASSWORD)) {
155                     if (passwordIndex != -1) {
156                         securityContext.handleBSPRule(BSPRule.R4222);
157                     }
158                     passwordIndex = i;
159                 } else if (xmlSecEvent.asStartElement().getName().equals(WSSConstants.TAG_WSU_CREATED)) {
160                     if (createdIndex != -1) {
161                         securityContext.handleBSPRule(BSPRule.R4223);
162                     }
163                     createdIndex = i;
164                 } else if (xmlSecEvent.asStartElement().getName().equals(WSSConstants.TAG_WSSE_NONCE)) {
165                     if (nonceIndex != -1) {
166                         securityContext.handleBSPRule(BSPRule.R4225);
167                     }
168                     nonceIndex = i;
169                 }
170             }
171         }
172 
173         PasswordString passwordType =
174                 XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSSE_PASSWORD);
175         if (passwordType != null && passwordType.getType() == null) {
176             securityContext.handleBSPRule(BSPRule.R4201);
177         }
178 
179         EncodedString encodedNonce =
180                 XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSSE_NONCE);
181         if (encodedNonce != null) {
182             if (encodedNonce.getEncodingType() == null) {
183                 securityContext.handleBSPRule(BSPRule.R4220);
184             } else if (!WSSConstants.SOAPMESSAGE_NS10_BASE64_ENCODING.equals(encodedNonce.getEncodingType())) {
185                 securityContext.handleBSPRule(BSPRule.R4221);
186             }
187         }
188 
189     }
190 
191     private Instant verifyCreated(
192         WSSSecurityProperties wssSecurityProperties,
193         UsernameTokenType usernameTokenType
194     ) throws WSSecurityException {
195         // Verify Created
196         int ttl = wssSecurityProperties.getUtTTL();
197         int futureTTL = wssSecurityProperties.getUtFutureTTL();
198 
199         final AttributedDateTime attributedDateTimeCreated =
200             XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSU_CREATED);
201 
202         if (attributedDateTimeCreated != null) {
203             // Parse the Date
204             ZonedDateTime created;
205             try {
206                 created = ZonedDateTime.parse(attributedDateTimeCreated.getValue());
207             } catch (DateTimeParseException e) {
208                 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
209             }
210 
211             // Validate whether the security semantics have expired
212             if (!DateUtil.verifyCreated(created.toInstant(), ttl, futureTTL)) {
213                 throw new WSSecurityException(WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
214             }
215             return created.toInstant();
216         }
217         return null;
218     }
219 }