1
Fork 0
turbonss/src/libnss.zig

732 lines
21 KiB
Zig
Raw Normal View History

2022-04-20 07:19:34 +03:00
const std = @import("std");
const os = std.os;
const fmt = std.fmt;
const mem = std.mem;
2022-07-09 13:00:45 +03:00
const math = std.math;
const heap = std.heap;
2022-07-06 13:19:15 +03:00
const once = std.once;
const Mutex = std.Thread.Mutex;
2022-07-09 13:00:45 +03:00
const Allocator = mem.Allocator;
2022-04-20 07:19:34 +03:00
const DB = @import("DB.zig");
const File = @import("File.zig");
2022-07-04 06:09:03 +03:00
const ErrCtx = @import("ErrCtx.zig");
2022-07-14 19:58:05 +03:00
const compress = @import("compress.zig");
2022-04-20 07:19:34 +03:00
const CGroup = @import("Group.zig").CGroup;
const PackedGroup = @import("PackedGroup.zig");
const CUser = @import("User.zig").CUser;
const PackedUser = @import("PackedUser.zig");
const ShellReader = @import("shell.zig").ShellReader;
2022-04-20 07:19:34 +03:00
const c = @cImport({
@cInclude("nss.h");
});
2022-07-14 21:30:36 +03:00
// cannot use os.getenv due to ziglang/zig#4524
extern fn getenv([*:0]const u8) ?[*:0]const u8;
2022-07-06 13:19:15 +03:00
const ENV_DB = "TURBONSS_DB";
2022-07-14 21:30:36 +03:00
const ENV_VERBOSE = "TURBONSS_VERBOSE";
2022-07-06 13:19:15 +03:00
const ENV_OMIT_MEMBERS = "TURBONSS_OMIT_MEMBERS";
2022-04-21 09:30:39 +03:00
2023-02-02 17:04:21 +02:00
export var turbonss_db_path: [*:0]const u8 = "/etc/turbonss/db.turbo";
2022-04-20 07:19:34 +03:00
// State is a type of the global variable holding the process state:
// the DB handle and all the iterators.
const State = struct {
file: File,
omit_members: bool,
v: bool = false,
getpwent_iterator_mu: Mutex = Mutex{},
2022-07-06 13:19:15 +03:00
getpwent_iterator: ?PackedUser.Iterator = null,
getgrent_iterator_mu: Mutex = Mutex{},
2022-07-06 13:19:15 +03:00
getgrent_iterator: ?PackedGroup.Iterator = null,
2022-07-09 13:00:45 +03:00
// allocator for
initgroups_dyn_allocator: Allocator,
2022-04-20 07:19:34 +03:00
};
2022-07-06 13:19:15 +03:00
// global_state is initialized on first call to an nss function
var global_state: ?State = null;
2022-07-06 13:29:36 +03:00
var global_init = once(init);
2022-04-20 07:19:34 +03:00
2022-07-06 13:19:15 +03:00
// assigns State from environment variables et al
fn init() void {
2022-07-14 21:30:36 +03:00
const verbose = blk: {
if (getenv(ENV_VERBOSE)) |env| {
const got = mem.sliceTo(env, 0);
if (mem.eql(u8, got, "0")) {
break :blk false;
} else if (mem.eql(u8, got, "1")) {
break :blk true;
} else {
std.debug.print(
"warning: unrecognized {s}={s}. Expected between 0 or 1\n",
.{ ENV_VERBOSE, got },
);
}
}
break :blk false;
};
const omit_members = blk: {
if (getenv(ENV_OMIT_MEMBERS)) |env| {
const got = mem.sliceTo(env, 0);
if (mem.eql(u8, got, "1")) {
break :blk true;
} else if (mem.eql(u8, got, "0")) {
break :blk false;
} else if (mem.eql(u8, got, "auto")) {
// not set, do autodiscover
} else {
std.debug.print(
"warning: unrecognized {s}={s}. Expected 0, 1 or auto\n",
.{ ENV_OMIT_MEMBERS, got },
);
}
}
2022-08-08 10:49:34 +03:00
// argv does not exist because
// https://github.com/ziglang/zig/issues/4524#issuecomment-1184748756
2023-02-02 17:04:21 +02:00
// so reading /proc/self/cmdline
2022-08-08 10:49:34 +03:00
const fd = os.openZ("/proc/self/cmdline", os.O.RDONLY, 0) catch break :blk false;
defer os.close(fd);
break :blk isId(fd);
2022-07-14 21:30:36 +03:00
};
const fname = if (getenv(ENV_DB)) |env|
mem.sliceTo(env, 0)
else
2023-02-02 17:04:21 +02:00
mem.sliceTo(turbonss_db_path, 0);
2022-07-14 20:05:24 +03:00
2022-07-06 13:19:15 +03:00
const file = File.open(fname) catch |err| {
if (verbose)
std.debug.print("open '{s}': {s}\n", .{ fname, @errorName(err) });
2022-04-20 07:19:34 +03:00
return;
};
2022-07-14 21:30:36 +03:00
if (verbose) {
std.debug.print("turbonss database '{s}' opened\n", .{fname});
std.debug.print("omitting members from getgr* calls: {any}\n", .{omit_members});
}
2022-04-20 07:19:34 +03:00
2022-07-06 13:19:15 +03:00
global_state = State{
.file = file,
.v = verbose,
2022-07-06 13:19:15 +03:00
.omit_members = omit_members,
2022-07-09 13:00:45 +03:00
.initgroups_dyn_allocator = heap.raw_c_allocator,
2022-07-06 13:19:15 +03:00
};
2022-04-20 07:19:34 +03:00
}
export fn _nss_turbo_getpwuid_r(
uid: c_uint,
2022-07-06 16:29:21 +03:00
passwd: *CUser,
buffer: [*]u8,
2022-07-06 13:53:34 +03:00
buflen: usize,
2022-04-20 07:19:34 +03:00
errnop: *c_int,
) c.enum_nss_status {
2022-07-07 21:20:54 +03:00
const db = getDBErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
return getpwuid_r(db, uid, passwd, buffer, buflen, errnop);
2022-07-09 16:50:49 +03:00
}
2022-07-07 21:20:54 +03:00
2022-07-09 16:50:49 +03:00
fn getpwuid_r(
db: *const DB,
uid: c_uint,
passwd: *CUser,
buffer: [*]u8,
buflen: usize,
errnop: *c_int,
) c.enum_nss_status {
2022-07-07 21:20:54 +03:00
var cuser = db.getpwuid(uid, buffer[0..buflen]) catch |err| switch (err) {
2023-06-06 20:33:07 +03:00
error.Overflow => return badFile(errnop),
2022-07-06 16:29:21 +03:00
error.BufferTooSmall => {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.RANGE);
2022-07-06 16:29:21 +03:00
return c.NSS_STATUS_TRYAGAIN;
},
2022-07-07 21:20:54 +03:00
};
2022-07-08 06:29:42 +03:00
const got_cuser = cuser orelse {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.NOENT);
2022-07-07 21:20:54 +03:00
return c.NSS_STATUS_NOTFOUND;
2022-07-08 06:29:42 +03:00
};
passwd.* = got_cuser;
return c.NSS_STATUS_SUCCESS;
2022-07-06 16:29:21 +03:00
}
2022-07-06 13:53:34 +03:00
2022-07-06 16:29:21 +03:00
export fn _nss_turbo_getpwnam_r(
2022-07-06 16:54:43 +03:00
name: [*:0]const u8,
2022-07-06 16:29:21 +03:00
passwd: *CUser,
buffer: [*]u8,
buflen: usize,
errnop: *c_int,
) c.enum_nss_status {
2022-07-07 21:20:54 +03:00
const db = getDBErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
return getpwnam_r(db, name, passwd, buffer, buflen, errnop);
2022-07-09 16:50:49 +03:00
}
fn getpwnam_r(
db: *const DB,
name: [*:0]const u8,
passwd: *CUser,
buffer: [*]u8,
buflen: usize,
errnop: *c_int,
) c.enum_nss_status {
2022-07-06 16:29:21 +03:00
const nameSlice = mem.sliceTo(name, 0);
2022-07-07 21:20:54 +03:00
var buf = buffer[0..buflen];
const cuser = db.getpwnam(nameSlice, buf) catch |err| switch (err) {
2023-06-06 20:33:07 +03:00
error.Overflow => return badFile(errnop),
2022-07-06 13:53:34 +03:00
error.BufferTooSmall => {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.RANGE);
2022-04-21 09:30:39 +03:00
return c.NSS_STATUS_TRYAGAIN;
},
2022-07-07 21:20:54 +03:00
};
2022-07-08 06:29:42 +03:00
const got_cuser = cuser orelse {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.NOENT);
2022-07-07 21:20:54 +03:00
return c.NSS_STATUS_NOTFOUND;
2022-07-08 06:29:42 +03:00
};
passwd.* = got_cuser;
return c.NSS_STATUS_SUCCESS;
2022-07-06 13:53:34 +03:00
}
2022-07-07 06:54:27 +03:00
export fn _nss_turbo_getgrgid_r(
gid: c_uint,
gr: *CGroup,
buffer: [*]u8,
buflen: usize,
errnop: *c_int,
) c.enum_nss_status {
2022-07-14 18:39:30 +03:00
global_init.call();
2023-06-06 19:23:27 +03:00
var state = &(global_state orelse {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.AGAIN);
2022-07-14 18:39:30 +03:00
return c.NSS_STATUS_UNAVAIL;
2023-06-06 19:23:27 +03:00
});
return getgrgid_r(state, gid, gr, buffer, buflen, errnop);
2022-07-09 16:50:49 +03:00
}
fn getgrgid_r(
state: *const State,
gid: c_uint,
gr: *CGroup,
buffer: [*]u8,
buflen: usize,
errnop: *c_int,
) c.enum_nss_status {
2022-07-08 08:40:24 +03:00
const db = state.file.db;
const omit_members = state.omit_members;
var buf = buffer[0..buflen];
2023-06-06 19:43:45 +03:00
const cgroup = db.getgrgid(gid, buf, omit_members) catch |err| switch (err) {
error.Overflow => return badFile(errnop),
2022-07-07 06:54:27 +03:00
error.BufferTooSmall => {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.RANGE);
2022-07-07 06:54:27 +03:00
return c.NSS_STATUS_TRYAGAIN;
},
2022-07-07 21:20:54 +03:00
};
2023-06-06 19:23:27 +03:00
gr.* = cgroup orelse {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.NOENT);
2022-07-07 21:20:54 +03:00
return c.NSS_STATUS_NOTFOUND;
2022-07-08 06:29:42 +03:00
};
return c.NSS_STATUS_SUCCESS;
2022-07-07 06:54:27 +03:00
}
export fn _nss_turbo_getgrnam_r(
name: [*:0]const u8,
group: *CGroup,
buffer: [*]u8,
buflen: usize,
errnop: *c_int,
) c.enum_nss_status {
2022-07-14 18:39:30 +03:00
global_init.call();
2023-06-06 19:23:27 +03:00
var state = &(global_state orelse {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.AGAIN);
2022-07-14 18:39:30 +03:00
return c.NSS_STATUS_UNAVAIL;
2023-06-06 19:23:27 +03:00
});
return getgrnam_r(state, name, group, buffer, buflen, errnop);
2022-07-09 16:50:49 +03:00
}
fn getgrnam_r(
state: *const State,
name: [*:0]const u8,
group: *CGroup,
buffer: [*]u8,
buflen: usize,
errnop: *c_int,
) c.enum_nss_status {
2022-07-08 08:40:24 +03:00
const db = state.file.db;
const omit_members = state.omit_members;
2022-07-07 06:54:27 +03:00
const nameSlice = mem.sliceTo(name, 0);
2022-07-07 21:20:54 +03:00
var buf = buffer[0..buflen];
2023-06-06 19:43:45 +03:00
const cgroup = db.getgrnam(nameSlice, buf, omit_members) catch |err| switch (err) {
error.Overflow => return badFile(errnop),
2022-07-07 06:54:27 +03:00
error.BufferTooSmall => {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.RANGE);
2022-07-07 06:54:27 +03:00
return c.NSS_STATUS_TRYAGAIN;
},
2022-07-07 21:20:54 +03:00
};
2023-06-06 19:43:45 +03:00
group.* = cgroup orelse {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.NOENT);
2022-07-07 21:20:54 +03:00
return c.NSS_STATUS_NOTFOUND;
2022-07-08 06:29:42 +03:00
};
return c.NSS_STATUS_SUCCESS;
2022-07-07 06:54:27 +03:00
}
export fn _nss_turbo_setpwent(_: c_int) c.enum_nss_status {
global_init.call();
2023-06-06 19:23:27 +03:00
var state = &(global_state orelse return c.NSS_STATUS_UNAVAIL);
return setpwent(state);
2022-07-09 16:50:49 +03:00
}
fn setpwent(state: *State) c.enum_nss_status {
2022-07-07 21:20:54 +03:00
state.getpwent_iterator_mu.lock();
defer state.getpwent_iterator_mu.unlock();
2022-07-11 14:14:21 +03:00
state.getpwent_iterator = PackedUser.iterator(
2023-06-06 19:43:45 +03:00
state.file.db.users,
state.file.db.header.num_users,
state.file.db.shellReader(),
2022-07-11 14:14:21 +03:00
);
return c.NSS_STATUS_SUCCESS;
}
export fn _nss_turbo_endpwent() c.enum_nss_status {
global_init.call();
2023-06-06 19:23:27 +03:00
var state = &(global_state orelse return c.NSS_STATUS_UNAVAIL);
return endpwent(state);
2022-07-09 16:50:49 +03:00
}
2022-07-08 06:29:42 +03:00
fn endpwent(state: *State) c.enum_nss_status {
2022-07-08 06:29:42 +03:00
state.getpwent_iterator_mu.lock();
state.getpwent_iterator = null;
state.getpwent_iterator_mu.unlock();
return c.NSS_STATUS_SUCCESS;
}
export fn _nss_turbo_setgrent(_: c_int) c.enum_nss_status {
2022-07-14 18:39:30 +03:00
global_init.call();
2023-06-06 19:23:27 +03:00
var state = &(global_state orelse return c.NSS_STATUS_UNAVAIL);
return setgrent(state);
2022-07-09 16:50:49 +03:00
}
fn setgrent(state: *State) c.enum_nss_status {
2022-07-07 21:20:54 +03:00
state.getgrent_iterator_mu.lock();
defer state.getgrent_iterator_mu.unlock();
state.getgrent_iterator = PackedGroup.iterator(
state.file.db.groups,
2022-07-14 11:09:01 +03:00
state.file.db.header.num_groups,
2022-07-07 21:20:54 +03:00
);
return c.NSS_STATUS_SUCCESS;
2022-07-07 21:20:54 +03:00
}
export fn _nss_turbo_endgrent() c.enum_nss_status {
2022-07-14 18:39:30 +03:00
global_init.call();
2023-06-06 19:23:27 +03:00
var state = &(global_state orelse return c.NSS_STATUS_UNAVAIL);
return endgrent(state);
2022-07-09 16:50:49 +03:00
}
2022-07-08 06:29:42 +03:00
fn endgrent(state: *State) c.enum_nss_status {
2022-07-07 21:20:54 +03:00
state.getgrent_iterator_mu.lock();
state.getgrent_iterator = null;
2022-07-08 06:29:42 +03:00
state.getgrent_iterator_mu.unlock();
return c.NSS_STATUS_SUCCESS;
2022-07-07 21:20:54 +03:00
}
export fn _nss_turbo_getgrent_r(
result: *CGroup,
buffer: [*]u8,
buflen: usize,
errnop: *c_int,
) c.enum_nss_status {
2022-07-14 18:39:30 +03:00
global_init.call();
2023-06-06 19:23:27 +03:00
var state = &(global_state orelse {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.AGAIN);
2022-07-14 18:39:30 +03:00
return c.NSS_STATUS_UNAVAIL;
2023-06-06 19:23:27 +03:00
});
return getgrent_r(state, result, buffer, buflen, errnop);
2022-07-09 16:50:49 +03:00
}
2022-07-07 21:20:54 +03:00
2022-07-09 16:50:49 +03:00
fn getgrent_r(
state: *State,
result: *CGroup,
buffer: [*]u8,
buflen: usize,
errnop: *c_int,
) c.enum_nss_status {
2022-07-07 21:20:54 +03:00
state.getgrent_iterator_mu.lock();
defer state.getgrent_iterator_mu.unlock();
2023-06-06 19:23:27 +03:00
var it = &(state.getgrent_iterator orelse {
2022-07-07 21:20:54 +03:00
// logic from _nss_systemd_getgrent_r
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.HOSTDOWN);
2022-07-07 21:20:54 +03:00
return c.NSS_STATUS_UNAVAIL;
2023-06-06 19:23:27 +03:00
});
2023-06-06 20:37:53 +03:00
const group = it.next() catch |err| switch (err) {
error.Overflow => return badFile(errnop),
} orelse {
2023-06-06 19:23:27 +03:00
errnop.* = 0;
return c.NSS_STATUS_NOTFOUND;
};
const cgroup1 = if (state.omit_members)
DB.packCGroupNoMembers(&group, buffer[0..buflen])
else
state.file.db.packCGroup(&group, buffer[0..buflen]);
if (cgroup1) |cgroup| {
result.* = cgroup;
return c.NSS_STATUS_SUCCESS;
} else |err| switch (err) {
error.Overflow => {
it.rollback();
return badFile(errnop);
},
2023-06-06 19:23:27 +03:00
error.BufferTooSmall => {
it.rollback();
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.RANGE);
2023-06-06 19:23:27 +03:00
return c.NSS_STATUS_TRYAGAIN;
},
2022-07-08 08:40:24 +03:00
}
2022-07-08 06:29:42 +03:00
}
export fn _nss_turbo_getpwent_r(
result: *CUser,
buffer: [*]u8,
buflen: usize,
errnop: *c_int,
) c.enum_nss_status {
2022-07-14 18:39:30 +03:00
global_init.call();
2023-06-06 19:23:27 +03:00
var state = &(global_state orelse {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.AGAIN);
2022-07-14 18:39:30 +03:00
return c.NSS_STATUS_UNAVAIL;
2023-06-06 19:23:27 +03:00
});
return getpwent_r(state, result, buffer, buflen, errnop);
2022-07-09 16:50:49 +03:00
}
2022-07-08 06:29:42 +03:00
2022-07-09 16:50:49 +03:00
fn getpwent_r(
state: *State,
result: *CUser,
buffer: [*]u8,
buflen: usize,
errnop: *c_int,
) c.enum_nss_status {
2022-07-08 06:29:42 +03:00
state.getpwent_iterator_mu.lock();
defer state.getpwent_iterator_mu.unlock();
2023-06-06 19:23:27 +03:00
var it = &(state.getpwent_iterator orelse {
2022-07-08 06:29:42 +03:00
// logic from _nss_systemd_getgrent_r
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.HOSTDOWN);
2022-07-08 06:29:42 +03:00
return c.NSS_STATUS_UNAVAIL;
2023-06-06 19:23:27 +03:00
});
2023-06-06 20:33:07 +03:00
const user = it.next() catch |err| switch (err) {
error.Overflow => return badFile(errnop),
} orelse {
2023-06-06 19:23:27 +03:00
errnop.* = 0;
return c.NSS_STATUS_NOTFOUND;
};
const buf = buffer[0..buflen];
const cuser = state.file.db.writeUser(user, buf) catch |err| switch (err) {
error.BufferTooSmall => {
it.rollback();
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.RANGE);
2023-06-06 19:23:27 +03:00
return c.NSS_STATUS_TRYAGAIN;
},
};
result.* = cuser;
return c.NSS_STATUS_SUCCESS;
2022-07-08 06:29:42 +03:00
}
export fn _nss_turbo_initgroups_dyn(
user_name: [*:0]const u8,
2022-07-09 16:50:49 +03:00
gid: u32,
2022-07-08 06:29:42 +03:00
start: *c_long,
size: *c_long,
2022-07-09 13:00:45 +03:00
groupsp: *[*]u32,
2022-07-08 06:29:42 +03:00
limit: c_long,
errnop: *c_int,
) c.enum_nss_status {
2022-07-14 18:39:30 +03:00
global_init.call();
2023-06-06 19:23:27 +03:00
var state = &(global_state orelse {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.AGAIN);
2022-07-14 18:39:30 +03:00
return c.NSS_STATUS_UNAVAIL;
2023-06-06 19:23:27 +03:00
});
return initgroups_dyn(state, user_name, gid, start, size, groupsp, limit, errnop);
2022-07-09 16:50:49 +03:00
}
2022-07-08 06:29:42 +03:00
2022-07-09 16:50:49 +03:00
fn initgroups_dyn(
state: *const State,
user_name: [*:0]const u8,
_: u32,
start: *c_long,
size: *c_long,
groupsp: *[*]u32,
limit: c_long,
errnop: *c_int,
) c.enum_nss_status {
const db = state.file.db;
2023-06-06 20:33:07 +03:00
const user = db.getUserByName(mem.sliceTo(user_name, 0)) catch |err| switch (err) {
error.Overflow => return badFile(errnop),
} orelse {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.NOENT);
2022-07-08 06:29:42 +03:00
return c.NSS_STATUS_NOTFOUND;
};
var gids = db.userGids(user.additional_gids_offset) catch |err| switch (err) {
error.Overflow => return badFile(errnop),
};
2023-06-06 19:12:46 +03:00
const remaining = gids.vit.remaining;
2022-07-09 13:00:45 +03:00
2022-07-15 10:32:45 +03:00
// the implementation below is ported from glibc's db-initgroups.c
// even though we know the size of the groups upfront, I found it too difficult
// to preallocate and juggle size, start and limit while keeping glibc happy.
var any: bool = false;
while (gids.next() catch |err| switch (err) {
error.Overflow => return badFile(errnop),
}) |gid| {
2022-07-15 10:32:45 +03:00
if (start.* == size.*) {
if (limit > 0 and size.* == limit)
return c.NSS_STATUS_SUCCESS;
2023-08-21 13:50:52 +03:00
const oldsize: u32 = @intCast(size.*);
2022-07-20 05:37:02 +03:00
const newsize_want = blk: {
// double the oldsize until it is above or equal 'remaining'
var res = oldsize;
while (res < remaining) res *= 2;
break :blk res;
};
2022-07-15 10:32:45 +03:00
const newsize: usize = if (limit <= 0)
2022-07-20 05:37:02 +03:00
newsize_want
2022-07-15 10:32:45 +03:00
else
2023-08-21 13:50:52 +03:00
@min(@as(usize, @intCast(limit)), newsize_want);
2022-07-15 10:32:45 +03:00
var buf = groupsp.*[0..oldsize];
const new_groups = state.initgroups_dyn_allocator.realloc(
buf,
newsize * @sizeOf(u32),
);
2022-07-15 10:32:45 +03:00
if (new_groups) |newgroups| {
groupsp.* = newgroups.ptr;
2023-08-21 13:50:52 +03:00
size.* = @intCast(newsize);
2022-07-15 10:32:45 +03:00
} else |err| switch (err) {
error.OutOfMemory => {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.NOMEM);
2022-07-15 10:32:45 +03:00
return c.NSS_STATUS_TRYAGAIN;
},
}
}
2022-07-09 13:00:45 +03:00
2022-07-15 10:32:45 +03:00
any = true;
2023-08-21 13:50:52 +03:00
groupsp.*[@intCast(start.*)] = @intCast(gid);
2022-07-09 13:00:45 +03:00
start.* += 1;
}
2022-07-15 10:32:45 +03:00
return if (any) c.NSS_STATUS_SUCCESS else c.NSS_STATUS_NOTFOUND;
}
2022-07-14 18:39:30 +03:00
fn getDBErrno(errnop: *c_int) ?*const DB {
2022-07-06 13:53:34 +03:00
global_init.call();
2023-06-06 19:23:27 +03:00
var state = &(global_state orelse {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.AGAIN);
2022-07-06 13:53:34 +03:00
return null;
2023-06-06 19:23:27 +03:00
});
return &state.file.db;
2022-04-21 09:30:39 +03:00
}
fn badFile(errnop: *c_int) c.enum_nss_status {
2023-06-20 13:01:32 +03:00
errnop.* = @intFromEnum(os.E.NOENT);
return c.NSS_STATUS_NOTFOUND;
}
2022-08-08 10:49:34 +03:00
// isId tells if this command is "id". Reads the cmdline
// from the given fd. Returns false on any error.
fn isId(fd: os.fd_t) bool {
var cmdline: [256]u8 = undefined;
const nb = os.read(fd, cmdline[0..]) catch return false;
const arg0 = mem.sliceTo(cmdline[0..nb], 0);
if (arg0.len < 2)
return false;
if (!mem.eql(u8, arg0[arg0.len - 2 .. arg0.len], "id"))
return false;
if (arg0.len == 2)
return true;
return arg0[arg0.len - 3] == '/';
}
2022-04-21 09:30:39 +03:00
const testing = std.testing;
2022-07-12 12:59:47 +03:00
test "libnss getpwuid_r and getpwnam_r" {
2022-07-06 16:54:43 +03:00
var tf = try File.TestDB.init(testing.allocator);
2022-06-07 06:30:18 +03:00
defer tf.deinit();
2022-07-09 16:50:49 +03:00
const state = State{
.file = tf.file,
.omit_members = false,
.initgroups_dyn_allocator = testing.failing_allocator,
};
2022-07-06 16:29:21 +03:00
2022-07-09 16:50:49 +03:00
var buf: [1024]u8 = undefined;
2022-07-06 16:29:21 +03:00
var errno: c_int = 0;
2022-07-06 16:54:43 +03:00
var passwd: CUser = undefined;
try testing.expectEqual(
c.NSS_STATUS_SUCCESS,
2022-07-09 16:50:49 +03:00
getpwuid_r(&state.file.db, 128, &passwd, &buf, buf.len, &errno),
2022-07-06 16:54:43 +03:00
);
2022-07-06 16:29:21 +03:00
try testing.expectEqual(@as(c_int, 0), errno);
2022-07-06 16:54:43 +03:00
try testVidmantas(passwd);
passwd = undefined;
try testing.expectEqual(
c.NSS_STATUS_SUCCESS,
2022-07-09 16:50:49 +03:00
getpwnam_r(&state.file.db, "vidmantas", &passwd, &buf, buf.len, &errno),
2022-07-06 16:54:43 +03:00
);
try testing.expectEqual(@as(c_int, 0), errno);
try testVidmantas(passwd);
passwd = undefined;
try testing.expectEqual(
c.NSS_STATUS_NOTFOUND,
2022-07-09 16:50:49 +03:00
getpwnam_r(&state.file.db, "does not exist", &passwd, &buf, buf.len, &errno),
2022-07-06 16:54:43 +03:00
);
2023-08-21 13:50:52 +03:00
try testing.expectEqual(@intFromEnum(os.E.NOENT), @as(u16, @intCast(errno)));
2022-07-06 16:54:43 +03:00
passwd = undefined;
var small_buffer: [1]u8 = undefined;
try testing.expectEqual(
c.NSS_STATUS_TRYAGAIN,
2022-07-09 16:50:49 +03:00
getpwuid_r(&state.file.db, 0, &passwd, &small_buffer, small_buffer.len, &errno),
2022-07-06 16:54:43 +03:00
);
2023-08-21 13:50:52 +03:00
try testing.expectEqual(@intFromEnum(os.E.RANGE), @as(u16, @intCast(errno)));
2022-07-06 16:54:43 +03:00
}
fn testVidmantas(u: CUser) !void {
try testing.expectEqualStrings("vidmantas", mem.sliceTo(u.pw_name, 0));
try testing.expectEqual(@as(u32, 128), u.pw_uid);
try testing.expectEqual(@as(u32, 128), u.pw_gid);
try testing.expectEqualStrings("Vidmantas Kaminskas", mem.sliceTo(u.pw_gecos, 0));
try testing.expectEqualStrings("/bin/bash", mem.sliceTo(u.pw_shell, 0));
2022-04-20 07:19:34 +03:00
}
2022-07-07 06:54:27 +03:00
2022-07-12 12:59:47 +03:00
test "libnss getgrgid_r and getgrnam_r" {
2022-07-07 06:54:27 +03:00
var tf = try File.TestDB.init(testing.allocator);
defer tf.deinit();
const state = State{
.file = tf.file,
.omit_members = false,
.initgroups_dyn_allocator = testing.failing_allocator,
};
2022-07-07 06:54:27 +03:00
2022-07-09 16:50:49 +03:00
var buf: [1024]u8 = undefined;
2022-07-07 06:54:27 +03:00
var errno: c_int = 0;
var group: CGroup = undefined;
try testing.expectEqual(
c.NSS_STATUS_SUCCESS,
getgrgid_r(&state, 100000, &group, &buf, buf.len, &errno),
2022-07-07 06:54:27 +03:00
);
try testing.expectEqual(@as(c_int, 0), errno);
try testSvcGroup(group);
2022-07-07 07:00:13 +03:00
group = undefined;
try testing.expectEqual(
c.NSS_STATUS_SUCCESS,
getgrnam_r(&state, "service-group", &group, &buf, buf.len, &errno),
2022-07-07 07:00:13 +03:00
);
try testing.expectEqual(@as(c_int, 0), errno);
try testSvcGroup(group);
2022-07-07 07:00:13 +03:00
group = undefined;
try testing.expectEqual(
c.NSS_STATUS_NOTFOUND,
getgrnam_r(&state, "does not exist", &group, &buf, buf.len, &errno),
2022-07-07 07:00:13 +03:00
);
2023-08-21 13:50:52 +03:00
try testing.expectEqual(@intFromEnum(os.E.NOENT), @as(u16, @intCast(errno)));
2022-07-07 06:54:27 +03:00
}
fn testSvcGroup(g: CGroup) !void {
2022-07-14 19:14:47 +03:00
try testing.expectEqual(@as(u32, 100000), g.gr_gid);
try testing.expectEqualStrings("service-group", mem.sliceTo(g.gr_name, 0));
const members = g.gr_mem;
try testing.expect(members[0] != null);
try testing.expect(members[1] != null);
try testing.expectEqualStrings("root", mem.sliceTo(members[0].?, 0));
try testing.expectEqualStrings("vidmantas", mem.sliceTo(members[1].?, 0));
try testing.expect(members[2] == null);
}
2022-07-12 12:59:47 +03:00
test "libnss initgroups_dyn" {
2022-07-09 13:00:45 +03:00
const allocator = testing.allocator;
2022-07-09 16:50:49 +03:00
var tf = try File.TestDB.init(allocator);
defer tf.deinit();
var state = State{
.file = tf.file,
.omit_members = false,
.initgroups_dyn_allocator = allocator,
};
2022-07-09 13:00:45 +03:00
var size: c_long = 3; // size of the gids array is this
2023-08-21 13:50:52 +03:00
var groups = try allocator.alloc(u32, @intCast(size)); // buffer too small
2022-07-09 13:00:45 +03:00
defer allocator.free(groups);
var errno: c_int = 42; // canary
groups[0] = 42; // canary
var start: c_long = 1; // canary is there
2022-07-09 16:50:49 +03:00
const status = initgroups_dyn(
&state,
2022-07-09 13:00:45 +03:00
"vidmantas",
128, // gid
2022-07-09 13:00:45 +03:00
&start,
&size,
&groups.ptr,
3, // limit: besides canary accept 2 groups
&errno,
);
try testing.expectEqual(c.NSS_STATUS_SUCCESS, status);
try testing.expectEqual(@as(c_int, 42), errno);
try testing.expectEqual(@as(u32, 42), groups[0]);
try testing.expectEqual(@as(u32, 9999), groups[1]);
try testing.expectEqual(@as(u32, 100000), groups[2]);
2022-07-07 06:54:27 +03:00
}
2022-08-08 10:49:34 +03:00
test "libnss isId" {
const examples = [_]struct {
cmdline: []const u8,
want: bool,
}{
.{ .want = true, .cmdline = "id\x00foo" },
.{ .want = true, .cmdline = "/usr/bin/id\x00foo" },
.{ .want = true, .cmdline = "id" },
.{ .want = true, .cmdline = "/usr/bin/id" },
.{ .want = false, .cmdline = "" },
.{ .want = false, .cmdline = "d\x00" },
.{ .want = false, .cmdline = "/fakeid" },
};
for (examples) |tt| {
const fd = try os.memfd_create("test_libnss_isId", 0);
defer os.close(fd);
_ = try os.write(fd, tt.cmdline);
try os.lseek_SET(fd, 0);
2023-06-06 19:23:27 +03:00
try testing.expectEqual(tt.want, isId(fd));
2022-08-08 10:49:34 +03:00
}
}