From 0e06bdd1f29c6d4d8ee7b46a9432bc285ed62cb6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 14 Jan 2019 11:41:44 -0500 Subject: [PATCH 01/19] zmcuston.conf => zmcustom.conf --- docs/installationguide/debian.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installationguide/debian.rst b/docs/installationguide/debian.rst index 0d4167b8f..b4053b75a 100644 --- a/docs/installationguide/debian.rst +++ b/docs/installationguide/debian.rst @@ -130,7 +130,7 @@ CTRL+x to exit **Step 12:** Please check the configuration Zoneminder 1.32.x - 1. Check path of ZM_PATH in '/etc/zm/conf.d/zmcuston.conf' is ZM_PATH_ZMS=/zm/cgi-bin/nph-zms + 1. Check path of ZM_PATH in '/etc/zm/conf.d/zmcustom.conf' is ZM_PATH_ZMS=/zm/cgi-bin/nph-zms :: cat /etc/zm/conf.d/zmcuston.conf From 083f2845996f0fc5670ae4aca55234af14d5b436 Mon Sep 17 00:00:00 2001 From: Matt N Date: Tue, 15 Jan 2019 06:01:58 -0800 Subject: [PATCH 02/19] Replace onclick inline event handlers for createPopup (#2410) * Move - + url = el.getAttribute("href"); + } else { + // buttons + url = el.getAttribute("data-url"); + } + var name = el.getAttribute("data-window-name"); + var tag = el.getAttribute("data-window-tag"); + var width = el.getAttribute("data-window-width"); + var height = el.getAttribute("data-window-height"); + createPopup(url, name, tag, width, height); + evt.preventDefault(); + }); +}); + function createEventPopup( eventId, eventFilter, width, height ) { var url = '?view=event&eid='+eventId; if ( eventFilter ) diff --git a/web/skins/classic/views/controlcaps.php b/web/skins/classic/views/controlcaps.php index debf3afb0..e64886040 100644 --- a/web/skins/classic/views/controlcaps.php +++ b/web/skins/classic/views/controlcaps.php @@ -63,7 +63,7 @@ foreach( $controls as $control ) { ?> - + @@ -80,7 +80,8 @@ foreach( $controls as $control )
- disabled="disabled"/> + +
diff --git a/web/skins/classic/views/devices.php b/web/skins/classic/views/devices.php index 34f2efd49..3133d1aab 100644 --- a/web/skins/classic/views/devices.php +++ b/web/skins/classic/views/devices.php @@ -75,7 +75,7 @@ foreach( $devices as $device )
- /> +
diff --git a/web/skins/classic/views/logout.php b/web/skins/classic/views/logout.php index 763c9db6b..13c000b88 100644 --- a/web/skins/classic/views/logout.php +++ b/web/skins/classic/views/logout.php @@ -36,9 +36,7 @@ xhtmlHeaders(__FILE__, translate('Logout') ); - - diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 8d7e518eb..faa74e7b0 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -468,15 +468,13 @@ if ( canEdit( 'Monitors' ) ) { } ?>
- + Id(), 'zmMonitorProbe' . $monitor->Id(), 'monitorprobe', translate('Probe')); ?> - -Id(), 'zmOnvifProbe' . $monitor->Id(), 'onvifprobe', translate('OnvifProbe')); } ?> - + Id(), 'zmMonitorPreset' . $monitor->Id(), 'monitorpreset', translate('Presets')); ?>
Type() == 'Local' ) { { ?> Controllable() ) { ?> checked="checked"/> -   +   diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index a0ebdce0a..4808b0598 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -140,9 +140,7 @@ xhtmlHeaders(__FILE__, translate('Montage'));
- - @@ -201,9 +199,13 @@ foreach ( $monitors as $monitor ) {
- Id() ?>" + data-name="zmWatchId() ?>" + data-tag="watch" + data-width="Width(), $monitor->PopupScale() ); ?>" + data-height="Height(), $monitor->PopupScale() ); ?>"> +
- +
@@ -258,7 +258,7 @@ foreach ( array_map('basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
- +
@@ -299,7 +299,7 @@ foreach ( array_map('basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
- +
diff --git a/web/skins/classic/views/zones.php b/web/skins/classic/views/zones.php index e9e5f292c..7c345071e 100644 --- a/web/skins/classic/views/zones.php +++ b/web/skins/classic/views/zones.php @@ -57,7 +57,7 @@ xhtmlHeaders(__FILE__, translate('Zones') );
- disabled="disabled"/> + Width(), $monitor->Height()), translate('AddNewZone'), canEdit('Monitors')); ?>
@@ -74,7 +74,7 @@ xhtmlHeaders(__FILE__, translate('Zones') ); foreach( $zones as $zone ) { ?> - + @@ -90,7 +90,12 @@ foreach( $zones as $zone ) { - + From 07d8ac1d49ef71a5fc7c8ac3c98d01f17de7db23 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Tue, 15 Jan 2019 08:05:11 -0600 Subject: [PATCH 03/19] implement timezone check function (#2387) * implement timezone check function * remove comment * also check if the timezone is valid * whitespace --- web/includes/functions.php | 23 +++++++++++++++++++++++ web/index.php | 9 +++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index 8c540a98f..cd278ba79 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -2272,6 +2272,29 @@ function csrf_startup() { csrf_conf('rewrite-js', 'includes/csrf/csrf-magic.js'); } +function check_timezone() { + $now = new DateTime(); + + $sys_tzoffset = trim(shell_exec('date "+%z"')); + $php_tzoffset = trim($now->format('O')); + $mysql_tzoffset = trim(dbFetchOne("SELECT TIME_FORMAT(TIMEDIFF(NOW(), UTC_TIMESTAMP),'%H%i');",'TIME_FORMAT(TIMEDIFF(NOW(), UTC_TIMESTAMP),\'%H%i\')')); + + Logger::Debug("System timezone offset determine to be: $sys_tzoffset,\x20 + PHP timezone offset determine to be: $php_tzoffset,\x20 + Mysql timezone offset determine to be: $mysql_tzoffset + "); + + if ( $sys_tzoffset != $php_tzoffset ) + Fatal("ZoneMinder is not installed properly: php's date.timezone does not match the system timezone!"); + + if ( $sys_tzoffset != $mysql_tzoffset ) + Error("ZoneMinder is not installed properly: mysql's timezone does not match the system timezone! Event lists will display incorrect times."); + + if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) + Fatal( "ZoneMinder is not installed properly: php's date.timezone is not set to a valid timezone" ); + +} + function unparse_url($parsed_url, $substitutions = array() ) { $fields = array('scheme','host','port','user','pass','path','query','fragment'); diff --git a/web/index.php b/web/index.php index ef55a81d6..d9677d146 100644 --- a/web/index.php +++ b/web/index.php @@ -69,11 +69,9 @@ define('ZM_BASE_PROTOCOL', $protocol); // Use relative URL's instead define('ZM_BASE_URL', ''); -// Check time zone is set -if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) { - date_default_timezone_set('UTC'); - Fatal( "ZoneMinder is not installed properly: php's date.timezone is not set to a valid timezone" ); -} +// Verify the system, php, and mysql timezones all match +require_once('includes/functions.php'); +check_timezone(); if ( isset($_GET['skin']) ) { $skin = $_GET['skin']; @@ -155,7 +153,6 @@ if ( ZM_OPT_USE_AUTH ) { session_write_close(); require_once('includes/lang.php'); -require_once('includes/functions.php'); # Running is global but only do the daemonCheck if it is actually needed $running = null; From 34224a957b692cf1a6a6d4e06dfd0bdd00aa66cc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 15 Jan 2019 11:36:13 -0500 Subject: [PATCH 04/19] cleanup error string --- src/zm_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_utils.cpp b/src/zm_utils.cpp index adc10cf8e..49b4d5ffc 100644 --- a/src/zm_utils.cpp +++ b/src/zm_utils.cpp @@ -422,7 +422,7 @@ void touch(const char *pathname) { 0666); if ( fd < 0 ) { // Couldn't open that path. - Error("Couldn't open() path \"%s in touch", pathname); + Error("Couldn't open() path %s in touch", pathname); return; } int rc = utimensat(AT_FDCWD, From 3182d8bab72f8e4d03899ca01c3f3ba02f688256 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 15 Jan 2019 11:36:56 -0500 Subject: [PATCH 05/19] implement to_json method so that defaults get included --- web/includes/Server.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/web/includes/Server.php b/web/includes/Server.php index 7424aafdf..f638e61a9 100644 --- a/web/includes/Server.php +++ b/web/includes/Server.php @@ -214,5 +214,19 @@ class Server { return $results[0]; } + public function to_json() { + $json = array(); + foreach ($this->defaults as $key => $value) { + if ( is_callable(array($this, $key)) ) { + $json[$key] = $this->$key(); + } else if ( array_key_exists($key, $this) ) { + $json[$key] = $this->{$key}; + } else { + $json[$key] = $this->defaults{$key}; + } + } + return json_encode($json); + } + } # end class Server ?> From 07c7c271a6e2affeede379ed3a17ae82d3a18491 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 15 Jan 2019 11:38:19 -0500 Subject: [PATCH 06/19] prevent error when event has no frames. Fix PathToIndex() -> PathToIndex. Fixes #2411 --- web/skins/classic/views/js/montagereview.js | 6 +++++- web/skins/classic/views/js/montagereview.js.php | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 9c4789e4e..3c8644ac9 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -53,6 +53,10 @@ function getFrame( monId, time ) { continue; var duration = Event.EndTimeSecs - Event.StartTimeSecs; + if ( ! Event.FramesById ) { + console.log("No FramesById for event " + event_id); + return; + } var frame = parseInt((time - Event.StartTimeSecs)/(duration)*Object.keys(Event.FramesById).length)+1; // Need to get frame by time, not some fun calc that assumes frames have the same mlength. // Frames are not sorted. @@ -119,7 +123,7 @@ function getImageSource( monId, time ) { var storage = Storage[Event.StorageId]; // monitorServerId may be 0, which gives us the default Server entry var server = storage.ServerId ? Servers[storage.ServerId] : Servers[monitorServerId[monId]]; - return server.PathToIndex() + + return server.PathToIndex + '?view=image&eid=' + Frame.EventId + '&fid='+Frame.FrameId + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height; diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index 1e91a8b23..0b41866b3 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -127,7 +127,8 @@ echo "\nvar Servers = [];\n"; $Server = new Server(); echo 'Servers[0] = new Server(' . json_encode($Server). ");\n"; foreach ( Server::find() as $Server ) { - echo 'Servers[' . $Server->Id() . '] = new Server(' . json_encode($Server). ");\n"; + echo '//'.$Server->PathToIndex()."\n"; // Otherwise get null when json_encoding + echo 'Servers[' . $Server->Id() . '] = new Server(' . $Server->to_json(). ");\n"; } From ac270059448571d1078f35799e02a729f9600e79 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 15 Jan 2019 11:38:43 -0500 Subject: [PATCH 07/19] remove debug --- web/skins/classic/views/js/montagereview.js.php | 1 - 1 file changed, 1 deletion(-) diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index 0b41866b3..a31bbfa9e 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -127,7 +127,6 @@ echo "\nvar Servers = [];\n"; $Server = new Server(); echo 'Servers[0] = new Server(' . json_encode($Server). ");\n"; foreach ( Server::find() as $Server ) { - echo '//'.$Server->PathToIndex()."\n"; // Otherwise get null when json_encoding echo 'Servers[' . $Server->Id() . '] = new Server(' . $Server->to_json(). ");\n"; } From d33fec9c3f3d1b5edddefec8f93305a827c4faea Mon Sep 17 00:00:00 2001 From: Matt N Date: Wed, 16 Jan 2019 06:59:58 -0800 Subject: [PATCH 08/19] Add a CSP script-src policy with nonce-source and convert more inline event handlers (#2413) * Add Content-Security-Policy-Report-Only: script-src 'self' 'nonce-' policy * Use @data-on-click-this to attach inline click event handlers which expect being called with 'this' Only handle ones that don't return a value. * Use @data-on-click to attach inline click event handlers with no args and no return value * Use @data-on-click-true to attach inline click event handlers with 'true' as the only argument * Enforce a script-src CSP on views without inline JS * Convert some onchange attributes to data-on-change --- web/includes/Group.php | 2 +- web/includes/functions.php | 22 ++++++++ web/index.php | 4 ++ .../classic/includes/export_functions.php | 12 ++--- web/skins/classic/includes/functions.php | 9 ++-- web/skins/classic/js/skin.js | 24 +++++++++ web/skins/classic/views/_monitor_filters.php | 10 ++-- web/skins/classic/views/bandwidth.php | 2 +- web/skins/classic/views/console.php | 12 ++--- web/skins/classic/views/control.php | 2 +- web/skins/classic/views/controlcap.php | 2 +- web/skins/classic/views/controlcaps.php | 4 +- web/skins/classic/views/controlpreset.php | 2 +- web/skins/classic/views/device.php | 2 +- web/skins/classic/views/devices.php | 4 +- web/skins/classic/views/donate.php | 4 +- web/skins/classic/views/download.php | 6 +-- web/skins/classic/views/error.php | 2 +- web/skins/classic/views/event.php | 54 +++++++++---------- web/skins/classic/views/eventdetail.php | 2 +- web/skins/classic/views/export.php | 16 +++--- web/skins/classic/views/filter.php | 34 ++++++------ web/skins/classic/views/frames.php | 2 +- web/skins/classic/views/function.php | 2 +- web/skins/classic/views/group.php | 2 +- web/skins/classic/views/groups.php | 6 +-- web/skins/classic/views/js/filter.js | 3 +- web/skins/classic/views/log.php | 26 ++++----- web/skins/classic/views/logout.php | 2 +- web/skins/classic/views/monitor.php | 2 +- web/skins/classic/views/monitorpreset.php | 3 +- web/skins/classic/views/monitorprobe.php | 4 +- web/skins/classic/views/monitors.php | 2 +- web/skins/classic/views/montage.php | 6 +-- web/skins/classic/views/montagereview.php | 16 +++--- web/skins/classic/views/onvifprobe.php | 10 ++-- web/skins/classic/views/optionhelp.php | 2 +- web/skins/classic/views/options.php | 6 +-- web/skins/classic/views/plugin.php | 3 +- web/skins/classic/views/privacy.php | 2 +- web/skins/classic/views/server.php | 2 +- web/skins/classic/views/settings.php | 2 +- web/skins/classic/views/storage.php | 2 +- web/skins/classic/views/user.php | 2 +- web/skins/classic/views/version.php | 10 ++-- web/skins/classic/views/video.php | 2 +- web/skins/classic/views/watch.php | 20 +++---- web/skins/classic/views/zone.php | 2 +- web/skins/classic/views/zones.php | 4 +- 49 files changed, 215 insertions(+), 161 deletions(-) diff --git a/web/includes/Group.php b/web/includes/Group.php index 82c1daba9..89849309b 100644 --- a/web/includes/Group.php +++ b/web/includes/Group.php @@ -188,7 +188,7 @@ class Group { session_write_close(); return htmlSelect( 'Group[]', Group::get_dropdown_options(), isset($_SESSION['Group'])?$_SESSION['Group']:null, array( - 'onchange' => 'this.form.submit();', + 'data-on-change' => 'submitThisForm', 'class'=>'chosen', 'multiple'=>'multiple', 'data-placeholder'=>'All', diff --git a/web/includes/functions.php b/web/includes/functions.php index cd278ba79..6473be91b 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -35,6 +35,28 @@ function noCacheHeaders() { header('Pragma: no-cache'); // HTTP/1.0 } +function CSPHeaders($view, $nonce) { + switch ($view) { + case "bandwidth": + case "function": + case "log": + case "logout": + case "options": + case "version": { + // Enforce script-src on pages where inline scripts and event handlers have been fixed. + // 'unsafe-inline' is only for backwards compatibility with browsers which + // only support CSP 1 (with no nonce-* support). + header("Content-Security-Policy: script-src 'unsafe-inline' 'self' 'nonce-$nonce'"); + break; + } + default: { + // Use Report-Only mode on all other pages. + header("Content-Security-Policy-Report-Only: script-src 'unsafe-inline' 'self' 'nonce-$nonce'"); + break; + } + } +} + function CORSHeaders() { if ( isset($_SERVER['HTTP_ORIGIN']) ) { diff --git a/web/index.php b/web/index.php index d9677d146..29e67d628 100644 --- a/web/index.php +++ b/web/index.php @@ -172,6 +172,10 @@ $view = null; if ( isset($_REQUEST['view']) ) $view = detaintPath($_REQUEST['view']); +# Add CSP Headers +$cspNonce = bin2hex(openssl_random_pseudo_bytes(16)); +CSPHeaders($view, $cspNonce); + $request = null; if ( isset($_REQUEST['request']) ) $request = detaintPath($_REQUEST['request']); diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index 7750f165b..eb505f18b 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -263,12 +263,12 @@ function exportEventImages( $event, $exportDetail, $exportFrames, $myfilelist )

- +
 
diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index e39c56b4a..535d8ba31 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -120,7 +120,7 @@ echo output_link_if_exists( array( - "; + echo ""; } # end if tab == skins ?> From a7db6f08f58074b3bb30b12aba35044890b7864a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 16 Jan 2019 13:47:50 -0500 Subject: [PATCH 17/19] single vs double quotes --- web/skins/classic/js/skin.js.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/js/skin.js.php b/web/skins/classic/js/skin.js.php index 0748f638b..c34a4b6b4 100644 --- a/web/skins/classic/js/skin.js.php +++ b/web/skins/classic/js/skin.js.php @@ -64,7 +64,7 @@ if ( ( ! empty($closePopup) ) and ( $closePopup == true ) ) { var focusWindow = ; -var imagePrefix = ""; +var imagePrefix = ""; var auth_hash; From b1cc0c2b821a030fdeb32b7c6b6b30b3a485ac98 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 16 Jan 2019 14:04:07 -0500 Subject: [PATCH 18/19] add CSP nonce to CSRF rewriting --- web/includes/csrf/csrf-magic.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/web/includes/csrf/csrf-magic.php b/web/includes/csrf/csrf-magic.php index 55819329c..692015e70 100644 --- a/web/includes/csrf/csrf-magic.php +++ b/web/includes/csrf/csrf-magic.php @@ -150,24 +150,25 @@ function csrf_ob_handler($buffer, $flags) { return $buffer; } } + global $cspNonce; $tokens = csrf_get_tokens(); $name = $GLOBALS['csrf']['input-name']; $endslash = $GLOBALS['csrf']['xhtml'] ? ' /' : ''; $input = ""; $buffer = preg_replace('#(]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer); if ($GLOBALS['csrf']['frame-breaker']) { - $buffer = str_ireplace('', '', $buffer); + $buffer = str_ireplace('', '', $buffer); } if ($js = $GLOBALS['csrf']['rewrite-js']) { $buffer = str_ireplace( '', - ''. - '', + '', $buffer ); - $script = ''; + $script = ''; $buffer = str_ireplace('', $script . '', $buffer, $count); if (!$count) { $buffer .= $script; @@ -183,6 +184,7 @@ function csrf_ob_handler($buffer, $flags) { */ function csrf_check($fatal = true) { if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; + global $cspNonce; csrf_start(); $name = $GLOBALS['csrf']['input-name']; $ok = false; From 1f3da476b8054af380cd01102829f708b3e5a8b4 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 16 Jan 2019 14:04:24 -0500 Subject: [PATCH 19/19] switch to single quotes --- web/includes/functions.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index 6ec0d5e22..3026efaf5 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -37,12 +37,12 @@ function noCacheHeaders() { function CSPHeaders($view, $nonce) { switch ($view) { - case "bandwidth": - case "function": - case "log": - case "logout": - case "options": - case "version": { + case 'bandwidth': + case 'function': + case 'log': + case 'logout': + case 'options': + case 'version': { // Enforce script-src on pages where inline scripts and event handlers have been fixed. // 'unsafe-inline' is only for backwards compatibility with browsers which // only support CSP 1 (with no nonce-* support).
Width(), $monitor->Height()), $zone['Name'], true, 'onclick="streamCmdQuit( true ); return( false );"'); ?>  / Width()*$monitor->Height()) ) ?> disabled="disabled"/>