totpcgi and freeRADIUS with VMware View

In version 5.1 VMware view added support for radius servers as a two factor authentication solution, Google Authenticator is an open source software based two factor authentication token developed by Google (http://en.wikipedia.org/wiki/Google_Authenticator). The following walkthrough details how to setup a linux based radius server with open source software that integrates VMware view, Google Authenticator and Active Directory.

Components

This walkthrough describes one way of configuring a radius environment suitable for use with VMware view that supports two factor authentication with Google Authenticator, it uses on several open source components to achieve this:

EDIT: 16th April 2013 - Konstantin Ryabitsev <mricon@kernel.org> (The author of totpcgi) pointed out to me that pam_url is not strictly necessary for this deployment guide, the totpcgi radius perl script doesn’t use pam_url.  I’ve left the pam_url configuration details in the walkthrough because they are important if you end up securing the linux server itself using totpcgi.
EDIT: 22 January 2014 – It’s been brought to my attention (see comments from Ethan on January 20th) that totpcgi 5.5.1 has been upgraded to make it play more nicely with Active Directory so the changes I suggest you make to totpcgi-handler.pl are no longer required, instead you just need to edit  totpcgi-handler.yaml appropriately for your installation.

Assumptions

This document assumes that you have already deployed VMware view; that you have a Microsoft Active Directory infrastructure and that you have deployed a Microsoft certificate Services root Certificate Authority (CA) in your active directory infrastructure. If you haven’t deployed a Microsoft root CA certain steps in this walkthrough that deal with SSL certificates may need to be adjusted to allow for an alternative certificate provider and CA.

Base System

This walkthrough is based on a linux centOS 6.3 minimal deployment, additional packages over and above the minimal deployment will be documented as the walkthrough progresses. I deployed this in a VM with a single processor, 2GB of RAM and a single 16GB hard disk with default centOS partitioning.


Getting Started

Before you begin here are some pre-requisite steps to prepare your system

Install VMware tools (If running under VMware)

Follow the instructions in the vSphere upgrade guideto install VMware tools in your linux VM.

Configure the EPEL Repository

Some of the packages necessary for this walkthrough are not included in the standard centOS package repositories, they are however available from the EPEL repository (http://fedoraproject.org/wiki/EPEL) – Extra Packages for Enterprise Linux (or EPEL) is a Fedora Special Interest Group that creates, maintains, and manages a high quality set of additional packages for Enterprise Linux, including, but not limited to, Red Hat Enterprise Linux (RHEL), CentOS and Scientific Linux (SL).

To configure your system for EPEL run the following command:

yum install http://www.mirrorservice.org/sites/dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm

Go ahead and install the package and allow it to install any necessary dependancies.

Loaded plugins: fastestmirror
Determining fastest mirrors
 * base: mirrors.coreix.net
 * extras: mirrors.coreix.net
 * updates: mirrors.coreix.net
Setting up Install Process
epel-release-6-8.noarch.rpm                              |  14 kB     00:00
Examining /var/tmp/yum-root-ntcvxa/epel-release-6-8.noarch.rpm: epel-release-6-8.noarch
Marking /var/tmp/yum-root-ntcvxa/epel-release-6-8.noarch.rpm to be installed
Resolving Dependencies
--> Running transaction check
---> Package epel-release.noarch 0:6-8 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package            Arch         Version   Repository                      Size
================================================================================
Installing:
 epel-release       noarch       6-8       /epel-release-6-8.noarch        22 k

Transaction Summary
================================================================================
Install       1 Package(s)

Total size: 22 k
Installed size: 22 k
Is this ok [y/N]:y
edit /etc/hosts

You should configure /etc/hosts so that the fully qualified domain name of the server is added to an appropriate entry (either localhost or a real IP address)
Example:

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4 totpcgi.mydomain.com
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

Installing totpcgi and pam_url

To install the necessary totpcgi, pam_url and ldap packages run the following command:

yum install totpcgi totpcgi-provisioning totpcgi-selinux pam_url python-ldap

Go ahead and install the packages and allow them to install any necessary dependancies.

Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
epel/metalink                                                                                             |  20 kB     00:00
 * base: mirrors.coreix.net
 * epel: mirrors.coreix.net
 * extras: mirrors.coreix.net
 * updates: mirrors.coreix.net
epel                                                                                                      | 4.2 kB     00:00
epel/primary_db                                                                                           | 5.0 MB     00:00
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package pam_url.x86_64 1:0.3.2-1.el6 will be installed
--> Processing Dependency: libconfig.so.8()(64bit) for package: 1:pam_url-0.3.2-1.el6.x86_64
---> Package totpcgi.noarch 0:0.5.4-1.el6 will be installed
--> Processing Dependency: python-totpcgi = 0.5.4-1.el6 for package: totpcgi-0.5.4-1.el6.noarch
--> Processing Dependency: mod_ssl for package: totpcgi-0.5.4-1.el6.noarch
--> Processing Dependency: httpd for package: totpcgi-0.5.4-1.el6.noarch
---> Package totpcgi-provisioning.noarch 0:0.5.4-1.el6 will be installed
--> Processing Dependency: python-qrcode for package: totpcgi-provisioning-0.5.4-1.el6.noarch
---> Package totpcgi-selinux.noarch 0:0.5.4-1.el6 will be installed
--> Running transaction check
---> Package httpd.x86_64 0:2.2.15-26.el6.centos will be installed
--> Processing Dependency: httpd-tools = 2.2.15-26.el6.centos for package: httpd-2.2.15-26.el6.centos.x86_64
--> Processing Dependency: apr-util-ldap for package: httpd-2.2.15-26.el6.centos.x86_64
--> Processing Dependency: /etc/mime.types for package: httpd-2.2.15-26.el6.centos.x86_64
--> Processing Dependency: libaprutil-1.so.0()(64bit) for package: httpd-2.2.15-26.el6.centos.x86_64
--> Processing Dependency: libapr-1.so.0()(64bit) for package: httpd-2.2.15-26.el6.centos.x86_64
---> Package libconfig.x86_64 0:1.3.2-1.1.el6 will be installed
---> Package mod_ssl.x86_64 1:2.2.15-26.el6.centos will be installed
---> Package python-qrcode.noarch 0:2.4.1-2.el6 will be installed
--> Processing Dependency: python-imaging for package: python-qrcode-2.4.1-2.el6.noarch
---> Package python-totpcgi.noarch 0:0.5.4-1.el6 will be installed
--> Processing Dependency: python-pyotp for package: python-totpcgi-0.5.4-1.el6.noarch
--> Processing Dependency: python-passlib for package: python-totpcgi-0.5.4-1.el6.noarch
--> Processing Dependency: python-crypto for package: python-totpcgi-0.5.4-1.el6.noarch
--> Processing Dependency: py-bcrypt for package: python-totpcgi-0.5.4-1.el6.noarch
--> Running transaction check
---> Package apr.x86_64 0:1.3.9-5.el6_2 will be installed
---> Package apr-util.x86_64 0:1.3.9-3.el6_0.1 will be installed
---> Package apr-util-ldap.x86_64 0:1.3.9-3.el6_0.1 will be installed
---> Package httpd-tools.x86_64 0:2.2.15-26.el6.centos will be installed
---> Package mailcap.noarch 0:2.1.31-2.el6 will be installed
---> Package py-bcrypt.x86_64 0:0.2-7.el6 will be installed
---> Package python-crypto.x86_64 0:2.0.1-22.el6 will be installed
---> Package python-imaging.x86_64 0:1.1.6-19.el6 will be installed
--> Processing Dependency: libjpeg.so.62()(64bit) for package: python-imaging-1.1.6-19.el6.x86_64
--> Processing Dependency: libfreetype.so.6()(64bit) for package: python-imaging-1.1.6-19.el6.x86_64
---> Package python-passlib.noarch 0:1.5.3-1.el6 will be installed
---> Package python-pyotp.noarch 0:1.3.1-1.el6 will be installed
--> Running transaction check
---> Package freetype.x86_64 0:2.3.11-14.el6_3.1 will be installed
---> Package libjpeg-turbo.x86_64 0:1.2.1-1.el6 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

=================================================================================================================================
 Package                             Arch                  Version                                  Repository              Size
=================================================================================================================================
Installing:
 pam_url                             x86_64                1:0.3.2-1.el6                            epel                    20 k
 totpcgi                             noarch                0.5.4-1.el6                              epel                    25 k
 totpcgi-provisioning                noarch                0.5.4-1.el6                              epel                    11 k
 totpcgi-selinux                     noarch                0.5.4-1.el6                              epel                    23 k
Installing for dependencies:
 apr                                 x86_64                1.3.9-5.el6_2                            base                   123 k
 apr-util                            x86_64                1.3.9-3.el6_0.1                          base                    87 k
 apr-util-ldap                       x86_64                1.3.9-3.el6_0.1                          base                    15 k
 freetype                            x86_64                2.3.11-14.el6_3.1                        updates                359 k
 httpd                               x86_64                2.2.15-26.el6.centos                     base                   821 k
 httpd-tools                         x86_64                2.2.15-26.el6.centos                     base                    72 k
 libconfig                           x86_64                1.3.2-1.1.el6                            base                    51 k
 libjpeg-turbo                       x86_64                1.2.1-1.el6                              base                   174 k
 mailcap                             noarch                2.1.31-2.el6                             base                    27 k
 mod_ssl                             x86_64                1:2.2.15-26.el6.centos                   base                    90 k
 py-bcrypt                           x86_64                0.2-7.el6                                epel                    19 k
 python-crypto                       x86_64                2.0.1-22.el6                             base                   159 k
 python-imaging                      x86_64                1.1.6-19.el6                             base                   388 k
 python-passlib                      noarch                1.5.3-1.el6                              epel                   290 k
 python-pyotp                        noarch                1.3.1-1.el6                              epel                    11 k
 python-qrcode                       noarch                2.4.1-2.el6                              epel                    31 k
 python-totpcgi                      noarch                0.5.4-1.el6                              epel                    41 k

Transaction Summary
=================================================================================================================================
Install      21 Package(s)

Total download size: 2.8 M
Installed size: 9.2 M
Is this ok [y/N]:y

Configure totpcgi

To get totpcgi to work correctly with Active Directory you will need to make some adjustments to the /etc/totpcgi/totpcgi.conf file and the /etc/totpcgi/provisioning.conf files:

/etc/totpcgi/totpcgi.conf

In the [main] section you should change require_pincode to True, in the [pincode_backend] section comment out the engine=file and pincode_file lines in the same section uncomment and configure the LDAP backend configuration with the ldap_url of one of your Active Directory domain controllers, ldap_dn set to $username and ldap_cacert set to /etc/pki/tls/certs/totpcgi-ca.crt
Example:

[main]
require_pincode = True
success_string = OK

[secret_backend]
engine = file
secrets_dir = /etc/totpcgi/totp

; For PostgreSQL backend:
;engine = pgsql
;pg_connect_string = user= password= host= dbname=

[pincode_backend]
;engine = file
;pincode_file = /etc/totpcgi/pincodes

; For PostgreSQL backend:
;engine = pgsql
;pg_connect_string = user= password= host= dbname=

; For LDAP backend (simple bind auth):
engine = ldap
ldap_url    = ldaps://mydc.mydomain.com:636/
ldap_dn     = $username
ldap_cacert = /etc/pki/tls/certs/totpcgi-ca.crt

[state_backend]
engine = file
state_dir = /var/lib/totpcgi

; For PostgreSQL backend:
;engine = pgsql
;pg_connect_string = user= password= host= dbname=
/etc/totpcgi/provisioning.conf

In the [secret] section change totp_user_mask to be $username. In the [pincode_backend] section repeat the same changes as you made in /etc/totpcgi/totpcgi.conf to comment out the file engine and configure the LDAP engine.
Example:

[secret]
# Whether to encrypt the secret when we generate it. Encrypting the secret
# with the user's pincode means that even if the .totp file is leaked, an
# attacker will not be able to get the secret without knowing the user's
# pincode. The downside is that if a user forgets their pincode, both the
# pincode and the secret will need to be fully re-provisioned.
# Setting to "True" will also turn off scratch-token support.
encrypt_secret = False

# You can allow for some clock drift between the client and server by setting
# the permitted window size. Window size is calculated in 10-second intervals,
# so a window size of 6 allows clock drift of 60 seconds in either direction.
window_size = 3

# First value is the number of times. Second value is the number of seconds.
# So, "3, 30" means "3 falures within 30 seconds"
rate_limit = 3, 30

# How many scratch tokens to generate. Note, that this setting is ignored
# if encrypt_secret is set to True.
scratch_tokens_n = 5

# This identifies the token in the Google Authenticator application. It comes
# very handy when users have more than one token, so set this to something
# descriptive of your environment.
totp_user_mask = $username

# Used by provisioning.cgi
# Where the provisioning CGI is located, with regards to the web root.
action_url = /index.cgi

# Used by provisioning.cgi
# Where provisioning.css and provisioning-print.css are located with regards
# to the web root.
css_root = /

# Used by provisioning.cgi
# Where to find the templates files.
templates_dir = /etc/totpcgi/templates

# Used by provisioning.cgi
# Whether to rely on HTTP auth to handle authentication.
# As we don't get the password, only the username, turning this on
# will automatically set encrypt_secret to false.
#
# Be careful turning this on.
trust_http_auth = False

[pincode]
# Which hashing mechanism to use. Valid entries: md5, bcrypt, sha256, sha512
usehash = sha256

# Whether to compile the DBM database (only meaningful with the file backend)
makedb = True

# The backends are pretty much the same as in totpcgi.conf, except if you
# are using the postgresql secret backend, you need to connect as a user
# that is allowed to modify user records (e.g. totpcgi_admin).
[secret_backend]
engine = file

; For PostgreSQL backend:
;engine = pgsql
;pg_connect_string = user= password= host= dbname=

[pincode_backend]
;engine = file
;pincode_file = /etc/totpcgi/pincodes

; For PostgreSQL backend:
;engine = pgsql
;pg_connect_string = user= password= host= dbname=

; For LDAP backend (simple bind auth):
engine = ldap
ldap_url    = ldaps://myca.mydomain.com:636/
ldap_dn     = $username
ldap_cacert = /etc/pki/tls/certs/totpcgi-ca.crt

[state_backend]
engine = file
state_dir = /var/lib/totpcgi

; For PostgreSQL backend:
;engine = pgsql
;pg_connect_string = user= password= host= dbname=
Customise totpcgi

You may wish to customise the html files in /etc/totpcgi/templates to suit your own environment. (The default files refer to “Example Company Ltd” and support@example.com – you should change these before you consider putting this solution into production.

Configure Apache

totpcgi uses Apache as a web server, when the totpcgi packages are installed sample Apache configurations are installed in the Apache configuration folders but these need to be edited to suit.

/etc/httpd/conf.d/ssl.conf

Edit the standard ssl.conf file to completely comment out the entire <VirtualHost _default_:443> section (lines 74 to the end)

/etc/httpd/conf.d/totpcgi.conf

Adjust the VirtualHost configuration to set this to be the default for port 8443, edit the ServerAdmin option and Server Name option to suit your environment. Configure the SSL settings as follows:

  • SSLCertificateFile /etc/pki/tls/certs/totpcgi-server.crt
  • SSLCertificateKeyFile /etc/pki/tls/private/totpcgi-server.key
  • SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt

Example:

Listen 8443
<VirtualHost _default_:8443>
        ServerAdmin admin@mydomain.com
        DocumentRoot /var/www/totpcgi
        ServerName totpcgi.mydomain.com
        ErrorLog /var/log/httpd/totpcgi-error.log
        CustomLog /var/log/httpd/totpcgi-access.log common
        SuexecUserGroup totpcgi totpcgi

        # Use this for totp.cgi
        AddHandler cgi-script .cgi
        DirectoryIndex index.cgi

        # Or use this for totp.fcgi:
        #AddHandler fcgid-script .fcgi
        #DirectoryIndex index.fcgi

        SSLEngine on
        SSLCertificateFile /etc/pki/tls/certs/totpcgi-server.crt
        SSLCertificateKeyFile /etc/pki/tls/private/totpcgi-server.key
        SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt

        SSLVerifyClient require
        SSLVerifyDepth 10

        CustomLog /var/log/httpd/totpcgi-ssl-request-log \
                "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

        <Directory "/var/www/totpcgi">
                Options ExecCGI
        </Directory>
</VirtualHost>
/etc/httpd/conf.d/totpcgi-provisioning.conf

Adjust the VirtualHost configuration to set this to be the default for port 443, edit the ServerAdmin option and Server Name option to suit your environment. Configure the SSL settings as follows:

  • SSLCertificateFile /etc/pki/tls/certs/totpcgi-server.crt
  • SSLCertificateKeyFile /etc/pki/tls/private/totpcgi-server.key
  • SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt

Example:

<VirtualHost _default_:443>
	ServerAdmin admin@mydomain.com
	DocumentRoot /var/www/totpcgi-provisioning
	ServerName totpcgi.mydomain.com
	ErrorLog /var/log/httpd/totpcgi-provisioning-error.log
	CustomLog /var/log/httpd/totpcgi-provisioning-access.log common
	SuexecUserGroup totpcgiprov totpcgiprov
	AddHandler cgi-script .cgi
	DirectoryIndex index.cgi

	SSLEngine on
	SSLCertificateFile /etc/pki/tls/certs/totpcgi-server.crt
	SSLCertificateKeyFile /etc/pki/tls/private/totpcgi-server.key
	SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt

	CustomLog /var/log/httpd/totpcgi-provisioning-ssl-request-log \
	 "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

	<Directory "/var/www/totpcgi-provisioning">
		Options ExecCGI
	</Directory>
</VirtualHost>

Configure SSL certificates

Before you go ahead and configure the necessary SSL certificates it is probably a good idea to install the openssh client tools, this will enable you to securely copy certificates to/from your linux system with scp.

yum install openssh-clients

Go ahea and allow the package to install and install any dependancies

Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirrors.coreix.net
 * epel: ftp.heanet.ie
 * extras: mirrors.coreix.net
 * updates: mirrors.coreix.net
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package openssh-clients.x86_64 0:5.3p1-84.1.el6 will be installed
--> Processing Dependency: libedit.so.0()(64bit) for package: openssh-clients-5.3p1-84.1.el6.x86_64
--> Running transaction check
---> Package libedit.x86_64 0:2.11-4.20080712cvs.1.el6 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

===================================================================================================================================================================================
 Package                                      Arch                                Version                                                  Repository                         Size
===================================================================================================================================================================================
Installing:
 openssh-clients                              x86_64                              5.3p1-84.1.el6                                           base                              355 k
Installing for dependencies:
 libedit                                      x86_64                              2.11-4.20080712cvs.1.el6                                 base                               74 k

Transaction Summary
===================================================================================================================================================================================
Install       2 Package(s)

Total download size: 429 k
Installed size: 1.2 M
Is this ok [y/N]: y
Download CA certificate

Once you have scp installed you need to download a copy of your Active Directory root CA certificate, this is usually available from a url in you environment that looks like this:
http://myca.mydomain.com/certsrv/Default.asp

Note:In some environments this url will need to specify the language that certificate services has been deployed under http://myca.mydomain.com/certsrv/en-us/Default.asp You should browse to your certificate services website – you’ll see a link for “Download a CA certificate, certification chain or CRL Screen Shot 2013 04 09 at 16 39 06
Once you select that link, change the encoding method to Base 64 and click the Download CA certificate option
Screen Shot 2013 04 08 at 17 45 20

You should save the resulting file on your own workstation; use SCP or similar to transfer the file to your linux server and save the file in /etc/pki/tls/certs/totpcgi-ca.crt(You will need this file later when configuring radius and pam_url.)

Add downloaded CA certificate to SSL root bundle
openssl x509 -in /etc/pki/tls/certs/totpcgi-ca.crt -noout -text >> /etc/pki/tls/certs/ca-bundle.crt
Generate private keys

You will need to generate private keys for totpcgi as a client and a server

openssl genrsa 2048 > /etc/pki/tls/private/totpcgi-server.key
openssl genrsa 2048 > /etc/pki/tls/private/totpcgi.key
Generate certificate signing requests

You will need to generate certificate requests for totpcgi as a client and a server

openssl req -out /etc/pki/tls/private/totpcgi-server.req -key /etc/pki/tls/private/totpcgi-server.key -new
openssl req -out /etc/pki/tls/private/totpcgi.req -key /etc/pki/tls/private/totpcgi.key -new

In both instances you will be prompted to supply standard information for your SSL certificate – it is critical that the common name matches the the ServerName entries you configured in the Apache configuration section (e.g. totpcgi.mydomain.com)

openssl req -out /etc/pki/tls/private/totpcgi.req -key /etc/pki/tls/private/totpcgi.key -new
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:IE
State or Province Name (full name) []:Dublin
Locality Name (eg, city) [Default City]:Dublin
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:IT
Common Name (eg, your name or your server's hostname) []:totpcgi.mydomain.com
Email Address []:admin@mydomain.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Sign your certificates and download the resulting signed certificate files

Once again you’ll need to access your Active Directory certificate services website, this time choose the option to Request a certificate Screen Shot 2013 04 09 at 16 39 06
Chose the option to submit an advanced certificate request.

Screen Shot 2013 04 09 at 17 12 25
Make sure that the Certificate Template is set to Web Server and paste the text contents of /etc/pki/tls/private/totp-server.req into the Saved Request text box

Screen Shot 2013 04 09 at 17 15 13
Press Submit and chose the option to download the certificate as a Base 64 encoded file

Screen Shot 2013 04 09 at 17 16 31
Save the resulting file on your workstation and once again use SCP or similar to transfer the file to your linux server saving the file as /etc/pki/tls/certs/totpcgi-server.crt

Repeat the process for the client certificate

Screen Shot 2013 04 09 at 16 39 06
Chose the option to submit an advanced certificate request.

Screen Shot 2013 04 09 at 17 12 25
Make sure that the Certificate Template is set to User and paste the text contents of /etc/pki/tls/private/totpcgi.req into the Saved Request text box

Screen Shot 2013 04 09 at 17 22 43
Press Submit and chose the option to download the certificate as a Base 64 encoded file

Screen Shot 2013 04 09 at 17 16 31
Save the resulting file on your workstation and once again use SCP or similar to transfer the file to your linux server saving the file as /etc/pki/tls/certs/totpcgi.crt

Configure iptables firewall

The firewall that ships with centOS 6.3 is very restricted – it is necessary to open some ports in the firewall in order to make all of this work; you should edit /etc/sysconfig/iptables and add entries to allow traffic on TCP ports 443 and 8443 as well as UDP port 1812.
Example:

# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 8443 -j ACCEPT
-A INPUT -m state --state NEW -m udp -p udp --dport 1812 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT

Restart iptables and http server

service iptables restart
service httpd restart

At this point totpcgi should be all configured and ready to start serving up secret provisioning and authentication requests.


Provision a Google Authenticator secret

From your workstation open a web browser to https://totpcgi.mydomain.com/ – you should be prompted to login to obtain a Google Authenticator secret
Screen Shot 2013 04 09 at 17 41 34

Login using a username@mydomain.com style of Username and your active directory password for pin code.

Once logged in you should see the provisioning page for your Google Authenticator secret.
Screen Shot 2013 04 09 at 17 51 55

Go ahead and use your Google Authenticator app on your phone to setup this token.


configure pam_url

You will need to edit /etc/pam_url.conf to use your installation of totpcgi, the key settings in this file will be the url and the SSL configurations
Example:

# pam_url configuration file

pam_url:
{
    settings:
    {
        url         = "https://totpcgi.mydomain.com:8443/"; # URI to fetch
        returncode  = "OK";                        # The remote script/cgi should return a 200 http code and this string as its only results
        userfield   = "user";                      # userfield name to send
        passwdfield = "token";                     # passwdfield name to send
        extradata   = "&do=login";                 # extra data to send
        prompt      = "Pin+Token? ";                   # password prompt
    };

    ssl:
    {
        verify_peer = true;                               # Verify peer?
        verify_host = true;                               # Make sure peer CN matches?
        client_cert = "/etc/pki/tls/certs/totpcgi.crt";   # Client-side certificate
        client_key  = "/etc/pki/tls/private/totpcgi.key"; # Client-side key
        ca_cert     = "/etc/pki/tls/certs/ca-bundle.crt"; # ca cert - defaults to ca-bundle.crt
    };
};

# END

Install freeRADIUS

yum install freeradius freeradius-utils freeradius-perl

Go ahead and install the packages and any dependancies

yum install freeradius freeradius-utils freeradius-perl
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirror01.th.ifl.net
 * epel: ftp.heanet.ie
 * extras: www.mirrorservice.org
 * updates: mirrors.coreix.net
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package freeradius.x86_64 0:2.1.12-4.el6_3 will be installed
--> Processing Dependency: libltdl.so.7()(64bit) for package: freeradius-2.1.12-4.el6_3.x86_64
---> Package freeradius-perl.x86_64 0:2.1.12-4.el6_3 will be installed
---> Package freeradius-utils.x86_64 0:2.1.12-4.el6_3 will be installed
--> Processing Dependency: libpcap >= 0.9.4 for package: freeradius-utils-2.1.12-4.el6_3.x86_64
--> Processing Dependency: perl(DBI) for package: freeradius-utils-2.1.12-4.el6_3.x86_64
--> Processing Dependency: libpcap.so.1()(64bit) for package: freeradius-utils-2.1.12-4.el6_3.x86_64
--> Running transaction check
---> Package libpcap.x86_64 14:1.0.0-6.20091201git117cb5.el6 will be installed
---> Package libtool-ltdl.x86_64 0:2.2.6-15.5.el6 will be installed
---> Package perl-DBI.x86_64 0:1.609-4.el6 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

=====================================================================================================================================
 Package                          Arch                   Version                                          Repository            Size
=====================================================================================================================================
Installing:
 freeradius                       x86_64                 2.1.12-4.el6_3                                   base                 1.4 M
 freeradius-perl                  x86_64                 2.1.12-4.el6_3                                   base                  52 k
 freeradius-utils                 x86_64                 2.1.12-4.el6_3                                   base                 120 k
Installing for dependencies:
 libpcap                          x86_64                 14:1.0.0-6.20091201git117cb5.el6                 base                 126 k
 libtool-ltdl                     x86_64                 2.2.6-15.5.el6                                   base                  44 k
 perl-DBI                         x86_64                 1.609-4.el6                                      base                 705 k

Transaction Summary
=====================================================================================================================================
Install       6 Package(s)

Total download size: 2.4 M
Installed size: 8.0 M
Is this ok [y/N]:

Install required perl packages

FreeRADIUS needs to make use of a number of perl libraries, this command will install the necessary supporting packages

yum install perl-Net-LibIDN perl-Net-SSLeay perl-IO-Socket-SSL perl-XML-NamespaceSupport perl-Compress-Raw-Zlib perl-URI perl-IO-Compress-Base perl-IO-Compress-Zlib perl-Compress-Zlib perl-XML-SAX perl-XML-LibXML perl-XML-Filter-BufferText perl-Digest-SHA1 perl-Digest-HMAC perl-Convert-ASN1 perl-HTML-Tagset perl-HTML-Parser perl-libwww-perl perl-GSSAPI perl-Authen-SASL perl-Text-Iconv perl-XML-SAX-Writer perl-LDAP perl-YAML-Syck perl-String-Escape perl-DBI

Go ahead and install the packages and any dependancies

Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirror01.th.ifl.net
 * epel: ftp.heanet.ie
 * extras: www.mirrorservice.org
 * updates: mirrors.coreix.net
Setting up Install Process
Package perl-DBI-1.609-4.el6.x86_64 already installed and latest version
Resolving Dependencies
--> Running transaction check
---> Package perl-Authen-SASL.noarch 0:2.13-2.el6 will be installed
---> Package perl-Compress-Raw-Zlib.x86_64 1:2.020-130.el6_4 will be installed
--> Processing Dependency: libz.so.1(ZLIB_1.2.2.3)(64bit) for package: 1:perl-Compress-Raw-Zlib-2.020-130.el6_4.x86_64
--> Processing Dependency: libz.so.1(ZLIB_1.2.2)(64bit) for package: 1:perl-Compress-Raw-Zlib-2.020-130.el6_4.x86_64
--> Processing Dependency: libz.so.1(ZLIB_1.2.0.8)(64bit) for package: 1:perl-Compress-Raw-Zlib-2.020-130.el6_4.x86_64
---> Package perl-Compress-Zlib.x86_64 0:2.020-130.el6_4 will be installed
---> Package perl-Convert-ASN1.noarch 0:0.22-1.el6 will be installed
---> Package perl-Digest-HMAC.noarch 0:1.01-22.el6 will be installed
---> Package perl-Digest-SHA1.x86_64 0:2.12-2.el6 will be installed
---> Package perl-GSSAPI.x86_64 0:0.26-6.el6 will be installed
---> Package perl-HTML-Parser.x86_64 0:3.64-2.el6 will be installed
---> Package perl-HTML-Tagset.noarch 0:3.20-4.el6 will be installed
---> Package perl-IO-Compress-Base.x86_64 0:2.020-130.el6_4 will be installed
---> Package perl-IO-Compress-Zlib.x86_64 0:2.020-130.el6_4 will be installed
---> Package perl-IO-Socket-SSL.noarch 0:1.31-2.el6 will be installed
---> Package perl-LDAP.noarch 1:0.40-1.el6 will be installed
---> Package perl-Net-LibIDN.x86_64 0:0.12-3.el6 will be installed
---> Package perl-Net-SSLeay.x86_64 0:1.35-9.el6 will be installed
---> Package perl-String-Escape.noarch 0:2010.002-3.el6 will be installed
---> Package perl-Text-Iconv.x86_64 0:1.7-6.el6 will be installed
---> Package perl-URI.noarch 0:1.40-2.el6 will be installed
---> Package perl-XML-Filter-BufferText.noarch 0:1.01-8.el6 will be installed
---> Package perl-XML-LibXML.x86_64 1:1.70-5.el6 will be installed
---> Package perl-XML-NamespaceSupport.noarch 0:1.10-3.el6 will be installed
---> Package perl-XML-SAX.noarch 0:0.96-7.el6 will be installed
---> Package perl-XML-SAX-Writer.noarch 0:0.50-8.el6 will be installed
---> Package perl-YAML-Syck.x86_64 0:1.07-4.el6 will be installed
---> Package perl-libwww-perl.noarch 0:5.833-2.el6 will be installed
--> Running transaction check
---> Package zlib.x86_64 0:1.2.3-27.el6 will be updated
---> Package zlib.x86_64 0:1.2.3-29.el6 will be an update
--> Finished Dependency Resolution

Dependencies Resolved

=====================================================================================================================================
 Package                                    Arch                   Version                             Repository               Size
=====================================================================================================================================
Installing:
 perl-Authen-SASL                           noarch                 2.13-2.el6                          base                     51 k
 perl-Compress-Raw-Zlib                     x86_64                 1:2.020-130.el6_4                   updates                  68 k
 perl-Compress-Zlib                         x86_64                 2.020-130.el6_4                     updates                  44 k
 perl-Convert-ASN1                          noarch                 0.22-1.el6                          base                     43 k
 perl-Digest-HMAC                           noarch                 1.01-22.el6                         base                     22 k
 perl-Digest-SHA1                           x86_64                 2.12-2.el6                          base                     49 k
 perl-GSSAPI                                x86_64                 0.26-6.el6                          base                     64 k
 perl-HTML-Parser                           x86_64                 3.64-2.el6                          base                    109 k
 perl-HTML-Tagset                           noarch                 3.20-4.el6                          base                     17 k
 perl-IO-Compress-Base                      x86_64                 2.020-130.el6_4                     updates                  68 k
 perl-IO-Compress-Zlib                      x86_64                 2.020-130.el6_4                     updates                 134 k
 perl-IO-Socket-SSL                         noarch                 1.31-2.el6                          base                     69 k
 perl-LDAP                                  noarch                 1:0.40-1.el6                        base                    354 k
 perl-Net-LibIDN                            x86_64                 0.12-3.el6                          base                     35 k
 perl-Net-SSLeay                            x86_64                 1.35-9.el6                          base                    173 k
 perl-String-Escape                         noarch                 2010.002-3.el6                      epel                     21 k
 perl-Text-Iconv                            x86_64                 1.7-6.el6                           base                     22 k
 perl-URI                                   noarch                 1.40-2.el6                          base                    117 k
 perl-XML-Filter-BufferText                 noarch                 1.01-8.el6                          base                    9.6 k
 perl-XML-LibXML                            x86_64                 1:1.70-5.el6                        base                    364 k
 perl-XML-NamespaceSupport                  noarch                 1.10-3.el6                          base                     17 k
 perl-XML-SAX                               noarch                 0.96-7.el6                          base                     78 k
 perl-XML-SAX-Writer                        noarch                 0.50-8.el6                          base                     24 k
 perl-YAML-Syck                             x86_64                 1.07-4.el6                          base                     75 k
 perl-libwww-perl                           noarch                 5.833-2.el6                         base                    387 k
Updating for dependencies:
 zlib                                       x86_64                 1.2.3-29.el6                        base                     73 k

Transaction Summary
=====================================================================================================================================
Install      25 Package(s)
Upgrade       1 Package(s)

Total download size: 2.4 M
Is this ok [y/N]: y

Configure freeRADIUS

Totpcgi comes with example freeRADIUS perl configuration files, we will be modifying those files to work with an Active Directory LDAP environment rather than the open source LDAP server expected by the example files. First we need to copy the example files into the freeRADIUS configuration folder.

cp /usr/share/doc/totpcgi-0.5.4/contrib/freeradius/totpcgi-handler.* /etc/raddb/
Edit /etc/raddb/totpcgi-handler.yaml

First we need to modify /etc/raddb/totpcgi-handler.yaml – this file needs to be modified to suit your own Active Directory environment but the following should give you an example of what’s required. Note that the CA and SSL settings at the end of the file should be exactly the same as the example I’ve provided – this ail allow freeRADIUS to work with the certificates we created earlier.

Details:

In this example I’ve configured an ldap query user called ldapuser@mydomain.com (a low permission account with access to query ldap but little else) – I’ve given freeRADIUS both the username and the password for this account.

I’ve specified that when authenticating users freeRADIUS should look for them in the ‘Employees’ OU within the root of my Active Directory domain.

For each user that successfully authenticates with username/password and Google Authenticator code freeRADIUS will apply an additional check and confirm that they are members of the Active Directory group called “VMware View Users” and freeRADIUS will look for that group in the ‘Groups’ OU within the root of my Active Directory domain.

# LDAP Binding information
bindDN          : 'ldapuser@mydomain.com'
bindPassword    : 'password for ldapuser@mydomain.com'
userSearchBase  : 'ou=Employees,dc=mydomain,dc=com'
groupSearchBase : 'OU=Groups,DC=mydomain,DC=com'
groupAttribute  : 'member'
groupAttributeIsDN : true
searchScope     : 'sub'

# USERNAME is replaced with the uid that is passed in
search     : '(&(userPrincipalName=USERNAME)(objectClass=user))'

# groupSearch may be a singleton or array
groupSearch :
 - '(&(CN=VMware View Users)(objectClass=group))'

# Therefore the user only needs to be a member of VMware View Users

ldaphosts  :
 - 'ldaps://dc1.mydomain.com'
 - 'ldaps://dc2.mydomain.com'

# URL for checking that the password+token is good
totpurl    : 'https://totpcgi.mydomain.com:8443/'

# CA Info for validiation into topt-cgi's https instance
ca_file    : '/etc/pki/tls/certs/ca-bundle.crt'
key_file   : '/etc/pki/tls/private/totpcgi.key'
cert_file  : '/etc/pki/tls/certs/totpcgi.crt'
Edit /etc/raddb/totpcgi-handler.pl

This file needs to be adjusted to replace the ‘uid’ on lines 56 and 172 with ‘userPrincipalName’ like this
Line 56

my @attrs = [
        'userPrincipalName',                    # to verify we're getting info on the correct user

Line 172

($mesg->entry(0)->get_value('userPrincipalName') == $RAD_REQUEST{'User-Name'}))
Edit /etc/raddb/sites-available/default

This file needs to be modified to allow freeRADIUS to support our add-on totpcgi perl handler.

First we will modify the authorize section:

Insert the following block between the files block and the sql block

#
#  The perl module will set Auth-Type to Perl if it has not
#  already been set
        perl

It should look like this

        #
        #  Read the 'users' file
        files

        #
        #  The perl module will set Auth-Type to Perl if it has not
        #  already been set
        perl

        #
        #  Look in an SQL database.  The schema of the database
        #  is meant to mirror the "users" file.
        #
        #  See "Authorization Queries" in sql.conf
#       sql

In the authenticate section first comment out the Auth-Type directives for PAP, CHAP and MS-CHAP

authenticate {
        #
        #  PAP authentication, when a back-end database listed
        #  in the 'authorize' section supplies a password.  The
        #  password can be clear-text, or encrypted.
        #Auth-Type PAP {
        #       pap
        #}

        #
        #  Most people want CHAP authentication
        #  A back-end database listed in the 'authorize' section
        #  MUST supply a CLEAR TEXT password.  Encrypted passwords
        #  won't work.
        #Auth-Type CHAP {
        #       chap
        #}

        #
        #  MSCHAP authentication.
        #Auth-Type MS-CHAP {
        #       mschap
        #}
        #

Next immediately after the now commented Auth-Type MS-CHAP section and an entry for Auth-Type Perl

#
        #  MSCHAP authentication.
        #Auth-Type MS-CHAP {
        #       mschap
        #}
        #

        #  Perl authentication.
        Auth-Type Perl {
                perl
        }

In the accounting section add an entry for perl after the directive for exec and before the directive for attr.filter.accounting_response

		# For Exec-Program and Exec-Program-Wait
        exec

        # For perl
        perl

        #  Filter attributes from the accounting response.
        attr_filter.accounting_response
Edit /etc/raddb/mdoules/perl

Edit this file to replace the default module directive with the totpcgi-handler.pl we configured earlier

module = ${confdir}/totpcgi-handler.pl
Edit /etc/raddb/users

In the /etc/raddb/users file I’ve added a default entry for Perl authentication at line 156

#DEFAULT        Service-Type == Framed-User, Huntgroup-Name == "delft"
#               Framed-IP-Address = 192.168.2.32+,
#               Fall-Through = Yes

DEFAULT         Auth-Type = Perl
                Fall-Through = yes

#
# Sample defaults for all framed connections.
Edit /etc/raddb/clients.conf

In the clients.conf file I’ve configured my radius server to service all of the local LAN on which it is installed, you will need to setup your freeRADIUS environment to suit your own needs. My example configuration here simply uncomments and modifies the sample configuration as shipped with freeRADIUS starting at line 196.

client 192.168.1.0/24 {
        secret          = SharedSecret
        shortname       = LANNetwork
}

Configuring SELinux

totpcgi comes with an SELinux policy that almost works perfectly – there is one small addition needed to allow the radius server to open the https connection to totpcgi on port 8443. Run this command to enable that policy change

setsebool -P allow_ypbind 1

Start Apache and freeRADIUS

service httpd restart
service radiusd restart

Enable Apache and freeRADIUS to restart automatically after reboot

chkconfig httpd on
chkconfig radiusd on

Testing the configuration

You can use the readiest tool to check to make sure your configuration is working correctly.
radtest user@mydomain.com ADpasswordGoogleAuthenticatorPincode totpcgi.mydomain.com 1812 SharedSecret 0
Like

radtest user@mydomain.com Password123456 totpcgi.mydomain.com 1812 SharedSecret  0
Sending Access-Request of id 228 to 192.168.1.14 port 1812
	User-Name = "user@mydomain.com"
	User-Password = "Password123456"
	NAS-IP-Address = 192.168.1.14
	NAS-Port = 1812
	Message-Authenticator = 0x00000000000000000000000000000000
Sending Access-Request of id 228 to 192.168.180.14 port 1812
	User-Name = "user@mydomain.com"
	User-Password = "Password123456"
	NAS-IP-Address = 192.168.1.14
	NAS-Port = 1812
	Message-Authenticator = 0x00000000000000000000000000000000
rad_recv: Access-Accept packet from host 192.168.180.14 port 1812, id=228, length=20

If you get back an Access-Accept packet then everything is working correctly.


Configuring VMware View

Open a web browser and navigate to your VMware View environment administration site, login as an administrator

  • Expand the View Configuration group in the left hand menu
  • Select the Servers option
  • Select the Connection Servers tab in the right hand panel
  • Highlight a connection server and click the Edit button
  • Select the Authentication tab

Screen Shot 2013 04 09 at 23 32 28

  • Change the 2-factor authentication setting to be RADIUS
  • Tick the Enforce 2-factor and Windows user name matching option
  • Click the Manage Authenticators button

Screen Shot 2013 04 09 at 23 34 54

  • Add an authenticator configured with appropriate settings for your newly configured radius server

Screen Shot 2013 04 09 at 23 36 11

Click OK on the next two screens to return to the main View administrative site.

That’s it, from now on any of your users will need to authenticate with their Active Directory credentials and their Google Authenticator token.

(Note: The way this works with my example configuration is that the user must first supply their AD credentials concatenated with their Google token (e.g. MyPassword123456) on the first authentication prompt, they will then be prompted a second time for just their AD credentials. I’m still trying to figure out how to avoid having two prompts)

71 responses to “totpcgi and freeRADIUS with VMware View

  1. Pingback: Open source 2-factor authentication for VMware view with Google Authenticator | VCDX or Bust

  2. Sean

    Hi, we’ve spoke on reddit a few times about this guide and we keep hitting snags. We noticed in the totpcgi and totpcgi-provisioning configs they assume the LDAP server is also the CA server. With active directory this is not considered best practice so we have seperate. Do you know if there is a way to configure it to use both servers one for CA validation and one for LDAP validation?

    • Hi!

      I suspect there’s some confusion being generated here because of the names of some of the variables in the configs. As far as I’ve been able to figure out the CA certificates are required in the configs in order to make sure that the various CGI elements can “trust” the servers they are connecting to – LDAPS on port 636 etc.

      The CA certs don’t have to be generated by the LDAP server, they do have to be the CA certificate that the LDAP server will be signed by.

      So if you have a CA server called caserver.domain.com and an LDAP server called ldapserver.domain.com then the CGI script that makes the connection to the LDAP server will expect to see a certificate come back with an id of ldapserver.domain.com and that cert should be signed by caserver.domain.com – totpcgi will use the CA cert on file from the config file in order to check that the CA signature on ldapserver.domain.com is valid (based on the assumption that it will be a private CA and not something signed by one of the ordinary trusted root CAs)

      Does this help or have I confused things even more?

  3. Sean

    I wanted to add something I found just yesterday with this. If you are testing like I was with a DC that is a CA and a DC that shares a different CA you have to remove any authorized tokens that get generated in /etc/totpcgi/totp or you will get invalid creds every time you try to login to that part agian, you don’t get an error that a secret exists because it technically doesn’t for that DC but it does exist in the grand scheme which is the error you will get if you go back to your previous DC for testing.

    Thanks

  4. Sean

    Every time I get closer I get further away.

    I’m getting Denied access by rlm_perl. From what I can tell I’m either not querying ldap with my ldap query account or something in my totpcgi-handler.yaml is set wrong. But everything looks right.

    • If I recall correctly when I got this far I ended up sticking debug print statements into the perl script to track what was happening – crude but it worked!

    • PS – I have actually modified my configuration to make this slightly simpler – you may want to consider doing the same yourself. The big change is that I edited /etc/totpcgi/totpcgi.conf to set require_pincode to False.

      This means that the provisioning screen requires authentication against Active Directory but the token authentication process doesn’t – from a vmware view perspective you then get two stages of authentication – stage 1 is just the 6 digit token code and stage 2 is your AD password.

      You might find it easier to debug if you aren’t also requiring AD password in the token authentication stage.

  5. Pingback: Totpcgi and FreeRadius for 2 factor for view envrionment

  6. Sean

    I’m still getting the rlm_perl issue. I still feel like this is maybe a issue with our active directory and getting a check that the user is ok.

    • Sean

      So I’ve gotten further. I turned on debugging mode for radius and I”m getting

      radiusd: #### Opening IP addresses and Ports ####
      listen {
      type = “auth”
      ipaddr = radiustest.msunet2k.edu IP address [172.19.4.162]
      port = 0
      Failed binding to authentication address 172.19.4.162 port 1812: Address already in use
      /etc/raddb/radiusd.conf[240]: Error binding to port for 172.19.4.162 port 1812
      [root@radiustest totpcgi]#

      from /etc/raddb/radiusd.conf

  7. Sean

    Yeah the service daemon was on….

    Looking at only perl msgs I get nothing but
    rlm_perl: Added pair Reply-Message = Denied access by rlm_perl authenticate function
    rlm_perl: Added pair Auth-Type = Perl

    • Ok – you can edit the /etc/raddb/totpcgi-handler.pl script to dump out messages as it is working – these should show up in the radius logs if you have debugging turned on.

      You should be able to add lines like the following:

      &radiusd::radlog(0,”rlm_perl::Detaching. Reloading. Done.”);

      If you step through the authentication subroutine adding debug statements you should be able to identify where in that routine it is going wrong:

      You can also run wireshark and capture the network traffic back to the totpcgi apache module to show that this call is working as expected – my $response = $ua->post($$config{‘totpurl’}, \%form);

      Of particular importance is making sure that the authenticate process correctly gets past this bit, because if it doesn’t that means that it is the totpcgi function that’s not worked correctly (ie the pincode wasn’t authenticated) it needs to get this bit right before it even looks at LDAP membership etc. And it **seems** like that’s not happening for you.

      my $response = $ua->post($$config{‘totpurl’}, \%form);

      if ($response->is_success) {
      # we get back a hash as we also want the DN if the user passed
      my $rethash = &check_ldap_attributes;
      $retcode = $$rethash{‘retcode’};
      } else {
      $RAD_REPLY{‘Reply-Message’} = ‘Denied access by rlm_perl authenticate function’;
      }

      • Sean

        I’m working on this, but I have to say I’m not very familiar with what I’m doing here.

  8. Sean

    I’ve been looking at the log and I’m wondering why it runs the rlm_perl functions twice. The first time it runs I get a clear and then when it runs a 2nd time it fails
    (I’ve redacted some info)
    # Executing section authorize from file /etc/raddb/sites-enabled/default
    +- entering group authorize {…}
    ++[preprocess] returns ok
    ++[chap] returns noop
    ++[mschap] returns noop
    ++[digest] returns noop
    [suffix] Looking up realm for User-Name =
    [suffix] No such realm
    ++[suffix] returns noop
    [eap] No EAP-Message, not doing EAP
    ++[eap] returns noop
    [files] users: Matched entry DEFAULT at line 160
    ++[files] returns ok
    rlm_perl: Added pair User-Name =
    rlm_perl: Added pair User-Password = 710177
    rlm_perl: Added pair NAS-Port = 18120
    rlm_perl: Added pair NAS-IP-Address =
    rlm_perl: Added pair Message-Authenticator = 0xa58e5a1a0e448959537b04baa83e00fb
    rlm_perl: Added pair Auth-Type = Perl
    ++[perl] returns ok
    ++[expiration] returns noop
    ++[logintime] returns noop
    [pap] WARNING! No “known good” password found for the user. Authentication may fail because of this.
    ++[pap] returns noop
    Found Auth-Type = Perl
    # Executing group from file /etc/raddb/sites-enabled/default
    +- entering group Perl {…}
    rlm_perl: rlm_perl::Detaching. Reloading. Done.
    rlm_perl: Added pair User-Name =
    rlm_perl: Added pair User-Password = 710177
    rlm_perl: Added pair NAS-IP-Address =
    rlm_perl: Added pair NAS-Port = 18120
    rlm_perl: Added pair Message-Authenticator = 0xa58e5a1a0e448959537b04baa83e00fb
    rlm_perl: Added pair Reply-Message = Denied access by rlm_perl authenticate function
    rlm_perl: Added pair Auth-Type = Perl
    ++[perl] returns reject
    Failed to authenticate the user.
    Using Post-Auth-Type Reject
    # Executing group from file /etc/raddb/sites-enabled/default
    +- entering group REJECT {…}
    [attr_filter.access_reject] expand: %{User-Name} ->
    attr_filter: Matched entry DEFAULT at line 11
    ++[attr_filter.access_reject] returns updated
    Delaying reject of request 1 for 1 seconds
    Going to the next request
    Waking up in 0.9 seconds.
    Sending delayed reject for request 1
    Sending Access-Reject of id 230 to 172.19.4.162 port 41940
    Reply-Message = “Denied access by rlm_perl authenticate function”
    Waking up in 4.9 seconds.
    Cleaning up request 1 ID 230 with timestamp +44
    Ready to process requests.

    • You are getting the rlm_perl notice twice because there are two functions being called Authorize and Authenticate – this is normal.

      I get similar results to yourself if I don’t provide a fully qualified username user@domain.com – if I just use username it doesn’t work.

      I found that I had to provision the username using a fully qualified name and run the radtest with a fully qualified name or it wouldn’t work.

      Does this help at all?

      • Sean

        Yeah it’s all getting me somewhere. I’m also working with totpcgi team to help diagnose the issue and just for record keeping they’re having me do a curl
        curl –cacert /etc/pki/tls/certs/totpcgi-ca.crt \
        –cert /etc/pki/tls/certs/totpcgi.crt \
        –data ‘user=[username];token=[token];mode=PAM_SM_AUTH’ \
        https://totp.example.com:8443/

        with my info put in and i’m getting a
        curl: (58) Unable to load client key -8178
        error when I try. So I’m thinking maybe something is wrong with my keys and certs somewhere.

  9. Certs are always a nuisance when they go wrong…

  10. Sean

    Got mine sorted!
    I had searchbase instead of userSearchBase in my yaml file for some reason. It works now.

  11. Brian

    Ugh like Sean I’m having problems with my certs it seems. I get the same curl error he gets when running the curl command. I’m sooo close and yet so far away :(

    • Sean

      For my certs here’s exactly what I had to do to get it working exactly right. First make sure your CA is 2008 so you get the ‘web server’ choice available to you.

      Next from the radius/totpcgi server download the CA chain cert.
      When you generate your requests make sure to remember which one is the server request(totpcgi-server.csr) and which one is the user(totpcgi.csr) This was one that really messed me up. The first couple of times I kept selecting webserver for both as opposed to webserver for server and user for user (because I didn’t slow down and read a bit.

      Then I modified my curl statement to:
      curl –cacert /etc/pki/tls/certs/CACert.cer –cert /etc/pki/tls/certs/totpcgi.crt –key /etc/pki/tls/private/totpcgi.key –data ‘user=user@domain.com;token=506705;mode=PAM_SM_AUTH’ https://totpcgiserver.com:8443/

      After that I had to move the totpcgi.key file and totpcgi.pem file into /etc/raddb/certs and make their owner root/radiusd (Though we weren’t sure what this acomplished. Let me know if this helps.

      • Brian

        Thanks for writing back Sean, that got me a little further. I now get an SSL connect error. I think the main thing was the addition of the –key statement, not sure putting the certs under the /etc/raddb did anything but I put them there anyhow. I did verify my certificates were created one as user and one as web-server. The web server one works perfectly, so I’m sure that one is good. The user one is associated with a specific user account though, and I’m not sure that is correct. Not even sure it really matters to be honest, but what appears in the subject of your user certificate?

  12. Sean

    Yeah your user cert should not be for a specific user. You generate it on the server you generate the server cert just set it as a user form cert instead.

    • Brian

      Interestingly even when I submitted it that way it still issues it to the user that submitted the request. The subject should be the name of the server right?

  13. Brian

    If you copy the crt file to a windows machine and open it, you can view all the attributes of the certificate. In the subject name (this usually represents who the certificate should be used for) mine shows the user name I’m logged in with while I’m submitting the request. I’m assuming that’s okay though because the only way to get a machine certificate with the machine name that I know of is to use command line certreq util but the only machine that you can force is local computer. The problem with that is that command is a windows command so running it from Linux won’t work.

    Either way though, I dropped a line over to totpcgi guys and they think I can get away with using a puppet certificate instead of generating a separate user cert. I’ll throw up some more information on this once I get it running.

    • Sean

      Interesting. I never even looked to see what was being generated other than the data you have to enter, like location, business, common name, etc.

  14. It is possible to examine the contents of a certificate without having to copy it to a windows machine.

    The following example reads in an x509 based certificate and displays a text representation of the information contained in the cert:

    openssl x509 -in totpcgi.crt -noout -text

    Just as an FYI when I do this on the user certificate that I’m using in my configuration it shows up with the name of the account who requested the certificate just like you have.

    My understanding of the way this works is that the radius daemon makes a web request to the totpcgi web server to check if the provided passcode is correct, in order to make sure there’s no man in the middle stuff going on both the web server and the radius daemon have been configured to use SSL encryption in a mutual authentication mode. Apache needs to be able to trust that the certificate provided by the radius daemon is valid, the easiest way to do that appears to be to have the radius daemon use a certificate signed by the same CA that signed the server SSL certificate for Apache. That’s why all of these various certificates are copied into different folders and that the CA certificate is also copied into various files so that radius and Apache can do their mutual authentication dance.

    • Sean

      I did this just to check it out and mine has the user name that I requested the cert with too, but I’ve had many different users testing the system now that it works and it hasn’t caused a single problem. That user account though is a view admin, an admin but not a domain admin (if that helps with any questions.

      • Brian

        Okay I figured it out. I have a more complex CA structure here, and I had to make a root chain CA that included all of my CA’s (root, intermediate, and issuing) and use that for all of the incidents of a CA certificate listed in this walk through. I was just using the issuing CA and that was working for some things but apparently it wanted the whole chain.

  15. Brian

    One other thing, instead of using userPrincipalName you could use sAMAccountName right? Then you’d not have to type username@domain.com instead you could just type username.

    • I’ve not tried it but you might also run into issues with the View configuration – I like to tick the box to have 2 factor and windows user name matching enabled. This option means the user only types their name once when logging into view not twice – it **may** be that if you modify the totpcgi stuff to avoid the need to type a domain that you might also have issues with the windows authentication and view.

      Would be interesting to know, I can’t test it here as we run multiple domains anyway so there’s a need to be able to specify the domain as well as the user when logging in.

  16. Brian

    You would also have to modify the totpcgi.conf file to include @domain.com after the username but that should be it.

  17. Sean

    I was wondering how to do that, because I’d like to clean it up. what would you specifically modify in the totpcgi config? just the variable to search for DN requests?

    • Brian

      in the provisioning.conf you need to add @domain.com to $username in the totp_user_mask for sure. I’m thinking a similar change in the totpcgi.conf file as well.

  18. Brian

    Okay I have this mostly working as I intended. One problem however is that after I type in the pin code I get an Authorization failed pop up before I type in my Windows password. It all works but it’s just an annoyance. Any idea how to silence that? Obviously the radius stuff is working at this point though and I’m pretty happy with it.

    • Brian

      BTW I did follow your suggestion so that I only type the pincode in not the password+pincode. Hopefully there is a way to make it a little more graceful.

  19. Sean and I both found that running the radius daemon with debugging turned on helped a lot in figuring these issues out, I suggest you stop the service and run it from a command prompt with (I think) -x to watch what it is doing/saying…

  20. Brian

    It seems like when the authentication happens, the radius server doesn’t strip the last 6 digits when it passes authentication to the AD to authenticate the user. I get an error every time I try to log in. Did you ever figure out how to get that second prompt to go away?

    • Not entirely – but what I have now works well for me, there’s still two prompts but one is just for the pincode and one is just for the password which makes it much easier to use and to explain to people how to use it – I posted a comment earlier this month that read…

      PS – I have actually modified my configuration to make this slightly simpler – you may want to consider doing the same yourself. The big change is that I edited /etc/totpcgi/totpcgi.conf to set require_pincode to False.

      This means that the provisioning screen requires authentication against Active Directory but the token authentication process doesn’t – from a vmware view perspective you then get two stages of authentication – stage 1 is just the 6 digit token code and stage 2 is your AD password.

      You might find it easier to debug if you aren’t also requiring AD password in the token authentication stage.

  21. Brian

    I saw that, and tried it myself, but it looks like it tries to authenticate against the AD anyway at the first step and I get a password failed message before I get the place where they enter their AD credentials. I wouldn’t mind just the token auth in the first screen and then the AD credentials at the second one. I think that’s preferable actually, but I don’t want my users complaining to me about a bad password prompt.

  22. Can you post a redacted copy of your radius log – if you modified /etc/totpcgi/totpcgi.conf then it shouldn’t be going anywhere near AD during the first step.

  23. Brian

    Listening on authentication address * port 1812
    Listening on accounting address * port 1813
    Listening on command file /var/run/radiusd/radiusd.sock
    Listening on authentication address 127.0.0.1 port 18120 as server inner-tunnel
    Listening on proxy address * port 1814
    Ready to process requests.
    rad_recv: Access-Request packet from host 204.132.58.142 port 61254, id=195, length=77
    NAS-IP-Address = 204.132.58.142
    NAS-Port = 1
    User-Name = “”
    User-Password = “494571″
    Message-Authenticator = 0xd8229836960d019698970ecd195108aa
    # Executing section authorize from file /etc/raddb/sites-enabled/default
    +- entering group authorize {…}
    ++[preprocess] returns ok
    ++[chap] returns noop
    ++[mschap] returns noop
    ++[digest] returns noop
    [suffix] No ‘@’ in User-Name = “”, looking up realm NULL
    [suffix] No such realm “NULL”
    ++[suffix] returns noop
    [eap] No EAP-Message, not doing EAP
    ++[eap] returns noop
    [files] users: Matched entry DEFAULT at line 153
    ++[files] returns ok
    rlm_perl: Added pair User-Name =
    rlm_perl: Added pair User-Password = 494571
    rlm_perl: Added pair NAS-Port = 1
    rlm_perl: Added pair NAS-IP-Address = 204.132.58.142
    rlm_perl: Added pair Message-Authenticator = 0xd8229836960d019698970ecd195108aa
    rlm_perl: Added pair Auth-Type = Perl
    ++[perl] returns ok
    ++[expiration] returns noop
    ++[logintime] returns noop
    [pap] WARNING! No “known good” password found for the user. Authentication may fail because of this.
    ++[pap] returns noop
    Found Auth-Type = Perl
    # Executing group from file /etc/raddb/sites-enabled/default
    +- entering group Perl {…}
    rlm_perl: Added pair User-Name =
    rlm_perl: Added pair User-Password = 494571
    rlm_perl: Added pair NAS-IP-Address = 204.132.58.142
    rlm_perl: Added pair NAS-Port = 1
    rlm_perl: Added pair Message-Authenticator = 0xd8229836960d019698970ecd195108aa
    rlm_perl: Added pair Auth-Type = Perl
    ++[perl] returns ok
    # Executing section post-auth from file /etc/raddb/sites-enabled/default
    +- entering group post-auth {…}
    ++[exec] returns noop
    Sending Access-Accept of id 195 to 204.132.58.142 port 61254
    Finished request 0.
    Going to the next request
    Waking up in 4.9 seconds.
    Cleaning up request 0 ID 195 with timestamp +29
    Ready to process requests.

    • That looks like it worked correctly – it accepted the google authenticator code you provided (Sending Access-Accept of id 195 to 204.132.58.142 port 61254) – however I note that it also complains about not seeing an @ sign in the user-name. In my environment I fully specify a username including the domain, have you tried doing the same to see if that works?

      • Brian

        Yeah I see that too now that you mention it. Not sure how I missed that before. I’ll have to figure out how to make the NULL domain work the way I intend I suppose :)

  24. Brian

    I figured out what my problem was………….I’m an idiot :)

    I ticked the option to use the same username and password for RADIUS and Windows Authenticaiton. SOOOO I did get it working after all.

    Thanks so much for all your help Charles, and Sean. You guys definitely helped out a ton on this. The suffix issue was a red herring btw, it was noop which means it didn’t pass but it didn’t fail and it passed to the next section.

    I think I’m on my way to understanding how it all works together, it’s definitely a learning curve.

  25. Leo

    Hi Charles,

    Can you please help me with this error, all works accept for this error.
    Thanks in advanced!

    [files] users: Matched entry DEFAULT at line 157
    ++[files] returns ok
    rlm_perl: Added pair User-Name =
    rlm_perl: Added pair User-Password = 882599
    rlm_perl: Added pair NAS-Port = 1
    rlm_perl: Added pair NAS-IP-Address = 10.167.1.19
    rlm_perl: Added pair Message-Authenticator = 0x31f40bdf5c5de4479a771f8f44ba78ec
    rlm_perl: Added pair Auth-Type = Perl
    ++[perl] returns ok
    ++[expiration] returns noop
    ++[logintime] returns noop
    Found Auth-Type = Perl
    # Executing group from file /etc/raddb/sites-enabled/default
    +- entering group Perl {…}
    rlm_perl: Added pair User-Name =
    rlm_perl: Added pair User-Password = 882599
    rlm_perl: Added pair NAS-IP-Address = 10.167.1.19
    rlm_perl: Added pair NAS-Port = 1
    rlm_perl: Added pair Message-Authenticator = 0x31f40bdf5c5de4479a771f8f44ba78ec
    rlm_perl: Added pair Reply-Message = Denied access by rlm_perl: account does not pass LDAP user filter
    rlm_perl: Added pair Auth-Type = Perl
    ++[perl] returns reject
    Failed to authenticate the user.
    Using Post-Auth-Type Reject
    # Executing group from file /etc/raddb/sites-enabled/default
    +- entering group REJECT {…}
    [attr_filter.access_reject] expand: %{User-Name} -> leodil
    attr_filter: Matched entry DEFAULT at line 11
    ++[attr_filter.access_reject] returns updated
    Delaying reject of request 0 for 1 seconds
    Going to the next request
    Waking up in 0.7 seconds.
    Sending delayed reject for request 0
    Sending Access-Reject of id 7 to 10.167.1.19 port 52231
    Reply-Message = “Denied access by rlm_perl: account does not pass LDAP user filter”
    Waking up in 4.9 seconds.
    Cleaning up request 0 ID 7 with timestamp +7
    Ready to process requests.

    • Brian

      You’re LDAP user filter is blocking the user. That could be either your search base DN is off, or you’re being too restrictive with user / group permissions. I believe all of this is set in the /etc/raddb/totpcgi-handler.yaml.

    • Ethan

      Leo see my comment below, changes were made to the totpcgi-handler files. It’s the last one in my thread.

  26. Matt Thomas

    Charles I am running into the same issues as others:
    [] # Executing section authorize from file /etc/raddb/sites-enabled/default
    rlm_perl: Added pair User-Name = user@domain.example.net
    rlm_perl: Added pair User-Password = Password+Token
    rlm_perl: Added pair NAS-Port = 1812
    rlm_perl: Added pair NAS-IP-Address = 127.0.0.1
    rlm_perl: Added pair Message-Authenticator = 0x6e02a0ebea2c45c880a75bcc05f100ff
    rlm_perl: Added pair Auth-Type = Perl
    [pap] WARNING! No “known good” password found for the user. Authentication may fail because of this.
    # Executing group from file /etc/raddb/sites-enabled/default
    rlm_perl: Added pair User-Name = user@domain.example.net
    rlm_perl: Added pair User-Password = Password+Token
    rlm_perl: Added pair NAS-IP-Address = 127.0.0.1
    rlm_perl: Added pair NAS-Port = 1812
    rlm_perl: Added pair Message-Authenticator = 0x6e02a0ebea2c45c880a75bcc05f100ff
    rlm_perl: Added pair Reply-Message = Denied access by rlm_perl authenticate function
    rlm_perl: Added pair Auth-Type = Perl
    # Executing group from file /etc/raddb/sites-enabled/default

    Could this be caused by certs not being trusted? I want to get it working before I try to tackle any cert issues.

  27. Ethan

    I am getting stuck doing the radtest. I am getting the error message below. I know all my certs are correct and I have checked the user crt against the key. My user cert has the machines FQDN, not a username in it. Is this going to make a difference? I can’t figure out which SSL cert is having a problem. Both my Root and Issuing Sub CA crt’s are both in the ca-bundle.crt

    Reply-Message = “Denied access by rlm_perl authenticate function. ERROR: 500 Can’t connect to machine.fu.bar:8443 (SSL connect attempt failed with unknown errorerror:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed)”

    • From what I’ve been able to tell with just a few minutes googling it looks like that particular error is well known as one caused when the server doesn’t trust the client certificate.

      You should check the client cert using the openSSL tools – Look online for the man page but it’s something like openssl x509 -noout -text /etc/pki/tls/certs/totpcgi.crt you want to be sure the common name is the same FQDN as the server cert and that boy of these match the host name of your server.

      If not then try regenerating the client cert again, making sure you have the common name set to the same FQDN as the server cert. Once regenerated and signed by your CA you’ll need to copy the file back into /etc/pki/tls/certs/totpcgi.crt

      • Ethan

        hmm just checked the subject and it’s CN=machine.fu.bar/emailAddress=requesting.username@fu.bar. I am guessing I need to get the email address removed from the subject maybe?

      • Totally – it should just be CN=machine.fu.bar, if you do the same to show your server cert what does it look like? I’d just be concerned that maybe this is something your CA is adding…

      • Ethan

        Well I can’t keep the chain going down there for some reason.

        I check the certs on both the linux systems and the issuing CA. The subjects all match and the subject field is CN=machine.fu.bar on from the cert server. It all looks to be configured properly.

      • We’ll if the server cert is ok I’d say it was just an error issuing the client side cert – try again making sure to have the CN just be the FQDN, oh and when you get the request signed by the CA make sure you continue to use the advanced route and do it just the same as the server cert. this isn’t a “user” cert its another server cert really just that it’s being used to verify he client to the server rather than the server to the client…

      • Ethan

        It looks like when using openssl s_client -connect x.x.x.x:8443 -CAfile /etc/pki/tls/certs/ca-bundle.crt my CA is not listed even though I know I added the files to the bundle. I will follow this to see where it leads.

      • Ethan

        I’ve made it a little further, but hit a different error.

        I am using a self signed CA and I had not imported that into the ca-bundle.crt, though i thought I had. Once I did that I get the following error below. I’ve also added my root ca cert to /etc/pki/tls/certs/ca.bundle.trusted.crt with no relief.

        Reply-Message = “Denied access by rlm_perl authenticate function. ERROR: 500 Can’t connect to machine.fu.bar:8443 (SSL connect attempt failed because of handshake problemserror:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca)”

      • Ethan

        Ok I’ve beat the SSL into submission. You must restart both httpd and radiusd when playing with ca-bundle.crt

        I am now getting Reply-Message = “Denied access by rlm_perl authenticate function. ERROR: 400 BAD REQUEST”

        Any ideas on that? I’ve turned off the pin requirement.

      • Personally I think at this point I’d start to try to debug the radius daemon as that’s likely to give you the clearest idea of what’s happening. You should stop the running daemon and then start radiusd from the command line with the -X switch that should give you more details on what is causing the error.

      • Ethan

        A couple of changes for those running totpcgi-0.5.5-1.el6.noarch or if you don’t have uid in your totpcgi-handler.pl.

        You will not need to edit the totpcgi-handler.pl at all, just leave it as is and make sure your totpcgi-handler.yaml is formatted correctly as below. There is an addition of one attribute named userAttribute that removes the need to edit the perl script. Comments dated Jul 18, 2013 on totpcgi-handler.pl file can be found here: https://github.com/mricon/totp-cgi/commits/master/contrib/freeradius/totpcgi-handler.pl

        # LDAP Binding information
        bindDN : ‘ldapuser@mydomain.com’
        bindPassword : ‘password for ldapuser@mydomain.com
        userSearchBase : ‘ou=Employees,dc=mydomain,dc=com’
        userAttribute : ‘userPrincipalName’
        groupSearchBase : ‘OU=Groups,DC=mydomain,DC=com’
        groupAttribute : ‘member’
        groupAttributeIsDN : true
        searchScope : ‘sub’

        # USERNAME is replaced with the uid that is passed in
        search : ‘(&(userPrincipalName=USERNAME)(objectClass=user))’

        # groupSearch may be a singleton or array
        groupSearch :
        – ‘(&(CN=VMware View Users)(objectClass=group))’

        # Therefore the user only needs to be a member of VMware View Users

        ldaphosts :
        – ‘ldaps://dc1.mydomain.com’
        – ‘ldaps://dc2.mydomain.com’

        # URL for checking that the password+token is good
        totpurl : ‘https://totpcgi.mydomain.com:8443/’

        # CA Info for validiation into topt-cgi’s https instance
        ca_file : ‘/etc/pki/tls/certs/ca-bundle.crt’
        key_file : ‘/etc/pki/tls/private/totpcgi.key’
        cert_file : ‘/etc/pki/tls/certs/totpcgi.crt’

      • Thanks for that!

        Much appreciated…..

  28. Þröstur Jónasson

    I ran into one problem in Cenos 6 x64 where the freeradius server was multithreaded and crashed the perl perl-Net-SSLeay library, which is a well known bug. I had to add the -t flag to the startup parameters of the RADIUS server to disable threading. But thanks for the information

  29. Just wwnt to say your article is as amazing.
    The clearness in your post is just great and i could assume
    you’re an expert on this subject. Fine with
    your permission let me to grab your RSS feed to keep up to
    date with forthcoming post. Thznks a milion and please ccontinue the rewarding work.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s