From c35bbc8c30a33ec27fb511f707f8f49afc033dd5 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 5 Sep 2022 17:18:23 -0500 Subject: [PATCH] Adds the Janus_pin to shared_memory, and a -j to zmu to fetch it --- .../ZoneMinder/lib/ZoneMinder/Memory.pm.in | 1 + src/zm_monitor.cpp | 1 + src/zm_monitor.h | 8 +++-- src/zm_monitor_janus.cpp | 32 +++++++------------ src/zmu.cpp | 14 ++++++-- web/includes/Monitor.php | 7 +++- web/js/MonitorStream.js | 7 ++-- web/skins/classic/views/js/cycle.js.php | 3 +- web/skins/classic/views/js/montage.js.php | 3 +- web/skins/classic/views/js/watch.js.php | 3 +- web/skins/classic/views/js/zone.js.php | 3 +- web/skins/classic/views/js/zones.js.php | 3 +- 12 files changed, 52 insertions(+), 33 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in index 59e94fffa..f1c5972c9 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in @@ -180,6 +180,7 @@ our %mem_data = ( alarm_cause => { type=>'int8[256]', seq=>$mem_seq++ }, video_fifo => { type=>'int8[64]', seq=>$mem_seq++ }, audio_fifo => { type=>'int8[64]', seq=>$mem_seq++ }, + janus_pin => { type=>'int8[64]', seq=>$mem_seq++ }, } }, trigger_data => { type=>'TriggerData', seq=>$mem_seq++, 'contents'=> { diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 106dce871..1dab0db63 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -967,6 +967,7 @@ bool Monitor::connect() { shared_data->alarm_cause[0] = 0; shared_data->video_fifo_path[0] = 0; shared_data->audio_fifo_path[0] = 0; + shared_data->janus_pin[0] = 0; shared_data->last_frame_score = 0; shared_data->audio_frequency = -1; shared_data->audio_channels = -1; diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 038597ad0..ac9706f0e 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -228,7 +228,7 @@ protected: char alarm_cause[256]; char video_fifo_path[64]; char audio_fifo_path[64]; - + char janus_pin[64]; } SharedData; enum TriggerState : uint32 { @@ -355,7 +355,6 @@ protected: int get_janus_session(); int get_janus_handle(); int get_janus_plugin(); - std::string get_stream_key(); }; @@ -377,6 +376,7 @@ protected: bool janus_audio_enabled; // Whether we tell Janus to try to include audio. std::string janus_profile_override; // The Profile-ID to force the stream to use. bool janus_use_rtsp_restream; // Point Janus at the ZM RTSP output, rather than the camera directly. + std::string janus_pin; // For security, we generate a pin required to view the stream. std::string protocol; std::string method; @@ -639,6 +639,10 @@ public: bool JanusAudioEnabled() { return janus_audio_enabled; } + inline const char* get_stream_key() { + return shared_data->janus_pin; + } + bool OnvifEnabled() { return onvif_event_listener; } diff --git a/src/zm_monitor_janus.cpp b/src/zm_monitor_janus.cpp index 630455259..d13b5c092 100644 --- a/src/zm_monitor_janus.cpp +++ b/src/zm_monitor_janus.cpp @@ -18,6 +18,7 @@ // #include "zm_monitor.h" +#include "zm_crypt.h" #include std::string escape_json_string( std::string input ); @@ -51,6 +52,9 @@ Monitor::JanusManager::JanusManager(Monitor *parent_) : } else { rtsp_path = parent->path; } + parent->janus_pin = generateKey(16); + Debug(1, "Monitor %u assigned secret %s", parent->id, parent->janus_pin.c_str()); + strncpy(parent->shared_data->janus_pin, parent->janus_pin.c_str(), 17); //copy the null termination, as we're in C land } Monitor::JanusManager::~JanusManager() { @@ -64,6 +68,8 @@ Monitor::JanusManager::~JanusManager() { std::string postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {"; postData += "\"request\" : \"destroy\", \"admin_key\" : \""; postData += config.janus_secret; + postData += "\", \"secret\" : \""; + postData += parent->janus_pin; postData += "\", \"id\" : "; postData += std::to_string(parent->id); postData += "}}"; @@ -152,6 +158,12 @@ int Monitor::JanusManager::add_to_janus() { postData += "\", \"type\" : \"rtsp\", \"rtsp_quirk\" : true, "; postData += "\"url\" : \""; postData += rtsp_path; + //secret prevents querying the mount for info, which leaks the camera's secrets. + postData += "\", \"secret\" : \""; + postData += parent->janus_pin; + //pin prevents viewing the video. + postData += "\", \"pin\" : \""; + postData += parent->janus_pin; if (profile_override[0] != '\0') { postData += "\", \"videofmtp\" : \""; postData += profile_override; @@ -206,26 +218,6 @@ size_t Monitor::JanusManager::WriteCallback(void *contents, size_t size, size_t return size * nmemb; } -/* -void Monitor::JanusManager::generateKey() -{ - const std::string CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - - std::random_device random_device; - std::mt19937 generator(random_device()); - std::uniform_int_distribution<> distribution(0, CHARACTERS.size() - 1); - - std::string random_string; - - for (std::size_t i = 0; i < 16; ++i) - { - random_string += CHARACTERS[distribution(generator)]; - } - - stream_key = random_string; -} -*/ - int Monitor::JanusManager::get_janus_session() { janus_session = ""; curl = curl_easy_init(); diff --git a/src/zmu.cpp b/src/zmu.cpp index 41fb0cbb0..653dd30c3 100644 --- a/src/zmu.cpp +++ b/src/zmu.cpp @@ -112,6 +112,7 @@ void Usage(int status=-1) { " -q, --query : Query the current settings for the monitor\n" " -s, --state : Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm,\n" " 3 = alert, 4 = tape\n" + " -j, --janus-pin : Output the pin, if set, used to secure Janus for this monitor \n" " -B, --brightness [value] : Output the current brightness, set to value if given \n" " -C, --contrast [value] : Output the current contrast, set to value if given \n" " -H, --hue [value] : Output the current hue, set to value if given \n" @@ -170,11 +171,12 @@ typedef enum { ZMU_RESUME = 0x00800000, ZMU_LIST = 0x10000000, ZMU_TRIGGER = 0x20000000, + ZMU_JANUS = 0x40000000, } Function; bool ValidateAccess(User *user, int mon_id, int function) { bool allowed = true; - if ( function & (ZMU_STATE|ZMU_IMAGE|ZMU_TIME|ZMU_READ_IDX|ZMU_WRITE_IDX|ZMU_FPS|ZMU_TRIGGER) ) { + if ( function & (ZMU_STATE|ZMU_IMAGE|ZMU_TIME|ZMU_READ_IDX|ZMU_WRITE_IDX|ZMU_FPS|ZMU_TRIGGER|ZMU_JANUS) ) { if ( user->getStream() < User::PERM_VIEW ) allowed = false; } @@ -224,6 +226,7 @@ int main(int argc, char *argv[]) { {"scale", 1, nullptr, 'S'}, {"timestamp", 2, nullptr, 't'}, {"state", 0, nullptr, 's'}, + {"janus-pin", 0, nullptr, 'j'}, {"brightness", 2, nullptr, 'B'}, {"contrast", 2, nullptr, 'C'}, {"hue", 2, nullptr, 'H'}, @@ -282,7 +285,7 @@ int main(int argc, char *argv[]) { while (1) { int option_index = 0; - int c = getopt_long(argc, argv, "d:m:vsEDLurweix::S:t::fz::ancqhlB::C::H::O::RWU:P:A:V:T:", long_options, &option_index); + int c = getopt_long(argc, argv, "d:m:vsjEDLurweix::S:t::fz::ancqhlB::C::H::O::RWU:P:A:V:T:", long_options, &option_index); if (c == -1) { break; } @@ -301,6 +304,9 @@ int main(int argc, char *argv[]) { case 's': function |= ZMU_STATE; break; + case 'j': + function |= ZMU_JANUS; + break; case 'x': function |= ZMU_TRIGGER; break; @@ -521,6 +527,10 @@ int main(int argc, char *argv[]) { char separator = ' '; bool have_output = false; + if ( function & ZMU_JANUS ) { + printf("%s", monitor->get_stream_key()); + have_output = true; + } if ( function & ZMU_STATE ) { Monitor::State state = monitor->GetState(); if ( verbose ) { diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 17ca28cd2..c1c1c45aa 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -268,7 +268,12 @@ public static function getStatuses() { 'ArchivedEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), 'ArchivedEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), ); - + public function Janus_Pin() { + $cmd = getZmuCommand(' --janus-pin -m '.$this->{'Id'}); + $output = shell_exec($cmd); + Debug("Running $cmd output: $output"); + return trim($output); + } public function Control() { if (!property_exists($this, 'Control')) { if ($this->ControlId()) diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index ef421b876..2f0eda93f 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -11,6 +11,7 @@ function MonitorStream(monitorData) { this.width = monitorData.width; this.height = monitorData.height; this.janusEnabled = monitorData.janusEnabled; + this.janusPin = monitorData.janus_pin; this.scale = 100; this.status = {capturefps: 0, analysisfps: 0}; // json object with alarmstatus, fps etc this.lastAlarmState = STATE_IDLE; @@ -191,7 +192,7 @@ function MonitorStream(monitorData) { janus = new Janus({server: server}); //new Janus }}); } - attachVideo(parseInt(this.id)); + attachVideo(parseInt(this.id), this.janusPin); this.statusCmdTimer = setTimeout(this.statusCmdQuery.bind(this), delay); return; } @@ -683,14 +684,14 @@ function MonitorStream(monitorData) { }; } // end function MonitorStream -async function attachVideo(id) { +async function attachVideo(id, pin) { await waitUntil(() => janus.isConnected() ); janus.attach({ plugin: "janus.plugin.streaming", opaqueId: "streamingtest-"+Janus.randomString(12), success: function(pluginHandle) { streaming[id] = pluginHandle; - var body = {"request": "watch", "id": id}; + var body = {"request": "watch", "id": id, "pin": pin}; streaming[id].send({"message": body}); }, error: function(error) { diff --git a/web/skins/classic/views/js/cycle.js.php b/web/skins/classic/views/js/cycle.js.php index f91275661..144798dca 100644 --- a/web/skins/classic/views/js/cycle.js.php +++ b/web/skins/classic/views/js/cycle.js.php @@ -23,7 +23,8 @@ monitorData[monitorData.length] = { 'onclick': function(){window.location.assign( '?view=watch&mid=Id() ?>' );}, 'type': 'Type() ?>', 'refresh': 'Refresh() ?>', - 'janusEnabled': JanusEnabled() ?> + 'janusEnabled': JanusEnabled() ?>, + 'janus_pin': 'Janus_Pin() ?>' }; UrlToZMS( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>', 'onclick': function(){window.location.assign( '?view=watch&mid=Id() ?>' );}, 'type': 'Type() ?>', - 'refresh': 'Refresh() ?>' + 'refresh': 'Refresh() ?>', + 'janus_pin': 'Janus_Pin() ?>' }; UrlToIndex() ?>', 'onclick': function(){window.location.assign( '?view=watch&mid=Id() ?>' );}, 'type': 'Type() ?>', - 'refresh': 'Refresh() ?>' + 'refresh': 'Refresh() ?>', + 'janus_pin': 'Janus_Pin() ?>' }; UrlToIndex( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>', 'url_to_zms': 'UrlToZMS( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>', 'type': 'Type() ?>', - 'refresh': 'Refresh() ?>' + 'refresh': 'Refresh() ?>', + 'janus_pin': 'Janus_Pin() ?>' }; var selfIntersecting = ; diff --git a/web/skins/classic/views/js/zones.js.php b/web/skins/classic/views/js/zones.js.php index 6d3d8e86e..88aee737b 100644 --- a/web/skins/classic/views/js/zones.js.php +++ b/web/skins/classic/views/js/zones.js.php @@ -12,7 +12,8 @@ monitorData[monitorData.length] = { 'url': 'UrlToIndex( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>', 'url_to_zms': 'UrlToZMS( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>', 'type': 'Type() ?>', - 'refresh': 'Refresh() ?>' + 'refresh': 'Refresh() ?>', + 'janus_pin': 'Janus_Pin() ?>' };