feat: implement username whitelist

This commit is contained in:
ExploHash 2025-12-12 14:57:19 +01:00
commit 3777d0801c
2 changed files with 58 additions and 9 deletions

View file

@ -93,3 +93,4 @@ x_cmd: []const u8 = build_options.prefix_directory ++ "/bin/X",
xauth_cmd: []const u8 = build_options.prefix_directory ++ "/bin/xauth",
xinitrc: ?[]const u8 = "~/.xinitrc",
xsessions: []const u8 = build_options.prefix_directory ++ "/share/xsessions",
username_whitelist: ?[]const u8 = null,

View file

@ -223,7 +223,7 @@ pub fn main() !void {
}
var maybe_uid_range_error: ?anyerror = null;
var usernames = try getAllUsernames(allocator, config.login_defs_path, &maybe_uid_range_error);
var usernames = try getAllUsernames(allocator, config.login_defs_path, config.username_whitelist, &maybe_uid_range_error);
defer {
for (usernames.items) |username| allocator.free(username);
usernames.deinit(allocator);
@ -486,7 +486,7 @@ pub fn main() !void {
const auto_user = config.auto_login_user orelse break :check_autologin;
const auto_session = config.auto_login_session orelse break :check_autologin;
if (!isValidUsername(auto_user, usernames)) {
if (!stringListContains(auto_user, usernames)) {
try info_line.addMessage(lang.err_pam_user_unknown, config.error_bg, config.error_fg);
try log_writer.print("autologin failed: username '{s}' not found\n", .{auto_user});
break :check_autologin;
@ -1294,9 +1294,9 @@ fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: Displa
}
}
fn isValidUsername(username: []const u8, usernames: StringList) bool {
for (usernames.items) |valid_username| {
if (std.mem.eql(u8, username, valid_username)) return true;
fn stringListContains(needle: []const u8, list: StringList) bool {
for (list.items) |valid_username| {
if (std.mem.eql(u8, needle, valid_username)) return true;
}
return false;
}
@ -1314,7 +1314,8 @@ fn findSessionByName(session: *Session, name: []const u8) ?usize {
return null;
}
fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8, uid_range_error: *?anyerror) !StringList {
fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8, username_whitelist_string: ?[]const u8, uid_range_error: *?anyerror) !StringList {
// Find all valid usernames
const uid_range = interop.getUserIdRange(allocator, login_defs_path) catch |err| no_uid_range: {
uid_range_error.* = err;
break :no_uid_range UidRange{
@ -1323,13 +1324,30 @@ fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8, ui
};
};
var whitelisted_usernames: StringList = .empty;
if (username_whitelist_string) |s| {
whitelisted_usernames = try parseCSVString(allocator, s);
}
var usernames: StringList = .empty;
var maybe_entry = interop.getNextUsernameEntry();
while (maybe_entry) |entry| {
// We check if the UID is equal to 0 because we always want to add root
// as a username (even if you can't log into it)
if (entry.uid >= uid_range.uid_min and entry.uid <= uid_range.uid_max or entry.uid == 0 and entry.username != null) {
var valid: bool = true;
// Check if not in valid UID range and not o
// because we always want to add root as a username (even if you can't log into it)
if ((entry.uid < uid_range.uid_min or entry.uid > uid_range.uid_max) and entry.uid != 0) {
valid = false;
}
// Check if not whitelist if enabled
if (username_whitelist_string != null and !stringListContains(entry.username.?, whitelisted_usernames)) {
valid = false;
}
// Valid and not null, add
if (valid and entry.username != null) {
const username = try allocator.dupe(u8, entry.username.?);
try usernames.append(allocator, username);
}
@ -1337,10 +1355,40 @@ fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8, ui
maybe_entry = interop.getNextUsernameEntry();
}
defer {
// Cleanup whitelisted_usernames
if (username_whitelist_string != null) {
for (whitelisted_usernames.items) |username| allocator.free(username);
whitelisted_usernames.deinit(allocator);
}
}
interop.closePasswordDatabase();
return usernames;
}
pub fn parseCSVString(allocator: std.mem.Allocator, input: []const u8) !StringList {
var list = StringList{};
var start: usize = 0;
while (start < input.len) {
var end = start;
while (end < input.len and input[end] != ',') : (end += 1) {}
const token = std.mem.trim(u8, input[start..end], " \t\n\r");
if (token.len > 0) { // Only add non-empty tokens
const duped = try allocator.dupe(u8, token);
try list.append(allocator, duped);
}
start = end + 1;
}
return list;
}
fn adjustBrightness(allocator: std.mem.Allocator, cmd: []const u8) !void {
var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, allocator);
brightness.stdout_behavior = .Ignore;