Changeset 2257


Ignore:
Timestamp:
Feb 27, 2014, 3:02:56 PM (9 years ago)
Author:
Nicklas Nordborg
Message:

References #580: Authenticate users using YubiKey? sticks

Implemented actual verification of passwords against YubiCload?. Seems to be working well. A manual configuration step to get a CLIENT_ID and CLIENT_KEY is needed when installing the extension for the first time. Instructions for this need to be written.

Location:
extensions/net.sf.basedb.yubikey/trunk
Files:
1 added
5 edited

Legend:

Unmodified
Added
Removed
  • extensions/net.sf.basedb.yubikey/trunk/build.xml

    r2256 r2257  
    5959    >
    6060    <copy todir="${dist}">
    61       <fileset dir="." includes="README,LICENSE" />
     61      <fileset dir="." includes="README,LICENSE,yubikey.properties" />
    6262      <fileset file="${jar.name}" />
    6363    </copy>
  • extensions/net.sf.basedb.yubikey/trunk/src/net/sf/basedb/yubikey/YubiKey.java

    r2255 r2257  
    2222package net.sf.basedb.yubikey;
    2323
     24import java.io.InputStream;
     25import java.net.URL;
     26import java.util.Properties;
     27
     28import net.sf.basedb.core.ConfigurationException;
    2429import net.sf.basedb.core.authentication.AuthenticationMethod;
     30import net.sf.basedb.util.Values;
    2531
    2632/**
     
    2935  @since 1.0
    3036*/
    31 public class YubiKey
     37public final class YubiKey
    3238{
    3339  /**
     
    3945  public static final AuthenticationMethod AUTHENTICATION_METHOD = AuthenticationMethod.getInstance("yubikey");
    4046 
     47  private static Properties config;
     48  private static Integer clientId;
     49  private static String clientKey;
     50 
     51  /**
     52    Load configuration file: /yubikey.properties
     53  */
     54  public static synchronized final Properties getConfig()
     55  {
     56    if (config == null)
     57    {
     58      try
     59      {
     60        URL configUrl = YubiKey.class.getResource("/yubikey.properties");
     61        InputStream is = configUrl == null ? null : configUrl.openStream();
     62        if (is == null)
     63        {
     64          throw new ConfigurationException("Can't find the configuration file. " +
     65              "Make sure '/yubikey.properties' is in the CLASSPATH.");
     66        }
     67        config = new Properties();
     68        config.load(is);
     69      }
     70      catch (Exception ex)
     71      {
     72        throw new ConfigurationException(ex.getMessage(), ex);
     73      }
     74     
     75      clientId = Values.getInteger(config.getProperty("client-id"), null);
     76      clientKey = config.getProperty("client-key");
     77     
     78      if (clientId == null || clientKey == null)
     79      {
     80        // Invalid configuration
     81        config = null;
     82        clientId = null;
     83        clientKey = null;
     84        throw new ConfigurationException("Missing configuration value: client-id or client-key");
     85      }
     86    }
     87    return config;
     88  }
     89 
     90  /**
     91    Get the configured CLIENT_ID to use when validating passwords
     92    with the YubiCloud service.
     93  */
     94  public static Integer getClientId()
     95  {
     96    return clientId;
     97  }
     98 
     99  /**
     100    Get the configured CLIENT_KEY to use when validating passwords
     101    with the YubiCloud service.
     102  */
     103  public static String getClientKey()
     104  {
     105    return clientKey;
     106  }
    41107}
  • extensions/net.sf.basedb.yubikey/trunk/src/net/sf/basedb/yubikey/YubiKeyAuthenticationManager.java

    r2255 r2257  
    1313import net.sf.basedb.core.data.UserData;
    1414
     15/**
     16  Authentication manager for YubiKey one-time-passwords.
     17  The 'login' string is expected to be a YubiKey one-time password, but
     18  may also be a regular login for user accounts that has not been YubiKey-enabled.
     19 
     20  To prevent YubiKey users to login using regular login, the first check is
     21  to see if the login matches an existing user and if that user is YubiKey-enabled.
     22 
     23  If no matching user is found and the login is a valid YubiKey password,
     24  a second user lookup is perfomed using the public ID part of the password.
     25  If a user is found the password is sent to YubiCloud for verification.
     26  If the verification is successful the final step is to use the regular
     27  internal verification to check the password.
     28 
     29  @author nicklas
     30  @since 1.0
     31*/
    1532public class YubiKeyAuthenticationManager
    1633  implements AuthenticationManager
     
    4158        throw new LoginException("User '" + login + "' must login with YubiKey!");
    4259      }
     60      // The user is not YubiKey-enabled; return null to continue with internal authentication
     61      return null;
    4362    }
    4463   
    4564    // Check if the login is a valid YubiKey one-time-password
    46     System.out.println("Checking format: "+ login);
    4765    if (YubicoClient.isValidOTPFormat(login))
    4866    {
    49       System.out.println("Valid: "+ login);
    5067      // Find user in BASE with externalId=YubiKey:<public-id>
    5168      String publicId = YubicoClient.getPublicId(login);
    5269      String externalId = "YubiKey:" + publicId;
    53       System.out.println("publicId: "+ publicId);
    5470      user = context.getUserByExternalId(externalId);
    55       System.out.println("user: "+ user);
    5671   
    5772      if (user != null)
    5873      {
    59         // Verify the one-time-password using YubiKey cload service
    60         //YubicoClient ybClient = YubicoClient.getClient(null);
    61         //YubicoResponse response = ybClient.verify(login);
    62         //if (response.getStatus() == YubicoResponseStatus.OK)
     74        // Verify the one-time-password using the YubiCloud service
     75        YubicoClient ybClient = null;
     76        YubicoResponse response = null;
     77        try
     78        {
     79          ybClient = YubicoClient.getClient(YubiKey.getClientId());
     80          ybClient.setKey(YubiKey.getClientKey());
     81          response = ybClient.verify(login);
     82        }
     83        catch (Exception e)
     84        {
     85          throw new LoginException(e.getMessage(), e);
     86        }
    6387       
    64         // Pretend to verify one-time-password
    65         if (YubicoClient.isValidOTPFormat(login))
     88        YubicoResponseStatus status = response.getStatus();
     89        /*
     90        System.out.println("status:" + response.getStatus());
     91        System.out.println("H:" + response.getH());
     92        System.out.println("Nonce:" + response.getNonce());
     93        System.out.println("otp:" + response.getOtp());
     94        //System.out.println("publicid:" + response.getPublicId());
     95        System.out.println("sessioncounter:" + response.getSessioncounter());
     96        System.out.println("sessionuse:" + response.getSessionuse());
     97        System.out.println("Sl:" + response.getSl());
     98        System.out.println("T:" + response.getT());
     99        System.out.println("Timestamp:" + response.getTimestamp());
     100        */
     101
     102        if (status != YubicoResponseStatus.OK)
    66103        {
    67           // YubiKey is valid -- verify password using internal authentication
    68           LoginRequest internal = new LoginRequest(user.getLogin(), request.getPassword());
    69           context.verifyUserInternal(internal);
    70           authInfo = new AuthenticatedUser(YubiKey.AUTHENTICATION_METHOD, user);
     104          String msg = "Invalid YubiKey";
     105          if (status == YubicoResponseStatus.BAD_OTP)
     106          {
     107            msg = "Invalid one-time-password format";
     108          }
     109          else if (status == YubicoResponseStatus.REPLAYED_OTP)
     110          {
     111            msg = "One-time-password has already been used";
     112          }
     113          throw new LoginException(msg + " (" + status + ")");
    71114        }
    72         else
    73         {
    74           throw new LoginException("Invalid YubiKey: "+login);
    75         }
     115
     116        // YubiKey is valid -- verify password using internal authentication
     117        LoginRequest internal = new LoginRequest(user.getLogin(), request.getPassword());
     118        context.verifyUserInternal(internal);
     119        authInfo = new AuthenticatedUser(YubiKey.AUTHENTICATION_METHOD, user);
    76120      }
    77121    }
  • extensions/net.sf.basedb.yubikey/trunk/src/net/sf/basedb/yubikey/YubiKeyAuthenticationManagerFactory.java

    r2252 r2257  
    11package net.sf.basedb.yubikey;
     2
    23
    34import net.sf.basedb.core.AuthenticationContext;
     
    1718{
    1819 
     20  private boolean hasConfig;
    1921 
    2022  public YubiKeyAuthenticationManagerFactory()
    21   {}
     23  {
     24    hasConfig = YubiKey.getConfig() != null;
     25  }
    2226
    2327  /**
    24     @return Always true
     28    @return Always true (if properly configured)
    2529  */
    2630  @Override
    2731  public boolean prepareContext(InvokationContext<? super AuthenticationManager> context)
    2832  {
    29     return true;
     33    return hasConfig;
    3034  }
    3135
  • extensions/net.sf.basedb.yubikey/trunk/src/net/sf/basedb/yubikey/YubiKeyLoginFormFactory.java

    r2256 r2257  
    2828        "Insert the <a href=\"http://www.yubico.com/\" target=\"_top\">YubiKey</a> stick into a USB slot in your computer and press "
    2929        + "the button when the green light is turned on. "
    30         + "Then enter your regular password.<br>"
     30        + "Then enter your regular password."
     31        + "<p style=\"margin-top: 0.5em;\">"
    3132        + "<b>Note!</b> Users without a YubiKey should login with their username.");
    3233 
Note: See TracChangeset for help on using the changeset viewer.