Archive

Archive for the ‘SSO’ Category

Spring security: CAS + LDAP

December 21st, 2011 No comments

After getting straight LDAP authentication to work with the spring-security-ldap plugin, I moved on to the next requirement which was integrating with CAS. Like many projects before us, we need to do authentication through CAS and then follow up authorization (i.e. role checking) through LDAP. The authentication part was easy thanks to the spring-security-cas plugin. However, there are two mildly annoying issues with the plugin as a whole:

First, once it is installed, you can’t turn it off (at least, not in development mode). The value of the cas.active setting is ignored unless you deploy as a war. There is already a bug filed for this and someone has submitted a simple patch. You can either build the patched plugin, or just tweak the few lines directly in your project’s copy of the plugin.

The second issue relates to auto-creating user accounts. I posted about using an AuthenticationEvent listener to do this last week. Unfortunately, this will not work with the default configuration of the CAS plugin. The plugin does not override the userDetailsService so you get the default GormUserDetailsService. That class will throw a “user not found” exception if there is no local user domain object for the given user name. If you have no need for role information (authorities in spring-security speak), then you can simply plug in a simplistic userDetailsService like this one:

import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUserDetailsService
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.User
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
import org.springframework.security.core.authority.GrantedAuthorityImpl

/**
 * Dumb service which just returns a UserDetails object with the username set.
 * @author esword
 */
class EmptyUserDetailsService implements GrailsUserDetailsService {

    /**
     * Taken from GormUserDetailsService: Some Spring Security classes expect at least one
     * role, so we give a user with no granted roles this one which gets past that restriction
     * but doesn't grant anything.
     */
    static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]

    UserDetails loadUserByUsername(String username, boolean loadRoles) {
        return loadUserByUsername(username)
    }

    UserDetails loadUserByUsername(String username) {
        new User(username, '', true, true, true, true, NO_ROLES)
    }
}

You could extend InMemoryUserDetailsManager if you didn’t want to re-create the UserDetails all the time, or wrap a GormUserDetailsService to first check if you have a local account and return info from that if so. I just threw together this class so that I could verify that the rest of the authentication process with CAS worked.

LDAP Integration

If you do need role information from LDAP, you will need to add a few more beans to your resources.groovy file. Someone posted a thread on the grails-dev mailing list about a year ago with the core information for this configuration. However, the example they give hard-codes the LDAP connection settings in the bean definitions themselves. Since our app is deployed with the LDAP plugin (it is turned off if CAS is turned on), I wanted to use the same property settings so that we could easily toggle back and forth between plain LDAP and CAS. Here is the revised bean configuration within resources.groovy:

    // If CAS is active and if ldap is configured, do UserDetails lookup from ldap to get the roles.
    // All of these setting names and how they are used come from reading the SpringSecurityLdapGrailsPlugin.groovy
    if (application.config.grails.plugins.springsecurity.cas.active) {
        def config = SpringSecurityUtils.securityConfig
        if (config.ldap.context.server) {
            SpringSecurityUtils.loadSecondaryConfig 'DefaultLdapSecurityConfig'
            config = SpringSecurityUtils.securityConfig

            initialDirContextFactory(org.springframework.security.ldap.DefaultSpringSecurityContextSource,
               config.ldap.context.server){
                userDn = config.ldap.context.managerDn
                password = config.ldap.context.managerPassword
            }

            ldapUserSearch(org.springframework.security.ldap.search.FilterBasedLdapUserSearch,
               config.ldap.search.base,
               config.ldap.search.filter,
                initialDirContextFactory){
            }

            ldapAuthoritiesPopulator(org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator,
                initialDirContextFactory,
               config.ldap.authorities.groupSearchBase){
                  groupRoleAttribute = config.ldap.authorities.groupRoleAttribute
                  groupSearchFilter = config.ldap.authorities.groupSearchFilter
                  searchSubtree = config.ldap.authorities.searchSubtree
                  rolePrefix = "ROLE_"
                  convertToUpperCase = config.ldap.mapper.convertToUpperCase
                  ignorePartialResultException = config.ldap.authorities.ignorePartialResultException
            }

            userDetailsService(org.springframework.security.ldap.userdetails.LdapUserDetailsService,
                ldapUserSearch,
                ldapAuthoritiesPopulator){
            }
        }
        else {
            //Dummy service if LDAP isn't set up
            userDetailsService(EmptyUserDetailsService)
        }
    }

Ideally, I would like to be able to keep the LDAP plugin turned on in an “authorization only” mode so that I could use the userDetailsService configuration directly from it. That is not yet possible with the plugin, so this is the next best thing. You still avoid having to write any new code in your application and at least get the benefit of being able to fall back on the default property settings for the LDAP plugin.

Auto-create User Domain Object with Spring Security

December 12th, 2011 No comments

For those who skip straight to the last page of a book to see how it ends – See Chap 7. Events of the spring-security-core plugin documentation.

For those who like a little more detail…

I just moved our grails app from using the shiro plugin to using the spring-security plugin(s). I like shiro’s filter-based config, but all the pre-built extension modules that Burt Beckwith has put together for spring-security (LDAP, CAS, etc.) makes it much easier for us to support the range of environments in which we have to deploy.

The one feature which took me a little while to figure out was how to have our app auto-create a user domain object when it is using an external authentication source. For example, say an instance of our app is configured to authentication against an LDAP server. The app has a MyUser class that holds local settings for users like preferences, documents, etc. When a user signs in for the first time and makes it past the authentication step, we need to automatically create a MyUser instance and associate it with the LDAP username. With the shiro-based authentication, we did this in the controller method which handled the authentication itself. Spring security works a little differently and there isn’t a central, post-authentication landing point.

If your app is always deployed with the same type of authentication (e.g. always with LDAP), you could put the persistence code into a custom UserDetailsService. There are several posts on the web that discuss creating a custom UserDetails object and a corresponding service for it, so this was the first approach I looked at. Chapter 11 of the spring-security-core plugin’s user guide has info on it as well. The primary shortcoming is that you can’t chain together UserDetailsServices. You have to implement one for each form of authentication with which you want to work.

If your app must work with a variety of authentication methods, it is easier to register a listener with Spring Security. Chapter 7 of the plugin guide discusses the two ways to do this. I found that handling the AuthenticationSuccessEvent was all I needed. Since we already had a Grail’s service that handles various user-related tasks, the listener object was dirt simple:

import org.springframework.beans.factory.InitializingBean
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.context.ApplicationListener
import org.springframework.security.authentication.event.AuthenticationSuccessEvent

class MyAuthenticationEventListener implements ApplicationListener<AuthenticationSuccessEvent>, InitializingBean, ApplicationContextAware {
    ApplicationContext applicationContext
    def userService

    void afterPropertiesSet() {
        userService = applicationContext.getBean('userService')
    }

    void onApplicationEvent(AuthenticationSuccessEvent e) {
        //the principal field of the source object is a UserDetails object of some form.
        //The spring-security API contract guarantees that at least the username field will be populated.
        userService.createUser(e.source.principal)
    }
}

Since the whole class really just has one line of “functional” code, I could have used the closure-based approach described in section 7.3 of the user guide. I just prefer to keep true code out of the Config.groovy file.

Then, within the UserService.createUser method, the key lines look something like this:

    def user = MyUser.findByUsername(userDetails.username)
    if (!user)
    {
        MyUser.withTransaction {status ->
            user = new MyUser(username:userDetails.username /*, set any other props you want to store locally*/)
            user.save(flush: true, failOnError:true)
        }
    }
    return user

One note of interest – without the withTransaction statement, you may get an exception stating that no hibernate session exists and one cannot be opened. The withTransaction closure wraps this up nicely for you.

What does Single Sign On Mean?

July 9th, 2009 No comments

About three years ago, I was asked to write a Single Sign On (SSO) solution for a set of web applications produced by my division. I quickly found out that what one person meant by SSO was not always the same as what another person meant. I wrote up some notes back then to help me explain the concepts to others so I could figure out exactly what they wanted, and also listed possible solutions to the different needs. I ended up writing our own library which handled the first and third of these concepts in order to meet the requirements as they emerged.

This subject area keeps poking its head back up, and I know it’s still tough for new developers to grasp, so I thought I should publish my old notes and pointers. These mostly deal with applications under JBoss and accessing Active Directory (either as an LDAP server or with kerberos) since that was our primary deployment environment.

I will update these as I can. I know that some new projects (like JOSSO) have emerged since I did my original research. One or more of these may make implementing a new solution much easier than it was when I started.

Centralized Authentication of Web Applications
When someone talks about using a central system like an LDAP or Active Directory (AD) server to maintain user information, s/he is thinking about one of three concepts, Individual Application Authorization, Manual Single Sign On (SSO), or Automatic SSO. Before discussing these, let’s make sure we understand the two parts of securing a web application.

Authentication vs Authorization
There are two key parts to letting a user into an application, authentication and authorization. Authentication determines who a user is (usually using a user name & password). Authorization determines what a user is allow to do within an app. Authorization is normally determined by checking role or group membership in some manner.

It is often the case that the phrase “authenticate a user” is used to refer to both authentication and authorization since almost all security procedures do both. Be warned that there is no standard for doing authorization, though there was a proposal in front of the kerberos standardization committee to add group/role information to kerberos tickets when I last looked into this in 2007. It may have been adopted since then.

Concept 1: Single Storage, Repeated Sign-On
This means enabling one or more web applications to use a directory server (LDAP in general or Active Directory (AD) specifically) to authenticate and authorize a user in an application. Every time a user wants to access a different application (on the same server or a different server), the user needs to enter login information. However, a user’s credentials are the same for all applications, making it easier for the user to remember and easier for an administrator to manage.

Generally speaking, this is pretty easy to implement. There are a many code samples on the net. A simple one that uses JNDI (the easiest way to do it) is on the OpenLDAP site. This example explains the standard way to authenticate a user against an LDAP server using two binds, one as a system account to verify an account exists and one as the actual user account to verify password info. The code to do it against an AD server is very similar.

JBoss
JBoss also has a built in mechanism that can do this. For a full explanation, see the Security on JBoss chapter of the JBoss admin guide (mostly sections 8.2-8.4). If you don’t want that much detail, the JBoss Getting Started doc with its sample application also talks about basic security setup. Two other sources of info:

JBoss Wiki note on LdapExtLoginModule
The note covers LDAP configuration in general, but gives a specific example about AD near the bottom. The example didn’t quite work when using a test Active Directory server that I had setup. I added the following block in the login-config.xml file in the $JBOSS_HOME/server/default/conf directory to create a security context that worked for us:

<application-policy name="LdapExtLogin"><authentication>        <login-module code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required">            <module-option name="java.naming.provider.url">ldap://www.ourserver.com:389/</module-option>            <module-option name="bindDN">cn=Binding Account,cn=Users,dc=test,dc=company,dc=com</module-option>            <module-option name="bindCredential">Pass123word</module-option>            <module-option name="baseCtxDN">cn=Users,dc=test,dc=company,dc=com</module-option>            <module-option name="baseFilter">(sAMAccountName={0})</module-option>            <module-option name="rolesCtxDN">cn=Users,dc=test,dc=company,dc=com</module-option>            <module-option name="roleFilter">(sAMAccountName={0})</module-option>            <module-option name="roleAttributeID">memberOf</module-option>            <module-option name="roleAttributeIsDN">true</module-option>            <module-option name="roleNameAttributeID">cn</module-option>            <module-option name="roleRecursion">-1</module-option>            <module-option name="searchScope">ONELEVEL_SCOPE</module-option>        </login-module>    </authentication></application-policy>

Add the following line to the jboss-web.xml file of a web application running under jboss, it will force authentication against the AD server:

<security-domain>java:/jaas/LdapExtLogin</security-domain>
Finally, you also need to add <security-constraint>, <login-config>, and <security-role> blocks to the web.xml file for the web app.

<security-constraint>     <web-resource-collection>         <web-resource-name>Sample Application2</web-resource-name>         <description>Require users to authenticate</description>         <url-pattern>*.jsp</url-pattern>         <http-method>POST</http-method>         <http-method>GET</http-method>     </web-resource-collection>     <auth-constraint>         <description>Only allow Authenticated_users role</description>         <role-name>NormalUsers</role-name>     </auth-constraint>     <user-data-constraint>         <description>Encryption is not required for the application in general.         </description>         <transport-guarantee>NONE</transport-guarantee>     </user-data-constraint> </security-constraint> <login-config>     <auth-method>FORM</auth-method>     <form-login-config>         <form-login-page>/login.jsp</form-login-page>         <form-error-page>/login_error.html</form-error-page>     </form-login-config> </login-config> <security-role>     <role-name>NormalUsers</role-name> </security-role></security-constraint>

Role Based Access Control (RBAC) with JBoss and LDAP
This is an example explaining how easy it is to plug into JBoss’s authentication framework. It does a lot more work than needed to use the built in LdapLoginModule described above. It is intended more to show how to write your own module. Be aware that the directory configuration used in this example does not match the structure of an AD server and will not work with the LdapLoginModule. The example uses user, group, and role concepts while the LdapLoginModule combines the group and role concepts.

Concept 2: Manual Single Sign On (SSO)
This SSO concept means that once a user has authenticated against a particular server for one application, she can access other applications/services on that server without having to re-enter a password. Careful of the phrase “on that server.” The implication is that all of services themselves reside on a single server (i.e. under a single JBoss instance). This is not necessarily the case. There are SSO solutions that can be integrated with a web application to allow it to run all of its authentication steps through another server. See the Yale CAS System and some further notes on it below.

An old listing of many SSO solutions for both web and fat-client applications and WS/SOA (Web Services/Service Oriented Architecture) apps is here. Yale’s CAS and JBoss’s built in authentication system were the most interesting to my group when I did the research because they did most of what we wanted. Others are more bare bones and require more work to implement, or are targeted at WS/SOA apps, or may work with LDAP in general, but not Active Directory specifically. A study of the CAS system and two others is here. I have not looked at the two other solutions.

JBoss
The built-in JBoss security system (using the LdapExtLoginModule described above) does support SSO, though a small change must be made to the Tomcat server.xml file as detailed on the JBoss Wiki SSO page. This tweak may no longer be required with newer versions of JBoss.

CAS
A good example of how to use this system is on DeveloperWorks. A couple of notes on it:

  • Requires HTTPS and some extra setup of Tomcat
  • Does allow SSO for apps running on multiple servers. Those apps just have to be able to reach the CAS server.
  • Unclear if it implements role-based access, or if it only checks that a user/pword are correct.
  • Someone posted how to integrate SecurityFilter (a Java library we use to manage user authentication) with CAS on the CAS wiki.

Concept 3: Automatic SSO
The SSO systems described above do not use the authentication credentials that a user receives by logging into his computer to automatically access a web application. You might call this “Auto Single Sign On” authorization. While this is relatively easy to do in fat-clients with JAAS, there is no universally implemented standard for doing it with web applications. Until recently, the only way to do it through a browser was with Microsoft specific technology called Integrated Windows Authentication (IWA). This has historically been called NT Challenge/Response or NTLM authentication, though NTLM is only one authentication mechanism in IWA. IWA can use Kerberos as it’s authentication mechanism as well. Kerberos is the default for Windows 2K and beyond. Compared to the three standard methods of authentication for web applications listed below (see User Login Authentication Schemes), IWA is similar to the Digest method in that the password is never sent over the wire. These pages gives a basic overview of how IWA works, with references to both NTLM and Kerberos authentication:
* http://support.microsoft.com/kb/264921/
* http://cyberforge.com/weblog/aniltj/archive/2004/10/25/705.aspx

Only Microsoft products on both the client side (Internet Explorer) and server side (IIS) have full, built-in and auto-activated support for this type of authentication. However, other web browsers and web application servers can be configured/extended to support a standard that IWA is based on. The general term for this form of authentication is SPNEGO, the Simple and Protected Negotiation mechanism. When used in web applications, it is called “HTTP Negotiate” authentication. This is as opposed to “HTTP Form” or “Http Basic” authentication, the two most common types.

SPNEGO Mechanisms
SPNEGO means Simple and Protected Negotiation. The negotiation part refers to the mechanism used to securely transfer user credentials from the client’s browser to the web app server. There are two primary options out in the world now – kerberos and NTLM. NTLM is a MS Windows specific protocol that is used by client machines to connect to Windows NT domains. Starting with Windows 2000 Server and the introduction of Active Directory servers as domain controllers, kerberos became the default authentication mechanism, though NTLM is still supported. This is useful if you only have Windows-based web clients because you don’t have to do any special configuration on the Active Directory server. If you want to use kerberos as the mechanism, you must add an entry to the Active Directory server for the web application host machine.

HTTP Negotiate Steps
When a web application tells a web client that Negotiate authentication is required, the client can determine what negotiation mechanism it wants to use. If the client is a Windows machine, the machine belongs to the Active Directory domain, and the user has logged into the client using a domain account, the web browser will first try to use kerberos. (Note that FireFox and Mozilla must have a preference setting changed to try kerberos. Internet Explorer will do it automatically.) The browser will attempt to obtain a kerberos ticket for the web application server from the AD domain controller. For this to work, AD must contain an entry for the web application server. If no such entry it present, the browser will revert to using NTLM as the authentication mechanism instead of kerberos. This will still happen without the user being prompted.

If the user is not logged in using a domain account (or Firefox/Mozilla is being used and has not been configured to use the negotiate authentication), she will be prompted to enter a login and password. Even though the login box looks like one you would see with BASIC authentication, the credentials are transferred in an encrypted NTLM block. Kerberos is not used. While NTLM is somewhat less secure than a full kerberos ticket exchange, it is much more secure than BASIC or FORM authentication, which transfers the user name and password in the clear.

Configuring Kerberos Negotiate Authentication
I’ll save this for a later post. My notes for this are tuned toward the solution I ended up implementing, so I’ll have to tweak them to be generally useful.

Random Tip: When testing kerberos – domain/realm must be entered in all caps – e.g. MYCOMPANY.COM. Otherwise you will get a “Pre-authentication information was invalid (24)” error.

Misc

User Authentication Schemes
The Sun webservices docs have a good summary of the standard types of authentication for web applications. Basic and Form based authentication are essentially the same from a security standpoint. They both send user name and password information in clear text to the server. Form authentication just lets you customize how it looks to the user in a web page.

Digest never sends the actual password across the wire. Instead, the server issues a “nonce” value (one-time, time-specific bit of data) to the client. The client then encrypts that with the users credentials (like an X.509 certificate), and sends back the username and encrypted result. The server can then use those to determine if the proper password/credentials were entered on the client side.

References
Here is where I got most of my understanding of kerberos, NTLM, and web-based security in general. Remember, I did this initial research almost 3 years ago, so some of these links may now be outdated.

Kerberos overview
http://www.microsoft.com/technet/prodtechnol/windows2000serv/deploy/confeat/kerberos.mspx

Kerberos under Windows
* http://support.microsoft.com/kb/326985/
* http://searchwinit.techtarget.com/tip/0,289483,sid1_gci942639,00.html

Examples using GSSAPI to connect a client machine to a server
* http://forum.java.sun.com/thread.jspa?threadID=579829&tstart=300 – Note that this is not targeted at web app (or a web server). Rather, it is showing how a client app running on a machine that has a kerberos ticket in its cache (i.e. where the user has logged into the machine as a domain user) can use it to authenticate against an LDAP server for doing operations.
* http://forum.java.sun.com/thread.jspa?threadID=638537 – Uses GSSAPI to authenticate to a LDAP directory (rather than simple authentication). Note that this is only useful for fat clients (or a server)

Browser Configuration
* http://ackbarr.xoops.org/archives/2005/03/31/integrated-windows-authentication-in-firefox/ – How to change the FF installer to auto-enable the settings.

NTLM References
* http://davenport.sourceforge.net/ntlm.html – The unofficial bible for how NTLM authentication works. This does not tell you directly how Internet Explorer gets the security context from a user. It does contain a nice list of “Links and References” near the end.
* http://www.theserverside.com/news/thread.tss?thread_id=28101 – Thread on how to do NTLM in a Java web app
* http://forums.mozillazine.org/viewtopic.php?p=631269 – Mozilla thread on NTLM support
* https://bugzilla.mozilla.org/show_bug.cgi?id=17578 – Mozilla Kerberos support
* http://curl.haxx.se/rfc/ntlm.html – NTLM in detail – including info on POST behavior

Negotiate authentication in various other web application servers
* http://lists.samba.org/archive/jcifs/2004-July/003651.html- Tomcat and SPNEGO
o Related: http://lists.samba.org/archive/jcifs/2005-April/004939.html
* http://appliedcrypto.com – Tomcat and SPNEGO/Kerberos (costs $$$). Their offerings are almost entirely based on various open source projects that they have pieced together
* http://blog.sun.com/roller/page/wyllys/?anchor=kerberos_web_authentiation_with_apache – Apache Kerberos plugins
* http://rc.vintela.com/topics/apache/mod_auth_vas/- Another Apache Plugin