Monday, November 9, 2015

How to create a custom URL for a tenant for API Manager Store


Default URL for API Manager Store is https://<hostname>:9443/store. Instead of that you can use a URL like https://store.wso2apps.com to access the Store.

Setting up a custom URL for Store:


 To enable the custom URL using a reverse proxy you need to install nginx. To install nginx and setup SSL certificates you can follow.

http://sanjeewamalalgoda.blogspot.com/2014/12/configure-wso2-api-manager-with-reverse.html

Installing Nginx:
sudo apt-get install nginx

Creating a SSL certificate:
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

Go to <APIM_HOME>/repository/resources/security folder using terminal.

Then use following command to add the certificate in to client trust store.

keytool -import -file /etc/nginx/ssl/nginx.crt1 -keystore client-truststore.jks -storepass wso2carbon -alias wso2carbon2

Use the following command to open nginx configuration in gedit.

sudo gedit /etc/nginx/sites-enabled/default

Add the following configuration:

You can use a host name you like such as "store.api.com" which I have used here.

server {
listen 443;
ssl on;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
location / {
proxy_set_header X-WSO2-Tenant "ten5.com";
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass https://localhost:9443/store/;
proxy_redirect https://localhost:9443/store/ /;
proxy_redirect https://store.apim.com/store/ /;
proxy_cookie_path /store /;
}
location ~ ^/(.*)registry/(.*)$ {
index index.html;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass https://127.0.0.1:9443/$1registry/$2;
}
}


You need to have put it in your /etc/hosts. For example you if you need to name your host as "store.apim.com" add  the following entry to /etc/hosts file. Replace the IP with your computer's IP.

10.100.7.77 store.apim.com

To start nginx use the following command.

sudo /etc/init.d/nginx start

After that start the server and go to "https://store.apim.com/" url from the browser and you can see it will directly go to the tenant store.


Few things to take a note:


  1. proxy_set_header X-WSO2-Tenant "ten5.com";
    • This a WSO2 specific header we set to specify the tenant the custom URL should apply. Not like the normal store (which will show all the tenants when go to the store) it will directly go to the specified tenant in the header.
  2. proxy_redirect  https://store.apim.com/store/ /; and proxy_redirect  https://localhost:9443/store/ /;
    • This need to be set to re-write the location header to the correct URL when the server send HTTP re-directions to the browser.

Configuring Store web app

Add following setting to site.conf of store webapp.

    "reverseProxy" : {
        "enabled" : "auto",    // values true , false , "auto" - will look for  X-Forwarded-* headers
        "host" : "sample.proxydomain.com", // If reverse proxy do not have a domain name use IP
        "context":"",
        "tenantHeader" : "X-WSO2-Tenant"
    }


Setup custom domain mapping in registry


You can change the gateway URL appears in store swagger as per your own domain. What you need to do is, configuring custom domain mapping for gateway in super tenant registry following manner.

Login to carbon management console as admin@carbon.super

Create a new resource in following path: (Resource name is the tenant domain name. In my example, ten1.com)

_system/governance/customurl/api-cloud/ten1.com/urlMapping/ten1.com

Add the following content:

{
    "tenantDomain": "ten1.com",
    "store" : {
        "customUrl" : "store.apim.com"  // custom domain for store
    },
    "gateway" : {
        "customUrl" : "gw.apim.com"   // custom domain for gateway
    }
}


This is how this would look like in management console.



Now you can see the custom URLs appears in swagger console instead of the default gateway URL.



Once you create an API and publishing it from tenant's Publisher, you can see following dialog appears. If you click on Goto API Store, you will also observe that you will go to your custom store domain; not the default store URL. This is actually redirecting to what you have configured in "customDomain" before.








Tuesday, October 27, 2015

Writing a custom NTLM grant handler and a sample client for API Manager with handshake support

NTLM is a challange/response based authentication protocol which is proprietary for Microsoft. If you are new to NTLM and need to have a basic idea what is happening you can read my previous blog post [1].

This blog explains how to write a custom grant handler to WSO2 API Manager to support NTLM grant type.

If you are going to try this please note you should be in a Windows environment. If you are trying this on a single machine (Client and server user accounts on a same computer), you would be able to try this without any issues (I have tried on a Windows 7/8 environments). But if you need to try an actual production environment (clients and server are different computers) , first of all you need to setup a Active Directory Domain. You can follow link [2] to setup that.

In this process we need to deal with Windows Platform related APIs. For that we can use Java Native Access (JNA) [3] library which is a simple way of native access without requiring to write JNI (Java Native Interface) code. There is another library which is built on top of JNA which is Waffle [4] which encapsulates all functionality you need to implement user authentication.

Following Maven dependencies can be used for setting up JNA and Waffle respectively.

<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>com.github.dblock.waffle</groupId>
<artifactId>waffle-jna</artifactId>
<version>1.8.0</version>
</dependency>
view raw jna_mvn.xml hosted with ❤ by GitHub


First step of the NTLM handshake is to create the Type1 token by the client.

String securityPackage = "Negotiate";
WindowsSecurityContextImpl clientContext = null;
IWindowsCredentialsHandle clientCredentials = null;
// client credentials handle
clientCredentials = WindowsCredentialsHandleImpl.getCurrent(securityPackage);
clientCredentials.initialize();
// initial client security context
clientContext = new WindowsSecurityContextImpl();
clientContext.setPrincipalName(WindowsAccountImpl.getCurrentUsername());
System.out.println("username :" + WindowsAccountImpl.getCurrentUsername());
clientContext.setCredentialsHandle(clientCredentials.getHandle());
clientContext.setSecurityPackage(securityPackage);
clientContext.initialize(null, null, "localhost");
String clientTokenType1 = Base64.encode(clientContext.getToken());
System.out.println("Generated NTLM token Type1:" + clientTokenType1);
Generated token is then encoded into Base64 and should be send as a header.

Server (APIM) recieves the token from the request header. Then it validates the Type 1 token and generates a Type 2 token. It is sent back to the client. Then client should generate a Type 3 token based on Type 2 token. It is sent to the server as a Header.
/* client receives a Token 2 as a Header in following format
WWW-Authenticate: NTLM <token2>
*/
Header tokenHeader = response.getFirstHeader("WWW-Authenticate");
String clientTokenType2 = tokenHeader.getValue().trim().split(" ")[1];
System.out.println("Recieved NTLM token Type2 From Server:" + clientTokenType2);
byte[] decodedToken2 = Base64.decode(clientTokenType2);
Sspi.SecBufferDesc continueToken = new Sspi.SecBufferDesc(Sspi.SECBUFFER_TOKEN, decodedToken2);
clientContext.initialize(clientContext.getHandle(), continueToken, "localhost");
byte[] clientTokenType3 = clientContext.getToken();
String encodedToken3 = Base64.encode(clientTokenType3);
System.out.println("Generated NTLM token Type3:" + encodedToken3);
//send the token 3 to server
response = invokeNTLMTokenEndpoint(encodedToken3);
HttpEntity entity = response.getEntity();
String responseString = EntityUtils.toString(entity, "UTF-8");
System.out.println(responseString);
Once server receives a token, it first determines the type of it. Type is the value of the 8th byte of the Base64 decoded NTLM token. If the server determines it as a Type 1 token, then it validates it and generates a Type 2 token based on that.
Following function can be used to determine the type of the token.

public int getNLTMMessageType(byte[] decodedNLTMMessage) throws IdentityOAuth2Exception {
int messageType;
if (decodedNLTMMessage.length > 8) {
messageType = decodedNLTMMessage[8];
} 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 < 1 || messageType > 3) {
throw new IdentityOAuth2Exception(
"Invalid NLTM message type:" + messageType + ". Should be one of 1,2 or 3.");
}
return messageType;
}
If the server determines it as a Type 1 token, then it validates it and generates a Type 2 token based on that. If it is Type 3 token, it will validate it and it will then identifies the user details communicating with the domain controller.
To write a custom grant handler we can basically follow the doc [5]. That includes a sample [6] code too. When writing our handler we need to basically extend AbstractAuthorizationGrantHandler when writing our class.
Following is the grant handler code.

package org.wso2.carbon.identity.oauth.custom.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 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 WindowsAuthProviderImpl provider = new WindowsAuthProviderImpl();
private String securityPackage = "Negotiate";
public int getNLTMMessageType(byte[] decodedNLTMMessage) throws IdentityOAuth2Exception {
int messageType;
if (decodedNLTMMessage.length > 8) {
messageType = decodedNLTMMessage[8];
} 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 < 1 || messageType > 3) {
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 == 1) {
serverContext = provider.acceptSecurityToken("server-connection", bytesToken, securityPackage);
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("WWW-Authenticate");
responseHeaders[0].setValue("NTLM " + type2Token);
tokReqMsgCtx.addProperty("RESPONSE_HEADERS_PROPERTY", responseHeaders);
return false;
} else if (tokenType == 3) {
serverContext = provider.acceptSecurityToken("server-connection", bytesToken, securityPackage);
String resourceOwnerUserNameWithDomain = serverContext.getIdentity().getFqn();
String resourceOwnerUserName = resourceOwnerUserNameWithDomain.split("\\\\")[1];
tokReqMsgCtx.setAuthorizedUser(resourceOwnerUserName);
return true;
} else {
log.error("Unknown NTLM token, Type " + tokenType + ":" + token);
return false;
}
} else {
if (log.isDebugEnabled()) {
log.debug("NTLM token is null");
}
throw new IdentityOAuth2Exception("NTLM token is null");
}
}
}
The link [7] consists of full code of client and server.

Once you completed writing the handler code, you can do the following configuration to enable the new custom grant type.

There is a section called "SupportedGrantTypes" in identity.xml in /repository/conf folder. (If you are using APIM 1.10.x this can be found at /repository/conf/identity folder). Add your new grant type to that section as a new SupportedGrantType. Correctly put the fully qualified class name of the custom grant handler under

<SupportedGrantType>
<GrantTypeName>iwa:ntlmhandshake</GrantTypeName>
<GrantTypeHandlerImplClass>org.wso2.carbon.identity.oauth2.token.handlers.grant.iwa.ntlm.NTLMAuthenticationGrantHandler</GrantTypeHandlerImplClass>
</SupportedGrantType>


After setting this you can start the server. You can use the provided sample client at [7] to test the new NTLM grant handler. You can follow the Readme.txt file which is added there.


[1] NTLM Authentication Basics : http://malinthaprasan.blogspot.com/2015/10/ntlm-authentication-basics.html
[2] Setting up an Active Directory Domain: http://roshanwijesena.blogspot.com/2015/05/ntlm-grant-type-support-with-wso2-api.html
[3] Java Native Access (JNA) Library: https://github.com/java-native-access/jna
[4] Waffle : https://github.com/dblock/waffle
[5] Writing a custom grant type : https://docs.wso2.com/display/IS500/Writing+a+Custom+OAuth+2.0+Grant+Type
[6] Custom grant type sample code : https://svn.wso2.org/repos/wso2/people/asela/oauth/custom-grant/
[7] Sample NTLM grant handler with handshake support and a sample client code: https://drive.google.com/open?id=0B1jOwGWdNiSNMEw3UFJ1R3Z4WFU

Saturday, October 17, 2015

NTLM Authentication basics

Introduction

NTLM which is denoted as NT LAN Manager (NTLM) is a suite of Microsoft security protocols (This is a Proprietary Microsoft authentication protocol) that provides authentication, integrity, and confidentiality to users which is used in various Microsoft network protocol implementations.

Basic Structure

This typically involves three systems as follows.
Three systems involved in NTLM authentication

Key Features 


Following are key features of the NTLM authentication scheme.
  1. Uses a challenge-response mechanism for authentication, in which clients are able to prove their identities without sending a password to the server. 
  2. Provides authentication, confidentiality and integrity
  3. Enables Single Sign On.

The Three-Step Handshake


NTLM It consists of three messages, commonly referred to as Type 1 (negotiation), Type 2 (challenge) and Type 3 (authentication).

Following is the message flow that happens between the client and the server.

Three step handshake of NTLM authentication protocol

  • (1) Client try to access the server's resource without authorization headers.
  • (2) Server responds with 401 Unauthorized. With WWW-Authenticate: NTLM header, server requests the client to send NTLM authentication token.
  • (3) Client re-sends the request with NTLM type 1 token. It initiates the NTLM authentication. This contains few information including the hostname and the domain name of the client [1].
NTLM Type 1 token structure
  • (4) Server receives the Type 1 token from the client. In response to that, sever sends Type 2 token which consists of server's NTLM challenge. [2]
NTLM Type 2 token structure
  • (5) In response to Type 2 token, client sends the Type 3 token as the final step in authentication. This demonstrates that the client has knowledge of the account password without sending the password directly. What actually client does is that it encrypts the server's challenge with its hashed password when generating the Type 3 token. Other than the response to the challenge, it consists the username too [3].

NTLM Type 3 token structure

 Once server receives the type 3 token, it sends the following three items to the domain controller [4].
  • User name
  • Challenge sent to the client
  • Response received from the client
The domain controller uses the user name to retrieve the hash of the user's password from the Security Account Manager database. It uses this password hash to encrypt the challenge. The domain controller compares the encrypted challenge it computed to the response computed by the client. If they are identical, authentication is successful.

Weaknesses


  1. Generated hashes of the client's password is not salted. So it remains same until the password is changed. So this is vulnerable to pass the hash [5] attack where once an attacker finds the hash once that can be used to encrypt the server's challenge successfully. 
  2. NTLM does not support mutual authentication. i.e only client is authenticated. The client has to assume that the server is legitimate.
  3. The password hash in NTLM is exposed each time the client uses NTLM for authenticating to a server. There are tools exists that scan network traffic for NTLM password hashes, capture them and then do a brute-force crack on them to derive the user's password.

Because of this problems Microsoft has recommended Kerberos protocol over NTLM which gives a greater security. It provides mutual authentication. The password hash is less frequently exposed as it is only required to send when the user requests a TGT (Ticket Granting Ticket) [6]. But still there are situations that Kerberos protocol cannot be applied. In such occasions NTLM is still used.

References