ioremap.net

Storage and beyond

Inotify PIDs and security.

Looks like my inotify patch was rejected because of security violation. This may sound as somewhat security flaw to allow any process to watch what IO is performed by other processes. Well, it is a valid observation, so I created new version, which only puts PID into the inotify message when either UID of the inotify backend is 0 or equals to UID of the process doing IO. So far without any comments though.

Since I can not get that this it a security flaw, and arguing about that will be endless, I accept that this limitation is valid. In the same line of ‘security’ flaws could be placed following small information leak I found in inotify.

In classical UNIX permission model it is not allowed to get directory listing if it does not have read permission and change directory to given one if it does not have execute bit. Even if you have created directory with some permissions/owner bits, switched current dir to this newly creaated, and then changed its permissions/owner bit, you will not be able to see neither its content nor newly created objects . Here is an example:

$ mkdir /tmp/test
$ chmod 700 /tmp/test
$ cd /tmp/test
/tmp/test$ ls -lai
total 24
9486756 drwx------  2 zbr  zbr   4096 2008-11-10 15:40 .
 123969 drwxrwxrwt 34 root root 20480 2008-11-10 15:40 ..
/tmp/test$ sudo chown 0.0 .
[sudo] password for zbr:
/tmp/test$ ls -lai
ls: .: Permission denied
/tmp/test$

With inotify you are able to watch what is being done in directory (or actually in any object) if you were able to attach a watch to its inode. So, if object had read permission, we are able to attach a watch to it, so if later it will change its permissions, watches will not be removed and we will be able to watch its content. Like this:

libionotify-1.1$ LD_PRELOAD=./libionotify.so ./inotify -r /tmp/
CREATE: /tmp/test
CREATE: /tmp/test/test1
WRITE : /tmp/test/test1
CREATE: /tmp/test/test2
WRITE : /tmp/test/test2
READ  : /tmp/test/test2

while in parallel we do:

$ mkdir /tmp/test
$ chmod 700 /tmp/test
$ cd /tmp/test
/tmp/test$ sudo chown 0.0 .
/tmp/test$ sudo dd if=/dev/zero of=./test1 bs=4k count=1
1+0 records in
1+0 records out
4096 bytes (4.1 kB) copied, 0.000326018 seconds, 12.6 MB/s
/tmp/test$ sudo dd if=/dev/zero of=./test2 bs=4k count=1
1+0 records in
1+0 records out
4096 bytes (4.1 kB) copied, 0.000321779 seconds, 12.7 MB/s
/tmp/test$ sudo cat ./test2

Fix would be to check inotify watch list for given inode when its permissions are changed.

Inotify is watching you!

Comments are currently closed.

12 Responses to “Inotify PIDs and security.”

  • Anonymous says:

    Hi Evgeniy,

    Instead of hardcoding uid 0, you might try checking for the proper capabilities instead. It is a much cleaner approach and less likely to be NAK’d. For something like your inotify patch, CAP_SYS_ADMIN might make sense? Take a look at capabilities(7)

    Here is a hokey 1liner to get the capabilities your kernel supports:
    awk ‘/^#define/{if ($2 ~ “CAP_”) print $2}’ /usr/include/linux/capability.h


    Jeff Schroeder
    http://www.digitalprognosis.com

  • Anonymous says:

    Actually this is better:
    awk ‘/^#define/{if ($2 ~ “^CAP_”) print $2}’ /usr/include/linux/capability.h


    Jeff Schroeder
    http://www.digitalprognosis.com

  • zbr says:

    Makes perfect sense, thank you.
    That’s the patch I’ve just submitted:

    diff --git a/fs/inotify.c b/fs/inotify.c
    index 690e725..835259d 100644
    --- a/fs/inotify.c
    +++ b/fs/inotify.c
    @@ -69,6 +69,9 @@ static atomic_t inotify_cookie;
      * inotify_add_watch() to the final put_inotify_watch().
      */
     
    +#define IH_FLAGS_ADMIN         (0x00000001)
    +/* handler owner has admin capabilities */
    +
     /*
      * struct inotify_handle - represents an inotify instance
      *
    @@ -80,6 +83,8 @@ struct inotify_handle {
            struct list_head        watches;        /* list of watches */
            atomic_t                count;          /* reference count */
            u32                     last_wd;        /* the last wd allocated */
    +       uid_t                   uid;            /* owner's uid */
    +       u32                     flags;          /* operation flags */
            const struct inotify_operations *in_ops; /* inotify caller operations */
     };
     
    @@ -292,6 +297,11 @@ void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie,
                            mutex_lock(&ih->mutex);
                            if (watch_mask & IN_ONESHOT)
                                    remove_watch_no_event(watch, ih);
    +
    +                       if (!cookie && ((ih->flags & IH_FLAGS_ADMIN) ||
    +                                       (current->uid == ih->uid)))
    +                               cookie = task_tgid_vnr(current);
    +
                            ih->in_ops->handle_event(watch, watch->wd, mask, cookie,
                                                     name, n_inode);
                            mutex_unlock(&ih->mutex);
    @@ -459,6 +469,10 @@ struct inotify_handle *inotify_init(const struct inotify_operations *ops)
            mutex_init(&ih->mutex);
            ih->last_wd = 0;
            ih->in_ops = ops;
    +       ih->uid = current->user->uid;
    +       ih->flags = 0;
    +       if (capable(CAP_SYS_ADMIN))
    +               ih->flags |= IH_FLAGS_ADMIN;
            atomic_set(&ih->count, 0);
            get_inotify_handle(ih);
     
  • Anonymous says:

    I’m confused if your patch to inotify outputs the PID that modified a file. I have an errant process that keeps nulling out one of my web home pages. Trying to see if hacked, but I need the process ID when the file gets modified. Does this patch do that? If not, any good linux utils that do? All inotify does for me is tell me that the file was modified, but not by whom. Actually, I know the file is modified by ‘root’, so really I need the actual PID. Any help is appreciated!

  • zbr says:

    of the prodcess which modified the file. If you use threads, it will actually be a thread ID.

  • phocean says:

    Hi,

    Thanks a lot for this nice work. First I couldn’t believe that libionotify does not have this feature.
    I would like to set it up on a folder where some files mysteriously disappear from time to time.
    It would help me to found out what PID is responsible of this mess. I would also use it for security purpose on my servers.

    But, I have one issue compiling your code :

    % make
    gcc -W -Wall -g -I/home/zbr/aWork/git/linux-2.6/linux-2.6.pohmelfs/ -I/home/zbr/aWork/git/linux-2.6/linux-2.6.pohmelfs//include -c -o inotify.o inotify.c
    gcc -W -Wall -g -I/home/zbr/aWork/git/linux-2.6/linux-2.6.pohmelfs/ -I/home/zbr/aWork/git/linux-2.6/linux-2.6.pohmelfs//include -c -o rbtree.o rbtree.c
    gcc -fPIC -shared -rdynamic -Wl,-soname,libionotify.so inotify.o rbtree.o -o libionotify.so
    /usr/lib64/gcc/x86_64-suse-linux/4.3/../../../../x86_64-suse-linux/bin/ld: inotify.o: relocation R_X86_64_32S against `a local symbol' can not be used when making a shared object; recompile with -fPIC
    inotify.o: could not read symbols: Bad value
    collect2: ld returned 1 exit status
    make: *** [libionotify.so] Erreur 1
    [1] 12765 exit 2 make

    It seems related to the fact that I am running on a 64 bits system. At least, it is what I understood from googling around.
    But I could not find an appropriate solution and my programming skills are too limited to go over it.

    Do you have any idea please ?

  • zbr says:

    Try adding -fPIC option to the compiler flags (CFLAGS variable in the makefile) and recompile the binaries, it should help.

  • phocean says:

    it did the trick ! Thanks a lot.

  • Anonymous says:

    Is it possible to extract the name of the user who made an action on the file (renamed, read, delete, etc.) ?
    My scenario is a bit more complicated. Let say I have a SAMBA server users are accesing. I want to know the name of the user who peformed an action on the file through SAMBA. The PID I receive will be of the SAMBA deamon, so how do I extract the user?

  • zbr says:

    You do not need inotify for this – samba supports extended logging which can dump needed info. This will be rather heavy though.

  • Anonymous says:

    Thanks for the reply!
    to generate to logs I need a pretty large amount of data will be generated by Samba. There are hunderds of users accessing files in a second.
    If I still would like to pin-point a specific file with inotify, and get the username, is that possible ?
    I am also interested in the ip address of the user who accessed the file. is that even possible in the filesystem level with inotify ?
    thanks

  • zbr says:

    No, it is not possible without samba hacks. Neither filesystem level nor inotify have this data.