ID ) ) { return new WP_Error( 'no-user', 'Invalid user data' ); } $cus = cerber_get_set( 'cerber_user', $user->ID ); $tfm = crb_array_get( $cus, 'tfm' ); if ( $tfm === 2 ) { return false; } $login = (string) $login; $go = false; if ( $tfm == 1 ) { $go = true; } else { $u_roles = null; if ( ! empty( $user->roles ) ) { $u_roles = $user->roles; } else { // a backup way $data = get_userdata( $user->ID ); if ( ! empty( $data->roles ) ) { $u_roles = $data->roles; } } if ( empty( $u_roles ) ) { return new WP_Error( 'no-roles', 'No roles found for the user #' . $user->ID ); } $go = self::check_role_policies( $cus, $u_roles ); } if ( ! $go ) { return false; } // This user must complete 2FA $ret = self::enforce2fa( $user, $login ); if ( is_wp_error( $ret ) ) { return $ret; } cerber_log( 400, $login, $user->ID ); wp_safe_redirect( get_home_url() ); exit; } /** * @param array $cus * @param array $roles * * @return bool */ private static function check_role_policies( $cus, $roles ) { foreach ( $roles as $role ) { $policies = cerber_get_role_policies( $role ); if ( empty( $policies['2famode'] ) ) { continue; } elseif ( $policies['2famode'] == 1 ) { return true; } if ( $history = crb_array_get( $cus, '2fa_history' ) ) { if ( ( $logins = crb_array_get( $policies, '2falogins' ) ) && ( $history[0] >= $logins ) ) { return true; } if ( ( $days = crb_array_get( $policies, '2fadays' ) ) && ( ( time() - $history[1] ) > $days * 24 * 3600 ) ) { return true; } } if ( $last_login = crb_array_get( $cus, 'last_login' ) ) { if ( crb_array_get( $policies, '2fanewip' ) ) { if ( $last_login['ip'] != cerber_get_remote_ip() ) { return true; } } if ( crb_array_get( $policies, '2fanewnet4' ) ) { if ( cerber_get_subnet_ipv4( $last_login['ip'] ) != cerber_get_subnet_ipv4( cerber_get_remote_ip() ) ) { return true; } } if ( crb_array_get( $policies, '2fanewua' ) ) { if ( $last_login['ua'] != sha1( crb_array_get( $_SERVER, 'HTTP_USER_AGENT', '' ) ) ) { return true; } } if ( crb_array_get( $policies, '2fanewcountry' ) ) { if ( lab_get_country( $last_login['ip'], false ) != lab_get_country( cerber_get_remote_ip(), false ) ) { return true; } } } } return false; } /** * Initiate 2FA process * * @param $user * @param string $login * * @return bool|string|WP_Error */ private static function enforce2fa( $user, $login = '' ) { if ( ! $pin = self::generate_pin( $user->ID ) ) { return new WP_Error( '2fa-error', 'Unable to create PIN for the user #' . $user->ID ); } $cus = cerber_get_set( 'cerber_user', $user->ID ); $cus['2fa']['login'] = $login; $cus['2fa']['to'] = cerber_2fa_get_redirect_to( $user ); $cus['2fa']['ajax'] = cerber_is_wp_ajax(); $cus['2fa']['interim'] = isset( $_REQUEST['interim-login'] ) ? 1 : 0; cerber_update_set( 'cerber_user', $cus, $user->ID ); return $pin; } /** * Generates PIN and its expiration * * @param $user_id * * @return bool|string */ private static function generate_pin( $user_id ) { $cus = cerber_get_set( 'cerber_user', $user_id ); if ( ! $cus || ! is_array( $cus ) ) { $cus = array(); } $pin = substr( str_shuffle( '1234567890' ), 0, CERBER_PIN_LENGTH ); $cus['2fa'] = array( 'pin' => $pin, 'expires' => time() + CERBER_PIN_EXPIRES * 60, 'attempts' => 0, 'ip' => cerber_get_remote_ip(), 'ua' => sha1( crb_array_get( $_SERVER, 'HTTP_USER_AGENT', '' ) ) ); if ( $ret = cerber_update_set( 'cerber_user', $cus, $user_id ) ) { self::send_user_pin( $user_id, $pin ); return $pin; } return false; } static function restrict_and_verify( $user_id = null ) { global $cerber_status; static $done = false; if ( $done ) { return; } $done = true; if ( ! $user_id && ! $user_id = get_current_user_id() ) { return; } self::$user_id = $user_id; $cus = cerber_get_set( 'cerber_user', $user_id ); if ( ! $cus || empty( $cus['2fa']['pin'] ) ) { return; } if ( crb_acl_is_white() ) { self::delete_2fa( $user_id ); return; } // Check user settings again $tfm = crb_array_get( $cus, 'tfm' ); if ( $tfm === 2 ) { self::delete_2fa( $user_id ); return; } elseif ( ! $tfm ) { $user = wp_get_current_user(); if ( ! self::check_role_policies( $cus, $user->roles ) ) { self::delete_2fa( $user->ID ); return; } } $twofactor = $cus['2fa']; // Check: must be the same browser if ( $twofactor['ip'] != cerber_get_remote_ip() || $twofactor['ua'] != sha1( crb_array_get( $_SERVER, 'HTTP_USER_AGENT', '' ) ) || ! cerber_is_ip_allowed() ) { self::delete_2fa( $user_id ); cerber_user_logout(); wp_redirect( get_home_url() ); exit; } // User wants to abort 2FA? if ( $now = cerber_get_get( 'cerber_2fa_now' ) ) { $go = null; if ( $now == 'different' ) { $go = wp_login_url( ( ! empty( $twofactor['to'] ) ) ? urldecode( $twofactor['to'] ) : '' ); } if ( $now == 'cancel' ) { $go = get_home_url(); } if ( $go ) { cerber_user_logout( 28 ); wp_redirect( $go ); exit; } } if ( $twofactor['attempts'] > 10 ) { cerber_block_add( cerber_get_remote_ip(), 721 ); cerber_user_logout(); wp_redirect( get_home_url() ); exit; } $new_pin = ''; if ( $twofactor['expires'] < time() ) { $new_pin = self::generate_pin( $user_id ); } // The first step of verification, ajax if ( cerber_is_http_post() ) { self::process_ajax( $new_pin ); } // The second, final step of verification if ( cerber_is_http_post() && ! empty( $twofactor['nonce'] ) && $_POST['cerber_tag'] === $twofactor['nonce'] && ( $pin = cerber_get_post( 'cerber_pin' ) ) && self::verify_pin( trim( $pin ) ) ) { unset( $cus['2fa'] ); $cus['2fa_history'] = array( 0, time() ); cerber_update_set( 'cerber_user', $cus, $user_id ); $cerber_status = 27; cerber_log( 5, $twofactor['login'], $user_id ); cerber_login_history( $user_id ); cerber_2fa_checker( true ); $url = ( ! empty( $twofactor['to'] ) ) ? $twofactor['to'] : get_home_url(); wp_safe_redirect( $url ); exit; } self::show_2fa_page(); exit; } static function process_ajax( $new_pin ) { if ( ( ! $nonce = cerber_get_post( 'the_2fa_nonce', '\w+' ) ) || ( ! $pin = cerber_get_post( 'cerber_verify_pin' ) ) ) { return; } $err = ''; if ( ! wp_verify_nonce( $nonce, 'crb-ajax-2fa' ) ) { $err = 'Nonce error.'; } elseif ( $new_pin) { $err = __('This verification PIN code is expired. We have just sent a new one to your email.','wp-cerber'); } elseif ( ! self::verify_pin( trim( $pin ), $nonce ) ) { $err = __('You have entered an incorrect verification PIN code','wp-cerber'); } echo json_encode( array( 'error' => $err ) ); exit; } private static function verify_pin( $pin, $nonce = null ) { $cus = cerber_get_set( 'cerber_user', self::$user_id ); if ( ! $cus || empty( $cus['2fa']['pin'] ) || $cus['2fa']['expires'] < time() ) { return false; } if ( (string) $pin === (string) $cus['2fa']['pin'] ) { $ret = true; if ( ! $nonce ) { return $ret; } $cus['2fa']['nonce'] = $nonce; } else { $cus['2fa']['attempts'] ++; $ret = false; } cerber_update_set( 'cerber_user', $cus, self::$user_id ); return $ret; } static function show_2fa_page( $echo = true ) { @ini_set( 'display_errors', 0 ); $ajax_vars = 'var ajaxurl = "' . admin_url( 'admin-ajax.php' ) . '";'; $ajax_vars .= 'var nonce2fa = "'. wp_create_nonce( 'crb-ajax-2fa' ).'";'; if ( ! defined( 'CONCATENATE_SCRIPTS' ) ) { define( 'CONCATENATE_SCRIPTS', false ); } wp_enqueue_script( 'jquery' ); ob_start(); ?> <?php _e( 'Please verify that it’s you', 'wp-cerber' ); ?>
user_login; $ds[] = 'IP: ' . cerber_get_remote_ip(); if ( $c = lab_get_country( cerber_get_remote_ip(), false ) ) { $ds[] = 'Location: ' . cerber_country_name( $c ) . ' (' . $c . ')'; } $ds[] = 'Browser: ' . substr( strip_tags( crb_array_get( $_SERVER, 'HTTP_USER_AGENT', 'Not set' ) ), 0, 1000 ); $ds[] = 'Date: ' . cerber_date( time() ); $body[] = ''; $body[] = __( 'Here are the details of the sign-in attempt', 'wp-cerber' ); $body[] = implode( "\n", $ds ); } $body = implode( "\n\n", $body ); $result = wp_mail( $to, $subj, $body ); if ( $result && ( $data->user_email != $to ) ) { // TODO Add a notification to the main email //wp_mail( $data->user_email, $subj, $body ); } } static function get_user_email( $user_id = null ) { if ( ! $user_id ) { $user_id = get_current_user_id(); } $cus = cerber_get_set( 'cerber_user', $user_id ); if ( $cus && ( $email = crb_array_get( $cus, 'tfemail' ) ) ) { return $email; } $data = get_userdata( $user_id ); return $data->user_email; } static function get_user_pin( $user_id ) { $cus = cerber_get_set( 'cerber_user', $user_id ); if ( ! $cus || empty( $cus['2fa']['pin'] ) || $cus['2fa']['expires'] < time() ) { return false; } return $cus['2fa']; } static function get_user_pin_info( $user_id ) { if ( ! $pin = self::get_user_pin( $user_id ) ) { return ''; } return '' . $pin['pin'] . ' ' . __( 'expires', 'wp-cerber' ) . ' ' . cerber_ago_time( $pin['expires'] ); } static function delete_2fa( $uid ) { if ( ! $uid = absint( $uid ) ) { return; } $cus = cerber_get_set( 'cerber_user', $uid ); if ( $cus && isset( $cus['2fa'] ) ) { unset( $cus['2fa'] ); cerber_update_set( 'cerber_user', $cus, $uid ); } } static function cerber_2fa_form() { $max = CERBER_PIN_LENGTH; $atts = 'pattern="\d{' . $max . '}" maxlength="' . $max . '" size="' . $max . '" title="' . __( 'only digits are allowed', 'wp-cerber' ) . '"'; // Please enter your PIN code to continue $email = self::get_user_email(); $text = __( "We've sent a verification PIN code to your email", 'wp-cerber' ) . ' ' . cerber_mask_email( $email ) . '

'. __( 'Enter the code from the email in the field below.', 'wp-cerber' ).'

'; //$change = '' . __( 'Sign in with a different account', 'wp-cerber' ) . ''; $change = '' . __( 'Try again', 'wp-cerber' ) . ''; $cancel = '' . __( 'Cancel', 'wp-cerber' ) . ''; $links = '

'.__( 'Did not receive an email?', 'wp-cerber' ) .'

'. $change . ' ' . __( 'or', 'wp-cerber' ) . ' ' . $cancel; ?>

>