more safety checks in user parsing
This commit is contained in:
parent
277a48296a
commit
e1cae43d08
24
src/DB.zig
24
src/DB.zig
|
@ -342,7 +342,7 @@ pub fn packCGroup(self: *const DB, group: *const PackedGroup, buf: []u8) error{
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (try it.next()) |member_offset| : (i += 1) {
|
while (try it.next()) |member_offset| : (i += 1) {
|
||||||
const entry = PackedUser.fromBytes(@alignCast(8, self.users[member_offset << 3 ..]));
|
const entry = try PackedUser.fromBytes(@alignCast(8, self.users[member_offset << 3 ..]));
|
||||||
const start = buf_offset;
|
const start = buf_offset;
|
||||||
const name = entry.user.name();
|
const name = entry.user.name();
|
||||||
if (buf_offset + name.len + 1 > buf.len)
|
if (buf_offset + name.len + 1 > buf.len)
|
||||||
|
@ -458,34 +458,36 @@ pub fn writeUser(self: *const DB, user: PackedUser, buf: []u8) error{BufferTooSm
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getUserByName(self: *const DB, name: []const u8) ?PackedUser {
|
pub fn getUserByName(self: *const DB, name: []const u8) error{Overflow}!?PackedUser {
|
||||||
const idx = bdz.search(self.bdz_username, name);
|
const idx = bdz.search(self.bdz_username, name);
|
||||||
// bdz may return a hash that's bigger than the number of users
|
// bdz may return a hash that's bigger than the number of users
|
||||||
if (idx >= self.header.num_users) return null;
|
if (idx >= self.header.num_users)
|
||||||
|
return null;
|
||||||
const offset = self.idx_name2user[idx];
|
const offset = self.idx_name2user[idx];
|
||||||
const user = PackedUser.fromBytes(@alignCast(8, self.users[offset << 3 ..])).user;
|
const user = (try PackedUser.fromBytes(@alignCast(8, self.users[offset << 3 ..]))).user;
|
||||||
if (!mem.eql(u8, name, user.name())) return null;
|
if (!mem.eql(u8, name, user.name())) return null;
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get a CUser entry by name.
|
// get a CUser entry by name.
|
||||||
pub fn getpwnam(self: *const DB, name: []const u8, buf: []u8) error{BufferTooSmall}!?CUser {
|
pub fn getpwnam(self: *const DB, name: []const u8, buf: []u8) error{ Overflow, BufferTooSmall }!?CUser {
|
||||||
const user = self.getUserByName(name) orelse return null;
|
const user = try self.getUserByName(name) orelse return null;
|
||||||
return try self.writeUser(user, buf);
|
return try self.writeUser(user, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getUserByUid(self: *const DB, uid: u32) ?PackedUser {
|
pub fn getUserByUid(self: *const DB, uid: u32) error{Overflow}!?PackedUser {
|
||||||
const idx = bdz.search_u32(self.bdz_uid, uid);
|
const idx = bdz.search_u32(self.bdz_uid, uid);
|
||||||
if (idx >= self.header.num_users) return null;
|
if (idx >= self.header.num_users)
|
||||||
|
return null;
|
||||||
const offset = self.idx_uid2user[idx];
|
const offset = self.idx_uid2user[idx];
|
||||||
const user = PackedUser.fromBytes(@alignCast(8, self.users[offset << 3 ..])).user;
|
const user = (try PackedUser.fromBytes(@alignCast(8, self.users[offset << 3 ..]))).user;
|
||||||
if (uid != user.uid()) return null;
|
if (uid != user.uid()) return null;
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get a CUser entry by uid.
|
// get a CUser entry by uid.
|
||||||
pub fn getpwuid(self: *const DB, uid: u32, buf: []u8) error{BufferTooSmall}!?CUser {
|
pub fn getpwuid(self: *const DB, uid: u32, buf: []u8) error{ Overflow, BufferTooSmall }!?CUser {
|
||||||
const user = self.getUserByUid(uid) orelse return null;
|
const user = try self.getUserByUid(uid) orelse return null;
|
||||||
return try self.writeUser(user, buf);
|
return try self.writeUser(user, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,13 +83,11 @@ pub const Entry = struct {
|
||||||
end: usize,
|
end: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn fromBytes(blob: []align(8) const u8) Entry {
|
pub fn fromBytes(blob: []align(8) const u8) error{Overflow}!Entry {
|
||||||
const start_var_payload = @bitSizeOf(Inner) / 8;
|
const start_var_payload = @bitSizeOf(Inner) / 8;
|
||||||
const inner = @ptrCast(*align(8) const Inner, blob[0..start_var_payload]);
|
const inner = @ptrCast(*align(8) const Inner, blob[0..start_var_payload]);
|
||||||
const end_strings = start_var_payload + inner.stringLength();
|
const end_strings = start_var_payload + inner.stringLength();
|
||||||
const gids_offset = compress.uvarint(blob[end_strings..]) catch |err| switch (err) {
|
const gids_offset = try compress.uvarint(blob[end_strings..]);
|
||||||
error.Overflow => unreachable,
|
|
||||||
};
|
|
||||||
const end_payload = end_strings + gids_offset.bytes_read;
|
const end_payload = end_strings + gids_offset.bytes_read;
|
||||||
|
|
||||||
return Entry{
|
return Entry{
|
||||||
|
@ -110,9 +108,9 @@ pub const Iterator = struct {
|
||||||
total: u32,
|
total: u32,
|
||||||
advanced_by: usize = 0,
|
advanced_by: usize = 0,
|
||||||
|
|
||||||
pub fn next(it: *Iterator) ?PackedUser {
|
pub fn next(it: *Iterator) error{Overflow}!?PackedUser {
|
||||||
if (it.idx == it.total) return null;
|
if (it.idx == it.total) return null;
|
||||||
const entry = fromBytes(@alignCast(8, it.section[it.next_start..]));
|
const entry = try fromBytes(@alignCast(8, it.section[it.next_start..]));
|
||||||
it.idx += 1;
|
it.idx += 1;
|
||||||
it.next_start += entry.end;
|
it.next_start += entry.end;
|
||||||
it.advanced_by = entry.end;
|
it.advanced_by = entry.end;
|
||||||
|
@ -296,7 +294,7 @@ test "PackedUser construct section" {
|
||||||
|
|
||||||
var i: u29 = 0;
|
var i: u29 = 0;
|
||||||
var it1 = PackedUser.iterator(buf.items, users.len, test_shell_reader);
|
var it1 = PackedUser.iterator(buf.items, users.len, test_shell_reader);
|
||||||
while (it1.next()) |user| : (i += 1) {
|
while (try it1.next()) |user| : (i += 1) {
|
||||||
try testing.expectEqual(users[i].uid, user.uid());
|
try testing.expectEqual(users[i].uid, user.uid());
|
||||||
try testing.expectEqual(users[i].gid, user.gid());
|
try testing.expectEqual(users[i].gid, user.gid());
|
||||||
try testing.expectEqual(user.additionalGidsOffset(), additional_gids);
|
try testing.expectEqual(user.additionalGidsOffset(), additional_gids);
|
||||||
|
|
|
@ -140,6 +140,7 @@ fn getpwuid_r(
|
||||||
errnop: *c_int,
|
errnop: *c_int,
|
||||||
) c.enum_nss_status {
|
) c.enum_nss_status {
|
||||||
var cuser = db.getpwuid(uid, buffer[0..buflen]) catch |err| switch (err) {
|
var cuser = db.getpwuid(uid, buffer[0..buflen]) catch |err| switch (err) {
|
||||||
|
error.Overflow => return badFile(errnop),
|
||||||
error.BufferTooSmall => {
|
error.BufferTooSmall => {
|
||||||
errnop.* = @enumToInt(os.E.RANGE);
|
errnop.* = @enumToInt(os.E.RANGE);
|
||||||
return c.NSS_STATUS_TRYAGAIN;
|
return c.NSS_STATUS_TRYAGAIN;
|
||||||
|
@ -178,6 +179,7 @@ fn getpwnam_r(
|
||||||
|
|
||||||
var buf = buffer[0..buflen];
|
var buf = buffer[0..buflen];
|
||||||
const cuser = db.getpwnam(nameSlice, buf) catch |err| switch (err) {
|
const cuser = db.getpwnam(nameSlice, buf) catch |err| switch (err) {
|
||||||
|
error.Overflow => return badFile(errnop),
|
||||||
error.BufferTooSmall => {
|
error.BufferTooSmall => {
|
||||||
errnop.* = @enumToInt(os.E.RANGE);
|
errnop.* = @enumToInt(os.E.RANGE);
|
||||||
return c.NSS_STATUS_TRYAGAIN;
|
return c.NSS_STATUS_TRYAGAIN;
|
||||||
|
@ -424,7 +426,9 @@ fn getpwent_r(
|
||||||
return c.NSS_STATUS_UNAVAIL;
|
return c.NSS_STATUS_UNAVAIL;
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = it.next() orelse {
|
const user = it.next() catch |err| switch (err) {
|
||||||
|
error.Overflow => return badFile(errnop),
|
||||||
|
} orelse {
|
||||||
errnop.* = 0;
|
errnop.* = 0;
|
||||||
return c.NSS_STATUS_NOTFOUND;
|
return c.NSS_STATUS_NOTFOUND;
|
||||||
};
|
};
|
||||||
|
@ -470,7 +474,9 @@ fn initgroups_dyn(
|
||||||
errnop: *c_int,
|
errnop: *c_int,
|
||||||
) c.enum_nss_status {
|
) c.enum_nss_status {
|
||||||
const db = state.file.db;
|
const db = state.file.db;
|
||||||
const user = db.getUserByName(mem.sliceTo(user_name, 0)) orelse {
|
const user = db.getUserByName(mem.sliceTo(user_name, 0)) catch |err| switch (err) {
|
||||||
|
error.Overflow => return badFile(errnop),
|
||||||
|
} orelse {
|
||||||
errnop.* = @enumToInt(os.E.NOENT);
|
errnop.* = @enumToInt(os.E.NOENT);
|
||||||
return c.NSS_STATUS_NOTFOUND;
|
return c.NSS_STATUS_NOTFOUND;
|
||||||
};
|
};
|
||||||
|
|
|
@ -159,7 +159,12 @@ fn execute(
|
||||||
db.header.num_users,
|
db.header.num_users,
|
||||||
db.shellReader(),
|
db.shellReader(),
|
||||||
);
|
);
|
||||||
while (it.next()) |packed_user| {
|
while (it.next() catch |err| switch (err) {
|
||||||
|
error.Overflow => {
|
||||||
|
stderr.print("ERROR: file '{s}' is corrupted\n", .{db_file}) catch {};
|
||||||
|
return 1;
|
||||||
|
},
|
||||||
|
}) |packed_user| {
|
||||||
const offset = packed_user.additional_gids_offset;
|
const offset = packed_user.additional_gids_offset;
|
||||||
const additional_gids = db.additional_gids[offset..];
|
const additional_gids = db.additional_gids[offset..];
|
||||||
const vit = compress.varintSliceIterator(additional_gids) catch |err| {
|
const vit = compress.varintSliceIterator(additional_gids) catch |err| {
|
||||||
|
|
|
@ -120,12 +120,12 @@ fn execute(
|
||||||
|
|
||||||
fn passwd(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) error{Overflow}!u8 {
|
fn passwd(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) error{Overflow}!u8 {
|
||||||
if (keys.len == 0)
|
if (keys.len == 0)
|
||||||
return passwdAll(stdout, db);
|
return try passwdAll(stdout, db);
|
||||||
var some_notfound = false;
|
var some_notfound = false;
|
||||||
const shell_reader = db.shellReader();
|
const shell_reader = db.shellReader();
|
||||||
for (keys) |key| {
|
for (keys) |key| {
|
||||||
const keyZ = mem.span(key);
|
const keyZ = mem.span(key);
|
||||||
const maybe_packed_user = if (fmt.parseUnsigned(u32, keyZ, 10)) |uid|
|
const maybe_packed_user = try if (fmt.parseUnsigned(u32, keyZ, 10)) |uid|
|
||||||
db.getUserByUid(uid)
|
db.getUserByUid(uid)
|
||||||
else |_|
|
else |_|
|
||||||
db.getUserByName(keyZ);
|
db.getUserByName(keyZ);
|
||||||
|
@ -142,10 +142,10 @@ fn passwd(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) error{Ove
|
||||||
return if (some_notfound) 2 else 0;
|
return if (some_notfound) 2 else 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn passwdAll(stdout: anytype, db: *const DB) u8 {
|
fn passwdAll(stdout: anytype, db: *const DB) error{Overflow}!u8 {
|
||||||
const shell_reader = db.shellReader();
|
const shell_reader = db.shellReader();
|
||||||
var it = PackedUser.iterator(db.users, db.header.num_users, shell_reader);
|
var it = PackedUser.iterator(db.users, db.header.num_users, shell_reader);
|
||||||
while (it.next()) |packed_user| {
|
while (try it.next()) |packed_user| {
|
||||||
const line = packed_user.toUser(db.shellReader()).toLine();
|
const line = packed_user.toUser(db.shellReader()).toLine();
|
||||||
stdout.writeAll(line.constSlice()) catch return 3;
|
stdout.writeAll(line.constSlice()) catch return 3;
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,7 @@ fn printGroup(stdout: anytype, db: *const DB, g: *const PackedGroup) error{Overf
|
||||||
var line_writer = io.bufferedWriter(stdout);
|
var line_writer = io.bufferedWriter(stdout);
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (try it.next()) |member_offset| : (i += 1) {
|
while (try it.next()) |member_offset| : (i += 1) {
|
||||||
const puser = PackedUser.fromBytes(@alignCast(8, db.users[member_offset << 3 ..]));
|
const puser = try PackedUser.fromBytes(@alignCast(8, db.users[member_offset << 3 ..]));
|
||||||
const name = puser.user.name();
|
const name = puser.user.name();
|
||||||
if (i != 0)
|
if (i != 0)
|
||||||
_ = line_writer.write(",") catch return 3;
|
_ = line_writer.write(",") catch return 3;
|
||||||
|
|
Loading…
Reference in New Issue