# HG changeset patch # User Dan # Date 1235628502 18000 # Node ID 9d2c4f04a0d0c03b686339cf36bdc9933d82ab96 First commit! Hoping everything works. diff -r 000000000000 -r 9d2c4f04a0d0 plugins/Yubikey.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/Yubikey.php Thu Feb 26 01:08:22 2009 -0500 @@ -0,0 +1,167 @@ + language ID => categories array, ( strings object => category \ + objects => strings ) + +All text leading up to first curly brace is stripped by the parser; using +a code tag makes jEdit and other editors do automatic indentation and +syntax highlighting on the language data. The use of the code tag is not +necessary; it is only included as a tool for development. + + +{ + // english + eng: { + categories: [ 'meta', 'yubiauth', 'yubiucp', 'yubiacp' ], + strings: { + meta: { + yubiauth: 'Yubikey authentication messages', + yubiucp: 'Yubikey user CP', + yubiacp: 'Yubikey admin CP', + }, + yubiauth: { + msg_please_touch_key: 'Please touch your Yubikey', + msg_close_instructions: 'Press Esc to cancel', + msg_invalid_chars: 'OTP contains invalid characters', + msg_validating_otp: 'Validating OTP...', + msg_otp_valid: 'OTP validated', + btn_enter_otp: 'Enter a Yubikey OTP', + lbl_otp_field: 'Yubikey OTP:', + + ctl_btn_change_key: 'Change key', + ctl_btn_clear: 'Clear', + ctl_btn_enroll: 'Enroll', + ctl_status_enrolled_pending: 'Enrolled (pending)', + ctl_status_empty: 'Not enrolled', + ctl_status_remove_pending: 'Removed (pending)', + ctl_status_enrolled: 'Enrolled', + + err_invalid_otp: 'Your login was rejected because the Yubikey OTP you entered contains invalid characters.', + err_invalid_auth_url: 'Login with Yubikey was rejected because the URL to the authentication server is not valid.', + err_nothing_provided: 'You did not provide a Yubikey OTP or a username. One of these is required for login to work.', + err_must_have_otp: 'Please provide a Yubikey OTP to log in to this account.', + err_must_have_username: 'Please provide your username.', + err_key_not_authorized: 'This Yubikey is not authorized on this site.', + err_otp_invalid_chars: '%this.yubiauth_err_invalid_otp%', + err_missing_api_key: 'Your OTP could not be validated because no Yubico API key is registered on this site.', + err_http_response_error: 'Your OTP could not be validated because the Yubico authentication server reported an error.', + err_malformed_response: 'Your OTP could not be validated because the Yubico authentication server returned an unexpected response.', + err_response_missing_sig: 'Your OTP could not be validated because the Yubico authentication server did not sign its response.', + err_response_invalid_sig: 'Your OTP could not be validated because the signature of the authentication response was invalid.', + err_response_missing_status: '%this.yubiauth_err_malformed_response%', + err_response_ok: 'OTP is OK', + err_response_bad_otp: 'Authentication failed because the Yubikey OTP is invalid.', + err_response_replayed_otp: 'Authentication failed because the Yubikey OTP you entered has been used before.', + err_response_bad_signature: 'Authentication failed because the Yubico authentication server reported an invalid signature.', + err_response_missing_parameter: 'Authentication failed because of a Dan Fuhry error.', + err_response_no_such_client: 'Authentication failed because the Yubikey you used is not registered with Yubico.', + err_response_operation_not_allowed: 'Authentication failed because the Enano server was denied the request to validate the OTP.', + err_response_backend_error: 'Authentication failed because an unexpected problem happened with the Yubico server.', + err_response_security_error: 'Authentication failed because the Yubico authentication server reported an unknown security error.', + + specialpage_yubikey: 'Yubikey API' + }, + yubiucp: { + panel_title: 'Yubikey settings', + + field_enable_title: 'Enable Yubikey support on my account:', + field_enable_hint: 'Disabling support will remove any keys that are enrolled for your account.', + field_enable: 'Enabled', + field_keys_title: 'Enrolled Yubikeys:', + field_keys_hint: 'Enroll a Yubikey to allow it to log into your account.', + field_keys_maximum: 'You can enroll up to %max% Yubikeys.', + field_normal_flags: 'When logging in:', + field_elev_flags: 'When performing sensitive operations:', + field_flags_keyonly: 'Only require my Yubikey', + field_flags_username: 'Require a username', + field_flags_userandpw: 'Require a username and password', + field_allow_plain_login: 'Allow me to log in without my Yubikey', + field_allow_plain_login_hint: 'If this option is turned off, you will be unable to access your account if all of your enrolled Yubikeys become lost or broken. However, turning this option off provides greater security.', + err_double_enrollment: 'One of the Yubikeys you tried to enroll is already enrolled on another account on this website. A single Yubikey can only be associated with one account at a time.', + }, + yubiacp: { + th: 'Yubikey authentication', + field_enable_title: 'Yubikey support:', + field_enable: 'Enable Yubikey authentication', + field_api_key: 'Yubico API key:', + field_api_key_id: 'Yubico numeric ID:', + field_auth_server: 'Authentication server URL:', + field_enroll_limit: 'Number of enrolled keys permitted per account:', + + err_invalid_auth_server: 'The URL to the Yubikey authentication server that you entered is invalid.' + } + } + } +} + +**!*/ + diff -r 000000000000 -r 9d2c4f04a0d0 plugins/yubikey/admincp.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/yubikey/admincp.php Thu Feb 26 01:08:22 2009 -0500 @@ -0,0 +1,82 @@ +attachHook('acp_general_users', 'yubikey_admin_cp_ui();'); +$plugins->attachHook('acp_general_save', 'yubikey_admin_cp_save();'); + +function yubikey_admin_cp_ui() +{ + global $lang; + ?> + + + get('yubiacp_th'); ?> + + + + + + get('yubiacp_field_enable_title'); ?> + + + + + + + + + get('yubiacp_field_api_key'); ?> + + + + + + + + + get('yubiacp_field_api_key_id'); ?> + + + + + + + + + get('yubiacp_field_auth_server'); ?> + + + + + + + + + get('yubiacp_field_enroll_limit'); ?> + + + + + + + ' . $lang->get('yubiacp_err_invalid_auth_server') . ''; +} + diff -r 000000000000 -r 9d2c4f04a0d0 plugins/yubikey/auth.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/yubikey/auth.php Thu Feb 26 01:08:22 2009 -0500 @@ -0,0 +1,242 @@ +attachHook('login_process_userdata_json', 'return yubikey_auth_hook_json($userinfo, $req["level"], @$req["remember"]);'); +// hook into special page init +$plugins->attachHook('session_started', 'yubikey_add_special_pages();'); + +function yubikey_auth_hook_json(&$userdata, $level, $remember) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + $do_validate_otp = false; + $do_validate_user = false; + $do_validate_pass = false; + + $user_flag = ( $level >= USER_LEVEL_CHPREF ) ? YK_SEC_ELEV_USERNAME : YK_SEC_NORMAL_USERNAME; + $pass_flag = ( $level >= USER_LEVEL_CHPREF ) ? YK_SEC_ELEV_PASSWORD : YK_SEC_NORMAL_PASSWORD; + + $auth_log_prefix = ( $level >= USER_LEVEL_CHPREF ) ? 'admin_' : ''; + + if ( !empty($userdata['username']) ) + { + // get flags + $q = $db->sql_query('SELECT user_id, user_yubikey_flags FROM ' . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '" . $db->escape(strtolower($userdata['username'])) . "';"); + if ( !$q ) + $db->die_json(); + + if ( $db->numrows() < 1 ) + { + // Username not found - let the main login function handle it + $db->free_result(); + return null; + } + list($user_id, $flags) = $db->fetchrow_num(); + $flags = intval($flags); + // At the point the username is validated. + $do_validate_user = false; + $do_validate_pass = $flags & $pass_flag; + if ( empty($userdata['yubikey_otp']) ) + { + // no OTP was provided + // make sure the user has allowed logging in with no OTP + if ( !($flags & YK_SEC_ALLOW_NO_OTP) ) + { + // We also might have no Yubikeys enrolled. + $q = $db->sql_query('SELECT 1 FROM ' . table_prefix . "yubikey WHERE user_id = $user_id;"); + if ( !$q ) + $db->die_json(); + + if ( $db->numrows() > 0 ) + { + // Yep at least one key is enrolled. + // I don't think these should be logged because they'll usually just be innocent mistakes. + $db->free_result(); + return array( + 'mode' => 'error', + 'error' => 'yubiauth_err_must_have_otp' + ); + } + // Nope, no keys enrolled, user hasn't enabled Yubikey support + $db->free_result(); + } + // we're ok, use normal password auth + return null; + } + else + { + // user did enter an OTP + $do_validate_otp = true; + } + } + else if ( !empty($userdata['yubikey_otp']) ) + { + // we have an OTP, but no username to work with + $yubi_uid = substr($userdata['yubikey_otp'], 0, 12); + if ( !preg_match('/^[cbdefghijklnrtuv]{12}$/', $yubi_uid ) ) + { + return array( + 'mode' => 'error', + 'error' => 'yubiauth_err_invalid_otp' + ); + } + $q = $db->sql_query('SELECT u.user_id, u.username, u.user_yubikey_flags FROM ' . table_prefix . "users AS u\n" + . " LEFT JOIN " . table_prefix . "yubikey AS y\n" + . " ON ( y.user_id = u.user_id )\n" + . " WHERE y.yubi_uid = '$yubi_uid'\n" + . " GROUP BY u.user_yubikey_flags;"); + if ( !$q ) + $db->_die(); + + if ( $db->numrows() < 1 ) + { + if ( !$do_validate_pass ) + $session->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES\n" + . ' (\'security\', \'' . $auth_log_prefix . 'auth_bad\', '.time().', \''.enano_date('d M Y h:i a').'\', \'(Yubikey)\', ' + . '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + + return array( + 'mode' => 'error', + 'error' => 'yubiauth_err_key_not_authorized' + ); + } + + list($user_id, $username, $flags) = $db->fetchrow_num(); + $do_validate_otp = true; + $do_validate_user = $flags & $user_flag; + $do_validate_pass = $flags & $pass_flag; + } + else + { + // Nothing - no username or OTP. This request can't be used; throw it out. + return array( + 'mode' => 'error', + 'error' => 'yubiauth_err_nothing_provided' + ); + } + if ( $do_validate_otp ) + { + // We need to validate the OTP. + $otp_check = yubikey_validate_otp($userdata['yubikey_otp']); + if ( !$otp_check['success'] ) + { + if ( !$do_validate_pass ) + $session->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES\n" + . ' (\'security\', \'' . $auth_log_prefix . 'auth_bad\', '.time().', \''.enano_date('d M Y h:i a').'\', \'(Yubikey)\', ' + . '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + return array( + 'mode' => 'error', + 'error' => 'yubiauth_err_' . $otp_check['error'] + ); + } + } + if ( $do_validate_user ) + { + if ( empty($username) ) + { + return array( + 'mode' => 'error', + 'error' => 'yubiauth_err_must_have_username' + ); + } + if ( strtolower($username) !== strtolower($userdata['username']) ) + { + // Username incorrect + if ( !$do_validate_pass ) + $session->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES\n" + . ' (\'security\', \'' . $auth_log_prefix . 'auth_bad\', '.time().', \''.enano_date('d M Y h:i a').'\', \'(Yubikey)\', ' + . '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + return array( + 'mode' => 'error', + 'error' => 'invalid_credentials' + ); + } + } + // Do we need to have the password validated? + if ( $do_validate_pass ) + { + // Yes; return and let the login API continue + return null; + } + else + { + // No password required; validated, issue session key + $session->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES\n" + . ' (\'security\', \'' . $auth_log_prefix . 'auth_good\', '.time().', \''.enano_date('d M Y h:i a').'\', \'' . $db->escape($userdata['username']) . '\', ' + . '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + + $q = $db->sql_query('SELECT password FROM ' . table_prefix . "users WHERE user_id = $user_id;"); + if ( !$q ) + $db->_die(); + + list($password) = $db->fetchrow_num(); + $db->free_result(); + + $session->register_session($user_id, $userdata['username'], $password, $level, $remember); + return true; + } +} + +function yubikey_add_special_pages() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + $paths->add_page(array( + 'name' => $lang->get('yubiauth_specialpage_yubikey'), + 'urlname' => 'Yubikey', + 'namespace' => 'Special', + 'visible' => 0, 'protected' => 0, 'comments_on' => 0, 'special' => 0 + )); +} + +function page_Special_Yubikey() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + header('Content-type: text/javascript'); + /* + if ( isset($_GET['validate_otp']) ) + { + echo enano_json_encode(yubikey_validate_otp($_GET['validate_otp'])); + return true; + } + */ + if ( isset($_GET['get_flags']) || isset($_POST['get_flags']) ) + { + $yubi_uid = substr($_REQUEST['get_flags'], 0, 12); + if ( !preg_match('/^[cbdefghijklnrtuv]{12}$/', $yubi_uid) ) + { + return print enano_json_encode(array( + 'mode' => 'error', + 'error' => 'invalid_otp' + )); + } + $q = $db->sql_query('SELECT u.user_yubikey_flags FROM ' . table_prefix . "users AS u\n" + . " LEFT JOIN " . table_prefix . "yubikey AS y\n" + . " ON ( y.user_id = u.user_id )\n" + . " WHERE y.yubi_uid = '$yubi_uid'\n" + . " GROUP BY u.user_yubikey_flags;"); + if ( !$q ) + $db->_die(); + + if ( $db->numrows() < 1 ) + { + return print enano_json_encode(array( + 'mode' => 'error', + 'error' => 'key_not_authorized' + )); + } + + list($flags) = $db->fetchrow_num(); + + echo enano_json_encode(array( + // We strip YK_SEC_ALLOW_NO_OTP here for security reasons. + 'flags' => intval($flags & ~YK_SEC_ALLOW_NO_OTP) + )); + + return true; + } +} + diff -r 000000000000 -r 9d2c4f04a0d0 plugins/yubikey/corelib.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/yubikey/corelib.php Thu Feb 26 01:08:22 2009 -0500 @@ -0,0 +1,178 @@ +'; + if ( $value ) + { + $html .= '' . $lang->get('yubiauth_ctl_status_enrolled') . ''; + $atext = $lang->get('yubiauth_ctl_btn_change_key'); + $classadd = ' abutton_green'; + } + else + { + $html .= '' . $lang->get('yubiauth_ctl_status_empty') . ''; + $atext = $lang->get('yubiauth_ctl_btn_enroll'); + $classadd = ''; + } + $html .= ' ' . $atext . ''; + if ( $value ) + { + $html .= ' ' + . $lang->get('yubiauth_ctl_btn_clear') . + ''; + } + $html = '' + . $html; // ''; + return $html; +} + +function yubikey_validate_otp($otp) +{ + $api_key = getConfig('yubikey_api_key'); + $api_id = getConfig('yubikey_api_key_id'); + if ( !$api_key || !$api_id ) + { + return array( + 'success' => false, + 'error' => 'missing_api_key' + ); + } + if ( !preg_match('/^[cbdefghijklnrtuv]{44}$/', $otp) ) + { + return array( + 'success' => false, + 'error' => 'otp_invalid_chars' + ); + } + // make HTTP request + require_once( ENANO_ROOT . '/includes/http.php' ); + $auth_url = getConfig('yubikey_auth_server', YK_DEFAULT_VERIFY_URL); + $auth_url = preg_replace('#^https?://#i', '', $auth_url); + if ( !preg_match('#^(\[?[a-z0-9-:]+(?:\.[a-z0-9-:]+\]?)*)(/.*)$#', $auth_url, $match) ) + { + return array( + 'success' => false, + 'error' => 'invalid_auth_url' + ); + } + $auth_server =& $match[1]; + $auth_uri =& $match[2]; + $req = new Request_HTTP($auth_server, $auth_uri); + $req->add_get('id', strval($api_id)); + $req->add_get('otp', $otp); + $req->add_get('h', yubikey_sign($req->parms_get)); + + $response = $req->get_response_body(); + + if ( $req->response_code != HTTP_OK ) + { + return array( + 'success' => false, + 'error' => 'http_response_error' + ); + } + $response = trim($response); + $response_nosig = preg_replace('/^h=(.+?)$/m', '', $response); + if ( !preg_match_all('/^([a-z0-9_]+)=(.*?)$/m', $response, $matches) ) + { + return array( + 'success' => false, + 'error' => 'malformed_response' + ); + } + $response = array(); + foreach ( $matches[0] as $i => $_ ) + { + $response[$matches[1][$i]] = $matches[2][$i]; + } + // make sure we have a status + if ( !isset($response['status']) ) + { + return array( + 'success' => false, + 'error' => 'response_missing_status' + ); + } + // verify response signature + // MISSING_PARAMETER is the ONLY situation under which an unsigned response is acceptable + if ( $response['status'] !== 'MISSING_PARAMETER' ) + { + if ( !isset($response['h']) ) + { + return array( + 'success' => false, + 'error' => 'response_missing_sig' + ); + } + if ( yubikey_sign($response) !== $response['h'] ) + { + return array( + 'success' => false, + 'error' => 'response_invalid_sig' + ); + } + } + if ( $response['status'] === 'OK' ) + { + return array( + 'success' => true + ); + } + else + { + return array( + 'success' => false, + 'error' => strtolower("response_{$response['status']}") + ); + } +} + +function yubikey_sign($arr) +{ + static $api_key = false; + + ksort($arr); + if ( isset($arr['h']) ) + unset($arr['h']); + + if ( !$api_key ) + { + $api_key = getConfig('yubikey_api_key'); + $api_key = hexencode(base64_decode($api_key), '', ''); + } + + $req = array(); + foreach ( $arr as $key => $val ) + { + $req[] = "$key=$val"; + } + $req = implode('&', $req); + + $sig = hmac_sha1($req, $api_key); + $sig = hexdecode($sig); + $sig = base64_encode($sig); + + return $sig; +} + +$plugins->attachHook('compile_template', 'yubikey_attach_headers($this);'); + +function yubikey_attach_headers(&$template) +{ + $template->add_header(''); + $template->add_header(''); +} + diff -r 000000000000 -r 9d2c4f04a0d0 plugins/yubikey/formicon.gif Binary file plugins/yubikey/formicon.gif has changed diff -r 000000000000 -r 9d2c4f04a0d0 plugins/yubikey/usercp.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/yubikey/usercp.php Thu Feb 26 01:08:22 2009 -0500 @@ -0,0 +1,267 @@ +attachHook("userprefs_jbox", "yubikey_ucp_setup();"); +$plugins->attachHook("userprefs_body", "return yubikey_user_cp(\$section);"); +$plugins->attachHook("login_form_html", "yubikey_inject_html_login();"); + +function yubikey_ucp_setup() +{ + userprefs_menu_add('usercp_sec_profile', 'yubiucp_panel_title', makeUrlNS('Special', 'Preferences/Yubikey')); +} + +function yubikey_user_cp($section) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + if ( $section !== 'Yubikey' ) + return false; + + $count_enabled = intval(getConfig('yubikey_enroll_limit', '3')); + + if ( isset($_POST['submit']) ) + { + csrf_request_confirm(); + + $keys = array(); + if ( isset($_POST['yubikey_enable']) ) + { + for ( $i = 0; $i < $count_enabled; $i++ ) + { + if ( !empty($_POST["yubikey_otp_$i"]) ) + { + $ckey =& $_POST["yubikey_otp_$i"]; + if ( preg_match('/^[cbdefghijklnrtuv]{12,44}$/', $ckey) ) + { + $ckey = substr($ckey, 0, 12); + $keys[] = $ckey; + } + unset($ckey); + } + } + } + // Check for double enrollment + $keys_check = "yubi_uid = '" . implode("' OR yubi_uid = '", $keys) . "'"; + $q = $db->sql_query('SELECT yubi_uid FROM ' . table_prefix . "yubikey WHERE ( $keys_check ) AND user_id != {$session->user_id};"); + if ( !$q ) + $db->_die(); + + if ( $db->numrows() > 0 ) + { + echo '
' . $lang->get('yubiucp_err_double_enrollment') . '
'; + while ( $row = $db->fetchrow() ) + { + foreach ( $keys as $i => $key ) + { + if ( $key == $row['yubi_uid'] ) + { + unset($keys[$i]); + } + } + } + $keys = array_values($keys); + } + $db->free_result(); + + // Remove all currently registered keys + $q = $db->sql_query('DELETE FROM ' . table_prefix . "yubikey WHERE user_id = {$session->user_id};"); + if ( !$q ) + $db->_die(); + + // Enroll any new keys + if ( !empty($keys) ) + { + $query = 'INSERT INTO ' . table_prefix . "yubikey(user_id, yubi_uid) VALUES\n " . + "( $session->user_id, '" . implode("' ),\n ( $session->user_id, '", $keys) . "' );"; + if ( !$db->sql_query($query) ) + $db->_die(); + } + + // Calculate flags + $yubi_flags = 0; + $yubi_flags |= intval($_POST['login_normal_flags']); + $yubi_flags |= intval($_POST['login_elev_flags']); + $yubi_flags |= ( isset($_POST['allow_no_yubikey']) ) ? YK_SEC_ALLOW_NO_OTP : 0; + + // update flags + $q = $db->sql_query('UPDATE ' . table_prefix . "users SET user_yubikey_flags = $yubi_flags WHERE user_id = {$session->user_id};"); + if ( !$q ) + $db->_die(); + } + else + { + // Fetch flags + $q = $db->sql_query('SELECT user_yubikey_flags FROM ' . table_prefix . "users WHERE user_id = {$session->user_id};"); + if ( !$q ) + $db->_die(); + + list($yubi_flags) = $db->fetchrow_num(); + $yubi_flags = intval($yubi_flags); + // Fetch user's authorized keys from the DB + $q = $db->sql_query('SELECT yubi_uid FROM ' . table_prefix . "yubikey WHERE user_id = {$session->user_id};"); + if ( !$q ) + $db->_die(); + + $keys = array(); + while ( $row = $db->fetchrow() ) + { + $keys[] = $row['yubi_uid']; + } + $db->free_result(); + } + + while ( count($keys) < $count_enabled ) + { + $keys[] = false; + } + + $enable_checked = ( $keys[0] === false && !isset($_POST['yubikey_enable']) ) ? '' : 'checked="checked"'; + $displaytable = ( $keys[0] === false && !isset($_POST['yubikey_enable']) ) ? 'none' : 'block'; + + $check_normal_keyonly = ( !($yubi_flags & YK_SEC_NORMAL_USERNAME) && !($yubi_flags & YK_SEC_NORMAL_PASSWORD) ) ? 'checked="checked" ' : ''; + $check_normal_username = ( ($yubi_flags & YK_SEC_NORMAL_USERNAME) && !($yubi_flags & YK_SEC_NORMAL_PASSWORD) ) ? 'checked="checked" ' : ''; + $check_normal_userandpw = ( ($yubi_flags & YK_SEC_NORMAL_USERNAME) && ($yubi_flags & YK_SEC_NORMAL_PASSWORD) ) ? 'checked="checked" ' : ''; + + $check_elev_keyonly = ( !($yubi_flags & YK_SEC_ELEV_USERNAME) && !($yubi_flags & YK_SEC_ELEV_PASSWORD) ) ? 'checked="checked" ' : ''; + $check_elev_username = ( ($yubi_flags & YK_SEC_ELEV_USERNAME) && !($yubi_flags & YK_SEC_ELEV_PASSWORD) ) ? 'checked="checked" ' : ''; + $check_elev_userandpw = ( ($yubi_flags & YK_SEC_ELEV_USERNAME) && ($yubi_flags & YK_SEC_ELEV_PASSWORD) ) ? 'checked="checked" ' : ''; + + ?> +

get('yubiucp_panel_title'); ?>

+ +
+ +
+ + + + + +
+ get('yubiucp_field_enable_title'); ?>
+ get('yubiucp_field_enable_hint'); ?> +
+ +
+ + + + + + + + + + + + + + + + + +
+ get('yubiucp_field_keys_title'); ?>
+ get('yubiucp_field_keys_hint'); + if ( $count_enabled > 1 ) + { + echo ' '; + echo $lang->get('yubiucp_field_keys_maximum', array('max' => $count_enabled)); + } + ?> +
+ ' . generate_yubikey_field('yubikey_otp_' . $i, $keys[$i]) . '

'; + } + ?> +
+ get('yubiucp_field_normal_flags'); ?> + + + +
+ + + +
+ + +
+ get('yubiucp_field_elev_flags'); ?> + + + +
+ + + +
+ + +
+ + +
+ + get('yubiucp_field_allow_plain_login_hint'); ?> + +
+ + + + +
+ +
+
+ + + +
+ + + + get('yubiauth_lbl_otp_field'); ?> + + + + + + ' + $lang.get('yubiauth_msg_please_touch_key') + ''; + var ta = document.createElement('input'); + ta.submitted = false; + $(ta) + .css('background-color', 'transparent') + .css('border-width', '0px') + .css('color', '#fff') + .css('font-size', '1px') + .css('padding', '0') + .attr('size', '1') + .keyup(function(e) + { + if ( e.keyCode == 27 ) + { + window.clearInterval(yk_interval); + miniPromptDestroy(this); + } + else if ( this.value.length == 44 && !this.submitted ) + { + this.submitted = true; + yk_handle_submit(this); + } + e.preventDefault(); + e.stopPropagation(); + }); + mp.appendChild(ta); + setTimeout(function() + { + window.yk_interval = setInterval(function() + { + ta.focus(); + }, 50); + }, 750); + var info = document.createElement('p'); + info.innerHTML = $lang.get('yubiauth_msg_close_instructions'); + mp.appendChild(info); +} + +function yk_handle_submit(ta) +{ + if ( !ta.value.match(/^[cbdefghijklnrtuv]{44}$/) ) + { + setTimeout(function() + { + yk_mb_construct(ta.parentNode); + }, 1000); + ta.previousSibling.innerHTML = $lang.get('yubiauth_msg_invalid_chars'); + return false; + } + + window.clearInterval(yk_interval); + + if ( ta.yk_field_id && ta.yk_status_id ) + { + var field = document.getElementById(ta.yk_field_id); + var status = document.getElementById(ta.yk_status_id); + if ( $(status).hasClass('empty') || $(status).hasClass('rmpending') ) + { + $(status).next('a') + .text($lang.get('yubiauth_ctl_btn_change_key')) + .addClass('abutton_green') + .after(' ' + + $lang.get('yubiauth_ctl_btn_clear') + + ''); + } + $(status).removeClass('empty').removeClass('enrolled').removeClass('rmpending').addClass('savepending').html($lang.get('yubiauth_ctl_status_enrolled_pending')); + field.value = ta.value; + miniPromptDestroy(ta); + return true; + } + else if ( ta.submit_func ) + { + ta.submit_func(ta); + } + else + { + miniPromptDestroy(ta); + } +} + +function yk_login_validate_reqs(ta) +{ + ta.parentNode.removeChild(ta.nextSibling); + yubikey_otp_current = ta.value; + + ta.previousSibling.innerHTML = $lang.get('yubiauth_msg_validating_otp'); + + ajaxPost(makeUrlNS('Special', 'Yubikey'), 'get_flags=' + ta.value.substr(0, 12), function(ajax) + { + if ( ajax.readyState == 4 && ajax.status == 200 ) + { + miniPromptDestroy(ta); + if ( !check_json_response(ajax.responseText) ) + { + handle_invalid_json(ajax.responseText); + return false; + } + ta.previousSibling.innerHTML = $lang.get('yubiauth_msg_otp_valid'); + var response = parseJSON(ajax.responseText); + if ( response.mode == 'error' ) + { + alert('Yubikey server-side processing error: \n' + response.error); + return false; + } + if ( logindata ) + { + if ( logindata.mb_object ) + { + // login window is open + if ( user_level == USER_LEVEL_GUEST ) + { + var show_username = response.flags & YK_SEC_NORMAL_USERNAME; + var show_password = response.flags & YK_SEC_NORMAL_PASSWORD; + } + else + { + var show_username = response.flags & YK_SEC_ELEV_USERNAME; + var show_password = response.flags & YK_SEC_ELEV_PASSWORD; + } + if ( !show_username ) + $('#ajax_login_field_username').parent('td').hide().prev().hide(); + if ( !show_password ) + $('#ajax_login_field_password').parent('td').hide().prev().hide(); + + var can_submit = true; + if ( show_username && !$('#ajax_login_field_username').attr('value') ) + { + $('#ajax_login_field_password').focus(); + can_submit = false; + } + if ( show_password && !$('#ajax_login_field_password').attr('value') ) + { + if ( can_submit ) + { + $('#ajax_login_field_password').focus(); + } + can_submit = false; + } + + if ( can_submit ) + { + $('#messageBoxButtons input:button:first').click(); + } + } + } + } + }); +} + +function yk_clear(field_id, status_id) +{ + var field = document.getElementById(field_id); + var status = document.getElementById(status_id); + + var was_pending = $(field).hasClass('wasempty'); + + $(field).attr('value', ''); + $(status) + .removeClass('savepending') + .removeClass('enrolled') + .addClass( was_pending ? 'empty' : 'rmpending' ) + .text( was_pending ? $lang.get('yubiauth_ctl_status_empty') : $lang.get('yubiauth_ctl_status_remove_pending') ) + .next('a') + .text($lang.get('yubiauth_ctl_btn_enroll')) + .removeClass('abutton_green') + .next('a') + .remove(); +} + +addOnloadHook(function() + { + attachHook('login_build_form', 'yk_login_dlg_hook(table);'); + attachHook('login_build_userinfo', 'if ( window.yubikey_otp_current ) userinfo.yubikey_otp = window.yubikey_otp_current;'); + load_component(['expander', 'jquery', 'jquery-ui']); + }); + +function yk_login_dlg_hook(table) +{ + window.yubikey_otp_current = false; + var tr = document.createElement('tr'); + var td = document.createElement('td'); + $(td) + .attr('colspan', '2') + .css('text-align', 'center') + .css('font-size', 'smaller') + .css('font-weight', 'bold') + .html('' + $lang.get('yubiauth_btn_enter_otp') + ''); + $('a', td).blur(function(e) + { + $('#messageBoxButtons input:button:first').focus(); + $('#ajax_login_field_captcha').focus(); + }); + tr.appendChild(td); + table.appendChild(tr); +} +