/* * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.carbon.identity.oauth2.token.handlers.grant.iwa.ntlm; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.ResponseHeader; import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext; import org.wso2.carbon.identity.oauth2.token.handlers.grant.AbstractAuthorizationGrantHandler; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; import waffle.util.Base64; import waffle.windows.auth.IWindowsSecurityContext; import waffle.windows.auth.impl.WindowsAuthProviderImpl; public class NTLMAuthenticationGrantHandlerWithHandshake extends AbstractAuthorizationGrantHandler { private static Log log = LogFactory.getLog(NTLMAuthenticationGrantHandlerWithHandshake.class); private static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; private static final String SCHEME_NTLM = "NTLM"; private static final String SERVER_CONNECTION = "server-connection"; private static final String SECURITY_PACKAGE = "Negotiate"; private static final String RESPONSE_HEADERS_PROPERTY = "RESPONSE_HEADERS"; //This is the index of the byte which represents the NTLM token type. private static final int MESSAGE_TYPE_BYTE_INDEX = 8; //NTLM Token types private static final int NTLM_TYPE_1_TOKEN = 1; private static final int NTLM_TYPE_3_TOKEN = 3; //'provider' stores the current state of the handshake. i.e. when it receives TYPE-1 token and validate it, it // generates the challenge for the client and stores it in its context. When the TYPE3 token is received as the final // step, this uses the stored challenge to validate the TYPE-3 token (response to the challenge). As this maintains the // status of the handshake, there should be only one instance of 'provider', hence it is made static. private static WindowsAuthProviderImpl provider = new WindowsAuthProviderImpl(); public int getNLTMMessageType(byte[] decodedNLTMMessage) throws IdentityOAuth2Exception { int messageType; if (decodedNLTMMessage.length > MESSAGE_TYPE_BYTE_INDEX) { messageType = decodedNLTMMessage[MESSAGE_TYPE_BYTE_INDEX]; } else { throw new IdentityOAuth2Exception( "Cannot extract message type from NLTM Token. Decoded token length is less than 8."); } //NLTM token type must be one of 1,2 or 3 if (messageType < NTLM_TYPE_1_TOKEN || messageType > NTLM_TYPE_3_TOKEN) { throw new IdentityOAuth2Exception( "Invalid NLTM message type:" + messageType + ". Should be one of 1,2 or 3."); } return messageType; } @Override public boolean validateGrant(OAuthTokenReqMessageContext tokReqMsgCtx) throws IdentityOAuth2Exception { boolean validGrant = super.validateGrant(tokReqMsgCtx); if (!validGrant) { return false; } String token = tokReqMsgCtx.getOauth2AccessTokenReqDTO().getWindowsToken(); IWindowsSecurityContext serverContext = null; if (token != null) { byte[] bytesToken = Base64.decode(token); int tokenType = getNLTMMessageType(bytesToken); if (log.isDebugEnabled()) { log.debug("Received NTLM token Type " + tokenType + ":" + token); } if (tokenType == NTLM_TYPE_1_TOKEN) { serverContext = provider.acceptSecurityToken(SERVER_CONNECTION, bytesToken, SECURITY_PACKAGE); String type2Token = Base64.encode(serverContext.getToken()); if (log.isDebugEnabled()) { log.debug("Sent NTLM token Type 2:" + type2Token); } ResponseHeader[] responseHeaders = new ResponseHeader[1]; responseHeaders[0] = new ResponseHeader(); responseHeaders[0].setKey(HEADER_WWW_AUTHENTICATE); responseHeaders[0].setValue(SCHEME_NTLM + " " + type2Token); tokReqMsgCtx.addProperty(RESPONSE_HEADERS_PROPERTY, responseHeaders); return false; } else if (tokenType == NTLM_TYPE_3_TOKEN) { serverContext = provider.acceptSecurityToken(SERVER_CONNECTION, bytesToken, SECURITY_PACKAGE); String resourceOwnerUserNameWithDomain = serverContext.getIdentity().getFqn(); String resourceOwnerUserName = resourceOwnerUserNameWithDomain.split("\\\\")[1]; tokReqMsgCtx.setAuthorizedUser(OAuth2Util.getUserFromUserName(resourceOwnerUserName)); return true; } else { log.error("Unknown NTLM token, Type " + tokenType + ":" + token); return false; } } else { throw new IdentityOAuth2Exception("Received NTLM token is null"); } } }