summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastien Martini <seb@dbzteam.org>2010-09-15 00:19:49 +0200
committerSebastien Martini <seb@dbzteam.org>2010-09-15 00:19:49 +0200
commit155e2f5d0dfeff210198856b2b03d53d8e810c52 (patch)
tree48512ea648bbe717f4416bdc8245e420cac64a0e
parent80e1afc95893350e6e59230993e26376e279327c (diff)
downloadpyinotify-155e2f5d0dfeff210198856b2b03d53d8e810c52.tar.gz
Modified the umask's value set in forked processes, tightened all files
creations rights and removed the daemonize option used for killing processes (security issue reported by Adrian thiefmaster@gamesurge.net).
-rw-r--r--python2/examples/daemon.py9
-rwxr-xr-xpython2/pyinotify.py61
-rwxr-xr-xpython3/pyinotify.py62
3 files changed, 47 insertions, 85 deletions
diff --git a/python2/examples/daemon.py b/python2/examples/daemon.py
index 26eaffe..aeb5249 100644
--- a/python2/examples/daemon.py
+++ b/python2/examples/daemon.py
@@ -37,10 +37,7 @@ on_loop_func = functools.partial(on_loop, counter=Counter())
# child process' PID is written to /tmp/pyinotify.pid (it also automatically
# deletes it when it exits normally). If no custom pid_file is provided it
# would write it more traditionally under /var/run/. /tmp/stdout.txt is used
-# as stdout stream thus traces of events will be written in it, force_kill
-# means that if there is already an /tmp/pyinotify.pid with a corresponding
-# running process it will be killed first. callback is the above function
-# and will be called after each event loop.
+# as stdout stream thus traces of events will be written in it. callback is
+# the above function and will be called after each event loop.
notifier.loop(daemonize=True, callback=on_loop_func,
- pid_file='/tmp/pyinotify.pid', force_kill=True,
- stdout='/tmp/stdout.txt')
+ pid_file='/tmp/pyinotify.pid', stdout='/tmp/stdout.txt')
diff --git a/python2/pyinotify.py b/python2/pyinotify.py
index d7fb8af..9c0aafc 100755
--- a/python2/pyinotify.py
+++ b/python2/pyinotify.py
@@ -970,16 +970,16 @@ class Stats(ProcessEvent):
def dump(self, filename):
"""
- Dumps statistics to file |filename|.
+ Dumps statistics.
- @param filename: pathname.
+ @param filename: filename where stats will be dumped, filename is
+ created and must not exist prior to this call.
@type filename: string
"""
- file_obj = file(filename, 'wb')
- try:
- file_obj.write(str(self))
- finally:
- file_obj.close()
+ flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL
+ fd = os.open(filename, flags, 0600)
+ os.write(fd, str(self))
+ os.close(fd)
def __str__(self, scale=45):
stats = self._stats_copy()
@@ -1199,11 +1199,10 @@ class Notifier:
if self._coalesce:
self._eventset.clear()
- def __daemonize(self, pid_file=None, force_kill=False, stdin=os.devnull,
- stdout=os.devnull, stderr=os.devnull):
+ def __daemonize(self, pid_file=None, stdin=os.devnull, stdout=os.devnull,
+ stderr=os.devnull):
"""
pid_file: file to which the pid will be written.
- force_kill: if True kill the process associated to pid_file.
stdin, stdout, stderr: files associated to common streams.
"""
if pid_file is None:
@@ -1211,30 +1210,9 @@ class Notifier:
basename = os.path.basename(sys.argv[0]) or 'pyinotify'
pid_file = os.path.join(dirname, basename + '.pid')
- if os.path.exists(pid_file):
- fo = file(pid_file, 'rb')
- try:
- try:
- pid = int(fo.read())
- except ValueError:
- pid = None
- if pid is not None:
- try:
- os.kill(pid, 0)
- except OSError, err:
- if err.errno == errno.ESRCH:
- log.debug(err)
- else:
- log.error(err)
- else:
- if not force_kill:
- s = 'There is already a pid file %s with pid %d'
- raise NotifierError(s % (pid_file, pid))
- else:
- os.kill(pid, 9)
- finally:
- fo.close()
-
+ if os.path.lexists(pid_file):
+ err = 'Cannot daemonize: pid file %s already exists.' % pid_file
+ raise NotifierError(err)
def fork_daemon():
# Adapted from Chad J. Schroeder's recipe
@@ -1247,7 +1225,7 @@ class Notifier:
if (pid == 0):
# child
os.chdir('/')
- os.umask(0)
+ os.umask(022)
else:
# parent 2
os._exit(0)
@@ -1257,20 +1235,19 @@ class Notifier:
fd_inp = os.open(stdin, os.O_RDONLY)
os.dup2(fd_inp, 0)
- fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT)
+ fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT, 0600)
os.dup2(fd_out, 1)
- fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT)
+ fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT, 0600)
os.dup2(fd_err, 2)
# Detach task
fork_daemon()
# Write pid
- file_obj = file(pid_file, 'wb')
- try:
- file_obj.write(str(os.getpid()) + '\n')
- finally:
- file_obj.close()
+ flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL
+ fd_pid = os.open(pid_file, flags, 0600)
+ os.write(fd_pid, str(os.getpid()) + '\n')
+ os.close(fd_pid)
atexit.register(lambda : os.unlink(pid_file))
diff --git a/python3/pyinotify.py b/python3/pyinotify.py
index 9a7afac..84fa17a 100755
--- a/python3/pyinotify.py
+++ b/python3/pyinotify.py
@@ -83,6 +83,7 @@ import ctypes
import ctypes.util
import asyncore
import glob
+import locale
try:
from functools import reduce
@@ -928,13 +929,16 @@ class Stats(ProcessEvent):
def dump(self, filename):
"""
- Dumps statistics to file |filename|.
+ Dumps statistics.
- @param filename: pathname.
+ @param filename: filename where stats will be dumped, filename is
+ created and must not exist prior to this call.
@type filename: string
"""
- with open(filename, 'w') as file_obj:
- file_obj.write(str(self))
+ flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL
+ fd = os.open(filename, flags, 0o0600)
+ os.write(fd, bytes(self.__str__(), locale.getpreferredencoding()))
+ os.close(fd)
def __str__(self, scale=45):
stats = self._stats_copy()
@@ -1156,11 +1160,10 @@ class Notifier:
if self._coalesce:
self._eventset.clear()
- def __daemonize(self, pid_file=None, force_kill=False, stdin=os.devnull,
- stdout=os.devnull, stderr=os.devnull):
+ def __daemonize(self, pid_file=None, stdin=os.devnull, stdout=os.devnull,
+ stderr=os.devnull):
"""
pid_file: file to which the pid will be written.
- force_kill: if True kill the process associated to pid_file.
stdin, stdout, stderr: files associated to common streams.
"""
if pid_file is None:
@@ -1168,27 +1171,9 @@ class Notifier:
basename = os.path.basename(sys.argv[0]) or 'pyinotify'
pid_file = os.path.join(dirname, basename + '.pid')
- if os.path.exists(pid_file):
- with open(pid_file, 'r') as fo:
- try:
- pid = int(fo.read())
- except ValueError:
- pid = None
- if pid is not None:
- try:
- os.kill(pid, 0)
- except OSError as err:
- if err.errno == errno.ESRCH:
- log.debug(err)
- else:
- log.error(err)
- else:
- if not force_kill:
- s = 'There is already a pid file %s with pid %d'
- raise NotifierError(s % (pid_file, pid))
- else:
- os.kill(pid, 9)
-
+ if os.path.lexists(pid_file):
+ err = 'Cannot daemonize: pid file %s already exists.' % pid_file
+ raise NotifierError(err)
def fork_daemon():
# Adapted from Chad J. Schroeder's recipe
@@ -1201,7 +1186,7 @@ class Notifier:
if (pid == 0):
# child
os.chdir('/')
- os.umask(0)
+ os.umask(0o022)
else:
# parent 2
os._exit(0)
@@ -1209,19 +1194,22 @@ class Notifier:
# parent 1
os._exit(0)
- fd_inp = open(stdin, 'r')
- os.dup2(fd_inp.fileno(), 0)
- fd_out = open(stdout, 'w')
- os.dup2(fd_out.fileno(), 1)
- fd_err = open(stderr, 'w')
- os.dup2(fd_err.fileno(), 2)
+ fd_inp = os.open(stdin, os.O_RDONLY)
+ os.dup2(fd_inp, 0)
+ fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT, 0o0600)
+ os.dup2(fd_out, 1)
+ fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT, 0o0600)
+ os.dup2(fd_err, 2)
# Detach task
fork_daemon()
# Write pid
- with open(pid_file, 'w') as file_obj:
- file_obj.write(str(os.getpid()) + '\n')
+ flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL
+ fd_pid = os.open(pid_file, flags, 0o0600)
+ os.write(fd_pid, bytes(str(os.getpid()) + '\n',
+ locale.getpreferredencoding()))
+ os.close(fd_pid)
atexit.register(lambda : os.unlink(pid_file))