2022-04-20 07:19:34 +03:00
|
|
|
const std = @import("std");
|
|
|
|
const os = std.os;
|
|
|
|
const fmt = std.fmt;
|
2022-07-06 13:19:15 +03:00
|
|
|
const log = std.log;
|
2022-04-20 07:19:34 +03:00
|
|
|
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;
|
2022-07-07 16:42:45 +03:00
|
|
|
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-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");
|
2022-07-07 16:42:45 +03:00
|
|
|
const ShellReader = @import("shell.zig").ShellReader;
|
2022-04-20 07:19:34 +03:00
|
|
|
|
|
|
|
const c = @cImport({
|
|
|
|
@cInclude("nss.h");
|
|
|
|
});
|
|
|
|
|
2022-07-06 13:19:15 +03:00
|
|
|
const ENV_DB = "TURBONSS_DB";
|
2022-07-06 14:06:50 +03:00
|
|
|
const ENV_LOGLEVEL = "TURBONSS_LOGLEVEL";
|
2022-07-06 13:19:15 +03:00
|
|
|
const ENV_OMIT_MEMBERS = "TURBONSS_OMIT_MEMBERS";
|
2022-04-21 09:30:39 +03:00
|
|
|
|
2022-07-06 16:46:24 +03:00
|
|
|
export var turbonss_db_path: [:0]const u8 = "/etc/turbonss/db.turbo";
|
2022-04-20 07:19:34 +03:00
|
|
|
|
2022-07-09 19:04:19 +03:00
|
|
|
pub const log_level: std.log.Level = .debug;
|
2022-07-06 13:19:15 +03:00
|
|
|
|
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 {
|
2022-07-07 16:42:45 +03:00
|
|
|
file: File,
|
|
|
|
omit_members: bool,
|
|
|
|
|
|
|
|
getpwent_iterator_mu: Mutex = Mutex{},
|
2022-07-06 13:19:15 +03:00
|
|
|
getpwent_iterator: ?PackedUser.Iterator = null,
|
2022-07-07 16:42:45 +03:00
|
|
|
|
|
|
|
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
|
2022-07-07 16:42:45 +03:00
|
|
|
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-09 17:33:13 +03:00
|
|
|
//if (os.getenvZ(ENV_LOGLEVEL)) |env| {
|
|
|
|
// const got = mem.sliceTo(env, 0);
|
|
|
|
// if (mem.eql(u8, got, "0")) {
|
|
|
|
// log_level = .err;
|
|
|
|
// } else if (mem.eql(u8, got, "1")) {
|
|
|
|
// log_level = .warn;
|
|
|
|
// } else if (mem.eql(u8, got, "2")) {
|
|
|
|
// log_level = .info;
|
|
|
|
// } else if (mem.eql(u8, got, "3")) {
|
|
|
|
// log_level = .debug;
|
|
|
|
// } else {
|
|
|
|
// std.debug.print(
|
|
|
|
// "warning: unrecognized {s}={s}. Expected between 0 and 3\n",
|
|
|
|
// .{ ENV_LOGLEVEL, got },
|
|
|
|
// );
|
|
|
|
// }
|
|
|
|
//}
|
2022-04-21 09:30:39 +03:00
|
|
|
|
2022-07-06 14:06:50 +03:00
|
|
|
const omit_members = blk: {
|
|
|
|
if (os.getenvZ(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 },
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (os.argv.len == 0) break :blk false;
|
|
|
|
break :blk mem.eql(u8, mem.sliceTo(os.argv[0], 0), "id");
|
|
|
|
};
|
2022-07-06 13:19:15 +03:00
|
|
|
log.debug("omitting members from getgr* calls: {any}\n", .{omit_members});
|
2022-04-21 09:30:39 +03:00
|
|
|
|
2022-07-06 16:46:24 +03:00
|
|
|
const fname = os.getenvZ(ENV_DB) orelse turbonss_db_path;
|
2022-07-06 14:06:50 +03:00
|
|
|
log.debug("opening '{s}'", .{fname});
|
|
|
|
|
2022-07-06 13:19:15 +03:00
|
|
|
const file = File.open(fname) catch |err| {
|
|
|
|
log.warn("open '{s}': {s}", .{ fname, @errorName(err) });
|
2022-04-20 07:19:34 +03:00
|
|
|
return;
|
|
|
|
};
|
2022-07-06 13:19:15 +03:00
|
|
|
log.debug("turbonss database opened", .{});
|
2022-04-20 07:19:34 +03:00
|
|
|
|
2022-07-06 13:19:15 +03:00
|
|
|
global_state = State{
|
|
|
|
.file = file,
|
|
|
|
.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;
|
2022-07-09 16:50:49 +03:00
|
|
|
return getpwuid_r(&db, uid, passwd, buffer, buflen, errnop);
|
|
|
|
}
|
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) {
|
2022-07-06 16:29:21 +03:00
|
|
|
error.BufferTooSmall => {
|
|
|
|
errnop.* = @enumToInt(os.E.RANGE);
|
|
|
|
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 {
|
2022-07-07 21:20:54 +03:00
|
|
|
errnop.* = @enumToInt(os.E.NOENT);
|
|
|
|
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;
|
2022-07-09 16:50:49 +03:00
|
|
|
return getpwnam_r(&db, name, passwd, buffer, buflen, errnop);
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2022-07-06 13:53:34 +03:00
|
|
|
error.BufferTooSmall => {
|
2022-04-21 09:30:39 +03:00
|
|
|
errnop.* = @enumToInt(os.E.RANGE);
|
|
|
|
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 {
|
2022-07-07 21:20:54 +03:00
|
|
|
errnop.* = @enumToInt(os.E.NOENT);
|
|
|
|
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-08 08:40:24 +03:00
|
|
|
const state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
|
2022-07-09 16:50:49 +03:00
|
|
|
return getgrgid_r(&state, gid, gr, buffer, buflen, errnop);
|
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
|
|
|
var cgroup = db.getgrgid(gid, buf, omit_members) catch |err| switch (err) {
|
2022-07-07 06:54:27 +03:00
|
|
|
error.BufferTooSmall => {
|
|
|
|
errnop.* = @enumToInt(os.E.RANGE);
|
|
|
|
return c.NSS_STATUS_TRYAGAIN;
|
|
|
|
},
|
2022-07-07 21:20:54 +03:00
|
|
|
};
|
|
|
|
|
2022-07-08 06:29:42 +03:00
|
|
|
const got_cgroup = cgroup orelse {
|
2022-07-07 21:20:54 +03:00
|
|
|
errnop.* = @enumToInt(os.E.NOENT);
|
|
|
|
return c.NSS_STATUS_NOTFOUND;
|
2022-07-08 06:29:42 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
gr.* = got_cgroup;
|
|
|
|
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-08 08:40:24 +03:00
|
|
|
const state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
|
2022-07-09 16:50:49 +03:00
|
|
|
return getgrnam_r(&state, name, group, buffer, buflen, errnop);
|
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
2022-07-08 08:40:24 +03:00
|
|
|
var cgroup = db.getgrnam(nameSlice, buf, omit_members) catch |err| switch (err) {
|
2022-07-07 06:54:27 +03:00
|
|
|
error.BufferTooSmall => {
|
|
|
|
errnop.* = @enumToInt(os.E.RANGE);
|
|
|
|
return c.NSS_STATUS_TRYAGAIN;
|
|
|
|
},
|
2022-07-07 21:20:54 +03:00
|
|
|
};
|
2022-07-08 06:29:42 +03:00
|
|
|
const got_cgroup = cgroup orelse {
|
2022-07-07 21:20:54 +03:00
|
|
|
errnop.* = @enumToInt(os.E.NOENT);
|
|
|
|
return c.NSS_STATUS_NOTFOUND;
|
2022-07-08 06:29:42 +03:00
|
|
|
};
|
|
|
|
group.* = got_cgroup;
|
|
|
|
return c.NSS_STATUS_SUCCESS;
|
2022-07-07 06:54:27 +03:00
|
|
|
}
|
|
|
|
|
2022-07-07 16:42:45 +03:00
|
|
|
export fn _nss_turbo_setpwent(_: c_int) void {
|
|
|
|
global_init.call();
|
2022-07-07 21:20:54 +03:00
|
|
|
var state = global_state orelse return;
|
2022-07-09 16:50:49 +03:00
|
|
|
setpwent(&state);
|
|
|
|
}
|
2022-07-07 16:42:45 +03:00
|
|
|
|
2022-07-09 16:50:49 +03:00
|
|
|
fn setpwent(state: *State) void {
|
2022-07-07 21:20:54 +03:00
|
|
|
state.getpwent_iterator_mu.lock();
|
|
|
|
defer state.getpwent_iterator_mu.unlock();
|
|
|
|
|
|
|
|
state.getpwent_iterator = PackedUser.iterator(
|
|
|
|
state.file.db.users,
|
|
|
|
ShellReader{
|
|
|
|
.index = state.file.db.shell_index,
|
|
|
|
.blob = state.file.db.shell_blob,
|
|
|
|
},
|
|
|
|
);
|
2022-07-07 16:42:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
export fn _nss_turbo_endpwent() void {
|
|
|
|
global_init.call();
|
2022-07-08 06:29:42 +03:00
|
|
|
var state = global_state orelse return;
|
2022-07-09 16:50:49 +03:00
|
|
|
endpwent(&state);
|
|
|
|
}
|
2022-07-08 06:29:42 +03:00
|
|
|
|
2022-07-09 16:50:49 +03:00
|
|
|
fn endpwent(state: *State) void {
|
2022-07-08 06:29:42 +03:00
|
|
|
state.getpwent_iterator_mu.lock();
|
|
|
|
state.getpwent_iterator = null;
|
|
|
|
state.getpwent_iterator_mu.unlock();
|
2022-07-07 16:42:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
export fn _nss_turbo_setgrent(_: c_int) void {
|
2022-07-07 21:20:54 +03:00
|
|
|
var state = getState() orelse return;
|
2022-07-09 16:50:49 +03:00
|
|
|
setgrent(&state);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn setgrent(state: *State) void {
|
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,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export fn _nss_turbo_endgrent() void {
|
|
|
|
var state = getState() orelse return;
|
2022-07-09 16:50:49 +03:00
|
|
|
endgrent(&state);
|
|
|
|
}
|
2022-07-08 06:29:42 +03:00
|
|
|
|
2022-07-09 16:50:49 +03:00
|
|
|
fn endgrent(state: *State) void {
|
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();
|
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 {
|
|
|
|
var state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
|
2022-07-09 16:50:49 +03:00
|
|
|
return getgrent_r(&state, result, buffer, buflen, errnop);
|
|
|
|
}
|
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();
|
|
|
|
|
|
|
|
var it = state.getgrent_iterator orelse {
|
|
|
|
// logic from _nss_systemd_getgrent_r
|
|
|
|
errnop.* = @enumToInt(os.E.HOSTDOWN);
|
|
|
|
return c.NSS_STATUS_UNAVAIL;
|
|
|
|
};
|
|
|
|
|
2022-07-08 06:29:42 +03:00
|
|
|
const group = it.next() orelse {
|
2022-07-07 21:20:54 +03:00
|
|
|
errnop.* = @enumToInt(os.E.NOENT);
|
|
|
|
return c.NSS_STATUS_NOTFOUND;
|
|
|
|
};
|
2022-07-07 16:42:45 +03:00
|
|
|
|
2022-07-08 08:40:24 +03:00
|
|
|
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) {
|
2022-07-07 21:20:54 +03:00
|
|
|
error.BufferTooSmall => {
|
|
|
|
errnop.* = @enumToInt(os.E.RANGE);
|
|
|
|
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 {
|
|
|
|
var state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
|
2022-07-09 16:50:49 +03:00
|
|
|
return getpwent_r(&state, result, buffer, buflen, errnop);
|
|
|
|
}
|
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();
|
|
|
|
|
|
|
|
var it = state.getpwent_iterator orelse {
|
|
|
|
// logic from _nss_systemd_getgrent_r
|
|
|
|
errnop.* = @enumToInt(os.E.HOSTDOWN);
|
|
|
|
return c.NSS_STATUS_UNAVAIL;
|
|
|
|
};
|
|
|
|
|
|
|
|
const user = it.next() orelse {
|
|
|
|
errnop.* = @enumToInt(os.E.NOENT);
|
|
|
|
return c.NSS_STATUS_NOTFOUND;
|
|
|
|
};
|
|
|
|
|
|
|
|
const buf = buffer[0..buflen];
|
|
|
|
const cuser = state.file.db.writeUser(user, buf) catch |err| switch (err) {
|
|
|
|
error.BufferTooSmall => {
|
|
|
|
errnop.* = @enumToInt(os.E.RANGE);
|
|
|
|
return c.NSS_STATUS_TRYAGAIN;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
result.* = cuser;
|
|
|
|
return c.NSS_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
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-09 13:00:45 +03:00
|
|
|
const state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
|
2022-07-09 16:50:49 +03:00
|
|
|
return initgroups_dyn(&state, user_name, gid, start, size, groupsp, limit, errnop);
|
|
|
|
}
|
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;
|
2022-07-08 06:29:42 +03:00
|
|
|
const user = db.getUser(mem.sliceTo(user_name, 0)) orelse {
|
|
|
|
errnop.* = @enumToInt(os.E.NOENT);
|
|
|
|
return c.NSS_STATUS_NOTFOUND;
|
|
|
|
};
|
|
|
|
|
2022-07-09 13:00:45 +03:00
|
|
|
var gids = db.userGids(user.additional_gids_offset);
|
|
|
|
if (size.* < gids.remaining()) {
|
|
|
|
const oldsize = @intCast(usize, size.*);
|
|
|
|
const newsize = if (limit <= 0)
|
|
|
|
oldsize + gids.remaining()
|
|
|
|
else
|
|
|
|
math.min(@intCast(usize, limit), oldsize + gids.remaining());
|
|
|
|
|
|
|
|
var buf = groupsp.*[0..oldsize];
|
|
|
|
const new_groups = state.initgroups_dyn_allocator.realloc(buf, newsize);
|
|
|
|
if (new_groups) |newgroups| {
|
|
|
|
groupsp.* = newgroups.ptr;
|
|
|
|
size.* = @intCast(c_long, newsize);
|
|
|
|
} else |err| switch (err) {
|
|
|
|
error.OutOfMemory => {
|
|
|
|
errnop.* = @enumToInt(os.E.NOMEM);
|
|
|
|
return c.NSS_STATUS_TRYAGAIN;
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2022-07-08 06:29:42 +03:00
|
|
|
|
2022-07-09 13:00:45 +03:00
|
|
|
// I was not able to understand the expected behavior of limit. In glibc
|
|
|
|
// compat-initgroups.c limit is used to malloc the buffer. Something like
|
|
|
|
// this is all over the place:
|
|
|
|
// if (limit > 0 && *size == limit) /* We reached the maximum. */
|
|
|
|
// return;
|
|
|
|
// Our implementation will thus limit the number of *added* entries.
|
|
|
|
|
|
|
|
var added: usize = 0;
|
|
|
|
while (gids.nextMust()) |gid| {
|
|
|
|
if (limit > 0 and added == limit) break;
|
|
|
|
|
|
|
|
added += 1;
|
|
|
|
groupsp.*[@intCast(usize, start.*)] = @intCast(u32, gid);
|
|
|
|
start.* += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return if (added > 0) c.NSS_STATUS_SUCCESS else c.NSS_STATUS_NOTFOUND;
|
2022-07-07 16:42:45 +03:00
|
|
|
}
|
|
|
|
|
2022-07-07 21:20:54 +03:00
|
|
|
fn getState() ?State {
|
2022-07-07 16:42:45 +03:00
|
|
|
global_init.call();
|
2022-07-07 21:20:54 +03:00
|
|
|
return global_state;
|
2022-07-07 16:42:45 +03:00
|
|
|
}
|
|
|
|
|
2022-07-07 21:20:54 +03:00
|
|
|
fn getStateErrno(errnop: *c_int) ?State {
|
2022-07-06 13:53:34 +03:00
|
|
|
global_init.call();
|
2022-07-07 16:42:45 +03:00
|
|
|
if (global_state) |state| {
|
2022-07-07 21:20:54 +03:00
|
|
|
return state;
|
2022-07-06 13:53:34 +03:00
|
|
|
} else {
|
|
|
|
errnop.* = @enumToInt(os.E.AGAIN);
|
|
|
|
return null;
|
|
|
|
}
|
2022-04-21 09:30:39 +03:00
|
|
|
}
|
|
|
|
|
2022-07-07 21:20:54 +03:00
|
|
|
fn getDBErrno(errnop: *c_int) ?DB {
|
|
|
|
if (getStateErrno(errnop)) |state| {
|
|
|
|
return state.file.db;
|
|
|
|
} else return null;
|
|
|
|
}
|
|
|
|
|
2022-04-21 09:30:39 +03:00
|
|
|
const testing = std.testing;
|
|
|
|
|
2022-07-06 16:54:43 +03:00
|
|
|
test "getpwuid_r and getpwnam_r" {
|
|
|
|
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
|
|
|
);
|
|
|
|
try testing.expectEqual(@enumToInt(os.E.NOENT), @intCast(u16, errno));
|
|
|
|
|
|
|
|
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
|
|
|
);
|
|
|
|
try testing.expectEqual(@enumToInt(os.E.RANGE), @intCast(u16, errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
test "getgrgid_r and getgrnam_r" {
|
|
|
|
var tf = try File.TestDB.init(testing.allocator);
|
|
|
|
defer tf.deinit();
|
|
|
|
const turbonss_db_path_old = turbonss_db_path;
|
|
|
|
turbonss_db_path = tf.path;
|
|
|
|
defer {
|
|
|
|
turbonss_db_path = turbonss_db_path_old;
|
|
|
|
}
|
|
|
|
|
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,
|
2022-07-09 16:50:49 +03:00
|
|
|
_nss_turbo_getgrgid_r(128, &group, &buf, buf.len, &errno),
|
2022-07-07 06:54:27 +03:00
|
|
|
);
|
|
|
|
try testing.expectEqual(@as(c_int, 0), errno);
|
|
|
|
try testVidmantasGroup(group);
|
2022-07-07 07:00:13 +03:00
|
|
|
|
|
|
|
group = undefined;
|
|
|
|
try testing.expectEqual(
|
|
|
|
c.NSS_STATUS_SUCCESS,
|
2022-07-09 16:50:49 +03:00
|
|
|
_nss_turbo_getgrnam_r("vidmantas", &group, &buf, buf.len, &errno),
|
2022-07-07 07:00:13 +03:00
|
|
|
);
|
|
|
|
try testing.expectEqual(@as(c_int, 0), errno);
|
|
|
|
try testVidmantasGroup(group);
|
|
|
|
|
|
|
|
group = undefined;
|
|
|
|
try testing.expectEqual(
|
|
|
|
c.NSS_STATUS_NOTFOUND,
|
2022-07-09 16:50:49 +03:00
|
|
|
_nss_turbo_getgrnam_r("does not exist", &group, &buf, buf.len, &errno),
|
2022-07-07 07:00:13 +03:00
|
|
|
);
|
|
|
|
try testing.expectEqual(@enumToInt(os.E.NOENT), @intCast(u16, errno));
|
2022-07-07 06:54:27 +03:00
|
|
|
}
|
|
|
|
|
2022-07-09 13:00:45 +03:00
|
|
|
test "initgroups_dyn" {
|
|
|
|
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
|
|
|
|
var groups = try allocator.alloc(u32, @intCast(usize, size)); // buffer too small
|
|
|
|
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",
|
|
|
|
0, // gid, ignored
|
|
|
|
&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, 128), groups[1]);
|
2022-07-09 16:50:49 +03:00
|
|
|
try testing.expectEqual(@as(u32, 9999), groups[2]);
|
2022-07-09 13:00:45 +03:00
|
|
|
}
|
|
|
|
|
2022-07-07 06:54:27 +03:00
|
|
|
fn testVidmantasGroup(g: CGroup) !void {
|
|
|
|
try testing.expectEqual(@as(u32, 128), g.gid);
|
2022-07-07 07:00:13 +03:00
|
|
|
try testing.expectEqualStrings("vidmantas", mem.sliceTo(g.name, 0));
|
2022-07-07 06:54:27 +03:00
|
|
|
const members = g.members;
|
|
|
|
try testing.expect(members[0] != null);
|
2022-07-07 07:00:13 +03:00
|
|
|
try testing.expectEqualStrings("vidmantas", mem.sliceTo(members[0].?, 0));
|
2022-07-07 06:54:27 +03:00
|
|
|
}
|