[PATCH V7] check and set search permission for a directory

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> V6 -> V7 fix "+import stat +import subprocess " V5 -> V6 address aline's comments V4 -> V5 check the group permission. V3 -> V4 rebase add src/kimchi/utils.py to PEP8 list ShaoHe Feng (1): add a method to fix search permissions Makefile.am | 1 + src/kimchi/utils.py | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 1 deletion(-) -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> First will use setfacl to set the path search permission for user or group, if failed then set search permission for others on the path. Usage: check_path_permission("/tmp/need/to/fix/path", username) fix_path_permission("/tmp/need/to/fix/path", username) Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- Makefile.am | 1 + src/kimchi/utils.py | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 04ad696..1f80e5f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,7 @@ PEP8_WHITELIST = \ src/kimchi/cachebust.py \ src/kimchi/config.py.in \ src/kimchi/control/*.py \ + src/kimchi/utils.py \ src/kimchi/disks.py \ src/kimchi/distroloader.py \ src/kimchi/exception.py \ diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py index af245c6..62a412d 100644 --- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -18,11 +18,16 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # import cherrypy +import grp import os +import pwd +import re +import stat +import subprocess import urllib2 @@ -34,6 +39,7 @@ from kimchi import config kimchi_log = cherrypy.log.error_log + def is_digit(value): if isinstance(value, int): return True @@ -96,3 +102,117 @@ def check_url_path(path): return False return True + + +def run_command(cmd): + try: + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, error = proc.communicate() + + kimchi_log.debug("Run command '%s'", " ".join(cmd)) + if out or error: + kimchi_log.debug("out:\n %s\nerror:\n %s", out, error) + except OSError as e: + kimchi_log.error("Failed to run command %s. %s", " ".join(cmd), + e.message) + return (None, None, None) + if proc.returncode != 0: + kimchi_log.debug("Cmd '%s' failed, error code: %s", cmd, error) + return (out, error, proc.returncode) + + +def name_uid(user): + return pwd.getpwnam(user).pw_uid + + +def get_group_ids(user): + gids = [g.gr_gid for g in grp.getgrall() if user in g.gr_mem] + gid = pwd.getpwnam(user).pw_gid + gids.append(grp.getgrgid(gid).gr_gid) + return gids + + +def get_groups(user): + gids = get_group_ids(user) + return [grp.getgrgid(gid).gr_name for gid in gids] + + +def run_setfacl_set_x(path, user="qemu"): + set_user = ["setfacl", "--modify", "user:%s:x" % user, path] + out, error, ret = run_command(set_user) + return ret == 0 + + +def run_getfacl(path, user="qemu", groups=[]): + cmd = ["getfacl", path] + out, error, ret = run_command(cmd) + if out and ret == 0: + res = re.findall("user:%s:..x" % user, out) + if res: + return True + res = re.findall("(?<=group:)(.*)(?=:..x)", + out) if groups else [] + return list(set(res) & set(groups)) != [] + return False + + +def set_x_on_path(path, who="other"): + S_IXWHO = ((stat.S_IXUSR if who == "user" else None) or + (stat.S_IXGRP if who == "group" else None) or + stat.S_IXOTH) + mode = os.stat(path).st_mode | S_IXWHO + os.chmod(path, mode) + ret = os.stat(path).st_mode == mode + if not ret: + kimchi_log.debug("faild to set +x attribute on %s", path) + return ret + + +def get_x_on_path(path, uid, gids=[]): + try: + st = os.stat(path) + mode = st.st_mode + return ((uid == st.st_uid and mode & stat.S_IXUSR == stat.S_IXUSR) or + (st.st_gid in gids and mode & stat.S_IXGRP == stat.S_IXGRP) or + mode & stat.S_IXOTH == stat.S_IXOTH) + except OSError as e: + kimchi_log.error("faild to get attribute on %s as user id: %s. %s", + path, uid, e.message) + return False + + +def check_path_permission(path, user='qemu', groups=[]): + groups = groups if groups else get_groups(user) + gids = [grp.getgrnam(group).gr_gid for group in groups] if groups else [] + path = os.path.abspath(path) + paths = path.split("/") + paths = paths[1:] + path = "/" + for p in paths: + path = os.path.join(path, p) + if not (get_x_on_path(path, name_uid(user), gids) or + run_getfacl(path, user, groups)): + return False + return True + + +def fix_path_permission(path, user='qemu', groups=[]): + groups = groups if groups else get_groups(user) + gids = [grp.getgrnam(group).gr_gid for group in groups] if groups else [] + path = os.path.abspath(path) + paths = path.split("/") + paths = paths[1:] + path = "/" + for p in paths: + path = os.path.join(path, p) + st = os.stat(path) + uid = name_uid(user) + who = 'user' if st.st_uid == uid else None + who = 'group' if not who and st.st_gid in gids else 'other' + if not (get_x_on_path(path, uid, gids) or + run_getfacl(path, user, groups) or + run_setfacl_set_x(path, user) or + set_x_on_path(path, who)): + return False + return True -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
First will use setfacl to set the path search permission for user or group, if failed then set search permission for others on the path.
Usage: check_path_permission("/tmp/need/to/fix/path", username) fix_path_permission("/tmp/need/to/fix/path", username)
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- Makefile.am | 1 + src/kimchi/utils.py | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 1 deletion(-)
diff --git a/Makefile.am b/Makefile.am index 04ad696..1f80e5f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,7 @@ PEP8_WHITELIST = \ src/kimchi/cachebust.py \ src/kimchi/config.py.in \ src/kimchi/control/*.py \ + src/kimchi/utils.py \ src/kimchi/disks.py \ src/kimchi/distroloader.py \ src/kimchi/exception.py \ diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py index af245c6..62a412d 100644 --- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -18,11 +18,16 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #
import cherrypy +import grp import os +import pwd +import re +import stat +import subprocess import urllib2
@@ -34,6 +39,7 @@ from kimchi import config
kimchi_log = cherrypy.log.error_log
+ def is_digit(value): if isinstance(value, int): return True @@ -96,3 +102,117 @@ def check_url_path(path): return False
return True + + +def run_command(cmd): + try: + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, error = proc.communicate() As this command will be used at multiple cases, we may not want to wait
On 2014年01月09日 08:01, shaohef@linux.vnet.ibm.com wrote: the execute result, do you think it's reasonable to add a timeout here?
+ + kimchi_log.debug("Run command '%s'", " ".join(cmd)) + if out or error: + kimchi_log.debug("out:\n %s\nerror:\n %s", out, error) + except OSError as e: + kimchi_log.error("Failed to run command %s. %s", " ".join(cmd), + e.message) + return (None, None, None) I think according to http://docs.python.org/3.3/library/subprocess.html#exceptions: Multiple exceptions will be raised and just except OSError may leak others. What about except all?
+ if proc.returncode != 0: + kimchi_log.debug("Cmd '%s' failed, error code: %s", cmd, error) + return (out, error, proc.returncode) + + +def name_uid(user): + return pwd.getpwnam(user).pw_uid + + +def get_group_ids(user): + gids = [g.gr_gid for g in grp.getgrall() if user in g.gr_mem] + gid = pwd.getpwnam(user).pw_gid + gids.append(grp.getgrgid(gid).gr_gid) + return gids + + +def get_groups(user): + gids = get_group_ids(user) + return [grp.getgrgid(gid).gr_name for gid in gids] + + +def run_setfacl_set_x(path, user="qemu"): + set_user = ["setfacl", "--modify", "user:%s:x" % user, path] + out, error, ret = run_command(set_user) + return ret == 0 + + +def run_getfacl(path, user="qemu", groups=[]): + cmd = ["getfacl", path] + out, error, ret = run_command(cmd) + if out and ret == 0: + res = re.findall("user:%s:..x" % user, out) + if res: + return True + res = re.findall("(?<=group:)(.*)(?=:..x)", + out) if groups else [] + return list(set(res) & set(groups)) != [] + return False + + +def set_x_on_path(path, who="other"): + S_IXWHO = ((stat.S_IXUSR if who == "user" else None) or + (stat.S_IXGRP if who == "group" else None) or + stat.S_IXOTH) + mode = os.stat(path).st_mode | S_IXWHO + os.chmod(path, mode) + ret = os.stat(path).st_mode == mode + if not ret: + kimchi_log.debug("faild to set +x attribute on %s", path) + return ret + + +def get_x_on_path(path, uid, gids=[]): + try: + st = os.stat(path) + mode = st.st_mode + return ((uid == st.st_uid and mode & stat.S_IXUSR == stat.S_IXUSR) or + (st.st_gid in gids and mode & stat.S_IXGRP == stat.S_IXGRP) or + mode & stat.S_IXOTH == stat.S_IXOTH) + except OSError as e: + kimchi_log.error("faild to get attribute on %s as user id: %s. %s", + path, uid, e.message) + return False + + +def check_path_permission(path, user='qemu', groups=[]): + groups = groups if groups else get_groups(user) + gids = [grp.getgrnam(group).gr_gid for group in groups] if groups else [] + path = os.path.abspath(path) + paths = path.split("/") + paths = paths[1:] + path = "/" + for p in paths: + path = os.path.join(path, p) + if not (get_x_on_path(path, name_uid(user), gids) or + run_getfacl(path, user, groups)): + return False + return True + + +def fix_path_permission(path, user='qemu', groups=[]): + groups = groups if groups else get_groups(user) + gids = [grp.getgrnam(group).gr_gid for group in groups] if groups else [] + path = os.path.abspath(path) + paths = path.split("/") + paths = paths[1:] + path = "/" + for p in paths: + path = os.path.join(path, p) + st = os.stat(path) + uid = name_uid(user) + who = 'user' if st.st_uid == uid else None + who = 'group' if not who and st.st_gid in gids else 'other' + if not (get_x_on_path(path, uid, gids) or + run_getfacl(path, user, groups) or + run_setfacl_set_x(path, user) or + set_x_on_path(path, who)): + return False + return True

On 01/09/2014 03:25 PM, Royce Lv wrote:
On 2014年01月09日 08:01, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
First will use setfacl to set the path search permission for user or group, if failed then set search permission for others on the path.
Usage: check_path_permission("/tmp/need/to/fix/path", username) fix_path_permission("/tmp/need/to/fix/path", username)
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- Makefile.am | 1 + src/kimchi/utils.py | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 1 deletion(-)
diff --git a/Makefile.am b/Makefile.am index 04ad696..1f80e5f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,7 @@ PEP8_WHITELIST = \ src/kimchi/cachebust.py \ src/kimchi/config.py.in \ src/kimchi/control/*.py \ + src/kimchi/utils.py \ src/kimchi/disks.py \ src/kimchi/distroloader.py \ src/kimchi/exception.py \ diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py index af245c6..62a412d 100644 --- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -18,11 +18,16 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #
import cherrypy +import grp import os +import pwd +import re +import stat +import subprocess import urllib2
@@ -34,6 +39,7 @@ from kimchi import config
kimchi_log = cherrypy.log.error_log
+ def is_digit(value): if isinstance(value, int): return True @@ -96,3 +102,117 @@ def check_url_path(path): return False
return True + + +def run_command(cmd): + try: + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, error = proc.communicate() As this command will be used at multiple cases, we may not want to wait the execute result, do you think it's reasonable to add a timeout here? ACK. we need a synchronous run_command with timeout. + + kimchi_log.debug("Run command '%s'", " ".join(cmd)) + if out or error: + kimchi_log.debug("out:\n %s\nerror:\n %s", out, error) + except OSError as e: + kimchi_log.error("Failed to run command %s. %s", " ".join(cmd), + e.message) + return (None, None, None) I think according to http://docs.python.org/3.3/library/subprocess.html#exceptions: Multiple exceptions will be raised and just except OSError may leak others. What about except all? ACK
+ if proc.returncode != 0: + kimchi_log.debug("Cmd '%s' failed, error code: %s", cmd, error) + return (out, error, proc.returncode) + + +def name_uid(user): + return pwd.getpwnam(user).pw_uid + + +def get_group_ids(user): + gids = [g.gr_gid for g in grp.getgrall() if user in g.gr_mem] + gid = pwd.getpwnam(user).pw_gid + gids.append(grp.getgrgid(gid).gr_gid) + return gids + + +def get_groups(user): + gids = get_group_ids(user) + return [grp.getgrgid(gid).gr_name for gid in gids] + + +def run_setfacl_set_x(path, user="qemu"): + set_user = ["setfacl", "--modify", "user:%s:x" % user, path] + out, error, ret = run_command(set_user) + return ret == 0 + + +def run_getfacl(path, user="qemu", groups=[]): + cmd = ["getfacl", path] + out, error, ret = run_command(cmd) + if out and ret == 0: + res = re.findall("user:%s:..x" % user, out) + if res: + return True + res = re.findall("(?<=group:)(.*)(?=:..x)", + out) if groups else [] + return list(set(res) & set(groups)) != [] + return False + + +def set_x_on_path(path, who="other"): + S_IXWHO = ((stat.S_IXUSR if who == "user" else None) or + (stat.S_IXGRP if who == "group" else None) or + stat.S_IXOTH) + mode = os.stat(path).st_mode | S_IXWHO + os.chmod(path, mode) + ret = os.stat(path).st_mode == mode + if not ret: + kimchi_log.debug("faild to set +x attribute on %s", path) + return ret + + +def get_x_on_path(path, uid, gids=[]): + try: + st = os.stat(path) + mode = st.st_mode + return ((uid == st.st_uid and mode & stat.S_IXUSR == stat.S_IXUSR) or + (st.st_gid in gids and mode & stat.S_IXGRP == stat.S_IXGRP) or + mode & stat.S_IXOTH == stat.S_IXOTH) + except OSError as e: + kimchi_log.error("faild to get attribute on %s as user id: %s. %s", + path, uid, e.message) + return False + + +def check_path_permission(path, user='qemu', groups=[]): + groups = groups if groups else get_groups(user) + gids = [grp.getgrnam(group).gr_gid for group in groups] if groups else [] + path = os.path.abspath(path) + paths = path.split("/") + paths = paths[1:] + path = "/" + for p in paths: + path = os.path.join(path, p) + if not (get_x_on_path(path, name_uid(user), gids) or + run_getfacl(path, user, groups)): + return False + return True + + +def fix_path_permission(path, user='qemu', groups=[]): + groups = groups if groups else get_groups(user) + gids = [grp.getgrnam(group).gr_gid for group in groups] if groups else [] + path = os.path.abspath(path) + paths = path.split("/") + paths = paths[1:] + path = "/" + for p in paths: + path = os.path.join(path, p) + st = os.stat(path) + uid = name_uid(user) + who = 'user' if st.st_uid == uid else None + who = 'group' if not who and st.st_gid in gids else 'other' + if not (get_x_on_path(path, uid, gids) or + run_getfacl(path, user, groups) or + run_setfacl_set_x(path, user) or + set_x_on_path(path, who)): + return False + return True
-- Thanks and best regards! Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center
participants (3)
-
Royce Lv
-
shaohef@linux.vnet.ibm.com
-
Sheldon