From 34ad0bf48aa860e42c60660533bf2f003918c178 Mon Sep 17 00:00:00 2001 From: VG Date: Mon, 6 Nov 2017 09:38:44 +0100 Subject: Change secure_open for a more secure function By using a directory and directory file descriptors, we avoid hardlink and symlinks attacks. --- clip | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/clip b/clip index 09e28f4..df72a9a 100755 --- a/clip +++ b/clip @@ -19,6 +19,7 @@ Use content: import contextlib +import functools import getpass import os import sys @@ -28,32 +29,30 @@ class Error(Exception): pass class SecurityError(Error): pass -def fileno(filelike): - if getattr(filelike, 'fileno'): - return filelike.fileno() - elif getattr(filelike, 'buffer'): - return filelike.buffer.fileno() - raise Error("fileno not found inside file-like object") +@contextlib.contextmanager +def secure_opendir(dirname): + os.makedirs(dirname, mode=0o700, exist_ok=True) + dfd = os.open(dirname, os.O_RDONLY | os.O_DIRECTORY) + try: + dir_stat = os.stat(dfd) + if (dir_stat.st_uid != os.getuid() or dir_stat.st_mode & 0o777 != 0o700): + raise SecurityError('Wrong directory permissions/owner') + yield dfd + finally: + os.close(dfd) @contextlib.contextmanager -def secure_open(path, mode='r', *l, **kw): - if os.path.islink(path): - raise SecurityError("The clipboard file can not be a symlink") - real_mode = mode.replace('w', 'a') - with open(path, real_mode, *l, **kw) as fo: - if os.fstat(fileno(fo)) != os.stat(path): - raise SecurityError("Intrusion might have been done on %s" % path) - if 'w' in mode: - os.lseek(fileno(fo), 0, os.SEEK_SET) - os.ftruncate(fileno(fo), 0) - os.fchmod(fileno(fo), 0o600) - yield fo +def secure_open(*l, **kw): + with secure_opendir(os.path.dirname(l[0])) as dfd: + _opener = functools.partial(os.open, dir_fd=dfd) + with open(*l, **kw, opener=_opener) as ffd: + yield ffd if __name__ == '__main__': os.umask(0o077) - clipboard_filepath = '/dev/shm/%s-clipboard' % getpass.getuser() + clipboard_filepath = '/dev/shm/%s-clipboard/content' % getpass.getuser() if(sys.stdin.isatty()): # Should write clipboard contents out to stdout try: -- cgit v1.2.3