Skip to content

Commit

Permalink
auth: add hyprlock-setpwhash target for setting the password hash
Browse files Browse the repository at this point in the history
  • Loading branch information
PaideiaDilemma committed Dec 16, 2024
1 parent 5b93c14 commit 9adc28c
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 26 deletions.
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,12 @@ protocol("protocols/wlr-screencopy-unstable-v1.xml"
protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml"
"linux-dmabuf-unstable-v1" false)

# hyprlock-setpwhash
add_executable(hyprlock-setpwhash "setpwhash/main.cpp")
target_link_libraries(hyprlock-setpwhash PRIVATE sodium hyprutils)

# Installation
install(TARGETS hyprlock)
install(TARGETS hyprlock hyprlock-setpwhash)

install(FILES ${CMAKE_SOURCE_DIR}/pam/hyprlock
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d)
Expand Down
150 changes: 150 additions & 0 deletions setpwhash/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#include "../src/helpers/Log.hpp"

#include <filesystem>
#include <hyprutils/path/Path.hpp>
#include <iostream>
#include <fstream>
#include <istream>
#include <sodium.h>
#include <string>
#include <termios.h>
#include <unistd.h>
#include <vector>

using std::filesystem::perms;

void setStdinEcho(bool enable = true) {
struct termios tty;
tcgetattr(STDIN_FILENO, &tty);
if (!enable)
tty.c_lflag &= ~ECHO;
else
tty.c_lflag |= ECHO;
RASSERT(tcsetattr(STDIN_FILENO, TCSANOW, &tty) == 0, "Failed to set terminal attributes");
}

void readPW(std::string& pw) {
setStdinEcho(false);
std::string input;
std::getline(std::cin, pw);
setStdinEcho(true);
}

enum HashCost { INTERACTIVE, MODERATE, SENSITIVE };

unsigned int getOpsLimit(HashCost cost) {
switch (cost) {
case HashCost::INTERACTIVE:
return crypto_pwhash_OPSLIMIT_INTERACTIVE;
case HashCost::MODERATE:
return crypto_pwhash_OPSLIMIT_MODERATE;
case HashCost::SENSITIVE:
return crypto_pwhash_OPSLIMIT_SENSITIVE;
default:
return crypto_pwhash_OPSLIMIT_MODERATE;
}
std::unreachable();
}

unsigned int getMemLimit(HashCost cost) {
switch (cost) {
case HashCost::INTERACTIVE:
return crypto_pwhash_MEMLIMIT_INTERACTIVE;
case HashCost::MODERATE:
return crypto_pwhash_MEMLIMIT_MODERATE;
case HashCost::SENSITIVE:
return crypto_pwhash_MEMLIMIT_SENSITIVE;
default:
return crypto_pwhash_MEMLIMIT_MODERATE;
}
std::unreachable();
}

void help() {
std::cout << "Usage: hyprlock-setpwhash [hashing cost]" << std::endl;
std::cout << "Set the password hash for hyprlock" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -h, --help Show this help message and exit" << std::endl;
std::cout << " [hashing cost] How computationally expensive should the hashing be?" << std::endl;
std::cout << " interactive - fast checking, least security" << std::endl;
std::cout << " moderate (default) - moderate checking speed, moderate security" << std::endl;
std::cout << " sensitive - slow checking speed, high security" << std::endl;
}


int main(int argc, char** argv, char** envp) {
std::vector<std::string> args(argv, argv + argc);

RASSERT(sodium_init() >= 0, "Failed to initialize libsodium");

auto hashCost = HashCost::MODERATE;
for (std::size_t i = 1; i < args.size(); ++i) {
const std::string arg = argv[i];

if (arg == "--help" || arg == "-h") {
help();
return 0;
}

if (arg == "interactive")
hashCost = HashCost::INTERACTIVE;
else if (arg == "moderate")
hashCost = HashCost::MODERATE;
else if (arg == "sensitive")
hashCost = HashCost::SENSITIVE;
else {
std::cerr << "Unknown argument: " << arg << std::endl;
help();
return 1;
}
}

static const auto [SECRETSCONF, DOTDIR] = Hyprutils::Path::findConfig("hyprlock_pwhash");
if (SECRETSCONF.has_value()) {
// check permissions
std::cout << SECRETSCONF.value() << " already exists" << std::endl;
std::cout << "Do you want to overwrite it? [y/N] ";
char C;
std::cin >> C;
std::cin.ignore(10, '\n');

if (C != 'y' && C != 'Y') {
std::cout << "Keeping existing secrets!" << std::endl;

const auto PERMS = std::filesystem::status(SECRETSCONF.value()).permissions();
if ((PERMS & perms::group_read) != perms::none || (PERMS & perms::group_write) != perms::none || (PERMS & perms::others_read) != perms::none ||
(PERMS & perms::others_write) != perms::none) {
std::cout << "Setting permissions of " << SECRETSCONF.value() << " to -rw-------" << std::endl;
// set perms to -rw-------
std::filesystem::permissions(SECRETSCONF.value(), perms::owner_read | perms::owner_write);
}
return 0;
}
}

RASSERT(DOTDIR.has_value(), "Failed to find config directory!");
const auto DEST = DOTDIR.value() + "/hypr/hyprlock_pwhash.conf";

std::string pw;
std::cout << "Enter password: ";

readPW(pw);

std::cout << "\r";

char hash[crypto_pwhash_STRBYTES];
if (crypto_pwhash_str(hash, pw.c_str(), pw.size(), getOpsLimit(hashCost), getMemLimit(hashCost)) != 0) {
Debug::log(ERR, "[Sodium] Failed to hash password");
return 1;
}

std::cout << "🔒 Writing password hash to " << DEST << std::endl;

std::ofstream out(DEST);
out << "pw_hash = " << hash << std::endl;
out.close();

// set perms to -rw-------
std::filesystem::permissions(DEST, perms::owner_read | perms::owner_write);
return 0;
}
23 changes: 0 additions & 23 deletions src/auth/SodiumPWHash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,30 +73,9 @@ void CSodiumPWHash::terminate() {
m_checkerThread.join();
}

void CSodiumPWHash::rehash(std::string& input) {
const auto CONFIGPATH = getSecretsConfigPath();

char hash[crypto_pwhash_STRBYTES];
if (crypto_pwhash_str(hash, input.c_str(), input.size(), crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) != 0) {
Debug::log(ERR, "[Sodium] Failed to hash password");
return;
}

std::ofstream out(CONFIGPATH);
out << "hyprlock {\n pw_hash = " << hash << "\n}\n";
out.close();

// set perms to -rw-------
using std::filesystem::perms;
std::filesystem::permissions(CONFIGPATH, perms::owner_read | perms::owner_write);
}

void CSodiumPWHash::checkerLoop() {
static auto* const PPWHASH = (Hyprlang::STRING*)getConfigValuePtr("pw_hash");
const auto PWHASH = std::string(*PPWHASH);
const bool NEEDSREHASH = crypto_pwhash_str_needs_rehash(PWHASH.c_str(), crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) != 0;
if (NEEDSREHASH)
Debug::log(WARN, "[Sodium] Password hash needs rehashing");

while (true) {
std::unique_lock<std::mutex> lk(m_sCheckerState.requestMutex);
Expand All @@ -110,8 +89,6 @@ void CSodiumPWHash::checkerLoop() {
Debug::log(ERR, "[SodiumAuth] Invalid password hash set in secrets.conf");
g_pAuth->enqueueFail();
} else if (crypto_pwhash_str_verify(PWHASH.c_str(), m_sCheckerState.input.c_str(), m_sCheckerState.input.length()) == 0) {
if (NEEDSREHASH)
rehash(m_sCheckerState.input);
g_pAuth->enqueueUnlock();
} else {
g_pAuth->enqueueFail();
Expand Down
2 changes: 0 additions & 2 deletions src/auth/SodiumPWHash.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,5 @@ class CSodiumPWHash : public IAuthImplementation {
std::thread m_checkerThread;
void checkerLoop();

void rehash(std::string& input);

Hyprlang::CConfig m_config;
};

0 comments on commit 9adc28c

Please sign in to comment.