AW: [jcifs] Re: Websphere+SSO+NTLM

Waldheim, Frank F.Waldheim at ing-diba.de
Tue Dec 12 08:26:04 GMT 2006


 

> -----Ursprüngliche Nachricht-----
> Von: jcifs-bounces+f.waldheim=ing-diba.de at lists.samba.org 
> [mailto:jcifs-bounces+f.waldheim=ing-diba.de at lists.samba.org] 
> Im Auftrag von altiuser
> Gesendet: Mittwoch, 6. Dezember 2006 16:12
> An: jcifs at lists.samba.org
> Betreff: [jcifs] Re: Websphere+SSO+NTLM
> 
> I am stranded, any one out there who can provide some leads, please? 
> 
> 
> 


hi there, 
there is a feature in websphere where you can assert credentials
to your container.
the technique utilized there is called TAI (Trusted Association Interceptor).

i do have a working prototype of a tai for websphere 6.0.x

please read:
http://www-128.ibm.com/developerworks/websphere/techjournal/0508_benantar/0508_benantar.html

to find out how to install such a TAI and what preconditions must be met by your surrounding
application.

my version is attached.

~fw






Diese E-Mail enthaelt vertrauliche und/oder rechtlich geschuetzte
Informationen. Wenn Sie nicht der richtige Adressat sind oder diese
E-Mail irrtuemlich erhalten haben, informieren Sie bitte sofort den
Absender und vernichten Sie diese Mail. Das unerlaubte Kopieren sowie
die unbefugte Weitergabe dieser Mail ist nicht gestattet.
 
This e-mail may contain confidential and/or privileged information.
If you are not the intended recipient (or have received this e-mail
in error) please notify the sender immediately and destroy this e-
mail.
Any unauthorized copying, disclosure or distribution of the material 
in
this e-mail is strictly forbidden.
-------------- next part --------------


import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Properties;

import javax.security.auth.Subject;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import jcifs.Config;
import jcifs.UniAddress;
import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.ntlmssp.Type3Message;
import jcifs.smb.NtlmChallenge;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbAuthException;
import jcifs.smb.SmbException;
import jcifs.smb.SmbSession;
import jcifs.smb.SmbTransport;
import jcifs.util.Base64;

import com.ibm.websphere.security.WebTrustAssociationException;
import com.ibm.websphere.security.WebTrustAssociationFailedException;
import com.ibm.wsspi.security.tai.TAIResult;
import com.ibm.wsspi.security.tai.TrustAssociationInterceptor;
import com.ing.diba.AuthData;

/**
 * the NTLM TAI Prototype
 * 
 * CCCs: 
 * - XXX: enable usefull logging? 
 * - XXX: make it configureable? especially isTargetInterceptor Logic
 * 

 * 
 */
public class NtlmTai implements TrustAssociationInterceptor {

	private static final String NTLMCHALLENGE = "ntlmchallenge";
	private static final String NTLMAUTH = "ntlmauth";

	/**
	 * configure the jcifs lib according to the properties used in 
	 * production by callimero
	 */
	public NtlmTai() {
		super();
		System.out.println("new NtmlTai");
		// try to configure the JcifLib
		Config.setProperty("jcifs.smb.client.domain", "DOMDIBACORP");
		Config.setProperty("jcifs.netbios.wins", "gf01swdc01,gh0aswdc02,gn01swdc01,gf02swdc01,gn01swdc02,gh0bswdc02,gn02swdc02,gf0bswdc02");
		Config.setProperty("jcifs.http.loadBalance", "true");
		Config.setProperty("jcifs.util.loglevel", "9");
		Config.setProperty("jcifs.smb.lmCompatibility", "3");
/*		Config.setProperty("jcifs.smb.client.username", "userCallimero");
		Config.setProperty("jcifs.smb.client.password", "Calli864ro");
*/		Config.setProperty("jcifs.smb.client.ssnLimit", "1");
		Config.setProperty("jcifs.smb.client.soTimeout", "3500");
		
	}

	/*
	 * only intercept for requests containing: "/optical" and "ntlm.ac" int the request uri
	 * XXX -  this really should be more clever
	 */
	public boolean isTargetInterceptor(HttpServletRequest req) throws WebTrustAssociationException {
		// we only work for windows-ntlm users using optical
		if (req.getContextPath().indexOf("optical") != -1 && req.getRequestURI().endsWith("ntlm.ac")) {
			System.out.println("NtlmTai will handle request");
			return true;
		} else {
			System.out.println("NtlmTai will NOT handle request");
			return false;
		}
	}

	/**
	 * just a convenience method - although actually illegal as it flushes the 
	 * response (by forwarding) and therefore leaves the TAIREsult with a
	 * IllegalStateException (as also documented in the servletspec)
	 * XXX - please find a more sophisticated control flow logic here
	 */
	private TAIResult gotoError(Exception sae, HttpServletRequest req, HttpServletResponse resp) throws WebTrustAssociationFailedException {
		try {
			req.setAttribute("error", sae);
			sae.printStackTrace();
			RequestDispatcher dispatcher = req.getRequestDispatcher("/error.jsp");
			dispatcher.forward(req, resp);
			return TAIResult.create(HttpServletResponse.SC_UNAUTHORIZED);
		} catch (Exception e) {
			e.printStackTrace();
			throw new WebTrustAssociationFailedException(e.getMessage());
		}
	}

	/*
	 * XXX: early out (after the selected phase) 
	 * XXX: the documentation suggests HTTPServletResponse.SC_CONTINUE 
	 *       as a TAIResult but the RFC for NTLM wants 401 (so i just dont know)
	 * */
	public TAIResult negotiateValidateandEstablishTrust(HttpServletRequest req, HttpServletResponse resp) throws WebTrustAssociationFailedException {
		// Phase 1:
		// if
		// - no 'Authorization' header is avail or
		// - we have such a header but it is not our beer or
		// - we don't have a session yet
		// ===> we have to tell the client to come back (and create a session for him).

		// check whether we are in the first phase
		String msg = req.getHeader("Authorization");
		// XXX bitte beachten: hier wird kein 'false'uebergeben - es wird also eine neue Session angelegt.
		HttpSession session = req.getSession();
		if (session == null || msg == null || (msg != null && !msg.startsWith("NTLM"))) {
			resp.setHeader("WWW-Authenticate", "NTLM");
			resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
			resp.setContentLength(0);
			if (session == null) {
				session = req.getSession(true);
				System.out.println("phase1: created session " + session.getId());
			} else {
				System.out.println("phase1: using session " + session.getId());
			}
			System.out.println("phase1: returning 401 - please come back");
			return TAIResult.create(HttpServletResponse.SC_UNAUTHORIZED);
		}

		// Phase 2:
		// if we get Type1:
		// - get a challenge
		// - store it in the session
		// - XXX -  remove challenge from session.
		// ==> create Type2 and send it back

		byte[] src = Base64.decode(msg.substring(5));

		try {
			if (src[8] == 1) {
				Type1Message type1 = new Type1Message(src);
				System.out.println("phase2: trying to get challenge");

				NtlmChallenge challenge = SmbSession.getChallengeForDomain();

				System.out.println("phase2: got challenge: " + challenge + " from DC " + challenge.dc);
				// note we set it here - recover it later and then remove it.
				session.setAttribute(NTLMCHALLENGE, challenge);
				
				Type2Message type2 = new Type2Message(type1, challenge.challenge, null);
				
				msg = Base64.encode(type2.toByteArray());
				resp.setHeader("WWW-Authenticate", "NTLM " + msg);
				resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
				resp.setContentLength(0);

				System.out.println("phase2:  returning 401 - please respond to challenge");
				return TAIResult.create(HttpServletResponse.SC_UNAUTHORIZED);
			}
			// leave the exceptions as they are during developement
			// XXX - collapse exceptions as they are all in no way recoverable
		} catch (SmbException sae) {
			return gotoError(sae, req, resp);
		} catch (UnknownHostException sae) {
			return gotoError(sae, req, resp);
		} catch (IOException sae) {
			return gotoError(sae, req, resp);
		}

		// Phase 3:
		// if we get a Type3
		// - get challenge from session
		// - get some info from msg
		// - create NtlmPasswordAuthentication
		// => authenticate user with the above
		try {
			if (src[8] == 3) {
				Type3Message type3 = new Type3Message(src);
				// XXX - pls understand this
				byte[] lmResponse = type3.getLMResponse();
				if (lmResponse == null) {
					lmResponse = new byte[0];
				}
				byte[] ntResponse = type3.getNTResponse();
				if (ntResponse == null) {
					ntResponse = new byte[0];
				}

				NtlmChallenge challenge = (NtlmChallenge) session.getAttribute(NTLMCHALLENGE);
				if (challenge != null) {
					System.out.println("phase3: recovered challenge " + challenge + " with DC " + challenge.dc);
				} else {
					System.out.println("phase3: could not find a challenge in the session - bouncing out");
					// should not happen - but doing the bounce out nevertheless as we really want a session here
					// XXX - bouncing out.
				}
				NtlmPasswordAuthentication ntlm = new NtlmPasswordAuthentication(type3.getDomain(), type3.getUser(), challenge.challenge, lmResponse,
						ntResponse);
				System.out.println("phase3: got ntlm token " + ntlm);
				// important note: please use the DC which created the challenge
				// to avoid "Illegal access to memory"
				UniAddress dc = challenge.dc;

				System.out.println("phase3: trying to login... (DC = '" + dc + "')");

				// if we have no exception we suceeded - return 200 - else. start again.
				try {
					SmbSession.logon(dc,challenge.localPort, ntlm );

					
					System.out.println("phase3: successfully authenticated user " + ntlm.getUsername());
					// now do the logoff which closes the actual connection
					
					// XXX -  just for the optical test thing - remove it pls
					session.setAttribute(NTLMAUTH, ntlm);
					Subject subject = AuthData.createSubject(ntlm.getUsername(), ntlm.getName(), ntlm.getPassword());

					// ok, our subject has a principal with the same name as ntlm.getName which is returned as a
					// principal-string here...
					return TAIResult.create(HttpServletResponse.SC_OK, ntlm.getName(), subject);
				} catch (SmbAuthException sae) {
					System.out.println("phase3: Unable to authenticate user, got SmbAuthException: " + ntlm.getName() + ": 0x"
							+ jcifs.util.Hexdump.toHexString(sae.getNtStatus(), 8) + ": " + sae);

					return gotoError(sae, req, resp);
				}
			}
			// leave the exceptions as they are during developement
			// XXX - collapse exceptions as they are all in no way recoverable
		} catch (SmbException sae) {
			return gotoError(sae, req, resp);
		} catch (UnknownHostException sae) {
			return gotoError(sae, req, resp);
		} catch (IOException sae) {
			return gotoError(sae, req, resp);
		}

		System.out.println("FATAL: surprinsingly reached the end of the wurst - returning error");
		return gotoError(new WebTrustAssociationFailedException(), req, resp);
	}

	/*
	 *  XXX - we could use it if we want to be configured via WAS custom Properties
	 */
	public int initialize(Properties arg0) throws WebTrustAssociationFailedException {
		return 0;
	}

	/*
	 * only for the container
	 */
	public String getVersion() {
		return "1.0";
	}

	/*
	 * i dunno
	 */
	public String getType() {
		return this.getClass().getName();
	}

	/*
	 * here we may have to really get rid of all the MS-stuff
	 * XXX - please do usefull things here
	 */
	public void cleanup() {

	}

}


More information about the jcifs mailing list