Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3
Excerpt

This HOWTO describes how to set up CAS to authenticate to LDAP using the GSSAPI mechanism with a Kerberos 5 backend.

Environment: 

Server: Fedora Core 6 + CAS 3.1 + Tomcat 5.5.20 + OpenLDAP 2.3.30 + Cyrus SASL 2.1.22 + Kerboeros 1.5-23

...

1. Edit /etc/named.conf, add langhua zone:

Code Block
borderStylesolid
title/etc/named.confborderStylesolid

zone "1.168.192.IN-ADDR.ARPA." IN {
        type master;
        file "192.168.1.db";
};

zone "langhua." IN {
        type master;
        file "named.langhua";
};

2. Create /var/named/named.langhua

Code Block
borderStylesolid
title/var/named/named.langhuaborderStylesolid

$TTL 1H
@	SOA	localhost.	root.localhost. (	2
						3H
						1H
						1W
						1H )
	NS	localhost.

auth.langhua.                          IN 1H A 192.168.1.110

_kerberos                              IN TXT "AUTH.LANGHUA"
_kerberos._udp.auth.langhua.           IN SRV 0 0 88  auth.langhua.
_kerberos-master._udp.auth.langhua.    IN SRV 0 0 88  auth.langhua.
_kerberos-adm._tcp.auth.langhua.       IN SRV 0 0 749 auth.langhua.
_kpasswd._udp.auth.langhua.            IN SRV 0 0 464 auth.langhua.

_ldap._tcp.auth.langhua.               IN SRV 0 0 389 auth.langhua.
_ldap._tcp.dc._msdcs.auth.langhua.     IN SRV 0 0 389 auth.langhua.
_kerberos._tcp.dc._msdcs.auth.langhua. IN SRV 0 0 88  auth.langhua.

3. Create /var/name/192.168.1.db

Code Block
borderStyle
borderStylesolid
title/var/name/192.168.1.dbsolid

$TTL 1H
@	SOA	localhost.	root.localhost. (	2
						3H
						1H
						1W
						1H )
	NS	localhost.
110	PTR	auth.langhua.

...

1. Config /etc/krb5.conf

Code Block
borderStylesolid
title/etc/krb5.confborderStylesolid

[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log

[libdefaults]
 default_realm = AUTH.LANGHUA
 dns_lookup_realm = false
 dns_lookup_kdc = false
 ticket_lifetime = 24h
 forwardable = true

[realms]
 AUTH.LANGHUA = {
  kdc = auth.langhua:88
  admin_server = auth.langhua:749
  default_domain = langhua
 }

[domain_realm]
.langhua = AUTH.LANGHUA

[kdc]
 profile = /var/kerberos/krb5kdc/kdc.conf

[appdefaults]
 pam = {
   debug = false
   ticket_lifetime = 36000
   renew_lifetime = 36000
   forwardable = true
   krb4_convert = false
 }

...

This file is configed in /etc/krb5.conf.

Code Block
borderStylesolid
title/var/kerberos/krb5kdc/kdc.conf
borderStylesolid

[kdcdefaults]
 acl_file = /var/kerberos/krb5kdc/kadm5.acl
 dict_file = /usr/share/dict/words
 admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab
 v4_mode = nopreauth

[realms]
  AUTH.LANGHUA = {
  master_key_type = des3-hmac-sha1
  supported_enctypes = des3-hmac-sha1:normal des-cbc-crc:normal
 }

...

This file is configed in /var/kerberos/krb5kdc/kdc.conf.

Code Block
borderStylesolid
title/var/kerberos/krb5kdc/kadm5.acl
borderStylesolid

kadmin/admin@AUTH.LANGHUA              *
ldap/auth.langhua@AUTH.LANGHUA         *
krbadm@AUTH.LANGHUA                    *
*/*@AUTH.LANGHUA                       i
host/auth.langhua@AUTH.LANGHUA         *

...

7. Edit /etc/krb.realms

Code Block
borderStylesolid
title/etc/krb.realmsborderStylesolid

auth.langhua            AUTH.LANGHUA
.langhua                AUTH.LANGHUA

8. Edit /etc/krb.conf

Code Block
borderStylesolid
title/etc/krb.confborderStylesolid

AUTH.LANGHUA
AUTH.LANGHUA	auth.langhua:88
AUTH.LANGHUA	auth.langhua:749 admin server

...

Config Cyrus SASL GSSAPI

1. Add ldap service

 

1.1 kadmin.local -q "addprinc -randkey ldap/auth.langhua@AUTH.LANGHUA"

...

3.2 sasl2-sample-client -s ldap -m GSSAPI auth.langhua

Code Block
borderStyle
borderStylesolid
titleOutput in terminalsolid

receiving capability list... recv:
{6}
GSSAPI
GSSAPI
please enter an authorization id:  test

send: {6}
GSSAPI
send:
{1}
Y
send:
{525}
`\[82\]\[2\]\[9\]\[6\]\[9\]*\[86\]H\[86\]\[F7\]\[12\]\[1\]\[2\]\[2\]\[1\]\[0\]n\[82\]\[1\]\[F8\]0\[82\]\[1\]\[F4\]\[A0\]\[3\]\[2\]\[1\]\[5\]\[A1\]\[3\]\[2\]\[1\]\[E\]\[A2\]\[7\]\[3\]\[5\]\[0\] \[0\]\[0\]\[0\]\[A3\]\[82\]\[1\]\[10\]a\[82\]\[1\]\[C\]0\[82\]\[1\]\[8\]\[A0\]\[3\]\[2\]\[1\]\[5\]\[A1\]\[E\]\[1B\]\[C\]AUTH.LANGHUA\[A2\]\[1F\]0\[1D\]\[A0\]\[3\]\[2\]\[1\]\[3\]\[A1\]\[16\]0\[14\]\[1B\]\[4\]ldap\[1B\]\[C\]auth.langhua\[A3\]\[81\]\[CF\]0\[81\]\[CC\]\[A0\]\[3\]\[2\]\[1\]\[10\]\[A1\]\[3\]\[2\]\[1\]\[4\]\[A2\]\[81\]\[BF\]\[4\]\[81\]\[BC\].\[A3\],FO\[CC\]\[12\]P*
{r_l\[D5\]\[A1\]\[9F\]\[D9\]\[7F\]\[FF\]A2\[9C\]\[E1\]\[DB\]KZ%\[D9\]\[83\]\[AD\]\[C5\]j\[84\]\[87\]\[FD\]\[EB\]\[C9\]\]DGS\[8C\]%\[16\]\[7F\]&0\[BC\]r(\[BD\]\[11\]\[FF\]v\[EE\]V\[DD\]I\[EA\]E<\[B8\]\[B\]\[1D\]2\[D4\]>\[CB\]~1\[FB\]\[1\]fC\[1F\]5\[EA\]\[CD\]\[7\]\[E7\]\[DB\]\[B9\]\[B1\]\[14\]S\[95\]6?8Ea\[F5\]6\[8A\]\[BB\]\[8D\]V\[B9\]\[C1\]\[A5\]\[8\]\[C2\]LH,5\[1A\]\[BC\]"\[91\]v\[CC\]\[B2\]\[2\]QJd}
*\[97\]V\[A9\]g\[B5\]\[F6\]\[D0\]\[C4\]\[C9\],\!\[DF\]\[F8\]\[8E\]\[A3\]\[16\]\[7\]\[E5\])N\[E6\]\[11\] \[8\]+\[85\]\[FD\]\[1B\]\[11\]\[C3\]\[96\]\[98\]\[80\]}g<\[A3\]5\[1E\]\[BC\]si\[8A\]c9\[8A\]\[1A\]w\[13\]\[1E\]\[18\]\[87\](\[98\]\[E5\]\[E7\]\[89\]\[85\]W\[AC\]s\[A5\]v\[D6\]5v\[80\]\[A3\]\[FA\]\[A4\]\[81\]\[CA\]0\[81\]\[C7\]\[A0\]\[3\]\[2\]\[1\]\[10\]\[A2\]\[81\]\[BF\]\[4\]\[81\]\[BC\]\[15\]>\[D6\]\[CB\]h\[1B\]\[9\]Y\[AC\]\[C8\]hh\[F1\]**_\[D4\]\[82\]GD\[FD\]4\[AA\]"$\[EC\]\[7F\]&\[B8\]\[F\]D\[7\]\[97\]\[82\]\[D\]\[A7\]\[D\]\[A2\]9\[8C\]\[88\]\[C0\]-\[DC\]\[E6\]\[A\]\[EB\]D6\[1F\]\[A4\]\[E2\]\[EE\]\[D1\]\[6\]x\[15\]\[D1\]\[EE\]4d\[86\]\[84\]\[99\]\[16\]\[AC\]\[F5\]C\[E8\]\[A9\]u#\[CE\]\[C5\]\[E1\]\[9B\]_*_\[E8\]\[B4\]\[93\]z\[E9\])\[15\]\[D\]L/E\[BD\]\[E6\]`\[1F\]_\[F2\]\[BC\]\[1A\]\[D7\]1\[BF\]\[C1\]\[2\]O\[8C\]\[1D\]\[A0\]\[C\]\[AB\]\[8B\]\[AB\]\[F5\]\[DB\]\[D4\]\[B1\]\[ED\]\[A\]\[AF\]\[9\]\[B\]s\[E9\]\[10\]\[FB\]u\[11\]\[E\]N\[1F\]*\[15\]\[AE\]h\[F5\]\[1C\]\[8E\]\[92\]\[AA\]6\|0\[F4\]\[FE\]\[CB\]\[9A\]\[9D\]c^o4\[CB\]\[8C\]\[B8\]\[99\]\[89\]\[A5\]\[18\]\[EA\]pE\[B1\]\[C7\]\[E8\]\[90\]\[82\]\[EE\]\[A6\](\[1A\]1Q\[D0\]\[81\]X\[C6\]q\[9B\]\[EA\]v\[C\]\[FA\]\[E9\]jd\[84\]\[BD\]\[CF\]\[90\]S\[8E\]P
recv:
{153}
`\[81\]\[96\]\[6\]\[9\]*\[86\]H\[86\]\[F7\]\[12\]\[1\]\[2\]\[2\]\[2\]\[0\]o\[81\]\[86\]0\[81\]\[83\]\[A0\]\[3\]\[2\]\[1\]\[5\]\[A1\]\[3\]\[2\]\[1\]\[F\]\[A2\]w0u\[A0\]\[3\]\[2\]\[1\]\[10\]\[A2\]n\[4\]l\[5\],\[B7\]\[F6\]\[CD\]\[C\]\[BF\]\[1\]{*\[E0\]\[1F\]\[92\]\[FC\]\[10\]\[94\]U\[DF\]\[9E\]\[BA\]k_\[A4\]G&\[EC\]g:\[BA\]J\[C6\]\[C1\]sQ:'\[87\]/\[95\]\[C8\]W\[C5\]^47N\[9B\]im`\[BB\]\[B4\]\[C5\]\[EB\]\[BC\]w\[80\]\[A7\]\[1D\]Q\[89\]\[8B\]L\[8D\]N\[E7\]\[87\]\[C2\]\[EB\]\[D9\]k{j\[D\]\[AD\]\[A3\]w\[B1\]\[A2\]\[B3\]X\[FD\]"\[99\]LG\[88\]^\[DE\]\[CD\]\[ED\]\[AC\]\[F8\]\[10\](\[FA\]h\[2\]\[B7\]L\[E2\]K6\[2\]\[F5\]C\[91\]u
send:
{0}
recv:
{65}
`?\[6\]\[9\]*\[86\]H\[86\]\[F7\]\[12\]\[1\]\[2\]\[2\]\[2\]\[1\]\[4\]\[0\]\[FF\]\[FF\]\[FF\]\[FF\]\[91\]w\[FA\]J\[96\]\[9D\]4\[8F\]Is\[90\]\[89\]\[F\]\[B2\]\[BC\]X\[2\].\[C\];\[89\]\[9A\]\[3\]\[F9\]\[CF\]DU\[BA\]\[98\]x#\[CF\]\[FF\]G\[9E\]:\[1\]\[0\]\[0\]\[0\]\[4\]\[4\]\[4\]\[4\]
send:
{73}
`G\[6\]\[9\]*\[86\]H\[86\]\[F7\]\[12\]\[1\]\[2\]\[2\]\[2\]\[1\]\[4\]\[0\]\[FF\]\[FF\]\[FF\]\[FF\]\[CB\]\[9F\]K\[D1\]\[81\]m\[E4\]\[A2\]\[A6\]\[E2\]\[C\]\[7F\]a\[8C\]\[1B\]\[A5\]\[CC\]m\[E6\]\[BB\]\[E4\]\[A4\]\[9F\]s\[B5\]\[F8\]\[1F\]\[D1\]\[3\]}\[95\]\[F1\]\[5\]\[C2\]s\[16\]\[1\]\[0\]\[0\]\[0\]test\[8\]\[8\]\[8\]\[8\]\[8\]\[8\]\[8\]\[8\]
successful authentication
closing connection

...

2. Edit /etc/slapd.conf

Code Block
borderStylesolid
title/etc/slapd.confborderStylesolid

#
# See slapd.conf(5) for details on configuration options.
# This file should NOT be world readable.
#
include		/etc/openldap/schema/core.schema
include		/etc/openldap/schema/cosine.schema
include		/etc/openldap/schema/inetorgperson.schema
include		/etc/openldap/schema/nis.schema
include     /etc/openldap/schema/krb5-kdc.schema

# Allow LDAPv2 client connections.  This is NOT the default.
allow bind_v2

# Do not enable referrals until AFTER you have a working directory
# service AND an understanding of referrals.
#referral	ldap://root.openldap.org

pidfile		/var/run/openldap/slapd.pid
argsfile	/var/run/openldap/slapd.args

# Load dynamic backend modules:
# modulepath	/usr/lib/openldap
# moduleload	back_bdb.la
# moduleload	back_ldap.la
# moduleload	back_ldbm.la
# moduleload	back_passwd.la
# moduleload	back_shell.la

# The next three lines allow use of TLS for encrypting connections using a
# dummy test certificate which you can generate by changing to
# /etc/pki/tls/certs, running "make slapd.pem", and fixing permissions on
# slapd.pem so that the ldap user or group can read it.  Your client software
# may balk at self-signed certificates, however.
# TLSCACertificateFile /etc/pki/tls/certs/ca-bundle.crt
# TLSCertificateFile /etc/openldap/slapd.pem
# TLSCertificateKeyFile /etc/pki/tls/certs/slapd.pem
TLSCACertificateFile /etc/pki/demoCA/cacert.pem
TLSCertificateFile /etc/pki/demoCA/certs/ldap-cert.pem
TLSCertificateKeyFile /etc/pki/demoCA/certs/ldap-key.pem
# TLSVerifyClient demand

# Sample security restrictions
#	Require integrity protection (prevent hijacking)
#	Require 112-bit (3DES or better) encryption for updates
#	Require 63-bit encryption for simple bind
# security ssf=1 update_ssf=112 simple_bind=64

# disallow bind_simple

# Sample access control policy:
#	Root DSE: allow anyone to read it
#	Subschema (sub)entry DSE: allow anyone to read it
#	Other DSEs:
#		Allow self write access
#		Allow authenticated users read access
#		Allow anonymous users to authenticate
#	Directives needed to implement policy:
# access to dn.base="" by * read
# access to dn.base="cn=Subschema" by * read

#
# if no access controls are present, the default policy
# allows anyone and everyone to read anything but restricts
# updates to rootdn.  (e.g., "access to * by * read")
#
# rootdn can always read and write EVERYTHING!
# access to * by * read

#######################################################################
# ldbm and/or bdb database definitions
#######################################################################

database	bdb
suffix		"o=langhua,c=cn"
# rootdn		"uid=root,o=langhua,c=cn"
# Cleartext passwords, especially for the rootdn, should
# be avoided.  See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
# rootpw		{SSHA}mpfjTVX6cJ+oMgoB1wIjJ8HTR/2kyBga

# The database directory MUST exist prior to running slapd AND
# should only be accessible by the slapd and slap tools.
# Mode 700 recommended.
directory	/var/lib/ldap

# Indices to maintain for this database
index objectClass                       eq,pres
index ou,cn,mail,surname,givenname      eq,pres,sub
index uidNumber,gidNumber,loginShell    eq,pres
index uid,memberUid                     eq,pres,sub
index nisMapName,nisMapEntry            eq,pres,sub

# Replicas of this database
# replogfile /var/lib/ldap/openldap-master-replog
# replica host=ldap-1.example.com:389 starttls=critical
#     bindmethod=sasl saslmech=GSSAPI
#     authcId=host/ldap-master.example.com@EXAMPLE.COM


# authzTo: uid=[HOWTO Setup LDAP GSSAPI+Kerberos Authentication in CAS^,]*,o=langhua,c=cn

sasl-realm	AUTH.LANGHUA
sasl-host	auth.langhua

access to attrs=userPassword
        by self write
        by dn="uid=test,cn=auth.langhua,cn=gssapi,cn=auth" write
        by anonymous auth
        by * none

access to *
        by dn="uid=test,cn=auth.langhua,cn=gssapi,cn=auth" write
        by self write
        by * read

...

1. Create /cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/BindLdapGssapiAuthenticationHandler.java

Code Block
borderStylesolid
title/cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/BindLdapGssapiAuthenticationHandler.java
borderStylesolid

/*
 * Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
 * distributed with this file and available online at
 * http://www.ja-sig.org/products/cas/overview/license/
 */
package org.jasig.cas.adaptors.ldap;

import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.jasig.cas.util.annotation.IsIn;

/**
 * Handler to do LDAP GSSAPI bind.
 *
 * @author Scott Battaglia
 * @version $Revision: 42053 $ $Date: 2007-06-10 09:17:55 -0400 (Sun, 10 Jun 2007) $
 * @since 3.0.3
 * @author Shi Yusen, shiys@langhua.cn
 */
public class BindLdapGssapiAuthenticationHandler extends
    AbstractLdapGssapiAuthenticationHandler {

    /** The default maximum number of results to return. */
    private static final int DEFAULT_MAX_NUMBER_OF_RESULTS = 1000;

    /** The default timeout. */
    private static final int DEFAULT_TIMEOUT = 1000;

    /** The search base to find the user under. */
    private String searchBase;

    /** The scope. */
    @IsIn({SearchControls.OBJECT_SCOPE, SearchControls.ONELEVEL_SCOPE,
        SearchControls.SUBTREE_SCOPE})
    private int scope = SearchControls.SUBTREE_SCOPE;

    /** The maximum number of results to return. */
    private int maxNumberResults = DEFAULT_MAX_NUMBER_OF_RESULTS;

    /** The amount of time to wait. */
    private int timeout = DEFAULT_TIMEOUT;

    /** Boolean of whether multiple accounts are allowed. */
    private boolean allowMultipleAccounts;

    protected final boolean authenticateUsernamePasswordInternal(
        final UsernamePasswordCredentials credentials)
        throws AuthenticationException {

        DirContext context;

        context = this.getContextSource().getDirContext(credentials.getUsername(), credentials.getPassword());

        if (context == null) {
        	return false;
        }

        return true;
    }

    protected String composeCompleteDnToCheck(final String dn,
        final UsernamePasswordCredentials credentials) {
        return dn + "," + this.searchBase;
    }

    /**
     * Method to return whether multiple accounts are allowed.
     * @return true if multiple accounts are allowed, false otherwise.
     */
    protected boolean isAllowMultipleAccounts() {
        return this.allowMultipleAccounts;
    }

    /**
     * Method to return the max number of results allowed.
     * @return the maximum number of results.
     */
    protected int getMaxNumberResults() {
        return this.maxNumberResults;
    }

    /**
     * Method to return the scope.
     * @return the scope
     */
    protected int getScope() {
        return this.scope;
    }

    /**
     * Method to return the search base.
     * @return the search base.
     */
    protected String getSearchBase() {
        return this.searchBase;
    }

    /**
     * Method to return the timeout.
     * @return the timeout.
     */
    protected int getTimeout() {
        return this.timeout;
    }

    public final void setScope(final int scope) {
        this.scope = scope;
    }

    /**
     * @param allowMultipleAccounts The allowMultipleAccounts to set.
     */
    public void setAllowMultipleAccounts(final boolean allowMultipleAccounts) {
        this.allowMultipleAccounts = allowMultipleAccounts;
    }

    /**
     * @param maxNumberResults The maxNumberResults to set.
     */
    public final void setMaxNumberResults(final int maxNumberResults) {
        this.maxNumberResults = maxNumberResults;
    }

    /**
     * @param searchBase The searchBase to set.
     */
    public final void setSearchBase(final String searchBase) {
        this.searchBase = searchBase;
    }

    /**
     * @param timeout The timeout to set.
     */
    public final void setTimeout(final int timeout) {
        this.timeout = timeout;
    }

}

2. Create /cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/AbstractLdapGssapiAuthenticationHandler.java

Code Block
borderStylesolid
title/cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/AbstractLdapGssapiAuthenticationHandler.javaborderStylesolid

/*
 * Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
 * distributed with this file and available online at
 * http://www.ja-sig.org/products/cas/overview/license/
 */
package org.jasig.cas.adaptors.ldap;

import org.jasig.cas.adaptors.ldap.util.AuthenticatedLdapGssapiContextSource;
import org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.jasiginspektr.cascommon.utilioc.annotation.NotNull;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.util.Assert;

/**
 * Abstract class to handle common LDAP functionality.
 *
 * @author Scott Battaglia
 * @version $Revision: 42053 $ $Date: 2007-06-10 09:17:55 -0400 (Sun, 10 Jun 2007) $
 * @since 3.0.3
 *
 * @author Shi Yusen, shiys@langhua.cn
 */
public abstract class AbstractLdapGssapiAuthenticationHandler extends
    AbstractUsernamePasswordAuthenticationHandler implements InitializingBean {

    /** LdapTemplate to execute ldap queries. */
    @NotNull
    private LdapTemplate ldapTemplate;

    /** Instance of ContextSource */
    @NotNull
    private AuthenticatedLdapGssapiContextSource contextSource;

    /** The filter path to the uid of the user. */
    @NotNull
    private String filter;

    /** Whether the LdapTemplate should ignore partial results. */
    private boolean ignorePartialResultException = false;

    /**
     * Method to set the datasource and generate a JdbcTemplate.
     *
     * @param dataSource the datasource to use.
     */
    public final void setContextSource(final AuthenticatedLdapGssapiContextSource contextSource) {
        this.contextSource = contextSource;
        this.ldapTemplate = new LdapTemplate(contextSource);
    }

    public final void setIgnorePartialResultException(final boolean ignorePartialResultException) {
        this.ignorePartialResultException = ignorePartialResultException;
    }

    /**
     * Method to return the LdapTemplate
     *
     * @return a fully created LdapTemplate.
     */
    protected final LdapTemplate getLdapTemplate() {
        return this.ldapTemplate;
    }

    protected final AuthenticatedLdapGssapiContextSource getContextSource() {
        return this.contextSource;
    }

    protected final String getFilter() {
        return this.filter;
    }

    public final void afterPropertiesSet() throws Exception {
        Assert.isTrue(this.filter.contains("%u"), "filter must contain %u");
        this.ldapTemplate.setIgnorePartialResultException(this.ignorePartialResultException);
    }

    /**
     * @param filter The filter to set.
     */
    public final void setFilter(final String filter) {
        this.filter = filter;
    }
}

3. Create /cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/util/AuthenticatedLdapGssapiContextSource.java

Code Block
borderStylesolid
title/cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/util/AuthenticatedLdapGssapiContextSource.javaborderStylesolid

/*
 * Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
 * distributed with this file and available online at
 * http://www.ja-sig.org/products/cas/overview/license/
 */
package org.jasig.cas.adaptors.ldap.util;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.ldap.support.LdapContextSource;

import sun.security.krb5.Credentials;

public class AuthenticatedLdapGssapiContextSource extends LdapContextSource {
	private DirContext context;

	public Hashtable<String, String> environment;

    public DirContext getDirContext(final String principal, final String credentials) {

    	if
(context == null) {             environment = (Hashtable) getAnonymousEnv().clone();

            Enumeration keys = environment.keys();
            while (keys.hasMoreElements())
            {
                String key = (String)keys.nextElement();
                System.setProperty(key, environment.get(key));
            }

            environment.put(Context.SECURITY_PRINCIPAL, principal);
            environment.put(Context.SECURITY_CREDENTIALS, credentials);

            LoginContext lc = null;
            try {
                lc = new LoginContext(AuthenticatedLdapGssapiContextSource.class.getName(),
                        new UsernamePasswordCallbackHandler(principal, credentials));
                lc.login();
            } catch (LoginException ex) {
                ex.printStackTrace();
                throw new DataAccessResourceFailureException("GSSAPI login problem: " + ex)return null;
            }

            context = (DirContext) Subject.doAs(lc.getSubject(), new LDAPAction(environment));

            if (context == null) {
                throw new DataAccessResourceFailureException("a problem with GSSAPI occurred - couldn't create a GSSAPI directory context");
            } else {
            	Iterator i = lc.getSubject().getPrivateCredentials().iterator();
            	while (i.hasNext()) {
            		Credentials creds = (Credentials) i.next();
            		if (creds.isForwardable()) {
                    	return context;
            		}
            	}
                throw new DataAccessResourceFailureException("a problem with GSSAPI occurred - please make sure your Credentials is forwardable. Try to use \"kinit -f yourpricinpal\".");
            }
    	}
else
{
    		return context;
    	}
    }

    /**
     * A simple JAAS CallbackHandler which accepts a Name String and Password
     * String in the constructor. Only NameCallbacks and PasswordCallbacks are
     * accepted in the callback array. This code based loosely on example given
     * in Sun's javadoc for CallbackHandler interface.
     *
     * Copied from org.jasig.cas.authentication.handler.support.JaasAuthenticationHandler
     */
    protected class UsernamePasswordCallbackHandler implements CallbackHandler {

        /** The username of the principal we are trying to authenticate. */
        private final String userName;

        /** The password of the principal we are trying to authenticate. */
        private final String password;

        /**
         * Constuctor accepts name and password to be used for authentication.
         *
         * @param userName name to be used for authentication
         * @param password Password to be used for authentication
         */
        protected UsernamePasswordCallbackHandler(final String userName,
            final String password) {
            this.userName = userName;
            this.password = password;

        }

        public void handle(final Callback[] callbacks)
            throws UnsupportedCallbackException {
            for (int i = 0; i < callbacks.length; i++) {
                final Callback callback = callbacks[i];

                if (callback.getClass().equals(NameCallback.class)) {
                    ((NameCallback) callback).setName(this.userName);
                } else if (callback.getClass().equals(PasswordCallback.class)) {
                    ((PasswordCallback) callback).setPassword(this.password
                        .toCharArray());
                } else {
                    throw new UnsupportedCallbackException(callback,
                        "Unrecognized Callback");
                }
            }
        }
    }
}

4. Create /cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/util/LDAPAction.java

Code Block
borderStylesolid
title/cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/util/LDAPAction.javaborderStylesolid

/*
 * Copyright 2008 The JA-SIG Collaborative. All rights reserved. See license
 * distributed with this file and available online at
 * http://www.ja-sig.org/products/cas/overview/license/
 */
package org.jasig.cas.adaptors.ldap.util;

import java.security.PrivilegedAction;
import java.util.Hashtable;

import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

/**
 * @author Shi Yusen
 *
 * Ref: http://forum.java.sun.com/thread.jspa?threadID=395518&messageID=2444444
 */
public class LDAPAction implements PrivilegedAction {

	private Hashtable env;

	public LDAPAction(Hashtable env){
		this.env = env;
	}

	/* (non-Javadoc)
	 * @see java.security.PrivilegedAction#run()
	 */
	public Object run() {

		DirContext result = null;

		try{
			result = new InitialDirContext(env);
		}catch(NamingException ex){
			ex.printStackTrace();
		}

		return result;
	}

}

...

6. Create $CAS/WEB-INF/gssapi.conf

Code Block
borderStylesolid
title$CAS/WEB-INF/gssapi.conf
borderStylesolid

org.jasig.cas.adaptors.ldap.util.AuthenticatedLdapGssapiContextSource {
  com.sun.security.auth.module.Krb5LoginModule required client=TRUE
  														useTicketCache=TRUE;
};

7. Edit $CAS/WEB-INF/deployerConfigContext.xml

Code Block
borderStylesolid
title$CAS/WEB-INF/deployerConfigContext.xmlborderStylesolid

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC  "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
	| deployerConfigContext.xml centralizes into one file some of the declarative configuration that
	| all CAS deployers will need to modify.
	|
	| This file declares some of the Spring-managed JavaBeans that make up a CAS deployment.
	| The beans declared in this file are instantiated at context initialization time by the Spring
	| ContextLoaderListener declared in web.xml.  It finds this file because this
	| file is among those declared in the context parameter "contextConfigLocation".
	|
	| By far the most common change you will need to make in this file is to change the last bean
	| declaration to replace the default SimpleTestUsernamePasswordAuthenticationHandler with
	| one implementing your approach for authenticating usernames and passwords.
	+-->
<beans>
	<!--
		| This bean declares our AuthenticationManager.  The CentralAuthenticationService service bean
		| declared in applicationContext.xml picks up this AuthenticationManager by reference to its id,
		| "authenticationManager".  Most deployers will be able to use the default AuthenticationManager
		| implementation and so do not need to change the class of this bean.  We include the whole
		| AuthenticationManager here in the userConfigContext.xml so that you can see the things you will
		| need to change in context.
		+-->
	<bean id="authenticationManager"
		class="org.jasig.cas.authentication.AuthenticationManagerImpl">
		<!--
			| This is the List of CredentialToPrincipalResolvers that identify what Principal is trying to authenticate.
			| The AuthenticationManagerImpl considers them in order, finding a CredentialToPrincipalResolver which
			| supports the presented credentials.
			|
			| AuthenticationManagerImpl uses these resolvers for two purposes.  First, it uses them to identify the Principal
			| attempting to authenticate to CAS /login .  In the default configuration, it is the DefaultCredentialsToPrincipalResolver
			| that fills this role.  If you are using some other kind of credentials than UsernamePasswordCredentials, you will need to replace
			| DefaultCredentialsToPrincipalResolver with a CredentialsToPrincipalResolver that supports the credentials you are
			| using.
			|
			| Second, AuthenticationManagerImpl uses these resolvers to identify a service requesting a proxy granting ticket.
			| In the default configuration, it is the HttpBasedServiceCredentialsToPrincipalResolver that serves this purpose.
			| You will need to change this list if you are identifying services by something more or other than their callback URL.
			+-->
		<property name="credentialsToPrincipalResolvers">
			<list>
				<!--
					| UsernamePasswordCredentialsToPrincipalResolver supports the UsernamePasswordCredentials that we use for /login
					| by default and produces SimplePrincipal instances conveying the username from the credentials.
					|
					| If you've changed your LoginFormAction to use credentials other than UsernamePasswordCredentials then you will also
					| need to change this bean declaration (or add additional declarations) to declare a CredentialsToPrincipalResolver that supports the
					| Credentials you are using.
					+-->
				<bean
					class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" />
				<!--
					| HttpBasedServiceCredentialsToPrincipalResolver supports HttpBasedCredentials.  It supports the CAS 2.0 approach of
					| authenticating services by SSL callback, extracting the callback URL from the Credentials and representing it as a
					| SimpleService identified by that callback URL.
					|
					| If you are representing services by something more or other than an HTTPS URL whereat they are able to
					| receive a proxy callback, you will need to change this bean declaration (or add additional declarations).
					+-->
				<bean
					class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />
			</list>
		</property>

		<!--
			| Whereas CredentialsToPrincipalResolvers identify who it is some Credentials might authenticate,
			| AuthenticationHandlers actually authenticate credentials.  Here we declare the AuthenticationHandlers that
			| authenticate the Principals that the CredentialsToPrincipalResolvers identified.  CAS will try these handlers in turn
			| until it finds one that both supports the Credentials presented and succeeds in authenticating.
			+-->
		<property name="authenticationHandlers">
			<list>
				<!--
					| This is the authentication handler that authenticates services by means of callback via SSL, thereby validating
					| a server side SSL certificate.
					+-->
				<bean
					class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" />

				<!--
					| This is the authentication handler declaration that every CAS deployer will need to change before deploying CAS
					| into production.  The default SimpleTestUsernamePasswordAuthenticationHandler authenticates UsernamePasswordCredentials
					| where the username equals the password.  You will need to replace this with an AuthenticationHandler that implements your
					| local authentication strategy.  You might accomplish this by coding a new such handler and declaring
					| edu.someschool.its.cas.MySpecialHandler here, or you might use one of the handlers provided in the adaptors modules.
					+-->
				<bean
					class="org.jasig.cas.adaptors.ldap.BindLdapGssapiAuthenticationHandler">
					<property name="filter" value="uid=%u" />
					<property name="searchBase" value="o=langhua,c=cn" />
					<property
						name="contextSource"
						ref="contextSource" />
				</bean>
			</list>
		</property>
	</bean>

	<bean id="contextSource" class="org.jasig.cas.adaptors.ldap.util.AuthenticatedLdapGssapiContextSource">
		<property name="anonymousReadOnly" value="true" />
		<property name="password" value="{password_goes_here}" />
		<property name="pooled" value="true" />
		<property name="urls">
			<list>
				<value>ldap://auth.langhua:389/</value>
			</list>
		</property>
		<property name="userName" value="{username_goes_here}" />
		<property name="baseEnvironmentProperties">
			<map>
				<entry>
				        <key><value>java.naming.factory.initial</value></key>
        				<value>com.sun.jndi.ldap.LdapCtxFactory</value>
        			</entry>
				<entry>
				        <key><value>java.naming.referral</value></key>
        				<value>ignore</value>
        			</entry>
				<entry>
				        <key><value>java.naming.ldap.attributes.binary</value></key>
        				<value>photo jpegphoto jpegPhoto</value>
        			</entry>
				<entry>
				        <key><value>java.naming.ldap.version</value></key>
        				<value>3</value>
        			</entry>
				<entry>
				        <key><value>java.naming.ldap.deleteRDN</value></key>
        				<value>false</value>
        			</entry>
				<entry>
				        <key><value>java.naming.ldap.derefAliases</value></key>
        				<value>searching</value>
        			</entry>
				<entry>
				        <key><value>javax.security.sasl.qop</value></key>
        				<value>auth-conf</value>
        			</entry>
				<entry>
				        <key><value>java.naming.provider.url</value></key>
        				<value>ldap://auth.langhua</value>
        			</entry>
				<entry>
				        <key><value>java.naming.security.authentication</value></key>
        				<value>GSSAPI</value>
        			</entry>
				<entry>
				        <key><value>java.security.auth.login.config</value></key>
        				<value>/usr/share/tomcat5/webapps/cas/WEB-INF/gssapi.conf</value>
        			</entry>
				<entry>
				        <key><value>javax.security.sasl.server.authentication</value></key>
        				<value>true</value>
        			</entry>
				<entry>
				        <key><value>javax.security.auth.useSubjectCredsOnly</value></key>
        				<value>false</value>
        			</entry>
        		</map>
		</property>
	</bean>
</beans>

...

Here is a picture to show the login process. Image Modified
 
Good Luck!

...