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.validate;
20  
21  import java.nio.charset.StandardCharsets;
22  import java.security.MessageDigest;
23  
24  import org.apache.wss4j.binding.wss10.AttributedString;
25  import org.apache.wss4j.binding.wss10.EncodedString;
26  import org.apache.wss4j.binding.wss10.PasswordString;
27  import org.apache.wss4j.binding.wss10.UsernameTokenType;
28  import org.apache.wss4j.binding.wsu10.AttributedDateTime;
29  import org.apache.wss4j.common.ext.WSPasswordCallback;
30  import org.apache.wss4j.common.ext.WSSecurityException;
31  import org.apache.wss4j.common.util.UsernameTokenUtil;
32  import org.apache.wss4j.stax.ext.WSSConstants;
33  import org.apache.wss4j.stax.securityToken.UsernameSecurityToken;
34  import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
35  import org.apache.wss4j.stax.utils.WSSUtils;
36  import org.apache.wss4j.stax.impl.securityToken.UsernameSecurityTokenImpl;
37  import org.apache.xml.security.stax.ext.XMLSecurityUtils;
38  import org.apache.xml.security.stax.securityToken.InboundSecurityToken;
39  import org.apache.xml.security.utils.XMLUtils;
40  
41  public class UsernameTokenValidatorImpl implements UsernameTokenValidator {
42  
43      private static final transient org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(UsernameTokenValidatorImpl.class);
44  
45      @Override
46      public <T extends UsernameSecurityToken & InboundSecurityToken> T validate(
47              UsernameTokenType usernameTokenType, TokenContext tokenContext) throws WSSecurityException {
48  
49          // If the UsernameToken is to be used for key derivation, the (1.1)
50          // spec says that it cannot contain a password, and it must contain
51          // an Iteration element
52          final byte[] salt = XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSSE11_SALT);
53          PasswordString passwordType = XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSSE_PASSWORD);
54          final Long iteration = XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSSE11_ITERATION);
55          if (salt != null && (passwordType != null || iteration == null)) {
56              throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "badTokenType01");
57          }
58  
59          boolean handleCustomPasswordTypes = tokenContext.getWssSecurityProperties().getHandleCustomPasswordTypes();
60          boolean allowUsernameTokenNoPassword =
61              tokenContext.getWssSecurityProperties().isAllowUsernameTokenNoPassword()
62                  || Boolean.parseBoolean((String)tokenContext.getWsSecurityContext().get(WSSConstants.PROP_ALLOW_USERNAMETOKEN_NOPASSWORD));
63  
64          // Check received password type against required type
65          WSSConstants.UsernameTokenPasswordType requiredPasswordType =
66              tokenContext.getWssSecurityProperties().getUsernameTokenPasswordType();
67          if (requiredPasswordType != null) {
68              if (passwordType == null || passwordType.getType() == null) {
69                  LOG.warn("Authentication failed as the received password type does not "
70                      + "match the required password type of: {}", requiredPasswordType);
71                  throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
72              }
73              WSSConstants.UsernameTokenPasswordType usernameTokenPasswordType =
74                  WSSConstants.UsernameTokenPasswordType.getUsernameTokenPasswordType(passwordType.getType());
75              if (requiredPasswordType != usernameTokenPasswordType) {
76                  LOG.warn("Authentication failed as the received password type does not "
77                      + "match the required password type of: {}", requiredPasswordType);
78                  throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
79              }
80          }
81  
82          WSSConstants.UsernameTokenPasswordType usernameTokenPasswordType = WSSConstants.UsernameTokenPasswordType.PASSWORD_NONE;
83          if (passwordType != null && passwordType.getType() != null) {
84              usernameTokenPasswordType = WSSConstants.UsernameTokenPasswordType.getUsernameTokenPasswordType(passwordType.getType());
85          }
86  
87          final AttributedString username = usernameTokenType.getUsername();
88          if (username == null) {
89              throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "badTokenType01");
90          }
91  
92          final EncodedString encodedNonce =
93                  XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSSE_NONCE);
94          byte[] nonceVal = null;
95          if (encodedNonce != null && encodedNonce.getValue() != null) {
96              nonceVal = XMLUtils.decode(encodedNonce.getValue());
97          }
98  
99          final AttributedDateTime attributedDateTimeCreated =
100                 XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSU_CREATED);
101 
102         String created = null;
103         if (attributedDateTimeCreated != null) {
104             created = attributedDateTimeCreated.getValue();
105         }
106 
107         if (usernameTokenPasswordType == WSSConstants.UsernameTokenPasswordType.PASSWORD_DIGEST) {
108             if (encodedNonce == null || attributedDateTimeCreated == null) {
109                 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "badTokenType01");
110             }
111 
112             if (!WSSConstants.SOAPMESSAGE_NS10_BASE64_ENCODING.equals(encodedNonce.getEncodingType())) {
113                 throw new WSSecurityException(WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN, "badTokenType01");
114             }
115 
116             verifyDigestPassword(username.getValue(), passwordType, nonceVal, created, tokenContext);
117         } else if (usernameTokenPasswordType == WSSConstants.UsernameTokenPasswordType.PASSWORD_TEXT
118                 || passwordType != null && passwordType.getValue() != null
119                 && usernameTokenPasswordType == WSSConstants.UsernameTokenPasswordType.PASSWORD_NONE) {
120 
121             verifyPlaintextPassword(username.getValue(), passwordType, tokenContext);
122         } else if (passwordType != null && passwordType.getValue() != null) {
123             if (!handleCustomPasswordTypes) {
124                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
125             }
126             verifyCustomPassword(username.getValue(), passwordType, tokenContext);
127         } else {
128             if (!allowUsernameTokenNoPassword) {
129                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
130             }
131         }
132 
133         final String password;
134         if (passwordType != null) {
135             password = passwordType.getValue();
136         } else if (salt != null) {
137             WSPasswordCallback pwCb = new WSPasswordCallback(username.getValue(),
138                    WSPasswordCallback.USERNAME_TOKEN);
139             try {
140                 WSSUtils.doPasswordCallback(tokenContext.getWssSecurityProperties().getCallbackHandler(), pwCb);
141             } catch (WSSecurityException e) {
142                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION, e);
143             }
144             password = pwCb.getPassword();
145         } else {
146             password = null;
147         }
148 
149         UsernameSecurityTokenImpl usernameSecurityToken = new UsernameSecurityTokenImpl(
150                 usernameTokenPasswordType, username.getValue(), password, created,
151                 nonceVal, salt, iteration,
152                 tokenContext.getWsSecurityContext(), usernameTokenType.getId(),
153                 WSSecurityTokenConstants.KEYIDENTIFIER_SECURITY_TOKEN_DIRECT_REFERENCE);
154         usernameSecurityToken.setElementPath(tokenContext.getElementPath());
155         usernameSecurityToken.setXMLSecEvent(tokenContext.getFirstXMLSecEvent());
156 
157         @SuppressWarnings("unchecked")
158         T token = (T)usernameSecurityToken;
159         return token;
160     }
161 
162     /**
163      * Verify a UsernameToken containing a password digest.
164      */
165     protected void verifyDigestPassword(
166         String username,
167         PasswordString passwordType,
168         byte[] nonceVal,
169         String created,
170         TokenContext tokenContext
171     ) throws WSSecurityException {
172         WSPasswordCallback pwCb = new WSPasswordCallback(username,
173                 null,
174                 passwordType.getType(),
175                 WSPasswordCallback.USERNAME_TOKEN);
176         try {
177             WSSUtils.doPasswordCallback(tokenContext.getWssSecurityProperties().getCallbackHandler(), pwCb);
178         } catch (WSSecurityException e) {
179             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION, e);
180         }
181 
182         if (pwCb.getPassword() == null) {
183             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
184         }
185 
186         byte[] passDigest = UsernameTokenUtil.doRawPasswordDigest(nonceVal, created,
187                 pwCb.getPassword().getBytes(StandardCharsets.UTF_8));
188         byte[] decodedPassword = XMLUtils.decode(passwordType.getValue());
189         if (!MessageDigest.isEqual(decodedPassword, passDigest)) {
190             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
191         }
192         passwordType.setValue(pwCb.getPassword());
193     }
194 
195     /**
196      * Verify a UsernameToken containing a plaintext password.
197      */
198     protected void verifyPlaintextPassword(
199         String username,
200         PasswordString passwordType,
201         TokenContext tokenContext
202     ) throws WSSecurityException {
203         WSPasswordCallback pwCb = new WSPasswordCallback(username,
204                 null,
205                 passwordType.getType(),
206                 WSPasswordCallback.USERNAME_TOKEN);
207         try {
208             WSSUtils.doPasswordCallback(tokenContext.getWssSecurityProperties().getCallbackHandler(), pwCb);
209         } catch (WSSecurityException e) {
210             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION, e);
211         }
212 
213         if (pwCb.getPassword() == null) {
214             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
215         }
216 
217         byte[] origPasswordBytes = pwCb.getPassword().getBytes(StandardCharsets.UTF_8);
218         byte[] passwordBytes = passwordType.getValue().getBytes(StandardCharsets.UTF_8);
219         if (!MessageDigest.isEqual(origPasswordBytes, passwordBytes)) {
220             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
221         }
222         passwordType.setValue(pwCb.getPassword());
223     }
224 
225     /**
226      * Verify a UsernameToken containing a password of some unknown (but specified) password
227      * type.
228      */
229     protected void verifyCustomPassword(
230         String username,
231         PasswordString passwordType,
232         TokenContext tokenContext
233     ) throws WSSecurityException {
234         verifyPlaintextPassword(username, passwordType, tokenContext);
235     }
236 }