', '', $config ); ob_start(); @eval( $config ); // This eval is OK. Getting site DB connection parameters. ob_end_clean(); if ( defined( 'DB_USER' ) && defined( 'DB_PASSWORD' ) && defined( 'DB_NAME' ) && defined( 'DB_HOST' ) ) { require_once( $db_class ); return new wpdb( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST ); } } return false; } function crb_get_mysql_var( $var ) { static $cache; if ( ! isset( $cache[ $var ] ) ) { if ( $v = cerber_db_get_row( 'SHOW VARIABLES LIKE "' . $var . '"' ) ) { $cache[ $var ] = $v['Value']; } else { $cache[ $var ] = false; } } return $cache[ $var ]; } /** * Retrieve a value from the key-value storage * * @param string $key * @param integer $id * @param bool $unserialize * @param bool $use_cache * * @return bool|array */ function cerber_get_set( $key, $id = null, $unserialize = true, $use_cache = null ) { if ( ! $key ) { return false; } $key = preg_replace( CRB_SANITIZE_KEY, '', $key ); $cache_key = 'crb#' . $key . '#'; $id = ( $id !== null ) ? absint( $id ) : 0; $cache_key .= $id; $ret = false; $use_cache = ( isset( $use_cache ) ) ? $use_cache : cerber_cache_is_enabled(); if ( $use_cache ) { $cache_value = cerber_cache_get( $cache_key, null ); if ( $cache_value !== null ) { return $cache_value; } } if ( $row = cerber_db_get_row( 'SELECT * FROM ' . cerber_get_db_prefix() . CERBER_SETS_TABLE . ' WHERE the_key = "' . $key . '" AND the_id = ' . $id ) ) { if ( $row['expires'] > 0 && $row['expires'] < time() ) { cerber_delete_set( $key, $id ); if ( $use_cache ) { cerber_cache_delete( $cache_key ); } return false; } if ( $unserialize ) { if ( ! empty( $row['the_value'] ) ) { $ret = crb_unserialize( $row['the_value'] ); } else { $ret = array(); } } else { $ret = $row['the_value']; } } if ( $use_cache ) { cerber_cache_set( $cache_key, $ret ); } return $ret; } /** * Update/insert value to the key-value storage * * @param string $key A unique key for the data set. Max length is 255. * @param $value * @param integer $id An additional numerical key * @param bool $serialize * @param integer $expires Unix timestamp (UTC) when this element will be deleted * @param bool $use_cache * * @return bool */ function cerber_update_set( $key, $value, $id = null, $serialize = true, $expires = null, $use_cache = null ) { if ( ! $key ) { return false; } $key = preg_replace( CRB_SANITIZE_KEY, '', $key ); $cache_key = 'crb#' . $key . '#'; $expires = ( $expires !== null ) ? absint( $expires ) : 0; $id = ( $id !== null ) ? absint( $id ) : 0; $cache_key .= $id; $use_cache = ( isset( $use_cache ) ) ? $use_cache : cerber_cache_is_enabled(); if ( $use_cache ) { cerber_cache_set( $cache_key, $value, $expires - time() ); } if ( $serialize && ! is_string( $value ) ) { $value = serialize( $value ); } $value = cerber_real_escape( $value ); if ( false !== cerber_get_set( $key, $id, false, false ) ) { $sql = 'UPDATE ' . cerber_get_db_prefix() . CERBER_SETS_TABLE . ' SET the_value = "' . $value . '", expires = ' . $expires . ' WHERE the_key = "' . $key . '" AND the_id = ' . $id; } else { $sql = 'INSERT INTO ' . cerber_get_db_prefix() . CERBER_SETS_TABLE . ' (the_key, the_id, the_value, expires) VALUES ("' . $key . '",' . $id . ',"' . $value . '",' . $expires . ')'; } unset( $value ); if ( cerber_db_query( $sql ) ) { return true; } else { return false; } } /** * Delete value from the storage * * @param string $key * @param integer $id * * @return bool */ function cerber_delete_set( $key, $id = null ) { $key = preg_replace( CRB_SANITIZE_KEY, '', $key ); $cache_key = 'crb#' . $key . '#'; $id = ( $id !== null ) ? absint( $id ) : 0; $cache_key .= $id; cerber_cache_delete( $cache_key ); if ( cerber_db_query( 'DELETE FROM ' . cerber_get_db_prefix() . CERBER_SETS_TABLE . ' WHERE the_key = "' . $key . '" AND the_id = ' . $id ) ) { return true; } return false; } /** * Clean up all expired sets. Usually by cron. * * @param bool $all if true, deletes all sets that has expiration * * @return bool */ function cerber_delete_expired_set( $all = false ) { if ( ! $all ) { $where = 'expires > 0 AND expires < ' . time(); } else { $where = 'expires > 0'; } if ( cerber_db_query( 'DELETE FROM ' . cerber_get_db_prefix() . CERBER_SETS_TABLE . ' WHERE ' . $where ) ) { return true; } else { return false; } } /** * Remove comments from a given piece of code * * @param string $string * * @return mixed */ function cerber_remove_comments( $string = '' ) { return preg_replace( '/#.*/', '', preg_replace( '#//.*#', '', preg_replace( '#/\*(?:[^*]*(?:\*(?!/))*)*\*/#', '', $string ) ) ); } /** * Set Cerber Groove to logged in user browser * * @param $expire */ function cerber_set_groove( $expire ) { if ( ! headers_sent() ) { cerber_set_cookie( CRB_GROOVE, md5( cerber_get_groove() ), $expire + 1 ); $groove_x = cerber_get_groove_x(); cerber_set_cookie( CRB_GROOVE . '_x_' . $groove_x[0], $groove_x[1], $expire + 1 ); } } function cerber_is_auth_cookie( $text ) { return ( 0 === strpos( $text, cerber_get_cookie_prefix() . CRB_GROOVE ) ); } /* Get the special Cerber Sign for using with cookies */ function cerber_get_groove() { $groove = cerber_get_site_option( 'cerber-groove', false ); if ( empty( $groove ) ) { $groove = crb_random_string( 16 ); update_site_option( 'cerber-groove', $groove ); } return $groove; } /* Check if the special Cerber Sign valid */ function cerber_check_groove( $hash = '' ) { if ( ! $hash ) { if ( ! $hash = cerber_get_cookie( CRB_GROOVE ) ) { return false; } } $groove = cerber_get_groove(); if ( $hash == md5( $groove ) ) { return true; } return false; } /** * @since 7.0 */ function cerber_check_groove_x() { $groove_x = cerber_get_groove_x(); if ( cerber_get_cookie( CRB_GROOVE . '_x_' . $groove_x[0] ) == $groove_x[1] ) { return true; } return false; } /* Get the special public Cerber Sign for using with cookies */ function cerber_get_groove_x( $regenerate = false ) { $groove_x = array(); if ( ! $regenerate ) { $groove_x = cerber_get_site_option( 'cerber-groove-x' ); } if ( $regenerate || empty( $groove_x ) ) { $groove_0 = crb_random_string( 24, 32 ); $groove_1 = crb_random_string( 24, 32 ); $groove_x = array( $groove_0, $groove_1 ); update_site_option( 'cerber-groove-x', $groove_x ); crb_update_cookie_dependent(); } return $groove_x; } function cerber_get_cookie_path() { if ( defined( 'COOKIEPATH' ) ) { return COOKIEPATH; } return '/'; } function cerber_set_cookie( $name, $value, $expire = 0, $path = "", $domain = "" ) { global $wp_cerber_cookies; if ( cerber_is_wp_cron() ) { return; } if ( ! $path ) { $path = cerber_get_cookie_path(); } $expire = absint( $expire ); $expire = ( $expire > 43009401600 ) ? 43009401600 : $expire; setcookie( cerber_get_cookie_prefix() . $name, $value, $expire, $path, $domain, is_ssl(), false ); // No rush here: PHP 7.3 only /*setcookie( cerber_get_cookie_prefix() . $name, $value, array( 'expires ' => $expire, 'path' => $path, 'domain' => $domain, 'secure' => is_ssl(), 'httponly' => false, 'samesite' => 'Strict', ) );*/ $wp_cerber_cookies[ cerber_get_cookie_prefix() . $name ] = array( $expire, $value ); /*if ( ( ! $cerber_cookies = cerber_get_set( 'cerber_sweets' ) ) || ! is_array( $cerber_cookies ) ) { $cerber_cookies = array(); } $cerber_cookies[ cerber_get_cookie_prefix() . $name ] = array( $expire, $value ); cerber_update_set( 'cerber_sweets', $cerber_cookies ); */ } /** * @param $name * @param bool $default * * @return string|boolean value of the cookie, false if the cookie is not set */ function cerber_get_cookie( $name, $default = false ) { return crb_array_get( $_COOKIE, cerber_get_cookie_prefix() . $name, $default ); } function cerber_get_cookie_prefix() { /* if ( defined( 'CERBER_COOKIE_PREFIX' ) && is_string( CERBER_COOKIE_PREFIX ) && preg_match( '/^\w+$/', CERBER_COOKIE_PREFIX ) ) { return CERBER_COOKIE_PREFIX; }*/ if ( $p = (string) crb_get_settings( 'cookiepref' ) ) { return $p; } return ''; } function crb_update_cookie_dependent() { static $done = false; if ( $done ) { return; } register_shutdown_function( function () { cerber_htaccess_sync( 'main' ); // keep the .htaccess rule is up to date } ); $done = true; } /** * Synchronize plugin settings with rules in the .htaccess file * * @param $file string * @param $settings array * * @return bool|string|WP_Error */ function cerber_htaccess_sync( $file, $settings = array() ) { if ( ! $settings ) { $settings = crb_get_settings(); } if ( 'main' == $file ) { $rules = array(); if ( ! empty( $settings['adminphp'] ) && ( ! defined( 'CONCATENATE_SCRIPTS' ) || ! CONCATENATE_SCRIPTS ) ) { // https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-6389 if ( ! apache_mod_loaded( 'mod_rewrite', true ) ) { cerber_add_issue( 'no_mod', 'The Apache mod_rewrite module is not enabled on your web server. Ask your server administrator for assistance.', 'adminphp' ); return new WP_Error( 'no_mod', 'The Apache mod_rewrite module is not enabled on your web server. Ask your server administrator for assistance.' ); } $groove_x = cerber_get_groove_x(); $cookie = cerber_get_cookie_prefix() . CRB_GROOVE . '_x_' . $groove_x[0]; $rules [] = '# Protection of admin scripts is enabled (CVE-2018-6389)'; $rules [] = ''; $rules [] = 'RewriteEngine On'; $rules [] = 'RewriteBase /'; $rules [] = 'RewriteCond %{REQUEST_URI} ^(.*)wp-admin/+load-scripts\.php$ [OR,NC]'; // @updated 8.1 $rules [] = 'RewriteCond %{REQUEST_URI} ^(.*)wp-admin/+load-styles\.php$ [NC]'; // @updated 8.1 $rules [] = 'RewriteCond %{HTTP_COOKIE} !' . $cookie . '=' . $groove_x[1]; $rules [] = 'RewriteRule (.*) - [R=403,L]'; $rules [] = ''; } return cerber_update_htaccess( $file, $rules ); } if ( 'media' == $file ) { /*if ( ! crb_is_php_mod() ) { return 'ERROR: The Apache PHP module mod_php is not active.'; }*/ $rules = array(); if ( ! empty( $settings['phpnoupl'] ) ) { $rules [] = ''; $rules [] = 'SetHandler none'; $rules [] = 'SetHandler default-handler'; $rules [] = 'Options -ExecCGI'; $rules [] = 'RemoveHandler .cgi .php .php3 .php4 .php5 .php7 .phtml .pl .py .pyc .pyo'; $rules [] = ''; $rules [] = ''; $rules [] = 'php_flag engine off'; $rules [] = ''; $rules [] = ''; $rules [] = 'php_flag engine off'; $rules [] = ''; } return cerber_update_htaccess( $file, $rules ); } return false; } /** * Remove Cerber rules from the .htaccess file * */ function cerber_htaccess_clean_up() { cerber_update_htaccess( 'main', array() ); cerber_update_htaccess( 'media', array() ); } /** * Update the .htaccess file * * @param $file * @param array $rules A set of rules (array of strings) for the section. If empty, the section will be cleaned. * * @return bool|string|WP_Error True on success, string with error message on failure */ function cerber_update_htaccess( $file, $rules = array() ) { if ( $file == 'main' ) { $htaccess = cerber_get_htaccess_file(); $marker = CERBER_MARKER1; } elseif ( $file == 'media' ) { $htaccess = cerber_get_upload_dir() . '/.htaccess'; $marker = CERBER_MARKER2; } else { return '???'; } if ( ! is_file( $htaccess ) ) { if ( ! touch( $htaccess ) ) { return new WP_Error( 'htaccess-io', 'ERROR: Unable to create the file ' . $htaccess ); } } elseif ( ! is_writable( $htaccess ) ) { return new WP_Error( 'htaccess-io', 'ERROR: Unable to get access to the file ' . $htaccess ); } $result = crb_insert_with_markers( $htaccess, $marker, $rules ); if ( $result || $result === 0 ) { $result = 'The ' . $htaccess . ' file has been updated'; } else { $result = new WP_Error( 'htaccess-io', 'ERROR: Unable to modify the file ' . $htaccess ); } return $result; } /** * Return .htaccess filename with full path * * @return bool|string full filename if the file can be written, false otherwise */ function cerber_get_htaccess_file() { require_once( ABSPATH . 'wp-admin/includes/file.php' ); $home_path = get_home_path(); return $home_path . '.htaccess'; } /** * Check if the remote domain match mask * * @param $domain_mask array|string Mask(s) to check remote domain against * * @return bool True if hostname match at least one domain from the list */ function cerber_check_remote_domain( $domain_mask ) { $hostname = @gethostbyaddr( cerber_get_remote_ip() ); if ( ! $hostname || filter_var( $hostname, FILTER_VALIDATE_IP ) ) { return false; } if ( ! is_array( $domain_mask ) ) { $domain_mask = array( $domain_mask ); } foreach ( $domain_mask as $mask ) { if ( substr_count( $mask, '.' ) != substr_count( $hostname, '.' ) ) { continue; } $parts = array_reverse( explode( '.', $hostname ) ); $ok = true; foreach ( array_reverse( explode( '.', $mask ) ) as $i => $item ) { if ( $item != '*' && $item != $parts[ $i ] ) { $ok = false; break; } } if ( $ok == true ) { return true; } } return false; } /** * Prepare files (install/deinstall) for different boot modes * * @param $mode int A plugin boot mode * @param $old_mode int * * @return bool|WP_Error * @since 6.3 */ function cerber_set_boot_mode( $mode = null, $old_mode = null ) { if ( $mode === null ) { $mode = crb_get_settings( 'boot-mode' ); } $source = dirname( cerber_plugin_file() ) . '/modules/aaa-wp-cerber.php'; $target = WPMU_PLUGIN_DIR . '/aaa-wp-cerber.php'; if ( $mode == 1 ) { if ( file_exists( $target ) ) { if ( sha1_file( $source, true ) == sha1_file( $target, true ) ) { return true; } } if ( ! is_dir( WPMU_PLUGIN_DIR ) ) { if ( ! mkdir( WPMU_PLUGIN_DIR, 0755, true ) ) { // TODO: try to set permissions for the parent folder return new WP_Error( 'cerber-boot', __( 'Unable to create the directory', 'wp-cerber' ) . ' ' . WPMU_PLUGIN_DIR ); } } if ( ! copy( $source, $target ) ) { if ( ! wp_is_writable( WPMU_PLUGIN_DIR ) ) { return new WP_Error( 'cerber-boot', __( 'Destination folder access denied', 'wp-cerber' ) . ' ' . WPMU_PLUGIN_DIR ); } elseif ( ! file_exists( $source ) ) { return new WP_Error( 'cerber-boot', __( 'File not found', 'wp-cerber' ) . ' ' . $source ); } return new WP_Error( 'cerber-boot', __( 'Unable to copy the file', 'wp-cerber' ) . ' ' . $source . ' to the folder ' . WPMU_PLUGIN_DIR ); } } else { if ( file_exists( $target ) ) { if ( ! unlink( $target ) ) { return new WP_Error( 'cerber-boot', __( 'Unable to delete the file', 'wp-cerber' ) . ' ' . $target ); } } return true; } return true; } /** * How the plugin was loaded (initialized) * * @return int * @since 6.3 */ function cerber_get_mode() { if ( function_exists( 'cerber_mode' ) && defined( 'CERBER_MODE' ) ) { return cerber_mode(); } return 0; } function cerber_is_permalink_enabled() { static $ret; if ( isset( $ret ) ) { return $ret; } $ret = ( get_option( 'permalink_structure' ) ) ? true : false; return $ret; } /** * Given the path of a file or directory, this function * will return the parent directory's path that is given levels up * * @param string $path * @param integer $levels * * @return string Parent directory's path */ function cerber_dirname( $path, $levels = 1 ) { if ( PHP_VERSION_ID >= 70000 || $levels == 1 ) { return dirname( $path, $levels ); } $ret = '.'; $path = explode( DIRECTORY_SEPARATOR, str_replace( array( '/', '\\' ), DIRECTORY_SEPARATOR, $path ) ); if ( 0 < ( count( $path ) - $levels ) ) { $path = array_slice( $path, 0, count( $path ) - $levels ); $ret = implode( DIRECTORY_SEPARATOR, $path ); } return $ret; } /** * Implement basename() with multibyte support * * @param $file_name * * @return string */ function cerber_mb_basename( $file_name ) { $pos = mb_strrpos( $file_name, DIRECTORY_SEPARATOR ); if ( $pos !== false ) { return mb_substr( $file_name, $pos + 1 ); } return $file_name; } function cerber_get_extension( $file_name ) { $file_name = cerber_mb_basename( $file_name ); $pos = mb_strpos( $file_name, '.' ); if ( $pos !== false ) { if ( $ext = mb_substr( $file_name, $pos + 1 ) ) { return mb_strtolower( $ext ); } } return ''; } /** * True if version of WP is equal or greater than specified one * * @param string $ver * * @return bool|int */ function crb_wp_version_compare( $ver ) { return version_compare( cerber_get_wp_version(), $ver, '>=' ); } /** * Returns an unaltered $wp_version variable * * @return string WordPress version */ function cerber_get_wp_version() { static $ver; if ( ! $ver ) { include( ABSPATH . WPINC . DIRECTORY_SEPARATOR . 'version.php' ); $ver = (string) $wp_version; } return $ver; } /** * Returns an unaltered $wp_local_package variable * * @return string WordPress locale * @since 8.8.7.2 */ function cerber_get_wp_locale() { static $lc; if ( ! $lc ) { global $wp_local_package; include( ABSPATH . WPINC . DIRECTORY_SEPARATOR . 'version.php' ); $lc = isset( $wp_local_package ) ? $wp_local_package : 'en_US'; } return $lc; } function crb_get_themes() { static $theme_headers = array( 'Name' => 'Theme Name', 'ThemeURI' => 'Theme URI', 'Description' => 'Description', 'Author' => 'Author', 'AuthorURI' => 'Author URI', 'Version' => 'Version', 'Template' => 'Template', 'Status' => 'Status', 'Tags' => 'Tags', 'TextDomain' => 'Text Domain', 'DomainPath' => 'Domain Path', ); $themes = array(); if ( $list = search_theme_directories() ) { foreach ( $list as $key => $info ) { $css = $info['theme_root'] . '/' . $info['theme_file']; if ( is_readable( $css ) ) { $themes[ $key ] = get_file_data( $info['theme_root'] . '/' . $info['theme_file'], $theme_headers, 'theme' ); $themes[ $key ]['theme_root'] = $info['theme_root']; $themes[ $key ]['theme_file'] = $info['theme_file']; } } } return $themes; } function cerber_is_base64_encoded( $val ) { $val = trim( $val ); if ( empty( $val ) || is_numeric( $val ) || strlen( $val ) < 8 || preg_match( '/[^A-Z0-9\+\/=]/i', $val ) ) { return false; } if ( $val = @base64_decode( $val ) ) { if ( ! preg_match( '/[\x00-\x08\x0B-\x0C\x0E-\x1F]/', $val ) ) { // ASCII control characters must not be if ( preg_match( '/[A-Z]/i', $val ) ) { // Latin chars must be return $val; } } } return false; } function crb_is_alphanumeric( $str ) { return ! preg_match( '/[^\w\-]/', $str ); } /** * @param array $arr * @param array $fields * * @return bool */ function crb_arrays_similar( &$arr, $fields ) { if ( crb_array_diff_keys( $arr, $fields ) ) { return false; } foreach ( $fields as $field => $pattern ) { if ( is_callable( $pattern ) ) { if ( ! call_user_func( $pattern, $arr[ $field ] ) ) { return false; } } else { if ( ! preg_match( $pattern, $arr[ $field ] ) ) { return false; } } } return true; } function cerber_get_html_label( $iid ) { //$css['scan-ilabel'] = 'color: #fff; margin-left: 6px; display: inline-block; line-height: 1em; padding: 3px 5px; font-size: 92%;'; $c = ( $iid == 1 ) ? '#33be84' : '#dc2f34'; return '' . cerber_get_issue_label( $iid ) . ''; } function crb_getallheaders() { if ( function_exists( 'getallheaders' ) ) { return getallheaders(); } // @since v. 7.7 for PHP-FPM $headers = array(); foreach ( $_SERVER as $name => $value ) { if ( substr( $name, 0, 5 ) == 'HTTP_' ) { $headers[ str_replace( ' ', '-', ucwords( strtolower( str_replace( '_', ' ', substr( $name, 5 ) ) ) ) ) ] = $value; } } return $headers; } /** * @param $msg * @param string $source */ function cerber_error_log( $msg, $source = '' ) { //if ( crb_get_settings( 'log_errors' ) ) { cerber_diag_log( $msg, $source, true ); //} } /** * Write message to the diagnostic log * * @param string|array $msg * @param string $source * @param bool $error * * @return bool|int */ function cerber_diag_log( $msg, $source = '', $error = false ) { if ( $source == 'CLOUD' ) { if ( ! defined( 'CERBER_CLOUD_DEBUG' ) || ( ! defined( 'WP_ADMIN' ) && ! defined( 'WP_NETWORK_ADMIN' ) ) ) { return false; } } if ( ! $msg || ! $log = @fopen( cerber_get_diag_log(), 'a' ) ) { return false; } if ( $source ) { $source = '[' . $source . ']'; } if ( $error ) { $source .= ' ERROR: '; } if ( ! is_array( $msg ) ) { $msg = array( $msg ); } foreach ( $msg as $line ) { if ( is_array( $line ) ) { $line = print_r( $line, 1 ); // workaround for CRB_Globals::$db_errors } //$ret = @fwrite( $log, '[' .cerber_get_remote_ip(). '][' . cerber_date( time() ) . ']' . $source . ' ' . $line . PHP_EOL ); $ret = @fwrite( $log, '[' . cerber_date( time(), false ) . ']' . $source . ' ' . $line . PHP_EOL ); } @fclose( $log ); return $ret; } function cerber_get_diag_log() { //$dir = ( defined( 'CERBER_DIAG_DIR' ) && is_dir( CERBER_DIAG_DIR ) ) ? CERBER_DIAG_DIR . '/' : cerber_get_the_folder(); if ( defined( 'CERBER_DIAG_DIR' ) && is_dir( CERBER_DIAG_DIR ) ) { $dir = CERBER_DIAG_DIR; } else { if ( ! $dir = cerber_get_the_folder() ) { return false; } } return rtrim( $dir, '/' ) . '/cerber-debug.log'; } function cerber_truncate_log( $bytes = 10000000 ) { $file = cerber_get_diag_log(); if ( ! is_file( $file ) || filesize( $file ) <= $bytes ) { return; } if ( $bytes == 0 ) { $log = @fopen( $file, 'w' ); @fclose( $log ); return; } if ( $text = file_get_contents( $file ) ) { $text = substr( $text, 0 - $bytes ); if ( ! $log = @fopen( $file, 'w' ) ) { return; } @fwrite( $log, $text ); @fclose( $log ); } } function crb_get_bloginfo( $what ) { static $info = array(); if ( ! isset( $info[ $what ] ) ) { $info[ $what ] = get_bloginfo( $what ); } return $info[ $what ]; } function crb_is_php_mod() { require_once( ABSPATH . 'wp-admin/includes/misc.php' ); if ( apache_mod_loaded( 'mod_php7' ) ) { return true; } if ( apache_mod_loaded( 'mod_php5' ) ) { return true; } return false; } /** * PHP implementation of fromCharCode * * @param $str * * @return string */ function cerber_fromcharcode( $str ) { $vals = explode( ',', $str ); $vals = array_map( function ( $v ) { $v = trim( $v ); if ( $v[0] == '0' ) { $v = ( $v[1] == 'x' || $v[1] == 'X' ) ? hexdec( $v ) : octdec( $v ); } else { $v = intval( $v ); } return '&#' . $v . ';'; }, $vals ); return mb_convert_encoding( implode( '', $vals ), 'UTF-8', 'HTML-ENTITIES' ); } /** * @param $dir string Directory to empty with a trailing directory separator * * @return int|WP_Error */ function cerber_empty_dir( $dir ) { //$trd = rtrim( $dir, '/\\' ); if ( ! @is_dir( $dir ) || 0 === strpos( $dir, ABSPATH ) ) { // Workaround for a non-legitimate use of this function return new WP_Error( 'no-dir', 'This directory cannot be emptied' ); } $files = @scandir( $dir ); if ( ! is_array( $files ) || empty( $files ) ) { return true; } $fs = cerber_init_wp_filesystem(); if ( is_wp_error( $fs ) ) { return $fs; } $ret = true; foreach ( $files as $file ) { $full = $dir . $file; if ( @is_file( $full ) ) { if ( ! @unlink( $full ) ) { $ret = false; } } elseif ( ! in_array( $file, array( '..', '.' ) ) && is_dir( $full ) ) { if ( ! $fs->rmdir( $full, true ) ) { $ret = false; } } } if ( ! $ret ) { return new WP_Error( 'file-deletion-error', 'Some files or subdirectories in this directory cannot be deleted: ' . $dir ); } return $ret; } /** * Tries to raise PHP limits * */ function crb_raise_limits( $mem = null ) { @ini_set( 'max_execution_time', 180 ); if ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) ) { @set_time_limit( 0 ); } if ( $mem ) { @ini_set( 'memory_limit', $mem ); } } /** * Masks email address * * @param string $email * * @return string */ function cerber_mask_email( $email ) { list( $box, $host ) = explode( '@', $email ); $box = str_pad( $box[0], strlen( $box ), '*' ); $host = str_pad( substr( $host, strrpos( $host, '.' ) ), strlen( $host ), '*', STR_PAD_LEFT ); return str_replace( '*', '∗', $box . '@' . $host ); } /** * Masks username (login) * * @param string $login * * @return string * * @since 8.9.6.4 */ function crb_mask_login( $login ) { if ( is_email( $login ) ) { return cerber_mask_email( $login ); } $strlen = mb_strlen( $login ); return str_pad( mb_substr( $login, 0, intdiv( $strlen, 2 ) ), $strlen, '*' ); } /** * Masks IP address * * @param string $ip * * @return string * * @since 8.9.6.4 */ function crb_mask_ip( $ip = '' ) { if ( cerber_is_ipv6( $ip ) ) { // Look for the third colon $pos = strpos( $ip, ':', strpos( $ip, ':', strpos( $ip, ':' ) + 1 ) + 1 ); $delimiter = ':'; } else { // Look for the second dot $pos = strpos( $ip, '.', strpos( $ip, '.' ) + 1 ); $delimiter = '.'; } if ( ! $pos ) { return $ip; } $net = substr( $ip, 0, $pos ); $sub = substr( $ip, $pos ); return $net . preg_replace( '/[^' . $delimiter . ']/', '*', $sub ); } /** * A modified clone of insert_with_markers() from wp-admin/includes/misc.php * Removed switch_to_locale() and related stuff that were introduced in WP 5.3. and cause problem if calling ite before 'init' hook. * * Inserts an array of strings into a file (.htaccess ), placing it between * BEGIN and END markers. * * Replaces existing marked info. Retains surrounding * data. Creates file if none exists. * * @param string $filename Filename to alter. * @param string $marker The marker to alter. * @param array|string $insertion The new content to insert. * * @return bool True on write success, false on failure. * @since 8.5.1 * */ function crb_insert_with_markers( $filename, $marker, $insertion ) { if ( ! file_exists( $filename ) ) { if ( ! is_writable( dirname( $filename ) ) ) { return false; } if ( ! touch( $filename ) ) { return false; } } elseif ( ! is_writeable( $filename ) ) { return false; } if ( ! is_array( $insertion ) ) { $insertion = explode( "\n", $insertion ); } $start_marker = "# BEGIN {$marker}"; $end_marker = "# END {$marker}"; $fp = fopen( $filename, 'r+' ); if ( ! $fp ) { return false; } // Attempt to get a lock. If the filesystem supports locking, this will block until the lock is acquired. flock( $fp, LOCK_EX ); $lines = array(); while ( ! feof( $fp ) ) { $lines[] = rtrim( fgets( $fp ), "\r\n" ); } // Split out the existing file into the preceding lines, and those that appear after the marker $pre_lines = array(); $post_lines = array(); $existing_lines = array(); $found_marker = false; $found_end_marker = false; foreach ( $lines as $line ) { if ( ! $found_marker && false !== strpos( $line, $start_marker ) ) { $found_marker = true; continue; } elseif ( ! $found_end_marker && false !== strpos( $line, $end_marker ) ) { $found_end_marker = true; continue; } if ( ! $found_marker ) { $pre_lines[] = $line; } elseif ( $found_marker && $found_end_marker ) { $post_lines[] = $line; } else { $existing_lines[] = $line; } } // Check to see if there was a change if ( $existing_lines === $insertion ) { flock( $fp, LOCK_UN ); fclose( $fp ); return true; } // Generate the new file data $new_file_data = implode( "\n", array_merge( $pre_lines, array( $start_marker ), $insertion, array( $end_marker ), $post_lines ) ); // Write to the start of the file, and truncate it to that length fseek( $fp, 0 ); $bytes = fwrite( $fp, $new_file_data ); if ( $bytes ) { ftruncate( $fp, ftell( $fp ) ); } fflush( $fp ); flock( $fp, LOCK_UN ); fclose( $fp ); return (bool) $bytes; } /** * @return WP_Error|WP_Filesystem_Direct */ function cerber_init_wp_filesystem() { global $wp_filesystem; if ( $wp_filesystem instanceof WP_Filesystem_Direct ) { // @since 8.1.5 return $wp_filesystem; } require_once( ABSPATH . 'wp-admin/includes/file.php' ); add_filter( 'filesystem_method', '__ret_direct' ); if ( ! WP_Filesystem() ) { return new WP_Error( 'cerber-file', 'Unable to init WP_Filesystem' ); } remove_filter( 'filesystem_method', '__ret_direct' ); return $wp_filesystem; } function __ret_direct() { return 'direct'; } /** * Returns a list of alert parameters for the currently displaying admin page in a specific order. * The keys are used to create an alert URL. * Values are used to calculate an alert hash. * * @return array The set of parameters */ function crb_get_alert_params() { // A set of alert parameters // A strictly particular order due to further using numeric array indexes. $params = CRB_ALERT_PARAMS; $get = crb_get_query_params(); if ( ! array_intersect_key( $params, $get ) ) { return $params; // No parameters in the current query } // The IP field is processed differently than other fields if ( ! empty( $get['filter_ip'] ) ) { $begin = 0; $end = 0; $ip = cerber_any2range( $get['filter_ip'] ); if ( is_array( $ip ) ) { $begin = $ip['begin']; $end = $ip['end']; $ip = 0; } elseif ( ! $ip ) { $ip = 0; } $params['begin'] = $begin; $params['end'] = $end; $params['filter_ip'] = $ip; } // Getting values of the request fields (used as alert parameters) $temp = $params; unset( $temp['begin'], $temp['end'], $temp['filter_ip'] ); foreach ( array_keys( $temp ) as $key ) { if ( ! empty( $get[ $key ] ) ) { if ( is_array( $get[ $key ] ) ) { $params[ $key ] = array_map( 'trim', $get[ $key ] ); } else { $params[ $key ] = trim( $get[ $key ] ); } } else { $params[ $key ] = 0; } } // Preparing/sanitizing values of the alert parameters if ( ! empty( $params['al_expires'] ) ) { $time = 24 * 3600 + strtotime( 'midnight ' . $params['al_expires'] ); $params['al_expires'] = $time - get_option( 'gmt_offset' ) * 3600; } $int_fields = array( 'al_limit', 'al_ignore_rate', 'al_send_emails', 'al_send_pushbullet' ); foreach ( $int_fields as $f ) { $params[ $f ] = absint( $params[ $f ] ); } if ( ! is_array( $params['filter_activity'] ) ) { $params['filter_activity'] = array( $params['filter_activity'] ); } $params['filter_activity'] = array_filter( $params['filter_activity'] ); // Basic XSS sanitization array_walk_recursive( $params, function ( &$item ) { $item = str_replace( array( '<', '>', '[', ']', '"', "'" ), '', $item ); } ); return $params; } /** * @param array $params * * @return string * * @since 8.9.6 */ function crb_get_alert_id( $params ) { return sha1( json_encode( array_values( array_diff_key( $params, array_flip( CRB_NON_ALERT_PARAMS ) ) ) ) ); } function crb_random_string( $length_min, $length_max = null, $inc_num = true, $upper_case = true, $extra = '' ) { static $alpha1 = 'abcdefghijklmnopqrstuvwxyz'; static $alpha2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; static $digits = '0123456789'; if ( ! $length_max ) { $length_max = $length_min; } $str = $alpha1; if ( $inc_num ) { $str .= $digits; } if ( $upper_case ) { $str .= $alpha2; } if ( $extra ) { $str .= $extra; } $n = (int) ceil( $length_max / strlen( $str ) ); if ( $n > 1 ) { $str = implode( '', array_fill( 0, $n, $str ) ); } $length = ( $length_min != $length_max ) ? rand( $length_min, $length_max ) : $length_min; return substr( str_shuffle( $str ), 0, $length ); } /** * Detects and decodes serialized or JSON encoded array * * @param $text string * * @return array * * @since 8.8 */ function crb_auto_decode( &$text ) { if ( ! $text ) { return array(); } if ( $text[0] == 'a' ) { return crb_unserialize( $text ); } return @json_decode( $text, true ); } /** * A safe version of unserialize() * * @param string $string * * @return mixed * */ function crb_unserialize( &$string ) { if ( PHP_VERSION_ID >= 70000 ) { return @unserialize( $string, [ 'allowed_classes' => false ] ); } return @unserialize( $string ); } function crb_get_review_url( $vendor = null ) { static $urls = array( 'tpilot' => array( 'https://www.trustpilot.com/review/wpcerber.com', 'https://www.trustpilot.com/evaluate/wpcerber.com' ), 'g2' => array( 'https://www.g2.com/products/cerber-security-antispam-malware-scan/reviews/start' ), 'wp' => array( 'https://wordpress.org/support/plugin/wp-cerber/reviews/#new-post' ), 'cap' => array( 'https://reviews.capterra.com/new/187653' ), ); $ret = $urls[ $vendor ]; if ( $vendor == 'tpilot' ) { shuffle( $ret ); } return $ret[0]; } function crb_was_activated( $ago ) { static $actvd; if ( ! isset( $actvd ) ) { if ( ! $actvd = cerber_get_set( '_activated' ) ) { return true; } } return ( ( (int) crb_array_get( $actvd, 'time' ) + $ago ) <= time() ); } /** * Return a "session verifier" to identify the current admin session among others admin sessions * * Copy of WP_Session_Tokens->hash_token(); * * @param $token * * @return string */ function cerber_hash_token( $token ) { // If ext/hash is not present, use sha1() instead. if ( function_exists( 'hash' ) ) { return hash( 'sha256', $token ); } else { return sha1( $token ); } } // The key-value cache final class CRB_Cache { private static $cache = array(); private static $stat = array(); private static $wp_cache_group = 'wp_cerber'; private static $wp_key_list = 'wp_cerber_list'; static function set( $key, $value, $expire = 0 ) { $exp = 0; if ( $expire > 0 ) { $exp = time() + (int) $expire; if ( $exp < time() ) { return false; } } $element = array( $value, $exp ); self::$cache[ $key ] = $element; if ( self::checker() ) { wp_cache_set( $key, $element, self::$wp_cache_group ); $entries = wp_cache_get( self::$wp_key_list, self::$wp_key_list ); if ( ! $entries ) { $entries = array(); } $entries[ $key ] = $expire; wp_cache_set( self::$wp_key_list, $entries, self::$wp_key_list ); } if ( ! isset( self::$stat[ $key ] ) ) { self::$stat[ $key ] = array( 0, 0 ); } self::$stat[ $key ][0] ++; return true; } static function get( $key, $default = null ) { $element = crb_array_get( self::$cache, $key ); if ( ! is_array( $element ) ) { if ( self::checker() ) { $element = wp_cache_get( $key, self::$wp_cache_group ); } } if ( ! is_array( $element ) ) { return $default; } if ( ! empty( $element[1] ) && $element[1] < time() ) { self::delete( $key ); return $default; } if ( ! isset( self::$stat[ $key ] ) ) { self::$stat[ $key ] = array( 0, 0 ); } self::$stat[ $key ][1] ++; return $element[0]; } static function delete( $key ) { if ( isset( self::$cache[ $key ] ) ) { unset( self::$cache[ $key ] ); } if ( self::checker() ) { wp_cache_delete( $key, self::$wp_cache_group ); } } static function reset() { self::$cache = array(); if ( $entries = wp_cache_get( self::$wp_key_list, self::$wp_key_list ) ) { foreach ( $entries as $entry => $exp ) { wp_cache_delete( $entry, self::$wp_cache_group ); } wp_cache_delete( self::$wp_key_list, self::$wp_key_list ); } } static function get_stat( $recheck = false ) { $entries = wp_cache_get( self::$wp_key_list, self::$wp_key_list ); if ( $recheck && $entries ) { // Make sure that our list of existing key doesn't contain wrong entries foreach ( $entries as $key => $exp ) { if ( ! $element = wp_cache_get( $key, self::$wp_cache_group ) ) { unset( $entries[ $key ] ); } } wp_cache_set( self::$wp_key_list, $entries, self::$wp_key_list ); } if ( empty( $entries ) ) { $entries = array(); } return array( self::$stat, $entries ); } static function checker() { $sid = get_wp_cerber()->getRequestID(); $check = wp_cache_get( '__checker__', self::$wp_cache_group ); if ( ! $check || ! isset( $check['t'] ) || ! isset( $check['s'] ) ) { wp_cache_set( '__checker__', array( 't' => time(), 's' => $sid ), self::$wp_cache_group ); return 0; } if ( $check['s'] == $sid ) { return 0; } return $check['t']; } } /** * @param $key string * @param $value mixed * @param $expire integer Element will expire in X seconds, 0 = never expires * * @return bool */ function cerber_cache_set( $key, $value, $expire = 0 ) { return CRB_Cache::set( $key, $value, $expire ); } /** * @param $key string * @param $default mixed * * @return mixed|null */ function cerber_cache_get( $key, $default = null ) { return CRB_Cache::get( $key, $default ); } function cerber_cache_delete( $key ) { CRB_Cache::delete( $key ); } function cerber_cache_enable() { global $cerber_use_cache; $cerber_use_cache = true; } function cerber_cache_disable() { global $cerber_use_cache; $cerber_use_cache = false; } function cerber_cache_is_enabled() { global $cerber_use_cache; return ! empty( $cerber_use_cache ); } /** * Retrieve and cache data from the DB. Make sense for heavy queries. * * @param array|string $sql One or more SQL queries with optional data format * @param string $table DB table we're caching data from * @param bool $cache_only * @param string[] $hash_fields Fields to calculate hash * @param int $order_by The key of the ORDER BY field in the $fieldset * * @return array|false * * @since 8.8.3.1 */ function crb_q_cache_get( $sql, $table, $cache_only = false, $hash_fields = array( 'stamp', 'ip', 'session_id' ), $order_by = 0 ) { global $wp_cerber_q_cache; if ( is_string( $sql ) ) { $sql = array( array( $sql ) ); } $single = ( count( $sql ) == 1 ); $run = true; $cache_key = 'q_cache_' . sha1( implode( '|', array_column( $sql, 0 ) ) ); $cache = cerber_get_set( $cache_key, 0, false, true ); if ( $cache ) { $cache = json_decode( $cache ); if ( $cache->hash == crb_get_table_hash( $table, $hash_fields, $order_by ) ) { $wp_cerber_q_cache = true; $run = false; } } if ( $run && $cache_only ) { return false; } if ( ! $run ) { $results = $cache->results; } else { $new_cache = array(); $new_cache['hash'] = crb_get_table_hash( $table, $hash_fields, $order_by ); $results = array(); foreach ( $sql as $query ) { $results[] = cerber_db_get_results( $query[0], crb_array_get( $query, 1 ) ); } $new_cache['results'] = $results; $new_cache = json_encode( $new_cache, JSON_UNESCAPED_UNICODE ); cerber_update_set( $cache_key, $new_cache, 0, false, time() + 7200, true ); } if ( $single ) { return $results[0]; } return $results; } /** * Returns pseudo "hash" for a given log table to detect changes in the table * * @param string $table * @param string[] $hash_fields * @param int $order_by * * @return string * @since 8.8.3.1 */ function crb_get_table_hash( $table, $hash_fields, $order_by ) { static $hashes; $fields = implode( ',', $hash_fields ); $key = sha1( $table . '|' . $fields . '|' . $order_by ); if ( ! isset( $hashes[ $key ] ) ) { if ( $data = cerber_db_get_row( 'SELECT ' . $fields . ' FROM ' . $table . ' ORDER BY ' . $hash_fields[ $order_by ] . ' DESC LIMIT 1' ) ) { $hashes[ $key ] = sha1( implode( '|', $data ) ); } else { $hashes[ $key ] = ''; } } return $hashes[ $key ]; } /** * A replacement for global PHP variables. It doesn't make them good (less ugly), but it helps to trace their usage easily (within IDE). * * @since 8.9.4 * */ class CRB_Globals { static $session_status; static $act_status; static $do_not_log = array(); static $reset_pwd_msg; static $reset_pwd_denied = false; static $user_id; static $req_status = 0; static $assets_url = ''; static $ajax_loader = ''; static $logged; static $blocked; static $db_requests = array(); static $db_errors = array(); static $bot_status = 0; static $doing_upgrade; /** * @param integer $val * * @return void */ static function set_bot_status( $val ) { self::$bot_status = $val; self::$act_status = $val; // For backward compatibility } /** * @param integer $val * * @return void */ static function set_act_status( $val ) { if ( ! self::$act_status ) { self::$act_status = $val; } } }