php_required, '<' )) {
add_action('all_admin_notices', array($this, 'admin_notice_insufficient_php'));
$abort = true;
}
if (!function_exists('mcrypt_get_iv_size')) {
add_action('all_admin_notices', array($this, 'admin_notice_missing_mcrypt'));
$abort = true;
}
if (!empty($abort)) return;
if (file_exists(SIMBA_TFA_PLUGIN_DIR.'/premium.php')) include_once(SIMBA_TFA_PLUGIN_DIR.'/premium.php');
add_action('wp_ajax_nopriv_simbatfa-init-otp', array($this, 'tfaInitLogin'));
add_action('wp_ajax_simbatfa_shared_ajax', array($this, 'shared_ajax'));
add_action('woocommerce_before_customer_login_form', array($this, 'woocommerce_before_customer_login_form'));
// The login form on the checkout doesn't call the woocommerce_before_customer_login_form action
add_action('woocommerce_before_checkout_form', array($this, 'woocommerce_before_customer_login_form'));
if (is_admin()) {
//Save settings
add_action('admin_init', array($this, 'check_possible_reset'));
//Add to Settings menu on sites
add_action('admin_menu', array($this, 'menu_entry_for_admin'));
//Add settings link in plugin list
$plugin = plugin_basename(__FILE__);
add_filter("plugin_action_links_".$plugin, array($this, 'addPluginSettingsLink' ));
add_filter('network_admin_plugin_action_links_'.$plugin, array($this, 'addPluginSettingsLink' ));
// Entry that everybody gets
add_action('network_admin_menu', array($this, 'admin_menu'));
add_action('admin_menu', array($this, 'admin_menu'));
} else {
add_action('init', array($this, 'check_possible_reset'));
}
add_action('plugins_loaded', array($this, 'plugins_loaded'));
add_action('init', array($this, 'init'));
//Show off sync message for hotp
add_action('admin_notices', array($this, 'tfaShowHOTPOffSyncMessage'));
add_action('login_enqueue_scripts', array($this, 'login_enqueue_scripts'));
if (!defined('TWO_FACTOR_DISABLE') || !TWO_FACTOR_DISABLE) {
add_filter('authenticate', array($this, 'tfaVerifyCodeAndUser'), 99999999999, 3);
}
if (file_exists(SIMBA_TFA_PLUGIN_DIR.'/updater/updater.php')) include_once(SIMBA_TFA_PLUGIN_DIR.'/updater/updater.php');
if (defined('DOING_AJAX') && DOING_AJAX && defined('WP_ADMIN') && WP_ADMIN && !empty($_REQUEST['action']) && 'simbatfa-init-otp' == $_REQUEST['action']) {
// Try to prevent PHP notices breaking the AJAX conversation
$this->output_buffering = true;
$this->logged = array();
set_error_handler(array($this, 'get_php_errors'), E_ALL & ~E_STRICT);
ob_start();
}
}
public function get_php_errors($errno, $errstr, $errfile, $errline) {
if (0 == error_reporting()) return true;
$logline = $this->php_error_to_logline($errno, $errstr, $errfile, $errline);
$this->logged[] = $logline;
# Don't pass it up the chain (since it's going to be output to the user always)
return true;
}
public function php_error_to_logline($errno, $errstr, $errfile, $errline) {
switch ($errno) {
case 1: $e_type = 'E_ERROR'; break;
case 2: $e_type = 'E_WARNING'; break;
case 4: $e_type = 'E_PARSE'; break;
case 8: $e_type = 'E_NOTICE'; break;
case 16: $e_type = 'E_CORE_ERROR'; break;
case 32: $e_type = 'E_CORE_WARNING'; break;
case 64: $e_type = 'E_COMPILE_ERROR'; break;
case 128: $e_type = 'E_COMPILE_WARNING'; break;
case 256: $e_type = 'E_USER_ERROR'; break;
case 512: $e_type = 'E_USER_WARNING'; break;
case 1024: $e_type = 'E_USER_NOTICE'; break;
case 2048: $e_type = 'E_STRICT'; break;
case 4096: $e_type = 'E_RECOVERABLE_ERROR'; break;
case 8192: $e_type = 'E_DEPRECATED'; break;
case 16384: $e_type = 'E_USER_DEPRECATED'; break;
case 30719: $e_type = 'E_ALL'; break;
default: $e_type = "E_UNKNOWN ($errno)"; break;
}
if (!is_string($errstr)) $errstr = serialize($errstr);
if (0 === strpos($errfile, ABSPATH)) $errfile = substr($errfile, strlen(ABSPATH));
return "PHP event: code $e_type: $errstr (line $errline, $errfile)";
}
public function init() {
if ((!is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) && is_user_logged_in() && file_exists(SIMBA_TFA_PLUGIN_DIR.'/includes/tfa_frontend.php')) {
$this->load_frontend();
} else {
add_shortcode('twofactor_user_settings', array($this, 'shortcode_when_not_logged_in'));
}
}
public function admin_notice_insufficient_php() {
$this->show_admin_warning(''.__('Higher PHP version required', 'updraftplus').' '.sprintf(__('The Two Factor Authentication plugin requires PHP version %s or higher - your current version is only %s.', SIMBA_TFA_TEXT_DOMAIN), $this->php_required, PHP_VERSION), 'error');
}
public function admin_notice_missing_mcrypt() {
$this->show_admin_warning(''.__('PHP Mcrypt module required', 'updraftplus').' '.__('The Two Factor Authentication plugin requires the PHP mcrypt module to be installed. Please ask your web hosting company to install it.', SIMBA_TFA_TEXT_DOMAIN), 'error');
}
public function show_admin_warning($message, $class = "updated") {
echo '
'."
$message
";
}
public function getTFA() {
if (!class_exists('HOTP')) require_once(SIMBA_TFA_PLUGIN_DIR.'/hotp-php-master/hotp.php');
if (!class_exists('Base32')) require_once(SIMBA_TFA_PLUGIN_DIR.'/Base32/Base32.php');
if (!class_exists('Simba_TFA')) require_once(SIMBA_TFA_PLUGIN_DIR.'/includes/class.TFA.php');
$tfa = new Simba_TFA(new Base32(), new HOTP());
return $tfa;
}
// "Shared" - i.e. could be called from either front-end or back-end
public function shared_ajax() {
if (empty($_POST['subaction']) || empty($_POST['nonce']) || !is_user_logged_in() || !wp_verify_nonce($_POST['nonce'], 'tfa_shared_nonce')) die('Security check (3).');
if ($_POST['subaction'] == 'refreshotp') {
global $current_user;
$tfa_priv_key_64 = get_user_meta($current_user->ID, 'tfa_priv_key_64', true);
if (!$tfa_priv_key_64) {
echo json_encode(array('code' => ''));
die;
}
echo json_encode(array('code' => $this->getTFA()->generateOTP($current_user->ID, $tfa_priv_key_64)));
exit;
}
}
public function tfaInitLogin() {
if (empty($_POST['user'])) die('Security check (2).');
if (defined('TWO_FACTOR_DISABLE') && TWO_FACTOR_DISABLE) {
$res = false;
} else {
$tfa = $this->getTFA();
$res = $tfa->preAuth(array('log' => (string)$_POST['user']));
}
$results = array('jsonstarter' => 'justhere', 'status' => $res);
if (!empty($this->output_buffering)) {
if (!empty($this->logged)) {
$results['php_output'] = $this->logged;
}
restore_error_handler();
$buffered = ob_get_clean();
if ($buffered) $results['extra_output'] = $buffered;
}
echo json_encode($results);
exit;
}
// Here's where the login action happens. Called on the 'authenticate' action.
public function tfaVerifyCodeAndUser($user, $username, $password) {
$tfa = $this->getTFA();
if (is_wp_error($user)) return $user;
$params = $_POST;
$params['log'] = $username;
$params['caller'] = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['REQUEST_URI'];
$code_ok = $tfa->authUserFromLogin($params);
if (is_wp_error($code_ok)) return $code_ok;
if (!$code_ok) return new WP_Error('authentication_failed', ''.__('Error:', SIMBA_TFA_TEXT_DOMAIN).' '.__('The one-time password (TFA code) you entered was incorrect.', SIMBA_TFA_TEXT_DOMAIN));
if ($user) return $user;
return wp_authenticate_username_password(null, $username, $password);
}
public function tfaRegisterTwoFactorAuthSettings()
{
global $wp_roles;
if (!isset($wp_roles))
$wp_roles = new WP_Roles();
foreach($wp_roles->role_names as $id => $name)
{
register_setting('tfa_user_roles_group', 'tfa_'.$id);
register_setting('tfa_user_roles_required_group', 'tfa_required_'.$id);
}
register_setting('tfa_user_roles_required_group', 'tfa_requireafter');
register_setting('simba_tfa_default_hmac_group', 'tfa_default_hmac');
register_setting('tfa_xmlrpc_status_group', 'tfa_xmlrpc_on');
}
public function tfaListEnableRadios($user_id, $long_label = false)
{
if(!$user_id)
return;
$setting = get_user_meta($user_id, 'tfa_enable_tfa', true);
$setting = !$setting ? false : $setting;
$tfa = $this->getTFA();
if ($tfa->isRequiredForUser($user_id)) {
$requireafter = absint($this->get_option('tfa_requireafter'));
echo '
'.sprintf(__('N.B. This site is configured to forbid you to log in if you disable two-factor authentication after your account is %d days old', SIMBA_TFA_TEXT_DOMAIN), $requireafter).'
';
}
$tfa_enabled_label = ($long_label) ? __('Enable two-factor authentication', SIMBA_TFA_TEXT_DOMAIN) : __('Enabled', SIMBA_TFA_TEXT_DOMAIN);
$tfa_disabled_label = ($long_label) ? __('Disable two-factor authentication', SIMBA_TFA_TEXT_DOMAIN) : __('Disabled', SIMBA_TFA_TEXT_DOMAIN);
print ' ';
print ' ';
}
public function tfaListAlgorithmRadios($user_id)
{
if(!$user_id) return;
$types = array('totp' => __('TOTP (time based - most common algorithm; used by Google Authenticator)', SIMBA_TFA_TEXT_DOMAIN), 'hotp' => __('HOTP (event based)', SIMBA_TFA_TEXT_DOMAIN));
$setting = get_user_meta($user_id, 'tfa_algorithm_type', true);
$setting = $setting === false || !$setting ? 'totp' : $setting;
foreach($types as $id => $name) {
print ' \n";
}
}
public function get_option($key) {
if (!is_multisite()) return get_option($key);
switch_to_blog(1);
$v = get_option($key);
restore_current_blog();
return $v;
}
public function tfaListUserRolesCheckboxes()
{
if (is_multisite()) {
// Not a real WP role; needs separate handling
$id = '_super_admin';
$name = __('Multisite Super Admin', SIMBA_TFA_TEXT_DOMAIN);
$setting = $this->get_option('tfa_'.$id);
$setting = $setting === false || $setting ? 1 : 0;
print ' \n";
}
global $wp_roles;
if (!isset($wp_roles)) $wp_roles = new WP_Roles();
foreach($wp_roles->role_names as $id => $name)
{
$setting = $this->get_option('tfa_'.$id);
$setting = $setting === false || $setting ? 1 : 0;
print ' \n";
}
}
public function tfaListDefaultHMACRadios()
{
$tfa = $this->getTFA();
$setting = $this->get_option('tfa_default_hmac');
$setting = $setting === false || !$setting ? $tfa->default_hmac : $setting;
$types = array('totp' => __('TOTP (time based - most common algorithm; used by Google Authenticator)', SIMBA_TFA_TEXT_DOMAIN), 'hotp' => __('HOTP (event based)', SIMBA_TFA_TEXT_DOMAIN));
foreach($types as $id => $name)
print ' '.' \n";
}
public function tfaListXMLRPCStatusRadios()
{
$tfa = $this->getTFA();
$setting = $this->get_option('tfa_xmlrpc_on');
$setting = $setting === false || !$setting ? 0 : 1;
$types = array(
'0' => __('Do not require 2FA over XMLRPC (best option if you must use XMLRPC and your client does not support 2FA)', SIMBA_TFA_TEXT_DOMAIN),
'1' => __('Do require 2FA over XMLRPC (best option if you do not use XMLRPC or are unsure)', SIMBA_TFA_TEXT_DOMAIN)
);
foreach($types as $id => $name)
print ' \n";
}
public function tfaShowAdminSettingsPage()
{
$tfa = $this->getTFA();
require_once(SIMBA_TFA_PLUGIN_DIR.'/includes/admin_settings.php');
}
public function tfaShowUserSettingsPage()
{
$tfa = $this->getTFA();
include SIMBA_TFA_PLUGIN_DIR.'/includes/user_settings.php';
}
public function admin_menu()
{
$tfa = $this->getTFA();
global $current_user;
if(!$tfa->isActivatedForUser($current_user->ID)) return;
add_menu_page(__('Two Factor Authentication', SIMBA_TFA_TEXT_DOMAIN), __('Two Factor Auth', SIMBA_TFA_TEXT_DOMAIN), 'read', 'two-factor-auth-user', array($this, 'tfaShowUserSettingsPage'), SIMBA_TFA_PLUGIN_URL.'/img/tfa_admin_icon_16x16.png', 72);
}
public function menu_entry_for_admin() {
// On multisite, only show the entry on site ID 1 - to ensure options get saved in the right place.
global $current_site, $wpdb;
// $current_site is not the right way to do this - it is internal, and could be anything
if (is_multisite() && (!is_super_admin() || !is_object($wpdb) || !isset($wpdb->blogid) || 1 != $wpdb->blogid)) return;
add_action( 'admin_init', array($this, 'tfaRegisterTwoFactorAuthSettings' ));
add_options_page(
__('Two Factor Authentication', SIMBA_TFA_TEXT_DOMAIN),
__('Two Factor Authentication', SIMBA_TFA_TEXT_DOMAIN),
'manage_options',
'two-factor-auth',
array($this, 'tfaShowAdminSettingsPage')
);
}
public function addPluginSettingsLink($links)
{
if (!is_network_admin()) {
$link = ''.__('Plugin settings', SIMBA_TFA_TEXT_DOMAIN).'';
array_unshift($links, $link);
} else {
switch_to_blog(1);
$link = ''.__('Plugin settings', SIMBA_TFA_TEXT_DOMAIN).'';
restore_current_blog();
array_unshift($links, $link);
}
$link2 = ''.__('User settings', SIMBA_TFA_TEXT_DOMAIN).'';
array_unshift($links, $link2);
return $links;
}
public function check_possible_reset() {
if(!empty($_GET['simbatfa_priv_key_reset']) && !empty($_REQUEST['nonce']) && wp_verify_nonce($_REQUEST['nonce'], 'simbatfa_reset_private_key'))
{
$this->reset_private_key_and_emergency_codes();
// if (empty($_REQUEST['noredirect'])) exit;
exit;
}
}
public function reset_private_key_and_emergency_codes() {
global $current_user;
delete_user_meta($current_user->ID, 'tfa_priv_key_64');
delete_user_meta($current_user->ID, 'simba_tfa_emergency_codes_64');
if (empty($_REQUEST['noredirect'])) {
wp_safe_redirect( admin_url('admin.php').'?page=two-factor-auth-user&settings-updated=1');
} else {
$url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . remove_query_arg(array('simbatfa_priv_key_reset', 'noredirect', 'nonce'));
wp_redirect(esc_url_raw($url));
}
}
public function reset_link($admin = true) {
$url_base = ($admin) ? admin_url('admin.php').'?page=two-factor-auth-user&settings-updated=1' : (( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST']);
$add_query_args = array(
'simbatfa_priv_key_reset' => 1,
);
if (!$admin) $add_query_args['noredirect'] = 1;
$url = $url_base.add_query_arg($add_query_args);
$url = wp_nonce_url($url, 'simbatfa_reset_private_key', 'nonce');
return ''.__('Reset private key', SIMBA_TFA_TEXT_DOMAIN).'';
}
public function footer() {
$ajax_url = admin_url('admin-ajax.php');
// It's possible that FORCE_ADMIN_SSL will make that SSL, whilst the user is on the front-end having logged in over non-SSL - and as a result, their login cookies won't get sent, and they're not registered as logged in.
if (!is_admin() && substr(strtolower($ajax_url), 0, 6) == 'https:' && !is_ssl()) {
$also_try = 'http:'.substr($ajax_url, 6);
}
?>
getTFA();
global $current_user;
if ($user_id == false) $user_id = $current_user->ID;
$tfa_priv_key_64 = get_user_meta($user_id, 'tfa_priv_key_64', true);
if(!$tfa_priv_key_64) $tfa_priv_key_64 = $tfa->addPrivateKey($user_id);
$tfa_priv_key = trim($tfa->getPrivateKeyPlain($tfa_priv_key_64, $user_id));
$tfa_priv_key_32 = Base32::encode($tfa_priv_key);
if ('full' == $type) {
?>
'.__('One-time emergency codes are a feature of the Premium version of this plugin.', SIMBA_TFA_TEXT_DOMAIN).'';
echo apply_filters('simba_tfa_emergency_codes_user_settings', $default_text, $user_id);
?>
'.__('(update)', SIMBA_TFA_TEXT_DOMAIN).'';
}
public function advanced_settings_box($submit_button_callback = false) {
$tfa = $this->getTFA();
global $current_user;
$algorithm_type = $tfa->getUserAlgorithm($current_user->ID);
?>
version;
wp_enqueue_script( 'tfa-ajax-request', SIMBA_TFA_PLUGIN_URL . '/includes/tfa.js', array( 'jquery' ), $script_ver );
$localize = array(
'ajaxurl' => admin_url('admin-ajax.php'),
'click_to_enter_otp' => __("Click to enter One Time Password", SIMBA_TFA_TEXT_DOMAIN),
'enter_username_first' => __('You have to enter a username first.', SIMBA_TFA_TEXT_DOMAIN),
'otp' => __("One Time Password (i.e. 2FA)", SIMBA_TFA_TEXT_DOMAIN),
'otp_login_help' => __('(check your OTP app to get this password)', SIMBA_TFA_TEXT_DOMAIN),
'nonce' => wp_create_nonce("simba_tfa_loginform_nonce")
);
// Spinner exists since WC 3.8. Use the proper functions to avoid SSL warnings.
if (file_exists(ABSPATH.'wp-admin/images/spinner.gif')) {
$localize['spinnerimg'] = admin_url('images/spinner.gif');
} elseif (file_exists(ABSPATH.WPINC.'/images/spinner.gif')) {
$localize['spinnerimg'] = includes_url('images/spinner.gif');
}
wp_localize_script( 'tfa-ajax-request', 'simba_tfasettings', $localize);
}
public function tfaShowHOTPOffSyncMessage()
{
global $current_user;
$is_off_sync = get_user_meta($current_user->ID, 'tfa_hotp_off_sync', true);
if(!$is_off_sync)
return;
?>
getTFA();
// Old
// $encode = 'otpauth://'.$algorithm_type.'/'.$url.':%2520'.$user->user_login.'%3Fsecret%3D'.Base32::encode($tfa_priv_key).'%26issuer='.$url.'%26counter='.$tfa->getUserCounter($user->ID);
//
// $ret = '';
// New
$encode = 'otpauth://'.$algorithm_type.'/'.$url.':'.$user->user_login.'?secret='.Base32::encode($tfa_priv_key).'&issuer='.$url.'&counter='.$tfa->getUserCounter($user->ID);
// $ret = '';
return $encode;
}
public function settings_intro_notices() {
?>
frontend)) $this->frontend = new TFA_Frontend($this);
return $this->frontend;
}
public function shortcode_when_not_logged_in() {
return '';
}
// WooCommerce login form
public function woocommerce_before_customer_login_form() {
$script_ver = (defined('WP_DEBUG') && WP_DEBUG) ? time() : $this->version;
wp_enqueue_script( 'tfa-wc-ajax-request', SIMBA_TFA_PLUGIN_URL.'/includes/wooextend.js', array('jquery'), $script_ver);
$localize = array(
'ajaxurl' => admin_url('admin-ajax.php'),
'click_to_enter_otp' => __("Enter One Time Password (if you have one)", SIMBA_TFA_TEXT_DOMAIN),
'enter_username_first' => __('You have to enter a username first.', SIMBA_TFA_TEXT_DOMAIN),
'otp' => __("One Time Password", SIMBA_TFA_TEXT_DOMAIN),
'nonce' => wp_create_nonce("simba_tfa_loginform_nonce"),
'otp_login_help' => __('(check your OTP app to get this password)', SIMBA_TFA_TEXT_DOMAIN),
);
// Spinner exists since WC 3.8. Use the proper functions to avoid SSL warnings.
if (file_exists(ABSPATH.'wp-admin/images/spinner.gif')) {
$localize['spinnerimg'] = admin_url('images/spinner.gif');
} elseif (file_exists(ABSPATH.WPINC.'/images/spinner.gif')) {
$localize['spinnerimg'] = includes_url('images/spinner.gif');
}
wp_localize_script( 'tfa-wc-ajax-request', 'simbatfa_wc_settings', $localize);
}
}
$simba_two_factor_authentication = new Simba_Two_Factor_Authentication();