5 Commits
1.2.1 ... 1.2.2

Author SHA1 Message Date
ab1e847d1c Fix exploit with trailing whitespace 2026-02-24 00:48:20 -06:00
62a03bfe6b Potential windows case insensitive filesystem issue fix 2026-02-24 00:34:59 -06:00
e77a8b3636 AsyncSyscall3 exploit fix 2026-02-24 00:01:39 -06:00
6bb7f03a3e file permissions fixes 2026-02-23 23:50:37 -06:00
8798a2f4fe 2 potential vulnerability fixes 2026-02-23 23:26:21 -06:00
29 changed files with 320 additions and 274 deletions

View File

@@ -33,7 +33,6 @@ local host_str = syscall.getHost() or "Unknown"
local cc_ver = host_str:match("ComputerCraft ([%d%.]+)") or host_str local cc_ver = host_str:match("ComputerCraft ([%d%.]+)") or host_str
local info = { local info = {
-- {label, value} label=nil means print value as-is (userhost / separator)
{nil, userhost}, {nil, userhost},
{nil, string.rep("-", #userhost)}, {nil, string.rep("-", #userhost)},
{"OS", syscall.version() or "Unknown"}, {"OS", syscall.version() or "Unknown"},
@@ -42,7 +41,7 @@ local info = {
{"Uptime", formatUptime(syscall.getUptime() or 0)}, {"Uptime", formatUptime(syscall.getUptime() or 0)},
{"Tasks", tostring(#(syscall.getTasks() or {}))}, {"Tasks", tostring(#(syscall.getTasks() or {}))},
{"Shell", syscall.getEnviron("SHELL") or "Unknown"}, {"Shell", syscall.getEnviron("SHELL") or "Unknown"},
{"Terminal", "TTY1"}, {"Terminal", "tty1"},
{"UID", tostring(syscall.getuid())}, {"UID", tostring(syscall.getuid())},
{"Packages", "n/a (spm)"}, {"Packages", "n/a (spm)"},
} }
@@ -71,15 +70,12 @@ local lines = math.max(#logo, #info)
for i = 1, lines do for i = 1, lines do
local logo_str = logo[i] or string.rep(" ", 36) local logo_str = logo[i] or string.rep(" ", 36)
-- print logo segment in cyan
c(C_LOGO) c(C_LOGO)
printInline(logo_str) printInline(logo_str)
-- print separator pipe
c(C_LABEL) c(C_LABEL)
printInline("| ") printInline("| ")
-- print info segment
local row = info[i] local row = info[i]
if row then if row then
if row[1] == nil and i == 1 then if row[1] == nil and i == 1 then

View File

@@ -1,6 +1,6 @@
--:Minify:-- --:Minify:--
syscall.open("/dev/tty/TTY1","r") --stdin (Device 0) syscall.open("/dev/tty/tty1","r") --stdin (Device 0)
syscall.open("/dev/tty/TTY1","w") --stdout (Device 1) syscall.open("/dev/tty/tty1","w") --stdout (Device 1)
syscall.open("/dev/null","w") --stderr (device 2) syscall.open("/dev/null","w") --stderr (device 2)
local success, errorMsg = xpcall(function() local success, errorMsg = xpcall(function()
@@ -957,8 +957,8 @@ local function runCommand(command)
end end
local proc = syscall.spawn(function(...) local proc = syscall.spawn(function(...)
syscall.open("/dev/tty/TTY1","r") syscall.open("/dev/tty/tty1","r")
syscall.open("/dev/tty/TTY1","w") syscall.open("/dev/tty/tty1","w")
syscall.open("/dev/null","w") syscall.open("/dev/null","w")
local ok2, msg = pcall(program, ...) local ok2, msg = pcall(program, ...)
if not ok2 then printError(progName, msg) end if not ok2 then printError(progName, msg) end

View File

@@ -1,6 +1,6 @@
--:Minify:-- --:Minify:--
syscall.open("/dev/tty/TTY1","r") syscall.open("/dev/tty/tty1","r")
syscall.open("/dev/tty/TTY1","w") syscall.open("/dev/tty/tty1","w")
syscall.open("/dev/null","r") syscall.open("/dev/null","r")
syscall.devctl(1,"clear") syscall.devctl(1,"clear")
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",1)
@@ -57,8 +57,8 @@ while true do
printInline("> ") printInline("> ")
end end
proc = syscall.spawn(function(...) proc = syscall.spawn(function(...)
syscall.open("/dev/tty/TTY1","r") syscall.open("/dev/tty/tty1","r")
syscall.open("/dev/tty/TTY1","w") syscall.open("/dev/tty/tty1","w")
syscall.open("/dev/null","w") syscall.open("/dev/null","w")
program(...) program(...)
end, path, nil, {table.unpack(split, 2)}) end, path, nil, {table.unpack(split, 2)})

View File

@@ -1,6 +1,6 @@
--:Minify:-- --:Minify:--
syscall.open("/dev/tty/TTY1", "r") --stdin (fd 0) syscall.open("/dev/tty/tty1", "r") --stdin (fd 0)
syscall.open("/dev/tty/TTY1", "w") --stdout (fd 1) syscall.open("/dev/tty/tty1", "w") --stdout (fd 1)
syscall.open("/dev/null", "w") --stderr (fd 2) syscall.open("/dev/null", "w") --stderr (fd 2)

View File

@@ -184,9 +184,16 @@ end
kernel.log("Gathering modules") kernel.log("Gathering modules")
for _, i in ipairs(ifs.list("/lib/modules")) do for _, i in ipairs(ifs.list("/lib/modules")) do
for _,v in ipairs(ifs.list("/lib/modules/"..i)) do local modlist = ifs.list("/lib/modules/"..i)
local prior=tonumber(v:sub(1,2)) if not modlist then
modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v kernel.log("WARNING: could not list /lib/modules/"..i.." (skipping)", "WARN", 8)
else
for _,v in ipairs(modlist) do
local prior=tonumber(v:sub(1,2))
if prior then
modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v
end
end
end end
end end

View File

@@ -1,214 +0,0 @@
-- :Minify:--
local kernel = ...
local P = kernel.vfs.P
local PERM = kernel.vfs.PERM
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
+ P.GROUP_R + P.GROUP_X
+ P.WORLD_R + P.WORLD_X
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
local RW____ = P.OWNER_R + P.OWNER_W
local RWXRWXRWX = PERM.RWXRWXRWX
local SUID_755 = PERM.SUID_755
local META_VERSION = 0x02
local rootDisk = kernel.disks["$"]
local function makeEntry(name, etype, owner, group, perms, cmeta)
cmeta = cmeta or ""
local plo = perms % 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
.. string.char(etype, olo, ohi, glo, ghi, plo, phi)
.. string.char(#cmeta) .. cmeta
end
local function writeMeta(dir, entries)
local diskDir = dir
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
local data = string.char(META_VERSION)
for _, e in ipairs(entries) do
data = data .. makeEntry(e[1], e[2] or 0x00, e[3], e[4], e[5], e[6])
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 REG = 0x00
-- All known /bin entries with their permissions
local BIN_ENTRIES = {
{"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")
-- / (only on fresh install — these dirs are stable)
writeMeta("/", {
{"bin", REG, 0, 0, RWX_RX_RX},
{"boot", REG, 0, 0, RWX_RX_RX},
{"dev", REG, 0, 0, RWX_RX_RX},
{"etc", REG, 0, 0, RWX_RX_RX},
{"home", REG, 0, 0, RWX_RX_RX},
{"lib", REG, 0, 0, RWX_RX_RX},
{"root", REG, 0, 0, RW____ },
{"sbin", REG, 0, 0, RWX_RX_RX},
{"tmp", REG, 0, 0, RWXRWXRWX},
{"usr", REG, 0, 0, RWX_RX_RX},
{"var", REG, 0, 0, RWX_RX_RX},
})
writeMeta("/bin/startup", {
{"test.lua", REG, 0, 0, RWX_RX_RX},
})
writeMeta("/etc", {
{"passwd", REG, 0, 0, RW_R_R},
{"shadow", REG, 0, 0, RW____},
{"pam.d", REG, 0, 0, RWX_RX_RX},
})
writeMeta("/etc/pam.d", {
{"secret", REG, 0, 0, RW____},
})
writeMeta("/sbin", {
{"init.lua", REG, 0, 0, RWX_RX_RX},
})
writeMeta("/boot", {
{"kernel.lua", REG, 0, 0, RW_R_R },
{"boot.cfg", REG, 0, 0, RW_R_R },
{"safeboot.cfg", REG, 0, 0, RW_R_R },
{"fstab", REG, 0, 0, RW_R_R },
{"initfs", REG, 0, 0, RW_R_R },
{"cct", REG, 0, 0, RWX_RX_RX},
{"oc", REG, 0, 0, RWX_RX_RX},
})
writeMeta("/lib", {
{"sys", REG, 0, 0, RWX_RX_RX},
{"modules", REG, 0, 0, RWX_RX_RX},
{"crypto", REG, 0, 0, RWX_RX_RX},
{"store", REG, 0, 0, RWX_RX_RX},
{"snip", REG, 0, 0, RW_R_R },
{"io", REG, 0, 0, RW_R_R },
{"bit32", REG, 0, 0, RW_R_R },
})
kernel.log("Filesystem permissions seeded.", "INFO")
else
kernel.log("Permissions already seeded, merging /bin updates...", "INFO")
end
-- Always merge /bin — adds missing entries and upgrades format to v2
mergeMeta("/bin", BIN_ENTRIES)
kernel.log("Permission module loaded.", "INFO")

View File

@@ -1,5 +0,0 @@
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

View File

@@ -1,4 +1,7 @@
-- :Minify:-- -- :Minify:--
local kernel = ...
kernel.allowGlobalOverwrites = true
function string.hasSuffix(str, suffix) function string.hasSuffix(str, suffix)
return string.sub(str, #suffix + 1) == suffix return string.sub(str, #suffix + 1) == suffix
end end
@@ -68,30 +71,24 @@ end
local function serialize(tbl, seen) local function serialize(tbl, seen)
seen = seen or {} seen = seen or {}
-- If we've seen this table before, return a placeholder to prevent infinite loops
if seen[tbl] then return '"[Circular Reference]"' end if seen[tbl] then return '"[Circular Reference]"' end
-- Mark this table as seen
seen[tbl] = true seen[tbl] = true
local output = "{" local output = "{"
local first = true local first = true
for i, v in pairs(tbl) do for i, v in pairs(tbl) do
-- Handle comma placement more cleanly
if not first then output = output .. "," end if not first then output = output .. "," end
first = false first = false
-- Serialize Key
if type(i) == "string" then if type(i) == "string" then
output = output .. "[\"" .. i .. "\"]=" output = output .. "[\"" .. i .. "\"]="
elseif type(i) == "number" then elseif type(i) == "number" then
output = output .. "[" .. tostring(i) .. "]=" output = output .. "[" .. tostring(i) .. "]="
end end
-- Serialize Value
if type(v) == "table" then if type(v) == "table" then
-- Pass the 'seen' table down to the recursive call
output = output .. serialize(v, seen) output = output .. serialize(v, seen)
elseif type(v) == "string" then elseif type(v) == "string" then
output = output .. "[=[" .. v .. "]=]" output = output .. "[=[" .. v .. "]=]"

View File

@@ -60,21 +60,18 @@ local function parseMetafile(raw)
local etype, owner, group, perms, cmeta local etype, owner, group, perms, cmeta
if version == 0x02 then if version == 0x02 then
-- v2: etype(1) + owner(2) + group(2) + perms(2) = 7 bytes
if p + 6 > #raw then break end if p + 6 > #raw then break end
etype = raw:byte(p); p = p + 1 etype = raw:byte(p); p = p + 1
owner = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2 owner = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
group = 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 perms = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
elseif version == 0x01 then 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
@@ -114,25 +111,58 @@ local function makeMetafile(meta)
return out return out
end end
local SAFE_COMPONENT_PATTERN = "^[A-Za-z0-9_.+%-%@%(%)%[%]]+$"
local function normalizePath(path) local function normalizePath(path)
local task = kernel.currentTask local task = kernel.currentTask
local cwd = task.cwd or "/" local cwd = task.cwd or "/"
if path:sub(1,1) ~= "/" then path = cwd .. "/" .. path end
local parts = {} if path:sub(1, 1) ~= "/" then
for part in path:gmatch("[^/]+") do path = cwd .. "/" .. path
if part == ".." then end
if #parts > 0 then table.remove(parts) end
elseif part ~= "." and part ~= "" then local stack = {}
table.insert(parts, part) local i = 1
local len = #path
while i <= len do
local j = path:find("/", i, true)
local comp
if j then
comp = path:sub(i, j - 1)
i = j + 1
else
comp = path:sub(i)
i = len + 1
end
comp = comp:match("^%s*(.-)%s*$")
if comp == "" or comp == "." then
elseif comp == ".." then
if #stack > 0 then
table.remove(stack)
end
else
comp = comp:lower()
if not comp:match(SAFE_COMPONENT_PATTERN) then
error("EINVAL: illegal characters in path component: " .. comp, 2)
end
if comp == ".meta" then
error("EINVAL: reserved path component: " .. comp, 2)
end
table.insert(stack, comp)
end end
end end
local result = "/" .. table.concat(parts, "/")
local result = "/" .. table.concat(stack, "/")
local root = task and task.root local root = task and task.root
if root and root ~= "/" then if root and root ~= "/" then
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then
result = root result = root
end end
end end
return result return result
end end
@@ -164,11 +194,10 @@ 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 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("EACCES: Cannot open metafile") end
local mp local mp
if parentDiskPath == "/" then if parentDiskPath == "/" then
mp = ".meta" mp = ".meta"
@@ -181,7 +210,6 @@ local function readMetaEntry(disk, parentDiskPath, filename)
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 if raw and #raw > 0 and raw:byte(1) ~= META_VERSION then
local upgraded = makeMetafile(parseMetafile(raw)) local upgraded = makeMetafile(parseMetafile(raw))
local wok, wf = pcall(function() return disk:open(mp, "w") end) local wok, wf = pcall(function() return disk:open(mp, "w") end)
@@ -440,7 +468,6 @@ 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 if isNew then
local euid = (task and (task.euid or task.uid)) or kernel.uid local euid = (task and (task.euid or task.uid)) or kernel.uid
local egid = (task and task.gid) or 0 local egid = (task and task.gid) or 0
@@ -629,7 +656,6 @@ function vfs.mkdir(path)
checkperms(parentMeta, "w") checkperms(parentMeta, "w")
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
disk:makeDirectory(diskPath) disk:makeDirectory(diskPath)
-- Stamp the new directory with the creating user's ownership
local task = kernel.currentTask local task = kernel.currentTask
local euid = (task and (task.euid or task.uid)) or kernel.uid local euid = (task and (task.euid or task.uid)) or kernel.uid
local egid = (task and task.gid) or 0 local egid = (task and task.gid) or 0

View File

@@ -385,11 +385,11 @@ kernel.processes.cctmond = function()
end end
end end
newtty(apis.term, "TTY1", fifo.pop) newtty(apis.term, "tty1", fifo.pop)
for i,v in ipairs({peripheral.find("monitor")}) do for i,v in ipairs({peripheral.find("monitor")}) do
v.setTextScale(.5) v.setTextScale(.5)
v.write("Initializing...") v.write("Initializing...")
newtty(v,"TTY"..tostring(i+1),function () end) newtty(v,"tty"..tostring(i+1),function () end)
end end

View File

@@ -21,7 +21,7 @@ local function readonly(tbl)
error("Attempt to modify global variable '" .. k .. "'", 2) error("Attempt to modify global variable '" .. k .. "'", 2)
end, end,
__pairs = function() __pairs = function(self)
local function iter(_, key) local function iter(_, key)
local nextKey, value = next(tbl, key) local nextKey, value = next(tbl, key)
if type(value) == "table" then if type(value) == "table" then
@@ -29,7 +29,7 @@ local function readonly(tbl)
end end
return nextKey, value return nextKey, value
end end
return iter, tbl, nil return iter, self, nil
end, end,
__ipairs = function() __ipairs = function()
@@ -50,10 +50,8 @@ local function readonly(tbl)
__metatable = false __metatable = false
}) })
end end
--local origLoad = load local origLoad = load
kernel._U = readonly(kernel._G) kernel._U = readonly(kernel._G)
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._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end
kernel.allowGlobalOverwrites = false

View File

@@ -5,6 +5,8 @@ local sys = {}
local nextpid = 2 local nextpid = 2
kernel.exitMain = false kernel.exitMain = false
local resumeWithTimeout = coroutine.resumeWithTimeout
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
end end
@@ -206,10 +208,14 @@ function sys.kill(pid)
return false, "Task does not exist" return false, "Task does not exist"
elseif task.status == "Z" then elseif task.status == "Z" then
return false, "Task is already dead" return false, "Task is already dead"
else
task.status = "Z"
return true
end end
local caller = kernel.currentTask
local ceuid = caller and (caller.euid or caller.uid) or kernel.uid
if ceuid ~= 0 and task.uid ~= (caller and caller.uid or kernel.uid) then
return false, "EPERM"
end
task.status = "Z"
return true
end end
function sys.stop(pid) function sys.stop(pid)
@@ -352,7 +358,7 @@ function kernel.main()
if task.sigq and #task.sigq ~= 0 and task.sigh then if task.sigq and #task.sigq ~= 0 and task.sigh then
local coro = coroutine.create(task.sigh) local coro = coroutine.create(task.sigh)
if kernel.config.preempt then if kernel.config.preempt then
coroutine.resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1)) resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1))
else else
coroutine.resume(coro, table.remove(task.sigq, 1)) coroutine.resume(coro, table.remove(task.sigq, 1))
end end
@@ -363,7 +369,7 @@ function kernel.main()
local ret local ret
if kernel.config.preempt then if kernel.config.preempt then
ret = { coroutine.resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) } ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) }
else else
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) } ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
end end

View File

@@ -0,0 +1,233 @@
-- :Minify:--
local kernel = ...
local P = kernel.vfs.P
local PERM = kernel.vfs.PERM
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
+ P.GROUP_R + P.GROUP_X
+ P.WORLD_R + P.WORLD_X
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
local RW____ = P.OWNER_R + P.OWNER_W
local RWXRWXRWX = PERM.RWXRWXRWX
local SUID_755 = PERM.SUID_755
local META_VERSION = 0x02
local rootDisk = kernel.disks["$"]
local function makeEntry(name, etype, owner, group, perms, cmeta)
cmeta = cmeta or ""
local plo = perms % 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
.. string.char(etype, olo, ohi, glo, ghi, plo, phi)
.. string.char(#cmeta) .. cmeta
end
local REG = 0x00
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")
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
existing = (kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw)) or {}
end
for _, e in ipairs(entries) do
local name = e[1]
local etype = e[2] or REG
local owner = e[3] or 0
local group = e[4] or 0
local perms = e[5] or RWX_RX_RX
local cmeta = e[6] or ""
existing[name] = {
etype = etype,
owner = owner,
group = group,
perms = perms,
cmeta = cmeta,
}
end
local data = string.char(META_VERSION)
for name, m in pairs(existing) do
data = data .. makeEntry(
name,
m.etype or REG,
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
kernel.log("Seeding filesystem permissions...", "INFO")
mergeMeta("/", {
{"bin", REG, 0, 0, RWX_RX_RX},
{"boot", REG, 0, 0, RWX_RX_RX},
{"dev", REG, 0, 0, RWX_RX_RX},
{"etc", REG, 0, 0, RWX_RX_RX},
{"home", REG, 0, 0, RWX_RX_RX},
{"lib", REG, 0, 0, RWX_RX_RX},
{"root", REG, 0, 0, RW____ },
{"sbin", REG, 0, 0, RWX_RX_RX},
{"tmp", REG, 0, 0, RWXRWXRWX},
{"usr", REG, 0, 0, RWX_RX_RX},
{"var", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/boot", {
{"kernel.lua", REG, 0, 0, RW_R_R },
{"boot.cfg", REG, 0, 0, RW_R_R },
{"safeboot.cfg", REG, 0, 0, RW_R_R },
{"fstab", REG, 0, 0, RW_R_R },
{"initfs", REG, 0, 0, RW_R_R },
{"cct", REG, 0, 0, RWX_RX_RX},
{"oc", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/boot/cct", {
{"boot.lua", REG, 0, 0, RW_R_R},
{"initdisks", REG, 0, 0, RW_R_R},
{"eeprom", REG, 0, 0, RW_R_R},
})
mergeMeta("/boot/oc", {
{"boot.lua", REG, 0, 0, RW_R_R},
{"initfs.lua",REG, 0, 0, RW_R_R},
{"eeprom", REG, 0, 0, RW_R_R},
})
mergeMeta("/sbin", {
{"init.lua", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/bin", {
{"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},
{"startup", 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},
})
mergeMeta("/bin/startup", {
{"test.lua", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/lib", {
{"sys", REG, 0, 0, RWX_RX_RX},
{"modules", REG, 0, 0, RWX_RX_RX},
{"crypto", REG, 0, 0, RWX_RX_RX},
{"store", REG, 0, 0, RWX_RX_RX},
{"snip", REG, 0, 0, RW_R_R },
{"io", REG, 0, 0, RW_R_R },
{"bit32", REG, 0, 0, RW_R_R },
})
mergeMeta("/lib/sys", {
{"fs", REG, 0, 0, RW_R_R},
{"hpv", REG, 0, 0, RW_R_R},
{"ipc", REG, 0, 0, RW_R_R},
{"term", REG, 0, 0, RW_R_R},
{"init", REG, 0, 0, RW_R_R},
})
mergeMeta("/lib/modules", {
{"hyperion", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/lib/modules/hyperion", {
{"01_stdlib.kmod", REG, 0, 0, RW_R_R},
{"10_vfs.kmod", REG, 0, 0, RW_R_R},
{"11_require.kmod", REG, 0, 0, RW_R_R},
{"12_devfs.kmod", REG, 0, 0, RW_R_R},
{"12_tmpfs.kmod", REG, 0, 0, RW_R_R},
{"13_loopdev.kmod", REG, 0, 0, RW_R_R},
{"14_keventd.kmod", REG, 0, 0, RW_R_R},
{"19_fstab.kmod", REG, 0, 0, RW_R_R},
{"20_signals.kmod", REG, 0, 0, RW_R_R},
{"20_socket.kmod", REG, 0, 0, RW_R_R},
{"26_tty.kmod", REG, 0, 0, RW_R_R},
{"30_userspace.kmod", REG, 0, 0, RW_R_R},
{"40_auth.kmod", REG, 0, 0, RW_R_R},
{"45_hypervisor.kmod", REG, 0, 0, RW_R_R},
{"47_dbg.kmod", REG, 0, 0, RW_R_R},
{"50_gpio.kmod", REG, 0, 0, RW_R_R},
{"70_stdlibadv.kmod", REG, 0, 0, RW_R_R},
{"90_init.kmod", REG, 0, 0, RW_R_R},
{"91_login.kmod", REG, 0, 0, RW_R_R},
{"92_permissions.kmod", REG, 0, 0, RW_R_R},
{"99_final.kmod", REG, 0, 0, RW_R_R},
})
mergeMeta("/etc", {
{"passwd", REG, 0, 0, RW_R_R },
{"shadow", REG, 0, 0, RW____ },
{"pam.d", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/etc/pam.d", {
{"secret", REG, 0, 0, RW____},
})
kernel.log("Filesystem permissions seeded.", "INFO")

View File

@@ -0,0 +1,2 @@
local kernel = ...
kernel.allowGlobalOverwrites = false

View File

@@ -203,7 +203,7 @@ def _make_firstboot_kmod(users):
lines.append("do") lines.append("do")
lines.append(" local ok, err = pcall(function()") lines.append(" local ok, err = pcall(function()")
lines.append(" kernel.vfs.remove('/lib/modules/Hyperion/50_firstboot_users.kmod')") lines.append(" kernel.vfs.remove('/lib/modules/hyperion/50_firstboot_users.kmod')")
lines.append(" end)") lines.append(" end)")
lines.append(" if not ok then") lines.append(" if not ok then")
lines.append(" kernel.log('FIRSTBOOT: could not self-delete: ' .. tostring(err), 'WARN')") lines.append(" kernel.log('FIRSTBOOT: could not self-delete: ' .. tostring(err), 'WARN')")
@@ -215,7 +215,7 @@ def _make_firstboot_kmod(users):
def inject_makeusers(users, arch): def inject_makeusers(users, arch):
base = BUILD_ROOT / "$" if arch else BUILD_ROOT base = BUILD_ROOT / "$" if arch else BUILD_ROOT
kmod_path = base / "lib" / "modules" / "Hyperion" / "50_firstboot_users.kmod" kmod_path = base / "lib" / "modules" / "hyperion" / "50_firstboot_users.kmod"
kmod_path.parent.mkdir(parents=True, exist_ok=True) kmod_path.parent.mkdir(parents=True, exist_ok=True)
kmod_path.write_text(_make_firstboot_kmod(users), encoding="utf-8") kmod_path.write_text(_make_firstboot_kmod(users), encoding="utf-8")
print(" Wrote first-boot user setup -> " + str(kmod_path.relative_to(BUILD_ROOT))) print(" Wrote first-boot user setup -> " + str(kmod_path.relative_to(BUILD_ROOT)))