Sunday 23 October 2011

Single Sign on

Single Sign on
This post is a bit of an experiment, rather than like most of my posts where I come up with an idea and document it once complete I am going to blog about my progress getting this to work as I go, a sort of impressionist blog, which is most likely the way they were originally intended to be done.

What is SSO?
The way I see single sign on, which may not be the right way to view it, is that a user in a company has to interact with a multitude of computer systems, each of which requires some form of authentication before access is granted and single sign on means that a user once authenticated in one system will auto-magically be authenticated in the others without being prompted for a username and password or whatever.

The first method that I am looking at is Kerberos, the way I think this works is that a user signs in using whatever means at their disposal (card reader, password or biometric scanner) then that user has a token that is given to any other systems that require authentication.
The premise here is that there is a single secure token provider that every application within the enterprise/corporation then trusts.

JAAS the java way
Being a Java developer on enterprise systems I would have been lucky to have not had to have some experience with using JAAS the java authentication authorisation service.  This is a pluggable system like much of the JEE components where any particular vendor can make their own implementation, and as luck would have it there is already a Kerberos version available.

JBoss
I am going to use JBoss (likely version 4 but maybe 7 as I kinda like where that's going) as my application server, I may show using GlassFish too, plus I may also show it being used in tomcat just for kicks.

Step 1
In order to actually test these things out I need to actually have a kerberos system setup, as I tend to use Linux the instructions for setting this up will be linux orientated.

I followed these instructions for installing and setting up a kerberos system.
I rapidly hit a stumbling block with the first pre-requisite of the installation
Before installing the Kerberos server a properly configured DNS server is needed for your domain
D'oh! I don't have a DNS server, so I went to get one and set it up.  Doing a quick google I found these instructions which are for Ubuntu and as I am using a Debian system thought it would work.  I didn't bother following any of the reverse DNS lookup instructions as (a) I am lazy and (b) I want to move on quickly from setting up a dns.  The only thing to note at this point is that nslookup gw didn't give any results.

Back to installing Kerberos
So now that dns seems to be installed time to go back to the kerberos instructions.

Everytime I try  kinit steve/admin I get

kinit: Cannot resolve network address for KDC in realm "RLJASSOCIATES" while getting initial credentials

I altered the instructions for both dns installation and kerberos installation so that the domain is RLJASSOCIATES.
I found a forum that states;
"each host's IP address must reverse-resolve the canonical name."
so I guess it's time to go back to the DNS setup instructions once more, damn it serves me right for being lazy in the first place!

Ok the DNS setup doesn't work right off the bat but I did find a nice little utility to test called named-checkzone which I can use to check the zone files for correctness.  Needless to say they weren't.  Well I've been over those DNS instructions many times now so I can be absolutely sure my setup matches and guess what...they don't work so I guess they're crap!

I think there might have been a missing entry in the RLJASSOCIATES.db file so I added these lines
ns        IN        A        192.168.1.34
@        IN        NS        ns.RLJASSOCIATES
and that seems to allow nslookup gw to work.

however running sudo kinit steve/admin still results in the error
kinit: Cannot resolve network address for KDC in realm "RLJASSOCIATES" while getting initial credentials
Setting up krb5.conf
I remembered that after the first fault with the kerberos setup I uninstalled it, then when I re-installed it I was never asked the same questions about default domains once the installation was complete, this may have been the route cause of this last failure.
Essentially there is a file called /etc/krb5.conf that seems to list settings that are relevant for different domains, so I took a guess at what to write and put this in;
    RLJASSOCIATES = {
                kdc = rjohnson-acer.RLJASSOCIATES
                admin_server = rjohnson-acer.RLJASSOCIATES
                default_domain = RLJASSOCIATES
    }
I have no idea if this is correct or not apart from when I ran this command kinit steve/admin instead of it failing with the usual error message shown above it prompted for the password, which I typed in and ... nothing happened which still means no error :)

The next fault to get over in the kerberos instructions is
#> kinit steve@RLJASSOCIATES
kinit: Client not found in Kerberos database while getting initial credentials
 Once again I have no idea what is causing this to happen, so on to professor google.
Not actually sure this is an error as the list of principals is now
kadmin:  list_principals
K/M@RLJASSOCIATES
kadmin/admin@RLJASSOCIATES
kadmin/changepw@RLJASSOCIATES
kadmin/history@RLJASSOCIATES
kadmin/rjohnson-acer@RLJASSOCIATES
krbtgt/RLJASSOCIATES@RLJASSOCIATES
steve/admin@RLJASSOCIATES
The big issue is that it says I am not entering the correct password for kadmin/admin and yet I know damn well what I entered so this might be an issue with the re-installation I tried earlier ooops!

JBoss
I am going to start with a vanilla installation of jboss-4.2.3.GA, simply because I wanted to start with a version 4 and this was the latest 4 I found on jboss community site, don't worry I'll show the same kerberos stuff on jboss-7.0.2, I would want to use 7.1.0 as this is the version where remote ejb calls are supported but it doesn't exist yet tee hee :)

Since JBoss comes with a jmx-console lets try to secure the access to this web app with kerberos.

The current setup for security on this app is;

   <application-policy name="jmx-console">
      <authentication>
          <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required">
              <module-option name="usersProperties">props/jmx-console-users.properties</module-option>
              <module-option name="rolesProperties">props/jmx-console-roles.properties</module-option>
          </login-module>
       </authentication>
    </application-policy>

So in order to use the same login that I want to use for kerberos I've created a user called steve and I've added that to the props/jmx-console-users.properties.
Stupidly the first time I added the new user I forgot to also add him to the roles properties file, the trouble is you can't sign in but you don't get any error messages in the jboss log which was a bit annoying.

Introducing Krb5LoginModule
I've never used this login module before so it's going to be a learning experience :)
I found some documentation for Krb5LoginModule and just decided to run with it.

So I changed the login-config.xml to read

    <application-policy name="jmx-console">
       <authentication>
          <login-module code="com.sun.security.auth.module.Krb5LoginModule" flag="required">
           <module-option name="debug">true</module-option>
          </login-module>
       </authentication>
    </application-policy>

As you can see I've only set a single option, this is just so I can see what happens, remember I have no idea.


Hooray
It looks like I was successful, the usual prompt popped up so I logged in as Steve and hey presto got this in the log

10:38:00,893 INFO  [STDOUT]         [Krb5LoginModule] user entered username: steve
10:38:00,978 INFO  [STDOUT] Acquire TGT using AS Exchange
10:38:01,056 INFO  [STDOUT] principal is steve@RLJASSOCIATES
10:38:01,057 INFO  [STDOUT] EncryptionKey: keyType=3 keyBytes (hex dump)=0000: FE 75 E3 7A 1A 29 7C A8  
10:38:01,057 INFO  [STDOUT] EncryptionKey: keyType=1 keyBytes (hex dump)=0000: FE 75 E3 7A 1A 29 7C A8  
10:38:01,057 INFO  [STDOUT] EncryptionKey: keyType=23 keyBytes (hex dump)=0000: 3D 8C CB 99 50 CC 9A 13   69 12 6D AA BF E7 C6 4E  =...P...i.m....N
10:38:01,058 INFO  [STDOUT] EncryptionKey: keyType=16 keyBytes (hex dump)=0000: 79 75 9B A7 67 EC BF 2F   7A 32 C1 C8 9E AB 57 0D  yu..g../z2....W.
0010: 5B B3 3D D0 64 F4 34 4F  
10:38:01,058 INFO  [STDOUT] EncryptionKey: keyType=17 keyBytes (hex dump)=0000: 87 17 D5 4C 61 35 48 4D   D4 8B 14 58 91 E5 E8 AE  ...La5HM...X....
10:38:01,078 INFO  [STDOUT] Commit Succeeded


Actually do SSO
So far we have a working test environment setup and have been able to make a call out to Kerberos to authenticate a user.

Although this is a good start to the experiment what we don't have are;
  • a way of automatically sending a username to do the login, this wouldn't just be a username but would need to be the TGT.
  • a way to set the Subject to the user signing in, currently we only have the principal.
I should have read the documentation as we really do have the subject of the person doing the login this is bundled up in the Credentials of the login itself, so score!!
The Connector 
The way that I want to pass the name through rather than prompt for it is to use the java system property user.name this seems like the easiest thing to do for this simple test.  Essentially the way I see this working is that the name is used to retrieve a TGT from the local cache if there is one, and of course if there is one then they are authenticated and this TGT can then be passed from app to app.  In order to do this before a call reaches the application running on the application server I intend to use connectors (or valves in tomcat) which means I need to brush up on my JCA knowledge first...more on this the next time I have some free time to add to it.

Lets take a step back
Before getting too far ahead of ourselves I think it's time to explain what exactly Kerberos will entail, much of the information about Kerberos in an easy to understand  style can be found on the MSDN site but here is the super abridged version of events.
The way my system is setup is that the is a server called the KDC (kerberos distribution centre) which has two roles.  The first role is upon request a valid user is given a ticket to get tickets (cunningly disguised as a TGT), then when a user wants to access a service they use the TGT to get a service request ticket.  Finally with this ticket the user authenticates his/her self with the service they wish to use.

The stage that our test system is at is that when a user requests the resource at /jmx-console 


Done...I think
Not sure if this is done now but it seems to be which is strange as I've set virtually nothing up?


login-config.xml
    <application-policy name="jmx-console">
       <authentication>
          <login-module code="com.sun.security.auth.module.Krb5LoginModule" flag="required">
        <module-option name="debug">true</module-option>
        <module-option name="useTicketCache">true</module-option>
        <module-option name="doNotPrompt">false</module-option>
        <module-option name="useKeyTab">true</module-option>
        <module-option name="useKeyTab">true</module-option>
          </login-module>
       </authentication>
    </application-policy>
Now when I log in to jmx-console it seems to authenticate me when I have a service ticket already.

Firefox
I also discovered that if you put about:config into firefox and then filter on negotiation you can set network.negotiate-auth.trusted-uris; to the domain kerberos authenticates against et voila ! .RLJASSOCIATES

But is it SSO
The simple truth is I don't know at the moment I haven't tested it and this is because I am not sure how to test it, so lets look back at square 1 what was the criteria;

  1. not be able to access resource (/jmx-console) if I am not authenticated.
  2. authenticate via the KDC.
  3. once authenticated don't get prompted for authentication again.
I think that I have managed 2 of the 3 above points, however the third is a partial victory.  I don't get asked for authentication when I visit the resource in a second tab of the browser but this is simply the typical cookie authentication, when I use a different browser I still get asked for authentication and it is this step that I need to get rid of in order to claim SSO works.  The trouble is I am not sure how to do that with the tools that I have used so far, I think I might need to write my own LoginModule or something. 

One More Piece of the puzzle
I've not had much time to look into all of this stuff at the moment and tidy up this rambling blog post, this link looks like it'll be quite useful.
http://download.oracle.com/javase/1.4.2/docs/guide/security/jgss/single-signon.html 
http://download.oracle.com/javase/1.5.0/docs/guide/security/jgss/tutorials/JGSSvsJSSE.html
http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html 

Thursday 13 October 2011

Exceptions (again)

The argument that keeps on giving :)
This is the fairest assessment that I think I've ever made of exception (specific to java, possibly).

Exceptions are a very good way of having more than one return type on call. Originally they were invented so that constructors could inform the caller of something rather than having to have a special "error" type of every class.
Handling exceptions early isn't always the best idea, code where an exception occurs may not be best suited to remedy it as the lowest level tends to be a generic handler of something.
Take for example the File API, if a file can't be found it is highly unlikely that the File object should be made to handle every failure adequately. Maybe it should simply return a null, but how then do we know the file couldn't be found rather than it couldn't be read? These 2 scenarios should be handled in very different ways (1) file not found: get the user to pick another (2) read error: try again X times then fail. Scenario 1 requires user interaction so bubble the exception up through the code so the user can pick another file.
There is also an argument against interpreting an exception at a lower level and sending specific "error" style replies up the chain.
  1. Multiple levels in the call stack will have to handle error return states.
  2. The returned objects would each have to have an error version created i.e.
    public A getA()
        public B doB()
            public C getC()
    
    each return type A, B, C would have to have an error version created, plus each call would have to handle this.
  3. There is already an adequate mechanism to repeatedly send exceptional responses up the stack regardless of method return type.
  4. Sometimes it is impractical to escape from a normal flow and return a meaningful error, the use of try catch blocks greatly simplifies this.

Also
Also the idea that exceptions are errors is flawed, exceptions are an
exceptional flow, the clearest use of this fact I think is the Thread
usage of InterruptedException. When things are running concurrently (even
pseudo concurrently) a message should be sent at any point during the flow
and the way to handle this is with exceptions.

The problem is that with legacy old shit (and I mean it is shit) does
the biggest sin of all which is to catch ALL general exceptions and simply
throw it without adding a cause to it. No attempt is made to handle the
exception where it is relevant which means the final resting place of a
thrown exception is the client which is why some people believe that the client
shouldn't handle them because 9 times in 10 the client is the wrong place
to handle them.
 
Stack Overflow profile for Richard Johnson at Stack Overflow, Q&A for professional and enthusiast programmers