On Tue, Nov 6, 2018 at 12:30 AM Paul Moore <paul(a)paul-moore.com> wrote:
On Wed, Oct 31, 2018 at 4:54 AM Ondrej Mosnacek
<omosnace(a)redhat.com> wrote:
> On Wed, Sep 19, 2018 at 5:44 PM Paul Moore <paul(a)paul-moore.com> wrote:
> > On Wed, Sep 19, 2018 at 7:01 AM Ondrej Mosnacek <omosnace(a)redhat.com>
wrote:
> > > On Wed, Sep 19, 2018 at 3:35 AM Paul Moore <paul(a)paul-moore.com>
wrote:
> > > > On Thu, Sep 13, 2018 at 10:13 AM Paul Moore
<paul(a)paul-moore.com> wrote:
> > > > > On Thu, Sep 13, 2018 at 9:58 AM Ondrej Mosnacek
<omosnace(a)redhat.com> wrote:
> > > > > > Paul, could you please answer this question so I can move
forward? :)
> > > > >
> > > > > Yep, sorry for the delay ...
> > > >
> > > > I just went back over the original problem, your proposed fix, and
all
> > > > of the discussion in this thread.
> > > >
> > > > Sadly, I don't think the patch you have proposed is the right
fix.
> > > >
> > > > As Steve has pointed out, the CWD path is the working directory from
> > > > which the current process was executed. I believe we should log the
> > > > full path, or as complete a path as possible, in the nametype=CWD
PATH
> > > > records. While the nametype=PARENT PATH records have a connection
> > > > with some of the other PATH records (e.g. DELETE and CREATE), the
> > > > nametype=PARENT PATH records are independent of the current working
> > > > directory, although they sometimes may be the same; in the cases
where
> > > > they are the same, this is purely a coincidence and is due to
> > > > operation being performed, not something that should be seen as a
> > > > flaw.
> > > >
> > > > From what I can tell, there are issues involving the nametype=PARENT
> > > > PATH records, especially when it comes to the *at() syscalls, but no
> > > > issue where the nametype=CWD PATH records have been wrong, is that
> > > > correct?
> > >
> > > Sorry, but I think you are completely misunderstanding the problem...
> > > I tried to explain it several times in different ways, but apparently
> > > I'm still not doing it right...
> > >
> > > First of all, there is no "nametype=CWD" PATH record. There is
only
> > > the classic CWD record that is associated to every syscall and I
don't
> > > touch that one at all. The information in the CWD record is perfectly
> > > fine.
> >
> > Yes, that was a casualty of me looking at too many audit logs too late
> > in the day, I mistakenly typed it as a nametype PATH record when CWD
> > is its own record type. My apologies.
> >
> > > Let me try to demonstrate it with some more verbose examples. (TL;DR:
> > > The info in the CWD record is correct, but it is being abused in
> > > audit_log_name().)
> > >
> > > EXAMPLE #1 (The non-edge case):
> > > 1. A userspace process calls rename("dir1/file1",
"dir2/file2") with
> > > CWD set to "/home/user".
> > > 2. The syscall causes four calls to __audit_inode(), which generate
> > > four 'struct audit_names' objects with the following information
> > > (maybe not in this specific order, but that doesn't matter):
> > > .name = "dir1/file1", .type = AUDIT_TYPE_PARENT, .name_len = 5
> > > .name = "dir1/file1", .type = AUDIT_TYPE_DELETE, .name_len =
AUDIT_NAME_FULL
> > > .name = "dir2/file2", .type = AUDIT_TYPE_PARENT, .name_len = 5
> > > .name = "dir2/file2", .type = AUDIT_TYPE_CREATE, .name_len =
AUDIT_NAME_FULL
> > > 3. At the end of the syscall, audit_log_name() is called on each of
> > > these objects and produces the following PATH records (simplifed):
> > > nametype=PARENT name="dir1/"
> > > nametype=DELETE name="dir1/file1"
> > > nametype=PARENT name="dir2/"
> > > nametype=CREATE name="dir2/file2"
> > >
> > > Notice that for the PARENT objects the .name_len is truncated to only
> > > the directory component.
> > >
> > > EXAMPLE #2 (The single-path-component case):
> > > 1. A userspace process calls rename("file1", "file2")
with CWD set to
> > > "/home/user".
> > > 2. The 'struct audit_names' objects will now be:
> > > .name = "file1", .type = AUDIT_TYPE_PARENT, .name_len = 0
> > > .name = "file1", .type = AUDIT_TYPE_DELETE, .name_len =
AUDIT_NAME_FULL
> > > .name = "file2", .type = AUDIT_TYPE_PARENT, .name_len = 0
> > > .name = "file2", .type = AUDIT_TYPE_CREATE, .name_len =
AUDIT_NAME_FULL
> > > 3. At the end of the syscall, audit_log_name() is called on each of
> > > these objects and produces the following PATH records (simplifed):
> > > nametype=PARENT name="/home/user"
> > > nametype=DELETE name="file1"
> > > nametype=PARENT name="/home/user"
> > > nametype=CREATE name="file2"
> > >
> > > Notice that in this case, the "clever" logic in
audit_log_name()
> > > wanted to avoid logging an empty path (name="") in the PARENT
records,
> > > so it instead put the CWD path in there ("/home/user"). In this
case
> > > this is perfectly valid (although could be a bit surprising that there
> > > is suddenly a full path instead of a relative one), since the full
> > > path of "file1" is actually "/home/user/file1".
> >
> > A quick comment on the "surprising" nature of seeing the
"/home/user"
> > path in the PARENT record instead of "/home/user/file1" - the PARENT
> > record is the file's parent directory (as you mention above), so it
> > would be surprising to see "/home/user/file1" in the PARENT record,
> > seeing just "/home/user" is valid and could be expected.
>
> Wait, no... I meant it is surprising that there is suddenly a full
> path to directory ("/home/user") instead of a relative one (which
> would be "." in this case). This happens if and only if the original
> relative path has just a single component, which is a strange
> condition for anyone who doesn't know how the audit_log_name()
> function is implemented. The fact that the PARENT record has a path
> to the parent si obviously logical and sound, I have no problem with
> that :)
>
> >
> > > EXAMPLE #3 (The non-edge renameat(2) case):
> > > 1. A userspace process calls the following syscalls (with CWD set to
> > > "/home/user"):
> > > int srcfd = open("/some/path1", O_DIRECTORY | O_PATH);
> > > int dstfd = open("/another/path2", O_DIRECTORY | O_PATH);
> > > renameat(srcfd, "dir1/file1", dstfd, "dir2/file2");
> > > 2. The 'name', 'type' and 'name_len' fields of the
'struct
> > > audit_names' objects will now be exactly the same as in EXAMPLE #1:
> > > .name = "dir1/file1", .type = AUDIT_TYPE_PARENT, .name_len = 5
> > > .name = "dir1/file1", .type = AUDIT_TYPE_DELETE, .name_len =
AUDIT_NAME_FULL
> > > .name = "dir2/file2", .type = AUDIT_TYPE_PARENT, .name_len = 5
> > > .name = "dir2/file2", .type = AUDIT_TYPE_CREATE, .name_len =
AUDIT_NAME_FULL
> > > 3. The type and name information in the PATH records will be also
> > > exactly the same:
> > > nametype=PARENT name="dir1/"
> > > nametype=DELETE name="dir1/file1"
> > > nametype=PARENT name="dir2/"
> > > nametype=CREATE name="dir2/file2"
> > >
> > > Even in this case the information in the records is correct (although
> > > there is no information telling us that "dir1/..." actually
> > > corresponds to "/some/path1/dir1/..." and "dir2/..."
actually
> > > corresponds to "/another/path2/dir2/...").
> >
> > Yeah, I'm starting to think we should always log the absolute path in
> > the PARENT record.
> >
> > > So far so good, but now we are coming to...
> > >
> > > EXAMPLE #4 (The single-path-component renameat(2) case):
> > > 1. A userspace process calls the following syscalls (with CWD set to
> > > "/home/user"):
> > > int srcfd = open("/some/path1", O_DIRECTORY | O_PATH);
> > > int dstfd = open("/another/path2", O_DIRECTORY | O_PATH);
> > > renameat(srcfd, "file1", dstfd, "file2");
> > > 2. The 'name', 'type' and 'name_len' fields of the
'struct
> > > audit_names' objects will now be exactly the same as in EXAMPLE #2:
> > > .name = "file1", .type = AUDIT_TYPE_PARENT, .name_len = 0
> > > .name = "file1", .type = AUDIT_TYPE_DELETE, .name_len =
AUDIT_NAME_FULL
> > > .name = "file2", .type = AUDIT_TYPE_PARENT, .name_len = 0
> > > .name = "file2", .type = AUDIT_TYPE_CREATE, .name_len =
AUDIT_NAME_FULL
> > > 3. The type and name information in the PATH records will be also
> > > exactly the same:
> > > nametype=PARENT name="/home/user"
> > > nametype=DELETE name="file1"
> > > nametype=PARENT name="/home/user"
> > > nametype=CREATE name="file2"
> > >
> > > ...and HERE is the problem. The parent of "file1" is not
"/home/user",
> > > it is "/some/path1", and the parent of "file2" is also
not
> > > "/home/user", it is "/another/path2".
> > >
> > > The proposed fix simply changes the handling of the name_len == 0 case
> > > to output "." instead of the CWD. This doesn't solve the
wider problem
> > > that we don't have the dirfd path information (GHAK #9), but it at
> > > least makes the situation in EXAMPLE #4 *not worse* than in EXAMPLE #3
> > > (i.e. it fixes the less demanding GHAK #95). As an additional minor
> > > benefit it also brings a bit more consistency - with it the PATH
> > > records will contain relative (resp. absolute) paths if and only if
> > > the corresponding path given to the syscall was relative (resp.
> > > absolute).
> > >
> > > I hope this finally clears things up.
> >
> > Yes, it does, thanks. My apologies for getting tangled up in the CWD logging.
> >
> > My current thinking is that logging relative paths in the
> > nametype=PARENT PATH record is a mistake. Yes, I understand there are
> > some cases where this information will be the same as the CWD
> > information, and that could add some additional log overhead, but as
> > tricky as path resolution can be I think this is the safest option and
> > the one I would like to see us pursue. This will likely require some
> > extra work for the *at() syscalls, but those aren't reported correctly
> > right now as discussed here and elsewhere.
> >
> > I would expect the non-PARENT PATH records to stay as they are, in
> > other words this should only affect things which are *not* (.name_len
> > == AUDIT_NAME_FULL).
>
> Well, logging (correct) absolute path in *all* PATH records would
> certainly solve the problem (and also GHAK #9) and considering all the
> problems with relative paths it might even be the most reasonable
> solution. However, doing so only in the case of PARENT records
> doesn't seem like a good idea to me... Consider the first two
> arguments of a renameat(2) syscall with olddirfd pointing to
> "/some/dir" and an oldpath of "subdir/file". We would get PATH
> records like this:
>
> nametype=PARENT path="/some/dir/subdir"
> nametype=DELETE path="subdir/file"
> [...]
Let's reset this discussion a bit ... if we abolish relative paths and
make everything absolute, is there even a need to log PARENT?
If there ever was such need, then this won't change when we switch to
absolute paths. The PATH records contain some fields (inode, dev, obj,
...) that can be different for the child and parent and I would say
these are the only new information that the PARENT records provide
over the corresponding CREATE/DELETE records. The parent's path could
be always inferred from the CREATE/DELETE record anyway (by simply
removing the last element). I think the only reason that PARENT PATH
records contain a "path" field is simply consistency (it is a
mandatory field). The advantage of this is that userspace then doesn't
need to jump through the hoops to process the records (the parent path
is "just there", no need to look for other corresponding records).
Of course all of the above are just educated guesses on the original
intentions, but when you look at some actual PATH records it does make
sense (IMHO).
--Ondrej Mosnacek <omosnace at redhat dot com>
Associate Software Engineer, Security Technologies
Red Hat, Inc.