Single sign-on is a great technology. Requiring users to login to multiple applications is huge hassle, encourages password reuse and simple passwords. Security needs to focus on usability. If you can make a user's life better while increasing security, everybody wins.
In this how-to we will set up the open-source CAS SSO product with one-time passwords for sessions and mutual https authentication for host authentication. Obviously using two-factor authentication for the login increases security because the user must have the factors to get access, in this case, knowledge of the PIN and possession of the private key embedded in the token. Less obvious is the benefit of strong mutual authentication. WiKID does this by downloading a hash of the CAS web site's SSL certificate with the one-time password. Before presenting the OTP, the token goes to the CAS URL via the user's connection, grabs the SSL cert and hashes it. If the hashes match, the OTP is presented and the default browser is launched to the CAS URL. If they do not match, then there is a potential attack and the user gets an error stating that the URL has changed. MiTM attacks are much easier to perform today thanks to the ubiquity of WiFi.
Building CAS and the cas.war file
First, CAS is built with Maven, so you need to install it:
sudo apt-get install maven2
Next download the latest version of CAS from the site. The current release is 3.4.11.
wget http://downloads.jasig.org/cas/cas-server-3.4.11-release.tar.gz
untar cas-server-3.4.11-release.tar.gz
Edit pom.xml
cd cas-server-3.4.11/cas-server-webapp/
vim pom.xml
I added the following dependency to add support for radius:
<dependency> <groupId>${project.groupId}</groupId> <artifactId>cas-server-support-radius</artifactId> <version>${project.version}</version> </dependency>
Edit deployerContext.xml
vim src/main/webapp/WEB-INF/deployerConfigContext.xml
In this file, you want to comment out the SimpleTestUsernamePasswordAuthenticationHandler, which is for demo purposes and add in information on your radius configuration. These beans are listed under Authentication Handlers.
<property name="authenticationHandlerskid10.jpeg CAS_wikid09.jpegCAS_wikid09.jpeg"> <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" p:httpClient-ref="httpClient" /> <!-- | 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.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" /> --> <bean class="org.jasig.cas.adaptors.radius.authentication.handler.support.RadiusAuthenticationHandler"> <property name="servers"> <bean class="org.jasig.cas.adaptors.radius.JRadiusServerImpl"> <constructor-arg index="0" value="10.100.0.170" /> <constructor-arg index="1" value="secret" /> <constructor-arg index="2"> <bean class="net.jradius.client.auth.PAPAuthenticator" /> </constructor-arg> </bean> </property> </bean> </list> </property>
In this example, the CAS server is talking directly to the WiKID server. You might also have a radius server such as freeradius or NPS between CAS and WiKID doing authorization. If you have more than one of these, you can list the servers for redundancy:
<bean class="org.jasig.cas.adaptors.radius.authentication.handler.support.RadiusAuthenticationHandler"> <property name="servers"> <list> <bean class="org.jasig.cas.adaptors.radius.JRadiusServerImpl"> <constructor-arg index="0" value="10.100.0.170" /> <constructor-arg index="1" value="secret" /> <constructor-arg index="2"> <bean class="net.jradius.client.auth.PAPAuthenticator" /> </constructor-arg> </bean> <bean class="org.jasig.cas.adaptors.radius.JRadiusServerImpl"> <constructor-arg index="0" value="10.100.0.171" /> <constructor-arg index="1" value="secret" /> <constructor-arg index="2"> <bean class="net.jradius.client.auth.PAPAuthenticator" /> </constructor-arg> </bean> </list> </property> <property name="failoverOnException" value="true" /> </bean>
You can now build the war file using Maven:
mvn clean package
Note that the CAS documentation for radius has an error in it. If you get the following error:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.jasig.cas.adaptors.radius.JRadiusServerImpl#1dd7736' defined in ServletContext resource [/WEB-INF/deployerConfigContext.xml]: Cannot create inner bean 'net.sf.jradius.client.auth.PAPAuthenticator#1c958af' of type [net.sf.jradius.client.auth.PAPAuthenticator] while setting constructor argument; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [net.sf.jradius.client.auth.PAPAuthenticator] for bean with name 'net.sf.jradius.client.auth.PAPAuthenticator#1c958af' defined in ServletContext resource [/WEB-INF/deployerConfigContext.xml]; nested exception is java.lang.ClassNotFoundException: net.sf.jradius.client.auth.PAPAuthenticator
This is due to using the outdated line
<bean class="net.sf.jradius.client.auth.PAPAuthenticator" />
Rather than the correct:
<bean class="net.jradius.client.auth.PAPAuthenticator" />
Configurating Tomcat
Star by downloading the latest tomcat and untar it. At the time of this writing that was 7.0.22.
sudo tar -xzvf apache-tomcat-7.0.22.tar.gz
Create an SSL keystore:
keytool -genkey -alias tomcat -keyalg RSA
You will probably want to import a signed certificate for production, but this will do for testing.
Edit the $tomcathome/conf/server.xml file to create an SSL port.
sudo vim conf/server.xml
Create the listener:
<Connector port="443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" keystoreFile="/path/to/.keystore" keystorePass="keystorePassphrase" clientAuth="false" sslProtocol="TLS" />
Start tomcat:
sudo $tomcathome/bin/startup.sh
If all is well, you should have a listener on port 8443
sudo netstat -anp | grep 8443
tcp6 0 0 :::8443 :::* LISTEN 10105/java
Copying the war file into tomcat's webapps directory will deploy it:
cp /home/username/cas-server-3.4.11/cas-server-webapp/target/cas.war $tomcathome/webapps/
If you look in $tomcathome/logs/catalina.out, you should see this line: "INFO: Deploying web application archive cas.war".
Configuring WiKID
Adding a Radius Network Client
The CAS server will be a radius network client to the WiKID Strong Authentication Server. On the WiKIDAdmin web ui, got the the Network Clients tab and select "Create a new network client", then enter the information appropriate for your CAS server, selecting RADIUS as the protocol.
Click Add or Modify and enter the shared secret. Make sure that the shared secret is the same as in the deployerContext.xml file.
Now let's add mutual https authentication to the mix. Go to the domains tab on the WiKIDAdmin and edit or create the domain you intend to use. Under the Registered URL, the URL for your CAS server:
The WiKID server will go to that URL and store a hash of the SSL certificate. Please note radius does a lot of caching, so you need to restart WiKID. If WiKID is controlling the firewall, this will also open a port to the CAS server. From the command line of the WiKID server run:
wikidctl restart
User registration and logging In
Registering the token
Start your WiKID software token. Select Action, Create new domain. Enter the 12 digit domain identifier for your WiKID server. This is typically the zero-padded IP address.
You will be prompted to set a PIN.
You will get back a registration code from the server. This registration must be validated for the user to login.
Log in to the WiKIDAdmin and click on the Users tab and then Manually validate a user. You will see your registration code.
Click on the registration code and enter your username on the following page.
Now, head back to the token to the and select Get Passcode.
Enter your PIN.
You will get back an OTP from the WiKID Strong Authentication Server. Additionally, your default browser will be opened the CAS login page as specified under the Registered URL.
Now Login to the CAS SSO page with your username and WiKID one-time passcode.
If you run the PC software token in debug mode, you will see the token validating the SSL certificate for you:
Received 128 bytes from server. validatedURL() processing response ... validatedURL() returned url: https://cas.wikidsystems.com/cas/login validatedURL() hash_from_server: 14Bqov7lBEMn+DavECDMovCBTF0= validatedURL() hash_from_me: 14Bqov7lBEMn+DavECDMovCBTF0= validatedURL() validated_url: https://cas.wikidsystems.com/cas/login Validity check returning: https://cas.wikidsystems.com/cas/login
If there is a Man-in-the-Middle attack the user will get an error that the URL has changed. The debug output will show that the hashes do not match:
Received 128 bytes from server. validatedURL() processing response ... validatedURL() hash_from_server: 14Bqov7lBEMn+DavECDMovCBTF0= validatedURL() hash_from_me: /HAtxIVzVL6yo1OjTkPca74xd8s= Validity check returning: null
Conclusion
Single sign-in is a great tool but it creates a "keys to the kingdom" situation where compromising a single set of credentials can result in a much larger breach than without SSO. Additionally, organizations are using SSO for cloud-based services such as Google Apps.