Repository: kovidgoyal/kitty

Issue:
Permission Error when using SSH kitten on FreeBSD
Good morning.  I was hoping you or someone could point me in the right direction, or is this possibly a bug?

When attempting to use the SSH kitten on FreeBSD I am getting this error:

```bash
$ kitty +kitten ssh test-host
[Errno 13] Permission denied: '/kssh-37040-ece2c2d9cdda503806bc6a18097a0bb79508d4e39266667b9aa16a06830dd0af'
Shared connection to [redacted] closed.
Exception ignored in atexit callback: <bound method SharedMemory.unlink of SharedMemory('/kssh-37040-ece2c2d9cdda503806bc6a18097a0bb79508d4e39266667b9aa16a06830dd0af', size=32718)>
Traceback (most recent call last):
  File "/usr/home/user/kitty/launcher/../../kitty/shm.py", line 180, in unlink
    shm_unlink(self._name)
PermissionError: [Errno 13] Permission denied: '/kssh-37040-ece2c2d9cdda503806bc6a18097a0bb79508d4e39266667b9aa16a06830dd0af'
```

I am not sure as to what resource the kitty process is lacking permission in order to rectify this.

This is on kitty v0.26.5.  Happens in every shell.


Discussion:
That error indicates shm_unlink() is failing on your system. I am not
familiar with FreeBSD so I cannot tell you why that might be. Relevant
code is in shm.py

Digging in to this, (and learning quite a bit about FreeBSD's shared memory APIs) I have discovered the issue.

According to the [man page](https://www.freebsd.org/cgi/man.cgi?query=shm_unlink&apropos=0&sektion=0&manpath=FreeBSD+13.1-RELEASE+and+Ports&arch=default&format=html) for `shm_unlink()`, `EACCES` is returned if the object does not have the write bit set.  Linux does not have this restriction.

```
$ posixshmcontrol ls
MODE    	OWNER	GROUP	SIZE	PATH
r--------	user	user	32722	/kssh-44964-82dce778a99a4e699b31559a7c176f8927326af241db04463f6ba763cbed879b
```

In `utils.py` the object is being created as read only
```python
def create_shared_memory(data: Any, prefix: str) -> str:
    import atexit
    import json
    import stat

    from kitty.shm import SharedMemory
    db = json.dumps(data).encode('utf-8')
    with SharedMemory(size=len(db) + SharedMemory.num_bytes_for_size, mode=stat.S_IREAD, prefix=prefix) as shm:
        shm.write_data_with_size(db)
        shm.flush()
        atexit.register(shm.unlink)
    return shm.name
```

The read only mode is later enforced in `main.py` when reading the object
```python
def read_data_from_shared_memory(shm_name: str) -> Any:
    with SharedMemory(shm_name, readonly=True) as shm:
        shm.unlink()
        if shm.stats.st_uid != os.geteuid() or shm.stats.st_gid != os.getegid():
            raise ValueError('Incorrect owner on pwfile')
        mode = stat.S_IMODE(shm.stats.st_mode)
        if mode != stat.S_IREAD:
            raise ValueError('Incorrect permissions on pwfile')
        return json.loads(shm.read_data_with_size())
```

Therefore, the cleanup code in `shm.py`
```python
    def unlink(self) -> None:
        """Requests that the underlying shared memory block be destroyed.

        In order to ensure proper cleanup of resources, unlink should be
        called once (and only once) across all processes which have access
        to the shared memory block."""
        if self._name:
            try:
                shm_unlink(self._name)
            except FileNotFoundError:
                pass
            self._name = ''
```
cannot work on FreeBSD as implemented.   I had to use posixshmcontrol with elevated permissions to clear out the list of orphaned shared objects.

What would be the security ramifications of setting the owner `w` bit on these objects instead of just `r` ?

Resolving patch:
diff --git a/kittens/ssh/main.py b/kittens/ssh/main.py
--- a/kittens/ssh/main.py
+++ b/kittens/ssh/main.py
@@ -50,9 +50,6 @@ def read_data_from_shared_memory(shm_name: str) -> Any:
         shm.unlink()
         if shm.stats.st_uid != os.geteuid() or shm.stats.st_gid != os.getegid():
             raise ValueError('Incorrect owner on pwfile')
-        mode = stat.S_IMODE(shm.stats.st_mode)
-        if mode != stat.S_IREAD:
-            raise ValueError('Incorrect permissions on pwfile')
         return json.loads(shm.read_data_with_size())
 
 


Test patch: