Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pkg_check_modules(
gbm
hyprutils>=0.8.0
sdbus-c++>=2.0.0
libsodium
hyprgraphics>=0.1.6)
find_library(PAM_FOUND NAMES pam libpam)
if(PAM_FOUND)
Expand Down Expand Up @@ -153,8 +154,12 @@ protocolnew("stable/viewporter" "viewporter" false)
protocolnew("staging/cursor-shape" "cursor-shape-v1" false)
protocolnew("stable/tablet" "tablet-v2" false)

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

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

install(FILES ${CMAKE_SOURCE_DIR}/pam/hyprlock
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d)
Expand Down
2 changes: 2 additions & 0 deletions nix/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
cairo,
libdrm,
libGL,
libsodium,
libxkbcommon,
libgbm,
hyprgraphics,
Expand Down Expand Up @@ -39,6 +40,7 @@ stdenv.mkDerivation {
cairo
libdrm
libGL
libsodium
libxkbcommon
libgbm
hyprgraphics
Expand Down
192 changes: 192 additions & 0 deletions setpwhash/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#include "../src/helpers/Log.hpp"

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

using std::filesystem::perms;

static 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");
}

// returns the first none-whitespace char
static int getChoice() {
std::string input;
std::getline(std::cin, input);
const auto p = input.find_first_not_of(" \n");
return (p == std::string::npos) ? 0 : input[p];
}

constexpr auto CHOOSELIMITSPROMPT = R"#(
Choose how hard it will be to brute force your password.
This also defines how long it will take to check the password.
1 - interactive (least security, pretty fast checking)
2 - moderate (medium security, takes below a second on most machines)
3 - sensitive (decent security, takes around 2-4 seconds on most machines)
Type 1, 2 or 3, or Enter for default (2): )#";

static unsigned int getOpsLimit(int choice) {
switch (choice) {
case '1': return crypto_pwhash_OPSLIMIT_INTERACTIVE;
case '2': return crypto_pwhash_OPSLIMIT_MODERATE;
case '3': return crypto_pwhash_OPSLIMIT_SENSITIVE;
default: return crypto_pwhash_OPSLIMIT_MODERATE;
}
std::unreachable();
}

static unsigned int getMemLimit(int choice) {
switch (choice) {
case '1': return crypto_pwhash_MEMLIMIT_INTERACTIVE;
case '2': return crypto_pwhash_MEMLIMIT_MODERATE;
case '3': return crypto_pwhash_MEMLIMIT_SENSITIVE;
default: return crypto_pwhash_MEMLIMIT_MODERATE;
}
std::unreachable();
}

static void help() {
std::println("Usage: hyprlock-setpwhash [options]\n\n"
"Options:\n"
" -c FILE, --config FILE - Specify config file to use\n"
" -h, --help - Show this help message\n\n"
"Interactive utility to set the password hash for hyprlock");
}

static std::optional<std::string> parseArg(const std::vector<std::string>& args, const std::string& flag, std::size_t& i) {
if (i + 1 < args.size()) {
return args[++i];
} else {
std::println(stderr, "Error: Missing value for {} option.", flag);
return std::nullopt;
}
}

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

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

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

if (arg == "--help" || arg == "-h") {
help();
return 0;
} else if ((arg == "--config" || arg == "-c") && i + 1 < (std::size_t)argc) {
if (auto value = parseArg(args, arg, i); value)
configPath = *value;
else
return 1;

} else {
std::cerr << "Unknown argument: " << arg << std::endl;
help();
return 1;
}
}

std::string DEST;
const auto [SECRETSCONF, DOTDIR] = Hyprutils::Path::findConfig("hyprlock_sodium");

if (!configPath.empty())
DEST = configPath;

else if (SECRETSCONF.has_value())
DEST = SECRETSCONF.value();

else {
RASSERT(DOTDIR.has_value(), "Failed to find config directory!");
DEST = DOTDIR.value() + "/hypr/hyprlock_sodium.conf";
}

if (std::filesystem::exists(DEST)) {
// check permissions
std::println("{} already exists.", DEST);
std::print("Do you want to overwrite it? [y/N] ");
const auto CHOICE = getChoice();

if (CHOICE != 'y' && CHOICE != 'Y') {
std::println("Keeping existing secrets!");

const auto PERMS = std::filesystem::status(DEST).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::println("Setting permissions of {} to -rw-------", DEST);

// set perms to -rw-------
std::filesystem::permissions(DEST, perms::owner_read | perms::owner_write);
}
return 0;
}
}

std::println("Note: We are going to write a password hash to {}\n"
" If you choose a weak password and this hash gets leaked,\n"
" someone might be able to guess your password using a password list or brute force.\n"
" So best to keep it safe and (or) choose a good password.",
DEST);

std::print(CHOOSELIMITSPROMPT);
const auto CHOICE = getChoice();

setStdinEcho(false);
std::string pw = "";
while (true) {
std::print("New password: ");
std::getline(std::cin, pw);

if (pw.empty()) {
std::println("\rEmpty password");
continue;
}

if (pw.size() < 4) {
std::println("\rPassword too short");
continue;
}

std::string pw2 = "";
std::print("\rRepeat password: ");
std::getline(std::cin, pw2);

if (pw != pw2) {
std::println("\rPasswords do not match");
continue;
}

break;
}
setStdinEcho(true);

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

{
std::ofstream out(DEST);
// set perms to -rw-------
std::filesystem::permissions(DEST, perms::owner_read | perms::owner_write);

out << "hash = " << hash << std::endl;
}

std::println("\nDone!");
return 0;
}
9 changes: 9 additions & 0 deletions src/auth/Auth.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "Auth.hpp"
#include "Pam.hpp"
#include "SodiumPWHash.hpp"
#include "Fingerprint.hpp"
#include "../config/ConfigManager.hpp"
#include "../core/hyprlock.hpp"
Expand All @@ -10,8 +11,16 @@

CAuth::CAuth() {
static const auto ENABLEPAM = g_pConfigManager->getValue<Hyprlang::INT>("auth:pam:enabled");
static const auto ENABLESODIUM = g_pConfigManager->getValue<Hyprlang::INT>("auth:sodium:enabled");

RASSERT(!(*ENABLEPAM && *ENABLESODIUM), "Only one of PAM or Sodium authentication methods can be enabled!")

if (*ENABLEPAM)
m_vImpls.emplace_back(makeShared<CPam>());

if (*ENABLESODIUM)
m_vImpls.emplace_back(makeShared<CSodiumPWHash>());

static const auto ENABLEFINGERPRINT = g_pConfigManager->getValue<Hyprlang::INT>("auth:fingerprint:enabled");
if (*ENABLEFINGERPRINT)
m_vImpls.emplace_back(makeShared<CFingerprint>());
Expand Down
5 changes: 3 additions & 2 deletions src/auth/Auth.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
#include "../core/Timer.hpp"

enum eAuthImplementations {
AUTH_IMPL_PAM = 0,
AUTH_IMPL_FINGERPRINT = 1,
AUTH_IMPL_PAM = 0,
AUTH_IMPL_FINGERPRINT = 1,
AUTH_IMPL_SODIUMPWHASH = 2,
};

class IAuthImplementation {
Expand Down
Loading