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.wsu10.TimestampType;
22  import org.apache.wss4j.common.bsp.BSPRule;
23  import org.apache.wss4j.common.ext.WSSecurityException;
24  import org.apache.wss4j.stax.ext.WSInboundSecurityContext;
25  import org.apache.wss4j.stax.ext.WSSConstants;
26  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
27  import org.apache.wss4j.stax.securityEvent.TimestampSecurityEvent;
28  import org.apache.wss4j.stax.validate.TimestampValidator;
29  import org.apache.wss4j.stax.validate.TimestampValidatorImpl;
30  import org.apache.wss4j.stax.validate.TokenContext;
31  import org.apache.xml.security.exceptions.XMLSecurityException;
32  import org.apache.xml.security.stax.ext.AbstractInputSecurityHeaderHandler;
33  import org.apache.xml.security.stax.ext.InputProcessorChain;
34  import org.apache.xml.security.stax.ext.XMLSecurityProperties;
35  import org.apache.xml.security.stax.ext.XMLSecurityUtils;
36  import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
37  import org.apache.xml.security.stax.impl.util.IDGenerator;
38  
39  import jakarta.xml.bind.JAXBElement;
40  import javax.xml.namespace.QName;
41  import javax.xml.stream.XMLStreamConstants;
42  
43  import java.time.ZoneOffset;
44  import java.time.ZonedDateTime;
45  import java.time.format.DateTimeParseException;
46  import java.time.temporal.ChronoField;
47  import java.util.Deque;
48  import java.util.List;
49  
50  public class TimestampInputHandler extends AbstractInputSecurityHeaderHandler {
51  
52      @Override
53      public void handle(final InputProcessorChain inputProcessorChain, final XMLSecurityProperties securityProperties,
54                         Deque<XMLSecEvent> eventQueue, Integer index) throws XMLSecurityException {
55  
56          final WSSSecurityProperties wssSecurityProperties = (WSSSecurityProperties) securityProperties;
57          final WSInboundSecurityContext wssecurityContextInbound =
58              (WSInboundSecurityContext) inputProcessorChain.getSecurityContext();
59  
60          //Chapter 10 Security Timestamps: ...may only be present at most once per header (that is, per SOAP actor/role)
61          Boolean alreadyProcessed = wssecurityContextInbound.<Boolean>get(WSSConstants.TIMESTAMP_PROCESSED);
62          if (Boolean.TRUE.equals(alreadyProcessed)) {
63              throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "invalidTimestamp",
64                                            new Object[] {"Message contains two or more timestamps"});
65          }
66          wssecurityContextInbound.put(WSSConstants.TIMESTAMP_PROCESSED, Boolean.TRUE);
67  
68          @SuppressWarnings("unchecked")
69          final TimestampType timestampType =
70                  ((JAXBElement<TimestampType>) parseStructure(eventQueue, index, securityProperties)).getValue();
71  
72          final List<XMLSecEvent> xmlSecEvents = getResponsibleXMLSecEvents(eventQueue, index);
73          List<QName> elementPath = getElementPath(eventQueue);
74  
75          checkBSPCompliance(inputProcessorChain, timestampType, xmlSecEvents);
76  
77          if (timestampType.getId() == null) {
78              timestampType.setId(IDGenerator.generateID(null));
79          }
80  
81          TimestampValidator timestampValidator = wssSecurityProperties.getValidator(WSSConstants.TAG_WSU_TIMESTAMP);
82          if (timestampValidator == null) {
83              timestampValidator = new TimestampValidatorImpl();
84          }
85          TokenContext tokenContext = new TokenContext(wssSecurityProperties, wssecurityContextInbound, xmlSecEvents, elementPath);
86          timestampValidator.validate(timestampType, tokenContext);
87  
88          TimestampSecurityEvent timestampSecurityEvent = new TimestampSecurityEvent();
89          if (timestampType.getCreated() != null) {
90              try {
91                  timestampSecurityEvent.setCreated(
92                          timestampType.getCreated().getAsZonedDateTime().toInstant());
93              } catch (IllegalArgumentException e) { //NOPMD
94                  //ignore
95              }
96          }
97          if (timestampType.getExpires() != null) {
98              try {
99                  timestampSecurityEvent.setExpires(
100                         timestampType.getExpires().getAsZonedDateTime().toInstant());
101             } catch (IllegalArgumentException e) { //NOPMD
102                 //ignore
103             }
104         }
105         timestampSecurityEvent.setCorrelationID(timestampType.getId());
106         wssecurityContextInbound.registerSecurityEvent(timestampSecurityEvent);
107         wssecurityContextInbound.put(WSSConstants.PROP_TIMESTAMP_SECURITYEVENT, timestampSecurityEvent);
108     }
109 
110     private void checkBSPCompliance(InputProcessorChain inputProcessorChain, TimestampType timestampType,
111                                     List<XMLSecEvent> xmlSecEvents) throws WSSecurityException {
112         final WSInboundSecurityContext securityContext = (WSInboundSecurityContext) inputProcessorChain.getSecurityContext();
113         if (timestampType.getCreated() == null) {
114             securityContext.handleBSPRule(BSPRule.R3203);
115         }
116 
117         int createdIndex = -1;
118         int expiresIndex = -1;
119         for (int i = 0; i < xmlSecEvents.size(); i++) {
120             XMLSecEvent xmlSecEvent = xmlSecEvents.get(i);
121             if (xmlSecEvent.getEventType() == XMLStreamConstants.START_ELEMENT) {
122                 QName name = xmlSecEvent.asStartElement().getName();
123 
124                 if (name.equals(WSSConstants.TAG_WSU_TIMESTAMP)) {
125                     continue;
126                 } else if (name.equals(WSSConstants.TAG_WSU_CREATED)) {
127                     if (createdIndex != -1) {
128                         securityContext.handleBSPRule(BSPRule.R3203);
129                     }
130                     if (expiresIndex != -1) {
131                         securityContext.handleBSPRule(BSPRule.R3221);
132                     }
133                     createdIndex = i;
134                 } else if (name.equals(WSSConstants.TAG_WSU_EXPIRES)) {
135                     if (expiresIndex != -1) {
136                         securityContext.handleBSPRule(BSPRule.R3224);
137                     }
138                     if (createdIndex == -1) {
139                         securityContext.handleBSPRule(BSPRule.R3221);
140                     }
141                     expiresIndex = i;
142                 } else {
143                     securityContext.handleBSPRule(BSPRule.R3222);
144                 }
145             }
146         }
147 
148         if (timestampType.getCreated() != null) {
149             ZonedDateTime createdDate;
150             try {
151                 createdDate = timestampType.getCreated().getAsZonedDateTime();
152             } catch (DateTimeParseException e) {
153                 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
154             }
155             
156             if (!ZoneOffset.UTC.equals(createdDate.getZone())) {
157                 securityContext.handleBSPRule(BSPRule.R3217);
158             }
159             
160             if (createdDate.getNano() > 0) {
161                 int milliseconds = createdDate.get(ChronoField.MILLI_OF_SECOND);
162                 if (milliseconds * 1000000 != createdDate.getNano()) {
163                     securityContext.handleBSPRule(BSPRule.R3220);
164                 }
165             }
166             
167             String valueType = XMLSecurityUtils.getQNameAttribute(timestampType.getCreated().getOtherAttributes(),
168                                                                   WSSConstants.ATT_NULL_VALUE_TYPE);
169             if (valueType != null) {
170                 securityContext.handleBSPRule(BSPRule.R3225);
171             }
172         } else {
173             securityContext.handleBSPRule(BSPRule.R3203);
174         }
175 
176         if (timestampType.getExpires() != null) {
177             ZonedDateTime expiresDate;
178             try {
179                 expiresDate = timestampType.getExpires().getAsZonedDateTime();
180             } catch (DateTimeParseException e) {
181                 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
182             }
183             
184             if (!ZoneOffset.UTC.equals(expiresDate.getZone())) {
185                 securityContext.handleBSPRule(BSPRule.R3223);
186             }
187             
188             if (expiresDate.getNano() > 0) {
189                 int milliseconds = expiresDate.get(ChronoField.MILLI_OF_SECOND);
190                 if (milliseconds * 1000000 != expiresDate.getNano()) {
191                     securityContext.handleBSPRule(BSPRule.R3229);
192                 }
193             }
194             
195             String valueType = XMLSecurityUtils.getQNameAttribute(timestampType.getExpires().getOtherAttributes(),
196                                                                   WSSConstants.ATT_NULL_VALUE_TYPE);
197             if (valueType != null) {
198                 securityContext.handleBSPRule(BSPRule.R3226);
199             }
200         }
201     }
202 
203 }