Skip to content

Commit

Permalink
feat: support password delegates
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
andrew-grechkin committed Dec 27, 2024
1 parent fec976d commit ee7b7c3
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 17 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Binary file added example/archive.7z
Binary file not shown.
1 change: 1 addition & 0 deletions example/pass
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SECRET
3 changes: 3 additions & 0 deletions example/password-kde
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

kdialog --password "Enter password for: $1"
5 changes: 5 additions & 0 deletions example/password-pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash

echo "Password for $1" >/dev/stderr

pass show 'password/from/pass'
2 changes: 2 additions & 0 deletions include/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <string>
#include <vector>

std::string chomp(const std::string& str);

std::string utf8raw(const std::wstring& wstr, const char* encoding);

std::string utf8(const wchar_t* str);
Expand Down
108 changes: 92 additions & 16 deletions src/fuse3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "string.hpp"
#include "version.hpp"

#include <array>
#include <cstring>
#include <fstream>
#include <filesystem>
Expand Down Expand Up @@ -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<char, 1024> buffer;
std::string result;
auto cmd = std::string(path.c_str()) + " '" + _archive.c_str() + "'";
std::unique_ptr<FILE, int (*)(FILE*)> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), static_cast<int>(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<IPassword> cmd_password = std::make_unique<PasswordFromString>(std::string());

class Fuse::Params: public fuse_cmdline_opts {
public:
Expand Down Expand Up @@ -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());
Expand All @@ -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()
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions src/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
#include <memory>
#include <iconv.h>

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);
Expand Down

0 comments on commit ee7b7c3

Please sign in to comment.