Security/Mentorships/MWoS/2014/OpenVPN MFA

From MozillaWiki
< Security‎ | Mentorships‎ | MWoS‎ | 2014
Jump to: navigation, search
WinterOfSecurity logo light horizontal.png



We are a team of three Senior Undergraduates at Indian Institute of Technology, Kanpur, majoring in Computer Science and Engineering. We have eclectic interests but most of them are in Systems and InfoSec. Shivanshu worked in GSoC, Harsh worked with Directi and Srijan interned at Microsoft Research India over the summers; so we've got some code crunching skills up our sleeves as well :D



Mozilla uses OpenVPN with MFA via deferred C plugins and pythons scripts. However, there are several caveats that require non-plugin based modifications, such as One Time Passwords (OTP) client input and session tracking. The goal of this project is to research and provide a first class user experience when using MFA with OpenVPN.



This project aims to add support for:

  • User session id and tracking
  • True multi-factor authentication support


This project only includes the official OpenVPN command line server and client. Support for 3rd party clients is out of the scope.

Success Criteria

  • Ability to log in with a 2nd factor to OpenVPN, lose the connection, reconnect with the session id and without getting a 2nd factor authentication prompt for a determined period of time. In a nutshell, have a similar experience to web-based MFA solutions.

Multi-factor Authentication Implementation Details

Our scheme of implementation provides support for three different types of authentication: OTP, Push and User-pass (as of yet) in addition to the certificate check that happens as a part of the normal TLS handshake.


In the server config, put the supported authentication methods as follows

mfa-method method-type [script_file via-env|via-file]

Here method type can be 'otp', 'push', 'user-pass'. For example

mfa-method otp via-file

The three methods differ in what credentials are asked from the user for authentication. In otp, only password is asked. In push, nothing is asked. This is meant for authentication by Push notification on mobile phones which can be handled by a deferred auth plugin on the server side. In user-pass, both username and password are asked from the user. If you want to use a plugin for authentication on the server, include the following lines in the config

mfa-method method-type
plugin plugin_shared_object_file

In the client config, put the following line

mfa-method mfa-type

User can authenticate using one method only. Multiple mfa-method lines will give an error.


We have modified the key-method 2 packet structure used for authentication to include MFA credentials. MFA username and password are sent to the script/plugin in a manner similar to the existing auth-user-pass soution. In case of OTP when there is no username, CN of the user is sent as username. In case of PUSH, CN is sent as username and an empty string as password. Three types of plugins are supported for MFA, one for each of push, otp and user-pass.




Session Support Implementation Details


The server needs to provide the following configuration option:

mfa-session-expiration session-validity

session-validity is the duration (in hours) for which the session cookie is valid.

The client needs to provide the following configuration option:

mfa-session-file filename

filename is the path to the file in which session tokens will be stored. If the filename is not provided, then we warn the user and disable session-support.


  • On startup the OpenVPN server generates a random key (48 bytes).
  • When a client successfully authenticates with MFA enabled, the server generates a session token using the tls1_PRF function with the key as the secret and (Client CN + Expiry timestamp) as the data. The expiry timestamp and the token are sent to the client.
  • When the client wants to connect later, it must send the token and the expiry timestamp. If verification succeeds, MFA auth is bypassed. If not, auth fails and the client is prompted for MFA authentication if the auth-retry directive is set to "interact".


We have a weekly meeting every wednesday, at 9AM PDT (9:30 IST).



We are now able to successfully authenticate using a session cookie without entering the credentials for the second factor. For now, we have hard-coded the cookie for the client.


Store the session cookies in a file on client side and read from the file while authenticating.



Got stuck on the following points

  • Handling timestamps for cookie expiration check
  • File functions to maintain a persistent list of cookies on the client side

Created new branch feature/session to work on session support. Refer:



Started working on session support. Check the prelogue for implementation details.



Patches sent to Mozilla's newly created fork of OpenVPN Added support for all possible combinations of backwards compatibility that we could think of. (This might break when we implement Session Support in further releases). Refer:



Second factor authentication using an external script is now supported. Currently, we are working on backwards compatibility to ensure that users running older versions of OpenVPN client are able to authenticate using old auth methods. We will be submitting patches to Mozilla's newly created fork of OpenVPN



We are now able to succesfully authenticate with a second factor using a test plugin.

Config changes

After the discussion in the last meeting, we have decided to drop the method-name option. To keep things simple and avoid conflicts among plugins, we have decided to support only one plugin of each type. Using multiple plugins for the same authentication type is a rare scenario and if required this functionality can be provided by a plugin.

Upcoming work

We will now add support for scripts and then start work on session resumption.



  • Pass credentials to script
  • Backward Compatibility: To ensure smooth transition to the MFA auth method, there will be a configurable parameter on the server specifying whether backward compatibility should be enabled. This will allow the server to gracefully fall back to old auth methods if the client does not support MFA. Since this allows a client to bypass MFA, this option should be disabled after all the clients have upgraded. When the option is disabled, authentication will fail if MFA is not supported.


We've dropped the initial plan of introducing a new key method 3 in favour of modifying key-method-2 with added MFA support under the compile flag ENABLE_MFA (enabled by default). This decision comes in light of the complexity of creating a new key-method and considering the fact that the new key-method had large chunks of code taken from key-method-2 as is.


To make use of multifactor-authentication, some directives have to be included in both - the client config and the server config.

Server Config

Server config specifies list of multifactor authentication (MFA) methods supported by the server. Any client connecting to the server must use a MFA method supported by the server. The directive used to specify MFA config is 'mfa-method'. The format fo specifying a MFA method is

mfa-method <method-name> <method-type> <script-file>

script-file is the path to an external script that will verify the MFA credentials and return a boolean to OpenVPN.

method-type can be one of the following

'user-pass': The client is prompted for a username and a password.

'otp' : One Time Password. Only a password is required.

'push': The plugin/script sends a push notification to the client to authenticate. OpenVPN does not prompt the user for any additional credentials. This method type can be used for other auth methods where user interaction with OpenVPN is not required.

Maximum 32 methods can be specifid in the server config in this way.

Client Config

To use multifactor authentication, client has to specify the method it will use to authenticate to the server. Client can specify only one MFA method in the following way

mfa-method <method-name> <method-type>

When client starts OpenVPN, the user is prompted for credentials according to the method specified in the config. Then, the credentials along with the method-name and method-type are sent to the server. If the server has a matching method-name and method-type in its config, it checks for the credentials using the script specified for that method in its config.


  • Instead of working on a POC program, we've started working on implementing MFA support directly in OpenVPN.
  • Compiler flag ENABLE_MFA has been implemented.
  • mfa parameters are read from server and client configuration.
  • sanity check of parameters configuration files has been implemented.
  • credentials for MFA have been acquired from client.
  • key_method_2_read and key_method_2_write have been modified to include MFA options.


Current work

We have looked at the OpenVPN code that handles authentication. The authentication is two step. In the first step, a TLS connection is set up using the certificates. After this, the tunnel is set up for which new keys are generated. If key method 2 is used, username/password can be used for authentication while setting up the tunnel.

Contents of authentication packet

* TLS plaintext packet (if key_method == 2):
*   Literal 0 (4 bytes).
*   key_method type (1 byte).
*   key_source structure (pre_master only defined for client ->
*       server).
*   options_string_length, including null (2 bytes).
*   Options string (n bytes, null terminated, client/server options
*       string must match).
*   [The username/password data below is optional, record can end
*       at this point.]
*   username_string_length, including null (2 bytes).
*   Username string (n bytes, null terminated).
*   password_string_length, including null (2 bytes).
*   Password string (n bytes, null terminated).

Username/password is passed to plugin/external script for verification and not handled by OpenVPN.

Proposed solution

We could use a new key method (key method 3) for MFA with a packet structure similar to that of key method 2. Options field can be used to specify the supported authentication methods. There can be an optional session identifier field for resuming sessions.

It would be convenient if we implement MFA before session support.

Discussion points

  • How secure are the proposed solutions?
  • Should MFA be mandatory for all clients or should it be opt-in?
  • OpenVPN doesn't use any database for persistent storage. It uses text files. (OpenVPN Access server uses sqlite3). Should we use files or sqlite for server side storage?
  • What if the server and client use different key methods?
  • Backward compatibility(if the server and client run different versions of openvpn?)

Upcoming work

  • Make a PoC program implementing session support.



  • Implement session support in openvpn
  • Implement MultiFactor Authentication in openVpn

Session Support

When a client connects, after the TLS session is established (handshake done): Server requests a list of authentication methods ( generally it'll be Certificate + OTP or Push). Authentication methods can be - Certificate + OTP/Push, The session id method is special and is the only optional method when it fails, auth fails and the client is asked to renegociate, this time, the server doesnt ask for a session id.. AND the client must present its ability to use a session id before the server presents the authentication methods

Session Resumption Alternatives

(inspired by TLS and )

Option 1

Store the session state on the server and an identifier on client side. When the client connects to the server and the client does not provide a session ticket , the server generates the ticket according to the following specifications.

The session state contains

  • the CN of the client
  • timestamp / expiry time
  • OpenVPN session ID
  • any other required fields

Generate a random key name (32 bits) and a random key (128 bits). Generate the HMAC-SHA of the session state appended with the key. Send the HMAC and key name to the client. Store the key name, key and session state in the server-side database.

When client requests a session resumption by sending the key name and the HMAC, the server looks up the session state and key using the key name, verifies the HMAC and the session state. Verification of session state involves checking the CN of the client and the expiration of the session (To avoid computing the HMAC on every request, we may cache the HMACs in the server database)

Option 2

Only client stores the session information

When the client connects to the server and the client does not provide a session ticket , the server generates the ticket according to the following specifications.

The session state contains

  • the CN of the client
  • timestamp / expiry time
  • OpenVPN session ID
  • any other required fields

The session ticket contains the following fields

  • key_name[16];
  • iv[16];
  • encrypted session state
  • mac[32];

from RFC5077 -

Here, key_name serves to identify a particular set of keys used to
protect the ticket.  It enables the server to easily recognize
tickets it has issued.  The key_name should be randomly generated to
avoid collisions between servers.  One possibility is to generate new
random keys and key_name every time the server is started.

The actual state information in encrypted_state is encrypted using
128-bit AES in CBC mode with the given IV.  The Message
Authentication Code (MAC) is calculated using HMAC-SHA-256 over
key_name (16 octets) and IV (16 octets), followed by the length of
the encrypted_state field (2 octets) and its contents (variable


Kick off meeting.


  • current work
  • blocking points
  • discussion points
  • upcoming work