# HG changeset patch # User Dan # Date 1249282379 14400 # Node ID 9fdc988ce46e4a4dcec541d9872094cd45c93496 # Parent 6edb31919f0ea1b54ff492974668797dac6cfb6b Added counter and ANY_CLIENT settings to ShowAESKey; Significant improvements to claim system: Added master switch for the whole system; Added ability for administrators to "su" to client ID 0 to manage pooled keys; Added ability for admins to release key when it is added diff -r 6edb31919f0e -r 9fdc988ce46e YubikeyManagement.php --- a/YubikeyManagement.php Sat Aug 01 01:42:21 2009 -0400 +++ b/YubikeyManagement.php Mon Aug 03 02:52:59 2009 -0400 @@ -90,8 +90,11 @@ Validation API URL: %validate_url%

Remember to secure your user account! Your Enano login is used to administer your YMS account. For maximum security, use the Yubikey Settings page of the User Control Panel to require both a password and a Yubikey OTP to log in.

', msg_no_yubikeys: 'No Yubikeys found', + msg_editing_zero: 'Notice: You are currently viewing the YMS profile for Client ID 0, the pool of claimable keys. By default, anybody can validate or claim these Yubikeys, but you can prevent validation of these keys by marking them inactive here. All key settings such as lifecycle state and notes are reset when a user claims a key here.', btn_add_key: 'Add Yubikey', btn_add_key_preregistered: 'Claim a New Key', + btn_switch_to_zero: 'Edit claimable pool', + btn_switch_from_zero: 'Switch back to my client', state_active: 'Active', state_inactive: 'Inactive', @@ -115,6 +118,9 @@ lbl_addkey_field_any_client_name: 'Allow validation by any client:', lbl_addkey_field_any_client_hint: 'If unchecked, OTPs from this Yubikey can only be verified by someone using your client ID. Check this if you plan to use this Yubikey on websites you don\'t control.', lbl_addkey_field_any_client: 'Other clients can validate OTPs from this key', + lbl_addkey_field_allow_claim_name: 'Place key in claimable pool:', + lbl_addkey_field_allow_claim_hint: 'After this key is added, YMS will release your ownership of this key so that other users may claim it.', + lbl_addkey_field_allow_claim: 'Release this key and allow others to claim it', btn_addkey_submit: 'Register key', msg_addkey_success: 'This key has been successfully registered.', @@ -130,10 +136,19 @@ lbl_custom_hint: 'For your security, this is used to validate your ownership of this Yubikey.', // AES key view interface + showaes_heading_main: 'View AES key and counters', showaes_th: 'AES secret key for key %public_id%', showaes_lbl_hex: 'Hex:', showaes_lbl_modhex: 'ModHex:', showaes_lbl_base64: 'Base64:', + showaes_th_counter: 'Counters', + showaes_field_session_count: 'Session count:', + showaes_field_session_count_hint: 'Incremented by 1 each time you insert this Yubikey into a USB port.', + showaes_field_otp_count: 'OTP count:', + showaes_field_otp_count_hint: 'Incremented by 1 each time you press the button on the Yubikey; reset when the Yubikey is plugged in.', + + err_expected_int: 'Expected an integer', + msg_counter_update_success: 'The counters for this Yubikey have been updated.', // API key view interface th_client_id: 'Client ID', @@ -161,7 +176,7 @@ btn_note_view: 'View or edit note', btn_note_create: 'No note; click to create', btn_delete_key: 'Delete key', - btn_show_aes: 'Show AES secret', + btn_show_aes: 'AES secret and counter information', btn_show_converter: 'Binary encoding converter', btn_show_client_info: 'View client info', @@ -172,6 +187,11 @@ acp_field_require_reauth_title: 'Require re-authentication to access YMS interface:', acp_field_require_reauth_hint: 'This can be redundant and unnecessary if the sole purpose of your Enano installation is for YMS purposes.', acp_field_require_reauth: 'YMS pages require re-authentication', + acp_field_claim_enable_title: 'Allow users to claim Yubikeys:', + acp_field_claim_enable_hint: 'If you plan to program your own Yubikeys and give them to others, enable this to allow them to create YMS accounts and "claim" the keys so they can see AES secrets and control settings on their keys.
+ If you enable this, all Administrators will see an option when adding a new key to put it into the pool of unclaimed keys.
+ To claim a Yubikey, YMS requires users to enter a valid OTP, and optionally, an additional field you may configure below.', + acp_field_claim_enable: 'Enable the claim system', acp_field_claimauth_enable_title: 'Use external authentication when claiming Yubikeys:', acp_field_claimauth_enable_hint: 'This allows you to require an additional value - for example, the receipt number from the user\'s Yubikey order - when Yubikeys are claimed.', acp_field_claimauth_enable: 'Require additional field to claim a Yubikey', diff -r 6edb31919f0e -r 9fdc988ce46e yms/admincp.php --- a/yms/admincp.php Sat Aug 01 01:42:21 2009 -0400 +++ b/yms/admincp.php Mon Aug 03 02:52:59 2009 -0400 @@ -21,6 +21,7 @@ if ( isset($_POST['submit']) ) { setConfig('yms_require_reauth', isset($_POST['require_reauth']) ? '1' : '0'); + setConfig('yms_claim_enable', isset($_POST['claim_enable']) ? '1' : '0'); setConfig('yms_claim_auth_enable', isset($_POST['claimauth_enable']) ? '1' : '0'); setConfig('yms_claim_auth_field', $_POST['claimauth_field']); setConfig('yms_claim_auth_url', $_POST['claimauth_url']); @@ -55,6 +56,19 @@ + get('yms_acp_field_claim_enable_title'); ?>
+ get('yms_acp_field_claim_enable_hint'); ?> + + + + + + + + get('yms_acp_field_claimauth_enable_title'); ?>
get('yms_acp_field_claimauth_enable_hint'); ?> diff -r 6edb31919f0e -r 9fdc988ce46e yms/backend.php --- a/yms/backend.php Sat Aug 01 01:42:21 2009 -0400 +++ b/yms/backend.php Mon Aug 03 02:52:59 2009 -0400 @@ -5,7 +5,7 @@ global $db, $session, $paths, $template, $plugins; // Common objects if ( $client_id === false ) - $client_id = $session->user_id; + $client_id = $GLOBALS['yms_client_id']; $key = yms_tobinary($key); $otp = yms_tobinary($otp); @@ -66,7 +66,7 @@ global $db, $session, $paths, $template, $plugins; // Common objects if ( $client_id === false ) - $client_id = $session->user_id; + $client_id = $GLOBALS['yms_client_id']; $otp = yms_tobinary($otp); @@ -114,7 +114,7 @@ global $db, $session, $paths, $template, $plugins; // Common objects if ( $client_id === false ) - $client_id = $session->user_id; + $client_id = $GLOBALS['yms_client_id']; $q = $db->sql_query('SELECT 1 FROM ' . table_prefix . "yms_yubikeys WHERE id = $id AND client_id = $client_id;"); if ( !$q ) @@ -176,6 +176,31 @@ return true; } +function yms_update_counters($id, $scount, $tcount, $client_id = false, $any_client = null) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( !$client_id ) + $client_id = intval($GLOBALS['yms_client_id']); + + foreach ( array($id, $scount, $tcount, $client_id) as $var ) + if ( (!is_int($var) && !is_string($var)) || (is_string($var) && !ctype_digit($var)) ) + return "yms_err_expected_int"; + + $any_client_sql = ''; + if ( is_bool($any_client) ) + { + $operand = $any_client ? "|" : "& ~"; + $any_client_sql = ", flags = flags " . $operand . YMS_ANY_CLIENT; + } + + $q = $db->sql_query('UPDATE ' . table_prefix . "yms_yubikeys SET session_count = {$scount}, token_count = {$tcount}{$any_client_sql} WHERE id = $id AND client_id = $client_id"); + if ( !$q ) + $db->_die(); + + return true; +} + function yms_get_url($url) { require_once(ENANO_ROOT . '/includes/http.php'); @@ -291,10 +316,10 @@ { return 'NO_SUCH_KEY'; } - if ( !($flags & YMS_ENABLED) ) - { - return 'NO_SUCH_KEY'; - } + } + if ( !($flags & YMS_ENABLED) ) + { + return 'NO_SUCH_KEY'; } // decode the OTP diff -r 6edb31919f0e -r 9fdc988ce46e yms/yms.php --- a/yms/yms.php Sat Aug 01 01:42:21 2009 -0400 +++ b/yms/yms.php Mon Aug 03 02:52:59 2009 -0400 @@ -5,6 +5,9 @@ global $db, $session, $paths, $template, $plugins; // Common objects global $lang; global $output; + global $yms_client_id; + + $yms_client_id = $session->user_id; // Require re-auth? if ( $session->auth_level < USER_LEVEL_CHPREF && getConfig('yms_require_reauth', 1) == 1 ) @@ -18,14 +21,45 @@ die_friendly($lang->get('yms_err_yubikey_plugin_missing_title'), '

' . $lang->get('yms_err_yubikey_plugin_missing_body') . '

'); } + // Client switch allowed? + if ( $session->user_level >= USER_LEVEL_ADMIN && getConfig('yms_claim_enable', 0) == 1 ) + { + $on_home = empty($_POST) && !$paths->getParam(0); + + // yes. + $configkey = "yms_zeroeditsess_{$session->user_id}"; + if ( getConfig($configkey, 0) == 1 && !isset($_GET['client_switch']) ) + { + // set to zero + $yms_client_id = 0; + } + else if ( !getConfig($configkey) && isset($_GET['client_switch']) ) + { + // set to zero + update config + $yms_client_id = 0; + setConfig($configkey, 1); + } + else if ( getConfig($configkey) && isset($_GET['client_switch']) ) + { + // turning off + setConfig($configkey, false); + } + + // display a notice + if ( $yms_client_id == 0 && $on_home ) + { + $output->add_after_header('
' . $lang->get('yms_msg_editing_zero') . '
'); + } + } + // Does the client exist? - $q = $db->sql_query('SELECT 1 FROM ' . table_prefix . "yms_clients WHERE id = {$session->user_id};"); + $q = $db->sql_query('SELECT 1 FROM ' . table_prefix . "yms_clients WHERE id = {$yms_client_id};"); if ( !$q ) $db->_die(); $client_exists = $db->numrows(); $db->free_result(); - if ( !$client_exists ) + if ( !$client_exists && $yms_client_id > 0 ) { redirect(makeUrlNS('Special', 'YMSCreateClient'), '', '', 0); } @@ -57,10 +91,20 @@ $enabled = $_POST['state'] == 'active'; $any_client = isset($_POST['any_client']); $notes = $_POST['notes']; + + // Release key? + if ( $session->user_level >= USER_LEVEL_ADMIN && getConfig('yms_claim_enable', 0) == 1 && isset($_POST['allow_claim']) ) + { + $client_id = 0; + // also allow anyone to validate OTPs from it and mark it as active + $any_client = true; + $enabled = true; + } + $result = yms_add_yubikey($_POST['add_aes'], $_POST['add_otp'], $client_id, $enabled, $any_client, $notes); yms_send_response('yms_msg_addkey_success', $result); } - else if ( isset($_POST['claim_otp']) ) + else if ( isset($_POST['claim_otp']) && getConfig('yms_claim_enable', 0) == 1 ) { // do we need to validate a custom field? if ( ($url = getConfig('yms_claim_auth_url')) && getConfig('yms_claim_auth_field') && getConfig('yms_claim_auth_enable', 0) == 1 ) @@ -89,6 +133,15 @@ $result = yms_delete_key($id); yms_send_response('yms_msg_delete_success', $result); } + else if ( isset($_POST['update_counters']) ) + { + $yk_id = $_POST['update_counters']; + $scount = $_POST['session_count']; + $tcount = $_POST['token_count']; + $any_client = isset($_POST['any_client']); + $result = yms_update_counters($yk_id, $scount, $tcount, false, $any_client); + yms_send_response('yms_msg_counter_update_success', $result); + } if ( isset($_GET['toggle']) && isset($_GET['state']) ) { @@ -98,7 +151,7 @@ else $expr = 'flags & ~' . YMS_ENABLED; - $q = $db->sql_query('UPDATE ' . table_prefix . "yms_yubikeys SET flags = $expr WHERE id = $id AND client_id = {$session->user_id};"); + $q = $db->sql_query('UPDATE ' . table_prefix . "yms_yubikeys SET flags = $expr WHERE id = $id AND client_id = {$yms_client_id};"); if ( !$q ) $db->die_json(); } @@ -124,15 +177,17 @@ href="" onclick="yms_showpage('AddKey'); return false;"> get('yms_btn_add_key'); ?> + 0 ): ?> get('yms_btn_add_key_preregistered'); ?> + sql_query('SELECT id, public_id, session_count, create_time, access_time, flags, notes FROM ' . table_prefix . "yms_yubikeys WHERE client_id = {$session->user_id} ORDER BY id ASC;"); + $q = $db->sql_query('SELECT id, public_id, session_count, create_time, access_time, flags, notes FROM ' . table_prefix . "yms_yubikeys WHERE client_id = {$yms_client_id} ORDER BY id ASC;"); if ( !$q ) $db->_die(); @@ -203,7 +258,11 @@ get('yms_btn_show_client_info'); ?> - + + get('yms_btn_switch_from_zero') : $lang->get('yms_btn_switch_to_zero'); ?> + + free_result($q); @@ -283,6 +342,22 @@ + + + + + get('yms_lbl_addkey_field_allow_claim_name'); ?>
+ get('yms_lbl_addkey_field_allow_claim_hint'); ?> + + + + + + + @@ -314,6 +389,9 @@ global $db, $session, $paths, $template, $plugins; // Common objects global $lang, $output; + if ( getConfig('yms_claim_enable', 0) != 1 ) + die(); + $output->add_after_header('