Skip to content

Commit

Permalink
tools/mpremote: Add option to mount cmd to allow "unsafe" symlinks.
Browse files Browse the repository at this point in the history
Specifying the option `--unsafe-links` (or `-l`) to `mpremote mount` will
allow symlinks to be followed in the local directory that point outside of
the base directory path.

For the unsafe case the `path_check()` method of `PyboardCommand` still
checks for a common path but without expanding symlinks.  While this check
is currently redundant, it makes the purpose of the method clearer for
possible future uses or extensions.
  • Loading branch information
rknegjens authored and dpgeorge committed Apr 11, 2022
1 parent 56978c3 commit d3d8627
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 8 deletions.
9 changes: 8 additions & 1 deletion docs/reference/mpremote.rst
Expand Up @@ -133,13 +133,20 @@ The full list of supported commands are:

.. code-block:: bash
$ mpremote mount <local-dir>
$ mpremote mount [options] <local-dir>
During usage, Ctrl-D will soft-reboot and normally reconnect the mount automatically.
If the unit has a main.py running at startup however the remount cannot occur.
In this case a raw mode soft reboot can be used: Ctrl-A Ctrl-D to reboot,
then Ctrl-B to get back to normal repl at which point the mount will be ready.

Options are:

- ``-l``, ``--unsafe-links``: By default an error will be raised if the device
accesses a file or directory which is outside (up one or more directory levels) the
local directory that is mounted. This option disables this check for symbolic
links, allowing the device to follow symbolic links outside of the local directory.

- unmount the local directory from the remote device:

.. code-block:: bash
Expand Down
17 changes: 15 additions & 2 deletions tools/mpremote/mpremote/main.py
Expand Up @@ -41,7 +41,16 @@
"disconnect": (False, False, 0, "disconnect current device"),
"resume": (False, False, 0, "resume a previous mpremote session (will not auto soft-reset)"),
"soft-reset": (False, True, 0, "perform a soft-reset of the device"),
"mount": (True, False, 1, "mount local directory on device"),
"mount": (
True,
False,
1,
"""\
mount local directory on device
options:
--unsafe-links, -l
follow symbolic links pointing outside of local directory""",
),
"umount": (True, False, 0, "unmount the local directory"),
"repl": (
False,
Expand Down Expand Up @@ -516,8 +525,12 @@ def main():
pyb.enter_raw_repl(soft_reset=True)
auto_soft_reset = False
elif cmd == "mount":
unsafe_links = False
if args[0] == "--unsafe-links" or args[0] == "-l":
args.pop(0)
unsafe_links = True
path = args.pop(0)
pyb.mount_local(path)
pyb.mount_local(path, unsafe_links=unsafe_links)
print(f"Local directory {path} is mounted at /remote")
elif cmd == "umount":
pyb.umount_local()
Expand Down
15 changes: 10 additions & 5 deletions tools/mpremote/mpremote/pyboardextended.py
Expand Up @@ -351,12 +351,13 @@ def __mount():


class PyboardCommand:
def __init__(self, fin, fout, path):
def __init__(self, fin, fout, path, unsafe_links=False):
self.fin = fin
self.fout = fout
self.root = path + "/"
self.data_ilistdir = ["", []]
self.data_files = []
self.unsafe_links = unsafe_links

def rd_s8(self):
return struct.unpack("<b", self.fin.read(1))[0]
Expand Down Expand Up @@ -397,8 +398,12 @@ def log_cmd(self, msg):
print(f"[{msg}]", end="\r\n")

def path_check(self, path):
parent = os.path.realpath(self.root)
child = os.path.realpath(path)
if not self.unsafe_links:
parent = os.path.realpath(self.root)
child = os.path.realpath(path)
else:
parent = os.path.abspath(self.root)
child = os.path.abspath(path)
if parent != os.path.commonpath([parent, child]):
raise OSError(EPERM, "") # File is outside mounted dir

Expand Down Expand Up @@ -612,13 +617,13 @@ def __init__(self, dev, *args, **kwargs):
self.device_name = dev
self.mounted = False

def mount_local(self, path):
def mount_local(self, path, unsafe_links=False):
fout = self.serial
if self.eval('"RemoteFS" in globals()') == b"False":
self.exec_(fs_hook_code)
self.exec_("__mount()")
self.mounted = True
self.cmd = PyboardCommand(self.serial, fout, path)
self.cmd = PyboardCommand(self.serial, fout, path, unsafe_links=unsafe_links)
self.serial = SerialIntercept(self.serial, self.cmd)

def write_ctrl_d(self, out_callback):
Expand Down

0 comments on commit d3d8627

Please sign in to comment.