2 Commits

Author SHA1 Message Date
a6d2f6dca7 /home/user owned by user, user starts in cwd /home/user 2026-02-23 23:05:13 -06:00
b015d5880a load vuln fixed, sudo fixed 2026-02-23 22:43:12 -06:00
13 changed files with 247 additions and 69 deletions

View File

@@ -64,12 +64,18 @@ local newGid = resolveGid(groupStr)
local function chgrpPath(path) local function chgrpPath(path)
local stat = syscall.stat(path) local stat = syscall.stat(path)
if not stat then if not stat then
print(name .. ": cannot stat '" .. path .. "': No such file or directory") print(name .. ": cannot stat '" .. path .. "': no such file or directory")
return false return false
end end
local ok, err = pcall(syscall.chown, path, stat.owner, newGid) local ok, err = pcall(syscall.chown, path, stat.owner, newGid)
if not ok then if not ok then
print(name .. ": cannot change group of '" .. path .. "': " .. tostring(err)) local msg = tostring(err)
if msg:find("EPERM") or msg:find("EACCES") then
msg = "operation not permitted (must be root)"
elseif msg:find("ENOENT") then
msg = "no such file or directory"
end
print(name .. ": cannot change group of '" .. path .. "': " .. msg)
return false return false
end end
return true return true

View File

@@ -220,7 +220,13 @@ local function chmodPath(path)
local ok, cerr = pcall(syscall.chmod, path, newPerms) local ok, cerr = pcall(syscall.chmod, path, newPerms)
if not ok then if not ok then
print(name .. ": cannot change permissions of '" .. path .. "': " .. tostring(cerr)) local msg = tostring(cerr)
if msg:find("EACCES") or msg:find("EPERM") then
msg = "permission denied"
elseif msg:find("ENOENT") then
msg = "no such file or directory"
end
print(name .. ": cannot change permissions of '" .. path .. "': " .. msg)
return false return false
end end
return true return true

View File

@@ -95,14 +95,20 @@ end
local function chownPath(path) local function chownPath(path)
local stat = syscall.stat(path) local stat = syscall.stat(path)
if not stat then if not stat then
print(name .. ": cannot stat '" .. path .. "': No such file or directory") print(name .. ": cannot stat '" .. path .. "': no such file or directory")
return false return false
end end
local uid = newUid ~= nil and newUid or stat.owner local uid = newUid ~= nil and newUid or stat.owner
local gid = newGid ~= nil and newGid or stat.group local gid = newGid ~= nil and newGid or stat.group
local ok, err = pcall(syscall.chown, path, uid, gid) local ok, err = pcall(syscall.chown, path, uid, gid)
if not ok then if not ok then
print(name .. ": cannot change owner of '" .. path .. "': " .. tostring(err)) local msg = tostring(err)
if msg:find("EPERM") or msg:find("EACCES") then
msg = "operation not permitted (must be root)"
elseif msg:find("ENOENT") then
msg = "no such file or directory"
end
print(name .. ": cannot change owner of '" .. path .. "': " .. msg)
return false return false
end end
return true return true

View File

@@ -17,7 +17,13 @@ local commandHistory = {}
local terminate = false local terminate = false
syscall.setEnviron("SHELL","rtbash") syscall.setEnviron("SHELL","rtbash")
syscall.setEnviron("PATH","/bin/") syscall.setEnviron("PATH","/bin/")
local _home = syscall.getEnviron("HOME")
if _home and _home ~= "" then
local ok = pcall(syscall.chdir, _home)
if not ok then syscall.chdir("/") end
else
syscall.chdir("/") syscall.chdir("/")
end
local oldWD = "" local oldWD = ""
for i = 1, 16 do for i = 1, 16 do

View File

@@ -96,6 +96,9 @@ local function spawnShell(username, uid, shell, homedir)
end end
local chdirOk, chdirErr = pcall(syscall.chdir, homedir) local chdirOk, chdirErr = pcall(syscall.chdir, homedir)
if not chdirOk then
pcall(syscall.chdir, "/")
end
local ok, err = pcall(syscall.execspawn, shell, username .. ":shell") local ok, err = pcall(syscall.execspawn, shell, username .. ":shell")
if not ok then if not ok then

View File

@@ -31,7 +31,7 @@ for j = i + 1, #cmdArgs do restArgs[#restArgs + 1] = cmdArgs[j] end
local currentUid = syscall.getuid() local currentUid = syscall.getuid()
local currentUser = syscall.getUsername(currentUid) or tostring(currentUid) local currentUser = syscall.getUsername(currentUid) or tostring(currentUid)
local targetUid = syscall.getuid(targetUser) local targetUid = syscall.getuidbyname(targetUser)
if not targetUid then if not targetUid then
print("sudo: user '" .. targetUser .. "' does not exist") print("sudo: user '" .. targetUser .. "' does not exist")
syscall.exit(1) syscall.exit(1)
@@ -39,7 +39,7 @@ if not targetUid then
end end
if currentUid ~= 0 then if currentUid ~= 0 then
printInline("[sudo] password for " .. currentUser .. ": ") printInline("[sudo] password for root: ")
local pw = "" local pw = ""
while true do while true do
local ch = syscall.read(0) local ch = syscall.read(0)
@@ -55,7 +55,7 @@ if currentUid ~= 0 then
end end
end end
local ok, err = syscall.elevate(currentUser, pw) local ok, err = syscall.elevate("root", pw)
if not ok then if not ok then
sleep(1) sleep(1)
print("sudo: Authentication failure") print("sudo: Authentication failure")
@@ -63,7 +63,7 @@ if currentUid ~= 0 then
return return
end end
if targetUid ~= 0 then if targetUid ~= currentUid then
syscall.setuid(targetUid) syscall.setuid(targetUid)
end end
else else

View File

@@ -14,6 +14,9 @@ for i,v in pairs(kernel.processes) do
end, i) end, i)
end end
if not fs.exists("/bin/startup") then
fs.mkdir("/bin/startup")
end
local files = fs.list("/bin/startup") local files = fs.list("/bin/startup")
if not files then error("Failed to list /bin/startup") end if not files then error("Failed to list /bin/startup") end
for i,v in ipairs(files) do for i,v in ipairs(files) do

View File

@@ -37,7 +37,7 @@ function peripheral.isPresent(name)
end end
function peripheral.getType(peripheral) function peripheral.getType(peripheral)
if type(peripheral) == "string" then -- Peripheral name passed if type(peripheral) == "string" then
if native.isPresent(peripheral) then if native.isPresent(peripheral) then
return native.getType(peripheral) return native.getType(peripheral)
end end
@@ -58,7 +58,7 @@ function peripheral.getType(peripheral)
end end
function peripheral.hasType(peripheral, peripheral_type) function peripheral.hasType(peripheral, peripheral_type)
if type(peripheral) == "string" then -- Peripheral name passed if type(peripheral) == "string" then
if native.isPresent(peripheral) then if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type) return native.hasType(peripheral, peripheral_type)
end end

View File

@@ -5,14 +5,14 @@ kernel.vfs = vfs
vfs.mounts = {["$"] = "/"} vfs.mounts = {["$"] = "/"}
vfs.disks = kernel.disks vfs.disks = kernel.disks
-- Metafile format (version 1) -- Metafile format (version 2)
-- File header: 1 byte = version (0x01) -- File header: 1 byte = version (0x02)
-- Per-entry: -- Per-entry:
-- 1 byte = name length -- 1 byte = name length
-- N bytes = name -- N bytes = name
-- 1 byte = entry type (0x00 = regular, 0x01 = symlink) -- 1 byte = entry type (0x00 = regular, 0x01 = symlink)
-- 1 byte = owner uid -- 2 bytes = owner uid (little-endian uint16)
-- 1 byte = group gid -- 2 bytes = group gid (little-endian uint16)
-- 2 bytes = perms (little-endian uint16) -- 2 bytes = perms (little-endian uint16)
-- bit 0 = world-write bit 1 = world-read -- bit 0 = world-write bit 1 = world-read
-- bit 2 = group-write bit 3 = group-read -- bit 2 = group-write bit 3 = group-read
@@ -24,12 +24,16 @@ vfs.disks = kernel.disks
-- 1 byte = cmeta length -- 1 byte = cmeta length
-- N bytes = cmeta (for symlinks: the link target path) -- N bytes = cmeta (for symlinks: the link target path)
-- --
-- Version 1:
-- 1 byte name len, N bytes name, 1 byte etype, 1 byte owner,
-- 1 byte group, 2 bytes perms (little-endian), 1 byte cmeta len, N bytes cmeta
--
-- Version 0: -- Version 0:
-- No file header. Per-entry: -- No file header. Per-entry:
-- 1 byte name len, N bytes name, 1 byte owner, 1 byte group, -- 1 byte name len, N bytes name, 1 byte owner, 1 byte group,
-- 1 byte perms (low 7 bits only), 1 byte cmeta len, N bytes cmeta -- 1 byte perms (low 7 bits only), 1 byte cmeta len, N bytes cmeta
local META_VERSION = 0x01 local META_VERSION = 0x02
local function bit_is_set(num, bit) local function bit_is_set(num, bit)
return math.floor(num / (2 ^ bit)) % 2 == 1 return math.floor(num / (2 ^ bit)) % 2 == 1
@@ -41,8 +45,9 @@ local function parseMetafile(raw)
local p = 1 local p = 1
local version = 0 local version = 0
if raw:byte(1) == META_VERSION then local firstByte = raw:byte(1)
version = META_VERSION if firstByte == 0x02 or firstByte == 0x01 then
version = firstByte
p = 2 p = 2
end end
@@ -54,13 +59,22 @@ local function parseMetafile(raw)
local etype, owner, group, perms, cmeta local etype, owner, group, perms, cmeta
if version == META_VERSION then if version == 0x02 then
-- v2: etype(1) + owner(2) + group(2) + perms(2) = 7 bytes
if p + 6 > #raw then break end
etype = raw:byte(p); p = p + 1
owner = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
group = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
perms = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
elseif version == 0x01 then
-- v1: etype(1) + owner(1) + group(1) + perms(2) = 5 bytes
if p + 4 > #raw then break end if p + 4 > #raw then break end
etype = raw:byte(p); p = p + 1 etype = raw:byte(p); p = p + 1
owner = raw:byte(p); p = p + 1 owner = raw:byte(p); p = p + 1
group = raw:byte(p); p = p + 1 group = raw:byte(p); p = p + 1
perms = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2 perms = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
else else
-- v0: owner(1) + group(1) + perms(1) = 3 bytes
if p + 2 > #raw then break end if p + 2 > #raw then break end
etype = 0x00 etype = 0x00
owner = raw:byte(p); p = p + 1 owner = raw:byte(p); p = p + 1
@@ -87,10 +101,14 @@ local function makeMetafile(meta)
for name, m in pairs(meta) do for name, m in pairs(meta) do
local plo = m.perms % 256 local plo = m.perms % 256
local phi = math.floor(m.perms / 256) % 256 local phi = math.floor(m.perms / 256) % 256
local olo = (m.owner or 0) % 256
local ohi = math.floor((m.owner or 0) / 256) % 256
local glo = (m.group or 0) % 256
local ghi = math.floor((m.group or 0) / 256) % 256
out = out out = out
.. string.char(#name) .. name .. string.char(#name) .. name
.. string.char(m.etype or 0x00) .. string.char(m.etype or 0x00)
.. string.char(m.owner, m.group, plo, phi) .. string.char(olo, ohi, glo, ghi, plo, phi)
.. string.char(#m.cmeta) .. m.cmeta .. string.char(#m.cmeta) .. m.cmeta
end end
return out return out
@@ -146,6 +164,9 @@ local function resolveMount(normalPath)
return vfs.disks[mountId], diskPath return vfs.disks[mountId], diskPath
end end
-- Expose parser for use by other modules (e.g. permissions seeder)
vfs._parseMetafile = parseMetafile
local function readMetaEntry(disk, parentDiskPath, filename) local function readMetaEntry(disk, parentDiskPath, filename)
if filename == ".meta" then error("Cannot open metafile") end if filename == ".meta" then error("Cannot open metafile") end
local mp local mp
@@ -159,6 +180,15 @@ local function readMetaEntry(disk, parentDiskPath, filename)
if not ok or not f then return nil end if not ok or not f then return nil end
local raw = f.read(65535) local raw = f.read(65535)
if f.close then f.close() end if f.close then f.close() end
-- Auto-upgrade: if this .meta file is not v2, rewrite it as v2 now
if raw and #raw > 0 and raw:byte(1) ~= META_VERSION then
local upgraded = makeMetafile(parseMetafile(raw))
local wok, wf = pcall(function() return disk:open(mp, "w") end)
if wok and wf then wf.write(upgraded); if wf.close then wf.close() end end
raw = upgraded
end
local parsed = parseMetafile(raw) local parsed = parseMetafile(raw)
return parsed[filename] return parsed[filename]
end end
@@ -400,6 +430,8 @@ function vfs.open(path, mode)
if not disk then error("NODISK") end if not disk then error("NODISK") end
local meta = getFileMeta(path) local meta = getFileMeta(path)
local isNew = (mode == "w" or mode == "a") and not disk:fileExists(diskPath)
checkperms(meta, mode == "r" and "r" or "w") checkperms(meta, mode == "r" and "r" or "w")
local handle local handle
@@ -408,6 +440,22 @@ function vfs.open(path, mode)
if type(handle) ~= "table" then error("ENFILE") end if type(handle) ~= "table" then error("ENFILE") end
end end
-- If this is a newly created file, stamp it with the creating user's ownership
if isNew then
local euid = (task and (task.euid or task.uid)) or kernel.uid
local egid = (task and task.gid) or 0
local norm = normalizePath(path)
local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end
local name = norm:match("[^/]+$")
if name then
local entry = { etype=0x00, owner=euid, group=egid,
perms=vfs.PERM.RW_R_R, cmeta="" }
pcall(writeMetaEntry, parent, name, entry, false)
meta = entry
end
end
local fobj = newFileObj(handle, mode, path, meta, disk:type(diskPath)) local fobj = newFileObj(handle, mode, path, meta, disk:type(diskPath))
if mode == "r" and bit_is_set(meta.perms, 6) then if mode == "r" and bit_is_set(meta.perms, 6) then
fobj.suid_owner = meta.owner fobj.suid_owner = meta.owner
@@ -574,15 +622,33 @@ function vfs.listdir(path)
end end
function vfs.mkdir(path) function vfs.mkdir(path)
local norm = normalizePath(path)
local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end
local parentMeta = getFileMeta(parent)
checkperms(parentMeta, "w")
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
checkperms(meta, "w")
disk:makeDirectory(diskPath) disk:makeDirectory(diskPath)
-- Stamp the new directory with the creating user's ownership
local task = kernel.currentTask
local euid = (task and (task.euid or task.uid)) or kernel.uid
local egid = (task and task.gid) or 0
local name = norm:match("[^/]+$")
if name then
local entry = { etype=0x00, owner=euid, group=egid,
perms=vfs.PERM.RWX_RX, cmeta="" }
pcall(writeMetaEntry, parent, name, entry, false)
end
end end
function vfs.remove(path) function vfs.remove(path)
local norm = resolveSymlinks(path, true)
local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end
local parentMeta = getFileMeta(parent)
checkperms(parentMeta, "w")
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
checkperms(meta, "w")
if kernel.unixSockets and kernel.unixSockets[path] then if kernel.unixSockets and kernel.unixSockets[path] then
kernel.unixSockets[path] = nil kernel.unixSockets[path] = nil

View File

@@ -3,6 +3,7 @@ local args = {...}
local kernel = args[1] local kernel = args[1]
kernel._G = _G kernel._G = _G
local function readonly(tbl) local function readonly(tbl)
return setmetatable({}, { return setmetatable({}, {
__index = function(_, key) __index = function(_, key)
@@ -49,8 +50,10 @@ local function readonly(tbl)
__metatable = false __metatable = false
}) })
end end
--local origLoad = load
kernel._U = readonly(kernel._G) kernel._U = readonly(kernel._G)
kernel.allowGlobalOverwrites = true kernel.allowGlobalOverwrites = true
kernel._U._G = kernel._U kernel._U._G = kernel._U
--kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end
kernel.allowGlobalOverwrites = false kernel.allowGlobalOverwrites = false

View File

@@ -394,6 +394,8 @@ function auth.newUser(username, password, gid, homedir, shell)
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
kernel.vfs.mkdir(homedir) kernel.vfs.mkdir(homedir)
-- Homedir must be owned by the new user, not root
pcall(kernel.vfs.chown, homedir, uid, uid)
end end
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid)) kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))
@@ -591,13 +593,13 @@ function auth.elevate(targetUsername, password)
local task = kernel.currentTask local task = kernel.currentTask
local prevUid = task.uid local prevUid = task.uid
task.uid = uid task.uid = 0
task.euid = uid task.euid = 0
task.gid = tonumber(entry[2]) or uid task.gid = 0
task.egid = tonumber(entry[2]) or uid task.egid = 0
kernel.uid = uid kernel.uid = 0
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> " .. tostring(uid) .. " (" .. targetUsername .. ")") kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> 0 (via " .. targetUsername .. ")")
return true, uid return true, uid
end end

View File

@@ -13,15 +13,19 @@ local RW____ = P.OWNER_R + P.OWNER_W
local RWXRWXRWX = PERM.RWXRWXRWX local RWXRWXRWX = PERM.RWXRWXRWX
local SUID_755 = PERM.SUID_755 local SUID_755 = PERM.SUID_755
local META_VERSION = 0x01 local META_VERSION = 0x02
local rootDisk = kernel.disks["$"] local rootDisk = kernel.disks["$"]
local function makeEntry(name, etype, owner, group, perms, cmeta) local function makeEntry(name, etype, owner, group, perms, cmeta)
cmeta = cmeta or "" cmeta = cmeta or ""
local plo = perms % 256 local plo = perms % 256
local phi = math.floor(perms / 256) % 256 local phi = math.floor(perms / 256) % 256
local olo = (owner or 0) % 256
local ohi = math.floor((owner or 0) / 256) % 256
local glo = (group or 0) % 256
local ghi = math.floor((group or 0) / 256) % 256
return string.char(#name) .. name return string.char(#name) .. name
.. string.char(etype, owner, group, plo, phi) .. string.char(etype, olo, ohi, glo, ghi, plo, phi)
.. string.char(#cmeta) .. cmeta .. string.char(#cmeta) .. cmeta
end end
@@ -47,12 +51,106 @@ end
local REG = 0x00 local REG = 0x00
if rootDisk:fileExists(".meta") then -- All known /bin entries with their permissions
kernel.log("Permissions already seeded, skipping.", "INFO") local BIN_ENTRIES = {
else {"cat", REG, 0, 0, RWX_RX_RX},
{"chattr", REG, 0, 0, RWX_RX_RX},
{"chgrp", REG, 0, 0, RWX_RX_RX},
{"chmod", REG, 0, 0, RWX_RX_RX},
{"chown", REG, 0, 0, RWX_RX_RX},
{"chroot", REG, 0, 0, RWX_RX_RX},
{"clear", REG, 0, 0, RWX_RX_RX},
{"echo", REG, 0, 0, RWX_RX_RX},
{"hfetch", REG, 0, 0, RWX_RX_RX},
{"help", REG, 0, 0, RWX_RX_RX},
{"hysh", REG, 0, 0, RWX_RX_RX},
{"hyshex", REG, 0, 0, RWX_RX_RX},
{"id", REG, 0, 0, RWX_RX_RX},
{"install", REG, 0, 0, RWX_RX_RX},
{"ln", REG, 0, 0, RWX_RX_RX},
{"login", REG, 0, 0, SUID_755 },
{"loimgcreate", REG, 0, 0, RWX_RX_RX},
{"looptest", REG, 0, 0, RWX_RX_RX},
{"losetup", REG, 0, 0, RWX_RX_RX},
{"ls", REG, 0, 0, RWX_RX_RX},
{"lsusers", REG, 0, 0, RWX_RX_RX},
{"lua", REG, 0, 0, RWX_RX_RX},
{"luaold", REG, 0, 0, RWX_RX_RX},
{"micro", REG, 0, 0, RWX_RX_RX},
{"mkdir", REG, 0, 0, RWX_RX_RX},
{"mount", REG, 0, 0, RWX_RX_RX},
{"passwd", REG, 0, 0, RWX_RX_RX},
{"ps", REG, 0, 0, RWX_RX_RX},
{"pwd", REG, 0, 0, RWX_RX_RX},
{"readlink", REG, 0, 0, RWX_RX_RX},
{"sed", REG, 0, 0, RWX_RX_RX},
{"socktest", REG, 0, 0, RWX_RX_RX},
{"spm", REG, 0, 0, RWX_RX_RX},
{"su", REG, 0, 0, SUID_755 },
{"sudo", REG, 0, 0, SUID_755 },
{"sysdump", REG, 0, 0, RWX_RX_RX},
{"umount", REG, 0, 0, RWX_RX_RX},
{"useradd", REG, 0, 0, RWX_RX_RX},
{"userdel", REG, 0, 0, RWX_RX_RX},
{"usermod", REG, 0, 0, RWX_RX_RX},
{"whoami", REG, 0, 0, RWX_RX_RX},
{"yes", REG, 0, 0, RWX_RX_RX},
{"startup", REG, 0, 0, RWX_RX_RX},
}
-- Merge entries: always ensure all known entries exist with correct permissions.
-- This handles both fresh installs and upgrades (adds missing entries, upgrades
-- the on-disk format to v2 by rewriting).
local function mergeMeta(dir, entries)
local diskDir = dir
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
-- Read existing meta (may be v1 or v2)
local existing = {}
local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end)
if rok and rf then
local raw = rf.read(65535)
if rf.close then rf.close() end
-- Parse using the VFS parser (handles v0/v1/v2)
existing = kernel.vfs and kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw) or {}
end
-- Add any missing entries (don't overwrite existing customised perms)
for _, e in ipairs(entries) do
if not existing[e[1]] then
existing[e[1]] = {
etype = e[2] or 0x00,
owner = e[3] or 0,
group = e[4] or 0,
perms = e[5] or RWX_RX_RX,
cmeta = e[6] or "",
}
end
end
-- Write back as v2
local data = string.char(META_VERSION)
for name, m in pairs(existing) do
data = data .. makeEntry(name, m.etype or 0x00, m.owner or 0, m.group or 0, m.perms or RWX_RX_RX, m.cmeta or "")
end
local ok, err = pcall(function()
local f = rootDisk:open(metaPath, "w")
f.write(data)
f.close()
end)
if not ok then
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 8)
end
end
local freshInstall = not rootDisk:fileExists(".meta")
if freshInstall then
kernel.log("Seeding filesystem permissions...", "INFO") kernel.log("Seeding filesystem permissions...", "INFO")
-- / -- / (only on fresh install — these dirs are stable)
writeMeta("/", { writeMeta("/", {
{"bin", REG, 0, 0, RWX_RX_RX}, {"bin", REG, 0, 0, RWX_RX_RX},
{"boot", REG, 0, 0, RWX_RX_RX}, {"boot", REG, 0, 0, RWX_RX_RX},
@@ -67,38 +165,10 @@ else
{"var", REG, 0, 0, RWX_RX_RX}, {"var", REG, 0, 0, RWX_RX_RX},
}) })
-- /bin
writeMeta("/bin", {
{"cat", REG, 0, 0, RWX_RX_RX},
{"clear", REG, 0, 0, RWX_RX_RX},
{"echo", REG, 0, 0, RWX_RX_RX},
{"hfetch", REG, 0, 0, RWX_RX_RX},
{"hysh", REG, 0, 0, RWX_RX_RX},
{"hyshex", REG, 0, 0, RWX_RX_RX},
{"install", REG, 0, 0, RWX_RX_RX},
{"login", REG, 0, 0, SUID_755 },
{"ls", REG, 0, 0, RWX_RX_RX},
{"lua", REG, 0, 0, RWX_RX_RX},
{"luaold", REG, 0, 0, RWX_RX_RX},
{"mkdir", REG, 0, 0, RWX_RX_RX},
{"ps", REG, 0, 0, RWX_RX_RX},
{"pwd", REG, 0, 0, RWX_RX_RX},
{"spm", REG, 0, 0, RWX_RX_RX},
{"su", REG, 0, 0, SUID_755 },
{"sudo", REG, 0, 0, SUID_755 },
{"sysdump", REG, 0, 0, RWX_RX_RX},
{"whoami", REG, 0, 0, RWX_RX_RX},
{"yes", REG, 0, 0, RWX_RX_RX},
{"startup", REG, 0, 0, RWX_RX_RX},
{"ln", REG, 0, 0, RWX_RX_RX},
{"readlink", REG, 0, 0, RWX_RX_RX},
})
writeMeta("/bin/startup", { writeMeta("/bin/startup", {
{"test.lua", REG, 0, 0, RWX_RX_RX}, {"test.lua", REG, 0, 0, RWX_RX_RX},
}) })
-- /etc
writeMeta("/etc", { writeMeta("/etc", {
{"passwd", REG, 0, 0, RW_R_R}, {"passwd", REG, 0, 0, RW_R_R},
{"shadow", REG, 0, 0, RW____}, {"shadow", REG, 0, 0, RW____},
@@ -109,12 +179,10 @@ else
{"secret", REG, 0, 0, RW____}, {"secret", REG, 0, 0, RW____},
}) })
-- /sbin
writeMeta("/sbin", { writeMeta("/sbin", {
{"init.lua", REG, 0, 0, RWX_RX_RX}, {"init.lua", REG, 0, 0, RWX_RX_RX},
}) })
-- /boot
writeMeta("/boot", { writeMeta("/boot", {
{"kernel.lua", REG, 0, 0, RW_R_R }, {"kernel.lua", REG, 0, 0, RW_R_R },
{"boot.cfg", REG, 0, 0, RW_R_R }, {"boot.cfg", REG, 0, 0, RW_R_R },
@@ -125,7 +193,6 @@ else
{"oc", REG, 0, 0, RWX_RX_RX}, {"oc", REG, 0, 0, RWX_RX_RX},
}) })
-- /lib
writeMeta("/lib", { writeMeta("/lib", {
{"sys", REG, 0, 0, RWX_RX_RX}, {"sys", REG, 0, 0, RWX_RX_RX},
{"modules", REG, 0, 0, RWX_RX_RX}, {"modules", REG, 0, 0, RWX_RX_RX},
@@ -137,6 +204,11 @@ else
}) })
kernel.log("Filesystem permissions seeded.", "INFO") kernel.log("Filesystem permissions seeded.", "INFO")
else
kernel.log("Permissions already seeded, merging /bin updates...", "INFO")
end end
-- Always merge /bin — adds missing entries and upgrades format to v2
mergeMeta("/bin", BIN_ENTRIES)
kernel.log("Permission module loaded.", "INFO") kernel.log("Permission module loaded.", "INFO")

View File

@@ -0,0 +1,5 @@
local args = {...}
local kernel = args[1]
local origLoad = load
--kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end