Linux-audit Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] audit.h: remove the macro AUDIT_ARCH_ARMEB definition
From: Li RongQing @ 2015-03-23  0:55 UTC (permalink / raw)
  To: Paul Moore
  Cc: Eric Paris, linux-audit-H+wXaHxf7aLQT0dZR+AlfA,
	linux-api-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <CAJFZqHxdPEhX+9z-FYUMvTF_6LVgK=gOetq0zT4UTZSgUGRqCQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Mon, Mar 23, 2015 at 8:51 AM, Li RongQing <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Fri, Mar 20, 2015 at 9:29 PM, Paul Moore <paul-r2n+y4ga6xFZroRs9YW3xA@public.gmane.org> wrote:
>> On Fri, Mar 20, 2015 at 12:55 AM,  <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>>> From: Li RongQing <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>>
>>> After 2f9783669 [ARM: 7412/1: audit: use only AUDIT_ARCH_ARM regardless
>>> of endianness], no kernel user uses this macro;
>>>
>>> Keeping this macro, only makes the compiling old version audit [before
>>> changeset 931 Improve ARM and AARCH64 support] success, but the audit
>>> program can not work with the kernel after 2f9783669 still,
>>> since no syscall entry is enabled for AUDIT_ARCH_ARMEB in kernel.
>>>
>>> so remove it to force to use the latest audit program
>>>
>>> Signed-off-by: Li RongQing <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>> ---
>>> other workaround is to define AUDIT_ARCH_ARMEB as AUDIT_ARCH_ARM,
>>> but it seems very strange
>>>
>>>  include/uapi/linux/audit.h | 1 -
>>>  1 file changed, 1 deletion(-)
>>
>> Since this #define lives in the user visible headers I don't want to
>> remove it and risk causing a userspace breakage.  Leaving the #define
>> in the header, even if it is


it is harm, when I compile the audit-2.3.2 for a arm machine, whose linux kernel
is 3.14; no compile error, but audit does not work;  spend one day debug to find
the root cause is  the audit used MACH_ARMEB, but kernel replaced MACH_ARMEB
 with MACH_ARM

 grep WITH_ARMEB ./lib/machinetab.h -A10
#ifdef WITH_ARMEB
_S(MACH_ARMEB,   "armeb"  )
_S(MACH_ARMEB,   "armv5tejl")
_S(MACH_ARMEB,   "armv5tel")
_S(MACH_ARMEB,   "armv6l")
_S(MACH_ARMEB,   "armv7l")
#endif

removal of MACH_ARMEB will let the user find this issue when compile, not
run.

-Roy

^ permalink raw reply

* Re: [PATCH] audit.h: remove the macro AUDIT_ARCH_ARMEB definition
From: Paul Moore @ 2015-03-24 21:16 UTC (permalink / raw)
  To: Li RongQing
  Cc: Eric Paris, linux-audit-H+wXaHxf7aLQT0dZR+AlfA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <CAJFZqHxfq0DSjKn-uqhT+pAeJ9u8ym2Mr=wsqvPyX7sCicPGZA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Sun, Mar 22, 2015 at 8:55 PM, Li RongQing <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Mon, Mar 23, 2015 at 8:51 AM, Li RongQing <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> On Fri, Mar 20, 2015 at 9:29 PM, Paul Moore <paul-r2n+y4ga6xFZroRs9YW3xA@public.gmane.org> wrote:
>>> On Fri, Mar 20, 2015 at 12:55 AM,  <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>>>> From: Li RongQing <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>>>
>>>> After 2f9783669 [ARM: 7412/1: audit: use only AUDIT_ARCH_ARM regardless
>>>> of endianness], no kernel user uses this macro;
>>>>
>>>> Keeping this macro, only makes the compiling old version audit [before
>>>> changeset 931 Improve ARM and AARCH64 support] success, but the audit
>>>> program can not work with the kernel after 2f9783669 still,
>>>> since no syscall entry is enabled for AUDIT_ARCH_ARMEB in kernel.
>>>>
>>>> so remove it to force to use the latest audit program
>>>>
>>>> Signed-off-by: Li RongQing <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>>> ---
>>>> other workaround is to define AUDIT_ARCH_ARMEB as AUDIT_ARCH_ARM,
>>>> but it seems very strange
>>>>
>>>>  include/uapi/linux/audit.h | 1 -
>>>>  1 file changed, 1 deletion(-)
>>>
>>> Since this #define lives in the user visible headers I don't want to
>>> remove it and risk causing a userspace breakage.  Leaving the #define
>>> in the header, even if it is
>
>
> it is harm, when I compile the audit-2.3.2 for a arm machine, whose linux kernel
> is 3.14; no compile error, but audit does not work;  spend one day debug to find
> the root cause is  the audit used MACH_ARMEB, but kernel replaced MACH_ARMEB
>  with MACH_ARM
>
>  grep WITH_ARMEB ./lib/machinetab.h -A10
> #ifdef WITH_ARMEB
> _S(MACH_ARMEB,   "armeb"  )
> _S(MACH_ARMEB,   "armv5tejl")
> _S(MACH_ARMEB,   "armv5tel")
> _S(MACH_ARMEB,   "armv6l")
> _S(MACH_ARMEB,   "armv7l")
> #endif
>
> removal of MACH_ARMEB will let the user find this issue when compile, not
> run.

[NOTE: Adding the linux-arm folks to this thread just in case they are
interested.]

I'm not an ARM expert, but the kernel #define for AUDIT_ARCH_ARMEB
(below) seems reasonable to me based on the other audit machine
definitions.  There is clearly a mismatch between the kernel and the
audit userspace, but I wonder if the issue should be addressed in the
userspace tools.  Further, you mention kernel 3.14, what happens with
kernel 3.19 and the most recent audit userspace tools?

  > grep "AUDIT_ARCH_ARMEB" include/uapi/linux/audit.h
  #define AUDIT_ARCH_ARMEB        (EM_ARM)

-Paul

-- 
paul moore
www.paul-moore.com

^ permalink raw reply

* Re: [PATCH] audit.h: remove the macro AUDIT_ARCH_ARMEB definition
From: Li RongQing @ 2015-03-25  0:01 UTC (permalink / raw)
  To: Paul Moore
  Cc: Eric Paris, linux-audit-H+wXaHxf7aLQT0dZR+AlfA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <CAHC9VhTbxf_u2FXkcdqofkhywbgrrjjUo_W7yqWXFm=wSJBOAQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Wed, Mar 25, 2015 at 5:16 AM, Paul Moore <paul-r2n+y4ga6xFZroRs9YW3xA@public.gmane.org> wrote:
> [NOTE: Adding the linux-arm folks to this thread just in case they are
> interested.]
>
> I'm not an ARM expert, but the kernel #define for AUDIT_ARCH_ARMEB
> (below) seems reasonable to me based on the other audit machine
> definitions.  There is clearly a mismatch between the kernel and the
> audit userspace, but I wonder if the issue should be addressed in the
> userspace tools.  Further, you mention kernel 3.14, what happens with
> kernel 3.19 and the most recent audit userspace tools?
>
>   > grep "AUDIT_ARCH_ARMEB" include/uapi/linux/audit.h
>   #define AUDIT_ARCH_ARMEB        (EM_ARM)


1.
if no the below commit, audit-2.3.2 is working;
but after the below commit, audit-2.3.2 is not working.

commit 2f978366984a418f38fcf44137be1fbc5a89cfd9
Author: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
Date:   Fri May 4 17:53:52 2012 +0100

    ARM: 7412/1: audit: use only AUDIT_ARCH_ARM regardless of endianness

    The machine endianness has no direct correspondence to the syscall ABI,
    so use only AUDIT_ARCH_ARM when identifying the ABI to the audit tools
    in userspace.

    Cc: stable-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
    Signed-off-by: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
    Signed-off-by: Russell King <rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>

diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index d8dbe9c..9650c14 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -906,12 +906,6 @@ long arch_ptrace(struct task_struct *child, long request,
        return ret;
 }

-#ifdef __ARMEB__
-#define AUDIT_ARCH_NR AUDIT_ARCH_ARMEB
-#else
-#define AUDIT_ARCH_NR AUDIT_ARCH_ARM
-#endif
-
 asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
 {
        unsigned long ip;
@@ -919,7 +913,7 @@ asmlinkage int syscall_trace(int why, struct
pt_regs *regs, int scno)
        if (why)
                audit_syscall_exit(regs);
        else
-               audit_syscall_entry(AUDIT_ARCH_NR, scno, regs->ARM_r0,
+               audit_syscall_entry(AUDIT_ARCH_ARM, scno, regs->ARM_r0,
                                    regs->ARM_r1, regs->ARM_r2, regs->ARM_r3);

        if (!test_thread_flag(TIF_SYSCALL_TRACE))


2.

The latest audit has the changeset of 931 [Improve ARM and AARCH64 support],
so audit can work again.


-Roy

^ permalink raw reply related

* Re: [PATCH] log rendering in real time in audit-viewer
From: Xeniya Muratova @ 2015-03-25 13:21 UTC (permalink / raw)
  To: mitr, linux-audit
In-Reply-To: <1384025748.230407.1427289658707.JavaMail.zimbra@itsirius.su>

This code extends audit-viewer _ParserEventSource to produce UpdatableEventSource (unprivileged).
I have not added this option to SourceDialog, so to see how updating works command line call may be used (python main.py -p -s /path/to/log)

diff -u -X ex or_src/event_source.py oav/src/event_source.py
--- or_src/event_source.py	2009-06-09 23:10:41.000000000 +0400
+++ oav/src/event_source.py	2015-03-25 16:09:59.933965077 +0300
@@ -19,7 +19,7 @@
 import datetime
 import os.path
 import re
-
+import os
 import auparse
 
 __all__ = ('ClientEventSource', 'ClientWithRotatedEventSource',
@@ -95,7 +95,7 @@
     '''A source of audit events, reading from an auparse parser.'''
 
     def read_events(self, filters, wanted_fields, want_other_fields,
-                    keep_raw_records):
+                    keep_raw_records, direction = None, tab = None):
         '''Return a sequence of audit events read from parser.
 
         Use filters to select events.  Store wanted_fields in event.fields, the
@@ -265,6 +265,106 @@
     def _create_parser(self):
         return auparse.AuParser(auparse.AUSOURCE_BUFFER, self.str)
 
+class UpdatableEventSourceReader():
+
+    '''A separate reader of audit events, created for each tab of main window.
+    To be used with UpdatableEventSource.
+
+    '''
+
+    def __init__(self, event_source, tab):
+        self.event_source = event_source
+        self.tab = tab
+        self.chunk_size = event_source.chunk_size
+        self.up_file = open(event_source.base_file)
+        self.bottom_file = open(event_source.base_file)
+        self.up_file.seek(0, 2)
+        self.up_pos = self.down_pos = self.up_file.tell()
+
+    def read_down(self):
+        ''' read older lines from file starting from self.down_pos'''
+        if os.path.exists(self.bottom_file.name): 
+            if os.stat(self.bottom_file.name).st_ino != os.fstat(self.bottom_file.fileno()).st_ino:
+                self.bottom_file.close()
+                self.tab.want_read_down = False
+                return ''
+        if self.down_pos <= self.event_source.avg_line_length * self.chunk_size:
+            self.bottom_file.seek(self.down_pos)
+            lines = self.bottom_file.read(self.down_pos)
+            try:
+                files = os.listdir(os.path.dirname(self.event_source.base_file))
+                files = sorted(files, key = lambda x : os.stat(x).st_mtime)
+                filename = self.bottom_file.name
+                self.bottom_file.close()
+                self.bottom_file = open(files[files.index(filename) + 1])
+            except:
+                self.tab.want_read_down = False
+                return ''
+        else:
+            self.bottom_file.seek(self.down_pos - self.event_source.avg_line_length * self.chunk_size)
+            lines = self.bottom_file.read(self.event_source.avg_line_length * self.chunk_size)
+            lines = lines[lines.find('\n') + 1:]
+            self.down_pos -= len(lines)
+        return lines
+
+    def read_up(self):
+        '''try to read new lines if there any after the previous read'''
+        if os.path.exists(self.up_file.name): 
+            if os.stat(self.up_file.name).st_ino != os.fstat(self.up_file.fileno()).st_ino:
+                self.up_file.close()
+                self.up_file = open(self.event_source.base_file)
+                self.up_pos = 0
+        else:
+            self.up_file.close()
+            self.tab.want_read_up = False
+            return ''
+        self.up_file.seek(0, 2)
+        file_end_pos = self.up_file.tell()
+        if self.up_pos != file_end_pos:
+            self.up_file.seek(self.up_pos)
+            if file_end_pos - self.up_pos > self.event_source.avg_line_length*self.chunk_size*5:
+                lines = self.up_file.read(self.event_source.avg_line_length*self.chunk_size)
+                file_end_pos = self.up_file.tell()
+            else:
+                lines = self.up_file.read()
+            if lines.endswith('\n'):
+                self.up_pos = file_end_pos          
+            else:              
+                lines = lines[:lines.rfind('\n') + 1]
+                self.up_pos += len(lines)                   
+            return lines
+        return ''
+
+
+class UpdatableEventSource(_ParserEventSource):
+
+    def __init__(self, base_file, chunk_size = 50, avg_line_length = 74):
+        self.chunk_size = chunk_size
+        self.avg_line_length = avg_line_length
+        self.readers = {}
+        self.base_file = base_file
+        self.current_lines = ''
+
+    def add_tab(self, tab):
+        self.readers[tab] = UpdatableEventSourceReader(self, tab)
+
+    def remove_tab(self, tab):
+        del self.readers[tab]
+
+    def read_events(self, filters, wanted_fields, want_other_fields,
+                    keep_raw_records, direction, tab):
+        if direction == 'up':
+            self.current_lines = self.readers[tab].read_up()
+        else:
+            self.current_lines = self.readers[tab].read_down()
+        return _ParserEventSource.read_events(self, filters, wanted_fields, want_other_fields,
+                    keep_raw_records)
+
+
+    def _create_parser(self):
+        return auparse.AuParser(auparse.AUSOURCE_BUFFER, self.current_lines)
+
+
 def check_expression(expr):
     '''Check expr.
 
diff -u -X ex or_src/list_tab.py oav/src/list_tab.py
--- or_src/list_tab.py	2009-12-19 10:00:00.000000000 +0300
+++ oav/src/list_tab.py	2015-03-25 15:42:07.797941743 +0300
@@ -28,6 +28,7 @@
 from search_entry import SearchEntry
 from tab import Tab
 import util
+import event_source
 
 __all__ = ('ListTab')
 
@@ -130,7 +131,7 @@
     date_column_label = '__audit_viewer_date'
 
     __list_number = 1
-    def __init__(self, filters, main_window, will_refresh = False):
+    def __init__(self, filters, main_window, will_refresh = True):
         Tab.__init__(self, filters, main_window, 'list_vbox')
 
         # date_column_label == event date, None == all other columns
@@ -157,6 +158,13 @@
         util.connect_and_run(self.selection, 'changed',
                              self.__selection_changed)
 
+        if isinstance(self.main_window.event_source, event_source.UpdatableEventSource):
+            self.main_window.event_source.add_tab(self)
+            self.want_read_up = True
+            self.want_read_down = True
+            self.__refresh_dont_read_events = False
+            self.refresh(True, 'up', True)
+            return
         self.__refresh_dont_read_events = will_refresh
         self.refresh()
         self.__refresh_dont_read_events = False
@@ -191,20 +199,21 @@
                                      % (util.filename_to_utf8(filename),
                                         e.strerror))
 
-    def refresh(self):
-        event_sequence = self.__refresh_get_event_sequence()
+    def refresh(self, updatable  = False, direction = None, init = False):
+        event_sequence = self.__refresh_get_event_sequence(direction, self)
         if event_sequence is None:
             return
-
-        if self.filters:
-            t = _(', ').join(f.ui_text() for f in self.filters)
-        else:
-            t = _('None')
-        self.list_filter_label.set_text(t)
-        self.__refresh_update_tree_view()
+        if not updatable or init:
+            if self.filters:
+                t = _(', ').join(f.ui_text() for f in self.filters)
+            else:
+                t = _('None')
+            self.list_filter_label.set_text(t)
+            self.__refresh_update_tree_view()
 
         events = self.__refresh_collect_events(event_sequence)
-        self.__refresh_update_store(events)
+        self.__refresh_update_store(events, updatable, direction)
+
 
     def report_on_view(self):
         self.main_window.new_report_tab(self.filters)
@@ -462,7 +471,7 @@
                                        for record in event.records
                                        for (key, value) in record.fields]))
 
-    def __refresh_get_event_sequence(self):
+    def __refresh_get_event_sequence(self, direction = None, tab = None):
         '''Return an event sequence (as if from self.main_window.read_events()).
 
         Return None on error.
@@ -480,7 +489,7 @@
             elif title is not self.date_column_label:
                 wanted_fields.add(title)
         return self.main_window.read_events(self.filters, wanted_fields,
-                                            want_other_fields, True)
+                                            want_other_fields, True, direction, tab)
 
     def __refresh_update_tree_view(self):
         '''Update self.list_tree_view for current configuration.
@@ -560,7 +569,32 @@
         events.sort(key = lambda event: event[0], reverse = self.sort_reverse)
         return events
 
-    def __refresh_update_store(self, events):
+    def __insert_row(self, event, direction):
+        ''' insert new row with event into self.store preserving sort order'''
+        it = None
+        if not self.sort_by:
+            if self.sort_reverse:
+                if direction == 'up':
+                    it = self.store.insert(0, event[1])
+                else:
+                    it = self.store.append(event[1])
+            else:
+                if direction == 'down':
+                    it = self.store.insert(0, event[1])
+                else:
+                    it = self.store.append(event[1])
+        else:
+            sort_field = self.__field_columns.index(self.sort_by) + 1                                
+            for i in range(len(self.store)):
+                if event[1][sort_field] <= self.store[i][sort_field] and self.sort_reverse or \
+                    event[1][sort_field] > self.store[i][sort_field] and not self.sort_reverse:
+                    it = self.store.insert(i, event[1])
+                    break
+            if not it:
+                it = self.store.append(event[1])
+        return it
+
+    def __refresh_update_store(self, events, updatable = False, direction = None):
         '''Update self.store and related data.
 
         events is the result of self.__refresh_collect_events().
@@ -571,11 +605,15 @@
             key = pos.event_key
             l = positions_for_event_key.setdefault(key, [])
             l.append(pos)
-        self.store.clear()
+        if not updatable:
+            self.store.clear()
         if (self.text_filter is None and
             len(positions_for_event_key) == 0): # Fast path
             for event in events:
-                self.store.append(event[1])
+                if not updatable:
+                    self.store.append(event[1])
+                else:
+                    self.__insert_row(event, direction)
         else:
             event_to_it = {}
             text_filter = self.text_filter
@@ -604,7 +642,10 @@
                              or (self.__other_column_event_text(event_tuple[0]).
                                  find(self.text_filter) == -1))):
                             continue
-                it = self.store.append(event_tuple)
+                if not updatable:
+                    it = self.store.append(event_tuple)
+                else:
+                    it = self.__insert_row(event, direction)
                 event_id = event_tuple[0].id
                 key = (event_id.serial, event_id.sec, event_id.milli)
                 if key in positions_for_event_key:
diff -u -X ex or_src/main.py oav/src/main.py
--- or_src/main.py	2008-06-26 00:17:59.000000000 +0400
+++ oav/src/main.py	2015-03-25 15:31:29.663932132 +0300
@@ -29,6 +29,7 @@
 from main_window import MainWindow
 import settings
 import util
+import event_source
 
 _ = gettext.gettext
 
@@ -48,12 +49,20 @@
                       help = _('do not attempt to start the privileged backend '
                                'for reading system audit logs'))
     parser.set_defaults(unprivileged = False)
+    parser.add_option('-p', '--updatable', action = 'store_true',
+                      dest = 'updatable',
+                      help = _('read new lines from log '))
+    parser.set_defaults(updatable = False)
+    parser.add_option('-s', '--source', type = 'string',
+                      dest = 'source',
+                      help = _('path to log file '))
     (options, args) = parser.parse_args()
 
     gnome.init(settings.gettext_domain, settings.version)
     gtk.glade.bindtextdomain(settings.gettext_domain, settings.localedir)
     gtk.glade.textdomain(settings.gettext_domain)
 
+    ev_source  = None
     if options.unprivileged:
         cl = None
     else:
@@ -66,7 +75,9 @@
             sys.exit(1)
         except client.ClientNotAvailableError:
             cl = None
+    if options.updatable:
+        ev_source = event_source.UpdatableEventSource(options.source)
 
-    w = MainWindow(cl)
+    w = MainWindow(cl, ev_source)
     if w.setup_initial_window(args):
         gtk.main()
diff -u -X ex or_src/main_window.py oav/src/main_window.py
--- or_src/main_window.py	2008-08-19 14:38:16.000000000 +0400
+++ oav/src/main_window.py	2015-03-23 19:13:04.399266459 +0300
@@ -135,6 +135,8 @@
 
         '''
         try:
+            if isinstance(self.event_source, event_source.UpdatableEventSource):
+                self.updater = gobject.idle_add(self.__refresh_all_tabs, True)
             if isinstance(self.event_source, event_source.EmptyEventSource):
                 self.__event_error_report_only_one_push()
                 if self.client is not None:
@@ -246,7 +248,7 @@
         return (filename, extension)
 
     def read_events(self, filters, wanted_fields, want_other_fields,
-                    keep_raw_records):
+                    keep_raw_records, direction  = None, tab = None):
         '''Read audit events.
 
         Return a sequence of events, or None on error (without throwing
@@ -262,7 +264,7 @@
         try:
             return self.event_source.read_events(filters, wanted_fields,
                                                  want_other_fields,
-                                                 keep_raw_records)
+                                                 keep_raw_records, direction, tab)
         except IOError, e:
             if (self.__event_error_report_only_one_depth == 0 or
                 not self.__event_error_reported):
@@ -381,16 +383,24 @@
         '''End a region in which only one error message should be reported.'''
         self.__event_error_report_only_one_depth -= 1
 
-    def __refresh_all_tabs(self):
+    def __refresh_all_tabs(self, updatable = False):
         '''Refresh all tabs, taking care to report errors only once.'''
         self.__event_error_report_only_one_push()
         try:
-            for page_num in xrange(self.main_notebook.get_n_pages()):
-                tab = self.__tab_objects[self.main_notebook
-                                         .get_nth_page(page_num)]
-                tab.refresh()
+            if not updatable:
+                for page_num in xrange(self.main_notebook.get_n_pages()):
+                    tab = self.__tab_objects[self.main_notebook
+                                             .get_nth_page(page_num)]
+                    tab.refresh()
+            else:
+                for page_num in xrange(self.main_notebook.get_n_pages()):
+                    tab = self.__tab_objects[self.main_notebook
+                                             .get_nth_page(page_num)]
+                    tab.refresh(True, 'up')
+                    tab.refresh(True, 'down')
         finally:
             self.__event_error_report_only_one_pop()
+        return True
 
     def __menu_new_list_activate(self, *_):
         self.new_list_tab([])

----- Исходное сообщение -----
От: "mitr" <mitr@redhat.com>
Кому: "Xeniya Muratova" <muratova@itsirius.su>
Копия: "linux-audit" <linux-audit@redhat.com>
Отправленные: Среда, 4 Март 2015 г 20:50:53
Тема: Re: log rendering in real time in audit-viewer

Hello,
> Hello Miloslav, and all the guys!
> 
> We use audit-viewer for events monitoring.
> Unfortunately, if some log is rather big it takes to much time for
> audit-viewer to parse and render it.
> Besides, we need to render log updates in real time, i.e. when a new line
> appears in a log, it should appear in a viewer too.
> Can you suggest the better way to extend audit-viewer to meet these
> requirements?

Well, write the code?  Something like inotify could be useful.  There isn’t any hidden switch to enable these features, if that is what you are asking.

As for performance, I may have missed something but I think I have squeezed as much as can be done with Python; improving performance further would very likely require a C extension.

(audit-viewer is a PyGtk2 application, and at I’m afraid I don’t currently have plans to port it to GTK+3/gobject-introspection or do any other non-trivial work on the project, at least in the near term.)
     Mirek

--
Linux-audit mailing list
Linux-audit@redhat.com
https://www.redhat.com/mailman/listinfo/linux-audit

^ permalink raw reply

* Re: log rendering in real time in audit-viewer
From: Xeniya Muratova @ 2015-03-30 13:52 UTC (permalink / raw)
  To: mitr; +Cc: linux-audit
In-Reply-To: <192757425.240881.1427723539724.JavaMail.zimbra@itsirius.su>

I have fixed bugs in inserting new events, and also added real time rendering option in source dialog, and will be grateful for feedback.

diff -u -X ex or_src/audit-viewer.glade oav/src/audit-viewer.glade
--- or_src/audit-viewer.glade	2012-09-22 04:04:40.000000000 +0400
+++ oav/src/audit-viewer.glade	2015-03-30 12:13:06.810004999 +0300
@@ -2664,6 +2664,16 @@
               </widget>
             </child>
             <child>
+              <widget class="GtkCheckButton" id="source_with_updating">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">_Render updates in real time</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+                <property name="draw_indicator">True</property>
+              </widget>
+            </child>
+            <child>
               <widget class="GtkVBox" id="vbox25">
                 <property name="visible">True</property>
                 <child>

diff -u -X ex or_src/event_source.py oav/src/event_source.py
--- or_src/event_source.py	2009-06-09 23:10:41.000000000 +0400
+++ oav/src/event_source.py	2015-03-30 12:41:11.028004422 +0300
@@ -19,7 +19,7 @@
 import datetime
 import os.path
 import re
-
+import os
 import auparse
 
 __all__ = ('ClientEventSource', 'ClientWithRotatedEventSource',
@@ -95,7 +95,7 @@
     '''A source of audit events, reading from an auparse parser.'''
 
     def read_events(self, filters, wanted_fields, want_other_fields,
-                    keep_raw_records):
+                    keep_raw_records, direction = None, tab = None):
         '''Return a sequence of audit events read from parser.
 
         Use filters to select events.  Store wanted_fields in event.fields, the
@@ -265,6 +265,107 @@
     def _create_parser(self):
         return auparse.AuParser(auparse.AUSOURCE_BUFFER, self.str)
 
+class UpdatableEventSourceReader():
+
+    '''A separate reader of audit events, created for each tab of main window.
+    To be used with UpdatableEventSource.
+
+    '''
+
+    def __init__(self, event_source, tab):
+        self.event_source = event_source
+        self.tab = tab
+        self.up_file = open(event_source.base)
+        self.bottom_file = open(event_source.base)
+        self.up_file.seek(0, 2)
+        self.up_pos = self.down_pos = self.up_file.tell()
+        self.bytes = self.event_source.avg_line_length * self.event_source.chunk_size
+
+    def read_down(self):
+        ''' read older lines from file starting from self.down_pos'''       
+        if os.path.exists(self.bottom_file.name): 
+            if os.stat(self.bottom_file.name).st_ino != os.fstat(self.bottom_file.fileno()).st_ino:
+                self.bottom_file.close()
+                self.tab.want_read_down = False
+                return ''
+        if self.down_pos <= self.bytes:
+            self.bottom_file.seek(0)
+            lines = self.bottom_file.read(self.down_pos)
+            try:
+                files = os.listdir(os.path.dirname(self.event_source.base))
+                files = sorted(files, key = lambda x : os.stat(x).st_mtime)
+                filename = self.bottom_file.name
+                self.bottom_file.close()
+                self.bottom_file = open(files[files.index(filename) + 1])
+                self.bottom_file.seek(0, 2)
+                self.down_pos = self.bottom_file.tell()
+            except:
+                self.tab.want_read_down = False
+        else:
+            self.bottom_file.seek(self.down_pos - self.bytes)
+            lines = self.bottom_file.read(self.bytes)
+            lines = lines[lines.find('\n') + 1:]
+            self.down_pos -= len(lines)
+        return lines
+
+    def read_up(self):
+        '''try to read new lines if there are any after the previous reads'''
+        if os.path.exists(self.up_file.name): 
+            if os.stat(self.up_file.name).st_ino != os.fstat(self.up_file.fileno()).st_ino:
+                self.up_file.close()
+                self.up_file = open(self.event_source.base)
+                self.up_pos = 0
+        else:
+            self.up_file.close()
+            self.tab.want_read_up = False
+            return ''
+        self.up_file.seek(0, 2)
+        file_end_pos = self.up_file.tell()
+        if self.up_pos != file_end_pos:
+            self.up_file.seek(self.up_pos)
+            if file_end_pos - self.up_pos > self.bytes*5:
+                lines = self.up_file.read(self.bytes)
+                file_end_pos = self.up_file.tell()
+            else:
+                lines = self.up_file.read()
+            if lines.endswith('\n'):
+                self.up_pos = file_end_pos          
+            else:              
+                lines = lines[:lines.rfind('\n') + 1]
+                self.up_pos += len(lines)                   
+            return lines
+        return ''
+
+
+class UpdatableEventSource(_ParserEventSource):
+
+    def __init__(self, base_file, chunk_size = 50, avg_line_length = 74):
+        self.chunk_size = chunk_size
+        self.avg_line_length = avg_line_length
+        self.readers = {}
+        self.base = base_file
+        self.current_lines = ''
+
+    def add_tab(self, tab):
+        self.readers[tab] = UpdatableEventSourceReader(self, tab)
+
+    def remove_tab(self, tab):
+        del self.readers[tab]
+
+    def read_events(self, filters, wanted_fields, want_other_fields,
+                    keep_raw_records, direction, tab):
+        if direction == 'up':
+            self.current_lines = self.readers[tab].read_up()
+        else:
+            self.current_lines = self.readers[tab].read_down()
+        return _ParserEventSource.read_events(self, filters, wanted_fields, want_other_fields,
+                    keep_raw_records)
+
+
+    def _create_parser(self):
+        return auparse.AuParser(auparse.AUSOURCE_BUFFER, self.current_lines)
+
+
 def check_expression(expr):
     '''Check expr.
 
diff -u -X ex or_src/list_tab.py oav/src/list_tab.py
--- or_src/list_tab.py	2009-12-19 10:00:00.000000000 +0300
+++ oav/src/list_tab.py	2015-03-30 13:26:44.579005427 +0300
@@ -28,6 +28,7 @@
 from search_entry import SearchEntry
 from tab import Tab
 import util
+import event_source
 
 __all__ = ('ListTab')
 
@@ -130,7 +131,7 @@
     date_column_label = '__audit_viewer_date'
 
     __list_number = 1
-    def __init__(self, filters, main_window, will_refresh = False):
+    def __init__(self, filters, main_window, will_refresh = True):
         Tab.__init__(self, filters, main_window, 'list_vbox')
 
         # date_column_label == event date, None == all other columns
@@ -157,6 +158,13 @@
         util.connect_and_run(self.selection, 'changed',
                              self.__selection_changed)
 
+        if isinstance(self.main_window.event_source, event_source.UpdatableEventSource):
+            self.main_window.event_source.add_tab(self)
+            self.want_read_up = True
+            self.want_read_down = True
+            self.__refresh_dont_read_events = False
+            self.refresh(False, 'up')
+            return
         self.__refresh_dont_read_events = will_refresh
         self.refresh()
         self.__refresh_dont_read_events = False
@@ -191,20 +199,25 @@
                                      % (util.filename_to_utf8(filename),
                                         e.strerror))
 
-    def refresh(self):
-        event_sequence = self.__refresh_get_event_sequence()
+    def refresh(self, updating  = False, direction = None):
+        if isinstance(self.main_window.event_source, event_source.UpdatableEventSource) and not updating:
+            self.want_read_down = True
+            self.want_read_up = True
+            self.main_window.event_source.readers[self] = event_source.UpdatableEventSourceReader(self.main_window.event_source, self)
+        event_sequence = self.__refresh_get_event_sequence(direction, self)
         if event_sequence is None:
             return
-
-        if self.filters:
-            t = _(', ').join(f.ui_text() for f in self.filters)
-        else:
-            t = _('None')
-        self.list_filter_label.set_text(t)
-        self.__refresh_update_tree_view()
+        if not updating:
+            if self.filters:
+                t = _(', ').join(f.ui_text() for f in self.filters)
+            else:
+                t = _('None')
+            self.list_filter_label.set_text(t)
+            self.__refresh_update_tree_view()
 
         events = self.__refresh_collect_events(event_sequence)
-        self.__refresh_update_store(events)
+        self.__refresh_update_store(events, updating)
+
 
     def report_on_view(self):
         self.main_window.new_report_tab(self.filters)
@@ -462,7 +475,7 @@
                                        for record in event.records
                                        for (key, value) in record.fields]))
 
-    def __refresh_get_event_sequence(self):
+    def __refresh_get_event_sequence(self, direction = None, tab = None):
         '''Return an event sequence (as if from self.main_window.read_events()).
 
         Return None on error.
@@ -480,7 +493,7 @@
             elif title is not self.date_column_label:
                 wanted_fields.add(title)
         return self.main_window.read_events(self.filters, wanted_fields,
-                                            want_other_fields, True)
+                                            want_other_fields, True, direction, tab)
 
     def __refresh_update_tree_view(self):
         '''Update self.list_tree_view for current configuration.
@@ -560,7 +573,32 @@
         events.sort(key = lambda event: event[0], reverse = self.sort_reverse)
         return events
 
-    def __refresh_update_store(self, events):
+    def __insert_row(self, event):
+        ''' insert new row with event into self.store preserving sort order'''
+        it = None
+        if not isinstance(self.main_window.event_source, event_source.UpdatableEventSource):
+            it = self.store.append(event[1])
+            return it
+        if self.sort_by and self.sort_by not in self.columns:
+            self.sort_by = None
+        if self.sort_by:
+            sort_field = self.__field_columns.index(self.sort_by) + 1
+            for i in range(len(self.store)):
+                if event[0] > self.store[i][sort_field] and self.sort_reverse or \
+                    event[0] <= self.store[i][sort_field] and not self.sort_reverse:
+                    it = self.store.insert(i, event[1])
+                    break
+        else:
+            for i in range(len(self.store)):
+                if event[1][0].id.sec > self.store[i][0].id.sec and self.sort_reverse or \
+                    event[1][0].id.sec <= self.store[i][0].id.sec and not self.sort_reverse:
+                    it = self.store.insert(i, event[1])
+                    break
+        if not it:
+            it = self.store.append(event[1])
+        return it
+
+    def __refresh_update_store(self, events, updating = False):
         '''Update self.store and related data.
 
         events is the result of self.__refresh_collect_events().
@@ -571,11 +609,12 @@
             key = pos.event_key
             l = positions_for_event_key.setdefault(key, [])
             l.append(pos)
-        self.store.clear()
+        if not updating:
+            self.store.clear()
         if (self.text_filter is None and
             len(positions_for_event_key) == 0): # Fast path
             for event in events:
-                self.store.append(event[1])
+                self.__insert_row(event)
         else:
             event_to_it = {}
             text_filter = self.text_filter
@@ -604,7 +643,8 @@
                              or (self.__other_column_event_text(event_tuple[0]).
                                  find(self.text_filter) == -1))):
                             continue
-                it = self.store.append(event_tuple)
+
+                it = self.__insert_row(event)
                 event_id = event_tuple[0].id
                 key = (event_id.serial, event_id.sec, event_id.milli)
                 if key in positions_for_event_key:
diff -u -X ex or_src/main.py oav/src/main.py
--- or_src/main.py	2008-06-26 00:17:59.000000000 +0400
+++ oav/src/main.py	2015-03-25 15:31:29.663932132 +0300
@@ -29,6 +29,7 @@
 from main_window import MainWindow
 import settings
 import util
+import event_source
 
 _ = gettext.gettext
 
@@ -48,12 +49,20 @@
                       help = _('do not attempt to start the privileged backend '
                                'for reading system audit logs'))
     parser.set_defaults(unprivileged = False)
+    parser.add_option('-p', '--updatable', action = 'store_true',
+                      dest = 'updatable',
+                      help = _('read new lines from log '))
+    parser.set_defaults(updatable = False)
+    parser.add_option('-s', '--source', type = 'string',
+                      dest = 'source',
+                      help = _('path to log file '))
     (options, args) = parser.parse_args()
 
     gnome.init(settings.gettext_domain, settings.version)
     gtk.glade.bindtextdomain(settings.gettext_domain, settings.localedir)
     gtk.glade.textdomain(settings.gettext_domain)
 
+    ev_source  = None
     if options.unprivileged:
         cl = None
     else:
@@ -66,7 +75,9 @@
             sys.exit(1)
         except client.ClientNotAvailableError:
             cl = None
+    if options.updatable:
+        ev_source = event_source.UpdatableEventSource(options.source)
 
-    w = MainWindow(cl)
+    w = MainWindow(cl, ev_source)
     if w.setup_initial_window(args):
         gtk.main()
diff -u -X ex or_src/main_window.py oav/src/main_window.py
--- or_src/main_window.py	2008-08-19 14:38:16.000000000 +0400
+++ oav/src/main_window.py	2015-03-30 13:21:25.607005136 +0300
@@ -135,6 +135,8 @@
 
         '''
         try:
+            if isinstance(self.event_source, event_source.UpdatableEventSource):
+                self.updater = gobject.idle_add(self.update_tabs)
             if isinstance(self.event_source, event_source.EmptyEventSource):
                 self.__event_error_report_only_one_push()
                 if self.client is not None:
@@ -246,7 +248,7 @@
         return (filename, extension)
 
     def read_events(self, filters, wanted_fields, want_other_fields,
-                    keep_raw_records):
+                    keep_raw_records, direction  = None, tab = None):
         '''Read audit events.
 
         Return a sequence of events, or None on error (without throwing
@@ -262,7 +264,7 @@
         try:
             return self.event_source.read_events(filters, wanted_fields,
                                                  want_other_fields,
-                                                 keep_raw_records)
+                                                 keep_raw_records, direction, tab)
         except IOError, e:
             if (self.__event_error_report_only_one_depth == 0 or
                 not self.__event_error_reported):
@@ -381,16 +383,30 @@
         '''End a region in which only one error message should be reported.'''
         self.__event_error_report_only_one_depth -= 1
 
-    def __refresh_all_tabs(self):
+    def update_tabs(self):
+        self.__refresh_all_tabs(True)
+        return True
+
+    def __refresh_all_tabs(self, updating = False):
         '''Refresh all tabs, taking care to report errors only once.'''
         self.__event_error_report_only_one_push()
         try:
-            for page_num in xrange(self.main_notebook.get_n_pages()):
-                tab = self.__tab_objects[self.main_notebook
-                                         .get_nth_page(page_num)]
-                tab.refresh()
+            if not updating:
+                for page_num in xrange(self.main_notebook.get_n_pages()):
+                    tab = self.__tab_objects[self.main_notebook
+                                             .get_nth_page(page_num)]
+                    tab.refresh()
+            else:
+                for page_num in xrange(self.main_notebook.get_n_pages()):
+                    tab = self.__tab_objects[self.main_notebook
+                                             .get_nth_page(page_num)]
+                    if tab.want_read_up:
+                        tab.refresh(True, 'up')
+                    if tab.want_read_down:
+                        tab.refresh(True, 'down')
         finally:
             self.__event_error_report_only_one_pop()
+        return True
 
     def __menu_new_list_activate(self, *_):
         self.new_list_tab([])
Only in oav/src/: patch
diff -u -X ex or_src/source_dialog.py oav/src/source_dialog.py
--- or_src/source_dialog.py	2009-06-09 22:35:26.000000000 +0400
+++ oav/src/source_dialog.py	2015-03-30 12:46:15.983005127 +0300
@@ -39,7 +39,8 @@
                            'source_path', 'source_path_browse',
                            'source_path_label',
                            'source_type_file', 'source_type_log',
-                           'source_with_rotated')
+                           'source_with_rotated',
+                           'source_with_updating')
 
     def __init__(self, parent, client):
         DialogBase.__init__(self, 'source_dialog', parent)
@@ -58,6 +59,8 @@
                              self.__source_type_log_toggled)
         util.connect_and_run(self.source_type_file, 'toggled',
                              self.__source_type_file_toggled)
+        util.connect_and_run(self.source_with_updating, 'toggled',
+                             self.__source_with_updating_toggled)
         self._setup_browse_button(self.source_path_browse, self.source_path,
                                   _('Audit Log File'),
                                   gtk.FILE_CHOOSER_ACTION_OPEN)
@@ -102,13 +105,20 @@
             self.source_with_rotated.set_active(False)
             self.source_type_file.set_active(True)
             self.source_path.set_text(source.path)
+        elif isinstance(source, event_source.FileWithRotatedEventSource):
+            self.source_with_rotated.set_active(True)
+            self.source_type_file.set_active(True)
+            self.source_path.set_text(source.base)
         else:
             assert isinstance(source,
-                              event_source.FileWithRotatedEventSource), \
+                              event_source.UpdatableEventSource), \
                 'Unexpected event source'
             self.source_with_rotated.set_active(True)
+            self.source_with_rotated.set_sensitive(False)
+            self.source_type_log.set_sensitive(False)
             self.source_type_file.set_active(True)
             self.source_path.set_text(source.base)
+            self.source_with_updating.set_active(True)
 
     def save(self, main_window):
         '''Modify main_window to reflect dialog state.'''
@@ -124,7 +134,10 @@
                 source = event_source.ClientEventSource(self.client, name)
         else:
             path = self.source_path.get_text()
-            if self.source_with_rotated.get_active():
+            if self.source_with_updating.get_active():
+                source = event_source.UpdatableEventSource(path)
+                main_window.updater = gobject.idle_add(main_window.update_tabs)
+            elif self.source_with_rotated.get_active():
                 source = event_source.FileWithRotatedEventSource(path)
             else:
                 source = event_source.FileEventSource(path)
@@ -175,11 +188,21 @@
                 if it is not None:
                     self.source_log.set_active_iter(it)
 
+    def __source_with_updating_toggled(self, *_):
+        is_set =  self.source_with_updating.get_active()
+        self.source_with_rotated.set_sensitive(not is_set)
+        self.source_type_log.set_sensitive(not is_set)
+        if is_set:
+            self.source_type_file.set_active(True)
+            self.source_type_log.set_active(False)
+            self.source_with_rotated.set_active(True)
+
     def __source_type_file_toggled(self, *_):
         util.set_sensitive_all(self.source_type_file.get_active(),
                                self.source_path_label, self.source_path,
                                self.source_path_browse)
 
+
     def __window_destroy(self, *_):
         self.emit('destroy')
         return False


diff -u -X ex or_src/event_source.py oav/src/event_source.py
--- or_src/event_source.py	2009-06-09 23:10:41.000000000 +0400
+++ oav/src/event_source.py	2015-03-30 12:41:11.028004422 +0300
@@ -19,7 +19,7 @@
 import datetime
 import os.path
 import re
-
+import os
 import auparse
 
 __all__ = ('ClientEventSource', 'ClientWithRotatedEventSource',
@@ -95,7 +95,7 @@
     '''A source of audit events, reading from an auparse parser.'''
 
     def read_events(self, filters, wanted_fields, want_other_fields,
-                    keep_raw_records):
+                    keep_raw_records, direction = None, tab = None):
         '''Return a sequence of audit events read from parser.
 
         Use filters to select events.  Store wanted_fields in event.fields, the
@@ -265,6 +265,107 @@
     def _create_parser(self):
         return auparse.AuParser(auparse.AUSOURCE_BUFFER, self.str)
 
+class UpdatableEventSourceReader():
+
+    '''A separate reader of audit events, created for each tab of main window.
+    To be used with UpdatableEventSource.
+
+    '''
+
+    def __init__(self, event_source, tab):
+        self.event_source = event_source
+        self.tab = tab
+        self.up_file = open(event_source.base)
+        self.bottom_file = open(event_source.base)
+        self.up_file.seek(0, 2)
+        self.up_pos = self.down_pos = self.up_file.tell()
+        self.bytes = self.event_source.avg_line_length * self.event_source.chunk_size
+
+    def read_down(self):
+        ''' read older lines from file starting from self.down_pos'''       
+        if os.path.exists(self.bottom_file.name): 
+            if os.stat(self.bottom_file.name).st_ino != os.fstat(self.bottom_file.fileno()).st_ino:
+                self.bottom_file.close()
+                self.tab.want_read_down = False
+                return ''
+        if self.down_pos <= self.bytes:
+            self.bottom_file.seek(0)
+            lines = self.bottom_file.read(self.down_pos)
+            try:
+                files = os.listdir(os.path.dirname(self.event_source.base))
+                files = sorted(files, key = lambda x : os.stat(x).st_mtime)
+                filename = self.bottom_file.name
+                self.bottom_file.close()
+                self.bottom_file = open(files[files.index(filename) + 1])
+                self.bottom_file.seek(0, 2)
+                self.down_pos = self.bottom_file.tell()
+            except:
+                self.tab.want_read_down = False
+        else:
+            self.bottom_file.seek(self.down_pos - self.bytes)
+            lines = self.bottom_file.read(self.bytes)
+            lines = lines[lines.find('\n') + 1:]
+            self.down_pos -= len(lines)
+        return lines
+
+    def read_up(self):
+        '''try to read new lines if there are any after the previous reads'''
+        if os.path.exists(self.up_file.name): 
+            if os.stat(self.up_file.name).st_ino != os.fstat(self.up_file.fileno()).st_ino:
+                self.up_file.close()
+                self.up_file = open(self.event_source.base)
+                self.up_pos = 0
+        else:
+            self.up_file.close()
+            self.tab.want_read_up = False
+            return ''
+        self.up_file.seek(0, 2)
+        file_end_pos = self.up_file.tell()
+        if self.up_pos != file_end_pos:
+            self.up_file.seek(self.up_pos)
+            if file_end_pos - self.up_pos > self.bytes*5:
+                lines = self.up_file.read(self.bytes)
+                file_end_pos = self.up_file.tell()
+            else:
+                lines = self.up_file.read()
+            if lines.endswith('\n'):
+                self.up_pos = file_end_pos          
+            else:              
+                lines = lines[:lines.rfind('\n') + 1]
+                self.up_pos += len(lines)                   
+            return lines
+        return ''
+
+
+class UpdatableEventSource(_ParserEventSource):
+
+    def __init__(self, base_file, chunk_size = 50, avg_line_length = 74):
+        self.chunk_size = chunk_size
+        self.avg_line_length = avg_line_length
+        self.readers = {}
+        self.base = base_file
+        self.current_lines = ''
+
+    def add_tab(self, tab):
+        self.readers[tab] = UpdatableEventSourceReader(self, tab)
+
+    def remove_tab(self, tab):
+        del self.readers[tab]
+
+    def read_events(self, filters, wanted_fields, want_other_fields,
+                    keep_raw_records, direction, tab):
+        if direction == 'up':
+            self.current_lines = self.readers[tab].read_up()
+        else:
+            self.current_lines = self.readers[tab].read_down()
+        return _ParserEventSource.read_events(self, filters, wanted_fields, want_other_fields,
+                    keep_raw_records)
+
+
+    def _create_parser(self):
+        return auparse.AuParser(auparse.AUSOURCE_BUFFER, self.current_lines)
+
+
 def check_expression(expr):
     '''Check expr.
 
diff -u -X ex or_src/list_tab.py oav/src/list_tab.py
--- or_src/list_tab.py	2009-12-19 10:00:00.000000000 +0300
+++ oav/src/list_tab.py	2015-03-30 13:26:44.579005427 +0300
@@ -28,6 +28,7 @@
 from search_entry import SearchEntry
 from tab import Tab
 import util
+import event_source
 
 __all__ = ('ListTab')
 
@@ -130,7 +131,7 @@
     date_column_label = '__audit_viewer_date'
 
     __list_number = 1
-    def __init__(self, filters, main_window, will_refresh = False):
+    def __init__(self, filters, main_window, will_refresh = True):
         Tab.__init__(self, filters, main_window, 'list_vbox')
 
         # date_column_label == event date, None == all other columns
@@ -157,6 +158,13 @@
         util.connect_and_run(self.selection, 'changed',
                              self.__selection_changed)
 
+        if isinstance(self.main_window.event_source, event_source.UpdatableEventSource):
+            self.main_window.event_source.add_tab(self)
+            self.want_read_up = True
+            self.want_read_down = True
+            self.__refresh_dont_read_events = False
+            self.refresh(False, 'up')
+            return
         self.__refresh_dont_read_events = will_refresh
         self.refresh()
         self.__refresh_dont_read_events = False
@@ -191,20 +199,25 @@
                                      % (util.filename_to_utf8(filename),
                                         e.strerror))
 
-    def refresh(self):
-        event_sequence = self.__refresh_get_event_sequence()
+    def refresh(self, updating  = False, direction = None):
+        if isinstance(self.main_window.event_source, event_source.UpdatableEventSource) and not updating:
+            self.want_read_down = True
+            self.want_read_up = True
+            self.main_window.event_source.readers[self] = event_source.UpdatableEventSourceReader(self.main_window.event_source, self)
+        event_sequence = self.__refresh_get_event_sequence(direction, self)
         if event_sequence is None:
             return
-
-        if self.filters:
-            t = _(', ').join(f.ui_text() for f in self.filters)
-        else:
-            t = _('None')
-        self.list_filter_label.set_text(t)
-        self.__refresh_update_tree_view()
+        if not updating:
+            if self.filters:
+                t = _(', ').join(f.ui_text() for f in self.filters)
+            else:
+                t = _('None')
+            self.list_filter_label.set_text(t)
+            self.__refresh_update_tree_view()
 
         events = self.__refresh_collect_events(event_sequence)
-        self.__refresh_update_store(events)
+        self.__refresh_update_store(events, updating)
+
 
     def report_on_view(self):
         self.main_window.new_report_tab(self.filters)
@@ -462,7 +475,7 @@
                                        for record in event.records
                                        for (key, value) in record.fields]))
 
-    def __refresh_get_event_sequence(self):
+    def __refresh_get_event_sequence(self, direction = None, tab = None):
         '''Return an event sequence (as if from self.main_window.read_events()).
 
         Return None on error.
@@ -480,7 +493,7 @@
             elif title is not self.date_column_label:
                 wanted_fields.add(title)
         return self.main_window.read_events(self.filters, wanted_fields,
-                                            want_other_fields, True)
+                                            want_other_fields, True, direction, tab)
 
     def __refresh_update_tree_view(self):
         '''Update self.list_tree_view for current configuration.
@@ -560,7 +573,32 @@
         events.sort(key = lambda event: event[0], reverse = self.sort_reverse)
         return events
 
-    def __refresh_update_store(self, events):
+    def __insert_row(self, event):
+        ''' insert new row with event into self.store preserving sort order'''
+        it = None
+        if not isinstance(self.main_window.event_source, event_source.UpdatableEventSource):
+            it = self.store.append(event[1])
+            return it
+        if self.sort_by and self.sort_by not in self.columns:
+            self.sort_by = None
+        if self.sort_by:
+            sort_field = self.__field_columns.index(self.sort_by) + 1
+            for i in range(len(self.store)):
+                if event[0] > self.store[i][sort_field] and self.sort_reverse or \
+                    event[0] <= self.store[i][sort_field] and not self.sort_reverse:
+                    it = self.store.insert(i, event[1])
+                    break
+        else:
+            for i in range(len(self.store)):
+                if event[1][0].id.sec > self.store[i][0].id.sec and self.sort_reverse or \
+                    event[1][0].id.sec <= self.store[i][0].id.sec and not self.sort_reverse:
+                    it = self.store.insert(i, event[1])
+                    break
+        if not it:
+            it = self.store.append(event[1])
+        return it
+
+    def __refresh_update_store(self, events, updating = False):
         '''Update self.store and related data.
 
         events is the result of self.__refresh_collect_events().
@@ -571,11 +609,12 @@
             key = pos.event_key
             l = positions_for_event_key.setdefault(key, [])
             l.append(pos)
-        self.store.clear()
+        if not updating:
+            self.store.clear()
         if (self.text_filter is None and
             len(positions_for_event_key) == 0): # Fast path
             for event in events:
-                self.store.append(event[1])
+                self.__insert_row(event)
         else:
             event_to_it = {}
             text_filter = self.text_filter
@@ -604,7 +643,8 @@
                              or (self.__other_column_event_text(event_tuple[0]).
                                  find(self.text_filter) == -1))):
                             continue
-                it = self.store.append(event_tuple)
+
+                it = self.__insert_row(event)
                 event_id = event_tuple[0].id
                 key = (event_id.serial, event_id.sec, event_id.milli)
                 if key in positions_for_event_key:
diff -u -X ex or_src/main.py oav/src/main.py
--- or_src/main.py	2008-06-26 00:17:59.000000000 +0400
+++ oav/src/main.py	2015-03-25 15:31:29.663932132 +0300
@@ -29,6 +29,7 @@
 from main_window import MainWindow
 import settings
 import util
+import event_source
 
 _ = gettext.gettext
 
@@ -48,12 +49,20 @@
                       help = _('do not attempt to start the privileged backend '
                                'for reading system audit logs'))
     parser.set_defaults(unprivileged = False)
+    parser.add_option('-p', '--updatable', action = 'store_true',
+                      dest = 'updatable',
+                      help = _('read new lines from log '))
+    parser.set_defaults(updatable = False)
+    parser.add_option('-s', '--source', type = 'string',
+                      dest = 'source',
+                      help = _('path to log file '))
     (options, args) = parser.parse_args()
 
     gnome.init(settings.gettext_domain, settings.version)
     gtk.glade.bindtextdomain(settings.gettext_domain, settings.localedir)
     gtk.glade.textdomain(settings.gettext_domain)
 
+    ev_source  = None
     if options.unprivileged:
         cl = None
     else:
@@ -66,7 +75,9 @@
             sys.exit(1)
         except client.ClientNotAvailableError:
             cl = None
+    if options.updatable:
+        ev_source = event_source.UpdatableEventSource(options.source)
 
-    w = MainWindow(cl)
+    w = MainWindow(cl, ev_source)
     if w.setup_initial_window(args):
         gtk.main()
diff -u -X ex or_src/main_window.py oav/src/main_window.py
--- or_src/main_window.py	2008-08-19 14:38:16.000000000 +0400
+++ oav/src/main_window.py	2015-03-30 13:21:25.607005136 +0300
@@ -135,6 +135,8 @@
 
         '''
         try:
+            if isinstance(self.event_source, event_source.UpdatableEventSource):
+                self.updater = gobject.idle_add(self.update_tabs)
             if isinstance(self.event_source, event_source.EmptyEventSource):
                 self.__event_error_report_only_one_push()
                 if self.client is not None:
@@ -246,7 +248,7 @@
         return (filename, extension)
 
     def read_events(self, filters, wanted_fields, want_other_fields,
-                    keep_raw_records):
+                    keep_raw_records, direction  = None, tab = None):
         '''Read audit events.
 
         Return a sequence of events, or None on error (without throwing
@@ -262,7 +264,7 @@
         try:
             return self.event_source.read_events(filters, wanted_fields,
                                                  want_other_fields,
-                                                 keep_raw_records)
+                                                 keep_raw_records, direction, tab)
         except IOError, e:
             if (self.__event_error_report_only_one_depth == 0 or
                 not self.__event_error_reported):
@@ -381,16 +383,30 @@
         '''End a region in which only one error message should be reported.'''
         self.__event_error_report_only_one_depth -= 1
 
-    def __refresh_all_tabs(self):
+    def update_tabs(self):
+        self.__refresh_all_tabs(True)
+        return True
+
+    def __refresh_all_tabs(self, updating = False):
         '''Refresh all tabs, taking care to report errors only once.'''
         self.__event_error_report_only_one_push()
         try:
-            for page_num in xrange(self.main_notebook.get_n_pages()):
-                tab = self.__tab_objects[self.main_notebook
-                                         .get_nth_page(page_num)]
-                tab.refresh()
+            if not updating:
+                for page_num in xrange(self.main_notebook.get_n_pages()):
+                    tab = self.__tab_objects[self.main_notebook
+                                             .get_nth_page(page_num)]
+                    tab.refresh()
+            else:
+                for page_num in xrange(self.main_notebook.get_n_pages()):
+                    tab = self.__tab_objects[self.main_notebook
+                                             .get_nth_page(page_num)]
+                    if tab.want_read_up:
+                        tab.refresh(True, 'up')
+                    if tab.want_read_down:
+                        tab.refresh(True, 'down')
         finally:
             self.__event_error_report_only_one_pop()
+        return True
 
     def __menu_new_list_activate(self, *_):
         self.new_list_tab([])



----- Исходное сообщение -----
От: "mitr" <mitr@redhat.com>
Кому: "Xeniya Muratova" <muratova@itsirius.su>
Копия: "linux-audit" <linux-audit@redhat.com>
Отправленные: Среда, 4 Март 2015 г 20:50:53
Тема: Re: log rendering in real time in audit-viewer

Hello,
> Hello Miloslav, and all the guys!
> 
> We use audit-viewer for events monitoring.
> Unfortunately, if some log is rather big it takes to much time for
> audit-viewer to parse and render it.
> Besides, we need to render log updates in real time, i.e. when a new line
> appears in a log, it should appear in a viewer too.
> Can you suggest the better way to extend audit-viewer to meet these
> requirements?

Well, write the code?  Something like inotify could be useful.  There isn’t any hidden switch to enable these features, if that is what you are asking.

As for performance, I may have missed something but I think I have squeezed as much as can be done with Python; improving performance further would very likely require a C extension.

(audit-viewer is a PyGtk2 application, and at I’m afraid I don’t currently have plans to port it to GTK+3/gobject-introspection or do any other non-trivial work on the project, at least in the near term.)
     Mirek



--
Linux-audit mailing list
Linux-audit@redhat.com
https://www.redhat.com/mailman/listinfo/linux-audit

^ permalink raw reply

* Re: [PATCH] audit.h: remove the macro AUDIT_ARCH_ARMEB definition
From: Li RongQing @ 2015-04-02  0:52 UTC (permalink / raw)
  To: Paul Moore
  Cc: Eric Paris, linux-audit-H+wXaHxf7aLQT0dZR+AlfA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <CAJFZqHzxXyuzi7Z9WawYKfFnTMfYfsRL0Qnt276p_t-AV9GB2A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Wed, Mar 25, 2015 at 8:01 AM, Li RongQing <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Wed, Mar 25, 2015 at 5:16 AM, Paul Moore <paul-r2n+y4ga6xFZroRs9YW3xA@public.gmane.org> wrote:
>> [NOTE: Adding the linux-arm folks to this thread just in case they are
>> interested.]
>>
>> I'm not an ARM expert, but the kernel #define for AUDIT_ARCH_ARMEB
>> (below) seems reasonable to me based on the other audit machine
>> definitions.  There is clearly a mismatch between the kernel and the
>> audit userspace, but I wonder if the issue should be addressed in the
>> userspace tools.  Further, you mention kernel 3.14, what happens with
>> kernel 3.19 and the most recent audit userspace tools?
>>
>>   > grep "AUDIT_ARCH_ARMEB" include/uapi/linux/audit.h
>>   #define AUDIT_ARCH_ARMEB        (EM_ARM)
>
>


ping...

-Roy

^ permalink raw reply

* Re: [PATCH] audit.h: remove the macro AUDIT_ARCH_ARMEB definition
From: Steve Grubb @ 2015-04-03 12:42 UTC (permalink / raw)
  To: linux-audit; +Cc: roy.qing.li, linux-api
In-Reply-To: <CAHC9VhR5VcaCtLG9hdVS2gZRWxVmdnpbK+fJwm6wCA8qyLebUQ@mail.gmail.com>

On Friday, March 20, 2015 09:29:14 AM Paul Moore wrote:
> On Fri, Mar 20, 2015 at 12:55 AM,  <roy.qing.li@gmail.com> wrote:
> > From: Li RongQing <roy.qing.li@gmail.com>
> > 
> > After 2f9783669 [ARM: 7412/1: audit: use only AUDIT_ARCH_ARM regardless
> > of endianness], no kernel user uses this macro;
> > 
> > Keeping this macro, only makes the compiling old version audit [before
> > changeset 931 Improve ARM and AARCH64 support] success, but the audit
> > program can not work with the kernel after 2f9783669 still,
> > since no syscall entry is enabled for AUDIT_ARCH_ARMEB in kernel.
> > 
> > so remove it to force to use the latest audit program
> > 
> > Signed-off-by: Li RongQing <roy.qing.li@gmail.com>
> > ---
> > other workaround is to define AUDIT_ARCH_ARMEB as AUDIT_ARCH_ARM,
> > but it seems very strange
> > 
> >  include/uapi/linux/audit.h | 1 -
> >  1 file changed, 1 deletion(-)
> 
> Since this #define lives in the user visible headers I don't want to
> remove it and risk causing a userspace breakage.  Leaving the #define
> in the header, even if it is unused by modern userspace, is harmless.

This macro doesn't appear to be in use by auditd.

-Steve

> > diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
> > index d3475e1..125aa49 100644
> > --- a/include/uapi/linux/audit.h
> > +++ b/include/uapi/linux/audit.h
> > @@ -351,7 +351,6 @@ enum {
> > 
> >  #define AUDIT_ARCH_AARCH64    
> >  (EM_AARCH64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) #define AUDIT_ARCH_ALPHA
> >        (EM_ALPHA|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) #define
> >  AUDIT_ARCH_ARM         (EM_ARM|__AUDIT_ARCH_LE)
> > 
> > -#define AUDIT_ARCH_ARMEB       (EM_ARM)
> > 
> >  #define AUDIT_ARCH_CRIS                (EM_CRIS|__AUDIT_ARCH_LE)
> >  #define AUDIT_ARCH_FRV         (EM_FRV)
> >  #define AUDIT_ARCH_I386                (EM_386|__AUDIT_ARCH_LE)
> > 
> > --
> > 2.1.0

^ permalink raw reply

* Suppress or exclude messages from /var/log/audit.log via audit.rules
From: [Cloud ASIA] Toshihiro Takehara @ 2015-04-03 15:23 UTC (permalink / raw)
  To: linux-audit


[-- Attachment #1.1: Type: text/plain, Size: 2694 bytes --]

Hi

My name is Takehara liveng in japan.

Now I set up audit.rules, then audit.log became very big.
The reason why is keepalived daemon and it's misc check shell adds some
entry every seconds.
I want to suppress or exclude log entry, and I searched the way like this.
  => https://www.redhat.com/archives/linux-audit/2011-October/msg00000.html
but I could not get effective answer.

Could you please tell me someone an effective way?


This is the audit.rules below.

# First rule - delete all
> -D
> # Increase the buffers to survive stress events.
> # Make this bigger for busy systems
> -b 320
> # Feel free to add below this line. See auditctl man page
> -a exit,always -F arch=b64 -F dir=/etc -F success=0 -S open -S truncate
> -a exit,always -F arch=b64 -S open -F uid=10
> -a exit,always -F arch=b64 -S open -F auid>=500 -F perm=wa
> -a exit,never -F arch=x86_64 -S all -F path=/root/mysql_status_check.sh
> -a never,exit -F arch=b32 -S open -S openat -F exit=-ENOENT
> -a never,exit -F arch=b64 -S open -S openat -F exit=-ENOENT
> -w /etc/sudoers -p wa -k sudoers-change
> -w /etc/ -p wa
> -w /var/lib/mysql -p wa



- keepalived is checking every seconds.
  /usr/sbin/keepalived
- misc check program
  /root/mysql_status_check.sh

type=SYSCALL msg=audit(1427989933.878:3632254): arch=c000003e syscall=2
success=yes exit=0 a0=4378a2 a1=2 a2=9 a3=8 items=1 ppid=43118 pid=3379
auid=501 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none)
ses=3 comm="keepalived" exe="/usr/sbin/keepalived" key=(null)

type=SYSCALL msg=audit(1427918414.323:2598129): arch=c000003e syscall=2
success=no exit=-6 a0=4a3155 a1=802 a2=1 a3=7fff4aefd1a0 items=1 ppid=20915
pid=20917 auid=501 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0
tty=(none) ses=3 comm="mysql_status_ch" exe="/bin/bash" key=(null)
type=SYSCALL msg=audit(1427918414.341:2598135): arch=c000003e syscall=2
success=yes exit=3 a0=f14470 a1=241 a2=1b6 a3=76 items=2 ppid=20916
pid=20947 auid=501 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0
tty=(none) ses=3 comm="mysql_status_ch" exe="/bin/bash" key=(null)


=========================
Cloud ASIA Co., Ltd. [ 株式会社クラウドエイジア ]
Founder & CEO Takehara Toshihiro

  〒174-0073
  33-14-101, Higashiyama-cho, Itabashi-ku, Tokyo, Japan
  TEL: +81-3-6869-2994  FAX: +81-3-6869-3974
  Mobile: +81-90-4737-8137
  Mobile in Laos: +856-20-5912-2188
  http://www.cloud-asia.co.jp
  http://www.facebook.com/cloud.asia.japan
  takehara@cloud-asia.co.jp

Lao Systems [ ラオシステムズ ]
  Founder & CEO Takehara Toshihiro
  http://lao-systems.jp/
=========================

[-- Attachment #1.2: Type: text/html, Size: 4166 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply

* Re: Suppress or exclude messages from /var/log/audit.log via audit.rules
From: Steve Grubb @ 2015-04-03 17:28 UTC (permalink / raw)
  To: linux-audit; +Cc: [Cloud ASIA] Toshihiro Takehara
In-Reply-To: <CAE215-musnk9qQ-GTdjnA5KLZDfspGAuscM9U_UySfWME_kryg@mail.gmail.com>

On Saturday, April 04, 2015 12:23:56 AM [Cloud ASIA] Toshihiro Takehara wrote:
> Now I set up audit.rules, then audit.log became very big.
> The reason why is keepalived daemon and it's misc check shell adds some
> entry every seconds.

Typically when logs get too big, its because of a problem in the rules. There 
are also times when a system is misconfigured and that will cause a flood, too.


> I want to suppress or exclude log entry, and I searched the way like this.
>   => https://www.redhat.com/archives/linux-audit/2011-October/msg00000.html
> but I could not get effective answer.
> 
> Could you please tell me someone an effective way?
> 
> 
> This is the audit.rules below.
> 
> # First rule - delete all
> 
> > -D
> > # Increase the buffers to survive stress events.
> > # Make this bigger for busy systems
> > -b 320
> > # Feel free to add below this line. See auditctl man page

For one thing, your analysis will be easier if you used "keys" to say what the 
event represents. For example, why open on uid = 10 and uid >=500? And is 500 
the minimal user id or is 1000 the minimal? Recent distributions have moved to 
1000 meaning the rules may need migrating from 500 to 1000.


> > -a exit,always -F arch=b64 -F dir=/etc -F success=0 -S open -S truncate
> > -a exit,always -F arch=b64 -S open -F uid=10
> > -a exit,always -F arch=b64 -S open -F auid>=500 -F perm=wa

The audit system uses unsigned numbers in the rule matching engine. That means 
-1 is >=500. So you have to add auid!=4294967295. And why do you want all 
opens? Both success and failure? Typically, programs open a lot of non-
existing files giving ENOENT.

> > -a exit,never -F arch=x86_64 -S all -F path=/root/mysql_status_check.sh
> > -a never,exit -F arch=b32 -S open -S openat -F exit=-ENOENT
> > -a never,exit -F arch=b64 -S open -S openat -F exit=-ENOENT

Typically for security auditiing, you are interested in the files that the user 
is denied access to rather than everything they access. To do this, I'd 
recommend:

-a always,exit -F arch=b64 -S open,truncate,creat,openat,open_by_handle_at -F 
exit=-EACCES -F auid>=500 -F auid!=4294967295 -F key=failed-access
-a always,exit -F arch=b64 -S open,truncate,creat,openat,open_by_handle_at -F 
exit=-EPERM -F auid>=500 -F auid!=4294967295 -F key=failed-access


> > -w /etc/sudoers -p wa -k sudoers-change
> > -w /etc/ -p wa
> > -w /var/lib/mysql -p wa
> 
> - keepalived is checking every seconds.
>   /usr/sbin/keepalived
> - misc check program
>   /root/mysql_status_check.sh
> 
> type=SYSCALL msg=audit(1427989933.878:3632254): arch=c000003e syscall=2
> success=yes exit=0 a0=4378a2 a1=2 a2=9 a3=8 items=1 ppid=43118 pid=3379
> auid=501 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none)
> ses=3 comm="keepalived" exe="/usr/sbin/keepalived" key=(null)

This is an open syscall from a program started by a user in session 3. They 
used the keepalived program to do it. Is this a daemon or a user program? If 
its a daemon, why does it have a auid and session set? That would be the 
source of the problem.


> type=SYSCALL msg=audit(1427918414.323:2598129): arch=c000003e syscall=2
> success=no exit=-6 a0=4a3155 a1=802 a2=1 a3=7fff4aefd1a0 items=1 ppid=20915
> pid=20917 auid=501 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0
> tty=(none) ses=3 comm="mysql_status_ch" exe="/bin/bash" key=(null)

Same issue different program.

> type=SYSCALL msg=audit(1427918414.341:2598135): arch=c000003e syscall=2
> success=yes exit=3 a0=f14470 a1=241 a2=1b6 a3=76 items=2 ppid=20916
> pid=20947 auid=501 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0
> tty=(none) ses=3 comm="mysql_status_ch" exe="/bin/bash" key=(null)

This seems to be a user program. But again, why do you want all open syscalls?

-Steve

^ permalink raw reply

* Re: Suppress or exclude messages from /var/log/audit.log via audit.rules
From: Steve Grubb @ 2015-04-04 14:47 UTC (permalink / raw)
  To: linux-audit; +Cc: [Cloud ASIA] Toshihiro Takehara
In-Reply-To: <CAE215-musnk9qQ-GTdjnA5KLZDfspGAuscM9U_UySfWME_kryg@mail.gmail.com>

On Saturday, April 04, 2015 12:23:56 AM [Cloud ASIA] Toshihiro Takehara wrote:
> This is the audit.rules below.
> 
> # First rule - delete all
> 
> > -D
> > # Increase the buffers to survive stress events.
> > # Make this bigger for busy systems
> > -b 320
> > # Feel free to add below this line. See auditctl man page
> > -a exit,always -F arch=b64 -F dir=/etc -F success=0 -S open -S truncate
> > -a exit,always -F arch=b64 -S open -F uid=10
> > -a exit,always -F arch=b64 -S open -F auid>=500 -F perm=wa
> > -a exit,never -F arch=x86_64 -S all -F path=/root/mysql_status_check.sh
> > -a never,exit -F arch=b32 -S open -S openat -F exit=-ENOENT
> > -a never,exit -F arch=b64 -S open -S openat -F exit=-ENOENT
> > -w /etc/sudoers -p wa -k sudoers-change
> > -w /etc/ -p wa
> > -w /var/lib/mysql -p wa

One thing I forgot to mention yesterday is that the kernel rule matching 
engine works from top to bottom. That means "never" should probably be before 
"always" when they both cover the same syscall and the "never" is a subset of 
the possible selections of the "always" rule.

-Steve

^ permalink raw reply

* Re: [PATCH] audit.h: remove the macro AUDIT_ARCH_ARMEB definition
From: Li RongQing @ 2015-04-10  0:21 UTC (permalink / raw)
  To: Steve Grubb
  Cc: linux-audit-H+wXaHxf7aLQT0dZR+AlfA, Paul Moore,
	linux-api-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <18079199.sKrYvVCScd@x2>

On Fri, Apr 3, 2015 at 8:42 PM, Steve Grubb <sgrubb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
>> Since this #define lives in the user visible headers I don't want to
>> remove it and risk causing a userspace breakage.  Leaving the #define
>> in the header, even if it is unused by modern userspace, is harmless.
>
> This macro doesn't appear to be in use by auditd.


ping ...

^ permalink raw reply

* Re: Suppress or exclude messages from /var/log/audit.log via audit.rules
From: [Cloud ASIA] Toshihiro Takehara @ 2015-04-14 14:42 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit
In-Reply-To: <10204897.6gJiKKv096@x2>


[-- Attachment #1.1: Type: text/plain, Size: 753 bytes --]

Dear Steve

Thank you very much for a lot of suggestions.
I apology for late response.

The messages you gave me was helpful, so I was able to resolve the problems.

Best Regards,
Toshihiro Takehara

=========================
Cloud ASIA Co., Ltd. [ 株式会社クラウドエイジア ]
Founder & CEO Takehara Toshihiro

  〒174-0073
  33-14-101, Higashiyama-cho, Itabashi-ku, Tokyo, Japan
  TEL: +81-3-6869-2994  FAX: +81-3-6869-3974
  Mobile: +81-90-4737-8137
  Mobile in Laos: +856-20-5912-2188
  http://www.cloud-asia.co.jp
  http://www.facebook.com/cloud.asia.japan
  takehara@cloud-asia.co.jp

Lao Systems [ ラオシステムズ ]
  Founder & CEO Takehara Toshihiro
  http://lao-systems.jp/
=========================

[-- Attachment #1.2: Type: text/html, Size: 1679 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply

* [PATCH] lsm: copy comm before calling audit_log to avoid race in string printing
From: Richard Guy Briggs @ 2015-04-14 15:01 UTC (permalink / raw)
  To: linux-security-module, linux-kernel, linux-audit
  Cc: jmorris, Richard Guy Briggs, sgrubb, eparis, pmoore,
	penguin-kernel

When task->comm is passed directly to audit_log_untrustedstring() without
getting a copy or using the task_lock, there is a race that could happen that
would output a NULL (\0) in the middle of the output string that would
effectively truncate the rest of the report text after the comm= field in the
audit log message, losing fields.

Using get_task_comm() to get a copy while acquiring the task_lock to prevent
this and to prevent the result from being a mixture of old and new values of
comm would incur potentially unacceptable overhead, considering that the value
can be influenced by userspace and therefore untrusted anyways.

Copy the value before passing it to audit_log_untrustedstring() ensures that a
local copy is used to calculate the length *and* subsequently printed.  Even if
this value contains a mix of old and new values, it will only calculate and
copy up to the first NULL, preventing the rest of the audit log message being
truncated.

Use a second local copy of comm to avoid a race between the first and second
calls to audit_log_untrustedstring() with comm.

Reported-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
---
 security/lsm_audit.c |   15 +++++++++------
 1 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index 69fdf3b..b526ddc 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -211,7 +211,7 @@ static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
 static void dump_common_audit_data(struct audit_buffer *ab,
 				   struct common_audit_data *a)
 {
-	struct task_struct *tsk = current;
+	char comm[sizeof(current->comm)];
 
 	/*
 	 * To keep stack sizes in check force programers to notice if they
@@ -220,8 +220,8 @@ static void dump_common_audit_data(struct audit_buffer *ab,
 	 */
 	BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2);
 
-	audit_log_format(ab, " pid=%d comm=", task_pid_nr(tsk));
-	audit_log_untrustedstring(ab, tsk->comm);
+	audit_log_format(ab, " pid=%d comm=", task_pid_nr(current));
+	audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm)));
 
 	switch (a->type) {
 	case LSM_AUDIT_DATA_NONE:
@@ -276,16 +276,19 @@ static void dump_common_audit_data(struct audit_buffer *ab,
 		audit_log_format(ab, " ino=%lu", inode->i_ino);
 		break;
 	}
-	case LSM_AUDIT_DATA_TASK:
-		tsk = a->u.tsk;
+	case LSM_AUDIT_DATA_TASK: {
+		struct task_struct *tsk = a->u.tsk;
 		if (tsk) {
 			pid_t pid = task_pid_nr(tsk);
 			if (pid) {
+				char comm[sizeof(tsk->comm)];
 				audit_log_format(ab, " pid=%d comm=", pid);
-				audit_log_untrustedstring(ab, tsk->comm);
+				audit_log_untrustedstring(ab,
+				    memcpy(comm, tsk->comm, sizeof(comm)));
 			}
 		}
 		break;
+	}
 	case LSM_AUDIT_DATA_NET:
 		if (a->u.net->sk) {
 			struct sock *sk = a->u.net->sk;
-- 
1.7.1


^ permalink raw reply related

* Re: [PATCH] lsm: copy comm before calling audit_log to avoid race in string printing
From: James Morris @ 2015-04-14 23:55 UTC (permalink / raw)
  To: Richard Guy Briggs
  Cc: linux-security-module, linux-kernel, linux-audit, sgrubb, eparis,
	pmoore, penguin-kernel
In-Reply-To: <2762230afd590c4c6f50c4905ef91738a4f38e4c.1429023587.git.rgb@redhat.com>

On Tue, 14 Apr 2015, Richard Guy Briggs wrote:

> When task->comm is passed directly to audit_log_untrustedstring() without
> getting a copy or using the task_lock, there is a race that could happen that
> would output a NULL (\0) in the middle of the output string that would
> effectively truncate the rest of the report text after the comm= field in the
> audit log message, losing fields.
> 
> Using get_task_comm() to get a copy while acquiring the task_lock to prevent
> this and to prevent the result from being a mixture of old and new values of
> comm would incur potentially unacceptable overhead, considering that the value
> can be influenced by userspace and therefore untrusted anyways.
> 
> Copy the value before passing it to audit_log_untrustedstring() ensures that a
> local copy is used to calculate the length *and* subsequently printed.  Even if
> this value contains a mix of old and new values, it will only calculate and
> copy up to the first NULL, preventing the rest of the audit log message being
> truncated.
> 
> Use a second local copy of comm to avoid a race between the first and second
> calls to audit_log_untrustedstring() with comm.
> 
> Reported-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Signed-off-by: Richard Guy Briggs <rgb@redhat.com>

Applied.

-- 
James Morris
<jmorris@namei.org>


^ permalink raw reply

* [PATCH] lsm: rename duplicate labels in LSM_AUDIT_DATA_TASK audit message type
From: Richard Guy Briggs @ 2015-04-15 18:08 UTC (permalink / raw)
  To: linux-security-module, linux-kernel, linux-audit
  Cc: Richard Guy Briggs, sgrubb, eparis, pmoore, jmorris

The LSM_AUDIT_DATA_TASK pid= and comm= labels are duplicates of those at the
start of this function with different values.  Rename them to their object
counterparts opid= and ocomm= to disambiguate.

Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
---
 security/lsm_audit.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index b526ddc..3323144 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -282,7 +282,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
 			pid_t pid = task_pid_nr(tsk);
 			if (pid) {
 				char comm[sizeof(tsk->comm)];
-				audit_log_format(ab, " pid=%d comm=", pid);
+				audit_log_format(ab, " opid=%d ocomm=", pid);
 				audit_log_untrustedstring(ab,
 				    memcpy(comm, tsk->comm, sizeof(comm)));
 			}
-- 
1.7.1


^ permalink raw reply related

* Recovery when disk_full_action=HALT
From: Andrew Ruch @ 2015-04-16 14:29 UTC (permalink / raw)
  To: linux-audit

Hello,

We have a RHEL6 system with the disk_full_action set to HALT. I'm
working on procedures for what to do if this case occurs. When the log
partition fills up, the system shuts down. However, the system will
not boot after this because as soon as auditd tries to start, the
system immediately shuts down again. What are the options for
recovering after this happens? I've come up with two:

1) Stop the boot process at grub and disable audit by adding a kernel
parameter 'audit=0'.

2) If grub timeout is 0, use a live CD to access the audit partition.


I'm sure there are some variations on option 1 using an interactive
boot. Are there any other options I missed, especially if grub timeout
has been set to 0?

Thanks,
Andrew Ruch

^ permalink raw reply

* Re: Recovery when disk_full_action=HALT
From: leam hall @ 2015-04-16 14:33 UTC (permalink / raw)
  To: linux-audit
In-Reply-To: <CAPubmWVv_7nkbTL5hSst-yASk81qci_rOpCx43bEU8QO2JTvyg@mail.gmail.com>


[-- Attachment #1.1: Type: text/plain, Size: 1188 bytes --]

My plan would be to implement logrotate and clean out things well. Another
path is to ensure the log volume is larger than " max number of logs" *
"max log size".

On Thu, Apr 16, 2015 at 10:29 AM, Andrew Ruch <adruch2002@gmail.com> wrote:

> Hello,
>
> We have a RHEL6 system with the disk_full_action set to HALT. I'm
> working on procedures for what to do if this case occurs. When the log
> partition fills up, the system shuts down. However, the system will
> not boot after this because as soon as auditd tries to start, the
> system immediately shuts down again. What are the options for
> recovering after this happens? I've come up with two:
>
> 1) Stop the boot process at grub and disable audit by adding a kernel
> parameter 'audit=0'.
>
> 2) If grub timeout is 0, use a live CD to access the audit partition.
>
>
> I'm sure there are some variations on option 1 using an interactive
> boot. Are there any other options I missed, especially if grub timeout
> has been set to 0?
>
> Thanks,
> Andrew Ruch
>
> --
> Linux-audit mailing list
> Linux-audit@redhat.com
> https://www.redhat.com/mailman/listinfo/linux-audit
>



-- 
Mind on a Mission <http://leamhall.blogspot.com/>

[-- Attachment #1.2: Type: text/html, Size: 1831 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply

* Re: Recovery when disk_full_action=HALT
From: Steve Grubb @ 2015-04-16 14:49 UTC (permalink / raw)
  To: linux-audit
In-Reply-To: <CAPubmWVv_7nkbTL5hSst-yASk81qci_rOpCx43bEU8QO2JTvyg@mail.gmail.com>

On Thursday, April 16, 2015 08:29:23 AM Andrew Ruch wrote:
> Hello,
> 
> We have a RHEL6 system with the disk_full_action set to HALT. I'm
> working on procedures for what to do if this case occurs. When the log
> partition fills up, the system shuts down. However, the system will
> not boot after this because as soon as auditd tries to start, the
> system immediately shuts down again. What are the options for
> recovering after this happens? I've come up with two:

Normally, I would think that system maintenance for a situation like this is 
to boot the computer into Single User Mode. You should have switched the 
system over to using sulogin as the shell for single user mode. This way its 
password protected. Then once in, do what you need to archive and make room 
again.


> 1) Stop the boot process at grub and disable audit by adding a kernel
> parameter 'audit=0'.

If you don't use single user mode, then there is the risk of someone doing 
something while the audit system can't record anything. You probably don't 
want that possibility either.


> 2) If grub timeout is 0, use a live CD to access the audit partition.

This would work also, but Single User Mode is so much easier. :-)
 
 
> I'm sure there are some variations on option 1 using an interactive
> boot. Are there any other options I missed, especially if grub timeout
> has been set to 0?

I wouldn't set it to 0. You can make it short like 2 or 3. But you need to be 
able to get into the editor to tell it 'S' for single user mode.

-Steve

^ permalink raw reply

* Recovery when disk_full_action=HALT
From: Paul Whitney @ 2015-04-16 15:03 UTC (permalink / raw)
  To: linux-audit


[-- Attachment #1.1: Type: text/plain, Size: 2452 bytes --]

Hi Andrew,

To add to Steve Grubb's response, part of the configuration should also include an option to notify the administrator via Email when the partition is at 90% capacity.  Of course, this can be adjusted to better suit your requirements (i.e. 90% on a Friday night might be too late).

Another solution is to use Steve's logrotate script.  We made some minor additions to it so that the script not only rotates the audit log every 24-hours, but then it gets compressed using BZIP2 and moved to a larger partition under an archive folder (i.e. /opt/AUDIT_ARCHIVE) and time stamped. This way your partition should rarely if ever fill up.

Cheers,
Paul M. Whitney
E-mail: paul.whitney@mac.com
Sent from my browser.



On Apr 16, 2015, at 10:52 AM, Steve Grubb <sgrubb@redhat.com> wrote:

On Thursday, April 16, 2015 08:29:23 AM Andrew Ruch wrote:
Hello,

We have a RHEL6 system with the disk_full_action set to HALT. I'm
working on procedures for what to do if this case occurs. When the log
partition fills up, the system shuts down. However, the system will
not boot after this because as soon as auditd tries to start, the
system immediately shuts down again. What are the options for
recovering after this happens? I've come up with two:

Normally, I would think that system maintenance for a situation like this is 
to boot the computer into Single User Mode. You should have switched the 
system over to using sulogin as the shell for single user mode. This way its 
password protected. Then once in, do what you need to archive and make room 
again.


1) Stop the boot process at grub and disable audit by adding a kernel
parameter 'audit=0'.

If you don't use single user mode, then there is the risk of someone doing 
something while the audit system can't record anything. You probably don't 
want that possibility either.


2) If grub timeout is 0, use a live CD to access the audit partition.

This would work also, but Single User Mode is so much easier. :-)


I'm sure there are some variations on option 1 using an interactive
boot. Are there any other options I missed, especially if grub timeout
has been set to 0?

I wouldn't set it to 0. You can make it short like 2 or 3. But you need to be 
able to get into the editor to tell it 'S' for single user mode.

-Steve

--
Linux-audit mailing list
Linux-audit@redhat.com
https://www.redhat.com/mailman/listinfo/linux-audit

[-- Attachment #1.2.1: Type: text/html, Size: 4267 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply

* [PATCH V6 00/10] namespaces: log namespaces per task
From: Richard Guy Briggs @ 2015-04-17  7:35 UTC (permalink / raw)
  To: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-audit-H+wXaHxf7aLQT0dZR+AlfA
  Cc: pmoore-H+wXaHxf7aLQT0dZR+AlfA, eparis-FjpueFixGhCM4zKIHC2jIg,
	sgrubb-H+wXaHxf7aLQT0dZR+AlfA,
	zohar-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
	ebiederm-aS9lmoZGLiVWk0Htik3J/w

The purpose is to track namespace instances in use by logged processes from the
perspective of init_*_ns by logging the namespace IDs (device ID and namespace
inode - offset).


1/10 exposes proc's ns entries structure which lists a number of useful
operations per namespace type for other subsystems to use.

2/10  proc_ns: define PROC_*_INIT_INO in terms of PROC_DYNAMIC_FIRST

3/10 provides an example of usage for audit_log_task_info() which is used by
syscall audits, among others.  audit_log_task() and audit_common_recv_message()
would be other potential use cases.

Proposed output format:
This differs slightly from Aristeu's patch because of the label conflict with
"pid=" due to including it in existing records rather than it being a seperate
record.  It has now returned to being a seperate record.  The proc device
major/minor are listed in hexadecimal and namespace IDs are the proc inode
minus the base offset.
	type=NS_INFO msg=audit(1408577535.306:82): dev=00:03 netns=3 utsns=-3 ipcns=-4 pidns=-1 userns=-2 mntns=0

4/10 change audit startup from __initcall to subsys_initcall to get it started
earlier to be able to receive initial namespace log messages.

5/10 tracks the creation and deletion of namespaces, listing the type of
namespace instance, proc device ID, related namespace id if there is one and
the newly minted namespace ID.

Proposed output format for initial namespace creation:
	type=AUDIT_NS_INIT_UTS msg=audit(1408577534.868:5): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_utsns=(none) utsns=-3 res=1
	type=AUDIT_NS_INIT_USER msg=audit(1408577534.868:6): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_userns=(none) userns=-2 res=1
	type=AUDIT_NS_INIT_PID msg=audit(1408577534.868:7): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_pidns=(none) pidns=-1 res=1
	type=AUDIT_NS_INIT_MNT msg=audit(1408577534.868:8): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_mntns=(none) mntns=0 res=1
	type=AUDIT_NS_INIT_IPC msg=audit(1408577534.868:9): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_ipcns=(none) ipcns=-4 res=1
	type=AUDIT_NS_INIT_NET msg=audit(1408577533.500:10): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_netns=(none) netns=2 res=1

And a CLONE action would result in:
	type=type=AUDIT_NS_INIT_NET msg=audit(1408577535.306:81): pid=481 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 dev=00:03 old_netns=2 netns=3 res=1

While deleting a namespace would result in:
	type=type=AUDIT_NS_DEL_MNT msg=audit(1408577552.221:85): pid=481 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 dev=00:03 mntns=4 res=1

6/10 accepts a PID from userspace and requests logging an AUDIT_NS_INFO record
type (CAP_AUDIT_CONTROL required).

7/10 is a macro for CLONE_NEW_* flags.

8/10 adds auditing on creation of namespace(s) in fork.

9/10 adds auditing a change of namespace on setns.

10/10 attaches a AUDIT_NS_INFO record to AUDIT_VIRT_CONTROL records
(CAP_AUDIT_WRITE required).


v5 -> v6:
	Switch to using namespace ID based on namespace proc inode minus base offset
	Added proc device ID to qualify proc inode reference
	Eliminate exposed /proc interface

v4 -> v5:
	Clean up prototypes for dependencies on CONFIG_NAMESPACES.
	Add AUDIT_NS_INFO record type to AUDIT_VIRT_CONTROL record.
	Log AUDIT_NS_INFO with PID.
	Move /proc/<pid>/ns_* patches to end of patchset to deprecate them.
	Log on changing ns (setns).
	Log on creating new namespaces when forking.
	Added a macro for CLONE_NEW*.

v3 -> v4:
	Seperate out the NS_INFO message from the SYSCALL message.
	Moved audit_log_namespace_info() out of audit_log_task_info().
	Use a seperate message type per namespace type for each of INIT/DEL.
	Make ns= easier to search across NS_INFO and NS_INIT/DEL_XXX msg types.
	Add /proc/<pid>/ns/ documentation.
	Fix dynamic initial ns logging.

v2 -> v3:
	Use atomic64_t in ns_serial to simplify it.
	Avoid funciton duplication in proc, keying on dentry.
	Squash down audit patch to avoid rcu sleep issues.
	Add tracking for creation and deletion of namespace instances.

v1 -> v2:
	Avoid rollover by switching from an int to a long long.
	Change rollover behaviour from simply avoiding zero to raising a BUG.
	Expose serial numbers in /proc/<pid>/ns/*_snum.
	Expose ns_entries and use it in audit.


Notes:
As for CAP_AUDIT_READ, a patchset has been accepted upstream to check
capabilities of userspace processes that try to join netlink broadcast groups.

This set does not try to solve the non-init namespace audit messages and
auditd problem yet.  That will come later, likely with additional auditd
instances running in another namespace with a limited ability to influence the
master auditd.  I echo Eric B's idea that messages destined for different
namespaces would have to be tailored for that namespace with references that
make sense (such as the right pid number reported to that pid namespace, and
not leaking info about parents or peers).

Questions:
Is there a way to link serial numbers of namespaces involved in migration of a
container to another kernel?  It sounds like what is needed is a part of a
mangement application that is able to pull the audit records from constituent
hosts to build an audit trail of a container.

What additional events should list this information?

Does this present any problematic information leaks?  Only CAP_AUDIT_CONTROL
(and now CAP_AUDIT_READ) in init_user_ns can get to this information in
the init namespace at the moment from audit.


Richard Guy Briggs (10):
  namespaces: expose ns_entries
  proc_ns: define PROC_*_INIT_INO in terms of PROC_DYNAMIC_FIRST
  audit: log namespace ID numbers
  audit: initialize at subsystem time rather than device time
  audit: log creation and deletion of namespace instances
  audit: dump namespace IDs for pid on receipt of AUDIT_NS_INFO
  sched: add a macro to ref all CLONE_NEW* flags
  fork: audit on creation of new namespace(s)
  audit: log on switching namespace (setns)
  audit: emit AUDIT_NS_INFO record with AUDIT_VIRT_CONTROL record

 fs/namespace.c                   |   13 +++
 fs/proc/generic.c                |    3 +-
 fs/proc/namespaces.c             |    2 +-
 include/linux/audit.h            |   20 +++++
 include/linux/proc_ns.h          |   10 ++-
 include/uapi/linux/audit.h       |   21 +++++
 include/uapi/linux/sched.h       |    6 ++
 ipc/namespace.c                  |   12 +++
 kernel/audit.c                   |  169 +++++++++++++++++++++++++++++++++++++-
 kernel/auditsc.c                 |    2 +
 kernel/fork.c                    |    3 +
 kernel/nsproxy.c                 |    4 +
 kernel/pid_namespace.c           |   13 +++
 kernel/user_namespace.c          |   13 +++
 kernel/utsname.c                 |   12 +++
 net/core/net_namespace.c         |   12 +++
 security/integrity/ima/ima_api.c |    2 +
 17 files changed, 309 insertions(+), 8 deletions(-)

^ permalink raw reply

* [PATCH V6 01/10] namespaces: expose ns_entries
From: Richard Guy Briggs @ 2015-04-17  7:35 UTC (permalink / raw)
  To: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-audit-H+wXaHxf7aLQT0dZR+AlfA
  Cc: pmoore-H+wXaHxf7aLQT0dZR+AlfA, eparis-FjpueFixGhCM4zKIHC2jIg,
	sgrubb-H+wXaHxf7aLQT0dZR+AlfA,
	zohar-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
	ebiederm-aS9lmoZGLiVWk0Htik3J/w
In-Reply-To: <cover.1429252659.git.rgb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

Expose ns_entries so subsystems other than proc can use this set of namespace
operations.

Signed-off-by: Richard Guy Briggs <rgb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 fs/proc/namespaces.c    |    2 +-
 include/linux/proc_ns.h |    1 +
 2 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index 8902609..310da74 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -15,7 +15,7 @@
 #include "internal.h"
 
 
-static const struct proc_ns_operations *ns_entries[] = {
+const struct proc_ns_operations *ns_entries[] = {
 #ifdef CONFIG_NET_NS
 	&netns_operations,
 #endif
diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h
index 34a1e10..09ff93c 100644
--- a/include/linux/proc_ns.h
+++ b/include/linux/proc_ns.h
@@ -27,6 +27,7 @@ extern const struct proc_ns_operations ipcns_operations;
 extern const struct proc_ns_operations pidns_operations;
 extern const struct proc_ns_operations userns_operations;
 extern const struct proc_ns_operations mntns_operations;
+extern const struct proc_ns_operations *ns_entries[];
 
 /*
  * We always define these enumerators
-- 
1.7.1

^ permalink raw reply related

* [PATCH V6 02/10] proc_ns: define PROC_*_INIT_INO in terms of PROC_DYNAMIC_FIRST
From: Richard Guy Briggs @ 2015-04-17  7:35 UTC (permalink / raw)
  To: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-audit-H+wXaHxf7aLQT0dZR+AlfA
  Cc: pmoore-H+wXaHxf7aLQT0dZR+AlfA, eparis-FjpueFixGhCM4zKIHC2jIg,
	sgrubb-H+wXaHxf7aLQT0dZR+AlfA,
	zohar-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
	ebiederm-aS9lmoZGLiVWk0Htik3J/w
In-Reply-To: <cover.1429252659.git.rgb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

Since PROC_*_INIT_INO are all defined relative to PROC_DYNAMIC_FIRST, make it
explicit.

Signed-off-by: Richard Guy Briggs <rgb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 fs/proc/generic.c       |    3 +--
 include/linux/proc_ns.h |    9 +++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index b7f268e..9f7726a 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -11,6 +11,7 @@
 #include <linux/errno.h>
 #include <linux/time.h>
 #include <linux/proc_fs.h>
+#include <linux/proc_ns.h>
 #include <linux/stat.h>
 #include <linux/mm.h>
 #include <linux/module.h>
@@ -121,8 +122,6 @@ static int xlate_proc_name(const char *name, struct proc_dir_entry **ret,
 static DEFINE_IDA(proc_inum_ida);
 static DEFINE_SPINLOCK(proc_inum_lock); /* protects the above */
 
-#define PROC_DYNAMIC_FIRST 0xF0000000U
-
 /*
  * Return an inode number between PROC_DYNAMIC_FIRST and
  * 0xffffffff, or zero on failure.
diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h
index 09ff93c..340372b 100644
--- a/include/linux/proc_ns.h
+++ b/include/linux/proc_ns.h
@@ -32,12 +32,13 @@ extern const struct proc_ns_operations *ns_entries[];
 /*
  * We always define these enumerators
  */
+#define PROC_DYNAMIC_FIRST 0xF0000000U
 enum {
 	PROC_ROOT_INO		= 1,
-	PROC_IPC_INIT_INO	= 0xEFFFFFFFU,
-	PROC_UTS_INIT_INO	= 0xEFFFFFFEU,
-	PROC_USER_INIT_INO	= 0xEFFFFFFDU,
-	PROC_PID_INIT_INO	= 0xEFFFFFFCU,
+	PROC_IPC_INIT_INO	= PROC_DYNAMIC_FIRST - 1,
+	PROC_UTS_INIT_INO	= PROC_DYNAMIC_FIRST - 2,
+	PROC_USER_INIT_INO	= PROC_DYNAMIC_FIRST - 3,
+	PROC_PID_INIT_INO	= PROC_DYNAMIC_FIRST - 4,
 };
 
 #ifdef CONFIG_PROC_FS
-- 
1.7.1

^ permalink raw reply related

* [PATCH V6 03/10] audit: log namespace ID numbers
From: Richard Guy Briggs @ 2015-04-17  7:35 UTC (permalink / raw)
  To: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-audit-H+wXaHxf7aLQT0dZR+AlfA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, pmoore-H+wXaHxf7aLQT0dZR+AlfA,
	linux-security-module-u79uwXL29TY76Z2rM5mHXA,
	linux-ima-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	eparis-FjpueFixGhCM4zKIHC2jIg, sgrubb-H+wXaHxf7aLQT0dZR+AlfA,
	zohar-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
	ebiederm-aS9lmoZGLiVWk0Htik3J/w
In-Reply-To: <cover.1429252659.git.rgb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

Log the namespace identifiers (device ID and proc inode minus base offset) of a
task in a new record type (1329) (usually accompanies audit_log_task_info()
type=SYSCALL record) which is used by syscall audits, among others..

Idea first presented:
	https://www.redhat.com/archives/linux-audit/2013-March/msg00020.html

Typical output format would look something like:
	type=NS_INFO msg=audit(1408577535.306:82): pid=374 dev=00:03 netns=116 utsns=-2 ipcns=-1 pidns=-4 userns=-3 mntns=0

The namespace identifier values are printed relative to PROC_DYNAMIC_FIRST
(0xF000000) (so the first 4 are negative).

Suggested-by: Aristeu Rozanski <arozansk-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Richard Guy Briggs <rgb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Acked-by: Serge Hallyn <serge.hallyn-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
---
 include/linux/audit.h            |    8 ++++++++
 include/uapi/linux/audit.h       |    1 +
 kernel/audit.c                   |   35 +++++++++++++++++++++++++++++++++++
 kernel/auditsc.c                 |    2 ++
 security/integrity/ima/ima_api.c |    2 ++
 5 files changed, 48 insertions(+), 0 deletions(-)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index b481779..71698ec 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -478,6 +478,12 @@ static inline void	    audit_log_secctx(struct audit_buffer *ab, u32 secid)
 extern int audit_log_task_context(struct audit_buffer *ab);
 extern void audit_log_task_info(struct audit_buffer *ab,
 				struct task_struct *tsk);
+#ifdef CONFIG_NAMESPACES
+extern void		    audit_log_ns_info(struct task_struct *tsk);
+#else
+static inline void	    audit_log_ns_info(struct task_struct *tsk)
+{ }
+#endif
 
 extern int		    audit_update_lsm_rules(void);
 
@@ -534,6 +540,8 @@ static inline int audit_log_task_context(struct audit_buffer *ab)
 static inline void audit_log_task_info(struct audit_buffer *ab,
 				       struct task_struct *tsk)
 { }
+static inline void audit_log_ns_info(struct task_struct *tsk)
+{ }
 #define audit_enabled 0
 #endif /* CONFIG_AUDIT */
 static inline void audit_log_string(struct audit_buffer *ab, const char *buf)
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 2ccf19e..1ffb151 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -110,6 +110,7 @@
 #define AUDIT_SECCOMP		1326	/* Secure Computing event */
 #define AUDIT_PROCTITLE		1327	/* Proctitle emit event */
 #define AUDIT_FEATURE_CHANGE	1328	/* audit log listing feature changes */
+#define AUDIT_NS_INFO		1329	/* Record process namespace IDs */
 
 #define AUDIT_AVC		1400	/* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR	1401	/* Internal SE Linux Errors */
diff --git a/kernel/audit.c b/kernel/audit.c
index d5a1220..68200ce 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -66,7 +66,9 @@
 #include <linux/freezer.h>
 #include <linux/tty.h>
 #include <linux/pid_namespace.h>
+#include <linux/proc_ns.h>
 #include <net/netns/generic.h>
+#include <linux/mount.h> /* struct vfsmount */
 
 #include "audit.h"
 
@@ -745,6 +747,8 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature
 			 audit_feature_names[which], !!old_feature, !!new_feature,
 			 !!old_lock, !!new_lock, res);
 	audit_log_end(ab);
+
+	audit_log_ns_info(current);
 }
 
 static int audit_set_feature(struct sk_buff *skb)
@@ -1653,6 +1657,35 @@ void audit_log_session_info(struct audit_buffer *ab)
 	audit_log_format(ab, " auid=%u ses=%u", auid, sessionid);
 }
 
+#ifdef CONFIG_NAMESPACES
+void audit_log_ns_info(struct task_struct *tsk)
+{
+	const struct proc_ns_operations **entry;
+	bool end = false;
+	struct audit_buffer *ab;
+	struct vfsmount *mnt = task_active_pid_ns(tsk)->proc_mnt;
+	struct super_block *sb = mnt->mnt_sb;
+
+	if (!tsk)
+		return;
+	ab = audit_log_start(tsk->audit_context, GFP_KERNEL,
+			     AUDIT_NS_INFO);
+	if (!ab)
+		return;
+	audit_log_format(ab, "pid=%d", task_pid_nr(tsk));
+	audit_log_format(ab, " dev=%02x:%02x", MAJOR(sb->s_dev), MINOR(sb->s_dev));
+	for (entry = ns_entries; !end; entry++) {
+		void *ns = (*entry)->get(tsk);
+
+		audit_log_format(ab, " %sns=%d", (*entry)->name,
+				 (*entry)->inum(ns) - PROC_DYNAMIC_FIRST);
+		(*entry)->put(ns);
+		end = (*entry)->type == CLONE_NEWNS;
+	}
+	audit_log_end(ab);
+}
+#endif /* CONFIG_NAMESPACES */
+
 void audit_log_key(struct audit_buffer *ab, char *key)
 {
 	audit_log_format(ab, " key=");
@@ -1935,6 +1968,8 @@ void audit_log_link_denied(const char *operation, struct path *link)
 	audit_log_format(ab, " res=0");
 	audit_log_end(ab);
 
+	audit_log_ns_info(current);
+
 	/* Generate AUDIT_PATH record with object. */
 	name->type = AUDIT_TYPE_NORMAL;
 	audit_copy_inode(name, link->dentry, link->dentry->d_inode);
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 4b89f7f..3ca3416 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1378,6 +1378,8 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
 	audit_log_key(ab, context->filterkey);
 	audit_log_end(ab);
 
+	audit_log_ns_info(tsk);
+
 	for (aux = context->aux; aux; aux = aux->next) {
 
 		ab = audit_log_start(context, GFP_KERNEL, aux->type);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index d9cd5ce..58ac695 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -323,6 +323,8 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
 	audit_log_task_info(ab, current);
 	audit_log_end(ab);
 
+	audit_log_ns_info(current);
+
 	iint->flags |= IMA_AUDITED;
 }
 
-- 
1.7.1

^ permalink raw reply related

* [PATCH V6 04/10] audit: initialize at subsystem time rather than device time
From: Richard Guy Briggs @ 2015-04-17  7:35 UTC (permalink / raw)
  To: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-audit-H+wXaHxf7aLQT0dZR+AlfA
  Cc: pmoore-H+wXaHxf7aLQT0dZR+AlfA, eparis-FjpueFixGhCM4zKIHC2jIg,
	sgrubb-H+wXaHxf7aLQT0dZR+AlfA,
	zohar-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
	ebiederm-aS9lmoZGLiVWk0Htik3J/w
In-Reply-To: <cover.1429252659.git.rgb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

The audit subsystem should be initialized a bit earlier so that it is in place
in time for initial namespace ID number logging.

Signed-off-by: Richard Guy Briggs <rgb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 kernel/audit.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/kernel/audit.c b/kernel/audit.c
index 68200ce..63f32f4 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1188,7 +1188,7 @@ static int __init audit_init(void)
 
 	return 0;
 }
-__initcall(audit_init);
+subsys_initcall(audit_init);
 
 /* Process kernel command-line parameter at boot time.  audit=0 or audit=1. */
 static int __init audit_enable(char *str)
-- 
1.7.1

^ permalink raw reply related

* [PATCH V6 05/10] audit: log creation and deletion of namespace instances
From: Richard Guy Briggs @ 2015-04-17  7:35 UTC (permalink / raw)
  To: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-audit-H+wXaHxf7aLQT0dZR+AlfA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA,
	viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn,
	pmoore-H+wXaHxf7aLQT0dZR+AlfA, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
	eparis-FjpueFixGhCM4zKIHC2jIg, sgrubb-H+wXaHxf7aLQT0dZR+AlfA,
	zohar-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
	ebiederm-aS9lmoZGLiVWk0Htik3J/w
In-Reply-To: <cover.1429252659.git.rgb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

Log the creation and deletion of namespace instances in all 6 types of
namespaces.

Twelve new audit message types have been introduced:
AUDIT_NS_INIT_MNT       1330    /* Record mount namespace instance creation */
AUDIT_NS_INIT_UTS       1331    /* Record UTS namespace instance creation */
AUDIT_NS_INIT_IPC       1332    /* Record IPC namespace instance creation */
AUDIT_NS_INIT_USER      1333    /* Record USER namespace instance creation */
AUDIT_NS_INIT_PID       1334    /* Record PID namespace instance creation */
AUDIT_NS_INIT_NET       1335    /* Record NET namespace instance creation */
AUDIT_NS_DEL_MNT        1336    /* Record mount namespace instance deletion */
AUDIT_NS_DEL_UTS        1337    /* Record UTS namespace instance deletion */
AUDIT_NS_DEL_IPC        1338    /* Record IPC namespace instance deletion */
AUDIT_NS_DEL_USER       1339    /* Record USER namespace instance deletion */
AUDIT_NS_DEL_PID        1340    /* Record PID namespace instance deletion */
AUDIT_NS_DEL_NET        1341    /* Record NET namespace instance deletion */

As suggested by Eric Paris, there are 12 message types, one for each of
creation and deletion, one for each type of namespace so that text searches are
easier in conjunction with the AUDIT_NS_INFO message type, being able to search
for all records such as "netns=4 " and to avoid fields disappearing per message
type to make ausearch more efficient.

A typical startup would look roughly like:

	type=AUDIT_NS_INIT_UTS msg=audit(1408577534.868:5): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_utsns=(none) utsns=-2 res=1
	type=AUDIT_NS_INIT_USER msg=audit(1408577534.868:6): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_userns=(none) userns=-3 res=1
	type=AUDIT_NS_INIT_PID msg=audit(1408577534.868:7): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_pidns=(none) pidns=-4 res=1
	type=AUDIT_NS_INIT_MNT msg=audit(1408577534.868:8): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_mntns=(none) mntns=0 res=1
	type=AUDIT_NS_INIT_IPC msg=audit(1408577534.868:9): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_ipcns=(none) ipcns=-1 res=1
	type=AUDIT_NS_INIT_NET msg=audit(1408577533.500:10): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=kernel dev=00:03 old_netns=(none) netns=2 res=1

And a CLONE action would result in:
	type=type=AUDIT_NS_INIT_NET msg=audit(1408577535.306:81): pid=481 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 dev=00:03 old_netns=2 netns=3 res=1

While deleting a namespace would result in:
	type=type=AUDIT_NS_DEL_MNT msg=audit(1408577552.221:85): pid=481 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 dev=00:03 mntns=4 res=1

If not "(none)", old_XXXns lists the namespace from which it was cloned.

Signed-off-by: Richard Guy Briggs <rgb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 fs/namespace.c             |   13 +++++++++
 include/linux/audit.h      |    8 +++++
 include/uapi/linux/audit.h |   12 ++++++++
 ipc/namespace.c            |   12 ++++++++
 kernel/audit.c             |   64 ++++++++++++++++++++++++++++++++++++++++++++
 kernel/pid_namespace.c     |   13 +++++++++
 kernel/user_namespace.c    |   13 +++++++++
 kernel/utsname.c           |   12 ++++++++
 net/core/net_namespace.c   |   12 ++++++++
 9 files changed, 159 insertions(+), 0 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index 182bc41..7b62543 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -24,6 +24,7 @@
 #include <linux/proc_ns.h>
 #include <linux/magic.h>
 #include <linux/bootmem.h>
+#include <linux/audit.h>
 #include "pnode.h"
 #include "internal.h"
 
@@ -2459,6 +2460,7 @@ dput_out:
 
 static void free_mnt_ns(struct mnt_namespace *ns)
 {
+	audit_log_ns_del(AUDIT_NS_DEL_MNT, ns->proc_inum);
 	proc_free_inum(ns->proc_inum);
 	put_user_ns(ns->user_ns);
 	kfree(ns);
@@ -2518,6 +2520,7 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
 	new_ns = alloc_mnt_ns(user_ns);
 	if (IS_ERR(new_ns))
 		return new_ns;
+	audit_log_ns_init(AUDIT_NS_INIT_MNT, ns->proc_inum, new_ns->proc_inum);
 
 	namespace_lock();
 	/* First pass: copy the tree topology */
@@ -2830,6 +2833,16 @@ static void __init init_mount_tree(void)
 	set_fs_root(current->fs, &root);
 }
 
+/* log the ID of init mnt namespace after audit service starts */
+static int __init mnt_ns_init_log(void)
+{
+	struct mnt_namespace *init_mnt_ns = init_task.nsproxy->mnt_ns;
+
+	audit_log_ns_init(AUDIT_NS_INIT_MNT, 0, init_mnt_ns->proc_inum);
+	return 0;
+}
+late_initcall(mnt_ns_init_log);
+
 void __init mnt_init(void)
 {
 	unsigned u;
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 71698ec..b28dfb0 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -484,6 +484,9 @@ extern void		    audit_log_ns_info(struct task_struct *tsk);
 static inline void	    audit_log_ns_info(struct task_struct *tsk)
 { }
 #endif
+extern void		    audit_log_ns_init(int type, unsigned int old_inum,
+					      unsigned int inum);
+extern void		    audit_log_ns_del(int type, unsigned int inum);
 
 extern int		    audit_update_lsm_rules(void);
 
@@ -542,6 +545,11 @@ static inline void audit_log_task_info(struct audit_buffer *ab,
 { }
 static inline void audit_log_ns_info(struct task_struct *tsk)
 { }
+static inline int audit_log_ns_init(int type, unsigned int old_inum,
+				    unsigned int inum)
+{ }
+static inline int audit_log_ns_del(int type, unsigned int inum)
+{ }
 #define audit_enabled 0
 #endif /* CONFIG_AUDIT */
 static inline void audit_log_string(struct audit_buffer *ab, const char *buf)
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 1ffb151..487cad6 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -111,6 +111,18 @@
 #define AUDIT_PROCTITLE		1327	/* Proctitle emit event */
 #define AUDIT_FEATURE_CHANGE	1328	/* audit log listing feature changes */
 #define AUDIT_NS_INFO		1329	/* Record process namespace IDs */
+#define AUDIT_NS_INIT_MNT	1330	/* Record mount namespace instance creation */
+#define AUDIT_NS_INIT_UTS	1331	/* Record UTS namespace instance creation */
+#define AUDIT_NS_INIT_IPC	1332	/* Record IPC namespace instance creation */
+#define AUDIT_NS_INIT_USER	1333	/* Record USER namespace instance creation */
+#define AUDIT_NS_INIT_PID	1334	/* Record PID namespace instance creation */
+#define AUDIT_NS_INIT_NET	1335	/* Record NET namespace instance creation */
+#define AUDIT_NS_DEL_MNT	1336	/* Record mount namespace instance deletion */
+#define AUDIT_NS_DEL_UTS	1337	/* Record UTS namespace instance deletion */
+#define AUDIT_NS_DEL_IPC	1338	/* Record IPC namespace instance deletion */
+#define AUDIT_NS_DEL_USER	1339	/* Record USER namespace instance deletion */
+#define AUDIT_NS_DEL_PID	1340	/* Record PID namespace instance deletion */
+#define AUDIT_NS_DEL_NET	1341	/* Record NET namespace instance deletion */
 
 #define AUDIT_AVC		1400	/* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR	1401	/* Internal SE Linux Errors */
diff --git a/ipc/namespace.c b/ipc/namespace.c
index 59451c1..73727ce 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -13,6 +13,7 @@
 #include <linux/mount.h>
 #include <linux/user_namespace.h>
 #include <linux/proc_ns.h>
+#include <linux/audit.h>
 
 #include "util.h"
 
@@ -41,6 +42,8 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
 	}
 	atomic_inc(&nr_ipc_ns);
 
+	audit_log_ns_init(AUDIT_NS_INIT_IPC, old_ns->proc_inum, ns->proc_inum);
+
 	sem_init_ns(ns);
 	msg_init_ns(ns);
 	shm_init_ns(ns);
@@ -119,6 +122,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)
 	 */
 	ipcns_notify(IPCNS_REMOVED);
 	put_user_ns(ns->user_ns);
+	audit_log_ns_del(AUDIT_NS_DEL_IPC, ns->proc_inum);
 	proc_free_inum(ns->proc_inum);
 	kfree(ns);
 }
@@ -197,3 +201,11 @@ const struct proc_ns_operations ipcns_operations = {
 	.install	= ipcns_install,
 	.inum		= ipcns_inum,
 };
+
+/* log the ID of init IPC namespace after audit service starts */
+static int __init ipc_namespaces_init(void)
+{
+	audit_log_ns_init(AUDIT_NS_INIT_IPC, 0, init_ipc_ns.proc_inum);
+	return 0;
+}
+late_initcall(ipc_namespaces_init);
diff --git a/kernel/audit.c b/kernel/audit.c
index 63f32f4..e6230c4 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1978,6 +1978,70 @@ out:
 	kfree(name);
 }
 
+#ifdef CONFIG_NAMESPACES
+static char *ns_name[] = {
+	"mnt",
+	"uts",
+	"ipc",
+	"user",
+	"pid",
+	"net",
+};
+
+/**
+ * audit_log_ns_init - report a namespace instance creation
+ * @type: type of audit namespace instance created message
+ * @old_inum: the ID number of the cloned namespace instance
+ * @inum: the ID number of the new namespace instance
+ */
+void  audit_log_ns_init(int type, unsigned int old_inum, unsigned int inum)
+{
+	struct audit_buffer *ab;
+	char *audit_ns_name = ns_name[type - AUDIT_NS_INIT_MNT];
+	struct vfsmount *mnt = task_active_pid_ns(current)->proc_mnt;
+	struct super_block *sb = mnt->mnt_sb;
+	char old_ns[16];
+
+	if (type < AUDIT_NS_INIT_MNT || type > AUDIT_NS_INIT_NET) {
+		WARN(1, "audit_log_ns_init: type:%d out of range", type);
+		return;
+	}
+	if (!old_inum)
+		sprintf(old_ns, "(none)");
+	else
+		sprintf(old_ns, "%d", old_inum - PROC_DYNAMIC_FIRST);
+	audit_log_common_recv_msg(&ab, type);
+	audit_log_format(ab, " dev=%02x:%02x old_%sns=%s %sns=%d res=1",
+			 MAJOR(sb->s_dev), MINOR(sb->s_dev),
+			 audit_ns_name, old_ns,
+			 audit_ns_name, inum - PROC_DYNAMIC_FIRST);
+	audit_log_end(ab);
+}
+
+/**
+ * audit_log_ns_del - report a namespace instance deleted
+ * @type: type of audit namespace instance deleted message
+ * @inum: the ID number of the namespace instance
+ */
+void audit_log_ns_del(int type, unsigned int inum)
+{
+	struct audit_buffer *ab;
+	char *audit_ns_name = ns_name[type - AUDIT_NS_DEL_MNT];
+	struct vfsmount *mnt = task_active_pid_ns(current)->proc_mnt;
+	struct super_block *sb = mnt->mnt_sb;
+
+	if (type < AUDIT_NS_DEL_MNT || type > AUDIT_NS_DEL_NET) {
+		WARN(1, "audit_log_ns_del: type:%d out of range", type);
+		return;
+	}
+	audit_log_common_recv_msg(&ab, type);
+	audit_log_format(ab, " dev=%02x:%02x %sns=%d res=1",
+			 MAJOR(sb->s_dev), MINOR(sb->s_dev), audit_ns_name,
+			 inum - PROC_DYNAMIC_FIRST);
+	audit_log_end(ab);
+}
+#endif /* CONFIG_NAMESPACES */
+
 /**
  * audit_log_end - end one audit record
  * @ab: the audit_buffer
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index db95d8e..d28fd14 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -18,6 +18,7 @@
 #include <linux/proc_ns.h>
 #include <linux/reboot.h>
 #include <linux/export.h>
+#include <linux/audit.h>
 
 struct pid_cache {
 	int nr_ids;
@@ -109,6 +110,9 @@ static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns
 	if (err)
 		goto out_free_map;
 
+	audit_log_ns_init(AUDIT_NS_INIT_PID, parent_pid_ns->proc_inum,
+			  ns->proc_inum);
+
 	kref_init(&ns->kref);
 	ns->level = level;
 	ns->parent = get_pid_ns(parent_pid_ns);
@@ -142,6 +146,7 @@ static void destroy_pid_namespace(struct pid_namespace *ns)
 {
 	int i;
 
+	audit_log_ns_del(AUDIT_NS_DEL_PID, ns->proc_inum);
 	proc_free_inum(ns->proc_inum);
 	for (i = 0; i < PIDMAP_ENTRIES; i++)
 		kfree(ns->pidmap[i].page);
@@ -388,3 +393,11 @@ static __init int pid_namespaces_init(void)
 }
 
 __initcall(pid_namespaces_init);
+
+/* log the ID of init PID namespace after audit service starts */
+static __init int pid_namespaces_late_init(void)
+{
+	audit_log_ns_init(AUDIT_NS_INIT_PID, 0, init_pid_ns.proc_inum);
+	return 0;
+}
+late_initcall(pid_namespaces_late_init);
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index fcc0256..89c2517 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -22,6 +22,7 @@
 #include <linux/ctype.h>
 #include <linux/projid.h>
 #include <linux/fs_struct.h>
+#include <linux/audit.h>
 
 static struct kmem_cache *user_ns_cachep __read_mostly;
 
@@ -92,6 +93,9 @@ int create_user_ns(struct cred *new)
 		return ret;
 	}
 
+	audit_log_ns_init(AUDIT_NS_INIT_USER, parent_ns->proc_inum,
+			  ns->proc_inum);
+
 	atomic_set(&ns->count, 1);
 	/* Leave the new->user_ns reference with the new user namespace. */
 	ns->parent = parent_ns;
@@ -136,6 +140,7 @@ void free_user_ns(struct user_namespace *ns)
 #ifdef CONFIG_PERSISTENT_KEYRINGS
 		key_put(ns->persistent_keyring_register);
 #endif
+		audit_log_ns_del(AUDIT_NS_DEL_USER, ns->proc_inum);
 		proc_free_inum(ns->proc_inum);
 		kmem_cache_free(user_ns_cachep, ns);
 		ns = parent;
@@ -909,3 +914,11 @@ static __init int user_namespaces_init(void)
 	return 0;
 }
 subsys_initcall(user_namespaces_init);
+
+/* log the ID of init user namespace after audit service starts */
+static __init int user_namespaces_late_init(void)
+{
+	audit_log_ns_init(AUDIT_NS_INIT_USER, 0, init_user_ns.proc_inum);
+	return 0;
+}
+late_initcall(user_namespaces_late_init);
diff --git a/kernel/utsname.c b/kernel/utsname.c
index fd39312..fa21e8d 100644
--- a/kernel/utsname.c
+++ b/kernel/utsname.c
@@ -16,6 +16,7 @@
 #include <linux/slab.h>
 #include <linux/user_namespace.h>
 #include <linux/proc_ns.h>
+#include <linux/audit.h>
 
 static struct uts_namespace *create_uts_ns(void)
 {
@@ -48,6 +49,8 @@ static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns,
 		return ERR_PTR(err);
 	}
 
+	audit_log_ns_init(AUDIT_NS_INIT_UTS, old_ns->proc_inum, ns->proc_inum);
+
 	down_read(&uts_sem);
 	memcpy(&ns->name, &old_ns->name, sizeof(ns->name));
 	ns->user_ns = get_user_ns(user_ns);
@@ -84,6 +87,7 @@ void free_uts_ns(struct kref *kref)
 
 	ns = container_of(kref, struct uts_namespace, kref);
 	put_user_ns(ns->user_ns);
+	audit_log_ns_del(AUDIT_NS_DEL_UTS, ns->proc_inum);
 	proc_free_inum(ns->proc_inum);
 	kfree(ns);
 }
@@ -138,3 +142,11 @@ const struct proc_ns_operations utsns_operations = {
 	.install	= utsns_install,
 	.inum		= utsns_inum,
 };
+
+/* log the ID of init UTS namespace after audit service starts */
+static int __init uts_namespaces_init(void)
+{
+	audit_log_ns_init(AUDIT_NS_INIT_UTS, 0, init_uts_ns.proc_inum);
+	return 0;
+}
+late_initcall(uts_namespaces_init);
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 85b6269..562eb85 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -17,6 +17,7 @@
 #include <linux/user_namespace.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
+#include <linux/audit.h>
 
 /*
  *	Our network namespace constructor/destructor lists
@@ -253,6 +254,8 @@ struct net *copy_net_ns(unsigned long flags,
 	mutex_lock(&net_mutex);
 	rv = setup_net(net, user_ns);
 	if (rv == 0) {
+		audit_log_ns_init(AUDIT_NS_INIT_NET, old_net->proc_inum,
+				  net->proc_inum);
 		rtnl_lock();
 		list_add_tail_rcu(&net->list, &net_namespace_list);
 		rtnl_unlock();
@@ -389,6 +392,7 @@ static __net_init int net_ns_net_init(struct net *net)
 
 static __net_exit void net_ns_net_exit(struct net *net)
 {
+	audit_log_ns_del(AUDIT_NS_DEL_NET, net->proc_inum);
 	proc_free_inum(net->proc_inum);
 }
 
@@ -435,6 +439,14 @@ static int __init net_ns_init(void)
 
 pure_initcall(net_ns_init);
 
+/* log the ID of init_net namespace after audit service starts */
+static int __init net_ns_init_log(void)
+{
+	audit_log_ns_init(AUDIT_NS_INIT_NET, 0, init_net.proc_inum);
+	return 0;
+}
+late_initcall(net_ns_init_log);
+
 #ifdef CONFIG_NET_NS
 static int __register_pernet_operations(struct list_head *list,
 					struct pernet_operations *ops)
-- 
1.7.1

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox