引用:https://wiki.jasig.org/pages/viewpage.action?pageId=10650669
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
Client: Fedora Core 6 + Firefox 2
Windows XP + IE6 SP2
Config DNS:
To make SSL and Kerberos work, I have to config DNS at the very beginning.
1. Edit /etc/named.conf, add langhua zone:
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
$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
$TTL 1H
@ SOA localhost. root.localhost. (
2
3H
1H
1W
1H )
NS localhost.
110
PTR auth.langhua.
|
4. nslookup auth.langhua
Server: 192.168.1.110
Address: 192.168.1.110#53
Name: auth.langhua
Address: 192.168.1.110
5. dig -x 192.168.1.110
; <<>> DiG 9.3.4-P1 <<>> -x 192.168.1.110
;; global options: printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 3829
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2
;; QUESTION SECTION:
;110.1.168.192.in-addr.arpa. IN PTR
;; ANSWER SECTION:
110.1.168.192.in-addr.arpa. 3600 IN PTR auth.langhua.
;; AUTHORITY SECTION:
1.168.192.in-addr.arpa. 3600 IN NS localhost.
;; ADDITIONAL SECTION:
localhost. 86400 IN A 127.0.0.1
localhost. 86400 IN AAAA ::1
;; Query time: 1 msec
;; SERVER: 192.168.1.110#53(192.168.1.110)
;; WHEN: Thu Nov 29 04:53:02 2007
;; MSG SIZE rcvd: 137
Config Kerberos:
1. Config /etc/krb5.conf
[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
}
|
2. kdb5_util create -s
3. Add users to Kerberos
kadmin.local -q "addprinc krbadm@AUTH.LANGHUA"
kadmin.local -q "addprinc ldapadm@AUTH.LANGHUA"
kadmin.local -q "addprinc host/auth.langhua@AUTH.LANGHUA"
kadmin.local -q "addprinc ldap/auth.langhua@AUTH.LANGHUA"
If you face this error in this step, delete the files under /var/kerberos/krb5kdc/ and redo the above step 2 and 3:
kadmin.local: Cannot find/read stored master key while initializing kadmin.local interface
4. Edit /var/kerberos/krb5kdc/kdc.conf
This file is configed in /etc/krb5.conf.
[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
}
|
5. Edit /var/kerberos/krb5kdc/kadm5.acl
This file is configed in /var/kerberos/krb5kdc/kdc.conf.
kadmin/admin
@AUTH
.LANGHUA *
ldap/auth.langhua
@AUTH
.LANGHUA *
krbadm
@AUTH
.LANGHUA *
*/*
@AUTH
.LANGHUA i
host/auth.langhua
@AUTH
.LANGHUA *
|
6. Config /var/kerberos/krb5kdc/kadm5.keytab
This file is configed in /var/kerberos/krb5kdc/kdc.conf.
kadmin.local -q "ktadd -k /var/kerberos/krb5kdc/kadm5.keytab kadmin/admin"
kadmin.local -q "ktadd -k /var/kerberos/krb5kdc/kadm5.keytab kadmin/changepw"
7. Edit /etc/krb.realms
auth.langhua AUTH.LANGHUA
.langhua AUTH.LANGHUA
|
8. Edit /etc/krb.conf
AUTH.LANGHUA
AUTH.LANGHUA auth.langhua:
88
AUTH.LANGHUA auth.langhua:
749
admin server
|
9. Start Kerberos
/etc/init.d/kadmin start
/etc/init.d/krb5kdc start
Test Kerberos
1. kadmin.local -q "ktadd -k /etc/krb5.keytab host/auth.langhua"
2. kinit -k host/auth.langhua
If the error "kinit(v5): Password incorrect while getting initial credentials" found, delete /etc/krb5.keytab, and redo step 1 and 2.
3. klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: host/auth.langhua@AUTH.LANGHUA
Valid starting Expires Service principal
11/29/07 02:44:44 11/30/07 02:44:44 krbtgt/AUTH.LANGHUA@AUTH.LANGHUA
Kerberos 4 ticket cache: /tmp/tkt0
klist: You have no tickets cached
4. Make sure kvno numbers are the same
4.1 kvno host/auth.langhua
host/auth.langhua@AUTH.LANGHUA: kvno = 5
4.2 klist -k /etc/krb5.keytab
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
--- -------------------------------------------------------------------
5 host/auth.langhua@AUTH.LANGHUA
5 host/auth.langhua@AUTH.LANGHUA
If the numbers are not same, redo step 1, 2, 3.
5. Test ktelnetd, krlogind, krshd
5.1 Edit ~/.k5login
host/auth.langhua@AUTH.LANGHUA
5.2 Start xinetd
chkconfig klogin on
chkconfig kshell on
chkconfig eklogin on
chkconfig krb5-telnet on
/etc/init.d/xinetd restart
5.3 krlogin auth.langhua -k AUTH.LANGHUA
This rlogin session is encrypting all data transmissions.
Last login: Thu Nov 29 04:46:08 from auth.langhua
You have new mail.
5.4 telnet -x 192.168.1.110 -k AUTH.LANGHUA
Trying 192.168.1.110...
Connected to auth.langhua (192.168.1.110).
Escape character is '^]'.
Waiting for encryption to be negotiated...
[ Kerberos V5 accepts you as ``host/auth.langhua@AUTH.LANGHUA'' ]
done.
Last login: Thu Nov 29 04:46:23 from auth.langhua
You have new mail.
5.5 rsh 192.168.1.110 -k AUTH.LANGHUA
Last login: Thu Nov 29 04:47:00 from auth.langhua
You have new mail.
Kerberos configuration is OK now.
Config Cyrus SASL GSSAPI
1. Add ldap service
1.1 kadmin.local -q "addprinc -randkey ldap/auth.langhua@AUTH.LANGHUA"
1.2 check /var/kerberos/krb5kdc/kadm5.acl has ldap service configed
1.3 kadmin.local -q "ktadd -k /etc/krb5.keytab ldap/auth.langhua"
2. Add a test user
2.1 kadmin.local -q "addprinc test@AUTH.LANGHUA"
2.2 kadmin.local -q "ktadd -k /etc/krb5.keytab test"
2.3 Edit ~/.k5login
test@AUTH.LANGHUA
3. Verify GSSAPI works on your system
3.1 sasl2-sample-server -s ldap -m GSSAPI
trying 10, 1, 6
trying 2, 1, 6
bind: Address already in use
3.2 sasl2-sample-client -s ldap -m GSSAPI auth.langhua
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
|
GSSAPI is OK now.
If the server and client can communicate but cannot authen successfully, perhaps it's caused by the driver of your ethernet card.
Config OpenLDAP GSSAPI
1. ldapsearch -Y GSSAPI -b 'o=langhua,c=cn' '(ou=worldwide)'
SASL/GSSAPI authentication started
SASL username: test@AUTH.LANGHUA
SASL SSF: 56
SASL installing layers
# extended LDIF
#
# LDAPv3
# base <o=langhua,c=cn> with scope subtree
# filter: (ou=worldwide)
# requesting: ALL
#
# worldwide, langhua, cn
dn: ou=worldwide,o=langhua,c=cn
objectClass: organizationalUnit
objectClass: top
ou: worldwide
# martin.peschke, worldwide, langhua, cn
dn: uid=martin.peschke,ou=worldwide,o=langhua,c=cn
uid: martin.peschke
ou: worldwide
objectClass: organizationalPerson
objectClass: uidObject
objectClass: person
objectClass: top
sn: Martin Peschke
cn: Martin Peschke
# search result
search: 4
result: 0 Success
# numResponses: 3
# numEntries: 2
2. Edit /etc/slapd.conf
#
# 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
|
3. /etc/init.d/ldap restart
4. Use JXplorer, select GSSAPI to login auth.langhua:389
If successful login, the OpenLDAP configuration is complete.
Config CAS
1. Create /cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/BindLdapGssapiAuthenticationHandler.java
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
*/
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
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
*/
package
org.jasig.cas.adaptors.ldap;
import
org.jasig.cas.adaptors.ldap.util.AuthenticatedLdapGssapiContextSource;
import
org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import
org.inspektr.common.ioc.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
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
*/
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) {
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();
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\"."
);
}
}
/**
* 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
/*
* Copyright 2008 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
*/
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
*
*/
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;
}
}
|
5. build and deploy CAS 3.1
6. Create $CAS/WEB-INF/gssapi.conf
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
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<!DOCTYPE beans PUBLIC
"-//SPRING//DTD BEAN//EN"
"<a href="http://www.springframework.org/dtd/spring-beans.dtd" "="" style="outline: none; color: rgb(50, 108, 166); border-top-left-radius: 0px !important; border-top-right-radius: 0px !important; border-bottom-right-radius: 0px !important; border-bottom-left-radius: 0px !important; background-image: none !important; border: 0px !important; bottom: auto !important; float: none !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; width: auto !important; box-sizing: content-box !important; font-size: 1em !important; min-height: auto !important;">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>
|
8. restart tomcat
9. kadmin.local -q "cpw -pw 111111 test"
Make CAS use password to authorize test.
Visit https://auth.langhua:8443/cas/
In client, use IE6 or firefox to visit https://auth.langhua:8443/cas/
username: test or test@AUTH.LANGHUA
password: 111111
Here is a picture to show the login process.
Good Luck!
Shi Yusen/Beijing Langhua Ltd.
http://www.langhua.cn/