def email_alert(siginfo, to_addrs): smtp_host = get_config('email','smtp_host') smtp_port = get_config('email','smtp_port', int) from_address = get_config('email','from_address') from_user, from_domain = parse_email_addr(from_address) if from_user is None: from_user = "******" if from_domain is None: from_domain = get_hostname() from_address = '%s@%s' % (from_user, from_domain) log_debug("alert smtp=%s:%d -> %s" % (smtp_host, smtp_port, ','.join(to_addrs))) siginfo.update_derived_template_substitutions() summary = siginfo.substitute(siginfo.summary()) subject = '[%s] %s' % (get_config('email','subject'), summary) text = siginfo.format_text() + siginfo.format_details() email_msg = MIMEMultipart('alternative') email_msg['Subject'] = subject email_msg['From'] = from_address email_msg['To'] = ', '.join(to_addrs) email_msg['Date'] = formatdate() email_msg.attach(MIMEText(text)) import smtplib try: smtp = smtplib.SMTP(smtp_host, smtp_port) smtp.sendmail(from_address, to_addrs, email_msg.as_string()) smtp.quit() except smtplib.SMTPException, e: syslog.syslog(syslog.LOG_ERR, "email failed: %s" % e)
def get_socket_paths(self): self.audit_socket_paths = [] audit_socket_path = get_config('audit','text_protocol_socket_path') self.audit_socket_paths.append(audit_socket_path) audit_socket_path = get_config('audit','binary_protocol_socket_path') self.audit_socket_paths.append(audit_socket_path)
def get_socket_paths(self): self.audit_socket_paths = [] audit_socket_path = get_config("audit", "text_protocol_socket_path") self.audit_socket_paths.append(audit_socket_path) audit_socket_path = get_config("audit", "binary_protocol_socket_path") self.audit_socket_paths.append(audit_socket_path)
def __init__(self, username): RpcChannel.__init__(self, channel_type='sealert') GObject.GObject.__init__(self) self.connection_state.connect('changed', self.on_connection_state_change) self.connect_rpc_interface('SEAlert', self) self.connect_rpc_interface('SETroubleshootDatabaseNotify', self) self.pkg_version = get_config('general', 'pkg_version') self.rpc_version = get_config('general', 'rpc_version') self.username = username self.retry_connection_if_closed = False self.connection_retry = Retry(self.retry_connection, self.get_connection_retry_interval, notify_interval=1.0) self.report_connect_failure = True self.database_name = 'audit_listener'
def report_problem(self, siginfo): siginfo = super(AlertPluginReportReceiver, self).report_problem(siginfo) if email_recipients is not None: to_addrs = [] for recipient in email_recipients.recipient_list: username = "******" % recipient.address action = siginfo.evaluate_filter_for_user(username, recipient.filter_type) if action != "ignore": log_debug("Email: siginfo.sig=%s" % siginfo.sig) to_addrs.append(recipient.address) if len(to_addrs): from setroubleshoot.email_alert import email_alert email_alert(siginfo, to_addrs) log_debug("sending alert to all clients") from setroubleshoot.html_util import html_to_text syslog.syslog(syslog.LOG_ERR, siginfo.summary() + _(" For complete SELinux messages run: sealert -l %s") % siginfo.local_id) for audit_record in siginfo.audit_event.records: if audit_record.record_type == 'AVC': pid = audit_record.fields["pid"] break if get_config('setroubleshootd_log', 'log_full_report', bool): systemd.journal.send(siginfo.format_text(), OBJECT_PID=pid) for u in siginfo.users: action = siginfo.evaluate_filter_for_user(u.username) if action == "ignore": return siginfo send_alert_notification(siginfo) return siginfo
def make_database_filepath(name): database_dir = get_config('database', 'database_dir') # strip off extension if one was provided name = re.sub('\\.xml$', '', name) filename = name + '_database.xml' filepath = os.path.join(database_dir, filename) return filepath
def open(self, logfile_path=None): if logfile_path is not None: self.logfile_path = logfile_path log_debug('%s.open(%s)' % (self.__class__.__name__, self.logfile_path)) try: stat_info = os.stat(self.logfile_path) self.file_size = stat_info[ST_SIZE] self.file = open(self.logfile_path) self.fileno = self.file.fileno() except EnvironmentError as e: syslog.syslog(syslog.LOG_ERR, '%s.open(): %s' % (self.__class__.__name__, e.strerror)) self.errno = e.errno self.strerror = e.strerror raise e self.n_bytes_read = 0 self.line_count = 0 self.record_count = 0 self.progress = 0.0 self.cancelled = False self.emit('progress', self.progress) logfile_basename = os.path.basename(self.logfile_path) self.friendly_name = "file: %s" % (os.path.splitext(logfile_basename)[0]) self.database = SETroubleshootDatabase(None, logfile_basename, friendly_name=self.friendly_name) self.record_reader = AuditRecordReader(AuditRecordReader.TEXT_FORMAT) self.record_receiver = AuditRecordReceiver() self.analyzer = Analyze() if not get_config('test', 'analyze', bool): self.report_receiver = PluginReportReceiver(self.database) else: self.report_receiver = TestPluginReportReceiver(self.database) return True
def find_program(prog): if os.path.isabs(prog): return prog basename = os.path.basename(prog) search_path = get_config('fix_command', 'prog_search_path').split(':') for d in search_path: path = os.path.join(d, basename) if os.path.exists(path): return path return None
def __init__(self, queue, report_receiver): # parent class constructor threading.Thread.__init__(self) self.queue = queue self.report_receiver = report_receiver self.record_receiver = AuditRecordReceiver() self.retry_interval = get_config("audit", "retry_interval", int) self.get_socket_paths() self.timeout_interval = 2 self.has_audit_eoe = False
def __init__(self, filepath, name, friendly_name=None): self.filepath = filepath self.notify = None self.properties = SEDatabaseProperties(name, friendly_name, self.filepath) self.lock = threading.Lock() self.file_exists = False self.modified_count = 0 self.auto_save_interval = 5 # auto save after this many seconds self.auto_save_threshold = 200 # auto save after this many changes self.auto_save_timer = None self.max_alerts = get_config('database', 'max_alerts', int) self.max_alert_age = None max_alert_age = get_config('database', 'max_alert_age') if max_alert_age is not None: max_alert_age = max_alert_age.strip() if max_alert_age: self.max_alert_age = parse_datetime_offset(max_alert_age) log_debug("created new database: name=%s, friendly_name=%s, filepath=%s" % (self.properties.name, self.properties.friendly_name, self.properties.filepath)) self.load()
def get_plugin_names(filter_glob=None): if filter_glob is None: filter_glob = '*' else: filter_glob = re.sub('.py$', '', filter_glob) plugin_dir = get_config('plugins', 'plugin_dir') plugin_names = [] for p in glob.glob(os.path.join(plugin_dir, filter_glob + ".py")): p = os.path.basename(p) if p in ['__init__.py']: continue plugin_name = os.path.splitext(os.path.basename(p))[0] plugin_names.append(plugin_name) return plugin_names
def load_plugins(filter_glob=None): plugin_dir = get_config('plugins', 'plugin_dir') plugin_base = os.path.basename(plugin_dir) plugins = [] plugin_names = get_plugin_names(filter_glob) log_debug("load_plugins() names=%s" % plugin_names) # load the parent (e.g. the package containing the submodules), required for python 2.5 and above module_name = plugin_base plugin_name = '__init__' if module_name not in sys.modules: try: import imp mod_fp, mod_path, mod_description = imp.find_module(plugin_name, [plugin_dir]) mod = imp.load_module(module_name, mod_fp, mod_path, mod_description) except Exception: syslog.syslog(syslog.LOG_ERR, "failed to initialize plugins in %s" % plugin_dir) return [] if mod_fp: mod_fp.close() for plugin_name in plugin_names: module_name = "%s.%s" % (plugin_base, plugin_name) mod = sys.modules.get(module_name) if mod is not None: log_debug("load_plugins() %s previously imported" % module_name) plugins.append(mod.plugin()) continue try: import imp mod_fp, mod_path, mod_description = imp.find_module(plugin_name, [plugin_dir]) mod = imp.load_module(module_name, mod_fp, mod_path, mod_description) plugins.append(mod.plugin()) except Exception: syslog.syslog(syslog.LOG_ERR, "failed to load %s plugin" % plugin_name) if mod_fp: mod_fp.close() plugins.sort(key=cmp_to_key(sort_plugins)) return plugins
def log_debug(*msg): global log_level if log_level == "unknown": log_level = get_config('sealert_log', 'level') if log_level == "debug": syslog.syslog(syslog.LOG_DEBUG, msg)
# (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # import gettext from setroubleshoot.config import parse_config_setting, get_config translation = gettext.translation(domain=get_config('general', 'i18n_text_domain'), localedir=get_config('general', 'i18n_locale_dir'), fallback=True) try: _ = translation.ugettext # Unicode version of gettext for Py2 except AttributeError: _ = translation.gettext # Python3 (uses unicode by default) from setroubleshoot.signature import * from setroubleshoot.util import * #import sys import os.path import re
def log_init(section): global log_level log_level = log_levels.get(get_config(section, 'level').upper()) if log_level is None: log_level = syslog.LOG_WARNING
def launch_web_browser_on_url(url): web_browser_launcher = get_config('helper_apps', 'web_browser_launcher') os.spawnl(os.P_NOWAIT, web_browser_launcher, web_browser_launcher, url)
#/usr/bin/env python # Author: Thomas Liu <*****@*****.**> import yum import gettext from setroubleshoot.config import parse_config_setting, get_config gettext.install(domain = get_config('general', 'i18n_text_domain'), localedir = get_config('general', 'i18n_locale_dir')) installed = [] try: yb = yum.YumBase() yb.conf.cache = True installed = yb.rpmdb.searchNevra('selinux-policy') if installed: for pkg in sorted(installed): if pkg.name == 'selinux-policy': print _("current: %s ") % pkg.printVer() try: pl = yb.doPackageLists(patterns=['selinux-policy']) except yum.Errors.RepoError, msg: yb.conf.cache = False pl = yb.doPackageLists(patterns=['selinux-policy']) if pl.available: for pkg in sorted(pl.available): print _("newer: %s ") % pkg.printVer() except yum.Errors.RepoError, msg: print "error: ", str(msg)
'XmlSerialize', ] import sys from types import * import libxml2 import syslog from setroubleshoot.config import get_config from setroubleshoot.errcode import * from setroubleshoot.util import * #------------------------------------------------------------------------ i18n_encoding = get_config('general', 'i18n_encoding') #------------------------------------------------------------------------ def validate_database_doc(doc): if doc is None: syslog.syslog(syslog.LOG_DEBUG, "validate_database_doc: doc is empty, validate fails") return False root_node = doc.getRootElement() if root_node is None: syslog.syslog(syslog.LOG_DEBUG, "validate_database_doc: root is empty, validate fails") return False version = root_node.prop('version') if version is None: syslog.syslog(syslog.LOG_DEBUG, "validate_database_doc: version is empty, validate fails") return False
import gettext #import errno as Errno import os #import pwd import signal import atexit #from stat import * import sys import syslog import systemd.journal #import threading #from types import * from setroubleshoot.config import parse_config_setting, get_config domain = get_config('general', 'i18n_text_domain') localedir = get_config('general', 'i18n_locale_dir') kwargs = {} if sys.version_info < (3,): kwargs['unicode'] = True gettext.install(domain=domain, localedir=localedir, **kwargs) translation = gettext.translation(domain=domain, localedir=localedir, fallback=True) try: _ = translation.ugettext # This raises exception in Python3, succ. in Py2 except AttributeError:
def get_default_port(): default_port = get_config('connection','default_port', int) return default_port
def get_socket_list_from_config(cfg_section): addr_string = get_config(cfg_section, 'address_list') socket_addresses = parse_socket_address_list(addr_string) return socket_addresses
rec += "<fix>" rec += se_plugin.fix_description rec += "</fix>\n" rec += "</setroubleshoot>" return rec __all__ = [ 'RunFaultServer', 'get_host_database', 'send_alert_notification', ] from setroubleshoot.config import get_config import gettext gettext.install(domain=get_config('general', 'i18n_text_domain'), localedir=get_config('general', 'i18n_locale_dir'), unicode=False, codeset=get_config('general', 'i18n_encoding')) from setroubleshoot.util import * se_plugins = {} plugins = load_plugins() for p in plugins: se_plugins[p.analysis_id.split(".")[1]] = p fd = open("/usr/share/system-config-selinux/selinux.tbl") booll = fd.readlines() fd.close() booldict = {}
import atexit #from stat import * import syslog import systemd.journal #import threading #from types import * from setroubleshoot.access_control import ServerAccess from setroubleshoot.analyze import (PluginReportReceiver, SETroubleshootDatabase, TestPluginReportReceiver, AnalyzeThread, ) from setroubleshoot.avc_audit import * from setroubleshoot.config import get_config if get_config('general', 'use_auparse', bool): from setroubleshoot.avc_auparse import * else: from setroubleshoot.avc_audit import AuditRecordReceiver from setroubleshoot.errcode import (ProgramError, ERR_NOT_AUTHENTICATED, ERR_USER_LOOKUP, ERR_USER_PROHIBITED, ERR_USER_PERMISSION, ERR_FILE_OPEN, ERR_DATABASE_NOT_FOUND, ) from setroubleshoot.rpc import (RpcChannel, ConnectionState,
def RunFaultServer(timeout=10): # FIXME audit2why.init() global host_database, analysis_queue, email_recipients signal.signal(signal.SIGHUP, sighandler) signal.signal(signal.SIGQUIT, sighandler) signal.signal(signal.SIGTERM, sighandler) signal.signal(signal.SIGALRM, sighandler) #interface_registry.dump_interfaces() try: # FIXME: should this be using our logging objects in log.py? # currently syslog is only used for putting an alert into # the syslog with it's id pkg_name = get_config('general','pkg_name') syslog.openlog(pkg_name) # Create an object responsible for sending notifications to clients client_notifier = ClientNotifier(connection_pool) # Create a database local to this host database_filename = get_config('database','filename') database_filepath = make_database_filepath(database_filename) assure_file_ownership_permissions(database_filepath, 0600, 'root', 'root') host_database = SETroubleshootDatabase(database_filepath, database_filename, friendly_name=_("Audit Listener")) host_database.set_notify(client_notifier) atexit.register(goodbye, host_database) deleted = False for i in host_database.sigs.signature_list: why, bools = audit2why.analyze(str(i.sig.scontext), str(i.sig.tcontext), str(i.sig.tclass), i.sig.access) if why == audit2why.ALLOW or why == audit2why.DONTAUDIT: if why == audit2why.ALLOW: reason = "allowed" else: reason = "dontaudit'd" syslog.syslog(syslog.LOG_ERR, "Deleting alert %s, it is %s in current policy" % (i.local_id, reason) ) deleted = True host_database.delete_signature(i.sig) if deleted: host_database.save(prune=True) # Attach the local database to an object which will send alerts # specific to this host if not get_config('test', 'analyze', bool): alert_receiver = AlertPluginReportReceiver(host_database) else: alert_receiver = TestPluginReportReceiver(host_database) # Create a synchronized queue for analysis requests import Queue analysis_queue = Queue.Queue(0) # Create a thread to peform analysis, it takes AVC objects off # the analysis queue and runs the plugins against the # AVC. Analysis requests in the queue may arrive from a # variety of places; from the audit system, from a log file # scan, etc. The disposition of the analysis (e.g. where the # results of the analysis are to go) are included in the queued # object along with the data to analyze. analyze_thread = AnalyzeThread(analysis_queue) analyze_thread.setDaemon(True) analyze_thread.start() # Create a thread to receive messages from the audit system. # This is a time sensitive operation, the primary job of this # thread is to receive the audit message as quickly as # possible and return to listening on the audit socket. When # it receives a complete audit event it places it in the # analysis queue where another thread will process it # independently. # audit_socket_thread = AuditSocketReceiverThread(analysis_queue, alert_receiver) # audit_socket_thread.setDaemon(True) # audit_socket_thread.start() # Initialize the email recipient list from setroubleshoot.signature import SEEmailRecipientSet email_recipients = SEEmailRecipientSet() assure_file_ownership_permissions(email_recipients_filepath, 0600, 'root', 'root') try: email_recipients.parse_recipient_file(email_recipients_filepath) except ProgramError, e: if e.errno == ERR_FILE_OPEN: syslog.syslog(syslog.LOG_DEBUG, e.strerror) else: raise e # Create a server to listen for alert clients and then run. listen_addresses = get_socket_list_from_config('listen_for_client') for listen_address in listen_addresses: listening_server = ListeningServer(listen_address, SetroubleshootdClientConnectionHandler) listening_server.open() dbus.glib.init_threads() setroubleshootd_dbus = SetroubleshootdDBus(analysis_queue, alert_receiver, timeout) main_loop = gobject.MainLoop() main_loop.run()
def init_privilege(self, privilege): cfg_names = [name.strip() for name in \ get_config('access', '%s_users' % privilege).split(',')] return cfg_names
# but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # import gettext from math import pi from subprocess import * from gettext import ngettext as P_ from setroubleshoot.config import parse_config_setting, get_config domain = get_config("general", "i18n_text_domain") gettext.install(domain=domain, unicode=True, localedir=get_config("general", "i18n_locale_dir")) translation = gettext.translation(domain, fallback=True) _ = translation.ugettext import sys, os from xml.dom import minidom from xmlrpclib import ProtocolError import gtk, glib import gtk.glade from setroubleshoot.errcode import * from setroubleshoot.signature import * from setroubleshoot.util import * from setroubleshoot.html_util import html_to_text import re import dbus import slip.dbus.service
def RunFaultServer(timeout=10): signal.alarm(timeout) sigalrm_handler = signal.signal(signal.SIGALRM, polling_failed_handler) # polling for /sys/fs/selinux/policy file while True: try: audit2why.init() signal.alarm(0) break # retry if init() failed to open /sys/fs/selinux/policy except ValueError as e: # The value error contains the following error message, # followed by strerror string (which can differ with localization) if "unable to open /sys/fs/selinux/policy" in str(e): continue raise e except SystemError as e: # As a result of a bug in audit2why.c, SystemError is raised instead of ValueError. # Python reports: "SystemError occurs as a direct cause of ValueError" # Error message of the ValueError is stored in __context__ # TODO: remove this except clause when the bug in audti2why is fixed if "unable to open /sys/fs/selinux/policy" in str(getattr(e, "__context__", "")): continue raise e global host_database, analysis_queue, email_recipients signal.signal(signal.SIGALRM, sigalrm_handler) signal.signal(signal.SIGHUP, sighandler) #interface_registry.dump_interfaces() try: # FIXME: should this be using our logging objects in log.py? # currently syslog is only used for putting an alert into # the syslog with it's id pkg_name = get_config('general', 'pkg_name') syslog.openlog(pkg_name) # Create an object responsible for sending notifications to clients client_notifier = ClientNotifier(connection_pool) # Create a database local to this host database_filename = get_config('database', 'filename') database_filepath = make_database_filepath(database_filename) assure_file_ownership_permissions(database_filepath, 0o600, 'setroubleshoot') host_database = SETroubleshootDatabase(database_filepath, database_filename, friendly_name=_("Audit Listener")) host_database.set_notify(client_notifier) atexit.register(goodbye, host_database) deleted = False for i in host_database.sigs.signature_list: why, bools = audit2why.analyze(str(i.sig.scontext), str(i.sig.tcontext), str(i.sig.tclass), i.sig.access) if why == audit2why.ALLOW or why == audit2why.DONTAUDIT: if why == audit2why.ALLOW: reason = "allowed" else: reason = "dontaudit'd" syslog.syslog(syslog.LOG_ERR, "Deleting alert %s, it is %s in current policy" % (i.local_id, reason)) deleted = True host_database.delete_signature(i.sig) if deleted: host_database.save(prune=True) # Attach the local database to an object which will send alerts # specific to this host if not get_config('test', 'analyze', bool): alert_receiver = AlertPluginReportReceiver(host_database) else: alert_receiver = TestPluginReportReceiver(host_database) # Create a synchronized queue for analysis requests import six.moves.queue analysis_queue = six.moves.queue.Queue(0) # Create a thread to peform analysis, it takes AVC objects off # the analysis queue and runs the plugins against the # AVC. Analysis requests in the queue may arrive from a # variety of places; from the audit system, from a log file # scan, etc. The disposition of the analysis (e.g. where the # results of the analysis are to go) are included in the queued # object along with the data to analyze. analyze_thread = AnalyzeThread(analysis_queue) analyze_thread.setDaemon(True) analyze_thread.start() # Create a thread to receive messages from the audit system. # This is a time sensitive operation, the primary job of this # thread is to receive the audit message as quickly as # possible and return to listening on the audit socket. When # it receives a complete audit event it places it in the # analysis queue where another thread will process it # independently. # audit_socket_thread = AuditSocketReceiverThread(analysis_queue, alert_receiver) # audit_socket_thread.setDaemon(True) # audit_socket_thread.start() # Initialize the email recipient list from setroubleshoot.signature import SEEmailRecipientSet email_recipients = SEEmailRecipientSet() assure_file_ownership_permissions(email_recipients_filepath, 0o600, 'setroubleshoot') try: email_recipients.parse_recipient_file(email_recipients_filepath) except ProgramError as e: if e.errno == ERR_FILE_OPEN: log_debug(e.strerror) else: raise e # Create a server to listen for alert clients and then run. listen_addresses = get_socket_list_from_config('listen_for_client') for listen_address in listen_addresses: listening_server = ListeningServer(listen_address, SetroubleshootdClientConnectionHandler) listening_server.open() dbus.glib.init_threads() setroubleshootd_dbus = SetroubleshootdDBus(analysis_queue, alert_receiver, timeout) main_loop = GLib.MainLoop() main_loop.run() except KeyboardInterrupt as e: log_debug("KeyboardInterrupt in RunFaultServer") except SystemExit as e: log_debug("raising SystemExit in RunFaultServer") except Exception as e: import traceback syslog_trace(traceback.format_exc()) syslog.syslog(syslog.LOG_ERR, "exception %s: %s" % (e.__class__.__name__, str(e)))