plugins/yubikey/corelib.php
changeset 0 9d2c4f04a0d0
child 3 d0fe7acaf0e8
equal deleted inserted replaced
-1:000000000000 0:9d2c4f04a0d0
       
     1 <?php
       
     2 
       
     3 define('YK_SEC_NORMAL_USERNAME', 1);
       
     4 define('YK_SEC_NORMAL_PASSWORD', 2);
       
     5 define('YK_SEC_ELEV_USERNAME', 4);
       
     6 define('YK_SEC_ELEV_PASSWORD', 8);
       
     7 define('YK_SEC_ALLOW_NO_OTP', 16);
       
     8 
       
     9 define('YK_DEFAULT_VERIFY_URL', 'http://api.yubico.com/wsapi/verify');
       
    10 
       
    11 function generate_yubikey_field($name = 'yubikey_otp', $value = false)
       
    12 {
       
    13   global $lang;
       
    14   
       
    15   $fid = substr(sha1(microtime() . mt_rand()), 0, 12);
       
    16   $class = $value ? 'wasfull' : 'wasempty';
       
    17   $html = '<input id="yubifield' . $fid . '" class="' . $class . '" type="hidden" name="' . $name . '" value="' . ( is_string($value) ? $value : '' ) . '" />';
       
    18   if ( $value )
       
    19   {
       
    20     $html .= '<span id="yubistat' . $fid . '" class="yubikey_status enrolled">' . $lang->get('yubiauth_ctl_status_enrolled') . '</span>';
       
    21     $atext = $lang->get('yubiauth_ctl_btn_change_key');
       
    22     $classadd = ' abutton_green';
       
    23   }
       
    24   else
       
    25   {
       
    26     $html .= '<span id="yubistat' . $fid . '" class="yubikey_status empty">' . $lang->get('yubiauth_ctl_status_empty') . '</span>';
       
    27     $atext = $lang->get('yubiauth_ctl_btn_enroll');
       
    28     $classadd = '';
       
    29   }
       
    30   $html .= ' <a class="abutton' . $classadd . ' yubikey_enroll" onclick="yk_mb_init(\'yubifield' . $fid . '\', \'yubistat' . $fid . '\'); return false;" href="#enroll">' . $atext . '</a>';
       
    31   if ( $value )
       
    32   {
       
    33     $html .= ' <a class="abutton abutton_red yubikey_enroll" onclick="yk_clear(\'yubifield' . $fid . '\', \'yubistat' . $fid . '\'); return false;" href="#enroll">'
       
    34              . $lang->get('yubiauth_ctl_btn_clear') .
       
    35              '</a>';
       
    36   }
       
    37   $html = '<noscript><input type="text" name="' . $name . '" class="yubikey_noscript" value="' . ( is_string($value) ? $value : '' ) . '" /> </noscript>'
       
    38           . $html; // '<script type="text/javascript">document.write(unescape("' . rawurlencode($html) . '"));</script>';
       
    39   return $html;
       
    40 }
       
    41 
       
    42 function yubikey_validate_otp($otp)
       
    43 {
       
    44   $api_key = getConfig('yubikey_api_key');
       
    45   $api_id  = getConfig('yubikey_api_key_id');
       
    46   if ( !$api_key || !$api_id )
       
    47   {
       
    48     return array(
       
    49         'success' => false,
       
    50         'error' => 'missing_api_key'
       
    51       );
       
    52   }
       
    53   if ( !preg_match('/^[cbdefghijklnrtuv]{44}$/', $otp) )
       
    54   {
       
    55     return array(
       
    56         'success' => false,
       
    57         'error' => 'otp_invalid_chars'
       
    58       );
       
    59   }
       
    60   // make HTTP request
       
    61   require_once( ENANO_ROOT . '/includes/http.php' );
       
    62   $auth_url = getConfig('yubikey_auth_server', YK_DEFAULT_VERIFY_URL);
       
    63   $auth_url = preg_replace('#^https?://#i', '', $auth_url);
       
    64   if ( !preg_match('#^(\[?[a-z0-9-:]+(?:\.[a-z0-9-:]+\]?)*)(/.*)$#', $auth_url, $match) )
       
    65   {
       
    66     return array(
       
    67         'success' => false,
       
    68         'error' => 'invalid_auth_url'
       
    69       );
       
    70   }
       
    71   $auth_server =& $match[1];
       
    72   $auth_uri =& $match[2];
       
    73   $req = new Request_HTTP($auth_server, $auth_uri);
       
    74   $req->add_get('id', strval($api_id));
       
    75   $req->add_get('otp', $otp);
       
    76   $req->add_get('h', yubikey_sign($req->parms_get));
       
    77   
       
    78   $response = $req->get_response_body();
       
    79   
       
    80   if ( $req->response_code != HTTP_OK )
       
    81   {
       
    82     return array(
       
    83         'success' => false,
       
    84         'error' => 'http_response_error'
       
    85       );
       
    86   }
       
    87   $response = trim($response);
       
    88   $response_nosig = preg_replace('/^h=(.+?)$/m', '', $response);
       
    89   if ( !preg_match_all('/^([a-z0-9_]+)=(.*?)$/m', $response, $matches) )
       
    90   {
       
    91     return array(
       
    92         'success' => false,
       
    93         'error' => 'malformed_response'
       
    94       );
       
    95   }
       
    96   $response = array();
       
    97   foreach ( $matches[0] as $i => $_ )
       
    98   {
       
    99     $response[$matches[1][$i]] = $matches[2][$i];
       
   100   }
       
   101   // make sure we have a status
       
   102   if ( !isset($response['status']) )
       
   103   {
       
   104     return array(
       
   105         'success' => false,
       
   106         'error' => 'response_missing_status'
       
   107       );
       
   108   }
       
   109   // verify response signature
       
   110   // MISSING_PARAMETER is the ONLY situation under which an unsigned response is acceptable
       
   111   if ( $response['status'] !== 'MISSING_PARAMETER' )
       
   112   {
       
   113     if ( !isset($response['h']) )
       
   114     {
       
   115       return array(
       
   116           'success' => false,
       
   117           'error' => 'response_missing_sig'
       
   118         );
       
   119     }
       
   120     if ( yubikey_sign($response) !== $response['h'] )
       
   121     {
       
   122       return array(
       
   123           'success' => false,
       
   124           'error' => 'response_invalid_sig'
       
   125         );
       
   126     }
       
   127   }
       
   128   if ( $response['status'] === 'OK' )
       
   129   {
       
   130     return array(
       
   131         'success' => true
       
   132       );
       
   133   }
       
   134   else
       
   135   {
       
   136     return array(
       
   137         'success' => false,
       
   138         'error' => strtolower("response_{$response['status']}")
       
   139       );
       
   140   }
       
   141 }
       
   142 
       
   143 function yubikey_sign($arr)
       
   144 {
       
   145   static $api_key = false;
       
   146   
       
   147   ksort($arr);
       
   148   if ( isset($arr['h']) )
       
   149     unset($arr['h']);
       
   150   
       
   151   if ( !$api_key )
       
   152   {
       
   153     $api_key = getConfig('yubikey_api_key');
       
   154     $api_key = hexencode(base64_decode($api_key), '', '');
       
   155   }
       
   156   
       
   157   $req = array();
       
   158   foreach ( $arr as $key => $val )
       
   159   {
       
   160     $req[] = "$key=$val";
       
   161   }
       
   162   $req = implode('&', $req);
       
   163   
       
   164   $sig = hmac_sha1($req, $api_key);
       
   165   $sig = hexdecode($sig);
       
   166   $sig = base64_encode($sig);
       
   167   
       
   168   return $sig;
       
   169 }
       
   170 
       
   171 $plugins->attachHook('compile_template', 'yubikey_attach_headers($this);');
       
   172 
       
   173 function yubikey_attach_headers(&$template)
       
   174 {
       
   175   $template->add_header('<script type="text/javascript" src="' . scriptPath . '/plugins/yubikey/yubikey.js"></script>');
       
   176   $template->add_header('<link rel="stylesheet" type="text/css" href="' . scriptPath . '/plugins/yubikey/yubikey.css" />');
       
   177 }
       
   178