From: Zhou Zheng Sheng <zhshzhou(a)linux.vnet.ibm.com>
In commit 456018a, we assign a TTY to kimchid in the SystemD service
file. This is because sudo requires a TTY by default for security
reason.
Usually Linux daemon detaches from controlling terminal so that it makes
use of the SIGHUP signal to reload its settings. If the daemon has a
console interface for the admin to interact (like a QEMU monitor), it
can have a TTY to run the console interface. At lease for now, Kimchi is
a traditional plain daemon that needs no TTY.
This patch creates a temporary PTY and assigns the slave to the sudo
process as the controlling terminal each time it runs sudo. In this way
we do not have to assign a TTY to kimchid. Maybe it's more portable than
specifying TTY in a SystemD service file.
Before running sudo, it creates a subprocess to make sure the child is
not the session leader. Then the child creates a new session and PYT,
and sets the slave as the controlling termial. At last the child calls
sudo. The result from the child is delivered to the parent via a shared
memory object.
Signed-off-by: Zhou Zheng Sheng <zhshzhou(a)linux.vnet.ibm.com>
---
contrib/kimchid.service.fedora | 3 ---
src/kimchi/auth.py | 24 ++++++++++++++++++++----
2 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/contrib/kimchid.service.fedora b/contrib/kimchid.service.fedora
index 5f274fb..7abe49b 100644
--- a/contrib/kimchid.service.fedora
+++ b/contrib/kimchid.service.fedora
@@ -8,9 +8,6 @@ Type=simple
ExecStart=/usr/bin/kimchid
ExecStop=/bin/kill -TERM $MAINPID
EnvironmentFile=/etc/kimchi/kimchi.conf
-StandardInput=tty
-StandardOutput=tty
-TTYPath=/dev/tty11
[Install]
WantedBy=multi-user.target
diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py
index b042d73..9f6f2f4 100644
--- a/src/kimchi/auth.py
+++ b/src/kimchi/auth.py
@@ -19,9 +19,14 @@
import base64
import cherrypy
+import fcntl
import grp
+import multiprocessing
+import os
import PAM
+import pty
import re
+import termios
import time
@@ -55,6 +60,18 @@ class User(object):
return self.user[USER_GROUPS]
def has_sudo(self):
+ result = multiprocessing.Value('i', 0, lock=False)
+ p = multiprocessing.Process(target=self._has_sudo, args=(result,))
+ p.start()
+ p.join()
+ self.user[USER_SUDO] = bool(result.value)
+ return self.user[USER_SUDO]
+
+ def _has_sudo(self, result):
+ _master, slave = pty.openpty()
+ os.setsid()
+ fcntl.ioctl(slave, termios.TIOCSCTTY, 0)
+
out, err, exit = run_command(['sudo', '-l', '-U',
self.user[USER_ID],
'sudo'])
if exit == 0:
@@ -67,15 +84,14 @@ class User(object):
self.user[USER_ID]])
for line in out.split('\n'):
if line and re.search("(ALL)", line):
- self.user[USER_SUDO] = True
+ result.value = 1
debug("User %s can run any command with sudo" %
- self.user[USER_ID])
- return self.user[USER_SUDO]
+ result.value)
+ return
debug("User %s can only run some commands with sudo" %
self.user[USER_ID])
else:
debug("User %s is not allowed to run sudo" % self.user[USER_ID])
- return self.user[USER_SUDO]
def get_user(self):
return self.user
--
1.8.5.3