0
|
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 |
|