File: //var/www/biblioteka/wp-content/plugins/ninjafirewall/lib/security_rules_update.php
<?php
/*
+---------------------------------------------------------------------+
| NinjaFirewall (WP Edition) |
| |
| (c) NinTechNet - https://nintechnet.com/ |
+---------------------------------------------------------------------+
| This program is free software: you can redistribute it and/or |
| modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation, either version 3 of |
| the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
+---------------------------------------------------------------------+ i18n+ / sa / 2
*/
if (! defined( 'NFW_ENGINE_VERSION' ) ) { die( 'Forbidden' ); }
// If your server can't remotely connect to a SSL port, add this
// to your wp-config.php script: `define('NFW_DONT_USE_SSL', 1);`
if ( defined( 'NFW_DONT_USE_SSL' ) ) {
$proto = "http";
} else {
$proto = "https";
}
$update_log = NFW_LOG_DIR . '/nfwlog/updates.php';
// Check which rules should be returned
if ( defined('NFW_WPWAF') ) {
$rules_type = 0;
} else {
$rules_type = 1;
}
$nfw_options = nfw_get_option('nfw_options');
if ( empty( $nfw_options['sched_updates'] ) || empty( $nfw_options['enable_updates'] ) ) {
$sched_updates = 0;
} else {
$sched_updates = (int) $nfw_options['sched_updates'];
}
if ( defined( 'NFUPDATESDO' ) && NFUPDATESDO == 2 ) {
// Installation
$update_url = array(
$proto . '://plugins.svn.wordpress.org/ninjafirewall/updates/',
'version3.txt',
'rules4.txt'
);
} else {
// Scheduled updates or plugin update
$caching_id = sha1( home_url() );
$update_url = array(
'https://api.nintechnet.com/ninjafirewall/rules-update',
"?version=4&cid={$caching_id}&edn=wp&rt={$rules_type}&su={$sched_updates}",
"?rules=4&cid={$caching_id}&edn=wp&rt={$rules_type}&su={$sched_updates}"
);
}
// NFUPDATESDO: scheduled update (1), installation (2) or plugin update (3 - deprecated since v3.8)
if (defined('NFUPDATESDO') ) {
$rules_lock = NFW_LOG_DIR .'/nfwlog/cache/rules';
if ( NFUPDATESDO != 2 ) { // Shouldn't apply to (re)installation
if ( $nfw_options['sched_updates'] == 1 ) {
$interval = 3000; // 50mn
} elseif ( $nfw_options['sched_updates'] == 2 ) {
$interval = 39600; // 11h
} else {
$interval = 82800; // 23h
}
if ( file_exists( $rules_lock ) ) {
$rules_lock_mtime = filemtime( $rules_lock );
if ( time() - $interval < $rules_lock_mtime ) {
return;
}
unlink( $rules_lock );
}
}
touch( $rules_lock );
define('NFW_RULES', nf_sub_do_updates( $update_url, $update_log, NFUPDATESDO ) );
return;
}
// Block immediately if user is not allowed
nf_not_allowed( 'block', __LINE__ );
// We stop and warn the user if the firewall is disabled
if (! defined('NF_DISABLED') ) {
is_nfw_enabled();
}
if (NF_DISABLED) {
echo '<div class="error notice is-dismissible"><p>' . __('Security rules cannot be updated when NinjaFirewall is disabled.', 'ninjafirewall') . '</p></div>';
return;
}
//Saved options
if (! empty( $_POST['nfw_act'] ) ) {
if ( empty( $_POST['nfwnonce'] ) || ! wp_verify_nonce( $_POST['nfwnonce'], 'updates_save' ) ) {
wp_nonce_ays('updates_save');
}
// Check updates now
if ( isset( $_POST['check_updates'] ) ) {
if ( $res = nf_sub_do_updates($update_url, $update_log, 0) ) {
echo '<div class="updated notice is-dismissible"><p>' . __('Security rules have been updated.', 'ninjafirewall') . '</p></div>';
} else {
echo '<div class="updated notice is-dismissible"><p>' . __('No security rules update available.', 'ninjafirewall') . '</p></div>';
}
// Enable flag to display log
$tmp_showlog = 1;
} else {
if ( isset( $_POST['save_options'] ) ) {
nf_sub_updates_save();
} elseif ( isset( $_POST['clear_log'] ) ) {
nf_sub_updates_clearlog($update_log);
}
echo '<div class="updated notice is-dismissible"><p>' . __('Your changes have been saved.', 'ninjafirewall') . '</p></div>';
}
// Reload options:
$nfw_options = nfw_get_option('nfw_options');
}
// If WP cron is disabled, we simply warn the user
if ( defined('DISABLE_WP_CRON') && DISABLE_WP_CRON == true ) {
echo '<div class="notice-warning notice is-dismissible"><p>' . sprintf( __('It seems that %s is set. Ensure you have another way to run WP-Cron, otherwise NinjaFirewall automatic updates will not work.', 'ninjafirewall'), '<code>DISABLE_WP_CRON</code>' ) . '</p></div>';
$cron_disabled = 1;
}
if ( empty($nfw_options['enable_updates']) ) {
$enable_updates = 0;
} else {
$enable_updates = 1;
}
if ( empty($nfw_options['sched_updates']) || ! preg_match('/^[2-3]$/', $nfw_options['sched_updates']) ) {
$sched_updates = 1;
} else {
$sched_updates = $nfw_options['sched_updates'];
}
if ( empty($nfw_options['notify_updates']) && isset($nfw_options['notify_updates']) ) {
$notify_updates = 0;
} else {
// Defaut if not set yet
$notify_updates = 1;
}
?>
<form method="post" name="fupdates">
<?php wp_nonce_field('updates_save', 'nfwnonce', 0); ?>
<table class="form-table nfw-table">
<tr style="background-color:#F9F9F9;border: solid 1px #DFDFDF;">
<th scope="row" class="row-med"><?php _e('Automatically update NinjaFirewall security rules', 'ninjafirewall') ?></th>
<td>
<?php nfw_toggle_switch( 'danger', 'enable_updates', __('Enabled', 'ninjafirewall'), __('Disabled', 'ninjafirewall'), 'large', $enable_updates, false, 'onclick="nfwjs_up_down(\'upd_table\');"' ) ?>
</td>
</tr>
</table>
<br />
<div id="upd_table"<?php echo $enable_updates == 1 ? '' : ' style="display:none"' ?>>
<table class="form-table nfw-table">
<tr>
<th scope="row" class="row-med"><?php _e('Check for updates', 'ninjafirewall') ?> <span class="ninjafirewall-tip" data-tip="<?php esc_attr_e('In the Premium version of NinjaFirewall, you can check for security rules updates as often as every 15 minutes, versus one hour for the free WP Edition.', 'ninjafirewall' ) ?>"></span></th>
<td>
<select name="sched_updates">
<option disabled><?php _e('Every 15 minutes', 'ninjafirewall') ?> (Premium)</option>
<option disabled><?php _e('Every 30 minutes', 'ninjafirewall') ?> (Premium)</option>
<option value="1"<?php selected($sched_updates, 1) ?>><?php _e('Hourly', 'ninjafirewall') ?></option>
<option value="2"<?php selected($sched_updates, 2) ?>><?php _e('Twicedaily', 'ninjafirewall') ?></option>
<option value="3"<?php selected($sched_updates, 3) ?>><?php _e('Daily', 'ninjafirewall') ?></option>
</select>
<?php
if ( $nextcron = wp_next_scheduled('nfsecupdates') ) {
$sched = new DateTime( date('M d, Y H:i:s', $nextcron) );
$now = new DateTime( date('M d, Y H:i:s', time() ) );
$diff = $now->diff($sched);
// Ensure that the scheduled scan time is in the future,
// not in the past, otherwise send a warning because wp-cron
// is obviously not working as expected
if ( $nextcron < time() ) {
// Don't display any message if WP-CRON is disabled
if ( empty( $cron_disabled ) ) {
?>
<p class="description" style="color:red"><?php _e('The next scheduled date is in the past! WordPress wp-cron may not be working, may have been disabled or is currently running. Try to reload this page in a few seconds.', 'ninjafirewall'); ?></p>
<?php
}
} else {
?>
<p class="description"><?php printf( __('Next scheduled update will start in approximately %s day, %s hour(s), %s minute(s) and %s seconds.', 'ninjafirewall'), $diff->format('%a') % 7, $diff->format('%h'), $diff->format('%i'), $diff->format('%s') ) ?></p>
<?php
}
}
?>
</td>
</tr>
<tr>
<th scope="row" class="row-med"><?php _e('Notification', 'ninjafirewall') ?></th>
<td>
<p><label><input type="checkbox" name="notify_updates" value="1"<?php checked($notify_updates, 1) ?> /><?php _e('Send me a report by email when security rules have been updated.', 'ninjafirewall') ?></label></p>
<p class="description"><?php _e('Reports will be sent to the contact email address defined in the Event Notifications menu.', 'ninjafirewall') ?></p>
</td>
</tr>
<?php
if (! empty($nfw_options['enable_updates']) || ! empty($tmp_showlog) ) {
$log_data = array();
if ( file_exists($update_log) ) {
$log_data = file($update_log);
} else {
$log_data[] = __('The updates log is currently empty.', 'ninjafirewall');
}
?>
<tr>
<th scope="row" class="row-med"><?php _e('Updates Log', 'ninjafirewall') ?></th>
<td>
<textarea class="large-text code" style="height:200px;" wrap="off" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"><?php
$reversed = array_reverse($log_data);
$count = 0;
foreach ($reversed as $key) {
if ( $key[0] == '<' ) { continue; }
echo htmlentities($key);
++$count;
}
if ( $count == 0 ) {
_e('The updates log is currently empty.', 'ninjafirewall');
}
?></textarea>
<p class="description"><?php _e('The log is deleted automatically.', 'ninjafirewall') ?></p>
</td>
</tr>
<?php
}
?>
</table>
</div>
<p>
<input name="nfw_act" type="hidden" value="1" />
<input name="save_options" type="submit" class="button-primary" value="<?php _e('Save Updates Options', 'ninjafirewall') ?>" />
<input name="check_updates" type="submit" class="button-secondary" value="<?php _e('Check For Updates Now!', 'ninjafirewall') ?>" />
<?php
if ( empty( $enable_updates ) || ! file_exists( $update_log ) ) {
$style = ' style="display:none"';
} else {
$style = '';
}
?>
<input name="clear_log" type="submit" value="<?php _e('Delete Log', 'ninjafirewall') ?>" class="button-secondary"<?php echo $style ?> />
</p>
</form>
<?php
// ---------------------------------------------------------------------
function nf_sub_updates_save() {
$nfw_options = nfw_get_option('nfw_options');
if ( empty($_POST['sched_updates']) || ! preg_match('/^[2-3]$/', $_POST['sched_updates']) ) {
$nfw_options['sched_updates'] = 1;
$schedtype = 'hourly';
} else {
$nfw_options['sched_updates'] = $_POST['sched_updates'];
if ($nfw_options['sched_updates'] == 2) {
$schedtype = 'twicedaily';
} else {
$schedtype = 'daily';
}
}
if ( empty($_POST['enable_updates']) ) {
$nfw_options['enable_updates'] = 0;
} else {
$nfw_options['enable_updates'] = 1;
}
if ( empty($_POST['notify_updates']) ) {
$nfw_options['notify_updates'] = 0;
} else {
$nfw_options['notify_updates'] = 1;
}
nfw_update_option('nfw_options', $nfw_options);
// Recreate cronjobs if needed
nfw_create_scheduled_tasks('nfsecupdates');
}
// ---------------------------------------------------------------------
function nf_sub_updates_clearlog($update_log) {
if (file_exists($update_log) ) {
@file_put_contents( $update_log, "<?php exit; ?>\n", LOCK_EX );
}
}
// ---------------------------------------------------------------------
function nf_sub_do_updates($update_url, $update_log, $NFUPDATESDO = 1) {
// Are we installing (2) or updating (3 - deprecated since v3.8) NinjaFirewall ?
if ( $NFUPDATESDO > 1 ) {
return nf_sub_updates_download($update_url, $update_log, 0);
}
$nfw_options = nfw_get_option('nfw_options');
// Don't do anything if NinjaFirewall is disabled :
if ( empty( $nfw_options['enabled'] ) ) { return 0; }
if (! $new_rules_version = nf_sub_updates_getversion($update_url, $nfw_options['rules_version'], $update_log) ) {
// Error or nothing to update :
return;
}
// There is a new version, let's fetch it:
if (! $data = nf_sub_updates_download($update_url, $update_log, $new_rules_version) ) {
// Error :
return;
}
// Make sure we received the right format:
if (! preg_match('/^a:\d+:{i:\d/', $data ) ) {
nf_sub_updates_log(
$update_log,
__('Error: Wrong rules format.', 'ninjafirewall')
);
return 0;
}
// Unserialize the new rules :
if (! $new_rules = @unserialize($data) ) {
nf_sub_updates_log(
$update_log,
__('Error: Unable to unserialize the new rules.', 'ninjafirewall')
);
return 0;
}
// One more check...:
if (! is_array($new_rules) || empty($new_rules[1]['cha'][1]['whe']) ) {
nf_sub_updates_log(
$update_log,
__('Error: Unserialized rules seem corrupted.', 'ninjafirewall')
);
return 0;
}
// dropins code:
if ( isset( $new_rules['dropins'] ) ) {
if ( $new_rules['dropins'] == 'delete' ) {
if ( file_exists( NFW_LOG_DIR .'/nfwlog/dropins.php' ) ) {
@unlink( NFW_LOG_DIR .'/nfwlog/dropins.php' );
}
} else {
$dropins = base64_decode( $new_rules['dropins'], true );
if ( $dropins !== false ) {
@file_put_contents( NFW_LOG_DIR .'/nfwlog/dropins.php', $dropins, LOCK_EX );
}
}
unset( $new_rules['dropins'] );
}
$nfw_rules = nfw_get_option('nfw_rules');
foreach ( $new_rules as $new_key => $new_value ) {
foreach ( $new_value as $key => $value ) {
// If that rule exists already, we keep its 'ena' flag value
// as it may have been changed by the user with the rules editor:
// v3.x:
if ( ( isset( $nfw_rules[$new_key]['ena'] ) ) && ( $key == 'ena' ) ) {
$new_rules[$new_key]['ena'] = $nfw_rules[$new_key]['ena'];
}
// v1.x:
if ( ( isset( $nfw_rules[$new_key]['on'] ) ) && ( $key == 'ena' ) ) {
$new_rules[$new_key]['ena'] = $nfw_rules[$new_key]['on'];
}
}
}
// v1.x:
if ( isset( $nfw_rules[NFW_DOC_ROOT]['what'] ) ) {
$new_rules[NFW_DOC_ROOT]['cha'][1]['wha']= str_replace( '/', '/[./]*', $nfw_rules[NFW_DOC_ROOT]['what'] );
$new_rules[NFW_DOC_ROOT]['ena'] = $nfw_rules[NFW_DOC_ROOT]['on'];
// v3.x:
} else {
$new_rules[NFW_DOC_ROOT]['cha'][1]['wha']= $nfw_rules[NFW_DOC_ROOT]['cha'][1]['wha'];
$new_rules[NFW_DOC_ROOT]['ena'] = $nfw_rules[NFW_DOC_ROOT]['ena'];
}
// NFW_OBJECTS (Block serialized PHP objects): we must keep the
// value defined by the user in the Firewall Policies page:
$new_rules[NFW_OBJECTS]['cha'][1]['whe'] = $nfw_rules[NFW_OBJECTS]['cha'][1]['whe'];
// Update rules in the DB :
nfw_update_option('nfw_rules', $new_rules);
// Update rules version in the options table :
$nfw_options['rules_version'] = $new_rules_version;
nfw_update_option('nfw_options', $nfw_options);
nf_sub_updates_log(
$update_log,
sprintf( __('Security rules updated to version %s.', 'ninjafirewall'),
preg_replace('/(\d{4})(\d\d)(\d\d)/', '$1-$2-$3', $new_rules_version) )
);
// Email the admin ?
if (! empty($nfw_options['notify_updates']) ) {
nf_sub_updates_notification($new_rules_version);
}
return 1;
}
// ---------------------------------------------------------------------
function nf_sub_updates_getversion($update_url, $rules_version, $update_log) {
global $wp_version;
$res = wp_remote_get(
$update_url[0] . $update_url[1],
array(
'timeout' => 20,
'httpversion' => '1.1' ,
'user-agent' => 'Mozilla/5.0 (compatible; NinjaFirewall/'.
NFW_ENGINE_VERSION .'; WordPress/'. $wp_version . ')',
'sslverify' => true
)
);
if (! is_wp_error($res) ) {
if ( $res['response']['code'] == 200 ) {
// Get the rules version:
$new_version = explode('|', rtrim($res['body']), 2);
// Ensure that the rules are compatible :
if ( $new_version[0] != 3 ) {
if (! isset( $new_version[1] ) ) { $new_version[1] = '004'; }
// This version of NinjaFirewall may be too old :
nf_sub_updates_log(
$update_log,
sprintf( __('Error: %s', 'ninjafirewall'), $new_version[1] )
);
return 0;
}
if (! preg_match('/^\d{8}\.\d+$/', $new_version[1]) ) {
// Not what we were expecting:
nf_sub_updates_log(
$update_log,
__('Error: Unable to retrieve the new rules version.', 'ninjafirewall')
);
return 0;
}
// Compare versions:
if ( version_compare($rules_version, $new_version[1], '<') ) {
return $new_version[1];
} else {
nf_sub_updates_log(
$update_log,
__('No security rules update available.', 'ninjafirewall')
);
}
// Not a 200 OK ret code :
} else {
nf_sub_updates_log(
$update_log,
sprintf( __('Error: Server returned a %s HTTP error code (#1).', 'ninjafirewall'), htmlspecialchars($res['response']['code']))
);
}
// Connection error :
} else {
nf_sub_updates_log(
$update_log,
__('Error: Unable to connect to the remote server', 'ninjafirewall') . htmlspecialchars(" ({$res->get_error_message()})")
);
}
return 0;
}
// ---------------------------------------------------------------------
function nf_sub_updates_download($update_url, $update_log, $new_rules_version) {
global $wp_version;
$res = wp_remote_get(
$update_url[0] . $update_url[2],
array(
'timeout' => 20,
'httpversion' => '1.1' ,
'user-agent' => 'Mozilla/5.0 (compatible; NinjaFirewall/'.
NFW_ENGINE_VERSION .'; WordPress/'. $wp_version . ')',
'sslverify' => true
)
);
if (! is_wp_error($res) ) {
if ( $res['response']['code'] == 200 ) {
$data = explode('|', rtrim($res['body']), 3);
// Rules version should match the one we just fetched
// unless we are intalling NinjaFirewall ($new_rules_version==0) :
if ( $new_rules_version && $new_rules_version != $data[0]) {
nf_sub_updates_log(
$update_log,
sprintf( __('Error: The new rules versions do not match (%s != %s).', 'ninjafirewall'), $new_rules_version, htmlspecialchars($data[0]))
);
return 0;
}
// Verify rules digital signature:
if ( function_exists( 'openssl_pkey_get_public') && function_exists( 'openssl_verify' ) && defined('OPENSSL_ALGO_SHA256') ) {
$public_key = rtrim( file_get_contents( __DIR__ .'/sign.pub' ) );
$pubkeyid = openssl_pkey_get_public( $public_key );
$verify = openssl_verify( $data[2], base64_decode( $data[1] ), $pubkeyid, OPENSSL_ALGO_SHA256);
if ( $verify != 1 ) {
nf_sub_updates_log(
$update_log,
sprintf( __('Error: The new rules %s digital signature is not correct. Aborting update, rules may have been tampered with.', 'ninjafirewall'), htmlspecialchars($data[0]) )
);
return 0;
}
}
// Save new rules version for install/upgrade:
define('NFW_NEWRULES_VERSION', $data[0]);
// Return the rules:
return @$data[2];
// Not a 200 OK ret code :
} else {
nf_sub_updates_log(
$update_log,
sprintf( __('Error: Server returned a %s HTTP error code (#2).', 'ninjafirewall'), htmlspecialchars($res['response']['code']))
);
}
// Connection error :
} else {
nf_sub_updates_log(
$update_log,
__('Error: Unable to connect to the remote server', 'ninjafirewall') . htmlspecialchars(" ({$res->get_error_message()})")
);
}
return 0;
}
// ---------------------------------------------------------------------
function nf_sub_updates_log($update_log, $msg) {
// If the log is bigger than 50Kb (+/- one month old), we flush it :
if ( file_exists($update_log) ) {
$log_stat = stat($update_log);
if ( $log_stat['size'] > 51200 ) {
@file_put_contents( $update_log, "<?php exit; ?>\n", LOCK_EX );
}
} else {
@file_put_contents( $update_log, "<?php exit; ?>\n", LOCK_EX );
}
@file_put_contents($update_log, date_i18n('[d/M/y:H:i:s O]') . " $msg\n", FILE_APPEND | LOCK_EX);
}
// ---------------------------------------------------------------------
function nf_sub_updates_notification( $new_rules_version ) {
if ( is_multisite() ) {
$url = network_home_url('/');
} else {
$url = home_url('/');
}
$rules = preg_replace('/(\d{4})(\d\d)(\d\d)/', '$1-$2-$3', $new_rules_version );
/**
* Email notification.
*/
$subject = [ ];
$content = [ $url, $rules, ucfirst( date_i18n('M d, Y @ H:i:s O') ) ];
NinjaFirewall_mail::send('rules_update', $subject, $content, '', [], 1 );
}
// ---------------------------------------------------------------------
// EOF