diff --git a/policycoreutils-rhat.patch b/policycoreutils-rhat.patch index a0f6d60..7ac33d2 100644 --- a/policycoreutils-rhat.patch +++ b/policycoreutils-rhat.patch @@ -655722,10 +655722,10 @@ index 568ebfd..306d9b7 100644 def __init__(self, store): diff --git a/policycoreutils-2.4/semanage/seobject/__init__.py b/policycoreutils-2.4/semanage/seobject/__init__.py new file mode 100644 -index 0000000..1cf9681 +index 0000000..c23ebef --- /dev/null +++ b/policycoreutils-2.4/semanage/seobject/__init__.py -@@ -0,0 +1,2251 @@ +@@ -0,0 +1,2271 @@ +#! /usr/bin/python3 -Es +# Copyright (C) 2005-2013 Red Hat +# see file 'COPYING' for use and warranty information @@ -655748,9 +655748,17 @@ index 0000000..1cf9681 +# 02111-1307 USA +# +# -+ -+import pwd, grp, string, selinux, tempfile, os, re, sys, stat, shutil -+from semanage import *; ++import pwd ++import grp ++import string ++import selinux ++import tempfile ++import os ++import re ++import sys ++import stat ++import shutil ++from semanage import * +PROGNAME = "policycoreutils" +import sepolicy +from sepolicy import boolean_desc, boolean_category, gen_bool_dict @@ -655758,7 +655766,6 @@ index 0000000..1cf9681 +from IPy import IP + +import gettext -+PROGNAME="policycoreutils" +gettext.bindtextdomain(PROGNAME, "/usr/share/locale") +gettext.textdomain(PROGNAME) +try: @@ -655778,70 +655785,73 @@ index 0000000..1cf9681 +import syslog + +file_types = {} -+file_types[""] = SEMANAGE_FCONTEXT_ALL; -+file_types["all files"] = SEMANAGE_FCONTEXT_ALL; -+file_types["a"] = SEMANAGE_FCONTEXT_ALL; -+file_types["regular file"] = SEMANAGE_FCONTEXT_REG; -+file_types["--"] = SEMANAGE_FCONTEXT_REG; -+file_types["f"] = SEMANAGE_FCONTEXT_REG; -+file_types["-d"] = SEMANAGE_FCONTEXT_DIR; -+file_types["directory"] = SEMANAGE_FCONTEXT_DIR; -+file_types["d"] = SEMANAGE_FCONTEXT_DIR; -+file_types["-c"] = SEMANAGE_FCONTEXT_CHAR; -+file_types["character device"] = SEMANAGE_FCONTEXT_CHAR; -+file_types["c"] = SEMANAGE_FCONTEXT_CHAR; -+file_types["-b"] = SEMANAGE_FCONTEXT_BLOCK; -+file_types["block device"] = SEMANAGE_FCONTEXT_BLOCK; -+file_types["b"] = SEMANAGE_FCONTEXT_BLOCK; -+file_types["-s"] = SEMANAGE_FCONTEXT_SOCK; -+file_types["socket"] = SEMANAGE_FCONTEXT_SOCK; -+file_types["s"] = SEMANAGE_FCONTEXT_SOCK; -+file_types["-l"] = SEMANAGE_FCONTEXT_LINK; -+file_types["l"] = SEMANAGE_FCONTEXT_LINK; -+file_types["symbolic link"] = SEMANAGE_FCONTEXT_LINK; -+file_types["p"] = SEMANAGE_FCONTEXT_PIPE; -+file_types["-p"] = SEMANAGE_FCONTEXT_PIPE; -+file_types["named pipe"] = SEMANAGE_FCONTEXT_PIPE; ++file_types[""] = SEMANAGE_FCONTEXT_ALL ++file_types["all files"] = SEMANAGE_FCONTEXT_ALL ++file_types["a"] = SEMANAGE_FCONTEXT_ALL ++file_types["regular file"] = SEMANAGE_FCONTEXT_REG ++file_types["--"] = SEMANAGE_FCONTEXT_REG ++file_types["f"] = SEMANAGE_FCONTEXT_REG ++file_types["-d"] = SEMANAGE_FCONTEXT_DIR ++file_types["directory"] = SEMANAGE_FCONTEXT_DIR ++file_types["d"] = SEMANAGE_FCONTEXT_DIR ++file_types["-c"] = SEMANAGE_FCONTEXT_CHAR ++file_types["character device"] = SEMANAGE_FCONTEXT_CHAR ++file_types["c"] = SEMANAGE_FCONTEXT_CHAR ++file_types["-b"] = SEMANAGE_FCONTEXT_BLOCK ++file_types["block device"] = SEMANAGE_FCONTEXT_BLOCK ++file_types["b"] = SEMANAGE_FCONTEXT_BLOCK ++file_types["-s"] = SEMANAGE_FCONTEXT_SOCK ++file_types["socket"] = SEMANAGE_FCONTEXT_SOCK ++file_types["s"] = SEMANAGE_FCONTEXT_SOCK ++file_types["-l"] = SEMANAGE_FCONTEXT_LINK ++file_types["l"] = SEMANAGE_FCONTEXT_LINK ++file_types["symbolic link"] = SEMANAGE_FCONTEXT_LINK ++file_types["p"] = SEMANAGE_FCONTEXT_PIPE ++file_types["-p"] = SEMANAGE_FCONTEXT_PIPE ++file_types["named pipe"] = SEMANAGE_FCONTEXT_PIPE + -+file_type_str_to_option = { "all files": "a", -+ "regular file":"f", -+ "directory":"d", -+ "character device":"c", -+ "block device":"b", -+ "socket file":"s", -+ "symbolic link":"l", -+ "named pipe":"p" } ++file_type_str_to_option = {"all files": "a", ++ "regular file":"f", ++ "directory":"d", ++ "character device":"c", ++ "block device":"b", ++ "socket file":"s", ++ "symbolic link":"l", ++ "named pipe":"p"} +try: + import audit + class logger: + def __init__(self): + self.audit_fd = audit.audit_open() + self.log_list = [] -+ def log(self, msg, name = "", sename = "", serole = "", serange = "", oldsename = "", oldserole = "", oldserange = ""): ++ def log(self, msg, name="", sename="", serole="", serange="", oldsename="", oldserole="", oldserange=""): + + sep = "-" + if sename != oldsename: -+ msg += sep + "sename"; sep = "," ++ msg += sep + "sename" ++ sep = "," + if serole != oldserole: -+ msg += sep + "role"; sep = "," ++ msg += sep + "role" ++ sep = "," + if serange != oldserange: -+ msg += sep + "range"; sep = "," ++ msg += sep + "range" ++ sep = "," + + self.log_list.append([self.audit_fd, audit.AUDIT_ROLE_ASSIGN, sys.argv[0], str(msg), name, 0, sename, serole, serange, oldsename, oldserole, oldserange, "", "", ""]) + -+ def log_remove(self, msg, name = "", sename = "", serole = "", serange = "", oldsename = "", oldserole = "", oldserange = ""): ++ def log_remove(self, msg, name="", sename="", serole="", serange="", oldsename="", oldserole="", oldserange=""): + self.log_list.append([self.audit_fd, audit.AUDIT_ROLE_REMOVE, sys.argv[0], str(msg), name, 0, sename, serole, serange, oldsename, oldserole, oldserange, "", "", ""]) + -+ def commit(self,success): ++ def commit(self, success): + for l in self.log_list: + audit.audit_log_semanage_message(*(l + [success])) + self.log_list = [] +except: + class logger: + def __init__(self): -+ self.log_list=[] ++ self.log_list = [] + -+ def log(self, msg, name = "", sename = "", serole = "", serange = "", oldsename = "", oldserole = "", oldserange = ""): ++ def log(self, msg, name="", sename="", serole="", serange="", oldsename="", oldserole="", oldserange=""): + message = " %s name=%s" % (msg, name) + if sename != "": + message += " sename=" + sename @@ -655857,10 +655867,10 @@ index 0000000..1cf9681 + message += " old_MLSRange=" + oldserange + self.log_list.append(message) + -+ def log_remove(self, msg, name = "", sename = "", serole = "", serange = "", oldsename = "", oldserole = "", oldserange = ""): ++ def log_remove(self, msg, name="", sename="", serole="", serange="", oldsename="", oldserole="", oldserange=""): + self.log(msg, name, sename, serole, serange, oldsename, oldserole, oldserange) + -+ def commit(self,success): ++ def commit(self, success): + if success == 1: + message = "Successful: " + else: @@ -655869,13 +655879,13 @@ index 0000000..1cf9681 + syslog.syslog(syslog.LOG_INFO, message + l) + +class nulllogger: -+ def log(self, msg, name = "", sename = "", serole = "", serange = "", oldsename = "", oldserole = "", oldserange = ""): ++ def log(self, msg, name="", sename="", serole="", serange="", oldsename="", oldserole="", oldserange=""): + pass + -+ def log_remove(self, msg, name = "", sename = "", serole = "", serange = "", oldsename = "", oldserole = "", oldserange = ""): ++ def log_remove(self, msg, name="", sename="", serole="", serange="", oldsename="", oldserole="", oldserange=""): + pass + -+ def commit(self,success): ++ def commit(self, success): + pass + +def validate_level(raw): @@ -655886,7 +655896,7 @@ index 0000000..1cf9681 + reg = sensitivity + "(-" + sensitivity + ")?" + "(:" + categories + ")?" + return re.search("^" + reg +"$", raw) + -+def translate(raw, prepend = 1): ++def translate(raw, prepend=1): + filler = "a:b:c:" + if prepend == 1: + context = "%s%s" % (filler, raw) @@ -655902,7 +655912,7 @@ index 0000000..1cf9681 + else: + return trans + -+def untranslate(trans, prepend = 1): ++def untranslate(trans, prepend=1): + filler = "a:b:c:" + if prepend == 1: + context = "%s%s" % (filler, trans) @@ -655919,10 +655929,12 @@ index 0000000..1cf9681 + else: + return raw + ++ +class semanageRecords: + transaction = False + handle = None + store = None ++ + def __init__(self, store): + global handle + self.load = True @@ -655948,7 +655960,7 @@ index 0000000..1cf9681 + raise ValueError(_("Could not create semanage handle")) + + if not semanageRecords.transaction and store != "": -+ semanage_select_store(handle, store, SEMANAGE_CON_DIRECT); ++ semanage_select_store(handle, store, SEMANAGE_CON_DIRECT) + semanageRecords.store = store + + if not semanage_is_managed(handle): @@ -655988,6 +656000,7 @@ index 0000000..1cf9681 + rc = semanage_begin_transaction(self.sh) + if rc < 0: + raise ValueError(_("Could not start semanage transaction")) ++ + def customized(self): + raise ValueError(_("Not yet implemented")) + @@ -656008,7 +656021,9 @@ index 0000000..1cf9681 + semanageRecords.transaction = False + self.commit() + ++ +class moduleRecords(semanageRecords): ++ + def __init__(self, store): + semanageRecords.__init__(self, store) + @@ -656040,8 +656055,8 @@ index 0000000..1cf9681 + l.append((name, enabled, priority, lang_ext)) + + # sort the list so they are in name order, but with higher priorities coming first -+ l.sort(key = lambda t: t[3], reverse=True) -+ l.sort(key = lambda t: t[0]) ++ l.sort(key=lambda t: t[3], reverse=True) ++ l.sort(key=lambda t: t[0]) + return l + + def customized(self): @@ -656050,7 +656065,7 @@ index 0000000..1cf9681 + return + return ["-d %s" % x[0] for x in [t for t in ALL if t[1] == 0]] + -+ def list(self, heading = True, locallist = False): ++ def list(self, heading=True, locallist=False): + ALL = self.get_all() + if len(ALL) == 0: + return @@ -656076,7 +656091,7 @@ index 0000000..1cf9681 + if rc < 0: + raise ValueError(_("Invalid priority %d (needs to be between 1 and 999)") % priority) + -+ rc = semanage_module_install_file(self.sh, module); ++ rc = semanage_module_install_file(self.sh, module) + if rc >= 0: + self.commit() + @@ -656101,7 +656116,7 @@ index 0000000..1cf9681 + def modify(self, file): + if not module: + raise ValueError(_("You did not define module name.")) -+ rc = semanage_module_upgrade_file(self.sh, file); ++ rc = semanage_module_upgrade_file(self.sh, file) + if rc >= 0: + self.commit() + @@ -656124,18 +656139,22 @@ index 0000000..1cf9681 + for m in l: + self.set_enabled(m, True) + ++ +class dontauditClass(semanageRecords): ++ + def __init__(self, store): + semanageRecords.__init__(self, store) + + def toggle(self, dontaudit): -+ if dontaudit not in [ "on", "off" ]: ++ if dontaudit not in ["on", "off"]: + raise ValueError(_("dontaudit requires either 'on' or 'off'")) + self.begin() -+ semanage_set_disable_dontaudit(self.sh, dontaudit == "off") ++ rc = semanage_set_disable_dontaudit(self.sh, dontaudit == "off") + self.commit() + ++ +class permissiveRecords(semanageRecords): ++ + def __init__(self, store): + semanageRecords.__init__(self, store) + @@ -656152,7 +656171,7 @@ index 0000000..1cf9681 + l.append(name.split("permissive_")[1]) + return l + -+ def list(self, heading = True, locallist = False): ++ def list(self, heading=True, locallist=False): + ALL = [y["name"] for y in [x for x in sepolicy.info(sepolicy.TYPE) if x["permissive"]]] + if len(ALL) == 0: + return @@ -656186,7 +656205,7 @@ index 0000000..1cf9681 + name = "permissive_%s" % setype + modtxt = "(typepermissive %s)" % type + -+ rc = semanage_module_install(self.sh, modtxt, len(modtxt), name, "cil"); ++ rc = semanage_module_install(self.sh, modtxt, len(modtxt), name, "cil") + if rc >= 0: + self.commit() + @@ -656207,7 +656226,7 @@ index 0000000..1cf9681 + self.delete(" ".join(l)) + +class loginRecords(semanageRecords): -+ def __init__(self, store = ""): ++ def __init__(self, store=""): + semanageRecords.__init__(self, store) + self.oldsename = None + self.oldserange = None @@ -656274,7 +656293,7 @@ index 0000000..1cf9681 + + semanage_seuser_key_free(k) + semanage_seuser_free(u) -+ self.mylog.log("login", name, sename=sename, serange=serange, serole=",".join(serole), oldserole=",".join(oldserole), oldsename=self.oldsename, oldserange=self.oldserange); ++ self.mylog.log("login", name, sename=sename, serange=serange, serole=",".join(serole), oldserole=",".join(oldserole), oldsename=self.oldsename, oldserange=self.oldserange) + + def add(self, name, sename, serange): + try: @@ -656285,7 +656304,7 @@ index 0000000..1cf9681 + self.mylog.commit(0) + raise error + -+ def __modify(self, name, sename = "", serange=None): ++ def __modify(self, name, sename="", serange=None): + rec, self.oldsename, self.oldserange = selinux.getseuserbyname(name) + if sename == "" and not serange: + raise ValueError(_("Requires seuser or serange")) @@ -656296,12 +656315,12 @@ index 0000000..1cf9681 + if sename != "": + RANGE, (rc, serole) = userrec.get(sename) + else: -+ serole=oldserole ++ serole = oldserole + + if serange: -+ self.serange=serange ++ self.serange = serange + else: -+ self.serange=RANGE ++ self.serange = RANGE + + (rc, k) = semanage_seuser_key_create(self.sh, name) + if rc < 0: @@ -656324,6 +656343,7 @@ index 0000000..1cf9681 + + if sename != "": + semanage_seuser_set_sename(self.sh, u, sename) ++ self.sename = sename + else: + self.sename = self.oldsename + @@ -656333,9 +656353,9 @@ index 0000000..1cf9681 + + semanage_seuser_key_free(k) + semanage_seuser_free(u) -+ self.mylog.log("login", name,sename=self.sename,serange=self.serange, serole=",".join(serole), oldserole=",".join(oldserole), oldsename=self.oldsename, oldserange=self.oldserange); ++ self.mylog.log("login", name, sename=self.sename, serange=self.serange, serole=",".join(serole), oldserole=",".join(oldserole), oldsename=self.oldsename, oldserange=self.oldserange) + -+ def modify(self, name, sename = "", serange = None): ++ def modify(self, name, sename="", serange=None): + try: + self.begin() + self.__modify(name, sename, serange) @@ -656374,7 +656394,7 @@ index 0000000..1cf9681 + rec, self.sename, self.serange = selinux.getseuserbyname("__default__") + RANGE, (rc, serole) = userrec.get(self.sename) + -+ self.mylog.log_remove("login", name, sename=self.sename, serange=self.serange, serole=",".join(serole), oldserole=",".join(oldserole), oldsename=self.oldsename, oldserange=self.oldserange); ++ self.mylog.log_remove("login", name, sename=self.sename, serange=self.serange, serole=",".join(serole), oldserole=",".join(oldserole), oldsename=self.oldsename, oldserange=self.oldserange) + + def delete(self, name): + try: @@ -656403,7 +656423,7 @@ index 0000000..1cf9681 + def get_all_logins(self): + ddict = {} + self.logins_path = selinux.selinux_policy_root() + "/logins" -+ for path,dirs,files in os.walk(self.logins_path): ++ for path, dirs, files in os.walk(self.logins_path): + if path == self.logins_path: + for name in files: + try: @@ -656415,7 +656435,7 @@ index 0000000..1cf9681 + pass + return ddict + -+ def get_all(self, locallist = False): ++ def get_all(self, locallist=False): + ddict = {} + if locallist: + (rc, self.ulist) = semanage_seuser_list_local(self.sh) @@ -656438,7 +656458,7 @@ index 0000000..1cf9681 + l.append("-a -s %s -r '%s' %s" % (ddict[k][0], ddict[k][1], k)) + return l + -+ def list(self,heading = True, locallist = False): ++ def list(self,heading=True, locallist=False): + ddict = self.get_all(locallist) + ldict = self.get_all_logins() + lkeys = list(ldict.keys()) @@ -656467,7 +656487,7 @@ index 0000000..1cf9681 + print("%-25s %-25s" % (k, ddict[k][0])) + +class seluserRecords(semanageRecords): -+ def __init__(self, store = ""): ++ def __init__(self, store=""): + semanageRecords.__init__(self, store) + + def get(self, name): @@ -656481,7 +656501,7 @@ index 0000000..1cf9681 + if rc < 0: + raise ValueError(_("Could not query user for %s") % name) + serange = semanage_user_get_mlsrange(u) -+ serole = semanage_user_get_roles(self.sh,u) ++ serole = semanage_user_get_roles(self.sh, u) + semanage_user_key_free(k) + semanage_user_free(u) + return serange, serole @@ -656536,7 +656556,7 @@ index 0000000..1cf9681 + rc = semanage_user_set_prefix(self.sh, u, prefix) + if rc < 0: + raise ValueError(_("Could not add prefix %(PREFIX)s for %(ROLE)s") % {"ROLE":r, "PREFIX": prefix}) -+ (rc, key) = semanage_user_key_extract(self.sh,u) ++ (rc, key) = semanage_user_key_extract(self.sh, u) + if rc < 0: + raise ValueError(_("Could not extract key for %s") % name) + @@ -656551,16 +656571,16 @@ index 0000000..1cf9681 + def add(self, name, roles, selevel, serange, prefix): + try: + self.begin() -+ self.__add( name, roles, selevel, serange, prefix) ++ self.__add(name, roles, selevel, serange, prefix) + self.commit() + except ValueError as error: + self.mylog.commit(0) + raise error + -+ def __modify(self, name, roles = [], selevel = "", serange = None, prefix = ""): ++ def __modify(self, name, roles=[], selevel="", serange=None, prefix=""): + oldserole = "" + oldserange = "" -+ newroles = ' '.join(roles); ++ newroles = ' '.join(roles) + if prefix == "" and len(roles) == 0 and not serange and selevel == "": + if is_mls_enabled == 1: + raise ValueError(_("Requires prefix, roles, level or range")) @@ -656584,7 +656604,7 @@ index 0000000..1cf9681 + oldserange = semanage_user_get_mlsrange(u) + (rc, rlist) = semanage_user_get_roles(self.sh, u) + if rc >= 0: -+ oldserole = ' '.join(rlist); ++ oldserole = ' '.join(rlist) + + if serange: + semanage_user_set_mlsrange(self.sh, u, untranslate(serange)) @@ -656609,12 +656629,12 @@ index 0000000..1cf9681 + semanage_user_key_free(k) + semanage_user_free(u) + -+ role=",".join(newroles.split()) -+ oldserole=",".join(oldserole.split()) ++ role = ",".join(newroles.split()) ++ oldserole = ",".join(oldserole.split()) + self.mylog.log("seuser", sename=name, oldsename=name, serole=role, serange=serange, oldserole=oldserole, oldserange=oldserange) + + -+ def modify(self, name, roles = [], selevel = "", serange = None, prefix = ""): ++ def modify(self, name, roles=[], selevel="", serange=None, prefix=""): + try: + self.begin() + self.__modify(name, roles, selevel, serange, prefix) @@ -656683,7 +656703,7 @@ index 0000000..1cf9681 + self.mylog.commit(0) + raise error + -+ def get_all(self, locallist = False): ++ def get_all(self, locallist=False): + ddict = {} + if locallist: + (rc, self.ulist) = semanage_user_list_local(self.sh) @@ -656698,7 +656718,7 @@ index 0000000..1cf9681 + if rc < 0: + raise ValueError(_("Could not list roles for user %s") % name) + -+ roles = ' '.join(rlist); ++ roles = ' '.join(rlist) + ddict[semanage_user_get_name(u)] = (semanage_user_get_prefix(u), semanage_user_get_mlslevel(u), semanage_user_get_mlsrange(u), roles) + + return ddict @@ -656712,7 +656732,7 @@ index 0000000..1cf9681 + l.append("-a -L %s -r %s -R '%s' %s" % (ddict[k][1], ddict[k][2], ddict[k][3], k)) + return l + -+ def list(self, heading = True, locallist = False): ++ def list(self, heading=True, locallist=False): + ddict = self.get_all(locallist) + keys = list(ddict.keys()) + if len(keys) == 0: @@ -656733,11 +656753,11 @@ index 0000000..1cf9681 + +class portRecords(semanageRecords): + try: -+ valid_types = sepolicy.info(sepolicy.ATTRIBUTE,"port_type")[0]["types"] ++ valid_types = sepolicy.info(sepolicy.ATTRIBUTE, "port_type")[0]["types"] + except RuntimeError: + valid_types = [] + -+ def __init__(self, store = ""): ++ def __init__(self, store=""): + semanageRecords.__init__(self, store) + + def __genkey(self, port, proto): @@ -656764,7 +656784,7 @@ index 0000000..1cf9681 + (rc, k) = semanage_port_key_create(self.sh, low, high, proto_d) + if rc < 0: + raise ValueError(_("Could not create a key for %(PROTOTYPE)s/%(PORT)s") % {"PROTOTYPE": proto, "PORT":port}) -+ return ( k, proto_d, low, high ) ++ return (k, proto_d, low, high) + + def __add(self, port, proto, serange, type): + if is_mls_enabled == 1: @@ -656779,7 +656799,7 @@ index 0000000..1cf9681 + if type not in self.valid_types: + raise ValueError(_("Type %s is invalid, must be a port type") % type) + -+ ( k, proto_d, low, high ) = self.__genkey(port, proto) ++ (k, proto_d, low, high) = self.__genkey(port, proto) + if semanageRecords.transaction: + (rc, exists) = semanage_port_exists_local(self.sh, k) + else: @@ -656843,7 +656863,7 @@ index 0000000..1cf9681 + if setype and setype not in self.valid_types: + raise ValueError(_("Type %s is invalid, must be a port type") % setype) + -+ ( k, proto_d, low, high ) = self.__genkey(port, proto) ++ (k, proto_d, low, high) = self.__genkey(port, proto) + + (rc, exists) = semanage_port_exists(self.sh, k) + if rc < 0: @@ -656887,7 +656907,7 @@ index 0000000..1cf9681 + low = semanage_port_get_low(port) + high = semanage_port_get_high(port) + port_str = "%s-%s" % (low, high) -+ ( k, proto_d, low, high ) = self.__genkey(port_str , proto_str) ++ (k, proto_d, low, high) = self.__genkey(port_str , proto_str) + if rc < 0: + raise ValueError(_("Could not create a key for %s") % port_str) + @@ -656899,7 +656919,7 @@ index 0000000..1cf9681 + self.commit() + + def __delete(self, port, proto): -+ ( k, proto_d, low, high ) = self.__genkey(port, proto) ++ (k, proto_d, low, high) = self.__genkey(port, proto) + (rc, exists) = semanage_port_exists(self.sh, k) + if rc < 0: + raise ValueError(_("Could not check if port %(PROTOCOL)s/%(PORT)s is defined") % {"PROTOCOL": proto, "PORT": port}) @@ -656923,7 +656943,7 @@ index 0000000..1cf9681 + self.__delete(port, proto) + self.commit() + -+ def get_all(self, locallist = False): ++ def get_all(self, locallist=False): + ddict = {} + if locallist: + (rc, self.plist) = semanage_port_list_local(self.sh) @@ -656945,7 +656965,7 @@ index 0000000..1cf9681 + ddict[(low, high, proto_str)] = (ctype, level) + return ddict + -+ def get_all_by_type(self, locallist = False): ++ def get_all_by_type(self, locallist=False): + ddict = {} + if locallist: + (rc, self.plist) = semanage_port_list_local(self.sh) @@ -656964,11 +656984,11 @@ index 0000000..1cf9681 + low = semanage_port_get_low(port) + high = semanage_port_get_high(port) + if (ctype, proto_str) not in list(ddict.keys()): -+ ddict[(ctype,proto_str)] = [] ++ ddict[(ctype, proto_str)] = [] + if low == high: -+ ddict[(ctype,proto_str)].append("%d" % low) ++ ddict[(ctype, proto_str)].append("%d" % low) + else: -+ ddict[(ctype,proto_str)].append("%d-%d" % (low, high)) ++ ddict[(ctype, proto_str)].append("%d-%d" % (low, high)) + return ddict + + def customized(self): @@ -656983,7 +657003,7 @@ index 0000000..1cf9681 + l.append("-a -t %s -p %s %s-%s" % (ddict[k][0], k[2], k[0], k[1])) + return l + -+ def list(self, heading = True, locallist = False): ++ def list(self, heading=True, locallist=False): + ddict = self.get_all_by_type(locallist) + keys = list(ddict.keys()) + if len(keys) == 0: @@ -657001,18 +657021,18 @@ index 0000000..1cf9681 + +class nodeRecords(semanageRecords): + try: -+ valid_types = sepolicy.info(sepolicy.ATTRIBUTE,"node_type")[0]["types"] ++ valid_types = sepolicy.info(sepolicy.ATTRIBUTE, "node_type")[0]["types"] + except RuntimeError: + valid_types = [] + -+ def __init__(self, store = ""): -+ semanageRecords.__init__(self,store) ++ def __init__(self, store=""): ++ semanageRecords.__init__(self, store) + self.protocol = ["ipv4", "ipv6"] + + def validate(self, addr, mask, protocol): -+ newaddr=addr -+ newmask=mask -+ newprotocol="" ++ newaddr = addr ++ newmask = mask ++ newprotocol = "" + + if addr == "": + raise ValueError(_("Node Address is required")) @@ -657190,9 +657210,9 @@ index 0000000..1cf9681 + self.__delete(semanage_node_get_addr(self.sh, node)[1], semanage_node_get_mask(self.sh, node)[1], self.protocol[semanage_node_get_proto(node)]) + self.commit() + -+ def get_all(self, locallist = False): ++ def get_all(self, locallist=False): + ddict = {} -+ if locallist : ++ if locallist: + (rc, self.ilist) = semanage_node_list_local(self.sh) + else: + (rc, self.ilist) = semanage_node_list(self.sh) @@ -657214,10 +657234,10 @@ index 0000000..1cf9681 + keys = list(ddict.keys()) + keys.sort() + for k in keys: -+ l.append("-a -M %s -p %s -t %s %s" % (k[1], k[2],ddict[k][2], k[0])) ++ l.append("-a -M %s -p %s -t %s %s" % (k[1], k[2], ddict[k][2], k[0])) + return l + -+ def list(self, heading = True, locallist = False): ++ def list(self, heading=True, locallist=False): + ddict = self.get_all(locallist) + keys = list(ddict.keys()) + if len(keys) == 0: @@ -657231,14 +657251,14 @@ index 0000000..1cf9681 + val = '' + for fields in k: + val = val + '\t' + str(fields) -+ print("%-18s %-18s %-5s %s:%s:%s:%s " % (k[0],k[1],k[2],ddict[k][0], ddict[k][1],ddict[k][2], translate(ddict[k][3], False))) ++ print("%-18s %-18s %-5s %s:%s:%s:%s " % (k[0], k[1], k[2], ddict[k][0], ddict[k][1], ddict[k][2], translate(ddict[k][3], False))) + else: + for k in keys: -+ print("%-18s %-18s %-5s %s:%s:%s " % (k[0],k[1],k[2],ddict[k][0], ddict[k][1],ddict[k][2])) ++ print("%-18s %-18s %-5s %s:%s:%s " % (k[0], k[1], k[2], ddict[k][0], ddict[k][1], ddict[k][2])) + + +class interfaceRecords(semanageRecords): -+ def __init__(self, store = ""): ++ def __init__(self, store=""): + semanageRecords.__init__(self, store) + + def __add(self, interface, serange, ctype): @@ -657384,7 +657404,7 @@ index 0000000..1cf9681 + self.__delete(semanage_iface_get_name(i)) + self.commit() + -+ def get_all(self, locallist = False): ++ def get_all(self, locallist=False): + ddict = {} + if locallist: + (rc, self.ilist) = semanage_iface_list_local(self.sh) @@ -657408,7 +657428,7 @@ index 0000000..1cf9681 + l.append("-a -t %s %s" % (ddict[k][2], k)) + return l + -+ def list(self, heading = True, locallist = False): ++ def list(self, heading=True, locallist=False): + ddict = self.get_all(locallist) + keys = list(ddict.keys()) + if len(keys) == 0: @@ -657419,20 +657439,20 @@ index 0000000..1cf9681 + print("%-30s %s\n" % (_("SELinux Interface"), _("Context"))) + if is_mls_enabled: + for k in keys: -+ print("%-30s %s:%s:%s:%s " % (k,ddict[k][0], ddict[k][1],ddict[k][2], translate(ddict[k][3], False))) ++ print("%-30s %s:%s:%s:%s " % (k, ddict[k][0], ddict[k][1], ddict[k][2], translate(ddict[k][3], False))) + else: + for k in keys: -+ print("%-30s %s:%s:%s " % (k,ddict[k][0], ddict[k][1],ddict[k][2])) ++ print("%-30s %s:%s:%s " % (k,ddict[k][0], ddict[k][1], ddict[k][2])) + +class fcontextRecords(semanageRecords): + try: -+ valid_types = sepolicy.info(sepolicy.ATTRIBUTE,"file_type")[0]["types"] -+ valid_types += sepolicy.info(sepolicy.ATTRIBUTE,"device_node")[0]["types"] ++ valid_types = sepolicy.info(sepolicy.ATTRIBUTE, "file_type")[0]["types"] ++ valid_types += sepolicy.info(sepolicy.ATTRIBUTE, "device_node")[0]["types"] + valid_types.append("<>") + except RuntimeError: + valid_types = [] + -+ def __init__(self, store = ""): ++ def __init__(self, store=""): + semanageRecords.__init__(self, store) + self.equiv = {} + self.equiv_dist = {} @@ -657476,17 +657496,17 @@ index 0000000..1cf9681 + os.chmod(tmpfile, os.stat(subs_file)[stat.ST_MODE]) + except: + pass -+ os.rename(tmpfile,subs_file) ++ os.rename(tmpfile, subs_file) + self.equal_ind = False + semanageRecords.commit(self) + + def add_equal(self, target, substitute): + self.begin() + if target != "/" and target[-1] == "/": -+ raise ValueError(_("Target %s is not valid. Target is not allowed to end with '/'") % target ) ++ raise ValueError(_("Target %s is not valid. Target is not allowed to end with '/'") % target) + + if substitute != "/" and substitute[-1] == "/": -+ raise ValueError(_("Substitute %s is not valid. Substitute is not allowed to end with '/'") % substitute ) ++ raise ValueError(_("Substitute %s is not valid. Substitute is not allowed to end with '/'") % substitute) + + if target in list(self.equiv.keys()): + raise ValueError(_("Equivalence class for %s already exists") % target) @@ -657509,7 +657529,7 @@ index 0000000..1cf9681 + self.equal_ind = True + self.commit() + -+ def createcon(self, target, seuser = "system_u"): ++ def createcon(self, target, seuser="system_u"): + (rc, con) = semanage_context_create(self.sh) + if rc < 0: + raise ValueError(_("Could not create context for %s") % target) @@ -657538,12 +657558,12 @@ index 0000000..1cf9681 + raise ValueError(_("File specification can not include spaces")) + for fdict in (self.equiv, self.equiv_dist): + for i in fdict: -+ if target.startswith(i+"/"): ++ if target.startswith(i + "/"): + t = re.sub(i, fdict[i], target) + raise ValueError(_("File spec %(TARGET)s conflicts with equivalency rule '%(SOURCE)s %(DEST)s'; Try adding '%(DEST1)s' instead") % {"TARGET":target, "SOURCE": i, "DEST":fdict[i], "DEST1": t}) + + -+ def __add(self, target, type, ftype = "", serange = None, seuser = "system_u"): ++ def __add(self, target, type, ftype="", serange=None, seuser="system_u"): + self.validate(target) + + if seuser == "": @@ -657609,7 +657629,7 @@ index 0000000..1cf9681 + semanage_fcontext_key_free(k) + semanage_fcontext_free(fcontext) + -+ def add(self, target, type, ftype = "", serange=None, seuser = "system_u"): ++ def add(self, target, type, ftype="", serange=None, seuser="system_u"): + self.begin() + self.__add(target, type, ftype, serange, seuser) + self.commit() @@ -657704,7 +657724,7 @@ index 0000000..1cf9681 + self.equal_ind = True + return + -+ (rc,k) = semanage_fcontext_key_create(self.sh, target, file_types[ftype]) ++ (rc, k) = semanage_fcontext_key_create(self.sh, target, file_types[ftype]) + if rc < 0: + raise ValueError(_("Could not create a key for %s") % target) + @@ -657728,10 +657748,10 @@ index 0000000..1cf9681 + + def delete(self, target, ftype): + self.begin() -+ self.__delete( target, ftype) ++ self.__delete(target, ftype) + self.commit() + -+ def get_all(self, locallist = False): ++ def get_all(self, locallist=False): + if locallist: + (rc, self.flist) = semanage_fcontext_list_local(self.sh) + else: @@ -657772,7 +657792,7 @@ index 0000000..1cf9681 + l.append("-a -e %s %s" % (self.equiv[target], target)) + return l + -+ def list(self, heading = True, locallist = False ): ++ def list(self, heading=True, locallist=False): + fcon_dict = self.get_all(locallist) + keys = list(fcon_dict.keys()) + if len(keys) != 0: @@ -657782,9 +657802,9 @@ index 0000000..1cf9681 + for k in keys: + if fcon_dict[k]: + if is_mls_enabled: -+ print("%-50s %-18s %s:%s:%s:%s " % (k[0], k[1], fcon_dict[k][0], fcon_dict[k][1], fcon_dict[k][2], translate(fcon_dict[k][3],False))) ++ print("%-50s %-18s %s:%s:%s:%s " % (k[0], k[1], fcon_dict[k][0], fcon_dict[k][1], fcon_dict[k][2], translate(fcon_dict[k][3], False))) + else: -+ print("%-50s %-18s %s:%s:%s " % (k[0], k[1], fcon_dict[k][0], fcon_dict[k][1],fcon_dict[k][2])) ++ print("%-50s %-18s %s:%s:%s " % (k[0], k[1], fcon_dict[k][0], fcon_dict[k][1], fcon_dict[k][2])) + else: + print("%-50s %-18s <>" % (k[0], k[1])) + @@ -657802,7 +657822,7 @@ index 0000000..1cf9681 + print("%s = %s" % (target, self.equiv[target])) + +class booleanRecords(semanageRecords): -+ def __init__(self, store = ""): ++ def __init__(self, store=""): + semanageRecords.__init__(self, store) + self.dict = {} + self.dict["TRUE"] = 1 @@ -657843,7 +657863,7 @@ index 0000000..1cf9681 + if value.upper() in self.dict: + semanage_bool_set_value(b, self.dict[value.upper()]) + else: -+ raise ValueError(_("You must specify one of the following values: %s") % ", ".join(list(self.dict.keys())) ) ++ raise ValueError(_("You must specify one of the following values: %s") % ", ".join(list(self.dict.keys()))) + + if self.modify_local and name in self.current_booleans: + rc = semanage_bool_set_active(self.sh, k, b) @@ -657855,7 +657875,7 @@ index 0000000..1cf9681 + semanage_bool_key_free(k) + semanage_bool_free(b) + -+ def modify(self, name, value = None, use_file = False): ++ def modify(self, name, value=None, use_file=False): + self.begin() + if use_file: + fd = open(name) @@ -657867,7 +657887,7 @@ index 0000000..1cf9681 + try: + boolname, val = b.split("=") + except ValueError: -+ raise ValueError(_("Bad format %(BOOLNAME)s: Record %(VALUE)s" % { "BOOLNAME": name, "VALUE": b } )) ++ raise ValueError(_("Bad format %(BOOLNAME)s: Record %(VALUE)s" % { "BOOLNAME": name, "VALUE": b })) + self.__mod(boolname.strip(), val.strip()) + fd.close() + else: @@ -657917,7 +657937,7 @@ index 0000000..1cf9681 + + self.commit() + -+ def get_all(self, locallist = False): ++ def get_all(self, locallist=False): + ddict = {} + if locallist: + (rc, self.blist) = semanage_bool_list_local(self.sh) @@ -657958,7 +657978,7 @@ index 0000000..1cf9681 + l.append("-m -%s %s" % (ddict[k][2], k)) + return l + -+ def list(self, heading = True, locallist = False, use_file = False): ++ def list(self, heading=True, locallist=False, use_file=False): + on_off = (_("off"), _("on")) + if use_file: + ddict = self.get_all(locallist) @@ -657973,7 +657993,7 @@ index 0000000..1cf9681 + return + + if heading: -+ print("%-30s %s %s %s\n" % (_("SELinux boolean"),_("State"), _("Default"), _("Description"))) ++ print("%-30s %s %s %s\n" % (_("SELinux boolean"), _("State"), _("Default"), _("Description"))) + for k in keys: + if ddict[k]: + print("%-30s (%-5s,%5s) %s" % (k, on_off[selinux.security_get_boolean_active(k)], on_off[ddict[k][2]], self.get_desc(k))) diff --git a/policycoreutils.spec b/policycoreutils.spec index 0774592..c87334c 100644 --- a/policycoreutils.spec +++ b/policycoreutils.spec @@ -7,7 +7,7 @@ Summary: SELinux policy core utilities Name: policycoreutils Version: 2.4 -Release: 7%{?dist} +Release: 8%{?dist} License: GPLv2 Group: System Environment/Base # https://github.com/SELinuxProject/selinux/wiki/Releases @@ -18,7 +18,7 @@ Source2: policycoreutils_man_ru2.tar.bz2 Source3: system-config-selinux.png Source4: sepolicy-icons.tgz # use make-rhat-patches.sh to create following patches from https://github.com/fedora-selinux/selinux/ -# HEAD https://github.com/fedora-selinux/selinux/commit/b7b250d47a5ae70efc95492cda499ee6a8ae12d8 +# HEAD https://github.com/fedora-selinux/selinux/commit/38d05b08329cb56bba1e64a37b9b166f2fa9f85c Patch: policycoreutils-rhat.patch Patch1: sepolgen-rhat.patch Obsoletes: policycoreutils < 2.0.61-2 @@ -399,6 +399,9 @@ The policycoreutils-restorecond package contains the restorecond service. %systemd_postun_with_restart restorecond.service %changelog +* Thu Aug 06 2015 Petr Lautrbach 2.4-8 +- Fix multiple python3 issues in sepolgen (#1249388,#1247575,#1247564) + * Mon Jul 27 2015 Petr Lautrbach 2.4-7 - policycoreutils-python3 depends on python-IPy-python3 diff --git a/sepolgen-rhat.patch b/sepolgen-rhat.patch index 1e236c4..490c231 100644 --- a/sepolgen-rhat.patch +++ b/sepolgen-rhat.patch @@ -122,10 +122,10 @@ index cf13210..60ff4e9 100644 else: role_type = refpolicy.RoleType() diff --git a/sepolgen-1.2.2/src/sepolgen/audit.py b/sepolgen-1.2.2/src/sepolgen/audit.py -index 56919be..ddad682 100644 +index 56919be..1c94daa 100644 --- a/sepolgen-1.2.2/src/sepolgen/audit.py +++ b/sepolgen-1.2.2/src/sepolgen/audit.py -@@ -17,11 +17,11 @@ +@@ -17,11 +17,12 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # @@ -136,10 +136,38 @@ index 56919be..ddad682 100644 +from . import refpolicy +from . import access ++from . import util # Convenience functions def get_audit_boot_msgs(): -@@ -169,6 +169,7 @@ class AVCMessage(AuditMessage): +@@ -42,6 +43,8 @@ def get_audit_boot_msgs(): + boottime = time.strftime("%X", s) + output = subprocess.Popen(["/sbin/ausearch", "-m", "AVC,USER_AVC,MAC_POLICY_LOAD,DAEMON_START,SELINUX_ERR", "-ts", bootdate, boottime], + stdout=subprocess.PIPE).communicate()[0] ++ if util.PY3: ++ output = util.decode_input(output) + return output + + def get_audit_msgs(): +@@ -55,6 +58,8 @@ def get_audit_msgs(): + import subprocess + output = subprocess.Popen(["/sbin/ausearch", "-m", "AVC,USER_AVC,MAC_POLICY_LOAD,DAEMON_START,SELINUX_ERR"], + stdout=subprocess.PIPE).communicate()[0] ++ if util.PY3: ++ output = util.decode_input(output) + return output + + def get_dmesg_msgs(): +@@ -66,6 +71,8 @@ def get_dmesg_msgs(): + import subprocess + output = subprocess.Popen(["/bin/dmesg"], + stdout=subprocess.PIPE).communicate()[0] ++ if util.PY3: ++ output = util.decode_input(output) + return output + + # Classes representing audit messages +@@ -169,6 +176,7 @@ class AVCMessage(AuditMessage): self.exe = "" self.path = "" self.name = "" @@ -147,7 +175,7 @@ index 56919be..ddad682 100644 self.accesses = [] self.denial = True self.type = audit2why.TERULE -@@ -230,6 +231,10 @@ class AVCMessage(AuditMessage): +@@ -230,6 +238,10 @@ class AVCMessage(AuditMessage): self.exe = fields[1][1:-1] elif fields[0] == "name": self.name = fields[1][1:-1] @@ -158,7 +186,7 @@ index 56919be..ddad682 100644 if not found_src or not found_tgt or not found_class or not found_access: raise ValueError("AVC message in invalid format [%s]\n" % self.message) -@@ -354,7 +359,9 @@ class AuditParser: +@@ -354,7 +366,9 @@ class AuditParser: self.path_msgs = [] self.by_header = { } self.check_input_file = False @@ -169,7 +197,7 @@ index 56919be..ddad682 100644 # Low-level parsing function - tries to determine if this audit # message is an SELinux related message and then parses it into # the appropriate AuditMessage subclass. This function deliberately -@@ -430,7 +437,7 @@ class AuditParser: +@@ -430,7 +444,7 @@ class AuditParser: # Group by audit header if msg.header != "": @@ -178,7 +206,7 @@ index 56919be..ddad682 100644 self.by_header[msg.header].append(msg) else: self.by_header[msg.header] = [msg] -@@ -492,6 +499,60 @@ class AuditParser: +@@ -492,6 +506,68 @@ class AuditParser: return role_types @@ -201,7 +229,11 @@ index 56919be..ddad682 100644 + try: + output = subprocess.check_output(command, + stderr=subprocess.STDOUT, -+ shell=True) ++ shell=True, ++ universal_newlines=True) ++ if util.PY3: ++ output = util.decode_input(output) ++ + try: + ino = int(inode) + except ValueError: @@ -218,11 +250,14 @@ index 56919be..ddad682 100644 + return path + + def __store_base_types(self): -+ import sepolicy -+ self.base_types = sepolicy.get_types_from_attribute("base_file_type") ++ # FIXME: this is a temporary workaround until sepolicy is ported to python 3 ++ # import sepolicy ++ # self.base_types = sepolicy.get_types_from_attribute("base_file_type") ++ self.base_types = [] + + def __get_base_type(self, tcontext, scontext): -+ import sepolicy ++ # FIXME: uncomment the following code when sepolicy is ported to python 3 ++ # import sepolicy + # Prevent unnecessary searching + if (self.old_scontext == scontext and + self.old_tcontext == tcontext): @@ -231,15 +266,16 @@ index 56919be..ddad682 100644 + self.old_tcontext = tcontext + for btype in self.base_types: + if btype == tcontext: -+ for writable in sepolicy.get_writable_files(scontext): -+ if writable.endswith(tcontext) and writable.startswith(scontext.rstrip("_t")): -+ return writable ++ # FIXME: uncomment the following code when sepolicy is ported to python 3 ++ # for writable in sepolicy.get_writable_files(scontext): ++ # if writable.endswith(tcontext) and writable.startswith(scontext.rstrip("_t")): ++ # return writable + return 0 + def to_access(self, avc_filter=None, only_denials=True): """Convert the audit logs access into a an access vector set. -@@ -510,16 +571,23 @@ class AuditParser: +@@ -510,16 +586,23 @@ class AuditParser: audit logs parsed by this object. """ av_set = access.AccessVectorSet() @@ -911,7 +947,7 @@ index 88c8a1f..d05d721 100644 self.classes[c] = { } cur = self.classes[c] diff --git a/sepolgen-1.2.2/src/sepolgen/output.py b/sepolgen-1.2.2/src/sepolgen/output.py -index 739452d..d8daedb 100644 +index 739452d..7a83aee 100644 --- a/sepolgen-1.2.2/src/sepolgen/output.py +++ b/sepolgen-1.2.2/src/sepolgen/output.py @@ -27,8 +27,12 @@ generating policy. This keeps the semantic / syntactic issues @@ -929,6 +965,24 @@ index 739452d..d8daedb 100644 class ModuleWriter: def __init__(self): +@@ -127,7 +131,7 @@ def sort_filter(module): + rules = [] + rules.extend(node.avrules()) + rules.extend(node.interface_calls()) +- rules.sort(rule_cmp) ++ rules.sort(key=util.cmp_to_key(rule_cmp)) + + cur = None + sep_rules = [] +@@ -151,7 +155,7 @@ def sort_filter(module): + + ras = [] + ras.extend(node.role_types()) +- ras.sort(role_type_cmp) ++ ras.sort(key=util.cmp_to_key(role_type_cmp)) + if len(ras): + comment = refpolicy.Comment() + comment.lines.append("============= ROLES ==============") diff --git a/sepolgen-1.2.2/src/sepolgen/policygen.py b/sepolgen-1.2.2/src/sepolgen/policygen.py index 5f38577..89366df 100644 --- a/sepolgen-1.2.2/src/sepolgen/policygen.py @@ -1256,7 +1310,7 @@ index 8ad64a9..a9bb92d 100644 class Require(Leaf): def __init__(self, parent=None): diff --git a/sepolgen-1.2.2/src/sepolgen/util.py b/sepolgen-1.2.2/src/sepolgen/util.py -index 74a11f5..4934bec 100644 +index 74a11f5..1fca971 100644 --- a/sepolgen-1.2.2/src/sepolgen/util.py +++ b/sepolgen-1.2.2/src/sepolgen/util.py @@ -16,6 +16,19 @@ @@ -1279,7 +1333,7 @@ index 74a11f5..4934bec 100644 class ConsoleProgressBar: def __init__(self, out, steps=100, indicator='#'): -@@ -76,6 +89,51 @@ def first(s, sorted=False): +@@ -76,6 +89,88 @@ def first(s, sorted=False): for x in s: return x @@ -1297,6 +1351,20 @@ index 74a11f5..4934bec 100644 + encoded_text = text.encode('utf-8') + return encoded_text + ++def decode_input(text): ++ import locale ++ """Decode given text via preferred system encoding""" ++ # locale will often find out the correct encoding ++ encoding = locale.getpreferredencoding() ++ try: ++ decoded_text = text.decode(encoding) ++ except UnicodeError: ++ # if it fails to find correct encoding then ascii is used ++ # which may lead to UnicodeError if `text` contains non ascii signs ++ # utf-8 is our guess to fix the situation ++ decoded_text = text.decode('utf-8') ++ return decoded_text ++ +class Comparison(): + """Class used when implementing rich comparison. + @@ -1325,6 +1393,29 @@ index 74a11f5..4934bec 100644 + def __ne__(self, other): + return self._compare(other, lambda a, b: a != b) + ++if sys.version_info < (2,7): ++ # cmp_to_key function is missing in python2.6 ++ def cmp_to_key(mycmp): ++ 'Convert a cmp= function into a key= function' ++ class K: ++ def __init__(self, obj, *args): ++ self.obj = obj ++ def __lt__(self, other): ++ return mycmp(self.obj, other.obj) < 0 ++ def __gt__(self, other): ++ return mycmp(self.obj, other.obj) > 0 ++ def __eq__(self, other): ++ return mycmp(self.obj, other.obj) == 0 ++ def __le__(self, other): ++ return mycmp(self.obj, other.obj) <= 0 ++ def __ge__(self, other): ++ return mycmp(self.obj, other.obj) >= 0 ++ def __ne__(self, other): ++ return mycmp(self.obj, other.obj) != 0 ++ return K ++else: ++ from functools import cmp_to_key ++ +def cmp(first, second): + return (first > second) - (second > first) +