Add no-posix files.

This commit is contained in:
2024-11-22 19:36:13 +01:00
parent 7d42a35f7a
commit 86fe1ba8b7
3 changed files with 73 additions and 224 deletions

View File

@@ -1,6 +1,6 @@
""" """
Install script for the Python jailer. Install script for the Python jailer.
Version: 0.2.0-alpha2 Version: 0.2.0-alpha1
""" """
import os import os
import shutil import shutil
@@ -43,7 +43,7 @@ if setup_posix.lower() == "y":
with open("./lib64", "a+") as f: with open("./lib64", "a+") as f:
f.write("symlnk /usr/lib64/") f.write("symlnk /usr/lib64/")
f.close() f.close()
with open("./usr/bin/sh", "a+") as f: with open("./usr/bin/sh.py", "a+") as f:
f.write("symlnk /usr/bin/shell.py") f.write("symlnk /usr/bin/shell.py")
f.close() f.close()
os.mkdir("./sys") os.mkdir("./sys")
@@ -85,4 +85,4 @@ else:
f.write(usrname) f.write(usrname)
f.close() f.close()
print("Install completed! Run ./main.py to start the kernel!") print("Install completed! Run ./main.py to start the kernel!")
input("Press <Enter> to exit! ") input("Press <Enter> to exit! ")

251
main.py
View File

@@ -1,193 +1,58 @@
""" """
This is the PyJail, a jailing tool for running Python apps in a sandboxed environment. This is a sort of OS built in Python, not bootable, but creates a custom directory structure and path definition.
Version: edge0003-base0.2.1 This is the "kernel", it hosts all features and runs all programs.
""" For safety reasons the kernel is isolated, which means that with every shell instance,
import os A new kernel instance will follow it. Same goes for every program, it will need to call upon a brand-new kernel instance.
import time Version: 0.1.1-nps3
import runpy """
import os
import runpy
class PyJail:
""" class PyJail:
The jail manager, handles all system calls and such. """
""" The "kernel" for PyNVOS
def __init__(self, debug=False): """
self.rootpath = "" def __init__(self):
self.rootpath = self.fs() self.rootpath = ""
self._debug = debug self.rootpath = self.fs()
with open(self.fs("/proc/klog"), "w") as f:
# Always use jailmgr.msg() from this point onwards. def run_program(self, path_to_bin):
f.write(f"[{time.time}] [jailmgr.__init__()] [INFO] START LOG") """
f.close() Runs a specified program.
with open(self.fs("/proc/kproc"), "w") as f: """
f.write("proc: jailmgr(1)") path_to_bin = self.fs(path_to_bin)
self._program_counter = 2 # print(path_to_bin)
f.close() # print(str(self.rootpath) + str(path_to_bin))
self._resolve_symlinks = False if os.path.isdir(self.fs("/bin")) else True if path_to_bin == 3 or path_to_bin == 2:
print("An error has occurred launching the program.")
def run_program(self, path_to_bin): else:
""" runpy.run_path(path_to_bin)
Runs a specified program.
""" def fs(self, check_path=None) -> str:
path_to_bin = self.fs(path_to_bin) """
if path_to_bin == 3 or path_to_bin == 2: Keeps track of the jailed filesystem and makes sure any calls to any
self.msg("jailmgr.run_program()", "An error has occurred launching the program.", True, file get done in the jailed filesystem
"WARNING") """
else: if check_path is not None:
with open(self.fs("/proc/kproc"), "a+") as f: if os.path.exists(f"{self.rootpath}{check_path}"):
f.write(f"proc: {path_to_bin}({self._program_counter})") if check_path.startswith("."):
self._program_counter += 1 check_path = check_path.lstrip(".")
runpy.run_path(path_to_bin) return os.getcwd() + check_path if "vfs" in os.getcwd() else 2
return self.rootpath + check_path
def msg(self, caller: str, message: str, emit: bool = False, log_level: str = "INFO"): elif self.rootpath in check_path:
""" print("ERR: Cannot parse rootpath, expected vfspath")
The custom message parser, can parse messages and alert apps of said messages. return 3
Replaces print statements. else:
Args: # Path is not in the jailed fs, so we say it doesn't exist.
caller: The program that called the logger print("ERR: File/directory doesn't exist in vfspath")
message: Is the message to parse. return 2
emit: If the message needs to be passed to apps. else:
log_level: The loglevel, either DEBUG, INFO, WARNING, ERROR, CRITICAL rootpath = os.getcwd() + "/vfs"
""" return rootpath
if self._debug is True:
emit = True @staticmethod
accepted_log_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] def is_posix_compatible() -> bool:
if log_level.upper() not in accepted_log_levels: """
self.msg(f"jailmgr.msg()",f"Not accepted loglevel!! {log_level}", False, "ERROR") Returns if the kernel is POSIX compatible.
return 1 """
msg = f"[{time.time}] [{caller}] [{log_level}] {message}" return False
with open(self.fs("/proc/klog"), "a+") as f:
f.write(msg)
if emit is True:
print(msg)
return 0
def fs(self, check_path=None, resolve_symlinks=True):
"""
Keeps track of the jailed filesystem and makes sure any calls to any
file get done in the jailed filesystem
"""
if check_path is not None:
if os.path.exists(f"{self.rootpath}{check_path}"):
if self._resolve_symlinks is True or resolve_symlinks is True:
# This exists to ease /bin and other symlinks that are always present.
# It allows the system to drastically speed up resolving symlinks.
symlinkable_dirs = ["/bin", "/sbin", "/lib", "/lib64"]
for directory in symlinkable_dirs:
if check_path.startswith(directory):
check_path_usr_merge = f"/usr{check_path}"
return self.rootpath + check_path_usr_merge
# Well, if it doesn't start with any of them, we need to check each and every directory.
check_path_split = check_path.split("/", -1)
prepend_path = ""
for i, path in enumerate(check_path_split):
prepend_path = f"{prepend_path}/{path}" if not path.endswith("/") else f"{prepend_path}/{path}/"
check_path_split[i] = f"{prepend_path}"
check_path = ""
for i, path in enumerate(check_path_split):
if os.path.isdir(path) and i != (len(check_path_split) - 1):
# Directory is not a symlink, we can just ignore and move on.
pass
elif os.path.isdir(path) and i == (len(check_path_split) - 1):
# The last thing to access was a directory. We can safely return the full path now.
return path
elif not os.path.isdir(path) and i != (len(check_path_split) - 1):
# This was not the last thing we needed to access, so we assume it's a symlink.
# One problem is that we can't be sure, so we make sure it is a symlink.
with open(self.fs(path, False)) as f:
is_symlink = f.read()
f.close()
if is_symlink.startswith("symlnk"):
# This is a symlink!
# Symlinks always contain the full literal path that they need to access, so we can
# take that and do the same trick to split it and add the next things to it.
is_symlink_split = is_symlink.split(" ", 1)
symlink_dest = is_symlink_split[1]
symlink_dest = f"{symlink_dest}/{path}"
return symlink_dest
else:
# This is either not a symlink or an improperly configured one.
self.msg("jailmgr.fs()", "reached non-symlink file not at end of list",
False, "ERROR")
self.msg("jailmgr.fs()", "What was assumed to be a directory isn't a"
" directory nor a symlink! This might be because of a "
"typo or misconfigured symlink. The directory in question: "
f"{path}", True, "WARNING")
return 2
return self.rootpath + check_path
if check_path.startswith("."):
check_path = check_path.lstrip(".")
return os.getcwd() + check_path if "vfs" in os.getcwd() else 2
elif self.rootpath in check_path:
self.msg(f"jailmgr.fs()", "Cannot parse rootpath, expected vfspath", log_level="ERROR")
return 3
else:
# Path is not in the jailed fs, so we say it doesn't exist.
self.msg(f"jailmgr.fs()", "File/directory doesn't exist in vfspath", log_level="ERROR")
return 2
else:
rootpath = os.getcwd() + "/vfs"
self.msg("jailmgr.fs()", message=rootpath, log_level="INFO")
return rootpath
def kver(self):
"""
Returns the kernel version
"""
return "edge0003-base0.2.1"
def netsock(self, ip, port, mode, msg):
"""
An easy interface to network sockets, built right into the jailmanager
Args:
ip: The IP of the server to access.
port: The port to access the server on
mode: Either UDP, TCP or PKG (HTTP)
msg: The message to send the server
Returns:
Whatever the server returns.
"""
if mode == "PKG":
import requests
else:
import socket
if mode == "TCP":
try:
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except Exception as e:
self.msg("jailmgr.netsock()", "Socket import failed!", False, "CRITICAL")
self.msg("jailmgr.netsock()", "An unexpected error occurred!", True, "ERROR")
return None
# Connect to the server
client_socket.connect((ip, port))
self.msg("jailmgr.netsock()", f"Connected to server at {server_ip}:{server_port}", False, "INFO")
# Send the message to the server
client_socket.send(message.encode())
# Receive the response from the server
response = client_socket.recv(1024).decode()
client_socket.close() # Close the connection
self.msg("jailmgr.netsock", f"Received from server: {response}", False "INFO")
return response
elif mode == "PKG":
# raise NotImplementedError("TODO: PKG will be implemented later!")
file_io = requests.get(ip)
if file_io.startswith("PYPAK PMD"):
with open(self.fs(f"/usr/netsock/cache/{pkg}.pmd"), "a+") as f:
f.write(file_io)
f.close()
else:
raise NotImplementedError("TODO: UDP will be implemented later!")
# raise NotImplementedError("TODO: Netsock will be implemented once 0.3.0 comes around!")

40
sh.py
View File

@@ -1,60 +1,44 @@
""" """
The shell for PyNVOS The shell for PyNVOS
Version: 0.2.0.0400 Version: 0.1.0-main1
""" """
import importlib import importlib
import os import os
import cmd import cmd
import shutil
# from ..sys.krnl import Kernel # from ..sys.krnl import Kernel
print(__name__) print(__name__)
class shell(cmd.Cmd): class shell(cmd.Cmd):
jail_mgr = importlib.import_module(".jail_mgr", "vfs.sys") intro = "Shell started, PyNVOS 0.1.1-main1"
jailmgr = jail_mgr.PyJail() prompt = "shell-0.1.0$ "
kver = jailmgr.kver()
intro = f"Shell started, PyNVOS {kver}"
prompt = "shell-0.2$ "
file = None file = None
krnl = importlib.import_module(".jail_mgr", "vfs.sys")
kernel = krnl.Kernel()
print(str(kernel) + " " + str(type(kernel)))
def do_cd(self, args): def do_cd(self, args):
"""Changes directory""" """Changes directory"""
args = shell.jailmgr.fs(args) args = shell.kernel.fs(args)
os.chdir(args) os.chdir(args)
def do_exec(self, args): def do_exec(self, args):
"""Allows you to execute a file""" """Allows you to execute a file"""
# Apps in /bin should be allowed to launch without first adding /bin/ or ./, just the name of the executable # Apps in /bin should be allowed to launch without first adding /bin/ or ./, just the name of the executable
# So for ledit it should be just 'ledit' and not /bin/ledit.py or ./ledit.py # So for ledit it should be just 'ledit' and not /bin/ledit.py or ./ledit.py
bins_in_bin = os.listdir(self.jailmgr.fs("/bin")) bins_in_bin = os.listdir(shell.kernel.fs("/bin"))
apps_strip = [] apps_strip = []
for apps in bins_in_bin: for apps in bins_in_bin:
if apps.endswith(".py"): if apps.endswith(".py"):
apps_strip.append(apps.strip(".py")) apps_strip.append(apps.strip(".py"))
if args in apps_strip: if args in apps_strip:
shell.jailmgr.run_program(f"/bin/{args}.py") shell.kernel.run_program(f"/bin/{args}.py")
else: else:
shell.jailmgr.run_program(args) shell.kernel.run_program(args)
def do_ls(self, none): def do_ls(self, none):
"""Lists the content of a directory""" """Lists the content of a directory"""
os.listdir(os.getcwd()) os.listdir(os.getcwd())
def do_pkg(self, pkg):
"""Downloads packages over the internet."""
self.jailmgr.netsock(f"https://pkg.novacow.ch/repo/{kver}/meta/{pkg}.pmd", None, "PKG", f"{pkg}")
shutil.copy(self.jailmgr.fs(f"/usr/netsock/cache/{pkg}.pmd"), self.jailmgr.fs(f"/usr/pkg/metacache/"))
with open(self.jailmgr.fs(f"/usr/pkgs/metacache/{pkg}.pmd"), "r") as f:
package_meta = f.read()
f.close()
print(package_meta)
y_n_confirmation = input("Do you want to install this package? [y/N] ")
if y_n_confirmation.lower() != "y":
print("Aborted.")
return
self.jailmgr.netsock(f"https://pkg.novacow.ch/repo/{kver}/main/static/binary/{pkg}.py", None, "PKG", f"{pkg}")
shutil.copy(self.jailmgr.fs(f"/usr/netsock/cache/{pkg}.py"), self.jailmgr.fs(f"/usr/bin/"))
def postloop(self): def postloop(self):
pass pass
@@ -62,6 +46,6 @@ class shell(cmd.Cmd):
if __name__ == '<run_path>': if __name__ == '<run_path>':
shell().cmdloop() shell().cmdloop()
if __name__ == '__main__': if __name__ == '__main__':
print("The shell can't be ran as a standalone program and must be ran in conjunction with the jail manager.") print("The shell can't be ran as a standalone program and must be ran in conjunction with the kernel.")
input("Press Enter to continue...") input("Press Enter to continue...")
exit(-1) exit(-1)