From ee7b7c3cef4a5c8c482f78a78871dd76041de21d Mon Sep 17 00:00:00 2001 From: Andrew Grechkin Date: Fri, 27 Dec 2024 16:09:15 +0100 Subject: [PATCH] feat: support password delegates Passfile can be an executable file. If this is the case, it will be called and it's output will be used as a password. This allows to use a password manager to store passwords or provide passwords interactively. --- CMakeLists.txt | 2 +- README.md | 24 ++++++++++ example/archive.7z | Bin 0 -> 333 bytes example/pass | 1 + example/password-kde | 3 ++ example/password-pass | 5 ++ include/string.hpp | 2 + src/fuse3.cpp | 108 +++++++++++++++++++++++++++++++++++------- src/string.cpp | 9 ++++ 9 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 example/archive.7z create mode 100644 example/pass create mode 100755 example/password-kde create mode 100755 example/password-pass diff --git a/CMakeLists.txt b/CMakeLists.txt index 85ab886..64e8840 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required (VERSION 3.12 FATAL_ERROR) project (fuse3-p7zip - VERSION 2.0.0 + VERSION 2.1.0 DESCRIPTION "fuse3 file system that uses the 7zip library to mount archives" LANGUAGES CXX ) diff --git a/README.md b/README.md index 26c0ad8..cb0034f 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,13 @@ Change where application searches for 7z.so library. By default this is `/usr/li Provide a password which will be used if archive is protected with a password. +* FUSE3_P7ZIP_PASSFILE + +Provide a password file which will be used if archive is protected with a password. + + If executable file is provided then its output to /dev/stdout will be used as a password. + If non-executable file is provided then its content will be used as a password. + * FUSE3_P7ZIP_FORCE_ENCODING Some archives (especially those ones created on windows) have file names encoded in non Unicode encoding. This @@ -97,6 +104,23 @@ To check the list of all available formats run `7z i` command ## Examples +### password protected archive, request password interactively (KDE password popup window) + +```bash +$ fuse3-p7zip example/archive.7z /tmp/mnt --passfile=example/password-kde +``` + +```bash +$ export FUSE3_P7ZIP_PASSFILE=example/password-kde +$ fuse3-p7zip example/archive.7z /tmp/mnt +``` + +### password protected archive, fetch password from a password storage + +```bash +$ fuse3-p7zip example/archive.7z /tmp/mnt --passfile=example/password-pass +``` + ### vifm config snippet (mount 7zip supported archive with fuse3-p7zip in vifm) > I suggest to use [archivemount](https://github.com/cybernoid/archivemount) for all tar-based archives as a priority and diff --git a/example/archive.7z b/example/archive.7z new file mode 100644 index 0000000000000000000000000000000000000000..4aa182b94809abbea36b434461773a4ffe3ca40c GIT binary patch literal 333 zcmV-T0kZx#dc3bE8~_B$&xe}|0RR910000Z0000000023uV}T?!ae->p-nx1TC;aw#-z#5R1nuVitJSMYWvJ*& zYU{5jh=so(IJk51P>lFkct%_hY{9ERa^Tg`jkN%ax|M$re$~Qi9_zUY>EDfP$mra1 z@=kt`Z*E%o0i_%O0D&_Ht`F5UC$VWhI@8V&0CakBjqmjZCOh$X9{d?S!BF1J;L;Wx zOYb0twkr9gJIDp`dvDDbE4oNrY! z00Z)u|KuvZ@d(8?$s2dTXSlnE@v1RJaFb&lhd>e0wP$rfxUebAU?%_o02c;;fB^}B fiU0=-0RRCb0|5aAT>uaO01SYx3IWy-^DY1YwJnuZ literal 0 HcmV?d00001 diff --git a/example/pass b/example/pass new file mode 100644 index 0000000..e97e9c7 --- /dev/null +++ b/example/pass @@ -0,0 +1 @@ +SECRET diff --git a/example/password-kde b/example/password-kde new file mode 100755 index 0000000..da3520a --- /dev/null +++ b/example/password-kde @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +kdialog --password "Enter password for: $1" diff --git a/example/password-pass b/example/password-pass new file mode 100755 index 0000000..22cba20 --- /dev/null +++ b/example/password-pass @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +echo "Password for $1" >/dev/stderr + +pass show 'password/from/pass' diff --git a/include/string.hpp b/include/string.hpp index 1ab5831..573c5dd 100644 --- a/include/string.hpp +++ b/include/string.hpp @@ -4,6 +4,8 @@ #include #include +std::string chomp(const std::string& str); + std::string utf8raw(const std::wstring& wstr, const char* encoding); std::string utf8(const wchar_t* str); diff --git a/src/fuse3.cpp b/src/fuse3.cpp index 40f7ee1..bb60b5f 100644 --- a/src/fuse3.cpp +++ b/src/fuse3.cpp @@ -5,6 +5,7 @@ #include "string.hpp" #include "version.hpp" +#include #include #include #include @@ -37,7 +38,71 @@ static struct cmd_params_t { static const fuse_opt opts_spec[] = {CMD_OPT("--password=%s", password), CMD_OPT("-p %s", password), CMD_OPT("--passfile=%s", passfile), FUSE_OPT_END}; -static std::string cmd_password; +class IPassword { +public: + virtual ~IPassword() noexcept = default; + + virtual std::string get() noexcept = 0; +}; + +class PasswordFromString: public IPassword { +public: + PasswordFromString(const std::string& password) noexcept + : _password(password) + {} + std::string get() noexcept + { + return _password; + } + +private: + std::string _password; +}; + +class PasswordFromFile: public IPassword { +public: + PasswordFromFile(const std::filesystem::path& path, const std::filesystem::path& arc) noexcept + : _archive(arc) + , _path(path) + {} + std::string get() noexcept + { + if (_password.empty()) { + if (access(_path.c_str(), X_OK) == 0) { + _password = chomp(exec(_path)); + } else { + std::ifstream passfile(_path); + std::istreambuf_iterator it(passfile); + std::string pass(it, {}); + _password = chomp(pass); + } + } + return _password; + } + +private: + std::string exec(const std::filesystem::path& path) + { + std::array buffer; + std::string result; + auto cmd = std::string(path.c_str()) + " '" + _archive.c_str() + "'"; + std::unique_ptr pipe(popen(cmd.c_str(), "r"), pclose); + if (!pipe) { + throw std::runtime_error("popen() failed!"); + } + while (fgets(buffer.data(), static_cast(buffer.size()), pipe.get()) != nullptr) { + result += buffer.data(); + } + + return result; + } + + std::filesystem::path _archive; + std::filesystem::path _path; + std::string _password; +}; + +static std::unique_ptr cmd_password = std::make_unique(std::string()); class Fuse::Params: public fuse_cmdline_opts { public: @@ -65,20 +130,6 @@ Fuse::Params::Params(int argc, char** argv) CheckResult(fuse_parse_cmdline(&args, this) == 0, "fuse_parse_cmdline"); - if (cmd_params.password) { - cmd_password = cmd_params.password; - } else if (cmd_params.passfile) { - std::ifstream passfile(cmd_params.passfile); - std::istreambuf_iterator it(passfile); - std::string pass(it, {}); - cmd_password = pass; - } else { - auto password = std::getenv("FUSE3_P7ZIP_PASSWORD"); - if (password) { - cmd_password = password; - } - } - if (show_version) { printf("%s\n", PROJECT_VERSION); // printf("FUSE library version %s\n", fuse_pkgversion()); @@ -93,6 +144,31 @@ Fuse::Params::Params(int argc, char** argv) fprintf(stderr, "error: no mountpoint specified\n"); exit(0); } + + auto env_password = std::getenv("FUSE3_P7ZIP_PASSWORD"); + if (env_password) { + LogDebug("using FUSE3_P7ZIP_PASSWORD\n"); + cmd_password.reset(new PasswordFromString(env_password)); + } + + if (cmd_params.password) { + LogDebug("using --password\n"); + cmd_password.reset(new PasswordFromString(cmd_params.password)); + } + + auto env_passfile = std::getenv("FUSE3_P7ZIP_PASSFILE"); + if (env_passfile && std::filesystem::exists(env_passfile) && std::filesystem::is_regular_file(env_passfile)) { + auto path = std::filesystem::absolute(std::filesystem::path(env_passfile)); + LogDebug("using FUSE3_P7ZIP_PASSFILE: %s\n", path.c_str()); + cmd_password.reset(new PasswordFromFile(path, cmd_params.cli_args[0])); + } + + if (cmd_params.passfile && std::filesystem::exists(cmd_params.passfile) && + std::filesystem::is_regular_file(cmd_params.passfile)) { + auto path = std::filesystem::absolute(std::filesystem::path(cmd_params.passfile)); + LogDebug("using --passfile: %s\n", path.c_str()); + cmd_password.reset(new PasswordFromFile(path, cmd_params.cli_args[0])); + } } void Fuse::Params::print_usage() @@ -249,7 +325,7 @@ const std::string& Fuse::path() const std::string Fuse::password() const { - return cmd_password; + return cmd_password->get(); } ssize_t Fuse::execute(sevenzip::IArchive* arc) diff --git a/src/string.cpp b/src/string.cpp index 9601e14..d35c32e 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -4,6 +4,15 @@ #include #include +std::string chomp(const std::string& str) +{ + auto pos = str.find_last_not_of("\n"); + if (pos != std::string::npos) { + return str.substr(0, pos + 1); + } + return str; +} + std::string utf8raw(const std::wstring& wstr, const char* encoding) { return utf8(std::string(wstr.begin(), wstr.end()).c_str(), encoding);