From 8674b32a15806f3b9d695d0a3017c9409555370b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 18 Apr 2022 13:33:10 -0400 Subject: [PATCH 1/3] Introduce mask_authentication function to replace username and password with * in url like strings --- src/zm_utils.cpp | 29 +++++++++++++++++++++++++++++ src/zm_utils.h | 2 ++ 2 files changed, 31 insertions(+) diff --git a/src/zm_utils.cpp b/src/zm_utils.cpp index 6ea4e0dae..8b7fb9ecd 100644 --- a/src/zm_utils.cpp +++ b/src/zm_utils.cpp @@ -442,3 +442,32 @@ std::string QueryString::parseValue(std::istream &input) { return UriDecode(url_encoded_value); } + +std::string mask_authentication(const std::string &url) { + std::string masked_url = url; + std::size_t at_at = masked_url.find("@"); + if (at_at == std::string::npos) { + return masked_url; + } + std::size_t password_at = masked_url.rfind(":", at_at); + + if (password_at == std::string::npos) { + // no : means no http:// either so something liek username@192.168.1.1 + masked_url.replace(0, at_at, at_at, '*'); + } else if (masked_url[password_at+1] == '/') { + // no password, something like http://username@192.168.1.1 + masked_url.replace(password_at+3, at_at-(password_at+3), at_at-(password_at+3), '*'); + } else { + // have username and password, something like http://username:password@192.168.1.1/ + masked_url.replace(password_at+1, at_at - (password_at+1), at_at - (password_at+1), '*'); + std::size_t username_at = masked_url.rfind("/", password_at); + if (username_at == std::string::npos) { + // Something like username:password@192.168.1.1 + masked_url.replace(0, password_at, password_at, '*'); + } else { + masked_url.replace(username_at+1, password_at-(username_at+1), password_at-(username_at+1), '*'); + // something like http://username:password@192.168.1.1/ + } + } + return masked_url; +} diff --git a/src/zm_utils.h b/src/zm_utils.h index d5ee88202..b9e978238 100644 --- a/src/zm_utils.h +++ b/src/zm_utils.h @@ -127,6 +127,8 @@ template constexpr std::size_t size(const T(&)[N]) noexcept { return N; } } +std::string mask_authentication(const std::string &url); + std::string UriDecode(const std::string &encoded); class QueryParameter { From c3d7c306fccfcf1ad991cc5671399e0b022c5e3a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 18 Apr 2022 13:33:22 -0400 Subject: [PATCH 2/3] Add tests for mask_authentication --- tests/zm_utils.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/zm_utils.cpp b/tests/zm_utils.cpp index 43b6a4a71..c09d5bdc6 100644 --- a/tests/zm_utils.cpp +++ b/tests/zm_utils.cpp @@ -250,3 +250,31 @@ TEST_CASE("QueryString") { REQUIRE(p2->values()[0] == "value2"); } } + +TEST_CASE("mask_authentication") { + SECTION("no authentication") { + std::string url("http://192.168.1.1"); + std::string result = mask_authentication(url); + REQUIRE(url == result); + } + SECTION("has username no password has scheme") { + std::string url("http://username@192.168.1.1"); + std::string result = mask_authentication(url); + REQUIRE(result == "http://********@192.168.1.1"); + } + SECTION("has username no password no scheme") { + std::string url("username@192.168.1.1"); + std::string result = mask_authentication(url); + REQUIRE(result == "********@192.168.1.1"); + } + SECTION("has username has password no scheme") { + std::string url("username:password@192.168.1.1"); + std::string result = mask_authentication(url); + REQUIRE(result == "********:********@192.168.1.1"); + } + SECTION("has username has password has scheme") { + std::string url("http://username:password@192.168.1.1"); + std::string result = mask_authentication(url); + REQUIRE(result == "http://********:********@192.168.1.1"); + } +} From 57fdbda7ec918732a02ee4a28c41e146b3cd1be6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 18 Apr 2022 13:33:58 -0400 Subject: [PATCH 3/3] add mMaskedPath and mMaskedSecondPath and use them when logging so that usernames and passwords are shown in logs. Fixes #3118 --- src/zm_ffmpeg_camera.cpp | 18 ++++++++++-------- src/zm_ffmpeg_camera.h | 2 ++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 117ddf422..d61b48636 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -118,6 +118,8 @@ FfmpegCamera::FfmpegCamera( hwaccel_name(p_hwaccel_name), hwaccel_device(p_hwaccel_device) { + mMaskedPath = mask_authentication(mPath); + mMaskedSecondPath = mask_authentication(mSecondPath); if ( capture ) { FFMPEGInit(); } @@ -160,12 +162,12 @@ FfmpegCamera::~FfmpegCamera() { int FfmpegCamera::PrimeCapture() { start_read_time = std::chrono::steady_clock::now(); if ( mCanCapture ) { - Debug(1, "Priming capture from %s, Closing", mPath.c_str()); + Debug(1, "Priming capture from %s, Closing", mMaskedPath.c_str()); Close(); } mVideoStreamId = -1; mAudioStreamId = -1; - Debug(1, "Priming capture from %s", mPath.c_str()); + Debug(1, "Priming capture from %s", mMaskedPath.c_str()); return OpenFfmpeg(); } @@ -282,7 +284,7 @@ int FfmpegCamera::OpenFfmpeg() { } } // end if RTSP - Debug(1, "Calling avformat_open_input for %s", mPath.c_str()); + Debug(1, "Calling avformat_open_input for %s", mMaskedPath.c_str()); mFormatContext = avformat_alloc_context(); mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback; @@ -308,7 +310,7 @@ int FfmpegCamera::OpenFfmpeg() { ret = avformat_find_stream_info(mFormatContext, nullptr); if (ret < 0) { Error("Unable to find stream info from %s due to: %s", - mPath.c_str(), av_make_error_string(ret).c_str()); + mMaskedPath.c_str(), av_make_error_string(ret).c_str()); avformat_close_input(&mFormatContext); return -1; } @@ -361,7 +363,7 @@ int FfmpegCamera::OpenFfmpeg() { mVideoCodec = avcodec_find_decoder(mVideoStream->codecpar->codec_id); if (!mVideoCodec) { // Try and get the codec from the codec context - Error("Can't find codec for video stream from %s", mPath.c_str()); + Error("Can't find codec for video stream from %s", mMaskedPath.c_str()); return -1; } } @@ -470,7 +472,7 @@ int FfmpegCamera::OpenFfmpeg() { Warning("Option %s not recognized by ffmpeg", e->key); } if (ret < 0) { - Error("Unable to open codec for video stream from %s", mPath.c_str()); + Error("Unable to open codec for video stream from %s", mMaskedPath.c_str()); av_dict_free(&opts); return -1; } @@ -491,7 +493,7 @@ int FfmpegCamera::OpenFfmpeg() { if ( mAudioStreamId >= 0 ) { const AVCodec *mAudioCodec = nullptr; if (!(mAudioCodec = avcodec_find_decoder(mAudioStream->codecpar->codec_id))) { - Debug(1, "Can't find codec for audio stream from %s", mPath.c_str()); + Debug(1, "Can't find codec for audio stream from %s", mMaskedPath.c_str()); } else { mAudioCodecContext = avcodec_alloc_context3(mAudioCodec); avcodec_parameters_to_context(mAudioCodecContext, mAudioStream->codecpar); @@ -499,7 +501,7 @@ int FfmpegCamera::OpenFfmpeg() { zm_dump_stream_format((mSecondFormatContext?mSecondFormatContext:mFormatContext), mAudioStreamId, 0, 0); // Open the codec if (avcodec_open2(mAudioCodecContext, mAudioCodec, nullptr) < 0) { - Error("Unable to open codec for audio stream from %s", mPath.c_str()); + Error("Unable to open codec for audio stream from %s", mMaskedPath.c_str()); return -1; } // end if opened } // end if found decoder diff --git a/src/zm_ffmpeg_camera.h b/src/zm_ffmpeg_camera.h index e320d3b31..42b716b03 100644 --- a/src/zm_ffmpeg_camera.h +++ b/src/zm_ffmpeg_camera.h @@ -36,7 +36,9 @@ typedef struct DecodeContext { class FfmpegCamera : public Camera { protected: std::string mPath; + std::string mMaskedPath; std::string mSecondPath; + std::string mMaskedSecondPath; std::string mMethod; std::string mOptions;