Make code more portable + remove mcookie usage

Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
AnErrupTion 2024-08-01 13:15:54 +02:00
parent 1314c57796
commit 61f3fadfbf
No known key found for this signature in database
GPG Key ID: 3E85EB44F610AD7F
9 changed files with 82 additions and 132 deletions

View File

@ -158,9 +158,6 @@ term_reset_cmd = $PREFIX_DIRECTORY/bin/tput reset
# Terminal restore cursor command # Terminal restore cursor command
term_restore_cursor_cmd = $PREFIX_DIRECTORY/bin/tput cnorm term_restore_cursor_cmd = $PREFIX_DIRECTORY/bin/tput cnorm
# Cookie generator
mcookie_cmd = $PREFIX_DIRECTORY/bin/mcookie
# Wayland setup command # Wayland setup command
wayland_cmd = $CONFIG_DIRECTORY/ly/wsetup.sh wayland_cmd = $CONFIG_DIRECTORY/ly/wsetup.sh

View File

@ -11,7 +11,6 @@ err_dgn_oob = log message
err_domain = invalid domain err_domain = invalid domain
err_envlist = failed to get envlist err_envlist = failed to get envlist
err_hostname = failed to get hostname err_hostname = failed to get hostname
err_mcookie = mcookie command failed
err_mlock = failed to lock password memory err_mlock = failed to lock password memory
err_null = null pointer err_null = null pointer
err_pam = pam transaction failed err_pam = pam transaction failed

View File

@ -11,7 +11,6 @@ err_dgn_oob = message
err_domain = domaine invalide err_domain = domaine invalide
err_envlist = échec de lecture de la liste d'environnement err_envlist = échec de lecture de la liste d'environnement
err_hostname = échec de lecture du nom d'hôte err_hostname = échec de lecture du nom d'hôte
err_mcookie = échec de la commande mcookie
err_mlock = échec du verrouillage mémoire err_mlock = échec du verrouillage mémoire
err_null = pointeur null err_null = pointeur null
err_pam = échec de la transaction pam err_pam = échec de la transaction pam

View File

@ -8,6 +8,7 @@ const Session = @import("tui/components/Session.zig");
const Text = @import("tui/components/Text.zig"); const Text = @import("tui/components/Text.zig");
const Config = @import("config/Config.zig"); const Config = @import("config/Config.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Md5 = std.crypto.hash.Md5;
const utmp = interop.utmp; const utmp = interop.utmp;
const Utmp = utmp.utmpx; const Utmp = utmp.utmpx;
const SharedError = @import("SharedError.zig"); const SharedError = @import("SharedError.zig");
@ -42,7 +43,7 @@ pub fn authenticate(config: Config, current_environment: Session.Environment, lo
}; };
var handle: ?*interop.pam.pam_handle = undefined; var handle: ?*interop.pam.pam_handle = undefined;
var status = interop.pam.pam_start(config.service_name.ptr, null, &conv, &handle); var status = interop.pam.pam_start(config.service_name, null, &conv, &handle);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
defer _ = interop.pam.pam_end(handle, status); defer _ = interop.pam.pam_end(handle, status);
@ -65,19 +66,19 @@ pub fn authenticate(config: Config, current_environment: Session.Environment, lo
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
defer status = interop.pam.pam_close_session(handle, 0); defer status = interop.pam.pam_close_session(handle, 0);
var pwd: *std.c.passwd = undefined; var pwd: *interop.pwd.passwd = undefined;
{ {
defer interop.endpwent(); defer interop.pwd.endpwent();
// Get password structure from username // Get password structure from username
pwd = std.c.getpwnam(login.ptr) orelse return error.GetPasswordNameFailed; pwd = interop.pwd.getpwnam(login) orelse return error.GetPasswordNameFailed;
} }
// Set user shell if it hasn't already been set // Set user shell if it hasn't already been set
if (pwd.pw_shell == null) { if (pwd.pw_shell == null) {
interop.setusershell(); interop.unistd.setusershell();
pwd.pw_shell = interop.getusershell(); pwd.pw_shell = interop.unistd.getusershell();
interop.endusershell(); interop.unistd.endusershell();
} }
var shared_err = try SharedError.init(); var shared_err = try SharedError.init();
@ -123,18 +124,22 @@ pub fn authenticate(config: Config, current_environment: Session.Environment, lo
fn startSession( fn startSession(
config: Config, config: Config,
pwd: *std.c.passwd, pwd: *interop.pwd.passwd,
handle: ?*interop.pam.pam_handle, handle: ?*interop.pam.pam_handle,
current_environment: Session.Environment, current_environment: Session.Environment,
) !void { ) !void {
const status = interop.initgroups(pwd.pw_name.?, pwd.pw_gid);
if (status != 0) return error.GroupInitializationFailed;
if (builtin.os.tag == .freebsd) { if (builtin.os.tag == .freebsd) {
// FreeBSD has initgroups() in unistd
const status = interop.unistd.initgroups(pwd.pw_name, pwd.pw_gid);
if (status != 0) return error.GroupInitializationFailed;
// FreeBSD sets the GID and UID with setusercontext() // FreeBSD sets the GID and UID with setusercontext()
const result = std.c.setusercontext(null, pwd, pwd.pw_uid, interop.logincap.LOGIN_SETALL); const result = std.c.setusercontext(null, pwd, pwd.pw_uid, interop.logincap.LOGIN_SETALL);
if (result != 0) return error.SetUserUidFailed; if (result != 0) return error.SetUserUidFailed;
} else { } else {
const status = interop.grp.initgroups(pwd.pw_name, pwd.pw_gid);
if (status != 0) return error.GroupInitializationFailed;
std.posix.setgid(pwd.pw_gid) catch return error.SetUserGidFailed; std.posix.setgid(pwd.pw_gid) catch return error.SetUserGidFailed;
std.posix.setuid(pwd.pw_uid) catch return error.SetUserUidFailed; std.posix.setuid(pwd.pw_uid) catch return error.SetUserUidFailed;
} }
@ -147,7 +152,7 @@ fn startSession(
if (pam_env_vars == null) return error.GetEnvListFailed; if (pam_env_vars == null) return error.GetEnvListFailed;
const env_list = std.mem.span(pam_env_vars.?); const env_list = std.mem.span(pam_env_vars.?);
for (env_list) |env_var| _ = interop.putenv(env_var.?); for (env_list) |env_var| _ = interop.stdlib.putenv(env_var);
// Execute what the user requested // Execute what the user requested
std.posix.chdirZ(pwd.pw_dir.?) catch return error.ChangeDirectoryFailed; std.posix.chdirZ(pwd.pw_dir.?) catch return error.ChangeDirectoryFailed;
@ -165,21 +170,21 @@ fn startSession(
} }
} }
fn initEnv(pwd: *std.c.passwd, path_env: ?[:0]const u8) !void { fn initEnv(pwd: *interop.pwd.passwd, path_env: ?[:0]const u8) !void {
_ = interop.setenv("HOME", pwd.pw_dir, 1); _ = interop.stdlib.setenv("HOME", pwd.pw_dir, 1);
_ = interop.setenv("PWD", pwd.pw_dir, 1); _ = interop.stdlib.setenv("PWD", pwd.pw_dir, 1);
_ = interop.setenv("SHELL", pwd.pw_shell, 1); _ = interop.stdlib.setenv("SHELL", pwd.pw_shell, 1);
_ = interop.setenv("USER", pwd.pw_name, 1); _ = interop.stdlib.setenv("USER", pwd.pw_name, 1);
_ = interop.setenv("LOGNAME", pwd.pw_name, 1); _ = interop.stdlib.setenv("LOGNAME", pwd.pw_name, 1);
if (path_env) |path| { if (path_env) |path| {
const status = interop.setenv("PATH", path, 1); const status = interop.stdlib.setenv("PATH", path, 1);
if (status != 0) return error.SetPathFailed; if (status != 0) return error.SetPathFailed;
} }
} }
fn setXdgSessionEnv(display_server: enums.DisplayServer) void { fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
_ = interop.setenv("XDG_SESSION_TYPE", switch (display_server) { _ = interop.stdlib.setenv("XDG_SESSION_TYPE", switch (display_server) {
.wayland => "wayland", .wayland => "wayland",
.shell => "tty", .shell => "tty",
.xinitrc, .x11 => "x11", .xinitrc, .x11 => "x11",
@ -192,19 +197,19 @@ fn setXdgEnv(tty_str: [:0]u8, desktop_name: [:0]const u8, xdg_desktop_names: [:0
// XDG_RUNTIME_DIR to fall back to directories inside user's home // XDG_RUNTIME_DIR to fall back to directories inside user's home
// directory. // directory.
if (builtin.os.tag != .freebsd) { if (builtin.os.tag != .freebsd) {
const uid = interop.getuid(); const uid = interop.unistd.getuid();
var uid_buffer: [10 + @sizeOf(u32) + 1]u8 = undefined; var uid_buffer: [10 + @sizeOf(u32) + 1]u8 = undefined;
const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid}); const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
_ = interop.setenv("XDG_RUNTIME_DIR", uid_str.ptr, 0); _ = interop.stdlib.setenv("XDG_RUNTIME_DIR", uid_str, 0);
} }
_ = interop.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names.ptr, 0); _ = interop.stdlib.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names, 0);
_ = interop.setenv("XDG_SESSION_CLASS", "user", 0); _ = interop.stdlib.setenv("XDG_SESSION_CLASS", "user", 0);
_ = interop.setenv("XDG_SESSION_ID", "1", 0); _ = interop.stdlib.setenv("XDG_SESSION_ID", "1", 0);
_ = interop.setenv("XDG_SESSION_DESKTOP", desktop_name.ptr, 0); _ = interop.stdlib.setenv("XDG_SESSION_DESKTOP", desktop_name, 0);
_ = interop.setenv("XDG_SEAT", "seat0", 0); _ = interop.stdlib.setenv("XDG_SEAT", "seat0", 0);
_ = interop.setenv("XDG_VTNR", tty_str.ptr, 0); _ = interop.stdlib.setenv("XDG_VTNR", tty_str, 0);
} }
fn loginConv( fn loginConv(
@ -235,7 +240,7 @@ fn loginConv(
status = interop.pam.PAM_BUF_ERR; status = interop.pam.PAM_BUF_ERR;
break :set_credentials; break :set_credentials;
}; };
response[i].resp = username.?.ptr; response[i].resp = username.?;
}, },
interop.pam.PAM_PROMPT_ECHO_OFF => { interop.pam.PAM_PROMPT_ECHO_OFF => {
const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr)); const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr));
@ -243,7 +248,7 @@ fn loginConv(
status = interop.pam.PAM_BUF_ERR; status = interop.pam.PAM_BUF_ERR;
break :set_credentials; break :set_credentials;
}; };
response[i].resp = password.?.ptr; response[i].resp = password.?;
}, },
interop.pam.PAM_ERROR_MSG => { interop.pam.PAM_ERROR_MSG => {
status = interop.pam.PAM_CONV_ERR; status = interop.pam.PAM_CONV_ERR;
@ -349,49 +354,30 @@ fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 {
return xauthority; return xauthority;
} }
pub fn mcookie(cmd: [:0]const u8) ![32]u8 { fn mcookie() [Md5.digest_length * 2]u8 {
const pipe = try std.posix.pipe(); var buf: [4096]u8 = undefined;
defer std.posix.close(pipe[1]); std.crypto.random.bytes(&buf);
const output = std.fs.File{ .handle = pipe[0] }; var out: [Md5.digest_length]u8 = undefined;
defer output.close(); Md5.hash(&buf, &out, .{});
const pid = try std.posix.fork(); return std.fmt.bytesToHex(&out, .lower);
if (pid == 0) {
std.posix.close(pipe[0]);
std.posix.dup2(pipe[1], std.posix.STDOUT_FILENO) catch std.process.exit(1);
std.posix.close(pipe[1]);
const args = [_:null]?[*:0]u8{};
std.posix.execveZ(cmd.ptr, &args, std.c.environ) catch {};
std.process.exit(1);
}
const result = std.posix.waitpid(pid, 0);
if (result.status != 0) return error.McookieFailed;
var buf: [32]u8 = undefined;
const len = try output.read(&buf);
if (len != 32) return error.McookieFailed;
return buf;
} }
fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, xauth_cmd: []const u8, mcookie_cmd: [:0]const u8) !void { fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, xauth_cmd: []const u8) !void {
var pwd_buf: [100]u8 = undefined; var pwd_buf: [100]u8 = undefined;
const pwd = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir}); const pwd = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir});
const xauthority = try createXauthFile(pwd); const xauthority = try createXauthFile(pwd);
_ = interop.setenv("XAUTHORITY", xauthority, 1); _ = interop.stdlib.setenv("XAUTHORITY", xauthority, 1);
_ = interop.setenv("DISPLAY", display_name, 1); _ = interop.stdlib.setenv("DISPLAY", display_name, 1);
const mcookie_output = try mcookie(mcookie_cmd); const magic_cookie = mcookie();
const pid = try std.posix.fork(); const pid = try std.posix.fork();
if (pid == 0) { if (pid == 0) {
var cmd_buffer: [1024]u8 = undefined; var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ xauth_cmd, display_name, mcookie_output }) catch std.process.exit(1); const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ xauth_cmd, display_name, magic_cookie }) catch std.process.exit(1);
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.posix.execveZ(shell, &args, std.c.environ) catch {}; std.posix.execveZ(shell, &args, std.c.environ) catch {};
std.process.exit(1); std.process.exit(1);
@ -417,7 +403,7 @@ fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, config: Config, de
const display_num = try getFreeDisplay(); const display_num = try getFreeDisplay();
var buf: [5]u8 = undefined; var buf: [5]u8 = undefined;
const display_name = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num}); const display_name = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num});
try xauth(display_name, shell, pw_dir, config.xauth_cmd, config.mcookie_cmd); try xauth(display_name, shell, pw_dir, config.xauth_cmd);
const pid = try std.posix.fork(); const pid = try std.posix.fork();
if (pid == 0) { if (pid == 0) {

View File

@ -28,7 +28,6 @@ lang: []const u8 = "en",
load: bool = true, load: bool = true,
margin_box_h: u8 = 2, margin_box_h: u8 = 2,
margin_box_v: u8 = 1, margin_box_v: u8 = 1,
mcookie_cmd: [:0]const u8 = build_options.prefix_directory ++ "/bin/mcookie",
min_refresh_delta: u16 = 5, min_refresh_delta: u16 = 5,
numlock: bool = false, numlock: bool = false,
path: ?[:0]const u8 = "/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin", path: ?[:0]const u8 = "/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin",

View File

@ -12,7 +12,6 @@ err_dgn_oob: []const u8 = "log message",
err_domain: []const u8 = "invalid domain", err_domain: []const u8 = "invalid domain",
err_envlist: []const u8 = "failed to get envlist", err_envlist: []const u8 = "failed to get envlist",
err_hostname: []const u8 = "failed to get hostname", err_hostname: []const u8 = "failed to get hostname",
err_mcookie: []const u8 = "mcookie command failed",
err_mlock: []const u8 = "failed to lock password memory", err_mlock: []const u8 = "failed to lock password memory",
err_null: []const u8 = "null pointer", err_null: []const u8 = "null pointer",
err_pam: []const u8 = "pam transaction failed", err_pam: []const u8 = "pam transaction failed",

View File

@ -62,7 +62,8 @@ pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniFie
if (std.mem.eql(u8, field.key, "wayland_specifier") or if (std.mem.eql(u8, field.key, "wayland_specifier") or
std.mem.eql(u8, field.key, "max_desktop_len") or std.mem.eql(u8, field.key, "max_desktop_len") or
std.mem.eql(u8, field.key, "max_login_len") or std.mem.eql(u8, field.key, "max_login_len") or
std.mem.eql(u8, field.key, "max_password_len")) std.mem.eql(u8, field.key, "max_password_len") or
std.mem.eql(u8, field.key, "mcookie_cmd"))
{ {
// The options don't exist anymore // The options don't exist anymore
mapped_config_fields = true; mapped_config_fields = true;

View File

@ -21,6 +21,22 @@ pub const unistd = @cImport({
@cInclude("unistd.h"); @cInclude("unistd.h");
}); });
pub const time = @cImport({
@cInclude("time.h");
});
pub const stdlib = @cImport({
@cInclude("stdlib.h");
});
pub const pwd = @cImport({
@cInclude("pwd.h");
});
pub const grp = @cImport({
@cInclude("grp.h");
});
// FreeBSD-specific headers // FreeBSD-specific headers
pub const logincap = @cImport({ pub const logincap = @cImport({
@cInclude("login_cap.h"); @cInclude("login_cap.h");
@ -40,38 +56,18 @@ pub const vt = @cImport({
@cInclude("sys/vt.h"); @cInclude("sys/vt.h");
}); });
pub const c_size = usize; // Used for getting & setting the lock state
pub const c_uid = u32; const LedState = if (builtin.os.tag.isBSD()) c_int else c_char;
pub const c_gid = u32; const get_led_state = if (builtin.os.tag.isBSD()) kbio.KDGETLED else kd.KDGKBLED;
pub const c_time = c_longlong; const set_led_state = if (builtin.os.tag.isBSD()) kbio.KDSETLED else kd.KDSKBLED;
pub const tm = extern struct { const numlock_led = if (builtin.os.tag.isBSD()) kbio.LED_NUM else kd.K_NUMLOCK;
tm_sec: c_int, const capslock_led = if (builtin.os.tag.isBSD()) kbio.LED_CAP else kd.K_CAPSLOCK;
tm_min: c_int,
tm_hour: c_int,
tm_mday: c_int,
tm_mon: c_int,
tm_year: c_int,
tm_wday: c_int,
tm_yday: c_int,
tm_isdst: c_int,
};
pub extern "c" fn localtime(timer: *const c_time) *tm;
pub extern "c" fn strftime(str: [*:0]u8, maxsize: c_size, format: [*:0]const u8, timeptr: *const tm) c_size;
pub extern "c" fn setenv(name: [*:0]const u8, value: ?[*:0]const u8, overwrite: c_int) c_int;
pub extern "c" fn putenv(name: [*:0]u8) c_int;
pub extern "c" fn getuid() c_uid;
pub extern "c" fn endpwent() void;
pub extern "c" fn setusershell() void;
pub extern "c" fn getusershell() [*:0]u8;
pub extern "c" fn endusershell() void;
pub extern "c" fn initgroups(user: [*:0]const u8, group: c_gid) c_int;
pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) ![]u8 { pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) ![]u8 {
const timer = std.time.timestamp(); const timer = std.time.timestamp();
const tm_info = localtime(&timer); const tm_info = time.localtime(&timer);
const len = strftime(buf, buf.len, format, tm_info); const len = time.strftime(buf, buf.len, format, tm_info);
if (len < 0) return error.CannotGetFormattedTime; if (len < 0) return error.CannotGetFormattedTime;
return buf[0..len]; return buf[0..len];
@ -92,47 +88,22 @@ pub fn getLockState(console_dev: []const u8) !struct {
const fd = try std.posix.open(console_dev, .{ .ACCMODE = .RDONLY }, 0); const fd = try std.posix.open(console_dev, .{ .ACCMODE = .RDONLY }, 0);
defer std.posix.close(fd); defer std.posix.close(fd);
var numlock = false; var led: LedState = undefined;
var capslock = false; _ = std.c.ioctl(fd, get_led_state, &led);
if (builtin.os.tag.isBSD()) {
var led: c_int = undefined;
_ = std.c.ioctl(fd, kbio.KDGETLED, &led);
numlock = (led & kbio.LED_NUM) != 0;
capslock = (led & kbio.LED_CAP) != 0;
} else {
var led: c_char = undefined;
_ = std.c.ioctl(fd, kd.KDGKBLED, &led);
numlock = (led & kd.K_NUMLOCK) != 0;
capslock = (led & kd.K_CAPSLOCK) != 0;
}
return .{ return .{
.numlock = numlock, .numlock = (led & numlock_led) != 0,
.capslock = capslock, .capslock = (led & capslock_led) != 0,
}; };
} }
pub fn setNumlock(val: bool) !void { pub fn setNumlock(val: bool) !void {
if (builtin.os.tag.isBSD()) { var led: LedState = undefined;
var led: c_int = undefined; _ = std.c.ioctl(0, get_led_state, &led);
_ = std.c.ioctl(0, kbio.KDGETLED, &led);
const numlock = (led & kbio.LED_NUM) != 0; const numlock = (led & numlock_led) != 0;
if (numlock != val) {
const status = std.c.ioctl(std.posix.STDIN_FILENO, kbio.KDSETLED, led ^ kbio.LED_NUM);
if (status != 0) return error.FailedToSetNumlock;
}
return;
}
var led: c_char = undefined;
_ = std.c.ioctl(0, kd.KDGKBLED, &led);
const numlock = (led & kd.K_NUMLOCK) != 0;
if (numlock != val) { if (numlock != val) {
const status = std.c.ioctl(std.posix.STDIN_FILENO, kd.KDSKBLED, led ^ kd.K_NUMLOCK); const status = std.c.ioctl(std.posix.STDIN_FILENO, set_led_state, led ^ numlock_led);
if (status != 0) return error.FailedToSetNumlock; if (status != 0) return error.FailedToSetNumlock;
} }
} }

View File

@ -215,7 +215,7 @@ pub fn main() !void {
const labels_max_length = @max(lang.login.len, lang.password.len); const labels_max_length = @max(lang.login.len, lang.password.len);
var seed: u64 = undefined; var seed: u64 = undefined;
try std.posix.getrandom(std.mem.asBytes(&seed)); // Get a random seed for the PRNG (used by animations) std.crypto.random.bytes(std.mem.asBytes(&seed)); // Get a random seed for the PRNG (used by animations)
var prng = std.Random.DefaultPrng.init(seed); var prng = std.Random.DefaultPrng.init(seed);
const random = prng.random(); const random = prng.random();
@ -770,7 +770,6 @@ fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 {
error.GetPasswordNameFailed => lang.err_pwnam, error.GetPasswordNameFailed => lang.err_pwnam,
error.GetEnvListFailed => lang.err_envlist, error.GetEnvListFailed => lang.err_envlist,
error.XauthFailed => lang.err_xauth, error.XauthFailed => lang.err_xauth,
error.McookieFailed => lang.err_mcookie,
error.XcbConnectionFailed => lang.err_xcb_conn, error.XcbConnectionFailed => lang.err_xcb_conn,
error.GroupInitializationFailed => lang.err_user_init, error.GroupInitializationFailed => lang.err_user_init,
error.SetUserGidFailed => lang.err_user_gid, error.SetUserGidFailed => lang.err_user_gid,