[apparmor] AppArmor profile name and hard link question

Li, Li lili at qca.qualcomm.com
Thu Sep 18 01:30:01 UTC 2014


Hello Seth,

Thanks for the quick response!

Well, I did a little bit more digging myself and looks like the problem I have may not be hard/symb link issues. It's file system issue.

The system I have use something called overlay-filesystem. https://git.kernel.org/cgit/linux/kernel/git/mszeredi/vfs.git/tree/Documentation/filesystems/overlayfs.txt?h=overlayfs.current

The link describe what it does. As I previously thought the two objects/files are hardlink, well it actually may not be, it's the "same" file showing up in both lower and upper layer. So essentially you have two mount points to the same object. It also mean the lookup of a requested object can exist in two "directory" if it exists on both upper and lower. I don't know how significant this have on the AppArmor code, from what I look at, it walks through dentry each time a file object is accessed.

For example, by using overlayfs, we have a lower fs mounted on '/rom', and a higher fs mounted on '/', you will find a exe file under both '/rom/bin/exe' and '/bin/exe' exists. And it's not a "hard link". For all practical purpose, all the access to the file is using upper fs, so no one will even notice there're two fs.
 
I did a bit debugging on the kernel AppArmor code, it looks like a "path" lookup failed each time a file under '/bin/exe' is accessed. But if I run it using '/rom/bin/exe', the lookup worked. I also looked at apparmor/path.c code:

Let's say the path passed in is 'bin/exe', and walk through the code, the function always return error -13, if the path name is the upper fs path. Hope this might help figure out where the problem is.

Lee

 56 static int d_namespace_path(struct path *path, char *buf, int buflen,
 57                             char **name, int flags)
 58 {
... <====== skipped, not executed

81         /* resolve paths relative to chroot?*/
 82         if (flags & PATH_CHROOT_REL) {
 83                 struct path root;
 84                 get_fs_root(current->fs, &root);
 85                 res = __d_path(path, &root, buf, buflen);
 86                 path_put(&root);
 87         } else {
 88                 res = d_absolute_path(path, buf, buflen);          <== here, the res will be '/bin/exe'
 89                 if (!our_mnt(path->mnt))                                        <== somehow the compare of mnt->mnt_ns and current->nsproxy->mnt_ns not equal
 90                         connected = 0;
 91         }
 92 
      <===== skipped
108 
109         *name = res;
110 
      <===== skipped

123         /* If the path is not connected to the expected root,
124          * check if it is a sysctl and handle specially else remove any
125          * leading / that __d_path may have returned.
126          * Unless
127          *     specifically directed to connect the path,
128          * OR
129          *     if in a chroot and doing chroot relative paths and the path
130          *     resolves to the namespace root (would be connected outside
131          *     of chroot) and specifically directed to connect paths to
132          *     namespace root.
133          */
134         if (!connected) {
135                 if (!(flags & PATH_CONNECT_PATH) &&
136                            !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
137                              our_mnt(path->mnt))) {
138                         /* disconnected path, don't return pathname starting
139                          * with '/'
140                          */
141                         error = -EACCES;                                           <====,   it returns error
142                         if (*res == '/')
143                                 *name = res + 1;
144                 }
145         }



-----Original Message-----
From: Seth Arnold [mailto:seth.arnold at canonical.com] 
Sent: Wednesday, September 17, 2014 5:23 PM
To: Li, Li
Cc: apparmor at lists.ubuntu.com
Subject: Re: [apparmor] AppArmor profile name and hard link question

On Wed, Sep 17, 2014 at 12:28:34AM +0000, Li, Li wrote:
> Hello,

Hello Lee,

> I am new to AppArmor. I am trying to port it to an embedded linux 
> platform. One problem I found is related to file system and/or hard 
> link issue.

Welcome aboard!

> The platform I have mount system files under /rom using squashfs first.
> Then mount another file system jffs2 as / and create hard links to all 
> the files under /rom. So it looks to the system everything is under "/".
> The problem is when I create a profile with '/path/tofile' as name, it 
> cannot be constrained even it detects there's a profile for it.  If I 
> create a profile with '/rom/path/tofile', it can detect it only when I 
> run the file using '/rom/path/tofile', not from the hard link 
> '/path/tofile'.

I'm not familiar with squashfs or jffs2 -- do they really allow hardlinks to cross filesystem boundaries? It was my understanding that hardlinks are constrained to live 'within' a single filesystem -- which is one reason why symbolic links were introduced.

Can you prepare a quick reproducer shell script which can be run easily on e.g. a desktop system for testing? I'm curious what exactly has been done.

> I understand there's some issues with links for apparmor to work 
> correctly, but is it already fixed? BTW, the kernel I have is 3.4 and 
> I also applied the 3.4 apparmor patches.

You'll have to be more specific. Here's some points that often help people new to AppArmor understand how it works behind the scenes:

AppArmor (well, the kernel) resolves symbolic links to a 'real' filename before mediation. Permissions aren't checked on symlinks.

A confined progam may get different allowed permissions on a file depending upon which hard link it uses to open the file: a file with multiple hardlinks could have different permissions for different names.

A confined program that creates hardlinks to files needs to have corresponding permissions to do so, and cannot expand its permissions with this mechanism.

A program with multiple hardlinks use the specific hardlink in question when determining attachment. For example, I always have a /tmp/bash profile loaded that allows some very small things necessary for bash to run, but nothing else. (It's great for testing.) But a /tmp/sh hardlink to /tmp/bash doesn't run confined because the profile is for /tmp/bash.

You can run this kind of testing like this:

cat >/etc/apparmor.d/tmp.bash <<EOF
#include <tunables/global>
/tmp/bash {
  #include <abstractions/base>
  /tmp/bash rm,
}
EOF
apparmor_parser --replace /etc/apparmor.d/tmp.bash cp /bin/bash /tmp/bash ln /tmp/bash /tmp/sh /tmp/bash  # notice this is confined, error messages etc ^D
/tmp/sh    # notice this is unconfined, no error messages ^D


I hope these cover your questions, but if you can prepare a quick shell script to demonstrate what you mean, that'll make sure we're not talking at cross-purposes to each other.

Thanks



More information about the AppArmor mailing list