From 2c37f6565d59b8d3404ae8321182cc3e33f0f67c Mon Sep 17 00:00:00 2001 From: Earnestly Date: Sat, 7 Feb 2026 22:15:56 +0000 Subject: [PATCH] Support for reading passwords from a specified fd This initial work adds the -pfd[N] flag to the 7z command so that an alternate file descriptor (fd) may be specified for reading the password instead of standard input (stdin). By adding this flag it becomes possible for 7z to accept data from stdin for use with the -si flag while also being being able to decrypt a password protected achieve without revealing the password on the command line. For example, generating a secret key and storing it in an encrypted archive without the need to expose any of the data to a filesystem: age-keygen | 7z a -pfd9 9< <(pass show archive) -siid.age archive.7z As a side effect the password is not echoed to the terminal, however this PR should not conflict with the work in #33. Note that the -p flag is necessary if the archive does not exist but should not be used if it does. --- CPP/7zip/UI/Common/ArchiveCommandLine.cpp | 27 +++++++++++++++++++ CPP/7zip/UI/Common/ArchiveCommandLine.h | 1 + CPP/7zip/UI/Console/List.cpp | 3 ++- CPP/7zip/UI/Console/List.h | 2 +- CPP/7zip/UI/Console/Main.cpp | 5 ++++ CPP/7zip/UI/Console/OpenCallbackConsole.cpp | 9 +++++++ CPP/7zip/UI/Console/OpenCallbackConsole.h | 1 + CPP/7zip/UI/Console/UpdateCallbackConsole.cpp | 19 +++++++++++++ CPP/7zip/UI/Console/UpdateCallbackConsole.h | 1 + CPP/7zip/UI/Console/UserInputUtils.h | 1 + 10 files changed, 67 insertions(+), 2 deletions(-) diff --git a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp index 7fe18fbc1..ae26ed1d7 100644 --- a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp +++ b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp @@ -120,6 +120,15 @@ static bool StringToUInt32(const wchar_t *s, UInt32 &v) return *end == 0; } +static bool StringToInt32(const wchar_t *s, Int32 &v) +{ + if (*s == 0) + return false; + const wchar_t *end; + v = ConvertStringToInt32(s, &end); + return *end == 0; +} + namespace NKey { enum Enum @@ -209,6 +218,7 @@ enum Enum #ifndef Z7_NO_CRYPTO , kPassword + , kPasswordFd #endif }; @@ -360,6 +370,7 @@ static const CSwitchForm kSwitchForms[] = #ifndef Z7_NO_CRYPTO , { "p", SWFRM_STRING } + , { "pfd", SWFRM_STRING } #endif }; @@ -1460,6 +1471,22 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options) options.PasswordEnabled = parser[NKey::kPassword].ThereIs; if (options.PasswordEnabled) options.Password = parser[NKey::kPassword].PostStrings[0]; + + options.PasswordFd = 0; + + if (parser[NKey::kPasswordFd].ThereIs) + { + const UString &s = parser[NKey::kPasswordFd].PostStrings[0]; + if (s.IsEmpty()) + throw CArcCmdLineException("No file descriptor given to -pfd", s); + else + { + Int32 v; + if (!StringToInt32(s, v)) + throw CArcCmdLineException("A file descriptor is required for -pfd", s); + options.PasswordFd = (int)v; + } + } #endif options.ShowDialog = parser[NKey::kShowDialog].ThereIs; diff --git a/CPP/7zip/UI/Common/ArchiveCommandLine.h b/CPP/7zip/UI/Common/ArchiveCommandLine.h index d17ec5a3c..bf7aa92a2 100644 --- a/CPP/7zip/UI/Common/ArchiveCommandLine.h +++ b/CPP/7zip/UI/Common/ArchiveCommandLine.h @@ -89,6 +89,7 @@ struct CArcCmdLineOptions #ifndef Z7_NO_CRYPTO bool PasswordEnabled; UString Password; + int PasswordFd; #endif UStringVector HashMethods; diff --git a/CPP/7zip/UI/Console/List.cpp b/CPP/7zip/UI/Console/List.cpp index 874caef3c..8b346d1e9 100644 --- a/CPP/7zip/UI/Console/List.cpp +++ b/CPP/7zip/UI/Console/List.cpp @@ -1081,7 +1081,7 @@ HRESULT ListArchives( const NWildcard::CCensorNode &wildcardCensor, bool enableHeaders, bool techMode, #ifndef Z7_NO_CRYPTO - bool &passwordEnabled, UString &password, + bool &passwordEnabled, UString &password, int &passwordFd, #endif #ifndef Z7_SFX const CObjectVector *props, @@ -1161,6 +1161,7 @@ HRESULT ListArchives( openCallback.PasswordIsDefined = passwordEnabled; openCallback.Password = password; + openCallback.PasswordFd = passwordFd; #endif diff --git a/CPP/7zip/UI/Console/List.h b/CPP/7zip/UI/Console/List.h index d87f512b6..ab43731c4 100644 --- a/CPP/7zip/UI/Console/List.h +++ b/CPP/7zip/UI/Console/List.h @@ -31,7 +31,7 @@ HRESULT ListArchives( const NWildcard::CCensorNode &wildcardCensor, bool enableHeaders, bool techMode, #ifndef Z7_NO_CRYPTO - bool &passwordEnabled, UString &password, + bool &passwordEnabled, UString &password, int &passwordFd, #endif #ifndef Z7_SFX const CObjectVector *props, diff --git a/CPP/7zip/UI/Console/Main.cpp b/CPP/7zip/UI/Console/Main.cpp index 90e00a495..86f7e42c7 100644 --- a/CPP/7zip/UI/Console/Main.cpp +++ b/CPP/7zip/UI/Console/Main.cpp @@ -163,6 +163,7 @@ static const char * const kHelpString = " -o{Directory} : set Output directory\n" #ifndef Z7_NO_CRYPTO " -p{Password} : set Password\n" + " -pfd{N} : read Password from fd\n" #endif " -r[-|0] : Recurse subdirectories for name search\n" " -sa{a|e|s} : set Archive name mode\n" @@ -1333,6 +1334,7 @@ int Main2( #ifndef Z7_NO_CRYPTO ecs->PasswordIsDefined = options.PasswordEnabled; ecs->Password = options.Password; + ecs->PasswordFd = options.PasswordFd; #endif ecs->Init(g_StdStream, g_ErrStream, percentsStream, options.DisablePercents); @@ -1517,6 +1519,7 @@ int Main2( #ifndef Z7_NO_CRYPTO options.PasswordEnabled, options.Password, + options.PasswordFd, #endif &options.Properties, numErrors, numWarnings); @@ -1551,6 +1554,7 @@ int Main2( (options.PasswordEnabled && !options.Password.IsEmpty()); openCallback.PasswordIsDefined = passwordIsDefined; openCallback.Password = options.Password; + openCallback.PasswordFd = options.PasswordFd; #endif CUpdateCallbackConsole callback; @@ -1564,6 +1568,7 @@ int Main2( callback.PasswordIsDefined = passwordIsDefined; callback.AskPassword = (options.PasswordEnabled && options.Password.IsEmpty()); callback.Password = options.Password; + callback.PasswordFd = options.PasswordFd; #endif callback.StdOutMode = uo.StdOutMode; diff --git a/CPP/7zip/UI/Console/OpenCallbackConsole.cpp b/CPP/7zip/UI/Console/OpenCallbackConsole.cpp index 1e7adf5d4..58b53b32a 100644 --- a/CPP/7zip/UI/Console/OpenCallbackConsole.cpp +++ b/CPP/7zip/UI/Console/OpenCallbackConsole.cpp @@ -87,6 +87,15 @@ HRESULT COpenCallbackConsole::Open_CryptoGetTextPassword(BSTR *password) if (!PasswordIsDefined) { ClosePercents(); + if (PasswordFd) { + FILE *_file = fdopen(PasswordFd, "r"); + + if (!_file) + return S_FALSE; + + g_StdIn = CStdInStream(_file); + } + RINOK(GetPassword_HRESULT(_so, Password)) PasswordIsDefined = true; } diff --git a/CPP/7zip/UI/Console/OpenCallbackConsole.h b/CPP/7zip/UI/Console/OpenCallbackConsole.h index 5e7c19cc0..9f8bdf6e1 100644 --- a/CPP/7zip/UI/Console/OpenCallbackConsole.h +++ b/CPP/7zip/UI/Console/OpenCallbackConsole.h @@ -67,6 +67,7 @@ class COpenCallbackConsole: public IOpenCallbackUI bool PasswordIsDefined; // bool PasswordWasAsked; UString Password; + int PasswordFd; #endif }; diff --git a/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp b/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp index 5185d5cc8..16006f5a0 100644 --- a/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp +++ b/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp @@ -828,6 +828,16 @@ HRESULT CUpdateCallbackConsole::CryptoGetTextPassword2(Int32 *passwordIsDefined, if (!PasswordIsDefined) { + if (PasswordFd) { + FILE *_file = fdopen(PasswordFd, "r"); + + if (!_file) + return S_FALSE; + + g_StdIn = CStdInStream(_file); + } + + if (AskPassword) { RINOK(GetPassword_HRESULT(_so, Password)) @@ -857,6 +867,15 @@ HRESULT CUpdateCallbackConsole::CryptoGetTextPassword(BSTR *password) if (!PasswordIsDefined) { { + if (PasswordFd) { + FILE *_file = fdopen(PasswordFd, "r"); + + if (!_file) + return S_FALSE; + + g_StdIn = CStdInStream(_file); + } + RINOK(GetPassword_HRESULT(_so, Password)) PasswordIsDefined = true; } diff --git a/CPP/7zip/UI/Console/UpdateCallbackConsole.h b/CPP/7zip/UI/Console/UpdateCallbackConsole.h index a3863711c..2a4d9a54e 100644 --- a/CPP/7zip/UI/Console/UpdateCallbackConsole.h +++ b/CPP/7zip/UI/Console/UpdateCallbackConsole.h @@ -122,6 +122,7 @@ class CUpdateCallbackConsole Z7_final: bool PasswordIsDefined; bool AskPassword; UString Password; + int PasswordFd; #endif CUpdateCallbackConsole(): diff --git a/CPP/7zip/UI/Console/UserInputUtils.h b/CPP/7zip/UI/Console/UserInputUtils.h index 695a3e66d..d8ffd1c2c 100644 --- a/CPP/7zip/UI/Console/UserInputUtils.h +++ b/CPP/7zip/UI/Console/UserInputUtils.h @@ -3,6 +3,7 @@ #ifndef ZIP7_INC_USER_INPUT_UTILS_H #define ZIP7_INC_USER_INPUT_UTILS_H +#include "../../../Common/StdInStream.h" #include "../../../Common/StdOutStream.h" namespace NUserAnswerMode {