Thick client authentication?

Feb 5, 2011 at 8:20 PM

Can I use Waffle to authenticate a user into a Swing application and obtain their group membership?

I'm using Sun's Krb5LoginModule now, which just retrieves the TGT from the cache (and renews it if expired). I have to do a separate Active Directory lookup to retrieve the groups. It'd be nice if I could do all that with one call from a LoginModule.

Thanks!

Feb 6, 2011 at 4:16 PM
Edited Feb 6, 2011 at 4:30 PM

I've tried using WindowsAuthProviderImpl.logonUser but it wants a password.

I picked up the code from http://code.dblock.org/ShowPost.aspx?Id=91 and using that I can login without entering a password (I receive a TGS-REP w/ticket so user has a current TGT) but I still don't have the group membership that I need.

I'm not a Windows guy or even very good with Kerberos, and I'm sure this is a common use case. I'm hoping there's something in the Waffle API that I missed. :)

Coordinator
Feb 6, 2011 at 8:21 PM

You should do this with the waffle WindowsAuthProviderImpl, see http://code.dblock.org/ShowPost.aspx?id=95. It hides all the complexity away.

For Swing, remember that the swing app runs on the client, not on the server. So you must make sure that you're doing the client part on the client (initialize) and the server part (accept) on the server (eg. in a service that's backing up the swing app) - otherwise you're doing a logon from client's machine to the client's machine and that's no longer called authentication - hacking a client-side app is trivial (i've seen more than one swing app do this, really).

Share some or all of your implementation, it would be useful for many people. Would be amazing if you contributed a sample swing app that uses Waffle to do authentication into waffle. 

Feb 7, 2011 at 3:23 PM
Edited Feb 7, 2011 at 3:37 PM

Thanks for the link - I have something that works now. Just to be clear, what I'm looking for is single sign-on into my thick client app based on the currently logged-in user. Am I doing something naive and dangerous?

At any rate, here's what I have. I didn't see any other way to get a WindowsIdentity for the current user, and that's where my group information is.

 

	HANDLEByReference phToken = null;
	try {
		phToken = new HANDLEByReference();
		HANDLE threadHandle = Kernel32.INSTANCE.GetCurrentThread();
		if (! Advapi32.INSTANCE.OpenThreadToken(threadHandle, WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, true, phToken)) 
		{
			HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
			if (! Advapi32.INSTANCE.OpenProcessToken(processHandle, WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, phToken)) 
			{
				throw new LastErrorException(Kernel32.INSTANCE.GetLastError());
			}
		}		
try {
WindowsIdentityImpl id = new WindowsIdentityImpl(phToken.getValue());
String idFQDN = id.getFqn();
IWindowsAccount[] idGroups = id.getGroups();
for (IWindowsAccount idAcct : idGroups) {
String idAcctDomain = idAcct.getDomain();
String idAcctFQDN = idAcct.getFqn();
String idAcctName = idAcct.getName();
}
} catch (Exception e) {
System.err.println("Failed to access currently logged on user: "+ e.getMessage());
}
} catch (Exception e) {
System.err.println("Failed to get token for currently logged on user: "+ e.getMessage());
} finally {
if (phToken != null) Kernel32.INSTANCE.CloseHandle(phToken.getValue());
}

If that's valid, I'll get you that sample Swing app! :)

 

Coordinator
Feb 7, 2011 at 3:53 PM

Well it works. But define "authenticate"? This is a client-side app and you're finding out who the currently running user is. It doesn't actually "authenticate", you're not getting "access denied" when you're an invalid user.

Feb 7, 2011 at 4:28 PM

I see your point. So, is there any good way to achieve this? I'm back to thinking about a current TGT, which would imply the user has authenticated against the domain controller. But if the user's account has expired or been disabled after the ticket was issued, that knowledge wouldn't be available without asking the AD. Is there a JNA mechanism for getting such status, or am I stuck with asking the user to enter a password?

Coordinator
Feb 7, 2011 at 4:32 PM

Tell me a bit about what your app does and why it requires authentication as well as where the authorization is stored for who can or cannot access the app.

Feb 7, 2011 at 5:23 PM

The app is a data analysis tool that may expose privileged data, and authentication is needed to ensure the user has a valid and current domain account. Authorization is based on AD group membership.

Coordinator
Feb 7, 2011 at 5:55 PM

I think you should do this:

  1. Start with a basic web page (not a swing app) that does login. So basically deploy Waffle on the server just like in waffle demos. Hook up the whole authorization part where login will fail if your user is not in the appropriate groups. Make sure it all works. What this does is hand off a Tomcat (if that's what you use) session ID to you in a cookie. Now every request you make client-to-server is already authenticated. 
  2. Add Negotiate support to your swing app. That doesn't necessarily need waffle code, because JRE http client implementation already supports NTLM. So all you need is to make a successful HTTP request from the swing app to the server and collect the JSESSIONID cookie. That one will have to be sent with every HTTP request obviously - but I am sure swing has something that lets you authenticate once and then make it all transparent.
Feb 8, 2011 at 4:14 PM

Just FYI to the forum, I've verified the code above DOES NOT prove you're logged in to a domain controller - it runs with a local computer account as well.

I've also verified that Sun's Krb5LoginModule DOES provide that proof. It talks to the controller and gets a service ticket, and will fail if you're not logged in to the domain or if your account is locked, etc. The downside is the amount of configuration overhead - the KDC URLs, etc. Once the LoginModule has run successfully, though, you could get the user's groups from Waffle.

The idea of adding an additional server just to do Negotiate sounded a bit heavy-handed, until I read the post on using Waffle with Jetty (http://waffle.codeplex.com/Thread/View.aspx?ThreadId=214211). Maybe embedding Jetty into my otherwise standalone Swing app would be easier than the Sun solution.

Coordinator
Feb 8, 2011 at 10:37 PM

I think I don't understand anymore ;) Where does this data that is being analyzed come from?

Feb 9, 2011 at 7:15 PM

Sorry - we're data mining, so we connect to databases and build data models for analysis and visualization. I guess you assumed the data came from a web server! ;) Nobody ever said English is a precise language....

Coordinator
Feb 9, 2011 at 9:39 PM

So why do you need "authentication" in the client app?

Feb 10, 2011 at 3:24 PM

My customer doesn't want to enter their password more than once a day, apparently, and demands "single sign-on" into the app. But if I just assume the current user's login then there's nothing to prevent the bad guy from using a local user account to impersonate my domain account, etc. If I can't check a password, I want to at least prove that someone else has. That's why Kerberos is so appealing - it's built into Windows and the protocol requires comparison of passwords. I assume it's possible to inject a fake TGT into the cache, which is why I'm interested in getting a service ticket from the KDC. But I don't know if that's possible without a service/server to access, which is what you've suggested.

Your reply to eKovak implies my quest for groups is flawed, i.e., they won't be current and my authorization is based on group membership. I'm starting to think I'll just have to say "No, you gotta login".

Coordinator
Feb 11, 2011 at 2:28 PM

I think there's something fundamentally wrong here. This is a client application. How can you build security into a client application without sending any authentication information to a server?

Lets say this client application gets data from some server X, why can't I just bypass this client application and make a call to server X? What prevents me from disassembling your client code (30 seconds) and changing the "if logged in" to "if not logged in" (another 30 seconds)? Same for authorization. The fact that your client-side code does if user.groups.includes("foo") doesn't do anything and will be trivial to simply remove.

 

Feb 19, 2011 at 12:49 PM

I agree, and obtaining a Kerberos service ticket satisfies the need to authenticate to a server.

On the Oracle Java site: http://download.oracle.com/javase/6/docs/technotes/guides/security/jgss/single-signon.html

This site describes it well and has working code: http://www.javaactivedirectory.com/?page_id=80.

The initial call to gssContext.initSecContext results in an exchange with the domain controller to get a service ticket, and that won't succeed unless you're logged into the domain, with a valid TGT and a valid domain account (not locked or disabled, etc). And for thick client SSO you can use an SPN like http/myApp-sso, i.e., it doesn't have to be tied to any specific server, and it can be used by all of the thick client instances at a site.

As for your second point, isn't that the plight of any Java program? Any type of authentication/authorization could be bypassed the way you describe.

Coordinator
Feb 19, 2011 at 2:09 PM
mermeister wrote:

As for your second point, isn't that the plight of any Java program? Any type of authentication/authorization could be bypassed the way you describe.

Absolutely not.

Feb 19, 2011 at 9:33 PM

Ok, what you told me on Feb 7 finally sank in. The thick client should just be a viewing mechanism, with the server worrying about authorization and acting as the data source. That way the client has no direct connections to sensitive databases and can see only what's allowed. And the JSESSIONID cookie maintains separate contexts for the different thick client users. Brilliant!

I like Kerberos better and better, the more I learn about it. Why is it desirable to allow NTLMv2 as a fallback? SPN configuration errors notwithstanding, isn't Kerberos always available in domains using Windows 2003 and better?

Feb 19, 2011 at 9:56 PM

Answered my own question - from http://windows-secure.net/Addison.Wesley-Windows.Server/0321305019/ch02lev1sec2.html

When is Kerberos Used?

Although Windows 2000, Windows XP, and Window Server 2003 computers joined in a Windows 2000 or Windows Server 2003 domain prefer the use of Kerberos for authentication, Kerberos may not always be used for authentication in these domains. The following list of authentication rules should be kept in mind when considering authentication in these domains. Pay special attention to the use of "will" and "may." When attempts at Kerberos authentication fail, authentication may fall back to some version of LM.

  • User and computer authentication from Windows 2000, Windows XP, and Windows Server 2003 computers joined in a Windows 2000 or Windows Server 2003 domain will attempt to use Kerberos.

  • User domain authentication from Windows 2000, Windows XP, and Windows Server 2003 computers not joined in a Windows 2000 or Windows Server 2003 domain will use NTLM by default. In this case, the user may have a Windows 2000 or Windows Server 2003 domain account, but the computer itself is not joined in the domain.

  • User domain authentication from Windows 95/98 computers will use LM by default.

  • User domain authentication from Windows NT 4.0 computers will use NTLM by default.

  • Windows NT 4.0 computers joined in a Windows 2000 or Windows Server 2003 domain will use NTLM by default.

  • Authentication for access to resources in a Windows 2000 or Windows Server 2003 domain will always use NTLM if an IP address is used to identify the source of the server (for example, if a share resource is entered in the form \\192.168.5.55\sharename).

  • Authentication for access to resources in a Windows 2000 or Windows Server 2003 domain from users or computers joined in a Windows 2000 or Windows Server 2003 domain will attempt to use Kerberos if the name of the server identifies the server (for example, if a share resource is entered in the form \\anyserver\sharename).

Coordinator
Feb 19, 2011 at 11:00 PM
mermeister wrote:

Ok, what you told me on Feb 7 finally sank in. The thick client should just be a viewing mechanism, with the server worrying about authorization and acting as the data source. That way the client has no direct connections to sensitive databases and can see only what's allowed. And the JSESSIONID cookie maintains separate contexts for the different thick client users. Brilliant!

Yes. Good ;)

Coordinator
Feb 19, 2011 at 11:09 PM
mermeister wrote:

I like Kerberos better and better, the more I learn about it. Why is it desirable to allow NTLMv2 as a fallback? SPN configuration errors notwithstanding, isn't Kerberos always available in domains using Windows 2003 and better?

There was an open position in building 41 in Redmond to help rewrite the Kerberos implementation for Windows Longhorn about ten years ago. I was working on this security and authentication thing then and was genuinely interested. I had the same question: what's wrong with NTLMv2? ;) Kerberos is a much better protocol with a lot of possibilities and features and is a standard. NTLMv2 is a Microsoft thing. That aside the user just wants to authenticate without having to type anything, so who cares. In terms of security/encryption strength NTLMv2 is as good as Kerberos.