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  
20  package org.apache.wss4j.dom.processor;
21  
22  import java.time.Instant;
23  import java.util.List;
24  
25  import org.apache.wss4j.common.principal.SAMLTokenPrincipalImpl;
26  import org.apache.wss4j.common.principal.WSUsernameTokenPrincipalImpl;
27  import org.apache.wss4j.common.util.UsernameTokenUtil;
28  import org.w3c.dom.Element;
29  import org.apache.wss4j.common.cache.ReplayCache;
30  import org.apache.wss4j.common.ext.WSSecurityException;
31  import org.apache.wss4j.dom.WSConstants;
32  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
33  import org.apache.wss4j.dom.handler.RequestData;
34  import org.apache.wss4j.dom.message.token.UsernameToken;
35  import org.apache.wss4j.dom.validate.Credential;
36  import org.apache.wss4j.dom.validate.Validator;
37  import org.apache.xml.security.utils.XMLUtils;
38  
39  public class UsernameTokenProcessor implements Processor {
40      private static final org.slf4j.Logger LOG =
41          org.slf4j.LoggerFactory.getLogger(UsernameTokenProcessor.class);
42  
43      public List<WSSecurityEngineResult> handleToken(
44          Element elem,
45          RequestData data
46      ) throws WSSecurityException {
47          LOG.debug("Found UsernameToken list element");
48          // See if the token has been previously processed
49          String id = elem.getAttributeNS(WSConstants.WSU_NS, "Id");
50          if (id.length() != 0) {
51              Element foundElement = data.getWsDocInfo().getTokenElement(id);
52              if (elem.equals(foundElement)) {
53                  WSSecurityEngineResult result = data.getWsDocInfo().getResult(id);
54                  return java.util.Collections.singletonList(result);
55              } else if (foundElement != null) {
56                  throw new WSSecurityException(
57                      WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "duplicateError"
58                  );
59              }
60          }
61  
62          Validator validator = data.getValidator(WSConstants.USERNAME_TOKEN);
63          Credential credential = handleUsernameToken(elem, validator, data);
64          UsernameToken token = credential.getUsernametoken();
65  
66          int action = WSConstants.UT;
67          byte[] secretKey = null;
68          if (token.getPassword() == null) {
69              action = WSConstants.UT_NOPASSWORD;
70              if (token.isDerivedKey()) {
71                  String rawPassword =
72                      UsernameTokenUtil.getRawPassword(data.getCallbackHandler(), token.getName(),
73                                                       token.getPassword(), token.getPasswordType());
74                  secretKey = token.getDerivedKey(data.getBSPEnforcer(), rawPassword);
75              }
76          }
77          WSSecurityEngineResult result = new WSSecurityEngineResult(action, token);
78          String tokenId = token.getID();
79          if (tokenId.length() != 0) {
80              result.put(WSSecurityEngineResult.TAG_ID, tokenId);
81          }
82          result.put(WSSecurityEngineResult.TAG_SECRET, secretKey);
83  
84          if (validator != null) {
85              result.put(WSSecurityEngineResult.TAG_VALIDATED_TOKEN, Boolean.TRUE);
86              if (credential.getTransformedToken() != null) {
87                  result.put(
88                      WSSecurityEngineResult.TAG_TRANSFORMED_TOKEN, credential.getTransformedToken()
89                  );
90                  if (credential.getPrincipal() != null) {
91                      result.put(WSSecurityEngineResult.TAG_PRINCIPAL, credential.getPrincipal());
92                  } else {
93                      SAMLTokenPrincipalImpl samlPrincipal =
94                          new SAMLTokenPrincipalImpl(credential.getTransformedToken());
95                      result.put(WSSecurityEngineResult.TAG_PRINCIPAL, samlPrincipal);
96                  }
97              } else if (credential.getPrincipal() != null) {
98                  result.put(WSSecurityEngineResult.TAG_PRINCIPAL, credential.getPrincipal());
99              } else {
100                 WSUsernameTokenPrincipalImpl principal =
101                     new WSUsernameTokenPrincipalImpl(token.getName(), token.isHashed());
102                 if (token.getNonce() != null) {
103                     principal.setNonce(XMLUtils.decode(token.getNonce()));
104                 }
105                 principal.setPassword(token.getPassword());
106                 principal.setCreatedTime(token.getCreated());
107                 principal.setPasswordType(token.getPasswordType());
108                 result.put(WSSecurityEngineResult.TAG_PRINCIPAL, principal);
109             }
110             result.put(WSSecurityEngineResult.TAG_SUBJECT, credential.getSubject());
111         }
112 
113         data.getWsDocInfo().addTokenElement(elem);
114         data.getWsDocInfo().addResult(result);
115         return java.util.Collections.singletonList(result);
116     }
117 
118     /**
119      * Check the UsernameToken element and validate it.
120      *
121      * @param token the DOM element that contains the UsernameToken
122      * @param data The RequestData object from which to obtain configuration
123      * @return a Credential object corresponding to the (validated) Username Token
124      * @throws WSSecurityException
125      */
126     private Credential
127     handleUsernameToken(
128         Element token,
129         Validator validator,
130         RequestData data
131     ) throws WSSecurityException {
132         boolean allowNamespaceQualifiedPasswordTypes = data.isAllowNamespaceQualifiedPasswordTypes();
133         int utTTL = data.getUtTTL();
134         int futureTimeToLive = data.getUtFutureTTL();
135 
136         //
137         // Parse and validate the UsernameToken element
138         //
139         UsernameToken ut =
140             new UsernameToken(token, allowNamespaceQualifiedPasswordTypes, data.getBSPEnforcer());
141 
142         // Validate whether the security semantics have expired
143         if (!ut.verifyCreated(utTTL, futureTimeToLive)) {
144             throw new WSSecurityException(WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
145         }
146 
147         // Test for replay attacks
148         ReplayCache replayCache = data.getNonceReplayCache();   //NOPMD
149         if (replayCache != null && ut.getNonce() != null) {
150             if (replayCache.contains(ut.getNonce())) {
151                 throw new WSSecurityException(
152                     WSSecurityException.ErrorCode.INVALID_SECURITY,
153                     "badUsernameToken",
154                     new Object[] {"A replay attack has been detected"}
155                 );
156             }
157 
158             // If no Created, then just cache for the default time
159             // Otherwise, cache for the configured TTL of the UsernameToken Created time, as any
160             // older token will just get rejected anyway
161             Instant created = ut.getCreatedDate();
162             if (created == null || utTTL <= 0) {
163                 replayCache.add(ut.getNonce());
164             } else {
165                 replayCache.add(ut.getNonce(), Instant.now().plusSeconds(utTTL));
166             }
167         }
168 
169         Credential credential = new Credential();
170         credential.setUsernametoken(ut);
171         if (validator != null) {
172             return validator.validate(credential, data);
173         }
174         return credential;
175     }
176 
177 }