AppArmor security module overview
Subsystems (23)
cover AppArmor security module overview
This post contains patches to include the AppArmor application security framework, with request for inclusion.
The patch series consists of four areas:
(1) Pass struct vfsmount through to LSM hooks.
Tony Jones has posted almost all of these patches here before on February 5; the feedback given has been incorporated.
(2) Fixes and improvements to d_path:
(a) make it unambiguous and exclude unreachable paths from /proc/mounts,
(b) make its result consistent in the face of remounts,
(c) introduce d_namespace_path(), a variant of d_path that goes up to the namespace root instead of the chroot.
Part (a) has been in the -mm tree for more than a month; this series includes a copy of the -mm patch. Parts (b) and (c) shouldn't be too controversial.
(3) Be able to distinguish file descriptor access from access by name in LSM hooks.
Applications expect different behavior from file descriptor accesses and accesses by name in some cases. We need to pass this information down the LSM hooks to allow AppArmor to tell which is which.
(4) The AppArmor LSM itself.
(See below.)
A tarball of the kernel patches, base user-space utilities, example profiles, and technical documentation (including a walk-through) are available at:
http://forgeftp.novell.com//apparmor/LKML_Submission-April_07/
The rest of this introduction gives a brief overview of AppArmor's overall design. We will try to address the concerns people had when AppArmor was previously posted here in April 06 in a follow-up post.
AppArmor's Overall Design
=========================
AppArmor protects systems from vulnerable software by confining processes, giving them "least privilege" access to the system's
resources: with least privilege, processes are allowed exactly what they need, nothing more, and nothing less. Systems are thus protected from bugs in applications that would lead to privilege escalation, such as remote system access because of a buffer overflow in a web server, etc.
AppArmor does this by defining application profiles which list allowed accesses, and assigning those profiles to processes. AppArmor does *not* require the user to confine all processes on the system. Rather, you need to provide AppArmor profiles for every process that is potentially subject to manipulation by the attacker. For instance, to defend against network attack, confine all process that access the network.
The corollary to this is that attacks against AppArmor that start with "assume some unconfined process does ..." are outside the AppArmor threat model. Any process that might do something malicious to an AppArmor system should be confined by an AppArmor profile.
The kernel manages many different kinds of resources. AppArmor currently controls access to two key resources among them: files, and POSIX Capabilities. (Additional protection for things like rlimits, interprocess communication, and network access are being worked on, and are expected to become available in a future version.)
File Access Control
-------------------
Application profiles control file access by pathname: each profile contains a list of fully qualified pathnames (potentially containing globbing) and associated access modes: read (r), write (w), different kinds of execute (ix, Px, px, Ux, ux), create hard-link (l), and memory map for execution (m).
For example, the following two rules permit read access to any file below /srv/www/htdocs/**.html, and memory map for execution (m), execute inheriting the current profile (ix), and read (r) access to /usr/sbin/suexec2:
/srv/www/htdocs/**.html r,
/usr/sbin/suexec2 mixr,POSIX Capabilities
------------------
Application profiles enumerate the POSIX capabilites that a process may use. (A process must have a capability both in its effective capabilities set and in its profile in order to use it. See the capabilities(7) manual page for details on capabilities.)
For example, the following rule allows processes to chroot(2):
capability sys_chroot,
An Example Profile
------------------
Here is an example profile for ntpd:
/usr/sbin/ntpd {
#include <abstractions/base>
#include <abstractions/nameservice>
capability ipc_lock,
capability net_bind_service,
capability setgid,
capability setuid,
capability sys_chroot,
capability sys_resource,
capability sys_time,
/drift/ntp.drift rwl,
/drift/ntp.drift.TEMP rwl,
/etc/ntp.conf r,
/etc/ntp/drift* rwl,
/etc/ntp/keys r,
/etc/ntp/step-tickers r,
/tmp/ntp* rwl,
/usr/sbin/ntpd rmix,
/var/lib/ntp/etc/ntp.conf.iburst r,
/var/lib/ntp/drift rwl,
/var/lib/ntp/drift.TEMP rwl,
/var/lib/ntp/drift/ntp.drift r,
/var/lib/ntp/var/run/ntp/ntpd.pid w,
/var/log/ntp w,
/var/log/ntp.log w,
/var/run/ntpd.pid w,
}Briefly, the /usr/sbin/ntpd in line 1 tells AppArmor to use this profile for /usr/sbin/ntpd. The include lines pull in some predefined abstractions containing frequently used access patterns which allow to keep profiles short, and the capability and file access rules have been explained above.
Explaining the AppArmor design in detail would take by far too much space here, so let me refer you to the technical documentation for that. Included is a low-level walk-through of the system and basic tools, and some examples. The manual pages included in the apparmor-parser package are worth a read as well.
PATCH 1/41 Pass struct vfsmount to the inode_create LSM hook
This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 9 ++++++--- security/dummy.c | 2 +- security/selinux/hooks.c | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-)
fs/namei.c +1 -1
@@ -1503,7 +1503,7 @@ int vfs_create(struct inode *dir, structreturn -EACCES ;/* shouldn't it be ENOSYS? */ mode &=S_IALLUGO ; mode |=S_IFREG ; - error =security_inode_create (dir, dentry, mode); + error =security_inode_create (dir, dentry, nd ? nd->mnt :NULL , mode);if (error)return error;DQUOT_INIT (dir);
include/linux/security.h +6 -3
@@ -283,6 +283,7 @@ struct request_sock;* Check permission to create a regular file. * @dir contains inode structure of the parent of the new file. * @dentry contains the dentry structure for the file to be created. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * @mode contains the file mode of the file to be created. * Return 0 if permission is granted. * @inode_link:
@@ -1204,8 +1205,8 @@ struct security_operations {void (*inode_free_security) (struct inode *inode);int (*inode_init_security) (struct inode *inode,struct inode *dir,char **name,void **value,size_t *len); -int (*inode_create) (struct inode *dir, -struct dentry *dentry,int mode); +int (*inode_create) (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode);int (*inode_link) (struct dentry *old_dentry,struct inode *dir,struct dentry *new_dentry);int (*inode_unlink) (struct inode *dir,struct dentry *dentry);
@@ -1611,11 +1612,12 @@ static inline int security_inode_init_sestatic inline int security_inode_create (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode) {if (unlikely (IS_PRIVATE (dir)))return 0 ; -return security_ops->inode_create (dir, dentry, mode); +return security_ops->inode_create (dir, dentry, mnt, mode); } static inline int security_inode_link (struct dentry *old_dentry,
@@ -2338,6 +2340,7 @@ static inline int security_inode_init_sestatic inline int security_inode_create (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode) {return 0 ;
security/dummy.c +1 -1
@@ -265,7 +265,7 @@ static int dummy_inode_init_security (st }static int dummy_inode_create (struct inode *inode,struct dentry *dentry, -int mask) +struct vfsmount *mnt,int mask) {return 0 ; }
security/selinux/hooks.c +2 -1
@@ -2178,7 +2178,8 @@ static int selinux_inode_init_security(sreturn 0 ; } -static int selinux_inode_create (struct inode *dir,struct dentry *dentry,int mask) +static int selinux_inode_create (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mask) {return may_create (dir, dentry,SECCLASS_FILE ); }
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 16 lines · On Thu, Apr 12, 2007 at 02:08:10AM -0700, [email protected] wrote:
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by Tony Jones <[email protected]>Signed-off-by Andreas Gruenbacher <[email protected]>Signed-off-by John Johansen <[email protected]>--- fs/namei.c | 2 +- include/linux/security.h | 9 ++++++--- security/dummy.c | 2 +- security/selinux/hooks.c | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-)
fs/namei.c int vfs_create(struct inode *dir, struct · line 1506
Quoted 5 lines
fs/namei.c +1
-1@@ -1503,7 +1503,7 @@ int vfs_create(struct inode *dir, structreturn -EACCES ;/* shouldn't it be ENOSYS? */ mode &=S_IALLUGO ; mode |=S_IFREG ; - error =security_inode_create (dir, dentry, mode); + error =security_inode_create (dir, dentry, nd ? nd->mnt :NULL , mode);
Once again very strong NACK. Every conditional passing of vfsmounts get my veto. As mentioned last time if you really want this send a patch series first that passed the vfsmount consistantly.
PATCH 1/2 Don't pass NULL nameidata to vfs_create
Quoted 3 lines · Christoph Hellwig
Once again very strong NACK. Every conditional passing of vfsmounts get my veto. As mentioned last time if you really want this send a patch series first that passed the vfsmount consistantly.
I don't consider it fair to NACK this patch just because some other part of the kernel uses vfs_create() in the wrong way -- those are really independent issues. The bug is not that hard to fix though; here is a proposed patch on top of the other AppArmor patches.
The offenders are nfsd and the mqueue filesystem. Instantiate a struct nameidata there.
The .dentry and .mnt fields can easily be filled in in nfsd. The mqueue filesystem is somewhat special: files that mq_open creates are on an internal mount and the mqueue filesystem is not generally visible (it may or may not be mounted). When passing a regular vfsmount to vfs_create the files would appear disconnected while they actually are kernel-internal objects. Use a NULL vfsmount there to avoid that -- passing the kernel-internal vfsmount there wouldn't help, anyway.
--- fs/namei.c | 7 ++++--- fs/nfsd/vfs.c | 23 +++++++++++++++++++---- ipc/mqueue.c | 6 +++++- 3 files changed, 28 insertions(+), 8 deletions(-)
fs/namei.c +1 -1
@@ -1503,7 +1503,7 @@ int vfs_create(struct inode *dir, structreturn -EACCES ;/* shouldn't it be ENOSYS? */ mode &=S_IALLUGO ; mode |=S_IFREG ; - error =security_inode_create (dir, dentry, nd ? nd->mnt :NULL , mode); + error =security_inode_create (dir, dentry, nd->mnt, mode);if (error)return error;DQUOT_INIT (dir);
fs/nfsd/vfs.c +19 -4
@@ -1108,6 +1108,18 @@ nfsd_commit(struct svc_rqst *rqstp, stru }#endif /* CONFIG_NFSD_V3 */ +static inline int +nfsd_do_create (struct inode *dir,struct dentry *child,struct vfsmount *mnt, +int mode) +{ +struct nameidata nd = { + .dentry = child, + .mnt = mnt, + }; + +return vfs_create (dir, child, mode, &nd); +} +/* * Create a file (regular, directory, device, fifo); UNIX sockets * not yet implemented.
@@ -1192,7 +1204,8 @@ nfsd_create(struct svc_rqst *rqstp, stru err =0 ;switch (type) {case S_IFREG : - host_err =vfs_create (dirp, dchild, iap->ia_mode,NULL ); + host_err =nfsd_do_create (dirp, dchild, exp->ex_mnt, + iap->ia_mode);break ;case S_IFDIR : host_err =vfs_mkdir (dirp, dchild, exp->ex_mnt, iap->ia_mode);
@@ -1253,6 +1266,7 @@ nfsd_create_v3(struct svc_rqst *rqstp, s int *truncp, int *created) { struct dentry *dentry, *dchild = NULL; +struct svc_export *exp;struct inode *dirp;__be32 err;int host_err;
@@ -1271,6 +1285,7 @@ nfsd_create_v3(struct svc_rqst *rqstp, s goto out; dentry = fhp->fh_dentry; + exp = fhp->fh_export; dirp = dentry->d_inode;/* Get all the sanity checks out of the way before
@@ -1288,7 +1303,7 @@ nfsd_create_v3(struct svc_rqst *rqstp, sif (IS_ERR (dchild)) goto out_nfserr; - err =fh_compose (resfhp, fhp->fh_export, dchild, fhp); + err =fh_compose (resfhp, exp, dchild, fhp);if (err) goto out;
@@ -1334,13 +1349,13 @@ nfsd_create_v3(struct svc_rqst *rqstp, s goto out; } - host_err =vfs_create (dirp, dchild, iap->ia_mode,NULL ); + host_err =nfsd_do_create (dirp, dchild, exp->ex_mnt, iap->ia_mode);if (host_err <0 ) goto out_nfserr;if (created) *created =1 ; -if (EX_ISSYNC (fhp->fh_export)) { +if (EX_ISSYNC (exp)) { err =nfserrno (nfsd_sync_dir (dentry));/* setattr will sync the child (or not) */ }
ipc/mqueue.c +5 -1
@@ -604,6 +604,10 @@ static int mq_attr_ok(struct mq_attr *atstatic struct file *do_create (struct dentry *dir,struct dentry *dentry,int oflag,mode_t mode,struct mq_attr __user *u_attr) { +struct nameidata nd = { + .dentry = dentry, +/* Not a regular create, so set .mnt to NULL. */ + };struct mq_attr attr;int ret;
@@ -619,7 +623,7 @@ static struct file *do_create(struct den } mode &= ~current->fs->umask; - ret =vfs_create (dir->d_inode, dentry, mode,NULL ); + ret =vfs_create (dir->d_inode, dentry, mode, &nd); dentry->d_fsdata =NULL ;if (ret) goto out;
Re: [nameidata 1/2] Don't pass NULL nameidata to vfs_create
Quoted 9 lines · Andreas Gruenbacher
Quoted 3 lines · Christoph Hellwig
Once again very strong NACK. Every conditional passing of vfsmounts get my veto. As mentioned last time if you really want this send a patch series first that passed the vfsmount consistantly.
I don't consider it fair to NACK this patch just because some other part of the kernel uses
vfs_create()in the wrong way -- those are really independent issues. The bug is not that hard to fix though; here is a proposed patch on top of the other AppArmor patches.
Note that it's not just this particular hunk, all these kinds of conditional passing are wrong.
Quoted 11 lines
The offenders are nfsd and the mqueue filesystem. Instantiate a struct nameidata there.
The .dentry and .mnt fields can easily be filled in in nfsd. The mqueue filesystem is somewhat special: files that
mq_opencreates are on an internal mount and the mqueue filesystem is not generally visible (it may or may not be mounted). When passing a regular vfsmount tovfs_createthe files would appear disconnected while they actually are kernel-internal objects. Use a NULL vfsmount there to avoid that -- passing the kernel-internal vfsmount there wouldn't help, anyway.
But anyway, creating fake nameidata structures is not really helpful. If there is a nameidata passed people expect it to be complete, and if you pass them to an LSM people will e.g. try to look into lookup intents. But similar to the per-mountpoint r/o work I suspect you're simply operating on the wrong level, but then again this might not be fixable due to the braindead apparmor design.
Re: [nameidata 1/2] Don't pass NULL nameidata to vfs_create
Quoted 4 lines · Christoph Hellwig
But anyway, creating fake nameidata structures is not really helpful. If there is a nameidata passed people expect it to be complete, and if you pass them to an LSM people will e.g. try to look into lookup intents.
I don't actually agree with that: when nfsd creates a file, it still is a file create no matter where it originates from, and so it does make sense to provide the appropriate intent information too. Struct nameidata contains other crap only needed during an actual lookup too --- that's a mess, and the nameidata2 patch shows one possibility how to deal with it. (It's the cleanest approach I could think of right now without cluttering the vfs code with insane amounts of dereferences.)
Thanks,
Andreas
Re: [nameidata 1/2] Don't pass NULL nameidata to vfs_create
Quoted 10 lines · Andreas Gruenbacher
Quoted 4 lines · Christoph Hellwig
But anyway, creating fake nameidata structures is not really helpful. If there is a nameidata passed people expect it to be complete, and if you pass them to an LSM people will e.g. try to look into lookup intents.
I don't actually agree with that: when nfsd creates a file, it still is a file create no matter where it originates from, and so it does make sense to provide the appropriate intent information too. Struct nameidata contains other crap only needed during an actual lookup too --- that's a mess, and the
You should provide intent information, yes - which your patch didn't :)
And yes, I didn't like doing the ugly intent in nameidata hack, it's creating loads of problems for various parties, e.g. the stackable filesystem folks. Now the basic intent in nameidata mistaken has been made even worse by passing back a struct file in it conditionally and doing lots of work in ->lookup that shouldn't be there. (Which btw, I expect to cause quite a few problems for apparmor or other lsms, but I guess so far no one has tried them on NFSv4)
Re: [nameidata 1/2] Don't pass NULL nameidata to vfs_create
Quoted 1 line · Christoph Hellwig
You should provide intent information, yes - which your patch didn't :)
Well, the information implicitly provided is "no intent": In do_create() in ipc/mqueue.c intents would be pretty pointless because the mqueue filesystem is local. In fs/nfsd/vfs.c, intents would make slightly more sense assuming that the underlying filesystem eported via nfsd is remote. That's an optimization, and not even a very important one.
Quoted 2 lines
(Which btw, I expect to cause quite a few problems for apparmor or other lsms, but I guess so far no one has tried them on NFSv4)
Pathname wise, NFSv4 should look like any other filesystem on the client side. On the Server side, the concept of pathnames doesn't really fly for nfs: if a directory contains more than one link to the same file, there is no way to tell those aliases from each other from the file descriptor. In addition, computing even the somewhat ambiguous pathnames that can be computed would require subtree checking. But trying to confine nfsd is pretty pointless
anyway: the deamon is privileged and can do whatever it wants. It makes more sense to export the right directories with the right permissions in the first place.
Thanks,
Andreas
Re: [nameidata 1/2] Don't pass NULL nameidata to vfs_create
In reply to Christoph HellwigQuoted 6 lines · Christoph Hellwig
Quoted 5 lines · Andreas Gruenbacher
Quoted 3 lines · Christoph Hellwig
Once again very strong NACK. Every conditional passing of vfsmounts get my veto. As mentioned last time if you really want this send a patch series first that passed the vfsmount consistantly.
Quoted 7 lines
Quoted 4 lines
I don't consider it fair to NACK this patch just because some other part of the kernel uses
vfs_create()in the wrong way -- those are really independent issues. The bug is not that hard to fix though; here is a proposed patch on top of the other AppArmor patches.Note that it's not just this particular hunk, all these kinds of conditional passing are wrong.
Yes, the vfs uses or passes through a NULL nameidata or vfsmount pointer and no intent information in several places:
(1) vfs_create() is called with a NULL nameidata in two places,
(2) lookup_one_len() and lookup_one_len_kern() pass a NULL nameidata to
permission(), d_op->d_revalidate(), and i_op->lookup(),
(3) file_permission() passes a NULL nameidata to permission().
(4) permission() is directly called with NULL nameidata in some places, and
In general it is incorrect to use NULL nameidata or vfsmounts because then the vfsmount->mnt_flags cannot be checked, and no intent information is available. Intents are an optional optimization for remote filesystems; we can simply ignore them for local filesystems. There are some cases where we are passing NULL which are real bugs, but there are also other cases in which the operations are not mount related: for example, filesystems like pipefs have the MS_NOUSER flag set which indicates that they cannot be mounted at all. On filesystems like sysfs, files are created and removed whether or not that filesystem is mounted.
The places where we should pass a proper nameidata / vfsmount but don't should be fixed. Those are long-standing issues in the vfs though, and at least some are not easy to correct. AppArmor is affected by some of the NULL vfsmount passing, and we found a few more problems when looking into this more closely, but most of that NULL passing doesn't affect AppArmor:
* In some places, vfs functions are called without nameidata / vfsmount, followed by calling lsm hooks with a vfsmount. In those cases, the vfs functions cannot check the vfsmount flags, but the lsm hooks will still do the appropriate checks.
* In the AppArmor security model, directory searching is always allowed, and the access check are done once the leaf dentry + vfsmount has been looked up. For example, granting write access to /var/tmp/foo in a profile is sufficient to allow that access; search access to /var and /var/tmp does not need to be specified. (The regular inode permission checks are of course still performed.)
* For filesystems like nfsd, the concept of pathname based access control doesn't make sense because of properties of the protocol: there is no reliable mapping from an nfs file handle to a particular file; aliases to the same file cannot be distinguished. (The AppArmor techdoc [1] describes this in more detail.)
Not allowing to confine nfsd by AppArmor doesn't hurt much: nfsd provides its own export access control mechanisms. Also, nfsd cannot really be confined in a secure way because of how it is implemented in the kernel. Gfs2 may have similar properties; I didn't actually look into the protocol.
So I propose to separate the vfs NULL nameidata / vfsmount passing discussion from the AppArmor discussion. I have no problem working on the vfs fixes -- we could some nice cleanups there -- but that really is independent of AppArmor.
We almost have the revised AppArmor patches ready. We'll include all the fixes that are truly necessary, and everything beyond that will be posted separately.
Quoted 4 lines
But anyway, creating fake nameidata structures is not really helpful. If there is a nameidata passed people expect it to be complete, and if you pass them to an LSM people will e.g. try to look into lookup intents.
Splitting up nameidata helps somewhat here. In cases where we don't have intent information available we can zero out the intent flags, and so in the worst case, we won't get the intent optimizations.
Thanks,
Andreas
Re: [nameidata 1/2] Don't pass NULL nameidata to vfs_create
In reply to Andreas Gruenbacherfs/nfsd/vfs.c nfsd_commit(struct svc_rqst *rqstp, stru · line 1122
Quoted 12 lines · Andreas Gruenbacher
fs/nfsd/vfs.c +12
-0+static inline int +nfsd_do_create (struct inode *dir,struct dentry *child,struct vfsmount *mnt, +int mode) +{ +struct nameidata nd = { + .dentry = child, + .mnt = mnt, + }; + +return vfs_create (dir, child, mode, &nd); +} +
Wouldn't it normally result in fewer instructions (on most architectures ... maybe not x86) to keep the same argument order as vfs_create? ie:
static inline int nfsd_do_create(struct inode *dir, struct dentry *child,
int mode, struct vfsmount *mnt)
PATCH 2/2 Pass no useless nameidata to the create, lookup, and permission IOPs
In reply to Christoph HellwigHere is a patch with request for comment.
The create, lookup, and permission inode operations are all passed a full nameidata. This is unfortunate because in nfsd and the mqueue filesystem we must instantiate a struct nameidata, but cannot provide all of the same information of a regular lookup. The unused fields take up space on the stack, but more importantly, it is not obvious which fields have meaningful values and which don't, and so things might easily break.
This patch introduces struct nameidata2 with only the fields that make sense independent of an actual lookup, and uses that struct in those places where a full nameidata is not needed.
The entire patch is a little big so I'm only including the key parts here. The complete version is here:
http://forgeftp.novell.com/apparmor/LKML_Submission-April_07/split-up-nameidata.diff
include/linux/namei.h +27 -10
@@ -14,21 +14,39 @@ struct open_intent {enum {MAX_NESTED_LINKS =8 }; +/** +* Fields shared between nameidata and nameidata2 -- nameidata2 could +* be embedded in nameidata, but then the vfs code would become +* cluttered with dereferences. +*/ +#define __NAMEIDATA2 \ + struct dentry *dentry; \ + struct vfsmount *mnt; \ + unsigned int flags; \ + \ + union { \ + struct open_intent open; \ + } intent; +struct nameidata { -struct dentry *dentry; -struct vfsmount *mnt; +__NAMEIDATA2 struct qstr last; -unsigned int flags;int last_type;unsigned depth;char *saved_names[MAX_NESTED_LINKS +1 ]; +}; -/* Intent data */ -union { -struct open_intent open; - } intent; +struct nameidata2 { + __NAMEIDATA2 }; +#undef __NAMEIDATA2 + +static inline struct nameidata2 *ND2 (struct nameidata *nd) +{ + return container_of(&nd->dentry, struct nameidata2, dentry); +} +struct path {struct vfsmount *mnt;struct dentry *dentry;
@@ -76,10 +94,9 @@ extern void path_release_on_umount(strucextern int __user_path_lookup_open (const char __user *,unsigned lookup_flags,struct nameidata *nd,int open_flags);extern int path_lookup_open (int dfd,const char *name,unsigned lookup_flags,struct nameidata *,int open_flags); -extern struct file *lookup_instantiate_filp (struct nameidata *nd,struct dentry *dentry, -int (*open)(struct inode *,struct file *)); +extern struct file *lookup_instantiate_filp (struct nameidata2 *nd,struct dentry *dentry,int (*open)(struct inode *,struct file *));extern struct file *nameidata_to_filp (struct nameidata *nd,int flags); -extern void release_open_intent (struct nameidata *); +extern void release_open_intent (struct nameidata2 *);extern struct dentry *lookup_one_len (const char *,struct dentry *,int );
fs/namei.c +19 -18
@@ -225,7 +225,7 @@ int generic_permission(struct inode *inoreturn -EACCES ; } -int permission (struct inode *inode,int mask,struct nameidata *nd) +int permission (struct inode *inode,int mask,struct nameidata2 *nd) {umode_t mode = inode->i_mode;int retval, submask;
@@ -278,7 +278,7 @@ int permission(struct inode *inode, int* for filesystem access without changing the "normal" uids which * are used for other things. */ -int vfs_permission (struct nameidata *nd,int mask) +int vfs_permission (struct nameidata2 *nd,int mask) {return permission (nd->dentry->d_inode, mask, nd); }
@@ -366,7 +366,7 @@ void path_release_on_umount(struct namei* release_open_intent - free up open intent resources * @nd: pointer to nameidata */ -void release_open_intent (struct nameidata *nd) +void release_open_intent (struct nameidata2 *nd) {if (nd->intent.open.file->f_path.dentry ==NULL )put_filp (nd->intent.open.file);
@@ -377,7 +377,7 @@ void release_open_intent(struct nameidatstatic inline struct dentry *do_revalidate (struct dentry *dentry,struct nameidata *nd) { -int status = dentry->d_op->d_revalidate (dentry, nd); +int status = dentry->d_op->d_revalidate (dentry,ND2 (nd));if (unlikely (status <=0 )) {/* * The dentry failed validation.
@@ -455,7 +455,7 @@ static int exec_permission_lite(struct ireturn -EACCES ; ok: -return security_inode_permission (inode,MAY_EXEC , nd); +return security_inode_permission (inode,MAY_EXEC ,ND2 (nd)); }/*
@@ -491,7 +491,7 @@ static struct dentry * real_lookup(strucstruct dentry * dentry =d_alloc (parent, name); result =ERR_PTR (-ENOMEM );if (dentry) { - result = dir->i_op->lookup (dir, dentry, nd); + result = dir->i_op->lookup (dir, dentry,ND2 (nd));if (result)dput (dentry);else
@@ -832,7 +832,7 @@ static fastcall int __link_path_walk(con nd->flags |=LOOKUP_CONTINUE ; err =exec_permission_lite (inode, nd);if (err == -EAGAIN ) - err =vfs_permission (nd,MAY_EXEC ); + err =vfs_permission (ND2 (nd),MAY_EXEC );if (err)break ;
@@ -978,7 +978,8 @@ return_reval: (nd->dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) { err = -ESTALE ;/* Note: we do not d_invalidate() */ -if (!nd->dentry->d_op->d_revalidate (nd->dentry, nd)) +if (!nd->dentry->d_op->d_revalidate (nd->dentry, +ND2 (nd)))break ; } return_base:
@@ -1194,7 +1195,7 @@ static int __path_lookup_intent_open(intpath_release (nd); } }else if (err !=0 ) -release_open_intent (nd); +release_open_intent (ND2 (nd));return err; }
@@ -1255,7 +1256,7 @@ static struct dentry * __lookup_hash(strint err; inode = base->d_inode; - err =permission (inode,MAY_EXEC , nd); + err =permission (inode,MAY_EXEC ,ND2 (nd)); dentry =ERR_PTR (err);if (err) goto out;
@@ -1277,7 +1278,7 @@ static struct dentry * __lookup_hash(str dentry =ERR_PTR (-ENOMEM );if (!new) goto out; - dentry = inode->i_op->lookup (inode, new, nd); + dentry = inode->i_op->lookup (inode, new,ND2 (nd));if (!dentry) dentry = new;else
@@ -1422,7 +1423,7 @@ static int may_delete(struct inode *dir,* 4. We can't do it if dir is immutable (done in permission()) */ static inline int may_create (struct inode *dir,struct dentry *child, -struct nameidata *nd) +struct nameidata2 *nd) {if (child->d_inode)return -EEXIST ;
@@ -1492,7 +1493,7 @@ void unlock_rename(struct dentry *p1, st }int vfs_create (struct inode *dir,struct dentry *dentry,int mode, -struct nameidata *nd) +struct nameidata2 *nd) {int error =may_create (dir, dentry, nd);
@@ -1528,7 +1529,7 @@ int may_open(struct nameidata *nd, int aif (S_ISDIR (inode->i_mode) && (flag &FMODE_WRITE ))return -EISDIR ; - error =vfs_permission (nd, acc_mode); + error =vfs_permission (ND2 (nd), acc_mode);if (error)return error;
@@ -1601,7 +1602,7 @@ static int open_namei_create(struct nameif (!IS_POSIXACL (dir->d_inode)) mode &= ~current->fs->umask; - error =vfs_create (dir->d_inode, path->dentry, mode, nd); + error =vfs_create (dir->d_inode, path->dentry, mode,ND2 (nd));mutex_unlock (&dir->d_inode->i_mutex);dput (nd->dentry); nd->dentry = path->dentry;
@@ -1734,7 +1735,7 @@ exit_dput:dput_path (&path, nd); exit:if (!IS_ERR (nd->intent.open.file)) -release_open_intent (nd); +release_open_intent (ND2 (nd));path_release (nd);return error;
@@ -1762,7 +1763,7 @@ do_link:* me so stupid? Anathema to whoever designed this non-sense * with "intent.open". */ -release_open_intent (nd); +release_open_intent (ND2 (nd));return error; } nd->flags &= ~LOOKUP_PARENT ;
@@ -1888,7 +1889,7 @@ asmlinkage long sys_mknodat(int dfd, conswitch (mode &S_IFMT ) {case 0 :case S_IFREG : error =vfs_create (nd.dentry->d_inode, dentry, mode, - &nd); +ND2 (&nd));break ;case S_IFCHR :case S_IFBLK : error = vfs_mknod(nd.dentry->d_inode, dentry, nd.mnt,
include/linux/fs.h +8 -7
@@ -289,6 +289,7 @@ extern int dir_notify_enable;struct hd_geometry ;struct iovec ;struct nameidata ; +struct nameidata2 ;struct kiocb ;struct pipe_inode_info ;struct poll_table_struct ;
@@ -981,8 +982,8 @@ extern void unlock_super(struct super_bl/* * VFS helper functions.. */ -extern int vfs_permission (struct nameidata *,int ); -extern int vfs_create (struct inode *,struct dentry *,int ,struct nameidata *); +extern int vfs_permission (struct nameidata2 *,int ); +extern int vfs_create (struct inode *,struct dentry *,int ,struct nameidata2 *);extern int vfs_mkdir (struct inode *,struct dentry *,struct vfsmount *,int );extern int vfs_mknod (struct inode *,struct dentry *,struct vfsmount *,int ,dev_t );extern int vfs_symlink (struct inode *,struct dentry *,struct vfsmount *,const char *,int );
@@ -1106,8 +1107,8 @@ struct file_operations { };struct inode_operations { -int (*create) (struct inode *,struct dentry *,int ,struct nameidata *); -struct dentry * (*lookup) (struct inode *,struct dentry *,struct nameidata *); +int (*create) (struct inode *,struct dentry *,int ,struct nameidata2 *); +struct dentry * (*lookup) (struct inode *,struct dentry *,struct nameidata2 *);int (*link) (struct dentry *,struct inode *,struct dentry *);int (*unlink) (struct inode *,struct dentry *);int (*symlink) (struct inode *,struct dentry *,const char *);
@@ -1120,7 +1121,7 @@ struct inode_operations {void * (*follow_link) (struct dentry *,struct nameidata *);void (*put_link) (struct dentry *,struct nameidata *,void *);void (*truncate) (struct inode *); -int (*permission) (struct inode *,int ,struct nameidata *); +int (*permission) (struct inode *,int ,struct nameidata2 *);int (*setattr) (struct dentry *,struct iattr *);int (*getattr) (struct vfsmount *mnt,struct dentry *,struct kstat *);int (*setxattr) (struct dentry *,const char *,const void *,size_t ,int );
@@ -1616,7 +1617,7 @@ extern int do_remount_sb(struct super_blextern sector_t bmap (struct inode *,sector_t );#endif extern int notify_change (struct dentry *,struct vfsmount *,struct iattr *); -extern int permission (struct inode *,int ,struct nameidata *); +extern int permission (struct inode *,int ,struct nameidata2 *);extern int generic_permission (struct inode *,int ,int (*check_acl)(struct inode *,int ));
@@ -1873,7 +1874,7 @@ extern int simple_prepare_write(struct fextern int simple_commit_write (struct file *file,struct page *page,unsigned offset,unsigned to); -extern struct dentry *simple_lookup (struct inode *,struct dentry *,struct nameidata *); +extern struct dentry *simple_lookup (struct inode *,struct dentry *,struct nameidata2 *);extern ssize_t generic_read_dir (struct file *,char __user *,size_t ,loff_t *);extern const struct file_operations simple_dir_operations;extern const struct inode_operations simple_dir_inode_operations;
Re: [nameidata 2/2] Pass no useless nameidata to the create, lookup, and permission IOPs
Quoted 6 lines · Andreas Gruenbacher
enum {
MAX_NESTED_LINKS= 8 };+/**
+ * Fields shared between nameidata and nameidata2 -- nameidata2 could
+ * be embedded in nameidata, but then the vfs code would become
+ * cluttered with dereferences.
you could use an anonymous struct embedeed in both.
include/linux/namei.h struct open_intent { · line 29
Quoted 9 lines
+ */
include/linux/namei.h +8
-0+#define __NAMEIDATA2 \ + struct dentry *dentry; \ + struct vfsmount *mnt; \ + unsigned int flags; \ + \ + union { \ + struct open_intent open; \ + } intent;
Or better just pass argument directly. We really should only pass down the the dentry and the intent to the filesystem. The filesystem has not business looking at mnt in the operations, and the relevant bits of flags (mostly whether it's O_EXCLUSIVE) should be stored in the intent, because that's the only way it should be used.
Doing it that way also allows to fix some braindead calling conventions like this one:
fs/namei.c int permission(struct inode *inode, int · line 283
Quoted 12 lines
fs/namei.c +1
-1-int permission (struct inode *inode,int mask,struct nameidata *nd) +int permission (struct inode *inode,int mask,struct nameidata2 *nd) {umode_t mode = inode->i_mode;int retval, submask;@@ -278,7 +278,7 @@ int permission(struct inode *inode, int* for filesystem access without changing the "normal" uids which * are used for other things. */ static inline struct dentry *do_revalidate (struct dentry *dentry,struct nameidata *nd)
Or this one.
fs/namei.c static struct dentry * real_lookup(struc · line 494
Quoted 2 lines
fs/namei.c +1
-1- result = dir->i_op->lookup (dir, dentry, nd); + result = dir->i_op->lookup (dir, dentry,ND2 (nd));
or this one.
etc..
Re: [nameidata 2/2] Pass no useless nameidata to the create, lookup, and permission IOPs
In reply to Andreas Gruenbacherinclude/linux/namei.h struct open_intent { · line 17
Quoted 9 lines · On Mon, 16 Apr 2007 18:29:20 +0200 Andreas Gruenbacher wrote:
Here is a patch with request for comment.
include/linux/namei.h +1
-0@@ -14,21 +14,39 @@ struct open_intent {enum {MAX_NESTED_LINKS =8 }; +/**
Please don't use the kernel-doc begin-marker "/**" when the comment block isn't in kernel-doc format.
include/linux/namei.h struct open_intent { · line 30
Quoted 13 lines
include/linux/namei.h +13
-0+* Fields shared between nameidata and nameidata2 -- nameidata2 could +* be embedded in nameidata, but then the vfs code would become +* cluttered with dereferences. +*/ +#define __NAMEIDATA2 \ + struct dentry *dentry; \ + struct vfsmount *mnt; \ + unsigned int flags; \ + \ + union { \ + struct open_intent open; \ + } intent; +
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
Re: [nameidata 2/2] Pass no useless nameidata to the create, lookup, and permission IOPs
Quoted 2 lines · Randy Dunlap
Please don't use the kernel-doc begin-marker "/**" when the comment block isn't in kernel-doc format.
Sigh, kernel-doc should improve things, not get in the way ...
Re: [nameidata 2/2] Pass no useless nameidata to the create, lookup, and permission IOPs
Quoted 5 lines · Andreas Gruenbacher
Quoted 2 lines · Randy Dunlap
Please don't use the kernel-doc begin-marker "/**" when the comment block isn't in kernel-doc format.
Sigh, kernel-doc should improve things, not get in the way ...
Well, I see it as sort of a language that is embedded in C source code.
But what are you suggesting?
Signature · 2 lines
~Randy *** Remember to use Documentation/SubmitChecklist when testing your code ***
Al Viro
NAKRe: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to [email protected]Quoted 1 line · On Thu, Apr 12, 2007 at 02:08:10AM -0700, [email protected] wrote:
This is needed for computing pathnames in the AppArmor LSM.
Which is an argument against said LSM in current form.
fs/namei.c int vfs_create(struct inode *dir, struct · line 1506
Quoted 2 lines
fs/namei.c +1
-1- error =security_inode_create (dir, dentry, mode); + error =security_inode_create (dir, dentry, nd ? nd->mnt :NULL , mode);
is a clear sign that interface is wrong. Leaving aside the general idiocy of "we prohibit to do something with file if mounted here, but if there's another mountpoint, well, we just miss", an API of that kind is just plain wrong. Either you can live without seeing vfsmount in that method (in which case you shouldn't pass it at all), or you have a hole.
NACK.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 4 lines · Al Viro
Quoted 1 line · On Thu, Apr 12, 2007 at 02:08:10AM -0700, [email protected] wrote:
This is needed for computing pathnames in the AppArmor LSM.
Which is an argument against said LSM in current form.
The fundamental model of AppArmor is to perform access checks based on the location of a file in the filesystem namespace, i.e., the pathname, and this can only be done with both the dentry and vfsmount. My understanding was that there was agreement at the last kernel summit that pathname based approaches should be allowed.
Quoted 4 lines
fs/namei.c int vfs_create(struct inode *dir, struct · line 1506
Quoted 2 lines
fs/namei.c +1
-1- error =security_inode_create (dir, dentry, mode); + error =security_inode_create (dir, dentry, nd ? nd->mnt :NULL , mode);is a clear sign that interface is wrong.
No. There are callers of vfs_create() that use a NULL nameidata, and that's what causes the problem here. Struct nameidata is pretty big, and so we don't want to allocate temporary nameidata objects all over the place. So to me the above NULL check seems the lesser evil.
One way to deal with the nameidata size problem is to split it up into one part for the real path lookups, and a much smaller part that only contains the dentry, vfsmount, and intent flags. This would allow to pass around the smaller nameidata much more consistently.
John has posted patches for that on May 14; subject [RFD Patch 0/4]. Feedback appreciated.
In several places, the NULL nameidata is wrong because we can't check the vfsmount flags or intent. Not having this information is causing problems for nfs too for example -- it's not an AppArmor specific problem.
Quoted 5 lines
Leaving aside the general idiocy of "we prohibit to do something with file if mounted here, but if there's another mountpoint, well, we just miss", an API of that kind is just plain wrong. Either you can live without seeing vfsmount in that method (in which case you shouldn't pass it at all), or you have a hole.
This is backwards from what AppArmor does. The policy defines which paths may be accessed; all paths not explicitly listed are denied. If files are mounted at multiple locations, then the policy may allow access to some locations but not to others. That's not a hole.
In fact this is not much different from traditional permissions on parent
directories: even if the same files are mounted at several locations, parent directory permissions may allow accessing only some of those locations.
Thanks,
Andreas
James Morris
maintainerRe: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 4 lines · Andreas Gruenbacher
This is backwards from what AppArmor does. The policy defines which paths may be accessed; all paths not explicitly listed are denied. If files are mounted at multiple locations, then the policy may allow access to some locations but not to others. That's not a hole.
I don't know what else you'd call it.
Would you mind providing some concrete examples of how such a model would be useful?
- James
Signature · 2 lines
James Morris <[email protected]>
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 7 lines · James Morris
Quoted 4 lines · Andreas Gruenbacher
This is backwards from what AppArmor does. The policy defines which paths may be accessed; all paths not explicitly listed are denied. If files are mounted at multiple locations, then the policy may allow access to some locations but not to others. That's not a hole.
I don't know what else you'd call it.
AppArmor doesn't label files; it's a different model from SELinux. Its policy defines which processes may access which paths. Even if for some reson the same files were visible elsewhere, the policy wouldn't cover those other paths, and so accessing them would be denied. So again, that's not a security hole.
Quoted 2 lines
Would you mind providing some concrete examples of how such a model would be useful?
The model is explained, with examples, in the technical documentation at
http://forgeftp.novell.com//apparmor/LKML_Submission-May_07/.
Thanks,
Andreas
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSMhook
In reply to James MorrisHello.
I think bind mounts were discussed when shared subtree ( http://lwn.net/Articles/159092/ ) was introduced.
For systems that allow users mount their CD/DVDs freely, bind mounts are used and labeling files is a convenient way to deny accessing somebody else's files.
But systems that don't allow users mount their CD/DVDs freely, bind mounts needn't to be used and using pathnames is a convenient way to deny accessing somebody else's files.
Pathname based access control/auditing system
works if the system doesn't use bind mounts.
However, there are distributions (e.g. Debian Etch) that always use bind mounts. In such distributions, pathname based access control/auditing system doesn't work.
This is not the fault of distributions nor
pathname based access control/auditing system.
It is possible to solve by passing vfsmount to VFS and LSM functions.
SELinux users are having a lot of trouble because pathnames in audit logs are not always complete.
AppArmor users are having a lot of trouble because pathnames which a process requested are ambiguous when bind mounts are used.
Being able to report pathnames that a process requested is not surprising when considering user friendliness.
I beleive passing vfsmount makes both users happy.
Thanks.
A message that is not in the archive (sent off-list or not yet received): [email protected]
James Morris
maintainerRe: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 4 lines · Andreas Gruenbacher
Quoted 2 lines
Would you mind providing some concrete examples of how such a model would be useful?
The model is explained, with examples, in the technical documentation at http://forgeftp.novell.com//apparmor/LKML_Submission-May_07/.
I'm asking specifically about a model where you'd want to provide differing access to the same objects based on the pathname used for access to the objects, per:
"If files are mounted at multiple locations, then the policy may allow
access to some locations but not to others. That's not a hole."
I don't see specific examples of this kind of policy in that document, and the document seems to be saying quite the opposite -- that the AA policy is only valid in conjunction with further constraints on how the objects may be accessed:
Section 3.2:
"Pathnames are meaningful only within a namespace. Each namespace has a
root where all the files, directories, and mount points are hanging off from.
The privilege of creating new namespaces is bound to the CAP_ SYS_ ADMIN capability, which grants a multitude of other things that would allow a process to break out of AppArmor confinement, so confined processes are not supposed to have this privilege, and processes with this capability need to be considered trusted. "
I can restate my question and ask why you'd want a security policy like:
Subject 'sysadmin' has:
read access to /etc/shadow
read/write access to /views/sysadmin/etc/shadow
where the objects referenced by the paths are identical and visible to the subject along both paths, in keeping with your description of "policy may allow access to some locations but not to others" ?
- James
Signature · 2 lines
James Morris <[email protected]>
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 7 lines · James Morris
Quoted 5 lines · Andreas Gruenbacher
Quoted 2 lines
Would you mind providing some concrete examples of how such a model would be useful?
The model is explained, with examples, in the technical documentation at
http://forgeftp.novell.com//apparmor/LKML_Submission-May_07/.
Quoted 8 lines
I'm asking specifically about a model where you'd want to provide differing access to the same objects based on the pathname used for access to the objects, per:
"If files are mounted at multiple locations, then the policy may allow
access to some locations but not to others. That's not a hole."I don't see specific examples of this kind of policy in that document,
I guess I see what you are getting at. If a user has read/write access to /views/sysadmin/etc/shadow and if that is an alias for /etc/shadow, then of course it doesn't help to give the user only read access to /etc/shadow to protect that file. So you can shoot yourself in the foot with mounts, and also with hardlinks. That's why confined processes are not allowed to mount stuff, and why hardlinks requires the appropriate profile permissions.
AppArmor depends on the namespace so it cares about namespace manipulations, just like SELinux depends on labels so labels matter there.
Quoted 3 lines
and the document seems to be saying quite the opposite -- that the AA policy is only valid in conjunction with further constraints on how the objects may be accessed:
Read it like this: we don't have a good idea how to support multiple namespaces so far. Currently, we interpret all pathnames relative to the namespace a process is in. Confined processes don't have the privilege to create or manipulate namespaces, which makes this safe. We may find a better future solution.
Quoted 9 lines
I can restate my question and ask why you'd want a security policy like:
Subject 'sysadmin' has:
read access to/etc/shadow
read/write access to /views/sysadmin/etc/shadowwhere the objects referenced by the paths are identical and visible to the subject along both paths, in keeping with your description of "policy may allow access to some locations but not to others" ?
I'm not aware of situations where giving different permissions to different paths to the same file would actually be useful. The security model doesn't prevent it though, and it's not a security hole.
Andreas
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 5 lines · Andreas Gruenbacher
Read it like this: we don't have a good idea how to support multiple namespaces so far. Currently, we interpret all pathnames relative to the namespace a process is in. Confined processes don't have the privilege to create or manipulate namespaces, which makes this safe. We may find a better future solution.
You also don't have a solution for multiple chroot jails, since they often have the same fs mounted in many places on given box *and* since the pathnames from the confined processes' POVs have fsck-all to do with each other.
It's really not kinder than multiple namespaces as far as your approach is concerned.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 8 lines · Al Viro
Quoted 5 lines · Andreas Gruenbacher
Read it like this: we don't have a good idea how to support multiple namespaces so far. Currently, we interpret all pathnames relative to the namespace a process is in. Confined processes don't have the privilege to create or manipulate namespaces, which makes this safe. We may find a better future solution.
Quoted 7 lines
You also don't have a solution for multiple chroot jails, since they often have the same fs mounted in many places on given box *and* since the pathnames from the confined processes' POVs have fsck-all to do with each other.
It's really not kinder than multiple namespaces as far as your approach is concerned.
Well, the pathnames we check against are namespace relative, so no matter what pathnames the chrooted processes think they are looking at, we always know the actual pathnames up to ``outside the chroot''. Having the same filesystem mounted in multiple chroots or in multiple locations in the same chroot doesn't matter.
The main problem I see when it comes to defining per-namespace policy is that namespaces are inherently anonymous, and there is no obvious way of associating different sets of profiles with different namespaces. Implementing something to also handle multiple namespaces doesn't seem hard -- after all, it's not such a difficult concept -- but I don't have a good enough idea what would work best.
Thanks,
Andreas
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Andreas GruenbacherQuoted 7 lines · --- Andreas Gruenbacher
Quoted 3 lines
where the objects referenced by the paths are identical and visible to the subject along both paths, in keeping with your description of "policy may allow access to some locations but not to others" ?
I'm not aware of situations where giving different permissions to different paths to the same file would actually be useful. The security model doesn't prevent it though, and it's not a security hole.
On Fedora zcat, gzip and gunzip are all links to the same file. I can imagine (although it is a bit of a stretch) allowing a set of users access to gunzip but not gzip (or the other way around). There are probably more sophisticated programs that have different behavior based on the name they're invoked by that would provide a more compelling arguement, assuming of course that you buy into the behavior-based-on-name scheme. What I think I'm suggesting is that AppArmor might be useful in addressing the fact that a file with multiple hard links is necessarily constrained to have the same access control on each of those names. That assumes one believes that such behavior is flawwed, and I'm not going to try to argue that. The question was about an example, and there is one.
Casey Schaufler
[email protected]
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 4 lines · Casey Schaufler
On Fedora zcat, gzip and gunzip are all links to the same file. I can imagine (although it is a bit of a stretch) allowing a set of users access to gunzip but not gzip (or the other way around). There are probably more sophisticated programs that have different
Quoted 8 lines
behavior based on the name they're invoked by that would provide a more compelling arguement, assuming of course that you buy into the behavior-based-on-name scheme. What I think I'm suggesting is that AppArmor might be useful in addressing the fact that a file with multiple hard links is necessarily constrained to have the same access control on each of those names. That assumes one believes that such behavior is flawwed, and I'm not going to try to argue that. The question was about an example, and there is one.
Different policy for different names of the same binary makes more obvious sense with chroot environments. That's slightly different from having different permissions for the same file within a single profile though.
Thanks,
Andreas
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Casey SchauflerQuoted 4 lines · Casey Schaufler
On Fedora zcat, gzip and gunzip are all links to the same file. I can imagine (although it is a bit of a stretch) allowing a set of users access to gunzip but not gzip (or the other way around). There are probably more sophisticated programs that have different
Quoted 8 lines
behavior based on the name they're invoked by that would provide a more compelling arguement, assuming of course that you buy into the behavior-based-on-name scheme. What I think I'm suggesting is that AppArmor might be useful in addressing the fact that a file with multiple hard links is necessarily constrained to have the same access control on each of those names. That assumes one believes that such behavior is flawwed, and I'm not going to try to argue that. The question was about an example, and there is one.
This doesn't work. The behavior depends on argv[0], which is not necessarily the same as the name of the file.
Signature · 1 line
Jeremy Maitin-Shepard
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 15 lines · --- Jeremy Maitin-Shepard
Quoted 4 lines · Casey Schaufler
On Fedora zcat, gzip and gunzip are all links to the same file. I can imagine (although it is a bit of a stretch) allowing a set of users access to gunzip but not gzip (or the other way around). There are probably more sophisticated programs that have different
Quoted 8 lines
behavior based on the name they're invoked by that would provide a more compelling arguement, assuming of course that you buy into the behavior-based-on-name scheme. What I think I'm suggesting is that AppArmor might be useful in addressing the fact that a file with multiple hard links is necessarily constrained to have the same access control on each of those names. That assumes one believes that such behavior is flawwed, and I'm not going to try to argue that. The question was about an example, and there is one.
Quoted 2 lines
This doesn't work. The behavior depends on argv[0], which is not necessarily the same as the name of the file.
Sorry, but I don't understand your objection. If AppArmor is configured to allow everyone access to /bin/gzip but only some people access to /bin/gunzip and (important detail) the single binary uses argv[0] as documented and (another important detail) there aren't other links named gunzip to the binary (ok, that's lots of if's) you should be fine. I suppose you could make a shell that lies to exec, but the AppArmor code could certainly check for that in exec by enforcing the argv[0] convention. It would be perfectly reasonable for a system that is so dependent on pathnames to require that.
Casey Schaufler
[email protected]
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 19 lines · Casey Schaufler
Quoted 15 lines · --- Jeremy Maitin-Shepard
Quoted 4 lines · Casey Schaufler
On Fedora zcat, gzip and gunzip are all links to the same file. I can imagine (although it is a bit of a stretch) allowing a set of users access to gunzip but not gzip (or the other way around). There are probably more sophisticated programs that have different
Quoted 8 lines
behavior based on the name they're invoked by that would provide a more compelling arguement, assuming of course that you buy into the behavior-based-on-name scheme. What I think I'm suggesting is that AppArmor might be useful in addressing the fact that a file with multiple hard links is necessarily constrained to have the same access control on each of those names. That assumes one believes that such behavior is flawwed, and I'm not going to try to argue that. The question was about an example, and there is one.
Quoted 2 lines
This doesn't work. The behavior depends on argv[0], which is not necessarily the same as the name of the file.
Quoted 9 lines
Sorry, but I don't understand your objection. If AppArmor is configured to allow everyone access to
/bin/gzipbut only some people access to/bin/gunzipand (important detail) the single binary uses argv[0] as documented and (another important detail) there aren't other links named gunzip to the binary (ok, that's lots of if's) you should be fine. I suppose you could make a shell that lies to exec, but the AppArmor code could certainly check for that in exec by enforcing the argv[0] convention. It would be perfectly reasonable for a system that is so dependent on pathnames to require that.
Well, my point was exactly that App Armor doesn't (as far as I know) do anything to enforce the argv[0] convention, nor would it in general prevent a confined program from making a symlink or hard link. Even disregarding that, it seems very fragile in general to make an suid program (there would be no point in confining the execution of a non-suid program) perform essentially access control based on argv[0].
Signature · 1 line
Jeremy Maitin-Shepard
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 8 lines · Jeremy Maitin-Shepard
[snip]
Well, my point was exactly that App Armor doesn't (as far as I know) do anything to enforce the argv[0] convention, nor would it in general prevent a confined program from making a symlink or hard link. Even disregarding that, it seems very fragile in general to make an suid program (there would be no point in confining the execution of a non-suid program) perform essentially access control based on argv[0].
Note that by "confining the execution of a non-suid program", I mean defining an App Armor profile that prevents the execution of a particular non-suid program, unless of course the program file itself contains secret information, which is irrelevant to this discussion.
Signature · 1 line
Jeremy Maitin-Shepard
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Jeremy Maitin-ShepardQuoted 3 lines · --- Jeremy Maitin-Shepard
...
Well, my point was exactly that App Armor doesn't (as far as I know) do anything to enforce the argv[0] convention,
Sounds like an opportunity for improvement then.
Quoted 5 lines
nor would it in general
prevent a confined program from making a symlink or hard link. Even disregarding that, it seems very fragile in general to make an suid program (there would be no point in confining the execution of a non-suid program) perform essentially access control based on argv[0].
I think that you're being generous calling it fragile, but that's my view, and I've seen much worse. I agree that it would be a Bad Idea, but the fact that I think it's a bad idea is not going to prevent very many people from trying it, and for those that do try it name based access control might seem like just the ticket to complete their nefarious schemes. Remember that security is a subjective thing, and using argv[0] and AppArmor together might make some people feel better.
Casey Schaufler
[email protected]
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Hello.
Quoted 5 lines · Casey Schaufler
Sorry, but I don't understand your objection. If AppArmor is configured to allow everyone access to
/bin/gzipbut only some people access to/bin/gunzipand (important detail) the single binary uses argv[0] as documented and (another important detail) there aren't other links named gunzip to the binary (ok, that's lots of if's) you should be fine.
The argv[0] defines the default behavior of hard linked or symbolic linked programs, but the behavior can be overridden using commandline options.
If you want to allow access to /bin/gzip but deny access to /bin/gunzip ,
you also need to deny access to "/bin/gzip -d" "/bin/gzip --decompress" "/bin/gzip --uncompress". It is impossible to do so because options to override the default behavior depends on program's design and you can't know
what programs and what options are there in the system.
Even if you know all programs and all options in the system,
it is a too tough job to find and reject options
that override the default behavior in the kernel space.
Quoted 3 lines
Quoted 2 lines
Well, my point was exactly that App Armor doesn't (as far as I know) do anything to enforce the argv[0] convention,
Sounds like an opportunity for improvement then.
There are (I think) three types of program invocation.
(1) Invocation of hard linked programs.
/bin/gzip and /bin/gunzip and /bin/zcat are hard links.
There is no problem because you can know which pathname was requested using d_namespace_path() with "struct linux_binprm"->file .
(2) Invocation of symbolic linked programs.
/sbin/pidof is a symbolic link to /sbin/killall .
There is a problem because you can't know which pathname was requested using d_namespace_path() with "struct linux_binprm"->file because the symbolic links were already derefernced inside open_exec(). To know which pathname was requested, you need to lookup using "struct linux_binprm"->filename without LOOKUP_FOLLOW and then use d_namespace_path().
Although there is a race condition that the pathname
the symbolic link "struct linux_binprm"->filename points to may change, but it is inevitable because you can't get dentry and vfsmount of both "without LOOKUP_FOLLOW flag" and "with LOOKUP_FOLLOW flag" at the same time.
(3) Invocation of dynamically created programs with random names.
/usr/sbin/logrotate creates files patterned /tmp/logrotate.?????? and executes these dynamically created files.
To keep execution of these dynamically created files under control, you need to aggregate pathnames of these files.
AppArmor can't define profile if the pathname of programs is random, can it?
Usually the argv[0] and the "struct linux_binprm"->filename are the same, but if you want to do something with argv[0], you will need to handle the (2) case to see whether the argv[0] and "struct linux_binprm"->filename are the same.
Thanks.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Casey SchauflerQuoted 6 lines · Casey Schaufler
Quoted 3 lines · --- Jeremy Maitin-Shepard
...
Well, my point was exactly that App Armor doesn't (as far as I know) do anything to enforce the argv[0] convention,Sounds like an opportunity for improvement then.
Jeez, what argv[0] convention are you both talking about? argv[0] is not guaranteed to have any association with the name of the executable. Feel free to have any discussion about argv[0] you want, but *please* keep it away from AppArmor, which really has nothing to do with it.
It would be nice if you could stop calling argv[0] checks ``name-based access control'': from the point of view of the kernel no access control is involved, and even application-level argv[0] based access control makes no sense whatsoever.
Thanks,
Andreas
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 11 lines · --- Andreas Gruenbacher
Quoted 6 lines · Casey Schaufler
Quoted 3 lines · --- Jeremy Maitin-Shepard
...
Well, my point was exactly that App Armor doesn't (as far as I know) do anything to enforce the argv[0] convention,Sounds like an opportunity for improvement then.
Jeez, what argv[0] convention are you both talking about?
From the exec(3) man page:
"The first argument, by convention, should point to the
file name associated with the file being executed."
since the man page calls it a convention, so do I.
Quoted 4 lines
argv[0] is not guaranteed to have any association with the name of the executable. Feel free to have any discussion about argv[0] you want, but *please* keep it away from AppArmor, which really has nothing to do with it.
As I pointed out, if you wanted to trust the argv[0] value (which I understand AppArmor makes no claims about) and you wanted to use the argv[0] value to determine application behavior (which several people claim is a Bad Idea) you could use Name Based Access Control to provide different access to the common binary. As I pointed out before, that's a lot of "if's".
Quoted 5 lines
It would be nice if you could stop calling argv[0] checks ``name-based access
control'': from the point of view of the kernel no access control is involved, and even application-level argv[0] based access control makes no sense whatsoever.
Fair enough, I don't believe that an argv[0] check ought to be used as a security mechanism. I am not convinced that everyone would agree with us.
Casey Schaufler
[email protected]
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 3 lines · Casey Schaufler
Fair enough, I don't believe that an argv[0] check ought to be used as a security mechanism. I am not convinced that everyone would agree with us.
Having seen my share of argv[0]-related security bugs in my years, I have to agree that it's a security crock. As to why some might not agree, you already put your finger on it earlier:
Quoted 2 lines · Casey Schaufler
nefarious schemes. Remember that security is a subjective thing, and using argv[0] and AppArmor together might make some people feel better.
Some people would rather just feel better...
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Casey SchauflerQuoted 3 lines · Casey Schaufler
[...] but the AppArmor code could certainly check for that in exec by enforcing the argv[0] convention. It would be perfectly reasonable for a system that is so dependent on pathnames to require that.
Hmm ... that's a strange idea. AppArmor cannot assume anything about argv[0], and it would be a really bad idea to change the well-established semantics of argv[0].
There is no actual need for looking at argv[0], though: AppArmor decides based on the actual pathname of the executable...
Thanks,
Andreas
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 6 lines · --- Andreas Gruenbacher
Quoted 3 lines · Casey Schaufler
[...] but the AppArmor code could certainly check for that in exec by enforcing the argv[0] convention. It would be perfectly reasonable for a system that is so dependent on pathnames to require that.
Hmm ... that's a strange idea.
Yeah, I get that a lot.
Quoted 9 lines
AppArmor cannot assume anything about argv[0],
and it would be a really bad idea to change the well-established semantics of
argv[0].
There is no actual need for looking at argv[0], though: AppArmor decides based
on the actual pathname of the executable...
Right. My point was that if you wanted to use the gzip/gunzip example of a file with two names being treated differently based on the name accessed as an argument for AppArmor you could. If you don't want to, that's ok too. Jeremy raised a reasonable objection, and AppArmor could address it if y'all chose to do so. I seriously doubt that enforcing the argv[0] convention would break much, and I also expect that if it did there's a Consultant's Retirement to be made fixing the security hole it points out.
Casey Schaufler
[email protected]
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 8 lines · Casey Schaufler
Quoted 6 lines · --- Andreas Gruenbacher
AppArmor cannot assume anything about argv[0],
and it would be a really bad idea to change the well-established semantics of
argv[0].
Quoted 7 lines
Quoted 4 lines
There is no actual need for looking at argv[0], though: AppArmor decides based
on the actual pathname of the executable...Right. My point was that if you wanted to use the gzip/gunzip example of a file with two names being treated differently based on the name accessed as an argument for AppArmor you could.
AppArmor detects the pathname of the file exec'd at the time the parent exec's it, and not anything inside the child involving argv[0].
As such, AA can detect whether you did exec("gzip") or exec("gunzip") and apply the policy relevant to the program. It could apply different policies to each of them, so whether it has access to /tmp/mumble/barf depends on whether you called it 'gzip' or 'gunzip'. Caveat: it makes no sense to profile either gzip or gunzip in the AppArmor model, so I won't defend what kind of policy you would put on them.
Finally, AA doesn't care what the contents of the executable are. We assume that it is a copy of metasploit or something, and confine it to access only the resources that the policy says.
Quoted 7 lines
If
you don't want to, that's ok too. Jeremy raised a reasonable objection, and AppArmor could address it if y'all chose to do so. I seriously doubt that enforcing the argv[0] convention would break much, and I also expect that if it did there's a Consultant's Retirement to be made fixing the security hole it points out.
AppArmor does address it, and I hope this explains how we detect which of multiple hard links to a file you used to access the file without mucking about with argv[0].
Crispin
Signature · 3 lines
Crispin Cowan, Ph.D. http://crispincowan.com/~crispin/ Director of Software Engineering http://novell.com Security: It's not linear
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 2 lines
As such, AA can detect whether you did
exec("gzip")orexec("gunzip")and apply the policy relevant to the program. It could apply different
That's not actually useful for programs which link the same binary to multiple names because if you don't consider argv[0] as well I can run /usr/bin/gzip passing argv[0] of "gunzip" and get one set of policies and the other set of behaviour.
And then we have user added hardlinks of course.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 7 lines · Alan Cox
Quoted 2 lines
As such, AA can detect whether you did
exec("gzip")orexec("gunzip")and apply the policy relevant to the program. It could apply differentThat's not actually useful for programs which link the same binary to multiple names because if you don't consider argv[0] as well I can run
/usr/bin/gzippassing argv[0] of "gunzip" and get one set of policies and the other set of behaviour.
I partially agree. Taken together with the policy of the calling process, things suddenly start to make more sense though (even if gzip/gunzip don't make good examples): if only allowed to execute /usr/bin/gzip, the calling process can still get the gunzip behavior, but it will be bound by the /usr/bin/gzip policy.
Controlling the policy is what we really care about; this limits the allowed behavior. We cannot really control the behavior of an application anyway (think of bugs alone), but we can set the bounds for that behavior.
Quoted 1 line
And then we have user added hardlinks of course.
Yes, allowing confined processes to change what they are allowed to execute under a more permissive policy is not such a good idea.
Thanks,
Andreas
James Morris
maintainerRe: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Crispin CowanQuoted 3 lines · Crispin Cowan
Finally, AA doesn't care what the contents of the executable are. We assume that it is a copy of metasploit or something, and confine it to access only the resources that the policy says.
As long as these resources are only files. There is no confinement beyond that.
- James
Signature · 2 lines
James Morris <[email protected]>
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Casey SchauflerQuoted 1 line · Casey Schaufler
On Fedora zcat, gzip and gunzip are all links to the same file. I can imagine (although it is a bit of a stretch) allowing a set of users access to gunzip but not gzip (or the other way around).
That is a COMPLETE straw-man argument. I can override your "check" with this absolutely trivial perl code:
exec { "/usr/bin/gunzip" } "gzip", "-9", "some/file/to.gz";
Pathname-based checks are pretty fundamentally insecure. If you want to protect a "name", then you should tag the "name" with security attributes (IE: AppArmor). On the other hand, if you actually want to protect the _data_, then tagging the _name_ is flawed; tag the *DATA* instead.
Cheers,
Kyle Moffett
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 9 lines · Kyle Moffett
Quoted 3 lines · Casey Schaufler
On Fedora zcat, gzip and gunzip are all links to the same file. I can imagine (although it is a bit of a stretch) allowing a set of users access to gunzip but not gzip (or the other way around).
That is a COMPLETE straw-man argument. I can override your "check" with this absolutely trivial perl code:
exec { "
/usr/bin/gunzip" } "gzip", "-9", "some/file/to.gz";
The above Perl code executes /usr/bin/gunzip and sets argv[0] to "gzip", so this confirms that the value of argv[0] is arbitrary. Well great, we already knew.
Quoted 1 line
Pathname-based checks are pretty fundamentally insecure.
Please don't mix apples and pears. Some utilities change their behavior based on argv[0], which indeed is not secure. It doesn't have to be for those utilities' needs. This is different from pathname-based access control, which is based on the pathname of the binary actually being executed.
AppArmor does not look at argv[0] for anything, and doing so would be insane. So please don't jump to the wrong conclusions.
You can have application-level argv[0] checks in the binary that is hardlinked to /bin/gzip, /bin/gunzip, /bin/zcat. At the same time, the kernel knows which of these three versions it is executing, so from that point of view, they could be as well be separate copies, and different policy can be defined for each one of them. (This is ignoring the fact that for small utilities like gzip, profile inheritance is usually more useful than defining separate profiles.)
Thanks,
Andreas
Re: Pass struct vfsmount to the inode_create LSM hook
Hello.
Quoted 7 lines · Andreas Gruenbacher
Quoted 1 line
exec { "
/usr/bin/gunzip" } "gzip", "-9", "some/file/to.gz";The above Perl code executes
/usr/bin/gunzipand sets argv[0] to "gzip", so this confirms that the value of argv[0] is arbitrary. Well great, we already knew.AppArmor does not look at argv[0] for anything, and doing so would be insane. So please don't jump to the wrong conclusions.
I agree that argv[0] checking is different from pathname-based access control or label-based access control, but I want to say argv[0] checking is still needed.
If you don't check argv[0], an attacker can request everything like
exec { "/bin/ls" } "/sbin/busybox", "cat", "/etc/shadow"; exec { "/bin/ls" } "/sbin/busybox", "rm", "/etc/shadow";
if /bin/ls and /bin/cat and /bin/rm are hardlinks of /sbin/busybox (e.g. embedded systems).
Therefore, TOMOYO Linux checks the combination of filename and argv[0] passed to execve().
Thanks.
Re: Pass struct vfsmount to the inode_create LSM hook
Quoted 18 lines · Tetsuo Handa
Hello.
Quoted 8 lines · Andreas Gruenbacher
Quoted 1 line
exec { "
/usr/bin/gunzip" } "gzip", "-9", "some/file/to.gz";The above Perl code executes
/usr/bin/gunzipand sets argv[0] to "gzip", so this confirms that the value of argv[0] is arbitrary. Well great, we already knew.AppArmor does not look at argv[0] for anything, and doing so would be insane. So please don't jump to the wrong conclusions.
I agree that argv[0] checking is different from pathname-based access control or label-based access control, but I want to say argv[0] checking is still needed.
If you don't check argv[0], an attacker can request everything like
Quoted 8 lines
exec { "
/bin/ls" } "/sbin/busybox", "cat", "/etc/shadow"; exec { "/bin/ls" } "/sbin/busybox", "rm", "/etc/shadow";if
/bin/lsand/bin/catand/bin/rmare hardlinks of/sbin/busybox(e.g. embedded systems).Therefore, TOMOYO Linux checks the combination of filename and argv[0] passed to
execve().
So you are indeed trying to control the value of argv[0]? Well, good luck with that, but it's totally insane. You are guaranteed to break some applications. AppArmor is not going to go down that route.
If /bin/cat and /bin/rm are binaries or hardlinks to the same busybox binary (rather than symlinks), different profiles could be used for each of them. The cat profile could have no more than read access anywhere, so this would prevent it from removing files. That's an effective access control mechanism.
Conspiring with busybox in the kernel and making it do the right thing (and just that) at the application level is not an effective security mechanism:
the kernel would still allow busybox to do anything, and there could be bugs in the application, busybox's calling convention could change to allow more than one command per invocation (e.g., ``cat /etc/shadow ; rm /etc/shadow''), etc.
Andreas
Re: Pass struct vfsmount to the inode_create LSM hook
Hello.
Quoted 4 lines · Andreas Gruenbacher
Quoted 2 lines
Therefore, TOMOYO Linux checks the combination of filename and argv[0] passed to
execve().So you are indeed trying to control the value of argv[0]? Well, good luck with that, but it's totally insane. You are guaranteed to break some applications.
TOMOYO Linux ristricts argv[0] using allow_argv0 syntax.
"allow_argv0 /bin/bash -bash" to allow passing "/bin/bash" to filename and "-bash" to argv[0] . "allow_argv0 /bin/gzip gunzip" to allow passing "/bin/gzip" to filename and "gunzip" to argv[0] . "allow_argv0 /sbin/busybox cat" to allow passing "/sbin/busybox" to filename and "cat" to argv[0] . No need to use allow_argv0 syntax if the basename of filename and basename of argv[0] are the same (i.e. "allow_argv0 /bin/bash bash" is not required).
TOMOYO Linux doesn't unconditionally forbid passing different values for filename and argv[0]. TOMOYO Linux allows passing different values for filename and argv[0] only if it is allowed by allow_argv0 syntax. Could you please explain me why this approach breaks applications?
Quoted 2 lines
If
/bin/catand/bin/rmare binaries or hardlinks to the same busybox binary (rather than symlinks), different profiles could be used for each of them.
It is true if all processes are kept under control (e.g. strict policy in SELinux). If there is a process that is not kept under control (e.g. targeted policy in SELinux), you can't protect the application.
For example, an administrator may wish to allow users run /bin/ls without applying profiles because /bin/ls won't read/write the content of files. But a malicious user may pass "/bin/ls" to filename and "rm" to argv[0] and "/etc/shadow" to argv[1].
A malicious user may pass "/bin/ls" to filename and "/usr/sbin/httpd" to argv[0], resulting behave as /usr/sbin/httpd without applying profiles for /usr/sbin/httpd .
Thanks.
Re: Pass struct vfsmount to the inode_create LSM hook
Quoted 11 lines · Tetsuo Handa
Hello.
Quoted 6 lines · Andreas Gruenbacher
Quoted 2 lines
Therefore, TOMOYO Linux checks the combination of filename and argv[0] passed to
execve().So you are indeed trying to control the value of argv[0]? Well, good luck with that, but it's totally insane. You are guaranteed to break some applications.
TOMOYO Linux restricts argv[0] using
allow_argv0syntax.
Alright, so it's configurable. This reduces it from being broken to being a truly bad idea.
Quoted 2 lines
For example, an administrator may wish to allow users to run
/bin/lswithout applying profiles because/bin/lswon't read/write the content of files.
No, forget that line of reasoning. As soon as you run anything unconfined that isn't trusted, you have basically lost control. Relying on the fact that /bin/ls won't do any harm is assuming to much. Features might exist that allow it to be abused, new features might get added, or there may be bugs. Just don't do it.
Andreas
Re: Pass struct vfsmount to the inode_create LSM hook
In reply to Tetsuo HandaQuoted 9 lines · Tetsuo Handa
Quoted 4 lines · Andreas Gruenbacher
Quoted 1 line · Tetsuo Handa
Therefore, TOMOYO Linux checks the combination of filename and argv[0] passed to
execve().So you are indeed trying to control the value of argv[0]? Well, good luck with that, but it's totally insane. You are guaranteed to break some applications.
TOMOYO Linux ristricts argv[0] using
allow_argv0syntax. "allow_argv0/bin/bash-bash" to allow passing "/bin/bash" to filename and "-bash" to argv[0]. "allow_argv0/bin/gzipgunzip" to allow passing "/bin/gzip" to filename and "gunzip" to argv[0]. "allow_argv0/sbin/busyboxcat" to allow passing "/sbin/busybox" to filename and "cat" to argv[0]. No need to useallow_argv0syntax if the basename of filename and basename of argv[0] are the same (i.e. "allow_argv0/bin/bashbash" is not required). TOMOYO Linux doesn't unconditionally forbid passing different values for filename and argv[0]. TOMOYO Linux allows passing different values for filename and argv[0] only if it is allowed byallow_argv0syntax.Could you please explain me why this approach breaks applications?
One of my servers runs 3 different instances of the "kadmind" Kerberos daemon, one for each realm which I need to be able to modify/change-passwords/etc. In order to differentiate and stop/restart the appropriate daemon, I have a simple starter script which runs each kadmind process with a unique name derived from the realm (EG: "kadmind(EXAMPLE.COM)", "kadmind(OTHER.EXAMPLE.COM)"). Since this is a Kerberos server I use a very strict SELinux-based policy, yet my management tools need to be able to easily add and remove realms in a secure fashion. It sounds like TOMOYO Linux would not be able to handle this situation at all; I would either have to completely turn off that security "feature" and lose most of the functionality of TOMOYO Linux, or hard-code the list of realms into the policy file and have to completely reload policy every time I need to add/remove realms (big gaping security hole).
Cheers,
Kyle Moffett
James Morris
maintainerRe: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Kyle MoffettQuoted 2 lines · Kyle Moffett
AppArmor). On the other hand, if you actually want to protect the _data_, then tagging the _name_ is flawed; tag the *DATA* instead.
Bingo.
(This is how traditional Unix DAC has always functioned, and is what SELinux does: object labeling).
- James
Signature · 2 lines
James Morris <[email protected]>
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
2007/5/27, James Morris <[email protected]>:
Quoted 8 lines · Kyle Moffett
Quoted 2 lines
AppArmor). On the other hand, if you actually want to protect the _data_, then tagging the _name_ is flawed; tag the *DATA* instead.
Bingo.
(This is how traditional Unix DAC has always functioned, and is what SELinux does: object labeling).
Object labeling (or labeled security) looks simple and straight forward way, but it's not.
(1) Object labeling has a assumption that labels are always properly defined and maintained. This can not be easily achieved.
(2) Also, assigning a label is something like inventing and assigning a *new* name (label name) to objects which can cause flaws.
I'm not saying labeled security or SELinux is wrong. I just wanted to remind that the important part is the "process" not the "result". :-)
Signature · 2 lines
Toshiharu Harada [email protected]
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 11 lines · Toshiharu Harada
2007/5/27, James Morris <[email protected]>:
Quoted 6 lines · Kyle Moffett
Quoted 1 line
AppArmor). On the other hand, if you actually want to protect the _data_, then tagging the _name_ is flawed; tag the *DATA* instead.
Bingo.
(This is how traditional Unix DAC has always functioned, and is what SELinux does: object labeling).
Object labeling (or labeled security) looks simple and straight forward way, but it's not.
(1) Object labeling has a assumption that labels are always properly defined and maintained. This can not be easily achieved.
That's a circular argument, and a fairly trivial one at that. If you can't properly manage your labels, then how do you expect any security at all? If you can't manage your "labels", then pathname-based security won't work either. This is analogous to saying "Pathname-based security has an assumption that path-permissions are always properly defined and maintained", which is equally obvious. If you can't achieve the first with reasonable security, then you probably can't achieve the second either. Also, if you can't manage correct object labeling then I'm very interested in how you are maintaining secure Linux systems without standard DAC.
Quoted 1 line
(2) Also, assigning a label is something like inventing and assigning a *new* name (label name) to objects which can cause flaws.
I don't understand how assigning new attributes to objects "can cause flaws", nor what flaws those might be; could you elaborate further? In particular, I don't see how this is really all that more complicated than defining additional access control in apache .htaccess files. The principle is the same: by stacking multiple independent security-verification mechanisms (Classical UNIX DAC and Apache permissions) you can increase security, albeit at an increased management cost. You might also note that ".htaccess" files are yet another form of successful "label-based" security; the security context for a directory depends on the .htaccess "label" file found within. The *exact* same principles apply to SELinux: you add additional attributes backed by a simple and powerful state-machine. The cross-checks are lower-level than those from .htaccess files, but the principles are the same.
Cheers,
Kyle Moffett
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 8 lines · Kyle Moffett
Quoted 2 lines · Toshiharu Harada
(1) Object labeling has a assumption that labels are always properly defined and maintained. This can not be easily achieved.
That's a circular argument, and a fairly trivial one at that. If you can't properly manage your labels, then how do you expect any security at all?
Unfortunately, it's not at all as simple as all that. Toshiharu is quite correct that it isn't always easy to actually implement. Consider how many ad-croc usages of 'restorecon' are needed to get a Fedora SELinux box through rc.sysinit:
% grep restorecon /etc/rc.sysinit
if [ -n "$SELINUX_STATE" -a -x /sbin/restorecon ] && LC_ALL=C fgrep -q " /dev " /proc/mounts ; then
/sbin/restorecon -R /dev 2>/dev/null
REBOOTFLAG=restorecon -v /sbin/init
[ -n "$SELINUX_STATE" ] && restorecon /dev/pts >/dev/null 2>&1
[ -n "$SELINUX_STATE" ] && restorecon /dev/mapper /dev/mapper/control >/dev/null 2>&1
[ -n "$SELINUX_STATE" -a -e "$path" ] && restorecon -R "$path"
[ -n "$SELINUX_STATE" -a -e "$path" ] && restorecon -R "$path"
[ -n "$SELINUX_STATE" -a -e "$path" ] && restorecon -R "$path" restorecon /etc/mtab /etc/ld.so.cache /etc/blkid/blkid.tab /etc/resolv.conf >/dev/null 2>&1
[ -n "$SELINUX_STATE" ] && restorecon /tmp
[ -n "$SELINUX_STATE" ] && restorecon /tmp/.ICE-unix >/dev/null 2>&1
And that's just getting the system up to single-user. Things like sendmail and sshd require more restorecon handholding in their rc.init files.
Or just look at the creeping horror that is 'restorecond' (in particular, consider that the default restorcond.conf contains the strings '~/public_html' and '~/.mozilla/plugins/libflashplayer.so'. Yee. Frikkin. Hah. ;)
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 11 lines · On May 26, 2007, at 22:37:02, [email protected] wrote:
Quoted 8 lines · Kyle Moffett
Quoted 2 lines · Toshiharu Harada
(1) Object labeling has a assumption that labels are always properly defined and maintained. This can not be easily achieved.
That's a circular argument, and a fairly trivial one at that. If you can't properly manage your labels, then how do you expect any security at all?
Unfortunately, it's not at all as simple as all that. Toshiharu is quite correct that it isn't always easy to actually implement. Consider how many ad-croc usages of 'restorecon' are needed to get a Fedora SELinux box through rc.sysinit:
While I don't think restorecon is really necessary to properly boot SELinux-enabled (I've got a Debian box with some heavily customized policy which does so just fine), I am of the opinion that adding a "name" parameter to the file/directory create actions would be useful. For example, with such support you could actually specify a type-transition rule conditional on a specific name or substring:
named_type_transition sshd_t tmp_t:sock_file prefix "ssh-" ssh_sock_t;
Useful options for matching would be "prefix", "suffix", "substr(start,len)". "regex" would be nice but is sorta computationally intensive and would be likely to cause more problems than it solves.
Quoted 2 lines
/sbin/restorecon-R /dev 2>/dev/null
[ -n "$SELINUX_STATE" ] && restorecon/dev/mapper/dev/mapper/control>/dev/null 2>&1
These can go away if you modify your policy and pass "-o fscontext=system_u:object_r:dev_t" to the mount command for the /dev tmpfs, changing both the filesystem and the default directory labels from the default "system_u:object_r:tmpfs_t". This will work as long as your policy files contain appropriate transitions from the dev_t type.
Quoted 4 lines
REBOOTFLAG=
restorecon -v /sbin/init
restorecon/etc/mtab/etc/ld.so.cache/etc/blkid/blkid.tab/etc/resolv.conf>/dev/null 2>&1 [ -n "$SELINUX_STATE" ] && restorecon /tmp
[ -n "$SELINUX_STATE" ] && restorecon/tmp/.ICE-unix>/dev/null 2>&1
I believe these are to handle rebooting from non-SELinux mode. There are two solutions to this kind of problem:
(1) Failing the boot if the labels are wrong
(2) Fixing the labels (and rebooting if necessary)
It would appear that FC does the latter, although for certain high-security systems (such as firewalls handling classified/confidential data), the first option is the only acceptable one.
Quoted 1 line
[ -n "$SELINUX_STATE" ] && restorecon
/dev/pts>/dev/null 2>&1
I think this can be handled with some combination of appropriate SELinux policy and mount options. At least, I don't need this in the boot scripts on my heavily customized Debian SELinux box.
Quoted 1 line
[ -n "$SELINUX_STATE" -a -e "$path" ] && restorecon -R "$path"
I don't know what the point of this generic line is; but I certainly don't have anything of the sort on my test system, and it appears to work just fine.
Quoted 3 lines
And that's just getting the system up to single-user. Things like sendmail and sshd require more restorecon handholding in their rc.init files.
Or just look at the creeping horror that is 'restorecond' (in particular, consider that the default restorcond.conf contains the strings '~/public_html' and '~/.mozilla/plugins/libflashplayer.so'. Yee. Frikkin. Hah. ;)
Part of the reason that Fedora has a large quantity of that restorecon and restorecond crap is that there is a certain amount of broken binary software needing executable stack/heap (such as flashplayer), programs without comprehensive or complete policies, or programs which by definition need extra support for SELinux.
For example, to really complete the SELinux model you need editors and tools which understand security labels the same way they understand UNIX permissions. With a bit of vim scripting I can probably make it run external commands to read file labels before it reads the file itself and modify /proc/self/attr/fscreate before writing out the file, similar to the way it would keep track of the standard DAC permissions on files it modifies.
There's also the fact that corporate products have fixed release schedules so remaining bugs in each release tend to get papered over instead of fixed properly (such as the restorecon in FC init-scripts). I haven't seen many problems with the SELinux model which aren't associated with working around buggy software or missing features.
Cheers,
Kyle Moffett
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Hi!
Quoted 21 lines
Quoted 11 lines
Quoted 5 lines
That's a circular argument, and a fairly trivial one at that. If you
can't properly manage your labels, then how do you expect any
security at all?Unfortunately, it's not at all as simple as all that. Toshiharu is quite correct that it isn't always easy
to actually implement. Consider how many ad-croc
usages of 'restorecon' are needed to get a Fedora SELinux box through rc.sysinit:While I don't think restorecon is really necessary to properly boot SELinux-enabled (I've got a Debian box with some heavily customized policy which does so just fine), I am of the opinion that adding a "name" parameter to the file/directory create actions would be useful. For example, with such support you could actually specify a type-transition rule conditional on a specific name or substring:
Quoted 7 lines
named_type_transitionsshd_ttmp_t:sock_file prefix "ssh-"ssh_sock_t;Useful options for matching would be "prefix", "suffix", "substr (start,len)". "regex" would be nice but is sorta computationally intensive and would be likely to cause more problems than it solves.
Could someone implement this? AFAICT that prevents SELinux from being superset of AppArmor... Doing this should be significantly simpler than whole AA, and hopefully it will end up less ugly, too.
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 8 lines · Pavel Machek
Quoted 5 lines · Kyle Moffett
I am of the opinion that adding a "name" parameter to the file/directory create actions would be useful. For example, with such support you could actually specify a type-transition rule conditional on a specific name or substring:
named_type_transitionsshd_ttmp_t:sock_file prefix "ssh-"ssh_sock_t;Useful options for matching would be "prefix", "suffix", "substr (start,len)". "regex" would be nice but is sorta computationally intensive and would be likely to cause more problems than it solves.
Could someone implement this? AFAICT that prevents SELinux from being superset of AppArmor... Doing this should be significantly simpler than whole AA, and hopefully it will end up less ugly, too.
Really it would need to extend all action-match items with new "named_" equivalents, and most callbacks would need to be extended to pass in an object name, if available. On the other hand, with such support implemented then the AppArmor policy compilation tools could be transformed into a simple SELinux policy generator. I estimate that the number of new lines of kernel code for such a modified SELinux would be 100x less than the kernel code in AppArmor.
Cheers,
Kyle Moffett
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Kyle Moffett2007/5/27, Kyle Moffett <[email protected]>:
Quoted 12 lines · Toshiharu Harada
Quoted 11 lines
2007/5/27, James Morris <[email protected]>:
Quoted 9 lines · Kyle Moffett
Quoted 3 lines
AppArmor). On the other hand, if you actually want to protect the _data_, then tagging the _name_ is flawed; tag the *DATA* instead.
Bingo.
(This is how traditional Unix DAC has always functioned, and is what SELinux does: object labeling).
Quoted 7 lines
Quoted 5 lines
Object labeling (or labeled security) looks simple and straight forward way, but it's not.
(1) Object labeling has a assumption that labels are always properly defined and maintained. This can not be easily achieved.
That's a circular argument, and a fairly trivial one at that.
Sorry Kyle, I don't think it's a trivial one. The opposite.
Quoted 2 lines
If you can't properly manage your labels, then how do you expect any security at all?
Please read my message again. I didn't say, "This can never be achieved". I said, "This can not be easily achieved".
Quoted 4 lines
If you can't manage your "labels", then pathname-
based security won't work either. This is analogous to saying "Pathname-based security has an assumption that path-permissions are always properly defined and maintained", which is equally obvious.
Yes,! You got the point.
Both label-based and pathname-based apporaches have the advantaes and difficluties.
In that sense they are equal. That's what I wanted to say. Both approaches can be improved and even can be used combined to overcome the difficulties. Let's not fight against and think together, then we can make Linux better.
Quoted 4 lines
If you can't achieve the first with reasonable security, then you probably can't achieve the second either. Also, if you can't manage correct object labeling then I'm very interested in how you are maintaining secure Linux systems without standard DAC.
I'm very interested in how you can know that you have the correct object labeling (this is my point). Could you tell? I assume your best efforts end up with
- have a proper fc definitions and guard them (this can be done)
- executes relabeling as needed (best efforts)
- hope everything work fine
Quoted 17 lines
Quoted 2 lines
(2) Also, assigning a label is something like inventing and assigning a *new* name (label name) to objects which can cause flaws.
I don't understand how assigning new attributes to objects "can cause flaws", nor what flaws those might be; could you elaborate further? In particular, I don't see how this is really all that more complicated than defining additional access control in apache .htaccess files. The principle is the same: by stacking multiple independent security-verification mechanisms (Classical UNIX DAC and Apache permissions) you can increase security, albeit at an increased management cost. You might also note that ".htaccess" files are yet another form of successful "label-based" security; the security context for a directory depends on the .htaccess "label" file found within. The *exact* same principles apply to SELinux: you add additional attributes backed by a simple and powerful state- machine. The cross-checks are lower-level than those from .htaccess files, but the principles are the same.
I don't deny DAC at all. If we deny DAC, we can't live with Linux it's the base. MAC can be used to cover the shortages of DAC and Linux's simple user model, that's it.
Quoted 1 line
From security point of view, simplicity is always the virtue and the way to go.
Inode combined label is guaranteed to be a single at any point time. This is the most noticeable advantage of label-based security. But writing policy with labels are somewhat indirect way (I mean, we need "ls -Z" or "ps -Z"). Indirect way can cause flaw so we need a lot of work that is what I wanted to tell.
Cheers,
Toshiharu Harada
[email protected]
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 10 lines · Toshiharu Harada
2007/5/27, Kyle Moffett <[email protected]>:
Quoted 9 lines · Toshiharu Harada
Quoted 8 lines
2007/5/27, James Morris <[email protected]>:
Quoted 6 lines · Kyle Moffett
Quoted 1 line
AppArmor). On the other hand, if you actually want to protect the _data_, then tagging the _name_ is flawed; tag the *DATA* instead.
Bingo.
(This is how traditional Unix DAC has always functioned, and is what SELinux does: object labeling).
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
Object labeling (or labeled security) looks simple and straight forward way, but it's not.
(1) Object labeling has a assumption that labels are always properly defined and maintained. This can not be easily achieved.
That's a circular argument, and a fairly trivial one at that.
Sorry Kyle, I don't think it's a trivial one. The opposite.
How is that argument not trivially circular? "Foo has an assumption that foo-property is always properly defined and maintained." That could be said about *anything*:
* Unix permissions have an assumption that mode bits are always
properly defined and maintained
* Apache .htaccess security has an assumtion that .htaccess files
are always properly defined and maintained.
* Functional email communication has an assumption that the email
servers are always properly defined and maintained
Quoted 3 lines
Quoted 1 line
If you can't properly manage your labels, then how do you expect any security at all?
Please read my message again. I didn't say, "This can never be achieved". I said, "This can not be easily achieved".
So you said "(data labels) can not be easily achieved". My question for you is: How do you manage secure UNIX systems without standard UNIX permission bits? Also: If you have problems with data labels then what makes pathname based labels "easier"? If there is something that could be done to improve SELinux and make it more readily configurable then it should probably be done.
Quoted 3 lines
Quoted 1 line
If you can't achieve the first with reasonable security, then you probably can't achieve the second either. Also, if you can't manage correct object labeling then I'm very interested in how you are maintaining secure Linux systems without standard DAC.
I'm very interested in how you can know that you have the correct object labeling (this is my point). Could you tell?
I know that I have the correct object labeling because:
1) I rewrote/modified the default policy to be extremely strict on
the system where I wanted the extra security and hassle.
2) I ensured that the type transitions were in place for almost
everything that needed to be done to administer the system.
3) I wrote a file-contexts file and relabeled *once*
4) I loaded the customized policy plus policy for restorecon and
relabeled for the last time
5) I reloaded the customized policy without restorecon privileges
and without the ability to reload the policy again.
6) I never reboot the system without enforcing mode.
7) If there are unexpected errors or files have incorrect labels,
I have to get the security auditor to log in on the affected system and relabel the problematic files manually (rare occurrence which requires excessive amounts of paperwork).
Quoted 7 lines
Quoted 3 lines
Quoted 1 line
(2) Also, assigning a label is something like inventing and assigning a *new* name (label name) to objects which can cause flaws.
I don't understand how assigning new attributes to objects "can cause flaws", nor what flaws those might be; could you elaborate further? In particular, I don't see how this is really all that more complicated than defining additional access control in apache .htaccess files. The principle is the same: by stacking multiple independent security-verification mechanisms (Classical UNIX DAC and Apache permissions) you can increase security, albeit at an increased management cost. You might also note that ".htaccess" files are yet another form of successful "label-based" security; the security context for a directory depends on the .htaccess "label" file found within. The *exact* same principles apply to SELinux: you add additional attributes backed by a simple and powerful state-machine. The cross-checks are lower-level than those from .htaccess files, but the principles are the same.
I don't deny DAC at all. If we deny DAC, we can't live with Linux it's the base. MAC can be used to cover the shortages of DAC and Linux's simple user model, that's it.
From security point of view, simplicity is always the virtue and the way to go. Inode combined label is guaranteed to be a single at any point time. This is the most noticeable advantage of label-based security.
I would argue that pathname-based security breaks the "simplicity is the best virtue (of a security system)" paradigm, because it attributes multiple potentially-conflicting labels to the same piece of data. It also cannot protect the secrecy of specific *data* as well as SELinux can. For example: In SELinux MLS a system could mark customer credit-card data as the "cust_private_info" category and it would be completely impossible for any program without the "cust_private_info" category to read that data, and even then it could only be written to files which also have "cust_private_info" set. While a few privileged programs may have "mlsread" or "mlswrite" attributes allowing them to override such restrictions, it's a much stronger security guarantee than pathname-based security could ever provide.
Quoted 1 line
But writing policy with labels are somewhat indirect way (I mean, we need "ls -Z" or "ps -Z"). Indirect way can cause flaw so we need a lot of work that is what I wanted to tell.
I don't really use "ls -Z" or "ps -Z" when writing SELinux policy; I do that only when I actually think I mislabeled files. Typically the SELinux-policy-development cycle is:
1) Modify and reload the policy
2) Relabel the affected files (either by hand or with some
automated tool like restorecon)
3) Rerun the problem program or daemon
4) Examine the errors in the audit logs. If there are no errors
and it works then you're finished.
5) Go back to step 1 and fix your policy
Cheers,
Kyle Moffett
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
2007/5/27, Kyle Moffett <[email protected]>:
Quoted 6 lines · Toshiharu Harada
Quoted 1 line
2007/5/27, Kyle Moffett <[email protected]>:
How is that argument not trivially circular? "Foo has an assumption that foo-property is always properly defined and maintained." That could be said about *anything*:
What I wanted to mention was the difficulties or efforts to make assumptions real. I never meant a circular argument, but if you felt so I apologize sincerely.
Quoted 6 lines
Quoted 5 lines
Quoted 2 lines
If you can't properly manage your labels, then how do you expect any security at all?
Please read my message again. I didn't say, "This can never be achieved". I said, "This can not be easily achieved".
Quoted 6 lines
So you said "(data labels) can not be easily achieved". My question for you is: How do you manage secure UNIX systems without standard UNIX permission bits? Also: If you have problems with data labels then what makes pathname based labels "easier"? If there is something that could be done to improve SELinux and make it more readily configurable then it should probably be done.
Permission bits can be checked easily with "ls" command, but assuring the correctness of labels are not that easy. I'll try to explain.
The correctness of the permission bit for a given file can be judged solely by the result of "ls" command. The correctness of the label, on the other hand, can't be judged without understanding of whole policy including domain transitions. (see the attached figure)
I can imagine that once one get the complete SELinux policy, then it is able to modify and maintain it.
I don't say making a complete SELinux policy is impossible, and actually you said you did it. But to be frank, I don't think you are the average level user at all. ;-)
Quoted 4 lines
Quoted 2 lines
I'm very interested in how you can know that you have the correct object labeling (this is my point). Could you tell?
I know that I have the correct object labeling because:
Do you mind if I add this?
0) I understood the default policy and perfectly understand the every behavior of my system.
this is where the difficulties exist.
Quoted 6 lines
1) I rewrote/modified the default policy to be extremely strict on
the system where I wanted the extra security and hassle.
2) I ensured that the type transitions were in place for almost
everything that needed to be done to administer the system.
3) I wrote a file-contexts file and relabeled *once*
4) I loaded the customized policy plus policy for restorecon and
Quoted 8 lines
relabeled for the last time
5) I reloaded the customized policy without restorecon privileges
and without the ability to reload the policy again.
6) I never reboot the system without enforcing mode.
7) If there are unexpected errors or files have incorrect labels,
I have to get the security auditor to log in on the affected system and relabel the problematic files manually (rare occurrence which requires excessive amounts of paperwork).
Thank you for the procedures. It's quite helpful.
Quoted 4 lines
Quoted 4 lines
I don't deny DAC at all. If we deny DAC, we can't live with Linux it's the base. MAC can be used to cover the shortages of DAC and Linux's simple user model, that's it.
Quoted 8 lines
Quoted 4 lines
From security point of view, simplicity is always the virtue and the way to go. Inode combined label is guaranteed to be a single at any point time. This is the most noticeable advantage of label- based security.
I would argue that pathname-based security breaks the "simplicity is the best virtue (of a security system)" paradigm, because it attributes multiple potentially-conflicting labels to the same piece
Every pathname-based security must provide the mechanism to prevent a conflicting/malicious access, otherwise it's junk.
I have a question for you. With current implementation of SELinux, only one label can be assigned. But there are cases that one object can be used in different context, so I think it might help if SELinux would allow objects to have multiple labels. (I'm not talking about conflicts here) What do you think?
Quoted 6 lines
Quoted 3 lines
But writing policy with labels are somewhat indirect way (I mean, we need "ls -Z" or "ps -Z"). Indirect way can cause flaw so we need a lot of work that is what I wanted to tell.
I don't really use "ls -Z" or "ps -Z" when writing SELinux policy; I do that only when I actually think I mislabeled files.
I believe what you wrote, but it may not be as easy for average Linux users.
Quoted 8 lines
Typically the SELinux-policy-development cycle is:
1) Modify and reload the policy
2) Relabel the affected files (either by hand or with some
automated tool like restorecon)
3) Rerun the problem program or daemon
4) Examine the errors in the audit logs. If there are no errors
and it works then you're finished.
5) Go back to step 1 and fix your policy
Cheers,
Toshiharu Harada
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 9 lines · Toshiharu Harada
2007/5/27, Kyle Moffett <[email protected]>:
Quoted 4 lines
Quoted 2 lines
Quoted 1 line
If you can't properly manage your labels, then how do you expect any security at all?
Please read my message again. I didn't say, "This can never be achieved". I said, "This can not be easily achieved".
So you said "(data labels) can not be easily achieved". My question for you is: How do you manage secure UNIX systems without standard UNIX permission bits? Also: If you have problems with data labels then what makes pathname based labels "easier"? If there is something that could be done to improve SELinux and make it more readily configurable then it should probably be done.
Permission bits can be checked easily with "ls" command but assuring the correctness of labels are not that easy. I'll try to explain.
The correctness of the permission bit for a given file can be judged solely by the result of "ls" command. The correctness of the label, on the other hand, can't be judged without understanding of whole policy including domain transitions. (see the attached figure) I can imagine that once one get the complete SELinux policy, then it is able to modify and maintain it.
That's why there are a number of efforts to make modular SELinux policies. A good SELinux policy provides a few core system types and labels which a policy developer needs to understand, as well as some good macros to simplify the human-editable policy files. For instance, in my customized policy a daemon run by an initscript which reads a single config file in /etc needs this policy (Note that I use "_d" as a suffix for process domains instead of the usual "_t"):
initrc_daemon(foo_exec_t, foo_d)daemon_config(foo_d, foo_conf_t)
Add maybe 2 lines for network port access, another 2 for database files in /var, plus maybe an "iptables" rule or two in your firewall file.
Quoted 1 line
I don't say making a complete SELinux policy is impossible, and actually you said you did it. But to be frank, I don't think you are the average level user at all. ;-)
Average users are not supposed to be writing security policy. To be honest, even average-level system administrators should not be writing security policy. It's OK for such sysadmins to tweak existing policy to give access to additional web-docs or such, but only expert sysadmin/developers or security professionals should be writing security policy. It's just too damn easy to get completely wrong.
Quoted 9 lines
Quoted 3 lines
Quoted 1 line
I'm very interested in how you can know that you have the correct object labeling (this is my point). Could you tell?
I know that I have the correct object labeling because:
Do you mind if I add this?
0) I understood the default policy and perfectly understand the every behavior of my system.
this is where the difficulties exist.
You don't have to understand the entire default policy; that's the point of modular policy. You only have to understand how to _use_ the interfaces of the system policy (which are documented) and how the particular daemon policy is supposed to work. The people developing the core system policy need to understand the inner workings of said policy, but they don't need to understand how the rest of the system works. The core functionality behind this separation is macro interfaces and attributes. By grouping types with attributes it is possible for arbitrary daemon types to categorize themselves under access rules defined by the base policy, and with interfaces the daemons don't really even need to know what those attributes are called.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
I don't deny DAC at all. If we deny DAC, we can't live with Linux it's the base. MAC can be used to cover the shortages of DAC and Linux's simple user model, that's it.
From security point of view, simplicity is always the virtue and the way to go. Inode combined label is guaranteed to be a single at any point time. This is the most noticeable advantage of label-based security.
I would argue that pathname-based security breaks the "simplicity is the best virtue (of a security system)" paradigm, because it attributes multiple potentially-conflicting labels to the same piece
I have a question for you. With current implementation of SELinux, only one label can be assigned. But there are cases
that one object can be used in different context, so I think it might help if SELinux would allow objects to have multiple labels. (I'm not talking about conflicts here) What do you think?
This is the whole advantage of SELinux type attributes: you can define a type "var_foo_t" which has a specific list of attributes; rules which accept type specifiers can also accept attribute specifiers as well. If what you want is a label which may be accessed in two different ways, then you declare attributes for each access method and declare a type which has the attributes "filetype", "access1", and "access2" (assuming access1 and access2 are the names of the attributes you declared and used in your access rules).
Quoted 6 lines
Quoted 4 lines
Quoted 1 line
But writing policy with labels are somewhat indirect way (I mean, we need "ls -Z" or "ps -Z"). Indirect way can cause flaw so we need a lot of work that is what I wanted to tell.
I don't really use "ls -Z" or "ps -Z" when writing SELinux policy; I do that only when I actually think I mislabeled files.
I believe what you wrote, but it may not be as easy for average Linux users.
As I said before, average Linux users should not be writing their own security policy. I have yet to meet an "average Linux user" who could reliably quote for me what the file permissions on the "/tmp" directory should be, or what the sticky bit was. A small percentage of average Linux system administrators don't get that right consistently, and if you don't understand the sticky bit then you should *definitely* not be controlling program permissions on a per-syscall basis.
Cheers,
Kyle Moffett
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 7 lines · Kyle Moffett
Average users are not supposed to be writing security policy. To be honest, even average-level system administrators should not be writing security policy. It's OK for such sysadmins to tweak existing policy to give access to additional web-docs or such, but only expert sysadmin/developers or security professionals should be writing security policy. It's just too damn easy to get completely wrong.
The single biggest challenge in computer security at the present time is how to build *and deploy* servers that stay reasonably secure even when run by the average wave-a-dead-chicken sysadmin, and desktop-class boxes that can survive the best attempts of Joe Sixpack's "Ooh shiny" reflex, and Joe's kid's attempts to evade the nannyware that Joe had somebody install.
(If you know how to build such things, don't bother replying. If you have actual field experience on getting significant percents of Joe Sixpacks to switch, I need to buy you a beer or something.. ;)
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 5 lines · [email protected] wrote:
Quoted 3 lines · Kyle Moffett
Average users are not supposed to be writing security policy. To be honest, even average-level system administrators should not be writing security policy.
That explains so much! "SELinux: you're too dumb to use it, so just keep your hands in your pockets." :-)
AppArmor was designed to allow your average sys admin to write a security policy. It makes different design choices than SELinux to achieve that goal. As a result, AppArmor is an utter failure when compared to SELinux's goals, and SELinux in turn is an utter failure when compared to AppArmor's goals.
Which is why we have LSM: so we don't have to have this argument here, again.
Quoted 6 lines
Quoted 6 lines
It's OK for such sysadmins to tweak
existing policy to give access to additional web-docs or such, but only expert sysadmin/developers or security professionals should be writing security policy. It's just too damn easy to get completely wrong.
Quoted 6 lines
The single biggest challenge in computer security at the present time is how to build *and deploy* servers that stay reasonably secure even when run by the average wave-a-dead-chicken sysadmin, and desktop-class boxes that can survive the best attempts of Joe Sixpack's "Ooh shiny" reflex, and Joe's kid's attempts to evade the nannyware that Joe had somebody install.
That is a tall order. You can mostly achieve it by not giving the user the root password, but I'm not sure you would like the result :-)
Both SELinux and AppArmor can be configured so tightly that you are not going to get to install malware, by preventing the user from installing software. This isn't what users want, so they invariably bypass security and install shiny things if they own the box. SELinux and AppArmor can't help but fail if you put them in that kind of harm's way.
Crispin
Signature · 3 lines
Crispin Cowan, Ph.D. http://crispincowan.com/~crispin/ Director of Software Engineering http://novell.com Security: It's not linear
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 4 lines
Quoted 2 lines
Quoted 2 lines
honest, even average-level system administrators should not be writing security policy.
That explains so much! "SELinux: you're too dumb to use it, so just keep your hands in your pockets." :-)
Hardly. And there are helper tools
Quoted 3 lines
AppArmor was designed to allow your average sys admin to write a security policy.
Which is a bit like giving a small child an Uzi 9mm and inviting them to teach themselves to shoot.
Quoted 5 lines
Both SELinux and AppArmor can be configured so tightly that you are not going to get to install malware, by preventing the user from installing software. This isn't what users want, so they invariably bypass security and install shiny things if they own the box. SELinux and AppArmor can't help but fail if you put them in that kind of harm's way.
That depends who owns the admin password. That kind of thing (coupled with 'can install approved packages' and suitable policy) works very well in some environments where the user doesn't get the admin password - ie much of business.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Crispin CowanHi!
Quoted 11 lines
Quoted 3 lines
Quoted 3 lines
Average users are not supposed to be writing security policy. To be honest, even average-level system administrators should not be writing security policy.
That explains so much! "SELinux: you're too dumb to use it, so just keep your hands in your pockets." :-)
AppArmor was designed to allow your average sys admin to write a security policy. It makes different design choices than SELinux to achieve that goal. As a result, AppArmor is an utter failure when compared to SELinux's goals, and SELinux in turn is an utter failure when compared to AppArmor's goals.
I'd not be that sure. SELinux can read AA config files, with some performance problems and bad problems with new files.
I bet solving the 'new files' problem is not going to take 20% of AA's size...
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Kyle Moffett2007/5/29, Kyle Moffett <[email protected]>:
Quoted 10 lines
Quoted 9 lines
Quoted 6 lines
Quoted 3 lines
But writing policy with labels are somewhat indirect way (I mean, we need "ls -Z" or "ps -Z"). Indirect way can cause flaw so we need a lot of work that is what I wanted to tell.
I don't really use "ls -Z" or "ps -Z" when writing SELinux policy; I do that only when I actually think I mislabeled files.
I believe what you wrote, but it may not be as easy for average Linux users.
Quoted 8 lines
As I said before, average Linux users should not be writing their own security policy. I have yet to meet an "average Linux user" who could reliably quote for me what the file permissions on the "/tmp" directory should be, or what the sticky bit was. A small percentage of average Linux system administrators don't get that right consistently, and if you don't understand the sticky bit then you should *definitely* not be controlling program permissions on a per- syscall basis.
Thank you for your detailed and thoughtful explanation. Things are much clear now for me. Although your explanation was quite persuasive, I still have some concerns.
Linux is now being used literately everywhere. As devices, technologies and Linux itself is evolving so quickly, I'm afraid the way you showed was right but could never meet the every goal perfectly. So some areas, including embedded and special distro I guess, there must be solutions and help for average level administrators.
I think there are two ways to make secure systems. One is just you wrote: "ask it professionals" way, the other is "making practices". You might ask "how?" My answer to the question is pahtname-based systems such as AppAmor and TOMOYO Linux.
They can't be compared to SELinux, but they should be considered to supplemental tools. At least they are helpful to analyze how Linux works. Tweeking SELinux policy is not easy but writing policies for them is relatively easy (I'm not talking about security here).
Not everybody can be a professional administrators, but he/she can be a professional administrator of his/her system. I believe there must be solutions for non professional administrators. That's why we developed TOMOYO Linux (http://tomoyo.sourceforge.jp/) and so was AppArmor I guess. You might laugh, but we are doing this because we want to contribute to Linux and its community. :)
Thanks,
Toshiharu Harada
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Kyle MoffettQuoted 1 line
Quoted 1 line
On the other hand, if you actually want to protect the _data_, then tagging the _name_ is flawed; tag the *DATA* instead.
Would it make sense to label the data (resource) with a list of paths (names) that can be used to access it?
Therefore the data would be protected against being accessed via alternative arbitrary names. This may be a simple label to maintain and (possibly to) enforce, allowing path based confinement to protect a resource. This may allow for the benefits of pathname based confinement while avoiding some of its problems.
Obviously this would not protect against a pathname pointing to arbitrary data…
Just a thought.
Z. Cliffe Schreuders.
-
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
CC trimmed to remove a few poor overloaded inboxes from this tangent.
Quoted 6 lines · Cliffe
Quoted 1 line · Kyle
On the other hand, if you actually want to protect the _data_, then tagging the _name_ is flawed; tag the *DATA* instead.
Would it make sense to label the data (resource) with a list of paths (names) that can be used to access it?
Therefore the data would be protected against being accessed via alternative arbitrary names. This may be a simple label to maintain and (possibly to) enforce, allowing path based confinement to protect a resource. This may allow for the benefits of pathname based confinement while avoiding some of its problems.
The primary problem with that is that "mv somefile otherfile" must change the labels, which means that every process that issues a rename() syscall needs to have special handling of labels. The other problem is that many of the features and capabilities of SELinux get left by the wayside. On an SELinux system 90% of the programs don't need to be modified to understand labels, since the policy can define automatic label transitions. SELinux also allows you to have conditional label privileges based on boolean variables, something that cannot be done if the privileges themselves are stored in the filesystem. Finally, such an approach does not allow you to differentiate between programs.
Cheers,
Kyle Moffett
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to CliffeQuoted 5 lines · --- Cliffe
Quoted 1 line
Quoted 1 line
On the other hand, if you actually want to protect the _data_, then
tagging the _name_ is flawed; tag the *DATA* instead.
Would it make sense to label the data (resource) with a list of paths (names) that can be used to access it?
Program Access Lists (PALs) were* a feature of UNICOS. PALs could contain not only the list of programs that could use them, but what attributes the processes required as well. Further, you could restrict or raise privilege based on the uid, gid, MAC label, and privilege state of the process during exec based on the PAL.
Quoted 5 lines
Therefore the data would be protected against being accessed via alternative arbitrary names. This may be a simple label to maintain and (possibly to) enforce, allowing path based confinement to protect a resource. This may allow for the benefits of pathname based confinement while avoiding some of its problems.
Yep, but you still have the label based system issues, the classic case being the text editor that uses "creat new", "unlink old", "rename new to old". When the labeling scheme is more sopisticated than "object gets label of subject" label management becomes a major issue.
Quoted 2 lines
Obviously this would not protect against a pathname pointing to arbitrary data…
Protecting special data is easy. Protecting arbitrary data is the problem.
Quoted 1 line
Just a thought.
Not a bad one, and it would be an easy and fun LSM to create. If I were teaching a Linux kernel programming course I would consider it for a class project.
-----
* I have used the past tense here in spite of the many instances of UNICOS still in operation. I don't believe that there is any current development on UNICOS.
Casey Schaufler
[email protected]
-
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 13 lines · Cliffe
The following would be used in conjunction with a pathname based confinement to try to provide some assurances about what a path refers to.
"
/etc/shadow" is a name to a sensitive resource. There is no guarantee that there are not other ways to access this resource. For example a link "/etc/shadowlink" or a rename to "/etc/shadownewname"Pathname based security has various advantages (such as the ability to have more readable and centralised policies by describing resources in terms of their pathnames) but the above is clearly a problem (if confidentiality or integrity of a specific resource is important).
Quoted 8 lines
Could we find a way to make paths a more reliable mechanism by labeling the file data with a simple description of names it is allowed to be accessed with?
If we want "
/etc/shadow" to be the only way to access the shadow file we could label the data with "/etc/shadow". Any attempts to access this data using a renamed file or link would be denied (attempts to link or rename could also be denied).
Eloquently put.
AppArmor actually does something similar to this, by mediating all of the ways that you can make an alias to a file. These are:
* Symbolic links: these actually don't work for making aliases with respect to LSM-based security systems such as AppArmor, SELinux, and TOMOYO Linux, because the symbolic link is resolved before the access request is presented to the LSM hooks. So if you create the symbolic link /tmp/harmless -> /etc/shadow and then the confined victim tries to access /tmp/harmless, what the LSM sees is an attempt to access /etc/shadow.
* Hard links: AppArmor explicitly mediates permission to make a hard link to a file. To be able to make a hard link /tmp/harmless -> /etc/shadow you have to have explicit permission to do so, or the attempt to make the link is blocked. This mediation is kept relatively simple by the fact that you can only hard link to files, and not to directories.
* Multiple mounts: Linux lets you mount the same file system multiple times at multiple mount points, so the same file could appear at two different places. AppArmor has very coarse mediation here, and simply prohibits all calls to mount() without special permission. So only highly privileged processes can create such an alias.
* Name spaces: Linux name spaces present a similar issue, and AppArmor's coarse blocking of mount() serves the same function; only highly privileged processes are permitted to create name spaces.
Quoted 2 lines
We could label a document with "
/home/me/documents/*". Then the document could be linked or renamed within that specified directory.
We are thinking about something kind of like this for finer-grained mediation of mount(). Currently it is all-or-nothing; we either totally block the ability to create aliases with multiple mount points or name spaces, or we totally trust the process with permission to do what it likes with multiple mount point and name space aliases.
What we want is a more nuanced mediation of mount point and name space manipulation, so that you can write a spec that says "/tmp/* can be aliased any way you like, but /etc/shadow cannot be aliased at all" or something like that.
However, not much work has been done on this, because at the moment the AppArmor team is concentrating on making the code for current functionality acceptable for upstreaming.
Quoted 4 lines
Obvious problems with this solution:
* it seems hard to determine what the label should be set to (setting it to "*" would be tempting and completely negate the whole thing, while setting it to the current filename would often be too restrictive).
This is equivalent to the question of what kind of access to grant to the data. I don't see it as really difficult. What is harder is that the specification has to specify what you can alias, and where it can appear, and dealing with the two factors tends to make one's head hurt.
I am reminded of the pithy quote "Noalias must go. This is not negotiable" -- Dennis Ritchie, referring to a proposed 'noalias' type qualifier in ANSI C http://www.lysator.liu.se/c/dmr-on-noalias.html
Mediating permission to make aliases is complicated :(
Quoted 2 lines
* when files are created these labels need to be created, and they may need to be changed.
.. if you use the approach of labeling the files :) In fact this problem is general: label-based security has a hard time dealing with new files in general, not just with respect to permission to create aliases. The security policy for a newly created file is determined by the label on the file, and so labeling new files is critical. SELinux uses a variety of smart heuristics to decide the label of a new file:
* the label of the parent directory
* the label of the creating process
* the domain for the creating process can do conditional things based on the label of the parent directory and the creating process
* applications that link to libselinux can overtly set the label according to application logic
AppArmor, not using labels, doesn't have this problem. You can write an AA profile with respect to files and directories that don't exist at all.
Quoted 3 lines
* if this type of solution requires significant additional management (as it seems to) it is probably not a suitable addition to a pathname based confinement mechanism.
As I explained above, a lot of it is already in place with our mediation of hard links. Our mediation of multiple mount points and name spaces is lame, but the extent to which this needs to be improved will largely be determined by how application developers choose to use these features. Because AppArmor is intended primarily to confine applications, an administrative approach of just using unconfined (completely trusted) shells to manipulate name spaces and multiple mount points suffices, and application manipulation of name spaces and multiple mount points is the only concern.
Quoted 2 lines
* A program creating a new file and copying the contents of the file is outside of the scope of this solution.
That is the information flow problem. While important, it is fairly separate from the alias problem. The information flow problem is general to all access control systems, while the alias problem is special to name-based access control systems.
This is one of the name/label trade-offs. Aliasing is problematic in name based systems, while new file creation is problematic in label based systems. This follows directly from the semantics each system is using:
* Names are references. Name based systems are really mediating access to references. Thus creating new references and who can access these new references is problematic.
* Labels are equivalence classes of objects. Label based systems are really mediating access to objects, regardless of the reference. Thus creating new objects and who can access these new objects is problematic.
Quoted 3 lines
* Stoping a path from pointing to arbitrary data is also outside the scope of this solution (for example "
/etc/shadow" could be replaced with another file).
This depends on where you place the "may alias" access control. You can put it on the source (/tmp/harmless -> somewhere) on the target (somewhere -> /etc/shadow) or somewhere else (what AppArmor does, associating the policy with confined processes).
Quoted 5 lines
Could a name based policy include the pathname as well as an inode number or some (more secure) identifying attribute (which could be calculated based on the supplied pathname)? A one-way-hash would identify but could not be used for each file a program accesses as they can change...
It could be done, but that has implications. That would mean that you get the "belt and suspenders" effect of all of the security benefits of (say) AppArmor and SELinux. But the non-overlapping security benefits are relatively small (SELinux advocates may dispute this, but whatever, it is some magnitude). On the other hand, you would get the management problems of both AppArmor and SELinux; you would have to solve both the new file problem, and the file alias problem. Think about what you would have to do to accommodate (say) a text editor's file save procedure of "write new file.tmp, rename oldfile to oldfile.old, rename newfile.tmp to oldfile, delete oldfile.old".
So it could be done, but AFAICT, with small benefit and large cost. More security may be more secure, but it isn't necessarily better.
Crispin
Signature · 3 lines
Crispin Cowan, Ph.D. http://crispincowan.com/~crispin/ Director of Software Engineering http://novell.com Security: It's not linear
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSMhook
Hello.
Quoted 10 lines · Crispin Cowan
AppArmor actually does something similar to this, by mediating all of the ways that you can make an alias to a file. These are:
* Symbolic links: these actually don't work for making aliases with respect to LSM-based security systems such as AppArmor, SELinux, and TOMOYO Linux, because the symbolic link is resolved before the access request is presented to the LSM hooks. So if you create the symbolic link
/tmp/harmless->/etc/shadowand then the confined victim tries to access/tmp/harmless, what the LSM sees is an attempt to access/etc/shadow.
Yes, TOMOYO does so too.
Although TOMOYO is not using LSM, TOMOYO checks it by manually inserted hooks that are called after resolving the symbolic links.
Quoted 6 lines
* Hard links: AppArmor explicitly mediates permission to make a hard link to a file. To be able to make a hard link
/tmp/harmless->/etc/shadowyou have to have explicit permission to do so, or the attempt to make the link is blocked. This mediation is kept relatively simple by the fact that you can only hard link to files, and not to directories.
Yes, TOMOYO does so too.
Conventional UNIX's access control can't restrict which path_to_file can link with which another_path_to_file because UNIX's access control is a label-based access control. Therefore "ln /etc/shadow /tmp/harmless" becomes a problem when introducing pathname-based access control.
TOMOYO checks the combination of both link source and destination,
using "allow_link path_to_link_source path_to_link_destination" syntax. Therefore "ln /etc/shadow /tmp/harmless" is impossible as long as "allow_link /etc/shadow /tmp/harmless" (or something like "allow_link /etc/\* /tmp/\*") is not given. TOMOYO checks rename operation as well using
"allow_rename path_to_oldname path_to_newname" syntax.
Quoted 6 lines
* Multiple mounts: Linux lets you mount the same file system multiple times at multiple mount points, so the same file could appear at two different places. AppArmor has very coarse mediation here, and simply prohibits all calls to
mount()without special permission. So only highly privileged processes can create such an alias.
Yes, TOMOYO does so too.
But TOMOYO checks not only the capability to call mount() system call
but also the combination of "path_to_device" "path_to_mountpoint" and "filesystem_type" using "allow_mount path_to_device path_to_mountpoint filesystem_type" ("allow_mount --bind path_to_bind_source path_to_bind_destionation" for bind mounts) syntax.
And, this is why this very long long thread began.
One of the reasons that TOMOYO doesn't use LSM is that
it is impossible to obtain "struct vfsmount" parameter inside some of LSM hook functions.
From the label-based access control's point of view,
bind mount interferes nothing with label based access control
and passing "struct vfsmount" parameter looks redundant.
But, from the pathname-based access control's point of view,
bind mount interferes severely with pathname-based access control
because it is impossible to determine which pathname was requested. Although both pathnames point to the same object,
TOMOYO focuses on the PROCEDURE FOR REACHING AN OBJECT
and being able to know the procedure is very important.
(In contrast, SELinux focuses on the ATTRIBUTE OF THE REQUESTED OBJECT, doesn't it?)
Quoted 2 lines
So it could be done, but AFAICT, with small benefit and large cost. More security may be more secure, but it isn't necessarily better.
I don't think so.
I think the pathname-based access control and the label-based access control are complementarity relation. TOMOYO can coexist with SELinux and AppArmor.
What is sad, it becomes impossible to coexist with SELinux and AppArmor if TOMOYO uses LSM.
Thanks.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSMhook
Quoted 3 lines · --- Tetsuo Handa
Conventional UNIX's access control can't restrict which
path_to_filecan link with whichanother_path_to_filebecause UNIX's access control is a label-based access control.
UNIX access control is attribute based, not label based. The distinction may be hair splitting in the current context, but could be significant later if the thread continues.
Casey Schaufler
[email protected]
James Morris
maintainerRe: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSMhook
Quoted 7 lines · Casey Schaufler
Quoted 3 lines
Conventional UNIX's access control can't restrict which
path_to_filecan link with whichanother_path_to_filebecause UNIX's access control is a label-based access control.UNIX access control is attribute based, not label based. The distinction may be hair splitting in the current context, but could be significant later if the thread continues.
What's important is that traditional DAC stores the security attributes of the object with the object. Call them what you want, it matters not.
- James
Signature · 2 lines
James Morris <[email protected]>
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSMhook
In reply to Tetsuo HandaQuoted 3 lines · Tetsuo Handa
But, from the pathname-based access control's point of view, bind mount interferes severely with pathname-based access control because it is impossible to determine which pathname was requested.
Wrong. It is very well possible to determine the path of a particular dentry (+ vfsmount) with bind mounts.
Quoted 3 lines
Although both pathnames point to the same object, TOMOYO focuses on the PROCEDURE FOR REACHING AN OBJECT and being able to know the procedure is very important.
This doesn't make sense, either. With the following sequence of syscalls of processes A and B (both of them in the namespace root),
A: B:
mkdir("/tmp/a")
chdir("/tmp/a")
rename("/tmp/a", "/tmp/b")
creat("f")the path being checked for the creat call must be "/tmp/b/f", even though process A never explicitly used "b". If that's not what TOMOYO is doing, then that's badly broken.
Andreas
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSMhook
Hello.
Quoted 5 lines · Andreas Gruenbacher
Quoted 3 lines
But, from the pathname-based access control's point of view, bind mount interferes severely with pathname-based access control because it is impossible to determine which pathname was requested.
Wrong. It is very well possible to determine the path of a particular dentry (+ vfsmount) with bind mounts.
AppArmor can't determine which pathname (/tmp/public/file or /tmp/secret/file) was requested by touch command if bound mount is used in the following way
# mkdir /tmp/public /tmp/secret # mount -t tmpfs none /tmp/public # mount --bind /tmp/public /tmp/secret # touch /tmp/public/file
because security_inode_create() doesn't receive vfsmount, can it? It is possible to determine that the requested pathname is either /tmp/public/file or /tmp/secret/file by comparing address of vfsmount available from current->namespace, but it is impossible to determine which one.
Quoted 3 lines
the path being checked for the creat call must be "
/tmp/b/f", even though process A never explicitly used "b". If that's not what TOMOYO is doing, then that's badly broken.
Yes, of course, TOMOYO checks "/tmp/b/f". What I meant PROCEDURE FOR REACHING is "which directory does the process need to go through to reach the requested file if the process's current directory is the root of the process's namespace". And in this case, it is "/tmp/b/f".
Thanks.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSMhook
Quoted 10 lines · Tetsuo Handa
AppArmor can't determine which pathname (
/tmp/public/fileor/tmp/secret/file) was requested by touch command if bound mount is used in the following way# mkdir /tmp/public /tmp/secret # mount -t tmpfs none /tmp/public # mount --bind /tmp/public /tmp/secret # touch /tmp/public/filebecause
security_inode_create()doesn't receive vfsmount, can it?
I don't know what you are talking about -- the very first patch in the AppArmor series adds the vfsmount parameter to security_inode_create().
Andreas
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSMhook
Hello.
Quoted 2 lines · Andreas Gruenbacher
I don't know what you are talking about -- the very first patch in the AppArmor series adds the vfsmount parameter to
security_inode_create().
I'm talking about "the current version" of AppArmor (I mean AppArmor available for OpenSuSE 10.1/10.2, but I should call "the previous version" of AppArmor
if you call AppArmor in this series of patch "the current version") can't handle bind mounts. I know "the next version" of AppArmor (I mean AppArmor in this series of patch) will be able to handle bind mounts better if these vfsmount patches are adopted.
Thanks.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Crispin CowanHi!
Quoted 8 lines
Quoted 4 lines
If we want "
/etc/shadow" to be the only way to access the shadow file we could label the data with "/etc/shadow". Any attempts to access this data using a renamed file or link would be denied (attempts to link or rename could also be denied).Eloquently put.
AppArmor actually does something similar to this, by mediating all of the ways that you can make an alias to a file. These are:
...
Quoted 1 line
* Hard links: AppArmor explicitly mediates permission to make a hard
Unfortunately, aparmor is by design limited to subset of distro (network daemons). Unfortunately, some other programs (passwd, vi) routinely make hardlinks. So AA mediating hardlink is not enough, as vi will happily hardlink /etc/shadow into /etc/.vi-shadow-1234.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 10 lines · Pavel Machek
Hi!
Quoted 8 lines
Quoted 4 lines
If we want "
/etc/shadow" to be the only way to access the shadow file we could label the data with "/etc/shadow". Any attempts to access this data using a renamed file or link would be denied (attempts to link or rename could also be denied).Eloquently put.
AppArmor actually does something similar to this, by mediating all of the ways that you can make an alias to a file. These are:
Quoted 7 lines
...
Quoted 1 line
* Hard links: AppArmor explicitly mediates permission to make a hard
Unfortunately, aparmor is by design limited to subset of distro (network daemons). Unfortunately, some other programs (passwd, vi) routinely make hardlinks. So AA mediating hardlink is not enough, as vi will happily hardlink
/etc/shadowinto/etc/.vi-shadow-1234.
but with the AA design of default deny this isn't a big problem unless you specificly allow some network daemon to access /etc/.vi-shadow-1234
in this context passwd and vi are considered trusted processes, they are used _after_ you authenticate onto the box.
no, this won't help you much against local users, but there are a _lot_ of boxes out there with few, if any, local users who don't also have the root password. AA helps the admin be safer when configuring netwrok daemons.
David Lang
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Hi!
Quoted 11 lines
Quoted 11 lines
Quoted 8 lines
Quoted 4 lines
If we want "
/etc/shadow" to be the only way to access the shadow file we could label the data with "/etc/shadow". Any attempts to access this data using a renamed file or link would be denied (attempts to link or rename could also be denied).Eloquently put.
AppArmor actually does something similar to this, by mediating all of the ways that you can make an alias to a file. These are:
...
Quoted 1 line
* Hard links: AppArmor explicitly mediates permission to make a hard
Quoted 7 lines
Quoted 4 lines
Unfortunately, aparmor is by design limited to subset of distro (network daemons). Unfortunately, some other programs (passwd, vi) routinely make hardlinks. So AA mediating hardlink is not enough, as vi will happily hardlink
/etc/shadowinto/etc/.vi-shadow-1234.but with the AA design of default deny this isn't a big problem unless you specificly allow some network daemon to access
/etc/.vi-shadow-1234
...or unless vi decides to hardlink into /tmp or something.
Quoted 3 lines
no, this won't help you much against local users, but there are a _lot_ of boxes out there with few, if any, local users who don't also have the root password. AA helps the admin be safer when configuring netwrok daemons.
Hmm, I guess I'd love "it is useless on multiuser boxes" to become standard part of AA advertising.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 1 line · [email protected] wrote:
no, this won't help you much against local users, [...]
Quoted 2 lines · Pavel Machek
Hmm, I guess I'd love "it is useless on multiuser boxes" to become standard part of AA advertising.
That's not quite what david@ said. As I understand it, AppArmor is not focused on preventing attacks by local users against other local users; that's not the main problem it is trying to solve. Rather, it's primary purpose is to deal with attacks by remote bad guys against your network servers. That is a laudable goal. Anything that helps reduce the impact of remote exploits is bound to be useful, even if doesn't do a darn thing to stop local users from attacking each other.
This means that AppArmor could still be useful on multiuser boxes, even if that utility is limited to defending (some) network daemons against remote attack (or, more precisely, reducing the damage done by a successful remote attack against a network daemon).
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Hi!
(Please do not drop me from cc list when replying).
Quoted 11 lines
Quoted 1 line
no, this won't help you much against local users, [...]
Quoted 2 lines · Pavel Machek
Hmm, I guess I'd love "it is useless on multiuser boxes" to become standard part of AA advertising.
That's not quite what david@ said. As I understand it, AppArmor is not focused on preventing attacks by local users against other local users; that's not the main problem it is trying to solve. Rather, it's primary purpose is to deal with attacks by remote bad guys against your network servers. That is a laudable goal.
It is also not going to prevent local users attacking network servers. Local users can ln arbitrary files, and then play tricks with network servers.
Quoted 4 lines
This means that AppArmor could still be useful on multiuser boxes, even if that utility is limited to defending (some) network daemons against remote attack (or, more precisely, reducing the damage done by a successful remote attack against a network daemon).
Yes, if there's significantly more remote bad guys than local bad guys, and if remote bad guys can't just get some local user first, AA still has some value.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 3 lines · Pavel Machek
Yes, if there's significantly more remote bad guys than local bad guys, and if remote bad guys can't just get some local user first, AA still has some value.
Experience over on the Windows side of the fence indicates that "remote bad guys get some local user first" is a *MAJOR* part of the current real-world threat model - the vast majority of successful attacks on end-user boxes these days start off with either "Get user to (click on link|open attachment)" or "Subvert the path to a website (either by hacking the real site or hijacking the DNS) and deliver a drive-by fruiting when the user visits the page".
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 11 lines · On Fri, 1 Jun 2007, [email protected] wrote:
Quoted 3 lines · Pavel Machek
Yes, if there's significantly more remote bad guys than local bad guys, and if remote bad guys can't just get some local user first, AA still has some value.
Experience over on the Windows side of the fence indicates that "remote bad guys get some local user first" is a *MAJOR* part of the current real-world threat model - the vast majority of successful attacks on end-user boxes these days start off with either "Get user to (click on link|open attachment)" or "Subvert the path to a website (either by hacking the real site or hijacking the DNS) and deliver a drive-by fruiting when the user visits the page".
and if your local non-root user can create a hard link to /etc/shadow and access it they own your box anyway (they can just set the root password to anything they want). since I don't hear about this happening there are other restrictions that prevent this anyway.
everyone recognises that AA has limits, but the way people are emphisising these acknowledged limits is beginning to sound a bit shrill.
David Lang
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 16 lines · On Fri 2007-06-01 11:00:50, [email protected] wrote:
Quoted 5 lines · On Fri, 1 Jun 2007, [email protected] wrote:
Quoted 3 lines · Pavel Machek
Yes, if there's significantly more remote bad guys than local bad guys, and if remote bad guys can't just get some local user first, AA still has some value.
Quoted 8 lines
Experience over on the Windows side of the fence indicates that "remote bad guys get some local user first" is a *MAJOR* part of the current real-world threat model - the vast majority of successful attacks on end-user boxes these
days start off with either "Get user to (click on link|open attachment)" or "Subvert the path to a website (either by hacking the real site or hijacking
the DNS) and deliver a drive-by fruiting when the user visits the page".
Quoted 3 lines
and if your local non-root user can create a hard link to
/etc/shadowand access it they own your box anyway (they can just set the root password to anything they want).
I think you need to look how unix security works:
pavel@amd:/tmp$ ln /etc/shadow . pavel@amd:/tmp$ cat shadow cat: shadow: Permission denied pavel@amd:/tmp$
Yes, regular users are permitted to hardlink shadow, no, it is not a security hole, yes, it is a problem for AA.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to [email protected]Quoted 6 lines · [email protected] writes:
Experience over on the Windows side of the fence indicates that "remote bad guys get some local user first" is a *MAJOR* part of the current real-world threat model - the vast majority of successful attacks on end-user boxes these days start off with either "Get user to (click on link|open attachment)" or "Subvert the path to a website (either by hacking the real site or hijacking the DNS) and deliver a drive-by fruiting when the user visits the page".
AppArmor isn't trying to defend everyday users from getting phished or social engineered; it is trying to protect servers from getting rooted because of security holes in their network daemons. I find that a laudable goal. Sure, it doesn't solve every security problem in the world, but so what? A tool that could solve that one security problem would still be a useful thing, even if it did nothing else.
I don't find the Windows stuff too relevant here. As I understand it, AppArmor isn't aimed at defending Windows desktop users; it is aimed at defending Linux servers. A pretty different environment, I'd say.
Ultimately, there are some things AppArmor may be good at, and there are also sure to be some things it is bloody useless for. My hammer isn't very good for screwing in screws, but I still find it useful. I confess I don't understand the kvetching about AppArmor's goals. What are you expecting, some kind of silver bullet?
A question I'd find more interesting is whether AppArmor is able to meet its stated goals, under a reasonable threat model, and with what degree of assurance, and at what cost. But I don't know whether that's relevant for the linux-kernel mailing list.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 1 line · David Wagner
I don't find the Windows stuff too relevant here.
I'm surprised. The only Windows-specific thing in the whole paragraph is that the attack described is currently wildly successful. And there *have* been known exploitable bugs in the Linux version of Firefox. In other words, all the pieces are in place for exactly the same thing to work on Linux.
The type of hardening that AppArmor can provide network-facing daemons is only protecting the system against attacks that aren't even a large part of the
threat model. Exploiting a broken PHP script? Happens all the time, and
AppArmor can't do much for it. SQL injection? Happens all the time, and it can't help much there either. Systems getting pwned because the sysadmin's laptop got hacked? Pretty common, and another thing that AppArmor won't slow down. But yes, I *will* grant that the next time there's a buffer overflow in Apache, AppArmor will be able to help *that*....
Quoted 3 lines
As I understand it,
AppArmor isn't aimed at defending Windows desktop users; it is aimed at defending Linux servers. A pretty different environment, I'd say.
The only reason you're not seeing the same exact threat model against Linux servers is because it's still a minority. It's *always* been true that one of the most productive attacks on a server has been to find a desktop that you can attack, and then abuse a trust relationship from the desktop to the server (and has been, ever since the server was a IBM mainframe and the desktop was an RJE station. Amazing how trusting OS/360 was of a card deck tossed into a remote card reader... :)
Quoted 3 lines
Ultimately, there are some things AppArmor may be good at, and there are also sure to be some things it is bloody useless for. My hammer isn't very good for screwing in screws, but I still find it useful.
The question is whether it's a hammer, a screwdriver, or that coping saw that you never seem to find a use for...
Quoted 2 lines
I confess I don't understand the kvetching about AppArmor's goals. What are you expecting, some kind of silver bullet?
Exactly the opposite - I'm worried that it will be treated as a silver bullet.
And historically, we've had an amazing amount of pushback againt things that are intrusive and only provide a partial solution.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 8 lines · On Sat, 2 Jun 2007, [email protected] wrote:
Quoted 1 line · David Wagner
I don't find the Windows stuff too relevant here.
I'm surprised. The only Windows-specific thing in the whole paragraph is that the attack described is currently wildly successful. And there *have* been known exploitable bugs in the Linux version of Firefox. In other words, all the pieces are in place for exactly the same thing to work on Linux.
Quoted 4 lines
The type of hardening that AppArmor can provide network-facing daemons is only protecting the system against attacks that aren't even a large part of the
threat model. Exploiting a broken PHP script? Happens all the time, and
AppArmor can't do much for it.
actually, this is _exactly_ where AppArmor is the most useful. if the PHP script is restricted by AppArmor it won't be able to go out and touch things that it's not supposed to.
Quoted 10 lines
SQL injection? Happens all the time, and it
can't help much there either. Systems getting pwned because the sysadmin's laptop got hacked? Pretty common, and another thing that AppArmor won't slow down. But yes, I *will* grant that the next time there's a buffer overflow in Apache, AppArmor will be able to help *that*....Quoted 3 lines
As I understand it,
AppArmor isn't aimed at defending Windows desktop users; it is aimed at defending Linux servers. A pretty different environment, I'd say.
Quoted 7 lines
The only reason you're not seeing the same exact threat model against Linux servers is because it's still a minority. It's *always* been true that one of the most productive attacks on a server has been to find a desktop that you can attack, and then abuse a trust relationship from the desktop to the server (and has been, ever since the server was a IBM mainframe and the desktop was an RJE station. Amazing how trusting OS/360 was of a card deck tossed into a remote card reader... :)
if you are targeting one specific company or one specific server then you are correct, however most attacks are not that targeted, they do things like useing google to find random servers that are running vunerable software and attack that (or just try the attack against random IP addresses in case it happens to be running the vunerable software)
David Lang
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 8 lines · On Sat, 02 Jun 2007 07:27:13 PDT, [email protected] said:
Quoted 4 lines
The type of hardening that AppArmor can provide network-facing daemons is only protecting the system against attacks that aren't even a large part of the
threat model. Exploiting a broken PHP script? Happens all the time, and
AppArmor can't do much for it.actually, this is _exactly_ where AppArmor is the most useful. if the PHP script is restricted by AppArmor it won't be able to go out and touch things that it's not supposed to.
OK. I'll bite. AppArmor basically only mediates filename objects.
What filename do you specify to stop it when the exploited PHP script is used bu a spammer to send mail to millions, when it was intended to send mail only to a specific set of people? Wait, that's a tcp connection to localhost:25.
What filename do you specifu to stop blog comment spam and other abuses of a content management system (remember that the PHP code *does* need write access to the files in question)?
It might be able to stop J Random SkriptKiddy from scribbling "Y0uz Ben Pwned" all over your home page, but it doesn't do much to control lots of other abuses of web apps. To be fair, SELinux can't help a lot more, because the problem often ends up being abuse of an access privilege that the program *should* have - for example, if it's supposed to query the database, it's hard to stop it from making an inappropriate query at the level that AppArmor and SELinux work at.
I'm not convinced that it's solving enough *actual* problems, given that we've rejected a lot of other "helps a little in some cases" code for kernel inclusion.
Quoted 2 lines
if you are targeting one specific company or one specific server then you are correct,
There's a lot of that going around. And they're the attacks that you need to worry about, because you're likely to end up as a headline.
Quoted 1 line
however most attacks are not that targeted,
There's a big difference between "most attacks" and "most attacks you should worry about".
Quoted 3 lines
they do things
like useing google to find random servers that are running vunerable software and attack that
Rmember that at a minimum, that also means that you're Goggleable as vulnerable to attacks that AppArmor can't stop. And yes, Googling for vulnerable software *is* one of the primary ways that blog spammers find the vulerable blogs.
If your site is run in such a way that you you have to worry about random attackers who use google, your site has *bigger* security issues, and thinking that AppArmor is going to improve things is exactly the sort of smoke screen magic bullet that we don't want putting in the kernel.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 7 lines · On Sat, 2 Jun 2007, [email protected] wrote:
Quoted 5 lines · On Sat, 02 Jun 2007 07:27:13 PDT, [email protected] said:
Quoted 4 lines
The type of hardening that AppArmor can provide network-facing daemons is only protecting the system against attacks that aren't even a large part of the
threat model. Exploiting a broken PHP script? Happens all the time, and
AppArmor can't do much for it.
Quoted 5 lines
Quoted 3 lines
actually, this is _exactly_ where AppArmor is the most useful. if the PHP script is restricted by AppArmor it won't be able to go out and touch things that it's not supposed to.
OK. I'll bite. AppArmor basically only mediates filename objects.
for now, the AppArmor folks have said that the next step is to move on to other objects (like network connections) after the initial step is accepted.
Quoted 7 lines
What filename do you specify to stop it when the exploited PHP script is used bu a spammer to send mail to millions, when it was intended to send mail only to a specific set of people? Wait, that's a tcp connection to localhost:25.
What filename do you specifu to stop blog comment spam and other abuses of a content management system (remember that the PHP code *does* need write access to the files in question)?
it stops the PHP script from spawning a shell that would be considered a 'local trusted user'
if you use SELinux and give the PHP script permission to write to your content management system then the PHP script can corrupt that system if it's exploited. AppArmor is the same way
this is Security 101 (or even more basic), if you grant a program access to do something you can't prevent that program from doing that something.
Quoted 6 lines
It might be able to stop J Random SkriptKiddy from scribbling "Y0uz Ben Pwned" all over your home page, but it doesn't do much to control lots of other abuses of web apps. To be fair, SELinux can't help a lot more, because the problem often ends up being abuse of an access privilege that the program *should* have - for example, if it's supposed to query the database, it's hard to stop it from making an inappropriate query at the level that AppArmor and SELinux work at.
it's not hard, it's impossible
how can you know if the command 'foo' sent to another program on your system will cause that system to erase everything it has access to or return 'bar'? you don't and no system tool can (and no system tool should)
if you want to control what commands one program can send to another you need a security proxy between them. that isn't what AppArmor or SELinux are trying to do so don't blame them for not doing it.
Quoted 3 lines
I'm not convinced that it's solving enough *actual* problems, given that we've rejected a lot of other "helps a little in some cases" code for kernel inclusion.
you seem to want a 'only let the system do what the programmer intended' command, and anything less isn't solving the actual problem. by that logic we shouldn't bother with passwords either, since they don't completely solve the problem
Quoted 10 lines
Quoted 2 lines
if you are targeting one specific company or one specific server then you are correct,
There's a lot of that going around. And they're the attacks that you need to worry about, because you're likely to end up as a headline.
Quoted 1 line
however most attacks are not that targeted,
There's a big difference between "most attacks" and "most attacks you should worry about".
you first want to knock off all the things in that 'most attacks' catagory so that you can pay close attention to the threats that are targeted directly at you.
Quoted 4 lines
Quoted 3 lines
they do things
like useing google to find random servers that are running vunerable software and attack that
Quoted 8 lines
Rmember that at a minimum, that also means that you're Goggleable as vulnerable to attacks that AppArmor can't stop. And yes, Googling for vulnerable software *is* one of the primary ways that blog spammers find the vulerable blogs.
If your site is run in such a way that you you have to worry about random attackers who use google, your site has *bigger* security issues, and thinking that AppArmor is going to improve things is exactly the sort of smoke screen magic bullet that we don't want putting in the kernel.
when the next exploit is found is network accessable server X, AppArmor can prevent it from doing anything to the system that server X wasn't already allowed to touch.
For example, this means that it probably won't be allowed to run bash and provide a shell to the outside attacker.
It also means that locally exploitable bugs are less likely to be able to be combined with remotely exploitable bugs into complete machine takeovers becouse the remotely exploitable server would have to have permission to access the locally exploitable software.
if you can't see any advantage to this then you would be happy doing a chmod -R 777 / on your system (becouse normal user permissions are a much more restricted way of limiting the same type of access)
David Lang
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 2 lines · On Sat, 02 Jun 2007 12:51:27 PDT, [email protected] said:
this is Security 101 (or even more basic), if you grant a program access to do something you can't prevent that program from doing that something.
And Security 102 is "most of the *real* trouble starts when authorized programs access authorized things in unintended ways".
Quoted 3 lines
if you want to control what commands one program can send to another you need a security proxy between them. that isn't what AppArmor or SELinux are trying to do so don't blame them for not doing it.
I'm not blaming them. I'm blaming the people who are trying to sell AppArmor as doing much more than a small bandaid on the totality of the security issue.
There's *lots* of attacks. AppArmor only addresses a very small segment of them, and it's quite arguably not even addressing the ones you should worry about.
Quoted 8 lines
Quoted 3 lines
I'm not convinced that it's solving enough *actual* problems, given that we've rejected a lot of other "helps a little in some cases" code for kernel inclusion.
you seem to want a 'only let the system do what the programmer intended' command, and anything less isn't solving the actual problem. by that logic we shouldn't bother with passwords either, since they don't completely solve the problem
No, I'm saying that we've had a *lot* of partial solutions, from Grsecurity to signed-modules, that didn't go in to the tree because they were too intrusive for the partial security they provided.
Quoted 3 lines
you first want to knock off all the things in that 'most attacks' catagory so that you can pay close attention to the threats that are targeted directly at you.
Quite frankly, if you've configured your system properly, and kept things patched, and all the other things you should be doing *anyhow*, the vast majority of "most attacks" really shouldn't be much more than "The daily report showed another 2,395 probes for that year-old exploit".
Remember - if you're getting hit by a "background noise" random attempt, it is almost certainly *not* a 0-day, because any attacker who's clued enough to *have* a 0-day has enough sense to save it for when he has a targeted attack planned. So there's 2 possibilities:
1) It's a random attack, and it's a *known* attack and other things (patches, IPS, and so on) should already be mitigating the attack.
2) You're a specific target, and the fact that AppArmor stopped the attack just means that a different attack will be made.
Quoted 2 lines
For example, this means that it probably won't be allowed to run bash and provide a shell to the outside attacker.
No, but I can mmap an area, make it executable, and download a block of executable code and branch to it, giving me a shell anyhow.
Quoted 4 lines
It also means that locally exploitable bugs are less likely to be able to be combined with remotely exploitable bugs into complete machine takeovers becouse the remotely exploitable server would have to have permission to access the locally exploitable software.
Right. Instead, the locally exploitable bug is combined with an exploitable browser bug instead. :)
Quoted 1 line
if you can't see any advantage to this then you would be happy doing a
Oddly enough, I don't see an advantage to only closing off half the avenues of attack. If you go digging through the SELinux and LSM archives, you'll probably find me arguing against the SELinux 'targeted' policy, for many of the same reasons. And the 'targeted' policy does a better containment of things than the AppArmor model.
Quoted 2 lines
chmod -R 777 / on your system (becouse normal user permissions are a much more restricted way of limiting the same type of access)
Bad analogy. You meant "chmod -R 777 /var /etc because the file permissions on the /lib and /usr trees are enough to stop most random attacks".
Yes, normal access permissions, being DAC, *are* a very limited way of applying MAC. And if you go back and read the LSM archives, the decision was made to apply permissions/ACLS DAC first, and then apply LSM MAC, mostly for performance and least-surprise reasons. There's absolutely no big theoretical reason why you couldn't chmod 777 everything, if you loaded an LSM that implemented the necessary MAC.
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to David WagnerHi!
(Please preserve cc lists when replying on l-k).
Quoted 7 lines
Quoted 6 lines
Experience over on the Windows side of the fence indicates that "remote bad guys get some local user first" is a *MAJOR* part of the current real-world threat model - the vast majority of successful attacks on end-user boxes these days start off with either "Get user to (click on link|open attachment)" or "Subvert the path to a website (either by hacking the real site or hijacking the DNS) and deliver a drive-by fruiting when the user visits the page".
Quoted 5 lines
AppArmor isn't trying to defend everyday users from getting phished or social engineered; it is trying to protect servers from getting rooted because of security holes in their network daemons. I find that a laudable goal. Sure, it doesn't solve every security problem in the world, but so what? A tool that could solve that one security problem
AA solves less problems than SELinux does. Some people like AA more, but I guess they should just learn SELinux.
And yes, I'm afraid this discussion is relevant on l-k, because we should have very good reasons before merging duplicate functionality.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
Quoted 1 line · --- Pavel Machek
AA solves less problems than SELinux does.
And vi solves less problems than OpenOffice.
vi is good for a different set of purposes than OpenOffice. AA and SELinux both aspire to being Security Solutions, but that does not make either a subset of the other.
Quoted 2 lines
Some people like AA more,
but I guess they should just learn SELinux.
Knowing the people involved I would suggest that the AA people did learn SELinux, and came to their own conclusions regarding it's applicability to their needs, and that those conclusions are not the same as yours.
Quoted 2 lines
And yes, I'm afraid this discussion is relevant on l-k, because we should have very good reasons before merging duplicate functionality.
'cmon, you know better than to claim that this is duplicate functionality. No one is arguing that. The arguments have been that the conceptual basis of named based access control are flawwed. As that argument has failed to move the AA adherants, the old sawhorse that SELinux does everything, or could be made to if you sweated the policy hard enough, got pulled out. No evidence to that effect, mind you, but the old "waves paw" nonetheless.
SELinux is the finest implementation of Type Enforcement on the planet. TE does not match everyone's definition of security. AA is an alternative that clearly has as tough a roe to hoe as SELinux did in 2001, when it was up against MLS system vendors who compared it to Froot Loops. Alternatives, even those that you don't personally care for, are good for you.
Casey Schaufler
[email protected]
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to Pavel MachekQuoted 4 lines · Pavel Machek
Quoted 2 lines
* Hard links: AppArmor explicitly mediates permission to make a hard
Unfortunately, aparmor is by design limited to subset of distro (network daemons).
That is not true. AppArmor is designed to confine any application you do not want to trust completely. This includes network daemons (Apache, Sendmail, BIND, etc.) network clients (Firefox, Thunderbird, GAIM/Pidgin, etc.) and any other application that mediates trust: where data on one side of the application is higher privilege than potential attackers on the other side of the application.
Quoted 4 lines
Unfortunately, some other programs (passwd, vi)
routinely make hardlinks. So AA mediating hardlink is not enough, as vi will happily hardlink/etc/shadowinto/etc/.vi-shadow-1234.
I have no idea what you are talking about. I routinely profile vim on stage in AppArmor presentations, and it presents no such problems.
Caveat: the circumstances under which you would actually want to profile vi are rather limited, most users would not want to do that.
Quoted 2 lines · In another post Pavel Machek
Hmm, I guess I'd love "it is useless on multiuser boxes" to become standard part of AA advertising.
That is usually around slide 7 of the standard AppArmor presentation, right next to the remarks about how mulituser boxes are nearly extinct dinosaurs. AppArmor gets some of its simplicity and ease of use by not considering that vanishing use case. Even so, AppArmor does have uses on multiuser boxes, just not the uses Pavel wishes for. Fine, use a different tool for a different task, AppArmor has plenty of use cases.
The limitation Pavel is referring to is that AppArmor does not secure processes that are not profiled by AppArmor. We know, this is intentional, and contributes to AppArmor's ease of use, and does not generate a hole if you consider every process exposed to (say) network attack and confine it.
Crispin
Signature · 3 lines
Crispin Cowan, Ph.D. http://crispincowan.com/~crispin/ Director of Software Engineering http://novell.com Security: It's not linear
Re: [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook
In reply to James MorrisHi,
2007/5/24, James Morris <[email protected]>:
Quoted 9 lines
I can restate my question and ask why you'd want a security policy like:
Subject 'sysadmin' has:
read access to/etc/shadow
read/write access to /views/sysadmin/etc/shadowwhere the objects referenced by the paths are identical and visible to the subject along both paths, in keeping with your description of "policy may allow access to some locations but not to others" ?
If I understand correctly, the original issue was whether to allow passing vfsmount to the inode_create LSM hook or not. Which is independent from AA or "pathname based MAC", I think.
It is proven that Linux can be used without that change, however it is also clear that current LSM cause the ambiguities as AA people has explained. Clearing ambiguities is a obvious gain to Linux and will make benefits for auditing besides "pathname based MAC".
So here's my opinion. If anybody can't explain clear reason (or needs) to keep these ambiguities unsolved, we should consider to merge the proposal.
Thanks.
Signature · 2 lines
Toshiharu Harada [email protected]
PATCH 2/41 Remove redundant check from proc_setattr()
In reply to [email protected]notify_change() already calls security_inode_setattr() before calling iop->setattr.
--- fs/proc/base.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
fs/proc/base.c +2 -5
@@ -344,11 +344,8 @@ static int proc_setattr(struct dentry *dreturn -EPERM ; error =inode_change_ok (inode, attr); -if (!error) { - error =security_inode_setattr (dentry, attr); -if (!error) - error =inode_setattr (inode, attr); - } +if (!error) + error =inode_setattr (inode, attr);return error; }
PATCH 3/41 Remove redundant check from proc_sys_setattr()
In reply to [email protected]notify_change() already calls security_inode_setattr() before calling iop->setattr.
--- fs/proc/proc_sysctl.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
fs/proc/proc_sysctl.c +2 -5
@@ -429,11 +429,8 @@ static int proc_sys_setattr(struct dentrreturn -EPERM ; error =inode_change_ok (inode, attr); -if (!error) { - error =security_inode_setattr (dentry, attr); -if (!error) - error =inode_setattr (inode, attr); - } +if (!error) + error =inode_setattr (inode, attr);return error; }
Re: [AppArmor 03/41] Remove redundant check from proc_sys_setattr()
Quoted 2 lines · On Thu, 12 Apr 2007 02:08:12 -0700 [email protected] wrote:
notify_change()already callssecurity_inode_setattr()before callingiop->setattr.
This is a behaviour change on all of these and limits some behaviour of existing established security modules
When inode_change_ok is called it has side effects. This includes clearing the SGID bit on attribute changes caused by chmod. If you make this change the results of some rulesets may be different before or after the change is made.
I'm not saying the change is wrong but it does change behaviour so that needs looking at closely (ditto all other attribute twiddles)
Alan
PATCH 4/41 Pass struct file down to remove_suid and children
In reply to [email protected]Pass struct path to remove_suid and should_remove_suid instead of only the dentry. Required by a later patch that adds a struct vfsmount parameter to notify_change().
--- fs/ntfs/file.c | 2 +- fs/ocfs2/file.c | 10 +++++----- fs/reiserfs/file.c | 2 +- fs/splice.c | 6 +++--- fs/xfs/linux-2.6/xfs_lrw.c | 2 +- include/linux/fs.h | 6 +++--- mm/filemap.c | 16 ++++++++-------- mm/filemap_xip.c | 2 +- mm/shmem.c | 2 +- 9 files changed, 24 insertions(+), 24 deletions(-)
fs/ntfs/file.c +1 -1
@@ -2162,7 +2162,7 @@ static ssize_t ntfs_file_aio_write_noloc goto out;if (!count) goto out; - err =remove_suid (file->f_path.dentry); + err =remove_suid (&file->f_path);if (err) goto out;file_update_time (file);
fs/ocfs2/file.c +5 -5
@@ -1035,13 +1035,13 @@ out:return ret; } -static int ocfs2_prepare_inode_for_write (struct dentry *dentry, +static int ocfs2_prepare_inode_for_write (struct path *path,loff_t *ppos,size_t count,int appending) {int ret =0 , meta_level = appending; -struct inode *inode = dentry->d_inode; +struct inode *inode = path->dentry->d_inode;u32 clusters;loff_t newsize, saved_pos;
@@ -1067,7 +1067,7 @@ static int ocfs2_prepare_inode_for_write* inode. There's also the dinode i_size state which * can be lost via setattr during extending writes (we * set inode->i_size at the end of a write. */ -if (should_remove_suid (dentry)) { +if (should_remove_suid (path)) {if (meta_level ==0 ) {ocfs2_meta_unlock (inode, meta_level); meta_level =1 ;
@@ -1176,7 +1176,7 @@ static ssize_t ocfs2_file_aio_write(stru goto out; } - ret =ocfs2_prepare_inode_for_write (filp->f_path.dentry, &iocb->ki_pos, + ret =ocfs2_prepare_inode_for_write (&filp->f_path, &iocb->ki_pos, iocb->ki_left, appending);if (ret <0 ) {mlog_errno (ret);
@@ -1239,7 +1239,7 @@ static ssize_t ocfs2_file_splice_write(s goto out; } - ret =ocfs2_prepare_inode_for_write (out->f_path.dentry, ppos, len,0 ); + ret =ocfs2_prepare_inode_for_write (&out->f_path, ppos, len,0 );if (ret <0 ) {mlog_errno (ret); goto out_unlock;
fs/reiserfs/file.c +1 -1
@@ -1353,7 +1353,7 @@ static ssize_t reiserfs_file_write(strucif (count ==0 ) goto out; - res =remove_suid (file->f_path.dentry); + res =remove_suid (&file->f_path);if (res) goto out;
fs/splice.c +3 -3
@@ -797,7 +797,7 @@ generic_file_splice_write_nolock(structssize_t ret;int err; - err =remove_suid (out->f_path.dentry); + err =remove_suid (&out->f_path);if (unlikely (err))return err;
@@ -843,10 +843,10 @@ generic_file_splice_write(struct pipe_inssize_t ret;int err; - err =should_remove_suid (out->f_path.dentry); + err =should_remove_suid (&out->f_path);if (unlikely (err)) {mutex_lock (&inode->i_mutex); - err =__remove_suid (out->f_path.dentry, err); + err =__remove_suid (&out->f_path, err);mutex_unlock (&inode->i_mutex);if (err)return err;
fs/xfs/linux-2.6/xfs_lrw.c +1 -1
@@ -799,7 +799,7 @@ start: !capable(CAP_FSETID)) { error =xfs_write_clear_setuid (xip);if (likely (!error)) - error = -remove_suid (file->f_path.dentry); + error = -remove_suid (&file->f_path);if (unlikely (error)) {xfs_iunlock (xip, iolock); goto out_unlock_mutex;
include/linux/fs.h +3 -3
@@ -1685,9 +1685,9 @@ extern void __iget(struct inode * inode)extern void clear_inode (struct inode *);extern void destroy_inode (struct inode *);extern struct inode *new_inode (struct super_block *); -extern int __remove_suid (struct dentry *,int ); -extern int should_remove_suid (struct dentry *); -extern int remove_suid (struct dentry *); +extern int __remove_suid (struct path *,int ); +extern int should_remove_suid (struct path *); +extern int remove_suid (struct path *);extern void __insert_inode_hash (struct inode *,unsigned long hashval);extern void remove_inode_hash (struct inode *);
mm/filemap.c +8 -8
@@ -1849,9 +1849,9 @@ repeat:* if suid or (sgid and xgrp) * remove privs */ -int should_remove_suid (struct dentry *dentry) +int should_remove_suid (struct path *path) { -mode_t mode = dentry->d_inode->i_mode; +mode_t mode = path->dentry->d_inode->i_mode;int kill =0 ;/* suid always must be killed */
@@ -1872,20 +1872,20 @@ int should_remove_suid(struct dentry *de }EXPORT_SYMBOL (should_remove_suid); -int __remove_suid (struct dentry *dentry,int kill) +int __remove_suid (struct path *path,int kill) {struct iattr newattrs; newattrs.ia_valid =ATTR_FORCE | kill; -return notify_change (dentry, &newattrs); +return notify_change (path->dentry, &newattrs); } -int remove_suid (struct dentry *dentry) +int remove_suid (struct path *path) { -int kill =should_remove_suid (dentry); +int kill =should_remove_suid (path);if (unlikely (kill)) -return __remove_suid (dentry, kill); +return __remove_suid (path, kill);return 0 ; }
@@ -2252,7 +2252,7 @@ __generic_file_aio_write_nolock(struct kif (count ==0 ) goto out; - err =remove_suid (file->f_path.dentry); + err =remove_suid (&file->f_path);if (err) goto out;
mm/filemap_xip.c +1 -1
@@ -405,7 +405,7 @@ xip_file_write(struct file *filp, constif (count ==0 ) goto out_backing; - ret =remove_suid (filp->f_path.dentry); + ret =remove_suid (&filp->f_path);if (ret) goto out_backing;
mm/shmem.c +1 -1
@@ -1516,7 +1516,7 @@ shmem_file_write(struct file *file, consif (err || !count) goto out; - err =remove_suid (file->f_path.dentry); + err =remove_suid (&file->f_path);if (err) goto out;
PATCH 5/41 Add a vfsmount parameter to notify_change()
In reply to [email protected]The vfsmount parameter must be set appropriately for files visibile outside the kernel. Files that are only used in a filesystem (e.g., reiserfs xattr files) will have a NULL vfsmount.
--- fs/attr.c | 3 ++- fs/ecryptfs/inode.c | 4 +++- fs/exec.c | 3 ++- fs/fat/file.c | 2 +- fs/hpfs/namei.c | 2 +- fs/namei.c | 3 ++- fs/nfsd/vfs.c | 8 ++++---- fs/open.c | 28 +++++++++++++++------------- fs/reiserfs/xattr.c | 6 +++--- fs/sysfs/file.c | 2 +- fs/utimes.c | 4 ++-- include/linux/fs.h | 6 +++--- mm/filemap.c | 2 +- mm/tiny-shmem.c | 2 +- 14 files changed, 41 insertions(+), 34 deletions(-)
fs/attr.c +2 -1
@@ -101,7 +101,8 @@ int inode_setattr(struct inode * inode, }EXPORT_SYMBOL (inode_setattr); -int notify_change (struct dentry * dentry,struct iattr * attr) +int notify_change (struct dentry *dentry,struct vfsmount *mnt, +struct iattr *attr) {struct inode *inode = dentry->d_inode;mode_t mode;
fs/ecryptfs/inode.c +3 -1
@@ -870,12 +870,14 @@ static int ecryptfs_setattr(struct dentr {int rc =0 ;struct dentry *lower_dentry; +struct vfsmount *lower_mnt;struct inode *inode;struct inode *lower_inode;struct ecryptfs_crypt_stat *crypt_stat; crypt_stat = &ecryptfs_inode_to_private (dentry->d_inode)->crypt_stat; lower_dentry =ecryptfs_dentry_to_lower (dentry); + lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry); inode = dentry->d_inode; lower_inode =ecryptfs_inode_to_lower (inode);if (ia->ia_valid &ATTR_SIZE ) {
@@ -890,7 +892,7 @@ static int ecryptfs_setattr(struct dentrif (rc <0 ) goto out; } - rc =notify_change (lower_dentry, ia); + rc =notify_change (lower_dentry, lower_mnt, ia); out:fsstack_copy_attr_all (inode, lower_inode,NULL );return rc;
fs/exec.c +2 -1
@@ -1532,7 +1532,8 @@ int do_coredump(long signr, int exit_cod goto close_fail;if (!file->f_op->write) goto close_fail; -if (!ispipe &&do_truncate (file->f_path.dentry,0 ,0 , file) !=0 ) +if (!ispipe && +do_truncate (file->f_path.dentry, file->f_path.mnt,0 ,0 , file) !=0 ) goto close_fail; retval = binfmt->core_dump (signr, regs, file);
fs/fat/file.c +1 -1
@@ -92,7 +92,7 @@ int fat_generic_ioctl(struct inode *inod }/* This MUST be done before doing anything irreversible... */ - err =notify_change (filp->f_path.dentry, &ia); + err =notify_change (filp->f_path.dentry, filp->f_path.mnt, &ia);if (err) goto up;
fs/hpfs/namei.c +1 -1
@@ -426,7 +426,7 @@ again:/*printk("HPFS: truncating file before delete.\n");*/ newattrs.ia_size =0 ; newattrs.ia_valid =ATTR_SIZE |ATTR_CTIME ; - err =notify_change (dentry, &newattrs); + err =notify_change (dentry,NULL , &newattrs);put_write_access (inode);if (!err) goto again;
fs/namei.c +2 -1
@@ -1580,7 +1580,8 @@ int may_open(struct nameidata *nd, int aif (!error) {DQUOT_INIT (inode); - error =do_truncate (dentry,0 ,ATTR_MTIME |ATTR_CTIME ,NULL ); + error =do_truncate (dentry, nd->mnt,0 , +ATTR_MTIME |ATTR_CTIME ,NULL ); }put_write_access (inode);if (error)
fs/nfsd/vfs.c +4 -4
@@ -358,7 +358,7 @@ nfsd_setattr(struct svc_rqst *rqstp, str err = nfserr_notsync;if (!check_guard || guardtime == inode->i_ctime.tv_sec) {fh_lock (fhp); - host_err =notify_change (dentry, iap); + host_err =notify_change (dentry, fhp->fh_export->ex_mnt, iap); err =nfserrno (host_err);fh_unlock (fhp); }
@@ -893,13 +893,13 @@ out:return err; } -static void kill_suid (struct dentry *dentry) +static void kill_suid (struct dentry *dentry,struct vfsmount *mnt) {struct iattr ia; ia.ia_valid =ATTR_KILL_SUID |ATTR_KILL_SGID ;mutex_lock (&dentry->d_inode->i_mutex); -notify_change (dentry, &ia); +notify_change (dentry, mnt, &ia);mutex_unlock (&dentry->d_inode->i_mutex); }
@@ -958,7 +958,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, s/* clear setuid/setgid flag after write */ if (host_err >=0 && (inode->i_mode & (S_ISUID |S_ISGID ))) -kill_suid (dentry); +kill_suid (dentry, exp->ex_mnt);if (host_err >=0 && stable) {static ino_t last_ino;
fs/open.c +15 -13
@@ -194,8 +194,8 @@ out:return error; } -int do_truncate (struct dentry *dentry,loff_t length,unsigned int time_attrs, -struct file *filp) +int do_truncate (struct dentry *dentry,struct vfsmount *mnt,loff_t length, +unsigned int time_attrs,struct file *filp) {int err;struct iattr newattrs;
@@ -212,7 +212,7 @@ int do_truncate(struct dentry *dentry, l }mutex_lock (&dentry->d_inode->i_mutex); - err =notify_change (dentry, &newattrs); + err =notify_change (dentry, mnt, &newattrs);mutex_unlock (&dentry->d_inode->i_mutex);return err; }
@@ -267,7 +267,7 @@ static long do_sys_truncate(const char _ error =locks_verify_truncate (inode,NULL , length);if (!error) {DQUOT_INIT (inode); - error =do_truncate (nd.dentry, length,0 ,NULL ); + error =do_truncate (nd.dentry, nd.mnt, length,0 ,NULL ); }put_write_access (inode);
@@ -319,7 +319,8 @@ static long do_sys_ftruncate(unsigned in error =locks_verify_truncate (inode, file, length);if (!error) - error =do_truncate (dentry, length,ATTR_MTIME |ATTR_CTIME , file); + error =do_truncate (dentry, file->f_path.mnt, length, +ATTR_MTIME |ATTR_CTIME , file); out_putf:fput (file); out:
@@ -519,7 +520,7 @@ asmlinkage long sys_fchmod(unsigned int mode = inode->i_mode; newattrs.ia_mode = (mode &S_IALLUGO ) | (inode->i_mode & ~S_IALLUGO ); newattrs.ia_valid =ATTR_MODE |ATTR_CTIME ; - err =notify_change (dentry, &newattrs); + err =notify_change (dentry, file->f_path.mnt, &newattrs);mutex_unlock (&inode->i_mutex); out_putf:
@@ -554,7 +555,7 @@ asmlinkage long sys_fchmodat(int dfd, co mode = inode->i_mode; newattrs.ia_mode = (mode &S_IALLUGO ) | (inode->i_mode & ~S_IALLUGO ); newattrs.ia_valid =ATTR_MODE |ATTR_CTIME ; - error =notify_change (nd.dentry, &newattrs); + error =notify_change (nd.dentry, nd.mnt, &newattrs);mutex_unlock (&inode->i_mutex); dput_and_out:
@@ -568,7 +569,8 @@ asmlinkage long sys_chmod(const char __ureturn sys_fchmodat (AT_FDCWD , filename, mode); } -static int chown_common (struct dentry * dentry,uid_t user,gid_t group) +static int chown_common (struct dentry * dentry,struct vfsmount *mnt, +uid_t user,gid_t group) {struct inode * inode;int error;
@@ -597,7 +599,7 @@ static int chown_common(struct dentry *if (!S_ISDIR (inode->i_mode)) newattrs.ia_valid |=ATTR_KILL_SUID |ATTR_KILL_SGID ;mutex_lock (&inode->i_mutex); - error =notify_change (dentry, &newattrs); + error =notify_change (dentry, mnt, &newattrs);mutex_unlock (&inode->i_mutex); out:return error;
@@ -611,7 +613,7 @@ asmlinkage long sys_chown(const char __u error =user_path_walk (filename, &nd);if (error) goto out; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group);path_release (&nd); out:return error;
@@ -631,7 +633,7 @@ asmlinkage long sys_fchownat(int dfd, co error =__user_walk_fd (dfd, filename, follow, &nd);if (error) goto out; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group);path_release (&nd); out:return error;
@@ -645,7 +647,7 @@ asmlinkage long sys_lchown(const char __ error =user_path_walk_link (filename, &nd);if (error) goto out; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group);path_release (&nd); out:return error;
@@ -664,7 +666,7 @@ asmlinkage long sys_fchown(unsigned int dentry = file->f_path.dentry;audit_inode (NULL , dentry->d_inode); - error =chown_common (dentry, user, group); + error =chown_common (dentry, file->f_path.mnt, user, group);fput (file); out:return error;
fs/reiserfs/xattr.c +3 -3
@@ -527,7 +527,7 @@ reiserfs_xattr_set(struct inode *inode, newattrs.ia_size = buffer_size; newattrs.ia_valid =ATTR_SIZE |ATTR_CTIME ;mutex_lock (&xinode->i_mutex); - err =notify_change (fp->f_path.dentry, &newattrs); + err =notify_change (fp->f_path.dentry,NULL , &newattrs);if (err) goto out_filp;
@@ -867,7 +867,7 @@ reiserfs_chown_xattrs_filler(void *buf, }if (!S_ISDIR (xafile->d_inode->i_mode)) - err =notify_change (xafile, attrs); + err =notify_change (xafile,NULL , attrs);dput (xafile);return err;
@@ -919,7 +919,7 @@ int reiserfs_chown_xattrs(struct inode * goto out_dir; } - err =notify_change (dir, attrs); + err =notify_change (dir,NULL , attrs);unlock_kernel (); out_dir:
fs/sysfs/file.c +1 -1
@@ -584,7 +584,7 @@ int sysfs_chmod_file(struct kobject *kob newattrs.ia_mode = (mode &S_IALLUGO ) | (inode->i_mode & ~S_IALLUGO ); newattrs.ia_valid =ATTR_MODE |ATTR_CTIME ; - res =notify_change (victim, &newattrs); + res =notify_change (victim,NULL , &newattrs);mutex_unlock (&inode->i_mutex); }dput (victim);
fs/utimes.c +2 -2
@@ -62,7 +62,7 @@ asmlinkage long sys_utime(char __user * goto dput_and_out; }mutex_lock (&inode->i_mutex); - error =notify_change (nd.dentry, &newattrs); + error =notify_change (nd.dentry, nd.mnt, &newattrs);mutex_unlock (&inode->i_mutex); dput_and_out:path_release (&nd);
@@ -115,7 +115,7 @@ long do_utimes(int dfd, char __user *fil goto dput_and_out; }mutex_lock (&inode->i_mutex); - error =notify_change (nd.dentry, &newattrs); + error =notify_change (nd.dentry, nd.mnt, &newattrs);mutex_unlock (&inode->i_mutex); dput_and_out:path_release (&nd);
include/linux/fs.h +3 -3
@@ -1458,8 +1458,8 @@ static inline int break_lease(struct ino/* fs/open.c */ -extern int do_truncate (struct dentry *,loff_t start,unsigned int time_attrs, -struct file *filp); +extern int do_truncate (struct dentry *,struct vfsmount *,loff_t start, +unsigned int time_attrs,struct file *filp);extern long do_sys_open (int fdf,const char __user *filename,int flags,int mode);extern struct file *filp_open (const char *,int ,int );
@@ -1612,7 +1612,7 @@ extern int do_remount_sb(struct super_bl#ifdef CONFIG_BLOCK extern sector_t bmap (struct inode *,sector_t );#endif -extern int notify_change (struct dentry *,struct iattr *); +extern int notify_change (struct dentry *,struct vfsmount *,struct iattr *);extern int permission (struct inode *,int ,struct nameidata *);extern int generic_permission (struct inode *,int ,int (*check_acl)(struct inode *,int ));
mm/filemap.c +1 -1
@@ -1877,7 +1877,7 @@ int __remove_suid(struct path *path, intstruct iattr newattrs; newattrs.ia_valid =ATTR_FORCE | kill; -return notify_change (path->dentry, &newattrs); +return notify_change (path->dentry, path->mnt, &newattrs); }int remove_suid (struct path *path)
mm/tiny-shmem.c +1 -1
@@ -86,7 +86,7 @@ struct file *shmem_file_setup(char *name file->f_mode =FMODE_WRITE |FMODE_READ ;/* notify everyone as to the change of file size */ - error =do_truncate (dentry, size,0 , file); + error =do_truncate (dentry, file->f_path.mnt, size,0 , file);if (error <0 ) goto close_file;
PATCH 6/41 Pass struct vfsmount to the inode_setattr LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/attr.c | 4 ++-- include/linux/security.h | 8 ++++++-- security/dummy.c | 3 ++- security/selinux/hooks.c | 5 +++-- 4 files changed, 13 insertions(+), 7 deletions(-)
fs/attr.c +2 -2
@@ -145,13 +145,13 @@ int notify_change(struct dentry *dentry,down_write (&dentry->d_inode->i_alloc_sem);if (inode->i_op && inode->i_op->setattr) { - error =security_inode_setattr (dentry, attr); + error =security_inode_setattr (dentry, mnt, attr);if (!error) error = inode->i_op->setattr (dentry, attr); }else { error =inode_change_ok (inode, attr);if (!error) - error =security_inode_setattr (dentry, attr); + error =security_inode_setattr (dentry, mnt, attr);if (!error) {if ((ia_valid &ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid &ATTR_GID && attr->ia_gid != inode->i_gid))
include/linux/security.h +6 -2
@@ -358,6 +358,7 @@ struct request_sock;* file attributes change (such as when a file is truncated, chown/chmod * operations, transferring disk quotas, etc). * @dentry contains the dentry structure for the file. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * @attr is the iattr structure containing the new file attributes. * Return 0 if permission is granted. * @inode_getattr:
@@ -1221,7 +1222,8 @@ struct security_operations {int (*inode_readlink) (struct dentry *dentry);int (*inode_follow_link) (struct dentry *dentry,struct nameidata *nd);int (*inode_permission) (struct inode *inode,int mask,struct nameidata *nd); -int (*inode_setattr) (struct dentry *dentry,struct iattr *attr); +int (*inode_setattr) (struct dentry *dentry,struct vfsmount *mnt, +struct iattr *attr);int (*inode_getattr) (struct vfsmount *mnt,struct dentry *dentry);void (*inode_delete) (struct inode *inode); int (*inode_setxattr) (struct dentry *dentry, char *name, void *value,
@@ -1708,11 +1710,12 @@ static inline int security_inode_permiss }static inline int security_inode_setattr (struct dentry *dentry, +struct vfsmount *mnt,struct iattr *attr) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_setattr (dentry, attr); +return security_ops->inode_setattr (dentry, mnt, attr); } static inline int security_inode_getattr (struct vfsmount *mnt,
@@ -2412,6 +2415,7 @@ static inline int security_inode_permiss }static inline int security_inode_setattr (struct dentry *dentry, +struct vfsmount *mnt,struct iattr *attr) {return 0 ;
security/dummy.c +2 -1
@@ -328,7 +328,8 @@ static int dummy_inode_permission (strucreturn 0 ; } -static int dummy_inode_setattr (struct dentry *dentry,struct iattr *iattr) +static int dummy_inode_setattr (struct dentry *dentry,struct vfsmount *mnt, +struct iattr *iattr) {return 0 ; }
security/selinux/hooks.c +3 -2
@@ -2269,11 +2269,12 @@ static int selinux_inode_permission(stru file_mask_to_av(inode->i_mode, mask), NULL); } -static int selinux_inode_setattr (struct dentry *dentry,struct iattr *iattr) +static int selinux_inode_setattr (struct dentry *dentry,struct vfsmount *mnt, +struct iattr *iattr) {int rc; - rc = secondary_ops->inode_setattr (dentry, iattr); + rc = secondary_ops->inode_setattr (dentry, mnt, iattr);if (rc)return rc;
PATCH 7/41 Add struct vfsmount parameter to vfs_mkdir()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 5 ++++- fs/namei.c | 5 +++-- fs/nfsd/nfs4recover.c | 3 ++- fs/nfsd/vfs.c | 3 ++- include/linux/fs.h | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-)
fs/ecryptfs/inode.c +4 -1
@@ -509,11 +509,14 @@ static int ecryptfs_mkdir(struct inode * {int rc;struct dentry *lower_dentry; +struct vfsmount *lower_mnt;struct dentry *lower_dir_dentry; lower_dentry =ecryptfs_dentry_to_lower (dentry); + lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry); lower_dir_dentry =lock_parent (lower_dentry); - rc =vfs_mkdir (lower_dir_dentry->d_inode, lower_dentry, mode); + rc =vfs_mkdir (lower_dir_dentry->d_inode, lower_dentry, lower_mnt, + mode);if (rc || !lower_dentry->d_inode) goto out; rc =ecryptfs_interpose (lower_dentry, dentry, dir->i_sb,0 );
fs/namei.c +3 -2
@@ -1916,7 +1916,8 @@ asmlinkage long sys_mknod(const char __ureturn sys_mknodat (AT_FDCWD , filename, mode, dev); } -int vfs_mkdir (struct inode *dir,struct dentry *dentry,int mode) +int vfs_mkdir (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt, +int mode) {int error =may_create (dir, dentry,NULL );
@@ -1960,7 +1961,7 @@ asmlinkage long sys_mkdirat(int dfd, conif (!IS_POSIXACL (nd.dentry->d_inode)) mode &= ~current->fs->umask; - error =vfs_mkdir (nd.dentry->d_inode, dentry, mode); + error =vfs_mkdir (nd.dentry->d_inode, dentry, nd.mnt, mode);dput (dentry); out_unlock:mutex_unlock (&nd.dentry->d_inode->i_mutex);
fs/nfsd/nfs4recover.c +2 -1
@@ -156,7 +156,8 @@ nfsd4_create_clid_dir(struct nfs4_clientdprintk ("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n" ); goto out_put; } - status =vfs_mkdir (rec_dir.dentry->d_inode, dentry,S_IRWXU ); + status =vfs_mkdir (rec_dir.dentry->d_inode, dentry, rec_dir.mnt, +S_IRWXU ); out_put:dput (dentry); out_unlock:
fs/nfsd/vfs.c +2 -1
@@ -1186,7 +1186,8 @@ nfsd_create(struct svc_rqst *rqstp, stru host_err =vfs_create (dirp, dchild, iap->ia_mode,NULL );break ;case S_IFDIR : - host_err =vfs_mkdir (dirp, dchild, iap->ia_mode); + host_err =vfs_mkdir (dirp, dchild, fhp->fh_export->ex_mnt, + iap->ia_mode);break ;case S_IFCHR :case S_IFBLK :
include/linux/fs.h +1 -1
@@ -980,7 +980,7 @@ extern void unlock_super(struct super_bl*/ extern int vfs_permission (struct nameidata *,int );extern int vfs_create (struct inode *,struct dentry *,int ,struct nameidata *); -extern int vfs_mkdir (struct inode *,struct dentry *,int ); +extern int vfs_mkdir (struct inode *,struct dentry *,struct vfsmount *,int );extern int vfs_mknod (struct inode *,struct dentry *,int ,dev_t );extern int vfs_symlink (struct inode *,struct dentry *,const char *,int );extern int vfs_link (struct dentry *,struct inode *,struct dentry *);
PATCH 8/41 Pass struct vfsmount to the inode_mkdir LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 8 ++++++-- security/dummy.c | 2 +- security/selinux/hooks.c | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-)
fs/namei.c +1 -1
@@ -1928,7 +1928,7 @@ int vfs_mkdir(struct inode *dir, structreturn -EPERM ; mode &= (S_IRWXUGO |S_ISVTX ); - error =security_inode_mkdir (dir, dentry, mode); + error =security_inode_mkdir (dir, dentry, mnt, mode);if (error)return error;
include/linux/security.h +6 -2
@@ -308,6 +308,7 @@ struct request_sock;* associated with inode strcture @dir. * @dir containst the inode structure of parent of the directory to be created. * @dentry contains the dentry structure of new directory. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * @mode contains the mode of new directory. * Return 0 if permission is granted. * @inode_rmdir:
@@ -1213,7 +1214,8 @@ struct security_operations {int (*inode_unlink) (struct inode *dir,struct dentry *dentry);int (*inode_symlink) (struct inode *dir,struct dentry *dentry,const char *old_name); -int (*inode_mkdir) (struct inode *dir,struct dentry *dentry,int mode); +int (*inode_mkdir) (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode);int (*inode_rmdir) (struct inode *dir,struct dentry *dentry);int (*inode_mknod) (struct inode *dir,struct dentry *dentry,int mode,dev_t dev);
@@ -1650,11 +1652,12 @@ static inline int security_inode_symlinkstatic inline int security_inode_mkdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode) {if (unlikely (IS_PRIVATE (dir)))return 0 ; -return security_ops->inode_mkdir (dir, dentry, mode); +return security_ops->inode_mkdir (dir, dentry, mnt, mode); } static inline int security_inode_rmdir (struct inode *dir,
@@ -2371,6 +2374,7 @@ static inline int security_inode_symlinkstatic inline int security_inode_mkdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode) {return 0 ;
security/dummy.c +1 -1
@@ -288,7 +288,7 @@ static int dummy_inode_symlink (struct i }static int dummy_inode_mkdir (struct inode *inode,struct dentry *dentry, -int mask) +struct vfsmount *mnt,int mask) {return 0 ; }
security/selinux/hooks.c +2 -1
@@ -2209,7 +2209,8 @@ static int selinux_inode_symlink(structreturn may_create (dir, dentry,SECCLASS_LNK_FILE ); } -static int selinux_inode_mkdir (struct inode *dir,struct dentry *dentry,int mask) +static int selinux_inode_mkdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mask) {return may_create (dir, dentry,SECCLASS_DIR ); }
PATCH 9/41 Add a struct vfsmount parameter to vfs_mknod()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 5 ++++- fs/namei.c | 10 ++++++---- fs/nfsd/vfs.c | 3 ++- include/linux/fs.h | 2 +- net/unix/af_unix.c | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-)
fs/ecryptfs/inode.c +4 -1
@@ -560,11 +560,14 @@ ecryptfs_mknod(struct inode *dir, struct {int rc;struct dentry *lower_dentry; +struct vfsmount *lower_mnt;struct dentry *lower_dir_dentry; lower_dentry =ecryptfs_dentry_to_lower (dentry); + lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry); lower_dir_dentry =lock_parent (lower_dentry); - rc =vfs_mknod (lower_dir_dentry->d_inode, lower_dentry, mode, dev); + rc =vfs_mknod (lower_dir_dentry->d_inode, lower_dentry, lower_mnt, mode, + dev);if (rc || !lower_dentry->d_inode) goto out; rc =ecryptfs_interpose (lower_dentry, dentry, dir->i_sb,0 );
fs/namei.c +6 -4
@@ -1837,7 +1837,8 @@ fail: }EXPORT_SYMBOL_GPL (lookup_create); -int vfs_mknod (struct inode *dir,struct dentry *dentry,int mode,dev_t dev) +int vfs_mknod (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt, +int mode,dev_t dev) {int error =may_create (dir, dentry,NULL );
@@ -1889,11 +1890,12 @@ asmlinkage long sys_mknodat(int dfd, con error =vfs_create (nd.dentry->d_inode,dentry,mode,&nd);break ;case S_IFCHR :case S_IFBLK : - error =vfs_mknod (nd.dentry->d_inode,dentry,mode, -new_decode_dev (dev)); + error =vfs_mknod (nd.dentry->d_inode, dentry, nd.mnt, + mode,new_decode_dev (dev));break ;case S_IFIFO :case S_IFSOCK : - error =vfs_mknod (nd.dentry->d_inode,dentry,mode,0 ); + error =vfs_mknod (nd.dentry->d_inode, dentry, nd.mnt, + mode,0 );break ;case S_IFDIR : error = -EPERM ;
fs/nfsd/vfs.c +2 -1
@@ -1193,7 +1193,8 @@ nfsd_create(struct svc_rqst *rqstp, strucase S_IFBLK :case S_IFIFO :case S_IFSOCK : - host_err =vfs_mknod (dirp, dchild, iap->ia_mode, rdev); + host_err =vfs_mknod (dirp, dchild, fhp->fh_export->ex_mnt, + iap->ia_mode, rdev);break ;default :printk ("nfsd: bad file type %o in nfsd_create\n" , type);
include/linux/fs.h +1 -1
@@ -981,7 +981,7 @@ extern void unlock_super(struct super_blextern int vfs_permission (struct nameidata *,int );extern int vfs_create (struct inode *,struct dentry *,int ,struct nameidata *);extern int vfs_mkdir (struct inode *,struct dentry *,struct vfsmount *,int ); -extern int vfs_mknod (struct inode *,struct dentry *,int ,dev_t ); +extern int vfs_mknod (struct inode *,struct dentry *,struct vfsmount *,int ,dev_t );extern int vfs_symlink (struct inode *,struct dentry *,const char *,int );extern int vfs_link (struct dentry *,struct inode *,struct dentry *);extern int vfs_rmdir (struct inode *,struct dentry *);
net/unix/af_unix.c +1 -1
@@ -808,7 +808,7 @@ static int unix_bind(struct socket *sock*/ mode =S_IFSOCK | (SOCK_INODE (sock)->i_mode & ~current->fs->umask); - err =vfs_mknod (nd.dentry->d_inode, dentry, mode,0 ); + err =vfs_mknod (nd.dentry->d_inode, dentry, nd.mnt, mode,0 );if (err) goto out_mknod_dput;mutex_unlock (&nd.dentry->d_inode->i_mutex);
PATCH 10/41 Pass struct vfsmount to the inode_mknod LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 7 +++++-- security/dummy.c | 2 +- security/selinux/hooks.c | 5 +++-- 4 files changed, 10 insertions(+), 6 deletions(-)
fs/namei.c +1 -1
@@ -1851,7 +1851,7 @@ int vfs_mknod(struct inode *dir, structif (!dir->i_op || !dir->i_op->mknod)return -EPERM ; - error =security_inode_mknod (dir, dentry, mode, dev); + error =security_inode_mknod (dir, dentry, mnt, mode, dev);if (error)return error;
include/linux/security.h +5 -2
@@ -323,6 +323,7 @@ struct request_sock;* and not this hook. * @dir contains the inode structure of parent of the new file. * @dentry contains the dentry structure of the new file. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * @mode contains the mode of the new file. * @dev contains the the device number. * Return 0 if permission is granted.
@@ -1218,7 +1219,7 @@ struct security_operations { struct vfsmount *mnt, int mode);int (*inode_rmdir) (struct inode *dir,struct dentry *dentry);int (*inode_mknod) (struct inode *dir,struct dentry *dentry, -int mode,dev_t dev); +struct vfsmount *mnt,int mode,dev_t dev);int (*inode_rename) (struct inode *old_dir,struct dentry *old_dentry,struct inode *new_dir,struct dentry *new_dentry);int (*inode_readlink) (struct dentry *dentry);
@@ -1670,11 +1671,12 @@ static inline int security_inode_rmdir (static inline int security_inode_mknod (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode,dev_t dev) {if (unlikely (IS_PRIVATE (dir)))return 0 ; -return security_ops->inode_mknod (dir, dentry, mode, dev); +return security_ops->inode_mknod (dir, dentry, mnt, mode, dev); } static inline int security_inode_rename (struct inode *old_dir,
@@ -2388,6 +2390,7 @@ static inline int security_inode_rmdir (static inline int security_inode_mknod (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode,dev_t dev) {return 0 ;
security/dummy.c +1 -1
@@ -299,7 +299,7 @@ static int dummy_inode_rmdir (struct ino }static int dummy_inode_mknod (struct inode *inode,struct dentry *dentry, -int mode,dev_t dev) +struct vfsmount *mnt,int mode,dev_t dev) {return 0 ; }
security/selinux/hooks.c +3 -2
@@ -2220,11 +2220,12 @@ static int selinux_inode_rmdir(struct inreturn may_link (dir, dentry,MAY_RMDIR ); } -static int selinux_inode_mknod (struct inode *dir,struct dentry *dentry,int mode,dev_t dev) +static int selinux_inode_mknod (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode,dev_t dev) {int rc; - rc = secondary_ops->inode_mknod (dir, dentry, mode, dev); + rc = secondary_ops->inode_mknod (dir, dentry, mnt, mode, dev);if (rc)return rc;
PATCH 11/41 Add a struct vfsmount parameter to vfs_symlink()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 4 +++- fs/namei.c | 6 ++++-- fs/nfsd/vfs.c | 7 +++++-- include/linux/fs.h | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-)
fs/ecryptfs/inode.c +3 -1
@@ -470,6 +470,7 @@ static int ecryptfs_symlink(struct inode {int rc;struct dentry *lower_dentry; +struct vfsmount *lower_mnt;struct dentry *lower_dir_dentry;umode_t mode;char *encoded_symname;
@@ -478,6 +479,7 @@ static int ecryptfs_symlink(struct inode lower_dentry =ecryptfs_dentry_to_lower (dentry);dget (lower_dentry); + lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry); lower_dir_dentry =lock_parent (lower_dentry); mode =S_IALLUGO ; encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname,
@@ -487,7 +489,7 @@ static int ecryptfs_symlink(struct inode rc = encoded_symlen; goto out_lock; } - rc =vfs_symlink (lower_dir_dentry->d_inode, lower_dentry, + rc =vfs_symlink (lower_dir_dentry->d_inode, lower_dentry, lower_mnt, encoded_symname, mode);kfree (encoded_symname);if (rc || !lower_dentry->d_inode)
fs/namei.c +4 -2
@@ -2185,7 +2185,8 @@ asmlinkage long sys_unlink(const char __return do_unlinkat (AT_FDCWD , pathname); } -int vfs_symlink (struct inode *dir,struct dentry *dentry,const char *oldname,int mode) +int vfs_symlink (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt, +const char *oldname,int mode) {int error =may_create (dir, dentry,NULL );
@@ -2231,7 +2232,8 @@ asmlinkage long sys_symlinkat(const charif (IS_ERR (dentry)) goto out_unlock; - error =vfs_symlink (nd.dentry->d_inode, dentry, from,S_IALLUGO ); + error =vfs_symlink (nd.dentry->d_inode, dentry, nd.mnt, from, +S_IALLUGO );dput (dentry); out_unlock:mutex_unlock (&nd.dentry->d_inode->i_mutex);
fs/nfsd/vfs.c +5 -2
@@ -1435,6 +1435,7 @@ nfsd_symlink(struct svc_rqst *rqstp, str struct iattr *iap) {struct dentry *dentry, *dnew; +struct vfsmount *mnt;__be32 err, cerr;int host_err;umode_t mode;
@@ -1461,6 +1462,7 @@ nfsd_symlink(struct svc_rqst *rqstp, strif (iap && (iap->ia_valid &ATTR_MODE )) mode = iap->ia_mode &S_IALLUGO ; + mnt = fhp->fh_export->ex_mnt;if (unlikely (path[plen] !=0 )) {char *path_alloced =kmalloc (plen+1 ,GFP_KERNEL );if (path_alloced ==NULL )
@@ -1468,11 +1470,12 @@ nfsd_symlink(struct svc_rqst *rqstp, strelse {strncpy (path_alloced, path, plen); path_alloced[plen] =0 ; - host_err =vfs_symlink (dentry->d_inode, dnew, path_alloced, mode); + host_err =vfs_symlink (dentry->d_inode, dnew, mnt, + path_alloced, mode);kfree (path_alloced); } }else - host_err =vfs_symlink (dentry->d_inode, dnew, path, mode); + host_err =vfs_symlink (dentry->d_inode, dnew, mnt, path, mode);if (!host_err) {if (EX_ISSYNC (fhp->fh_export))
include/linux/fs.h +1 -1
@@ -982,7 +982,7 @@ extern int vfs_permission(struct nameidaextern int vfs_create (struct inode *,struct dentry *,int ,struct nameidata *);extern int vfs_mkdir (struct inode *,struct dentry *,struct vfsmount *,int );extern int vfs_mknod (struct inode *,struct dentry *,struct vfsmount *,int ,dev_t ); -extern int vfs_symlink (struct inode *,struct dentry *,const char *,int ); +extern int vfs_symlink (struct inode *,struct dentry *,struct vfsmount *,const char *,int );extern int vfs_link (struct dentry *,struct inode *,struct dentry *);extern int vfs_rmdir (struct inode *,struct dentry *);extern int vfs_unlink (struct inode *,struct dentry *);
PATCH 12/41 Pass struct vfsmount to the inode_symlink LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 9 ++++++--- security/dummy.c | 2 +- security/selinux/hooks.c | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-)
fs/namei.c +1 -1
@@ -2196,7 +2196,7 @@ int vfs_symlink(struct inode *dir, strucif (!dir->i_op || !dir->i_op->symlink)return -EPERM ; - error =security_inode_symlink (dir, dentry, oldname); + error =security_inode_symlink (dir, dentry, mnt, oldname);if (error)return error;
include/linux/security.h +6 -3
@@ -301,6 +301,7 @@ struct request_sock;* Check the permission to create a symbolic link to a file. * @dir contains the inode structure of parent directory of the symbolic link. * @dentry contains the dentry structure of the symbolic link. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * @old_name contains the pathname of file. * Return 0 if permission is granted. * @inode_mkdir:
@@ -1213,8 +1214,8 @@ struct security_operations {int (*inode_link) (struct dentry *old_dentry,struct inode *dir,struct dentry *new_dentry);int (*inode_unlink) (struct inode *dir,struct dentry *dentry); -int (*inode_symlink) (struct inode *dir, -struct dentry *dentry,const char *old_name); +int (*inode_symlink) (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,const char *old_name);int (*inode_mkdir) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode);int (*inode_rmdir) (struct inode *dir,struct dentry *dentry);
@@ -1644,11 +1645,12 @@ static inline int security_inode_unlinkstatic inline int security_inode_symlink (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,const char *old_name) {if (unlikely (IS_PRIVATE (dir)))return 0 ; -return security_ops->inode_symlink (dir, dentry, old_name); +return security_ops->inode_symlink (dir, dentry, mnt, old_name); } static inline int security_inode_mkdir (struct inode *dir,
@@ -2369,6 +2371,7 @@ static inline int security_inode_unlinkstatic inline int security_inode_symlink (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,const char *old_name) {return 0 ;
security/dummy.c +1 -1
@@ -282,7 +282,7 @@ static int dummy_inode_unlink (struct in }static int dummy_inode_symlink (struct inode *inode,struct dentry *dentry, -const char *name) +struct vfsmount *mnt,const char *name) {return 0 ; }
security/selinux/hooks.c +2 -1
@@ -2204,7 +2204,8 @@ static int selinux_inode_unlink(struct ireturn may_link (dir, dentry,MAY_UNLINK ); } -static int selinux_inode_symlink (struct inode *dir,struct dentry *dentry,const char *name) +static int selinux_inode_symlink (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,const char *name) {return may_create (dir, dentry,SECCLASS_LNK_FILE ); }
PATCH 13/41 Pass struct vfsmount to the inode_readlink LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/stat.c | 2 +- include/linux/security.h | 11 +++++++---- security/dummy.c | 2 +- security/selinux/hooks.c | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-)
fs/stat.c +1 -1
@@ -307,7 +307,7 @@ asmlinkage long sys_readlinkat(int dfd, error = -EINVAL ;if (inode->i_op && inode->i_op->readlink) { - error =security_inode_readlink (nd.dentry); + error =security_inode_readlink (nd.dentry, nd.mnt);if (!error) {touch_atime (nd.mnt, nd.dentry); error = inode->i_op->readlink (nd.dentry, buf, bufsiz);
include/linux/security.h +7 -4
@@ -338,6 +338,7 @@ struct request_sock;* @inode_readlink: * Check the permission to read the symbolic link. * @dentry contains the dentry structure for the file link. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * Return 0 if permission is granted. * @inode_follow_link: * Check permission to follow a symbolic link when looking up a pathname.
@@ -1223,7 +1224,7 @@ struct security_operations { struct vfsmount *mnt, int mode, dev_t dev);int (*inode_rename) (struct inode *old_dir,struct dentry *old_dentry,struct inode *new_dir,struct dentry *new_dentry); -int (*inode_readlink) (struct dentry *dentry); +int (*inode_readlink) (struct dentry *dentry,struct vfsmount *mnt);int (*inode_follow_link) (struct dentry *dentry,struct nameidata *nd);int (*inode_permission) (struct inode *inode,int mask,struct nameidata *nd); int (*inode_setattr) (struct dentry *dentry, struct vfsmount *mnt,
@@ -1693,11 +1694,12 @@ static inline int security_inode_rename new_dir, new_dentry); } -static inline int security_inode_readlink (struct dentry *dentry) +static inline int security_inode_readlink (struct dentry *dentry, +struct vfsmount *mnt) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_readlink (dentry); +return security_ops->inode_readlink (dentry, mnt); } static inline int security_inode_follow_link (struct dentry *dentry,
@@ -2407,7 +2409,8 @@ static inline int security_inode_renamereturn 0 ; } -static inline int security_inode_readlink (struct dentry *dentry) +static inline int security_inode_readlink (struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/dummy.c +1 -1
@@ -312,7 +312,7 @@ static int dummy_inode_rename (struct inreturn 0 ; } -static int dummy_inode_readlink (struct dentry *dentry) +static int dummy_inode_readlink (struct dentry *dentry,struct vfsmount *mnt) {return 0 ; }
security/selinux/hooks.c +1 -1
@@ -2239,7 +2239,7 @@ static int selinux_inode_rename(struct ireturn may_rename (old_inode, old_dentry, new_inode, new_dentry); } -static int selinux_inode_readlink (struct dentry *dentry) +static int selinux_inode_readlink (struct dentry *dentry,struct vfsmount *mnt) {return dentry_has_perm (current,NULL , dentry,FILE__READ ); }
PATCH 14/41 Add struct vfsmount parameters to vfs_link()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 9 +++++++-- fs/namei.c | 5 +++-- fs/nfsd/vfs.c | 3 ++- include/linux/fs.h | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-)
fs/ecryptfs/inode.c +7 -2
@@ -411,19 +411,24 @@ static int ecryptfs_link(struct dentry * struct dentry *new_dentry) {struct dentry *lower_old_dentry; +struct vfsmount *lower_old_mnt;struct dentry *lower_new_dentry; +struct vfsmount *lower_new_mnt;struct dentry *lower_dir_dentry;u64 file_size_save;int rc; file_size_save =i_size_read (old_dentry->d_inode); lower_old_dentry =ecryptfs_dentry_to_lower (old_dentry); + lower_old_mnt =ecryptfs_dentry_to_lower_mnt (old_dentry); lower_new_dentry =ecryptfs_dentry_to_lower (new_dentry); + lower_new_mnt =ecryptfs_dentry_to_lower_mnt (new_dentry);dget (lower_old_dentry);dget (lower_new_dentry); lower_dir_dentry =lock_parent (lower_new_dentry); - rc =vfs_link (lower_old_dentry, lower_dir_dentry->d_inode, - lower_new_dentry); + rc =vfs_link (lower_old_dentry, lower_old_mnt, + lower_dir_dentry->d_inode, lower_new_dentry, + lower_new_mnt);if (rc || !lower_new_dentry->d_inode) goto out_lock; rc =ecryptfs_interpose (lower_new_dentry, new_dentry, dir->i_sb,0 );
fs/namei.c +3 -2
@@ -2250,7 +2250,7 @@ asmlinkage long sys_symlink(const char _return sys_symlinkat (oldname,AT_FDCWD , newname); } -int vfs_link (struct dentry *old_dentry,struct inode *dir,struct dentry *new_dentry) +int vfs_link (struct dentry *old_dentry,struct vfsmount *old_mnt,struct inode *dir,struct dentry *new_dentry,struct vfsmount *new_mnt) {struct inode *inode = old_dentry->d_inode;int error;
@@ -2328,7 +2328,8 @@ asmlinkage long sys_linkat(int olddfd, c error =PTR_ERR (new_dentry);if (IS_ERR (new_dentry)) goto out_unlock; - error =vfs_link (old_nd.dentry, nd.dentry->d_inode, new_dentry); + error =vfs_link (old_nd.dentry, old_nd.mnt, nd.dentry->d_inode, + new_dentry, nd.mnt);dput (new_dentry); out_unlock:mutex_unlock (&nd.dentry->d_inode->i_mutex);
fs/nfsd/vfs.c +2 -1
@@ -1534,7 +1534,8 @@ nfsd_link(struct svc_rqst *rqstp, struct dold = tfhp->fh_dentry; dest = dold->d_inode; - host_err =vfs_link (dold, dirp, dnew); + host_err =vfs_link (dold, tfhp->fh_export->ex_mnt, dirp, + dnew, ffhp->fh_export->ex_mnt);if (!host_err) {if (EX_ISSYNC (ffhp->fh_export)) { err =nfserrno (nfsd_sync_dir (ddir));
include/linux/fs.h +1 -1
@@ -983,7 +983,7 @@ extern int vfs_create(struct inode *, stextern int vfs_mkdir (struct inode *,struct dentry *,struct vfsmount *,int );extern int vfs_mknod (struct inode *,struct dentry *,struct vfsmount *,int ,dev_t );extern int vfs_symlink (struct inode *,struct dentry *,struct vfsmount *,const char *,int ); -extern int vfs_link (struct dentry *,struct inode *,struct dentry *); +extern int vfs_link (struct dentry *,struct vfsmount *,struct inode *,struct dentry *,struct vfsmount *);extern int vfs_rmdir (struct inode *,struct dentry *);extern int vfs_unlink (struct inode *,struct dentry *);extern int vfs_rename (struct inode *,struct dentry *,struct inode *,struct dentry *);
PATCH 15/41 Pass the struct vfsmounts to the inode_link LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 3 ++- include/linux/security.h | 18 +++++++++++++----- security/dummy.c | 6 ++++-- security/selinux/hooks.c | 9 +++++++-- 4 files changed, 26 insertions(+), 10 deletions(-)
fs/namei.c +2 -1
@@ -2275,7 +2275,8 @@ int vfs_link(struct dentry *old_dentry,if (S_ISDIR (old_dentry->d_inode->i_mode))return -EPERM ; - error =security_inode_link (old_dentry, dir, new_dentry); + error =security_inode_link (old_dentry, old_mnt, dir, new_dentry, + new_mnt);if (error)return error;
include/linux/security.h +13 -5
@@ -289,8 +289,10 @@ struct request_sock;* @inode_link: * Check permission before creating a new hard link to a file. * @old_dentry contains the dentry structure for an existing link to the file. +* @old_mnt is the vfsmount corresponding to @old_dentry (may be NULL). * @dir contains the inode structure of the parent directory of the new link. * @new_dentry contains the dentry structure for the new link. +* @new_mnt is the vfsmount corresponding to @new_dentry (may be NULL). * Return 0 if permission is granted. * @inode_unlink: * Check the permission to remove a hard link to a file.
@@ -1212,8 +1214,9 @@ struct security_operations { char **name, void **value, size_t *len);int (*inode_create) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode); -int (*inode_link) (struct dentry *old_dentry, -struct inode *dir,struct dentry *new_dentry); +int (*inode_link) (struct dentry *old_dentry,struct vfsmount *old_mnt, +struct inode *dir,struct dentry *new_dentry, +struct vfsmount *new_mnt);int (*inode_unlink) (struct inode *dir,struct dentry *dentry);int (*inode_symlink) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,const char *old_name);
@@ -1628,12 +1631,15 @@ static inline int security_inode_create }static inline int security_inode_link (struct dentry *old_dentry, +struct vfsmount *old_mnt,struct inode *dir, -struct dentry *new_dentry) +struct dentry *new_dentry, +struct vfsmount *new_mnt) {if (unlikely (IS_PRIVATE (old_dentry->d_inode)))return 0 ; -return security_ops->inode_link (old_dentry, dir, new_dentry); +return security_ops->inode_link (old_dentry, old_mnt, dir, + new_dentry, new_mnt); } static inline int security_inode_unlink (struct inode *dir,
@@ -2359,8 +2365,10 @@ static inline int security_inode_create }static inline int security_inode_link (struct dentry *old_dentry, +struct vfsmount *old_mnt,struct inode *dir, -struct dentry *new_dentry) +struct dentry *new_dentry, +struct vfsmount *new_mnt) {return 0 ; }
security/dummy.c +4 -2
@@ -270,8 +270,10 @@ static int dummy_inode_create (struct inreturn 0 ; } -static int dummy_inode_link (struct dentry *old_dentry,struct inode *inode, -struct dentry *new_dentry) +static int dummy_inode_link (struct dentry *old_dentry, +struct vfsmount *old_mnt,struct inode *inode, +struct dentry *new_dentry, +struct vfsmount *new_mnt) {return 0 ; }
security/selinux/hooks.c +7 -2
@@ -2184,11 +2184,16 @@ static int selinux_inode_create(struct ireturn may_create (dir, dentry,SECCLASS_FILE ); } -static int selinux_inode_link (struct dentry *old_dentry,struct inode *dir,struct dentry *new_dentry) +static int selinux_inode_link (struct dentry *old_dentry, +struct vfsmount *old_mnt, +struct inode *dir, +struct dentry *new_dentry, +struct vfsmount *new_mnt) {int rc; - rc = secondary_ops->inode_link (old_dentry,dir,new_dentry); + rc = secondary_ops->inode_link (old_dentry, old_mnt, dir, new_dentry, + new_mnt);if (rc)return rc;return may_link (dir, old_dentry,MAY_LINK );
PATCH 16/41 Add a struct vfsmount parameter to vfs_rmdir()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 4 +++- fs/namei.c | 4 ++-- fs/nfsd/nfs4recover.c | 2 +- fs/nfsd/vfs.c | 2 +- fs/reiserfs/xattr.c | 2 +- include/linux/fs.h | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-)
fs/ecryptfs/inode.c +3 -1
@@ -542,14 +542,16 @@ out:static int ecryptfs_rmdir (struct inode *dir,struct dentry *dentry) {struct dentry *lower_dentry; +struct vfsmount *lower_mnt;struct dentry *lower_dir_dentry;int rc; lower_dentry =ecryptfs_dentry_to_lower (dentry); + lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry);dget (dentry); lower_dir_dentry =lock_parent (lower_dentry);dget (lower_dentry); - rc =vfs_rmdir (lower_dir_dentry->d_inode, lower_dentry); + rc =vfs_rmdir (lower_dir_dentry->d_inode, lower_dentry, lower_mnt);dput (lower_dentry);if (!rc)d_delete (lower_dentry);
fs/namei.c +2 -2
@@ -2006,7 +2006,7 @@ void dentry_unhash(struct dentry *dentryspin_unlock (&dcache_lock); } -int vfs_rmdir (struct inode *dir,struct dentry *dentry) +int vfs_rmdir (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt) {int error =may_delete (dir, dentry,1 );
@@ -2070,7 +2070,7 @@ static long do_rmdir(int dfd, const char error =PTR_ERR (dentry);if (IS_ERR (dentry)) goto exit2; - error =vfs_rmdir (nd.dentry->d_inode, dentry); + error =vfs_rmdir (nd.dentry->d_inode, dentry, nd.mnt);dput (dentry); exit2:mutex_unlock (&nd.dentry->d_inode->i_mutex);
fs/nfsd/nfs4recover.c +1 -1
@@ -276,7 +276,7 @@ nfsd4_clear_clid_dir(struct dentry *dir,* a kernel from the future.... */ nfsd4_list_rec_dir (dentry, nfsd4_remove_clid_file);mutex_lock_nested (&dir->d_inode->i_mutex,I_MUTEX_PARENT ); - status =vfs_rmdir (dir->d_inode, dentry); + status =vfs_rmdir (dir->d_inode, dentry, rec_dir.mnt);mutex_unlock (&dir->d_inode->i_mutex);return status; }
fs/nfsd/vfs.c +1 -1
@@ -1702,7 +1702,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru#endif host_err =vfs_unlink (dirp, rdentry); }else {/* It's RMDIR */ - host_err =vfs_rmdir (dirp, rdentry); + host_err =vfs_rmdir (dirp, rdentry, fhp->fh_export->ex_mnt); }dput (rdentry);
fs/reiserfs/xattr.c +1 -1
@@ -823,7 +823,7 @@ int reiserfs_delete_xattrs(struct inodeif (dir->d_inode->i_nlink <=2 ) { root =get_xa_root (inode->i_sb);reiserfs_write_lock_xattrs (inode->i_sb); - err =vfs_rmdir (root->d_inode, dir); + err =vfs_rmdir (root->d_inode, dir,NULL );reiserfs_write_unlock_xattrs (inode->i_sb);dput (root); }else {
include/linux/fs.h +1 -1
@@ -984,7 +984,7 @@ extern int vfs_mkdir(struct inode *, strextern int vfs_mknod (struct inode *,struct dentry *,struct vfsmount *,int ,dev_t );extern int vfs_symlink (struct inode *,struct dentry *,struct vfsmount *,const char *,int );extern int vfs_link (struct dentry *,struct vfsmount *,struct inode *,struct dentry *,struct vfsmount *); -extern int vfs_rmdir (struct inode *,struct dentry *); +extern int vfs_rmdir (struct inode *,struct dentry *,struct vfsmount *);extern int vfs_unlink (struct inode *,struct dentry *);extern int vfs_rename (struct inode *,struct dentry *,struct inode *,struct dentry *);
PATCH 17/41 Pass struct vfsmount to the inode_rmdir LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 12 ++++++++---- security/dummy.c | 3 ++- security/selinux/hooks.c | 3 ++- 4 files changed, 13 insertions(+), 7 deletions(-)
fs/namei.c +1 -1
@@ -2023,7 +2023,7 @@ int vfs_rmdir(struct inode *dir, structif (d_mountpoint (dentry)) error = -EBUSY ;else { - error =security_inode_rmdir (dir, dentry); + error =security_inode_rmdir (dir, dentry, mnt);if (!error) { error = dir->i_op->rmdir (dir, dentry);if (!error)
include/linux/security.h +8 -4
@@ -318,6 +318,7 @@ struct request_sock;* Check the permission to remove a directory. * @dir contains the inode structure of parent of the directory to be removed. * @dentry contains the dentry structure of directory to be removed. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * Return 0 if permission is granted. * @inode_mknod: * Check permissions when creating a special file (or a socket or a fifo
@@ -1222,7 +1223,8 @@ struct security_operations { struct vfsmount *mnt, const char *old_name);int (*inode_mkdir) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode); -int (*inode_rmdir) (struct inode *dir,struct dentry *dentry); +int (*inode_rmdir) (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt);int (*inode_mknod) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode,dev_t dev); int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
@@ -1671,11 +1673,12 @@ static inline int security_inode_mkdir ( }static inline int security_inode_rmdir (struct inode *dir, -struct dentry *dentry) +struct dentry *dentry, +struct vfsmount *mnt) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_rmdir (dir, dentry); +return security_ops->inode_rmdir (dir, dentry, mnt); } static inline int security_inode_mknod (struct inode *dir,
@@ -2396,7 +2399,8 @@ static inline int security_inode_mkdir ( }static inline int security_inode_rmdir (struct inode *dir, -struct dentry *dentry) +struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/dummy.c +2 -1
@@ -295,7 +295,8 @@ static int dummy_inode_mkdir (struct inoreturn 0 ; } -static int dummy_inode_rmdir (struct inode *inode,struct dentry *dentry) +static int dummy_inode_rmdir (struct inode *inode,struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/selinux/hooks.c +2 -1
@@ -2221,7 +2221,8 @@ static int selinux_inode_mkdir(struct inreturn may_create (dir, dentry,SECCLASS_DIR ); } -static int selinux_inode_rmdir (struct inode *dir,struct dentry *dentry) +static int selinux_inode_rmdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt) {return may_link (dir, dentry,MAY_RMDIR ); }
PATCH 18/41 call lsm hook before unhashing dentry in vfs_rmdir()
In reply to [email protected]If we unhash the dentry before calling the security_inode_rmdir hook, we cannot compute the file's pathname in the hook anymore. AppArmor needs to know the filename in order to decide whether a file may be deleted, though.
--- fs/namei.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
fs/namei.c +7 -6
@@ -2016,6 +2016,10 @@ int vfs_rmdir(struct inode *dir, structif (!dir->i_op || !dir->i_op->rmdir)return -EPERM ; + error =security_inode_rmdir (dir, dentry, mnt); +if (error) +return error; +DQUOT_INIT (dir);mutex_lock (&dentry->d_inode->i_mutex);
@@ -2023,12 +2027,9 @@ int vfs_rmdir(struct inode *dir, structif (d_mountpoint (dentry)) error = -EBUSY ;else { - error =security_inode_rmdir (dir, dentry, mnt); -if (!error) { - error = dir->i_op->rmdir (dir, dentry); -if (!error) - dentry->d_inode->i_flags |=S_DEAD ; - } + error = dir->i_op->rmdir (dir, dentry); +if (!error) + dentry->d_inode->i_flags |=S_DEAD ; }mutex_unlock (&dentry->d_inode->i_mutex);if (!error) {
PATCH 19/41 Add a struct vfsmount parameter to vfs_unlink()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 3 ++- fs/namei.c | 4 ++-- fs/nfsd/nfs4recover.c | 2 +- fs/nfsd/vfs.c | 2 +- include/linux/fs.h | 2 +- ipc/mqueue.c | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-)
fs/ecryptfs/inode.c +2 -1
@@ -453,10 +453,11 @@ static int ecryptfs_unlink(struct inode {int rc =0 ;struct dentry *lower_dentry =ecryptfs_dentry_to_lower (dentry); +struct vfsmount *lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry);struct inode *lower_dir_inode =ecryptfs_inode_to_lower (dir);lock_parent (lower_dentry); - rc =vfs_unlink (lower_dir_inode, lower_dentry); + rc =vfs_unlink (lower_dir_inode, lower_dentry, lower_mnt);if (rc) {printk (KERN_ERR "Error in vfs_unlink; rc = [%d]\n" , rc); goto out_unlock;
fs/namei.c +2 -2
@@ -2087,7 +2087,7 @@ asmlinkage long sys_rmdir(const char __ureturn do_rmdir (AT_FDCWD , pathname); } -int vfs_unlink (struct inode *dir,struct dentry *dentry) +int vfs_unlink (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt) {int error =may_delete (dir, dentry,0 );
@@ -2151,7 +2151,7 @@ static long do_unlinkat(int dfd, const c inode = dentry->d_inode;if (inode)atomic_inc (&inode->i_count); - error =vfs_unlink (nd.dentry->d_inode, dentry); + error =vfs_unlink (nd.dentry->d_inode, dentry, nd.mnt); exit2:dput (dentry); }
fs/nfsd/nfs4recover.c +1 -1
@@ -261,7 +261,7 @@ nfsd4_remove_clid_file(struct dentry *direturn -EINVAL ; }mutex_lock_nested (&dir->d_inode->i_mutex,I_MUTEX_PARENT ); - status =vfs_unlink (dir->d_inode, dentry); + status =vfs_unlink (dir->d_inode, dentry, rec_dir.mnt);mutex_unlock (&dir->d_inode->i_mutex);return status; }
fs/nfsd/vfs.c +1 -1
@@ -1700,7 +1700,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru host_err = -EPERM; } else#endif - host_err =vfs_unlink (dirp, rdentry); + host_err =vfs_unlink (dirp, rdentry, fhp->fh_export->ex_mnt); }else {/* It's RMDIR */ host_err =vfs_rmdir (dirp, rdentry, fhp->fh_export->ex_mnt); }
include/linux/fs.h +1 -1
@@ -985,7 +985,7 @@ extern int vfs_mknod(struct inode *, strextern int vfs_symlink (struct inode *,struct dentry *,struct vfsmount *,const char *,int );extern int vfs_link (struct dentry *,struct vfsmount *,struct inode *,struct dentry *,struct vfsmount *);extern int vfs_rmdir (struct inode *,struct dentry *,struct vfsmount *); -extern int vfs_unlink (struct inode *,struct dentry *); +extern int vfs_unlink (struct inode *,struct dentry *,struct vfsmount *);extern int vfs_rename (struct inode *,struct dentry *,struct inode *,struct dentry *);/*
ipc/mqueue.c +1 -1
@@ -748,7 +748,7 @@ asmlinkage long sys_mq_unlink(const charif (inode)atomic_inc (&inode->i_count); - err =vfs_unlink (dentry->d_parent->d_inode, dentry); + err =vfs_unlink (dentry->d_parent->d_inode, dentry, mqueue_mnt); out_err:dput (dentry);
PATCH 20/41 Pass struct vfsmount to the inode_unlink LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 12 ++++++++---- security/dummy.c | 3 ++- security/selinux/hooks.c | 5 +++-- 4 files changed, 14 insertions(+), 8 deletions(-)
fs/namei.c +1 -1
@@ -2103,7 +2103,7 @@ int vfs_unlink(struct inode *dir, structif (d_mountpoint (dentry)) error = -EBUSY ;else { - error =security_inode_unlink (dir, dentry); + error =security_inode_unlink (dir, dentry, mnt);if (!error) error = dir->i_op->unlink (dir, dentry); }
include/linux/security.h +8 -4
@@ -298,6 +298,7 @@ struct request_sock;* Check the permission to remove a hard link to a file. * @dir contains the inode structure of parent directory of the file. * @dentry contains the dentry structure for file to be unlinked. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * Return 0 if permission is granted. * @inode_symlink: * Check the permission to create a symbolic link to a file.
@@ -1218,7 +1219,8 @@ struct security_operations {int (*inode_link) (struct dentry *old_dentry,struct vfsmount *old_mnt,struct inode *dir,struct dentry *new_dentry,struct vfsmount *new_mnt); -int (*inode_unlink) (struct inode *dir,struct dentry *dentry); +int (*inode_unlink) (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt);int (*inode_symlink) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,const char *old_name); int (*inode_mkdir) (struct inode *dir, struct dentry *dentry,
@@ -1645,11 +1647,12 @@ static inline int security_inode_link (s }static inline int security_inode_unlink (struct inode *dir, -struct dentry *dentry) +struct dentry *dentry, +struct vfsmount *mnt) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_unlink (dir, dentry); +return security_ops->inode_unlink (dir, dentry, mnt); } static inline int security_inode_symlink (struct inode *dir,
@@ -2377,7 +2380,8 @@ static inline int security_inode_link (s }static inline int security_inode_unlink (struct inode *dir, -struct dentry *dentry) +struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/dummy.c +2 -1
@@ -278,7 +278,8 @@ static int dummy_inode_link (struct dentreturn 0 ; } -static int dummy_inode_unlink (struct inode *inode,struct dentry *dentry) +static int dummy_inode_unlink (struct inode *inode,struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/selinux/hooks.c +3 -2
@@ -2199,11 +2199,12 @@ static int selinux_inode_link(struct denreturn may_link (dir, old_dentry,MAY_LINK ); } -static int selinux_inode_unlink (struct inode *dir,struct dentry *dentry) +static int selinux_inode_unlink (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt) {int rc; - rc = secondary_ops->inode_unlink (dir, dentry); + rc = secondary_ops->inode_unlink (dir, dentry, mnt);if (rc)return rc;return may_link (dir, dentry,MAY_UNLINK );
PATCH 21/41 Add struct vfsmount parameters to vfs_rename()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 7 ++++++- fs/namei.c | 19 ++++++++++++------- fs/nfsd/vfs.c | 3 ++- include/linux/fs.h | 2 +- 4 files changed, 21 insertions(+), 10 deletions(-)
fs/ecryptfs/inode.c +6 -1
@@ -598,19 +598,24 @@ ecryptfs_rename(struct inode *old_dir, s {int rc;struct dentry *lower_old_dentry; +struct vfsmount *lower_old_mnt;struct dentry *lower_new_dentry; +struct vfsmount *lower_new_mnt;struct dentry *lower_old_dir_dentry;struct dentry *lower_new_dir_dentry; lower_old_dentry =ecryptfs_dentry_to_lower (old_dentry); + lower_old_mnt =ecryptfs_dentry_to_lower_mnt (old_dentry); lower_new_dentry =ecryptfs_dentry_to_lower (new_dentry); + lower_new_mnt =ecryptfs_dentry_to_lower_mnt (new_dentry);dget (lower_old_dentry);dget (lower_new_dentry); lower_old_dir_dentry =dget_parent (lower_old_dentry); lower_new_dir_dentry =dget_parent (lower_new_dentry);lock_rename (lower_old_dir_dentry, lower_new_dir_dentry); rc =vfs_rename (lower_old_dir_dentry->d_inode, lower_old_dentry, - lower_new_dir_dentry->d_inode, lower_new_dentry); + lower_old_mnt, lower_new_dir_dentry->d_inode, + lower_new_dentry, lower_new_mnt);if (rc) goto out_lock;fsstack_copy_attr_all (new_dir, lower_new_dir_dentry->d_inode,NULL );
fs/namei.c +12 -7
@@ -2383,7 +2383,8 @@ asmlinkage long sys_link(const char __us* locking]. */ static int vfs_rename_dir (struct inode *old_dir,struct dentry *old_dentry, -struct inode *new_dir,struct dentry *new_dentry) +struct vfsmount *old_mnt,struct inode *new_dir, +struct dentry *new_dentry,struct vfsmount *new_mnt) {int error =0 ;struct inode *target;
@@ -2426,7 +2427,8 @@ static int vfs_rename_dir(struct inode * }static int vfs_rename_other (struct inode *old_dir,struct dentry *old_dentry, -struct inode *new_dir,struct dentry *new_dentry) +struct vfsmount *old_mnt,struct inode *new_dir, +struct dentry *new_dentry,struct vfsmount *new_mnt) {struct inode *target;int error;
@@ -2454,7 +2456,8 @@ static int vfs_rename_other(struct inode }int vfs_rename (struct inode *old_dir,struct dentry *old_dentry, -struct inode *new_dir,struct dentry *new_dentry) +struct vfsmount *old_mnt,struct inode *new_dir, +struct dentry *new_dentry,struct vfsmount *new_mnt) {int error;int is_dir =S_ISDIR (old_dentry->d_inode->i_mode);
@@ -2483,9 +2486,11 @@ int vfs_rename(struct inode *old_dir, st old_name =fsnotify_oldname_init (old_dentry->d_name.name);if (is_dir) - error =vfs_rename_dir (old_dir,old_dentry,new_dir,new_dentry); + error =vfs_rename_dir (old_dir, old_dentry, old_mnt, + new_dir, new_dentry, new_mnt);else - error =vfs_rename_other (old_dir,old_dentry,new_dir,new_dentry); + error =vfs_rename_other (old_dir, old_dentry, old_mnt, + new_dir, new_dentry, new_mnt);if (!error) {const char *new_name = old_dentry->d_name.name; fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir,
@@ -2557,8 +2562,8 @@ static int do_rename(int olddfd, const cif (new_dentry == trap) goto exit5; - error =vfs_rename (old_dir->d_inode, old_dentry, - new_dir->d_inode, new_dentry); + error =vfs_rename (old_dir->d_inode, old_dentry, oldnd.mnt, + new_dir->d_inode, new_dentry, newnd.mnt); exit5:dput (new_dentry); exit4:
fs/nfsd/vfs.c +2 -1
@@ -1628,7 +1628,8 @@ nfsd_rename(struct svc_rqst *rqstp, stru host_err = -EPERM ; } else#endif - host_err =vfs_rename (fdir, odentry, tdir, ndentry); + host_err =vfs_rename (fdir, odentry, ffhp->fh_export->ex_mnt, + tdir, ndentry, tfhp->fh_export->ex_mnt);if (!host_err &&EX_ISSYNC (tfhp->fh_export)) { host_err =nfsd_sync_dir (tdentry);if (!host_err)
include/linux/fs.h +1 -1
@@ -986,7 +986,7 @@ extern int vfs_symlink(struct inode *, sextern int vfs_link (struct dentry *,struct vfsmount *,struct inode *,struct dentry *,struct vfsmount *);extern int vfs_rmdir (struct inode *,struct dentry *,struct vfsmount *);extern int vfs_unlink (struct inode *,struct dentry *,struct vfsmount *); -extern int vfs_rename (struct inode *,struct dentry *,struct inode *,struct dentry *); +extern int vfs_rename (struct inode *,struct dentry *,struct vfsmount *,struct inode *,struct dentry *,struct vfsmount *);/* * VFS dentry helper functions.
PATCH 22/41 Pass struct vfsmount to the inode_rename LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 6 ++++-- include/linux/security.h | 18 +++++++++++++----- security/dummy.c | 4 +++- security/selinux/hooks.c | 8 ++++++-- 4 files changed, 26 insertions(+), 10 deletions(-)
fs/namei.c +4 -2
@@ -2399,7 +2399,8 @@ static int vfs_rename_dir(struct inode *return error; } - error =security_inode_rename (old_dir, old_dentry, new_dir, new_dentry); + error =security_inode_rename (old_dir, old_dentry, old_mnt, + new_dir, new_dentry, new_mnt);if (error)return error;
@@ -2433,7 +2434,8 @@ static int vfs_rename_other(struct inodestruct inode *target;int error; - error =security_inode_rename (old_dir, old_dentry, new_dir, new_dentry); + error =security_inode_rename (old_dir, old_dentry, old_mnt, + new_dir, new_dentry, new_mnt);if (error)return error;
include/linux/security.h +13 -5
@@ -336,8 +336,10 @@ struct request_sock;* Check for permission to rename a file or directory. * @old_dir contains the inode structure for parent of the old link. * @old_dentry contains the dentry structure of the old link. +* @old_mnt is the vfsmount corresponding to @old_dentry (may be NULL). * @new_dir contains the inode structure for parent of the new link. * @new_dentry contains the dentry structure of the new link. +* @new_mnt is the vfsmount corresponding to @new_dentry (may be NULL). * Return 0 if permission is granted. * @inode_readlink: * Check the permission to read the symbolic link.
@@ -1230,7 +1232,9 @@ struct security_operations {int (*inode_mknod) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode,dev_t dev);int (*inode_rename) (struct inode *old_dir,struct dentry *old_dentry, -struct inode *new_dir,struct dentry *new_dentry); +struct vfsmount *old_mnt, +struct inode *new_dir,struct dentry *new_dentry, +struct vfsmount *new_mnt);int (*inode_readlink) (struct dentry *dentry,struct vfsmount *mnt);int (*inode_follow_link) (struct dentry *dentry,struct nameidata *nd);int (*inode_permission) (struct inode *inode,int mask,struct nameidata *nd);
@@ -1696,14 +1700,16 @@ static inline int security_inode_mknod (static inline int security_inode_rename (struct inode *old_dir,struct dentry *old_dentry, +struct vfsmount *old_mnt,struct inode *new_dir, -struct dentry *new_dentry) +struct dentry *new_dentry, +struct vfsmount *new_mnt) {if (unlikely (IS_PRIVATE (old_dentry->d_inode) || (new_dentry->d_inode &&IS_PRIVATE (new_dentry->d_inode))))return 0 ; -return security_ops->inode_rename (old_dir, old_dentry, - new_dir, new_dentry); +return security_ops->inode_rename (old_dir, old_dentry, old_mnt, + new_dir, new_dentry, new_mnt); } static inline int security_inode_readlink (struct dentry *dentry,
@@ -2419,8 +2425,10 @@ static inline int security_inode_mknod (static inline int security_inode_rename (struct inode *old_dir,struct dentry *old_dentry, +struct vfsmount *old_mnt,struct inode *new_dir, -struct dentry *new_dentry) +struct dentry *new_dentry, +struct vfsmount *new_mnt) {return 0 ; }
security/dummy.c +3 -1
@@ -310,8 +310,10 @@ static int dummy_inode_mknod (struct inostatic int dummy_inode_rename (struct inode *old_inode,struct dentry *old_dentry, +struct vfsmount *old_mnt,struct inode *new_inode, -struct dentry *new_dentry) +struct dentry *new_dentry, +struct vfsmount *new_mnt) {return 0 ; }
security/selinux/hooks.c +6 -2
@@ -2240,8 +2240,12 @@ static int selinux_inode_mknod(struct inreturn may_create (dir, dentry,inode_mode_to_security_class (mode)); } -static int selinux_inode_rename (struct inode *old_inode,struct dentry *old_dentry, -struct inode *new_inode,struct dentry *new_dentry) +static int selinux_inode_rename (struct inode *old_inode, +struct dentry *old_dentry, +struct vfsmount *old_mnt, +struct inode *new_inode, +struct dentry *new_dentry, +struct vfsmount *new_mnt) {return may_rename (old_inode, old_dentry, new_inode, new_dentry); }
PATCH 23/41 Add a struct vfsmount parameter to vfs_setxattr()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/nfsd/vfs.c | 16 +++++++++++----- fs/xattr.c | 16 ++++++++-------- include/linux/xattr.h | 3 ++- 3 files changed, 21 insertions(+), 14 deletions(-)
fs/nfsd/vfs.c +11 -5
@@ -396,7 +396,8 @@ static ssize_t nfsd_getxattr(struct dent#if defined(CONFIG_NFSD_V4 )static int -set_nfsv4_acl_one (struct dentry *dentry,struct posix_acl *pacl,char *key) +set_nfsv4_acl_one (struct dentry *dentry,struct vfsmount *mnt, +struct posix_acl *pacl,char *key) {int len;size_t buflen;
@@ -415,7 +416,7 @@ set_nfsv4_acl_one(struct dentry *dentry, goto out; } - error =vfs_setxattr (dentry, key, buf, len,0 ); + error =vfs_setxattr (dentry, mnt, key, buf, len,0 ); out:kfree (buf);return error;
@@ -428,6 +429,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst__be32 error;int host_error;struct dentry *dentry; +struct vfsmount *mnt;struct inode *inode;struct posix_acl *pacl =NULL , *dpacl =NULL ;unsigned int flags =0 ;
@@ -438,6 +440,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst goto out; dentry = fhp->fh_dentry; + mnt = fhp->fh_export->ex_mnt; inode = dentry->d_inode;if (S_ISDIR (inode->i_mode)) flags =NFS4_ACL_DIR ;
@@ -449,12 +452,14 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst }else if (host_error <0 ) goto out_nfserr; - host_error =set_nfsv4_acl_one (dentry, pacl,POSIX_ACL_XATTR_ACCESS ); + host_error =set_nfsv4_acl_one (dentry, mnt, pacl, +POSIX_ACL_XATTR_ACCESS );if (host_error <0 ) goto out_nfserr;if (S_ISDIR (inode->i_mode)) { - host_error =set_nfsv4_acl_one (dentry, dpacl,POSIX_ACL_XATTR_DEFAULT ); + host_error =set_nfsv4_acl_one (dentry, mnt, dpacl, +POSIX_ACL_XATTR_DEFAULT );if (host_error <0 ) goto out_nfserr; }
@@ -1978,7 +1983,8 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i size =0 ;if (size) - error =vfs_setxattr (fhp->fh_dentry, name, value, size,0 ); + error =vfs_setxattr (fhp->fh_dentry, fhp->fh_export->ex_mnt, + name, value, size,0 );else {if (!S_ISDIR (inode->i_mode) && type ==ACL_TYPE_DEFAULT ) error =0 ;
fs/xattr.c +8 -8
@@ -70,8 +70,8 @@ xattr_permission(struct inode *inode, co }int -vfs_setxattr (struct dentry *dentry,char *name,void *value, -size_t size,int flags) +vfs_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, +void *value,size_t size,int flags) {struct inode *inode = dentry->d_inode;int error;
@@ -194,8 +194,8 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);* Extended attribute SET operations */ static long -setxattr (struct dentry *d,char __user *name,void __user *value, -size_t size,int flags) +setxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name, +void __user *value,size_t size,int flags) {int error;void *kvalue =NULL ;
@@ -222,7 +222,7 @@ setxattr(struct dentry *d, char __user * } } - error =vfs_setxattr (d, kname, kvalue, size, flags); + error =vfs_setxattr (dentry, mnt, kname, kvalue, size, flags);kfree (kvalue);return error; }
@@ -237,7 +237,7 @@ sys_setxattr(char __user *path, char __u error =user_path_walk (path, &nd);if (error)return error; - error =setxattr (nd.dentry, name, value, size, flags); + error =setxattr (nd.dentry, nd.mnt, name, value, size, flags);path_release (&nd);return error; }
@@ -252,7 +252,7 @@ sys_lsetxattr(char __user *path, char __ error =user_path_walk_link (path, &nd);if (error)return error; - error =setxattr (nd.dentry, name, value, size, flags); + error =setxattr (nd.dentry, nd.mnt, name, value, size, flags);path_release (&nd);return error; }
@@ -270,7 +270,7 @@ sys_fsetxattr(int fd, char __user *name,return error; dentry = f->f_path.dentry;audit_inode (NULL , dentry->d_inode); - error =setxattr (dentry, name, value, size, flags); + error =setxattr (dentry, f->f_vfsmnt, name, value, size, flags);fput (f);return error; }
include/linux/xattr.h +2 -1
@@ -48,7 +48,8 @@ struct xattr_handler {ssize_t vfs_getxattr (struct dentry *,char *,void *,size_t );ssize_t vfs_listxattr (struct dentry *d,char *list,size_t size); -int vfs_setxattr (struct dentry *,char *,void *,size_t ,int ); +int vfs_setxattr (struct dentry *,struct vfsmount *,char *,void *,size_t , +int );int vfs_removexattr (struct dentry *,char *);ssize_t generic_getxattr (struct dentry *dentry,const char *name,void *buffer,size_t size);
PATCH 24/41 Pass struct vfsmount to the inode_setxattr LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/xattr.c | 4 ++-- include/linux/security.h | 40 +++++++++++++++++++++++++--------------- security/commoncap.c | 4 ++-- security/dummy.c | 9 ++++++--- security/selinux/hooks.c | 8 ++++++-- 5 files changed, 41 insertions(+), 24 deletions(-)
fs/xattr.c +2 -2
@@ -81,7 +81,7 @@ vfs_setxattr(struct dentry *dentry, strureturn error;mutex_lock (&inode->i_mutex); - error =security_inode_setxattr (dentry, name, value, size, flags); + error =security_inode_setxattr (dentry, mnt, name, value, size, flags);if (error) goto out; error = -EOPNOTSUPP ;
@@ -89,7 +89,7 @@ vfs_setxattr(struct dentry *dentry, stru error = inode->i_op->setxattr (dentry, name, value, size, flags);if (!error) {fsnotify_xattr (dentry); -security_inode_post_setxattr (dentry, name, value, +security_inode_post_setxattr (dentry, mnt, name, value, size, flags); } } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
include/linux/security.h +25 -15
@@ -49,7 +49,7 @@ extern void cap_capset_set (struct task_extern int cap_bprm_set_security (struct linux_binprm *bprm);extern void cap_bprm_apply_creds (struct linux_binprm *bprm,int unsafe);extern int cap_bprm_secureexec (struct linux_binprm *bprm); -extern int cap_inode_setxattr (struct dentry *dentry,char *name,void *value,size_t size,int flags); +extern int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags);extern int cap_inode_removexattr (struct dentry *dentry,char *name);extern int cap_task_post_setuid (uid_t old_ruid,uid_t old_euid,uid_t old_suid,int flags);extern void cap_task_reparent_to_init (struct task_struct *p);
@@ -384,11 +384,11 @@ struct request_sock;* inode. * @inode_setxattr: * Check permission before setting the extended attributes -* @value identified by @name for @dentry. +* @value identified by @name for @dentry and @mnt. * Return 0 if permission is granted. * @inode_post_setxattr: * Update inode security field after successful setxattr operation. -* @value identified by @name for @dentry. +* @value identified by @name for @dentry and @mnt. * @inode_getxattr: * Check permission before obtaining the extended attributes * identified by @name for @dentry.
@@ -1242,9 +1242,11 @@ struct security_operations { struct iattr *attr);int (*inode_getattr) (struct vfsmount *mnt,struct dentry *dentry);void (*inode_delete) (struct inode *inode); -int (*inode_setxattr) (struct dentry *dentry,char *name,void *value, -size_t size,int flags); -void (*inode_post_setxattr) (struct dentry *dentry,char *name,void *value, +int (*inode_setxattr) (struct dentry *dentry,struct vfsmount *mnt, +char *name,void *value,size_t size,int flags); +void (*inode_post_setxattr) (struct dentry *dentry, +struct vfsmount *mnt, +char *name,void *value,size_t size,int flags);int (*inode_getxattr) (struct dentry *dentry,char *name);int (*inode_listxattr) (struct dentry *dentry);
@@ -1760,20 +1762,24 @@ static inline void security_inode_delete security_ops->inode_delete (inode); } -static inline int security_inode_setxattr (struct dentry *dentry,char *name, +static inline int security_inode_setxattr (struct dentry *dentry, +struct vfsmount *mnt,char *name,void *value,size_t size,int flags) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_setxattr (dentry, name, value, size, flags); +return security_ops->inode_setxattr (dentry, mnt, name, value, size, + flags); } -static inline void security_inode_post_setxattr (struct dentry *dentry,char *name, -void *value,size_t size,int flags) +static inline void security_inode_post_setxattr (struct dentry *dentry, +struct vfsmount *mnt, +char *name,void *value, +size_t size,int flags) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return ; - security_ops->inode_post_setxattr (dentry, name, value, size, flags); + security_ops->inode_post_setxattr (dentry, mnt, name, value, size, flags); }static inline int security_inode_getxattr (struct dentry *dentry,char *name)
@@ -2467,14 +2473,18 @@ static inline int security_inode_getattrstatic inline void security_inode_delete (struct inode *inode) { } -static inline int security_inode_setxattr (struct dentry *dentry,char *name, +static inline int security_inode_setxattr (struct dentry *dentry, +struct vfsmount *mnt,char *name,void *value,size_t size,int flags) { -return cap_inode_setxattr (dentry, name, value, size, flags); +return cap_inode_setxattr (dentry, mnt, name, value, size, flags); } -static inline void security_inode_post_setxattr (struct dentry *dentry,char *name, -void *value,size_t size,int flags) +static inline void security_inode_post_setxattr (struct dentry *dentry, +struct vfsmount *mnt, +char *name, +void *value,size_t size, +int flags) { }static inline int security_inode_getxattr (struct dentry *dentry,char *name)
security/commoncap.c +2 -2
@@ -191,8 +191,8 @@ int cap_bprm_secureexec (struct linux_bi current->egid != current->gid); } -int cap_inode_setxattr (struct dentry *dentry,char *name,void *value, -size_t size,int flags) +int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, +void *value,size_t size,int flags) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
security/dummy.c +6 -3
@@ -350,8 +350,9 @@ static void dummy_inode_delete (struct ireturn ; } -static int dummy_inode_setxattr (struct dentry *dentry,char *name,void *value, -size_t size,int flags) +static int dummy_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name,void *value,size_t size, +int flags) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
@@ -360,7 +361,9 @@ static int dummy_inode_setxattr (structreturn 0 ; } -static void dummy_inode_post_setxattr (struct dentry *dentry,char *name,void *value, +static void dummy_inode_post_setxattr (struct dentry *dentry, +struct vfsmount *mnt, +char *name,void *value,size_t size,int flags) { }
security/selinux/hooks.c +6 -2
@@ -2307,7 +2307,9 @@ static int selinux_inode_getattr(structreturn dentry_has_perm (current, mnt, dentry,FILE__GETATTR ); } -static int selinux_inode_setxattr (struct dentry *dentry,char *name,void *value,size_t size,int flags) +static int selinux_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name,void *value,size_t size, +int flags) {struct task_security_struct *tsec = current->security;struct inode *inode = dentry->d_inode;
@@ -2367,7 +2369,9 @@ static int selinux_inode_setxattr(struct &ad); } -static void selinux_inode_post_setxattr (struct dentry *dentry,char *name, +static void selinux_inode_post_setxattr (struct dentry *dentry, +struct vfsmount *mnt, +char *name,void *value,size_t size,int flags) {struct inode *inode = dentry->d_inode;
PATCH 25/41 Add a struct vfsmount parameter to vfs_getxattr()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/nfsd/nfs4xdr.c | 2 +- fs/nfsd/vfs.c | 21 ++++++++++++--------- fs/xattr.c | 14 ++++++++------ include/linux/nfsd/nfsd.h | 3 ++- include/linux/xattr.h | 3 ++- 5 files changed, 25 insertions(+), 18 deletions(-)
fs/nfsd/nfs4xdr.c +1 -1
@@ -1470,7 +1470,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s }if (bmval0 & (FATTR4_WORD0_ACL |FATTR4_WORD0_ACLSUPPORT |FATTR4_WORD0_SUPPORTED_ATTRS )) { - err =nfsd4_get_nfs4_acl (rqstp, dentry, &acl); + err =nfsd4_get_nfs4_acl (rqstp, dentry, exp->ex_mnt, &acl); aclsupport = (err ==0 );if (bmval0 &FATTR4_WORD0_ACL ) {if (err == -EOPNOTSUPP )
fs/nfsd/vfs.c +12 -9
@@ -378,11 +378,12 @@ out_nfserr:#if defined(CONFIG_NFSD_V2_ACL ) || \ defined(CONFIG_NFSD_V3_ACL ) || \ defined(CONFIG_NFSD_V4 ) -static ssize_t nfsd_getxattr (struct dentry *dentry,char *key,void **buf) +static ssize_t nfsd_getxattr (struct dentry *dentry,struct vfsmount *mnt, +char *key,void **buf) {ssize_t buflen; - buflen =vfs_getxattr (dentry, key,NULL ,0 ); + buflen =vfs_getxattr (dentry, mnt, key,NULL ,0 );if (buflen <=0 )return buflen;
@@ -390,7 +391,7 @@ static ssize_t nfsd_getxattr(struct dentif (!*buf)return -ENOMEM ; - return vfs_getxattr(dentry, key, *buf, buflen); + return vfs_getxattr(dentry, mnt, key, *buf, buflen); }#endif
@@ -479,13 +480,13 @@ out_nfserr: }static struct posix_acl * -_get_posix_acl (struct dentry *dentry,char *key) +_get_posix_acl (struct dentry *dentry,struct vfsmount *mnt,char *key) {void *buf =NULL ;struct posix_acl *pacl =NULL ;int buflen; - buflen =nfsd_getxattr (dentry, key, &buf); + buflen =nfsd_getxattr (dentry, mnt, key, &buf);if (!buflen) buflen = -ENODATA ;if (buflen <=0 )
@@ -497,14 +498,15 @@ _get_posix_acl(struct dentry *dentry, ch }int -nfsd4_get_nfs4_acl (struct svc_rqst *rqstp,struct dentry *dentry,struct nfs4_acl **acl) +nfsd4_get_nfs4_acl (struct svc_rqst *rqstp,struct dentry *dentry, +struct vfsmount *mnt,struct nfs4_acl **acl) {struct inode *inode = dentry->d_inode;int error =0 ;struct posix_acl *pacl =NULL , *dpacl =NULL ;unsigned int flags =0 ; - pacl =_get_posix_acl (dentry,POSIX_ACL_XATTR_ACCESS ); + pacl =_get_posix_acl (dentry, mnt,POSIX_ACL_XATTR_ACCESS );if (IS_ERR (pacl) &&PTR_ERR (pacl) == -ENODATA ) pacl =posix_acl_from_mode (inode->i_mode,GFP_KERNEL );if (IS_ERR (pacl)) {
@@ -514,7 +516,7 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqst }if (S_ISDIR (inode->i_mode)) { - dpacl =_get_posix_acl (dentry,POSIX_ACL_XATTR_DEFAULT ); + dpacl =_get_posix_acl (dentry, mnt,POSIX_ACL_XATTR_DEFAULT );if (IS_ERR (dpacl) &&PTR_ERR (dpacl) == -ENODATA ) dpacl =NULL ;else if (IS_ERR (dpacl)) {
@@ -1938,7 +1940,8 @@ nfsd_get_posix_acl(struct svc_fh *fhp, ireturn ERR_PTR (-EOPNOTSUPP ); } - size =nfsd_getxattr (fhp->fh_dentry, name, &value); + size =nfsd_getxattr (fhp->fh_dentry, fhp->fh_export->ex_mnt, name, + &value);if (size <0 )return ERR_PTR (size);
fs/xattr.c +8 -6
@@ -107,7 +107,8 @@ out:EXPORT_SYMBOL_GPL (vfs_setxattr);ssize_t -vfs_getxattr (struct dentry *dentry,char *name,void *value,size_t size) +vfs_getxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, +void *value,size_t size) {struct inode *inode = dentry->d_inode;int error;
@@ -279,7 +280,8 @@ sys_fsetxattr(int fd, char __user *name,* Extended attribute GET operations */ static ssize_t -getxattr (struct dentry *d,char __user *name,void __user *value,size_t size) +getxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name, +void __user *value,size_t size) {ssize_t error;void *kvalue =NULL ;
@@ -299,7 +301,7 @@ getxattr(struct dentry *d, char __user *return -ENOMEM ; } - error =vfs_getxattr (d, kname, kvalue, size); + error =vfs_getxattr (dentry, mnt, kname, kvalue, size);if (error >0 ) {if (size &©_to_user (value, kvalue, error)) error = -EFAULT ;
@@ -322,7 +324,7 @@ sys_getxattr(char __user *path, char __u error =user_path_walk (path, &nd);if (error)return error; - error =getxattr (nd.dentry, name, value, size); + error =getxattr (nd.dentry, nd.mnt, name, value, size);path_release (&nd);return error; }
@@ -337,7 +339,7 @@ sys_lgetxattr(char __user *path, char __ error =user_path_walk_link (path, &nd);if (error)return error; - error =getxattr (nd.dentry, name, value, size); + error =getxattr (nd.dentry, nd.mnt, name, value, size);path_release (&nd);return error; }
@@ -351,7 +353,7 @@ sys_fgetxattr(int fd, char __user *name, f =fget (fd);if (!f)return error; - error =getxattr (f->f_path.dentry, name, value, size); + error =getxattr (f->f_path.dentry, f->f_path.mnt, name, value, size);fput (f);return error; }
include/linux/nfsd/nfsd.h +2 -1
@@ -77,7 +77,8 @@ __be32 nfsd_setattr(struct svc_rqst *,#ifdef CONFIG_NFSD_V4 __be32 nfsd4_set_nfs4_acl (struct svc_rqst *,struct svc_fh *,struct nfs4_acl *); -int nfsd4_get_nfs4_acl (struct svc_rqst *,struct dentry *,struct nfs4_acl **); +int nfsd4_get_nfs4_acl (struct svc_rqst *,struct dentry *, +struct vfsmount *mnt,struct nfs4_acl **);#endif /* CONFIG_NFSD_V4 */ __be32 nfsd_create(struct svc_rqst *, struct svc_fh *, char *name, int len, struct iattr *attrs,
include/linux/xattr.h +2 -1
@@ -46,7 +46,8 @@ struct xattr_handler { size_t size, int flags); }; -ssize_t vfs_getxattr (struct dentry *,char *,void *,size_t ); +ssize_t vfs_getxattr (struct dentry *,struct vfsmount *,char *,void *, +size_t );ssize_t vfs_listxattr (struct dentry *d,char *list,size_t size);int vfs_setxattr (struct dentry *,struct vfsmount *,char *,void *,size_t ,int );
PATCH 26/41 Pass struct vfsmount to the inode_getxattr LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/xattr.c | 2 +- include/linux/security.h | 13 ++++++++----- security/dummy.c | 3 ++- security/selinux/hooks.c | 3 ++- 4 files changed, 13 insertions(+), 8 deletions(-)
fs/xattr.c +1 -1
@@ -117,7 +117,7 @@ vfs_getxattr(struct dentry *dentry, struif (error)return error; - error =security_inode_getxattr (dentry, name); + error =security_inode_getxattr (dentry, mnt, name);if (error)return error;
include/linux/security.h +8 -5
@@ -391,7 +391,7 @@ struct request_sock;* @value identified by @name for @dentry and @mnt. * @inode_getxattr: * Check permission before obtaining the extended attributes -* identified by @name for @dentry. +* identified by @name for @dentry and @mnt. * Return 0 if permission is granted. * @inode_listxattr: * Check permission before obtaining the list of extended attribute
@@ -1248,7 +1248,8 @@ struct security_operations {struct vfsmount *mnt, char *name, void *value, size_t size, int flags); -int (*inode_getxattr) (struct dentry *dentry,char *name); +int (*inode_getxattr) (struct dentry *dentry,struct vfsmount *mnt, +char *name);int (*inode_listxattr) (struct dentry *dentry);int (*inode_removexattr) (struct dentry *dentry,char *name);const char *(*inode_xattr_getsuffix) (void );
@@ -1782,11 +1783,12 @@ static inline void security_inode_post_s security_ops->inode_post_setxattr (dentry, mnt, name, value, size, flags); } -static inline int security_inode_getxattr (struct dentry *dentry,char *name) +static inline int security_inode_getxattr (struct dentry *dentry, +struct vfsmount *mnt,char *name) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_getxattr (dentry, name); +return security_ops->inode_getxattr (dentry, mnt, name); }static inline int security_inode_listxattr (struct dentry *dentry)
@@ -2487,7 +2489,8 @@ static inline void security_inode_post_s int flags) { } -static inline int security_inode_getxattr (struct dentry *dentry,char *name) +static inline int security_inode_getxattr (struct dentry *dentry, +struct vfsmount *mnt,char *name) {return 0 ; }
security/dummy.c +2 -1
@@ -368,7 +368,8 @@ static void dummy_inode_post_setxattr (s { } -static int dummy_inode_getxattr (struct dentry *dentry,char *name) +static int dummy_inode_getxattr (struct dentry *dentry, +struct vfsmount *mnt,char *name) {return 0 ; }
security/selinux/hooks.c +2 -1
@@ -2395,7 +2395,8 @@ static void selinux_inode_post_setxattr(return ; } -static int selinux_inode_getxattr (struct dentry *dentry,char *name) +static int selinux_inode_getxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name) {return dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); }
PATCH 27/41 Add a struct vfsmount parameter to vfs_listxattr()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/xattr.c | 25 ++++++++++++++----------- include/linux/xattr.h | 3 ++- 2 files changed, 16 insertions(+), 12 deletions(-)
fs/xattr.c +14 -11
@@ -144,18 +144,20 @@ vfs_getxattr(struct dentry *dentry, struEXPORT_SYMBOL_GPL (vfs_getxattr);ssize_t -vfs_listxattr (struct dentry *d,char *list,size_t size) +vfs_listxattr (struct dentry *dentry,struct vfsmount *mnt,char *list, +size_t size) { +struct inode *inode = dentry->d_inode;ssize_t error; - error =security_inode_listxattr (d); + error =security_inode_listxattr (dentry);if (error)return error; error = -EOPNOTSUPP ; -if (d->d_inode->i_op && d->d_inode->i_op->listxattr) { - error = d->d_inode->i_op->listxattr (d, list, size); - }else { - error =security_inode_listsecurity (d->d_inode, list, size); +if (inode->i_op && inode->i_op->listxattr) + error = inode->i_op->listxattr (dentry, list, size); +else { + error =security_inode_listsecurity (inode, list, size);if (size && error > size) error = -ERANGE ; }
@@ -362,7 +364,8 @@ sys_fgetxattr(int fd, char __user *name,* Extended attribute LIST operations */ static ssize_t -listxattr (struct dentry *d,char __user *list,size_t size) +listxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *list, +size_t size) {ssize_t error;char *klist =NULL ;
@@ -375,7 +378,7 @@ listxattr(struct dentry *d, char __userreturn -ENOMEM ; } - error =vfs_listxattr (d, klist, size); + error =vfs_listxattr (dentry, mnt, klist, size);if (error >0 ) {if (size &©_to_user (list, klist, error)) error = -EFAULT ;
@@ -397,7 +400,7 @@ sys_listxattr(char __user *path, char __ error =user_path_walk (path, &nd);if (error)return error; - error =listxattr (nd.dentry, list, size); + error =listxattr (nd.dentry, nd.mnt, list, size);path_release (&nd);return error; }
@@ -411,7 +414,7 @@ sys_llistxattr(char __user *path, char _ error =user_path_walk_link (path, &nd);if (error)return error; - error =listxattr (nd.dentry, list, size); + error =listxattr (nd.dentry, nd.mnt, list, size);path_release (&nd);return error; }
@@ -425,7 +428,7 @@ sys_flistxattr(int fd, char __user *list f =fget (fd);if (!f)return error; - error =listxattr (f->f_path.dentry, list, size); + error =listxattr (f->f_path.dentry, f->f_path.mnt, list, size);fput (f);return error; }
include/linux/xattr.h +2 -1
@@ -48,7 +48,8 @@ struct xattr_handler {ssize_t vfs_getxattr (struct dentry *,struct vfsmount *,char *,void *,size_t ); -ssize_t vfs_listxattr (struct dentry *d,char *list,size_t size); +ssize_t vfs_listxattr (struct dentry *d,struct vfsmount *,char *list, +size_t size);int vfs_setxattr (struct dentry *,struct vfsmount *,char *,void *,size_t ,int );int vfs_removexattr (struct dentry *,char *);
PATCH 28/41 Pass struct vfsmount to the inode_listxattr LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/xattr.c | 2 +- include/linux/security.h | 12 +++++++----- security/dummy.c | 2 +- security/selinux/hooks.c | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-)
fs/xattr.c +1 -1
@@ -150,7 +150,7 @@ vfs_listxattr(struct dentry *dentry, strstruct inode *inode = dentry->d_inode;ssize_t error; - error =security_inode_listxattr (dentry); + error =security_inode_listxattr (dentry, mnt);if (error)return error; error = -EOPNOTSUPP ;
include/linux/security.h +7 -5
@@ -395,7 +395,7 @@ struct request_sock;* Return 0 if permission is granted. * @inode_listxattr: * Check permission before obtaining the list of extended attribute -* names for @dentry. +* names for @dentry and @mnt. * Return 0 if permission is granted. * @inode_removexattr: * Check permission before removing the extended attribute
@@ -1250,7 +1250,7 @@ struct security_operations { size_t size, int flags);int (*inode_getxattr) (struct dentry *dentry,struct vfsmount *mnt,char *name); -int (*inode_listxattr) (struct dentry *dentry); +int (*inode_listxattr) (struct dentry *dentry,struct vfsmount *mnt);int (*inode_removexattr) (struct dentry *dentry,char *name);const char *(*inode_xattr_getsuffix) (void );int (*inode_getsecurity)(const struct inode *inode,const char *name,void *buffer,size_t size,int err);
@@ -1791,11 +1791,12 @@ static inline int security_inode_getxattreturn security_ops->inode_getxattr (dentry, mnt, name); } -static inline int security_inode_listxattr (struct dentry *dentry) +static inline int security_inode_listxattr (struct dentry *dentry, +struct vfsmount *mnt) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_listxattr (dentry); +return security_ops->inode_listxattr (dentry, mnt); }static inline int security_inode_removexattr (struct dentry *dentry,char *name)
@@ -2495,7 +2496,8 @@ static inline int security_inode_getxattreturn 0 ; } -static inline int security_inode_listxattr (struct dentry *dentry) +static inline int security_inode_listxattr (struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/dummy.c +1 -1
@@ -374,7 +374,7 @@ static int dummy_inode_getxattr (structreturn 0 ; } -static int dummy_inode_listxattr (struct dentry *dentry) +static int dummy_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt) {return 0 ; }
security/selinux/hooks.c +1 -1
@@ -2401,7 +2401,7 @@ static int selinux_inode_getxattr (strucreturn dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); } -static int selinux_inode_listxattr (struct dentry *dentry) +static int selinux_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt) {return dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); }
PATCH 29/41 Add a struct vfsmount parameter to vfs_removexattr()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/nfsd/vfs.c | 3 ++- fs/xattr.c | 12 ++++++------ include/linux/xattr.h | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-)
fs/nfsd/vfs.c +2 -1
@@ -1992,7 +1992,8 @@ nfsd_set_posix_acl(struct svc_fh *fhp, iif (!S_ISDIR (inode->i_mode) && type ==ACL_TYPE_DEFAULT ) error =0 ;else { - error =vfs_removexattr (fhp->fh_dentry, name); + error =vfs_removexattr (fhp->fh_dentry, + fhp->fh_export->ex_mnt, name);if (error == -ENODATA ) error =0 ; }
fs/xattr.c +6 -6
@@ -166,7 +166,7 @@ vfs_listxattr(struct dentry *dentry, strEXPORT_SYMBOL_GPL (vfs_listxattr);int -vfs_removexattr (struct dentry *dentry,char *name) +vfs_removexattr (struct dentry *dentry,struct vfsmount *mnt,char *name) {struct inode *inode = dentry->d_inode;int error;
@@ -437,7 +437,7 @@ sys_flistxattr(int fd, char __user *list* Extended attribute REMOVE operations */ static long -removexattr (struct dentry *d,char __user *name) +removexattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name) {int error;char kname[XATTR_NAME_MAX +1 ];
@@ -448,7 +448,7 @@ removexattr(struct dentry *d, char __useif (error <0 )return error; -return vfs_removexattr (d, kname); +return vfs_removexattr (dentry, mnt, kname); }asmlinkage long
@@ -460,7 +460,7 @@ sys_removexattr(char __user *path, char error =user_path_walk (path, &nd);if (error)return error; - error =removexattr (nd.dentry, name); + error =removexattr (nd.dentry, nd.mnt, name);path_release (&nd);return error; }
@@ -474,7 +474,7 @@ sys_lremovexattr(char __user *path, char error =user_path_walk_link (path, &nd);if (error)return error; - error =removexattr (nd.dentry, name); + error =removexattr (nd.dentry, nd.mnt, name);path_release (&nd);return error; }
@@ -491,7 +491,7 @@ sys_fremovexattr(int fd, char __user *nareturn error; dentry = f->f_path.dentry;audit_inode (NULL , dentry->d_inode); - error =removexattr (dentry, name); + error =removexattr (dentry, f->f_path.mnt, name);fput (f);return error; }
include/linux/xattr.h +1 -1
@@ -52,7 +52,7 @@ ssize_t vfs_listxattr(struct dentry *d, size_t size);int vfs_setxattr (struct dentry *,struct vfsmount *,char *,void *,size_t ,int ); -int vfs_removexattr (struct dentry *,char *); +int vfs_removexattr (struct dentry *,struct vfsmount *,char *);ssize_t generic_getxattr (struct dentry *dentry,const char *name,void *buffer,size_t size);ssize_t generic_listxattr (struct dentry *dentry,char *buffer,size_t buffer_size);
PATCH 30/41 Pass struct vfsmount to the inode_removexattr LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/xattr.c | 2 +- include/linux/security.h | 15 +++++++++------ security/commoncap.c | 3 ++- security/dummy.c | 3 ++- security/selinux/hooks.c | 3 ++- 5 files changed, 16 insertions(+), 10 deletions(-)
fs/xattr.c +1 -1
@@ -178,7 +178,7 @@ vfs_removexattr(struct dentry *dentry, sif (error)return error; - error =security_inode_removexattr (dentry, name); + error =security_inode_removexattr (dentry, mnt, name);if (error)return error;
include/linux/security.h +9 -6
@@ -50,7 +50,7 @@ extern int cap_bprm_set_security (structextern void cap_bprm_apply_creds (struct linux_binprm *bprm,int unsafe);extern int cap_bprm_secureexec (struct linux_binprm *bprm);extern int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags); -extern int cap_inode_removexattr (struct dentry *dentry,char *name); +extern int cap_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt,char *name);extern int cap_task_post_setuid (uid_t old_ruid,uid_t old_euid,uid_t old_suid,int flags);extern void cap_task_reparent_to_init (struct task_struct *p);extern int cap_syslog (int type);
@@ -1251,7 +1251,8 @@ struct security_operations {int (*inode_getxattr) (struct dentry *dentry,struct vfsmount *mnt,char *name);int (*inode_listxattr) (struct dentry *dentry,struct vfsmount *mnt); -int (*inode_removexattr) (struct dentry *dentry,char *name); +int (*inode_removexattr) (struct dentry *dentry,struct vfsmount *mnt, +char *name);const char *(*inode_xattr_getsuffix) (void );int (*inode_getsecurity)(const struct inode *inode,const char *name,void *buffer,size_t size,int err);int (*inode_setsecurity)(struct inode *inode,const char *name,const void *value,size_t size,int flags);
@@ -1799,11 +1800,12 @@ static inline int security_inode_listxatreturn security_ops->inode_listxattr (dentry, mnt); } -static inline int security_inode_removexattr (struct dentry *dentry,char *name) +static inline int security_inode_removexattr (struct dentry *dentry, +struct vfsmount *mnt,char *name) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_removexattr (dentry, name); +return security_ops->inode_removexattr (dentry, mnt, name); }static inline const char *security_inode_xattr_getsuffix (void )
@@ -2502,9 +2504,10 @@ static inline int security_inode_listxatreturn 0 ; } -static inline int security_inode_removexattr (struct dentry *dentry,char *name) +static inline int security_inode_removexattr (struct dentry *dentry, +struct vfsmount *mnt,char *name) { -return cap_inode_removexattr (dentry, name); +return cap_inode_removexattr (dentry, mnt, name); }static inline const char *security_inode_xattr_getsuffix (void )
security/commoncap.c +2 -1
@@ -201,7 +201,8 @@ int cap_inode_setxattr(struct dentry *dereturn 0 ; } -int cap_inode_removexattr (struct dentry *dentry,char *name) +int cap_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, +char *name) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
security/dummy.c +2 -1
@@ -379,7 +379,8 @@ static int dummy_inode_listxattr (structreturn 0 ; } -static int dummy_inode_removexattr (struct dentry *dentry,char *name) +static int dummy_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, +char *name) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
security/selinux/hooks.c +2 -1
@@ -2406,7 +2406,8 @@ static int selinux_inode_listxattr (strureturn dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); } -static int selinux_inode_removexattr (struct dentry *dentry, char *name) +static int selinux_inode_removexattr (struct dentry *dentry, + struct vfsmount *mnt, char *name) { if (strcmp(name, XATTR_NAME_SELINUX)) { if (!strncmp(name, XATTR_SECURITY_PREFIX,
PATCH 31/41 Fix __d_path() for lazy unmounts and make it unambiguous; exclude unreachable mount points from /proc/mounts
In reply to [email protected]First, when d_path() hits a lazily unmounted mount point, it tries to prepend the name of the lazily unmounted dentry to the path name. It gets this wrong, and also overwrites the slash that separates the name from the following pathname component.
Second, it isn't always possible to tell from the __d_path result whether the specified root and rootmnt (i.e., the chroot) was reached: lazy unmounts of bind mounts will produce a path that does start with a non-slash so we can tell from that, but other lazy unmounts will produce a path that starts with a slash, just like "ordinary" paths.
Third, sys_getcwd() shouldn't return disconnected paths. The patch checks for that, and makes it fail with -ENOENT in that case.
Fourth, this now allows us to tell unreachable mount points from reachable ones when generating the /proc/mounts and /proc/$pid/mountstats files. Unreachable mount points are not interesting to processes (they can't get there, anyway), so we hide unreachable mounts. In particular, ordinary processes also will no longer see the rootfs mount (it is unreachable, after all). The rootfs mount point will still be reachable to processes like the initial initrd init process, and so those processes will continue to see this mount point.
The attached patch cleans up __d_path() to fix the bug with overlapping pathname components. It also adds a @fail_deleted argument, which allows to get rid of some of the mess in sys_getcwd(). We make sure that paths will only start with a slash if the path leads all the way up to the root. If the resulting path would otherwise be empty, we return "." instead so that some users of seq_path for files in /proc won't break.
The @fail_deleted argument allows sys_getcwd() to be simplified. Grabbing the dcache_lock can be moved into __d_path().
The @fail_deleted argument could be added to d_path() as well: this would allow callers to recognize deleted files without having to resort to the ambiguous check for the " (deleted)" string at the end of the pathnames. This is not currently done, but it might be worthwhile.
This patch also removes some code duplication between mounts_open() and mountstats_open().
--- fs/dcache.c | 157 +++++++++++++++++++++++++++++++-------------------------- fs/namespace.c | 23 +++++++- fs/proc/base.c | 52 +++++++----------- 3 files changed, 131 insertions(+), 101 deletions(-)
fs/dcache.c +88 -69
@@ -1732,52 +1732,51 @@ shouldnt_be_hashed: }/** -* d_path - return the path of a dentry +* __d_path - return the path of a dentry * @dentry: dentry to report * @vfsmnt: vfsmnt to which the dentry belongs * @root: root dentry * @rootmnt: vfsmnt to which the root dentry belongs * @buffer: buffer to return value in * @buflen: buffer length +* @fail_deleted: what to return for deleted files * -* Convert a dentry into an ASCII path name. If the entry has been deleted +* Convert a dentry into an ASCII path name. If the entry has been deleted, +* then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise, * the string " (deleted)" is appended. Note that this is ambiguous. * -* Returns the buffer or an error code if the path was too long. +* If @dentry is not connected to @root, the path returned will be relative +* (i.e., it will not start with a slash). * -* "buflen" should be positive. Caller holds the dcache_lock. +* Returns the buffer or an error code. */ -static char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, -struct dentry *root,struct vfsmount *rootmnt, -char *buffer,int buflen) -{ -char * end = buffer+buflen; -char * retval; -int namelen; +static char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, +struct dentry *root,struct vfsmount *rootmnt, +char *buffer,int buflen,int fail_deleted) +{ +int namelen, is_slash; + +if (buflen <2 ) +return ERR_PTR (-ENAMETOOLONG ); + buffer += --buflen; + *buffer ='\0' ; - *--end ='\0' ; - buflen--; +spin_lock (&dcache_lock);if (!IS_ROOT (dentry) &&d_unhashed (dentry)) { - buflen -=10 ; - end -=10 ; -if (buflen <0 ) +if (fail_deleted) { + buffer =ERR_PTR (-ENOENT ); + goto out; + } +if (buflen <10 ) goto Elong; -memcpy (end," (deleted)" ,10 ); + buflen -=10 ; + buffer -=10 ; +memcpy (buffer," (deleted)" ,10 ); } - -if (buflen <1 ) - goto Elong; -/* Get '/' right */ - retval = end-1 ; - *retval ='/' ; - -for (;;) { +while (dentry != root || vfsmnt != rootmnt) {struct dentry * parent; -if (dentry == root && vfsmnt == rootmnt) -break ;if (dentry == vfsmnt->mnt_root ||IS_ROOT (dentry)) { -/* Global root? */ spin_lock (&vfsmount_lock);if (vfsmnt->mnt_parent == vfsmnt) {spin_unlock (&vfsmount_lock);
@@ -1791,33 +1790,63 @@ static char * __d_path( struct dentry *d parent = dentry->d_parent;prefetch (parent); namelen = dentry->d_name.len; - buflen -= namelen +1 ; -if (buflen <0 ) +if (buflen < namelen +1 ) goto Elong; - end -= namelen; -memcpy (end, dentry->d_name.name, namelen); - *--end ='/' ; - retval = end; + buflen -= namelen +1 ; + buffer -= namelen; +memcpy (buffer, dentry->d_name.name, namelen); + *--buffer ='/' ; dentry = parent; } +/* Get '/' right. */ +if (*buffer !='/' ) + *--buffer ='/' ; -return retval; +out: +spin_unlock (&dcache_lock); +return buffer; global_root: +/* +* We went past the (vfsmount, dentry) we were looking for and have +* either hit a root dentry, a lazily unmounted dentry, an +* unconnected dentry, or the file is on a pseudo filesystem. +*/ namelen = dentry->d_name.len; - buflen -= namelen; -if (buflen <0 ) + is_slash = (namelen ==1 && *dentry->d_name.name =='/' ); +if (is_slash || (dentry->d_sb->s_flags &MS_NOUSER )) { +/* +* Make sure we won't return a pathname starting with '/'. +* +* Historically, we also glue together the root dentry and +* remaining name for pseudo filesystems like pipefs, which +* have the MS_NOUSER flag set. This results in pathnames +* like "pipe:[439336]". +*/ +if (*buffer =='/' ) { + buffer++; + buflen++; + } +if (is_slash) { +if (*buffer =='\0' ) + *--buffer ='.' ; + goto out; + } + } +if (buflen < namelen) goto Elong; - retval -= namelen-1 ;/* hit the slash */ -memcpy (retval, dentry->d_name.name, namelen); -return retval; + buffer -= namelen; +memcpy (buffer, dentry->d_name.name, namelen); + goto out; + Elong: -return ERR_PTR (-ENAMETOOLONG ); + buffer =ERR_PTR (-ENAMETOOLONG ); + goto out; }/* write full pathname into buffer and return start of pathname */ -char *d_path (struct dentry *dentry,struct vfsmount *vfsmnt, -char *buf,int buflen) +char *d_path (struct dentry *dentry,struct vfsmount *vfsmnt,char *buf, +int buflen) {char *res;struct vfsmount *rootmnt;
@@ -1827,9 +1856,7 @@ char * d_path(struct dentry *dentry, str rootmnt =mntget (current->fs->rootmnt); root =dget (current->fs->root);read_unlock (¤t->fs->lock); -spin_lock (&dcache_lock); - res =__d_path (dentry, vfsmnt, root, rootmnt, buf, buflen); -spin_unlock (&dcache_lock); + res =__d_path (dentry, vfsmnt, root, rootmnt, buf, buflen,0 );dput (root);mntput (rootmnt);return res;
@@ -1855,10 +1882,10 @@ char * d_path(struct dentry *dentry, str*/ asmlinkage long sys_getcwd (char __user *buf,unsigned long size) { -int error; +int error, len;struct vfsmount *pwdmnt, *rootmnt;struct dentry *pwd, *root; -char *page = (char *)__get_free_page (GFP_USER ); +char *page = (char *)__get_free_page (GFP_USER ), *cwd;if (!page)return -ENOMEM ;
@@ -1870,29 +1897,21 @@ asmlinkage long sys_getcwd(char __user * root =dget (current->fs->root);read_unlock (¤t->fs->lock); + cwd =__d_path (pwd, pwdmnt, root, rootmnt, page,PAGE_SIZE ,1 ); + error =PTR_ERR (cwd); +if (IS_ERR (cwd)) + goto out; error = -ENOENT ; -/* Has the current directory has been unlinked? */ -spin_lock (&dcache_lock); -if (pwd->d_parent == pwd || !d_unhashed (pwd)) { -unsigned long len; -char * cwd; - - cwd =__d_path (pwd, pwdmnt, root, rootmnt, page,PAGE_SIZE ); -spin_unlock (&dcache_lock); - - error =PTR_ERR (cwd); -if (IS_ERR (cwd)) - goto out; +if (*cwd !='/' ) + goto out; - error = -ERANGE ; - len =PAGE_SIZE + page - cwd; -if (len <= size) { - error = len; -if (copy_to_user (buf, cwd, len)) - error = -EFAULT ; - } - }else -spin_unlock (&dcache_lock); + error = -ERANGE ; + len =PAGE_SIZE + page - cwd; +if (len <= size) { + error = len; +if (copy_to_user (buf, cwd, len)) + error = -EFAULT ; + } out:dput (pwd);
fs/namespace.c +21 -2
@@ -348,8 +348,16 @@ static inline void mangle(struct seq_filseq_escape (m, s," \t\n\\" ); } +/* Keep in sync with fs/proc/base.c! */ +struct proc_mounts { +struct seq_file m; +void *page; +int event; +}; +static int show_vfsmnt (struct seq_file *m,void *v) { + void *page = container_of(m, struct proc_mounts, m)->page;struct vfsmount *mnt = v;int err =0 ; static struct proc_fs_info {
@@ -371,10 +379,15 @@ static int show_vfsmnt(struct seq_file * { 0, NULL } };struct proc_fs_info *fs_infop; +char *path; + + path =d_path (mnt->mnt_root, mnt, page,PAGE_SIZE ); +if (IS_ERR (path) || *path !='/' ) +return err;mangle (m, mnt->mnt_devname ? mnt->mnt_devname :"none" );seq_putc (m,' ' ); -seq_path (m, mnt, mnt->mnt_root," \t\n\\" ); +mangle (m, path);seq_putc (m,' ' );mangle (m, mnt->mnt_sb->s_type->name);seq_puts (m, mnt->mnt_sb->s_flags &MS_RDONLY ?" ro" :" rw" );
@@ -401,8 +414,14 @@ struct seq_operations mounts_op = {static int show_vfsstat (struct seq_file *m,void *v) { + void *page = container_of(m, struct proc_mounts, m)->page;struct vfsmount *mnt = v;int err =0 ; +char *path; + + path =d_path (mnt->mnt_root, mnt, page,PAGE_SIZE ); +if (IS_ERR (path) || *path !='/' ) +return err;/* error or path unreachable from chroot */ /* device */ if (mnt->mnt_devname) {
@@ -413,7 +432,7 @@ static int show_vfsstat(struct seq_file/* mount point */ seq_puts (m," mounted on " ); -seq_path (m, mnt, mnt->mnt_root," \t\n\\" ); +mangle (m, path);seq_putc (m,' ' );/* file system type */
fs/proc/base.c +22 -30
95 lines not on this page · raw message
Re: [AppArmor 31/41] Fix __d_path() for lazy unmounts and make it unambiguous; exclude unreachable mount points from /proc/mounts
Quoted 2 lines
Third,
sys_getcwd()shouldn't return disconnected paths. The patch checks for that, and makes it fail with-ENOENTin that case
That is a fairly significant and sudden change to the existing kernel/user interface.
Quoted 4 lines
Fourth, this now allows us to tell unreachable mount points from reachable ones when generating the
/proc/mountsand/proc/$pid/mountstatsfiles. Unreachable mount points are not interesting to processes (they can't get there, anyway), so we hide unreachable mounts. In particular, ordinary
This is untrue. The process can get there (via fd passing with another task) and the process can be producing output for the human operators, who most definitely need to know and see this stuff.
Quoted 3 lines
Signed-off-by Andreas Gruenbacher <[email protected]>Reviewed-by NeilBrown <[email protected]>Signed-off-by Andrew Morton <[email protected]>
I don't think this is fit to apply in current form. The hiding of mounts and mountstats is the wrong approach. The changes to getcwd behaviour bother me too as we are changing user space behaviour without warning.
The general idea of pushing some of the fail detect logic into __d_path() seems good though.
Alan
Re: [AppArmor 31/41] Fix __d_path() for lazy unmounts and make it unambiguous; exclude unreachable mount points from /proc/mounts
Quoted 1 line · Alan Cox
Quoted 1 line
Third,
sys_getcwd()shouldn't return disconnected paths. The patch checks
for
Quoted 4 lines
Quoted 1 line
that, and makes it fail with
-ENOENTin that caseThat is a fairly significant and sudden change to the existing kernel/user interface.
Well, this is not meant for 2.6.21. I hope it is possible to change it in early 2.6.22; otherwise if we can't fix mistakes from the past we are pretty doomed.
The problem with unreachable paths is that they are meaningless to the process -- the unreachable path does not really work as a path anymore, it could at best serve some informational value -- at worst it could lead to hard to track down misbehavior. ENOENT is a documented error code for ``directory has been unlinked''. The man page does not mention unreachable paths, but the documentation can easily be changed.
What's an unreachable path to one process may be a reachable path for another, or may not be reachable at all (such as the rootfs).
Quoted 7 lines
Quoted 4 lines
Fourth, this now allows us to tell unreachable mount points from reachable ones when generating the
/proc/mountsand/proc/$pid/mountstatsfiles. Unreachable mount points are not interesting to processes (they can't get there, anyway), so we hide unreachable mounts. In particular, ordinaryThis is untrue. The process can get there (via fd passing with another task)
Process can access file descriptors which are unreachable via path name just fine indeed, but those fds still don't have a valid path in the context of that process.
Mount points in /proc/mounts as well as paths returned by getcwd() are relative to the chroot of the querying process, and listing a mount point that is unreachable by that process just doesn't help anybody -- that particular process cannot do anything with them anyway.
Quoted 2 lines
and the process can be producing output for the human operators, who most definitely need to know and see this stuff.
We are only talking about mount points unreachable by a particular process; this does not mean that the mount point isn't reachable by other processes. Human operators can choose the context from which they are looking at /proc/mounts. If they are looking form the "real" root, the will see all mounts that any process can reach (in that namespace).
The rootfs is an example of a mount point that is not reachable by any process (after the initrd init process): listing it in /proc/mounts is totally pointless, for example -- the rootfs has no meaning to any of those processes. (From the point of view of the initrd init process the rootfs is reachable of course, and so it also shows up in /proc/mounts.)
Another context in which the current /proc/mounts doesn't make sense is chroot
environments: right now if you have proc mounted on /proc as usual as well as in a chroot, from the point of view of the "real" root, /proc/mounts will contain something like this (omitting all other mounts):
proc /proc proc rw 0 0
proc /chroot/proc proc rw 0 0
Quoted 1 line
From the point of view of the chroot, we get:
proc /proc proc rw 0 0
proc /proc proc rw 0 0
So there is no way to tell what's really mounted where (and how) from within the chroot. That's really quite broken.
Now it would be possible to use some other syntax to disambiguate unreachable mounts in /proc/mounts, like letting such paths start with "//", or removing leading slashes in those cases. At least removing leading slashes is quite likely to break applications though, and such a hack wouldn't really help anybody.
Whatever syntax we could come up with, those paths would be meaningless in the given context. They may be meaningful in other context -- exactly if the paths are reachable.
Quoted 3 lines
I don't think this is fit to apply in current form. The hiding of mounts and mountstats is the wrong approach. The changes to getcwd behaviour bother me too as we are changing user space behaviour without warning.
Which kind of warning do you have in mind?
Thanks,
Andreas
Re: [AppArmor 31/41] Fix __d_path() for lazy unmounts and make it unambiguous; exclude unreachable mount points from /proc/mounts
Quoted 6 lines
Quoted 2 lines
That is a fairly significant and sudden change to the existing kernel/user interface.
Well, this is not meant for 2.6.21. I hope it is possible to change it in early 2.6.22; otherwise if we can't fix mistakes from the past we are pretty doomed.
I don't believe the existing behaviour _IS_ a mistake.
Quoted 6 lines
Quoted 2 lines
This is untrue. The process can get there (via fd passing with another task)
Process can access file descriptors which are unreachable via path name just fine indeed, but those fds still don't have a valid path in the context of that process.
Which while problematic to your name based security is just fine to everything else.
Quoted 5 lines
We are only talking about mount points unreachable by a particular process; this does not mean that the mount point isn't reachable by other processes. Human operators can choose the context from which they are looking at
/proc/mounts. If they are looking form the "real" root, the will see all mounts that any process can reach (in that namespace).
Ok, providing the "real" root sees them all it isn't so bad, but to assume you can filter based upon what the task can see is dodgy as an assumption.
Re: [AppArmor 31/41] Fix __d_path() for lazy unmounts and make it unambiguous; exclude unreachable mount points from /proc/mounts
Quoted 1 line · Alan Cox
I don't believe the existing behaviour _IS_ a mistake.
So what would be the arguments why this behavior makes sense, other than legacy?
For /proc/mounts, one could argue that the admin might want to see everything, but then that's not actually true even today because /proc/mounts doesn't show lazyily unmounted stuff or mounts from other namespaces, so that everything is quite relative.
Along that line of argumentation, I would at least expect unambiguous output, to be able to tell which mountpoints are actually meaningful to the requesting process. It's not only human operators looking at /proc/mounts; applications care as well.
But after thinking about this issue quite a while, I really can't see what that should be good for. The current /proc/mounts interface is obviously broken; the chroot example should have demonstrated that. There are also unnecessary special cases because of that, such as having to filter out the rootfs entry when trying to figure out what's really mounted on /, and having to guess what's there and what's not in a particular context. The more complex mount scenarios will get, the more obviously broken the current /proc/mounts interface will become.
The getcwd() case is even stronger as the "see everything" argument makes even less sense there. I really can't see why the kernel should return processes fake pathnames. The process is explicitly asking for the current pathname to the working directory, it doesn't want to know what the pathname was at some previous point in time.
Quoted 6 lines
Quoted 3 lines
Process can access file descriptors which are unreachable via path name just fine indeed, but those fds still don't have a valid path in the context of that process.
Which while problematic to your name based security is just fine to everything else.
Actually, no. We could live fine with leaving getcwd() and /proc/mounts as ambiguous / weird / broken as they are right now. All it would take would be to reambiguate the result of the unambiguous __d_path(), which is really easy. Everything that cares about real pathnames would use the unambiguous version while the legacy interfaces would use the ambiguous version. But that really wouldn't make sense.
Quoted 3 lines
Ok, providing the "real" root sees them all it isn't so bad, but to assume you can filter based upon what the task can see is dodgy as an assumption.
Why?
Thanks,
Andreas
Re: [AppArmor 31/41] Fix __d_path() for lazy unmounts and make it unambiguous; exclude unreachable mount points from /proc/mounts
Quoted 4 lines
For
/proc/mounts, one could argue that the admin might want to see everything, but then that's not actually true even today because/proc/mountsdoesn't show lazyily unmounted stuff or mounts from other namespaces, so that everything is quite relative.
The current state is not good either
Quoted 5 lines
The
getcwd()case is even stronger as the "see everything" argument makes even less sense there. I really can't see why the kernel should return processes fake pathnames. The process is explicitly asking for the current pathname to the working directory, it doesn't want to know what the pathname was at some previous point in time.
Can you prove no existing application on the planet relies on the existing behaviour ? Actually more limited but sane as a test would be "Can you prove that the glibc behaviour visible to applications does not change"
Quoted 6 lines
Actually, no. We could live fine with leaving
getcwd()and/proc/mountsas ambiguous / weird / broken as they are right now. All it would take would be to reambiguate the result of the unambiguous__d_path(), which is really easy. Everything that cares about real pathnames would use the unambiguous version while the legacy interfaces would use the ambiguous version. But that really wouldn't make sense.
I disagree - firstly because of not breaking stuff, and secondly because it separates two discussions - merging AppArmor being one of them , and the correct behaviour for getcwd & /proc/mounts being the other.
Quoted 5 lines
Quoted 3 lines
Ok, providing the "real" root sees them all it isn't so bad, but to assume you can filter based upon what the task can see is dodgy as an assumption.
Why?
Because the viewing apparatus on the other side of the monitor is not operating in current directories/contexts.
Alan
cover Fixes to d_path: Respin
Quoted 4 lines · Alan Cox
Can you prove no existing application on the planet relies on the existing behaviour ? Actually more limited but sane as a test would be "Can you prove that the glibc behaviour visible to applications does not change"
As far as I can see, glibc internally looks at /proc/mounts (or else mtab) to find out where tmpfs is mounted for opening files there, and to look up filesystem information for statfs(), while accessing that path, too. Fstatfs() also looks into the same files, but it only matches by filesystem type, so this is only a very unreliable heuristic, anyway.
So judging from that, glibc users should be fine.
Quoted 3 lines
I disagree - firstly because of not breaking stuff, and secondly because it separates two discussions - merging AppArmor being one of them , and the correct behaviour for getcwd &
/proc/mountsbeing the other.
I agree with the separation of discussion argument. Here are patches that change getcwd() and /proc/mounts independent of the changes that AppArmor depends on.
Thanks for your feedback!
Andreas
Re: [d_path 0/7] Fixes to d_path: Respin
Quoted 7 lines
As far as I can see, glibc internally looks at
/proc/mounts(or else mtab) to find out where tmpfs is mounted for opening files there, and to look up filesystem information forstatfs(), while accessing that path, too.Fstatfs()also looks into the same files, but it only matches by filesystem type, so this is only a very unreliable heuristic, anyway.So judging from that, glibc users should be fine.
So glibc does use it and you will change behaviour
Quoted 7 lines
Quoted 3 lines
I disagree - firstly because of not breaking stuff, and secondly because it separates two discussions - merging AppArmor being one of them , and the correct behaviour for getcwd &
/proc/mountsbeing the other.I agree with the separation of discussion argument. Here are patches that change
getcwd()and/proc/mountsindependent of the changes that AppArmor depends on.
More useful would be AppArmour without the changes to getcwd and /proc/mounts
Re: [d_path 0/7] Fixes to d_path: Respin
Quoted 9 lines · Alan Cox
Quoted 7 lines
As far as I can see, glibc internally looks at
/proc/mounts(or else mtab) to find out where tmpfs is mounted for opening files there, and to look up filesystem information forstatfs(), while accessing that path, too.Fstatfs()also looks into the same files, but it only matches by filesystem type, so this is only a very unreliable heuristic, anyway.So judging from that, glibc users should be fine.
So glibc does use it and you will change behaviour
Not for statfs(), shm_open(), and sem_open().
Possibly for fstatfs(): fstatfs() has no way of looking up mount points per path name in /proc/mounts, and so it resorts to mapping from the numeric statfs->f_type to the filesystem name (e.g., "ext3"), looks up the first mount point with that name, and sets the statfs->f_flag flags based on that entry. This field may change from one arbitrary value to another.
Andreas
Re: [d_path 0/7] Fixes to d_path: Respin
Quoted 5 lines · Andreas Gruenbacher
Possibly for
fstatfs():fstatfs()has no way of looking up mount points per path name in/proc/mounts, and so it resorts to mapping from the numericstatfs->f_typeto the filesystem name (e.g., "ext3"), looks up the first mount point with that name, and sets thestatfs->f_flagflags based on that entry. This field may change from one arbitrary value to another.
What are you talking about? fstatfs is a syscall, we do nothing but copying values around at userlevel.
statvfs on the other hand does use /proc/mounts. And it most certainly does look at the mount point before looking at the filesystem type.
Re: [d_path 0/7] Fixes to d_path: Respin
Quoted 8 lines · Ulrich Drepper
Quoted 6 lines · Andreas Gruenbacher
Possibly for
fstatfs():fstatfs()has no way of looking up mount points per path name in/proc/mounts, and so it resorts to mapping from the numericstatfs->f_typeto the filesystem name (e.g., "ext3"), looks up the first mount point with that name, and sets thestatfs->f_flagflags based on that entry. This field may change from one arbitrary value to another.
Quoted 6 lines
What are you talking about? fstatfs is a syscall, we do nothing but copying values around at userlevel.
statvfs on the other hand does use
/proc/mounts. And it most certainly does look at the mount point before looking at the filesystem type.
Yes, that one, sorry. The values it obtains that way are not reliable.
Thanks,
Andreas
Re: [d_path 0/7] Fixes to d_path: Respin
Quoted 1 line · Andreas Gruenbacher
Yes, that one, sorry. The values it obtains that way are not reliable.
Why should the mount point info together with the filesystem type not be reliable? You're trying to find an excuse to break tings, that seems all there is.
Re: [d_path 0/7] Fixes to d_path: Respin
Quoted 5 lines · Ulrich Drepper
Quoted 1 line · Andreas Gruenbacher
Yes, that one, sorry. The values it obtains that way are not reliable.
Why should the mount point info together with the filesystem type not be reliable?
Ah ... I overlooked that fstatvfs() also checks the device number, not only the name, and so then it can find the right device from that.
So for how glibc uses /proc/mounts in fstatvfs(), hiding unreachable mount points from /proc/mounts wouldn't improve things. The heuristic already doesn't work for file descriptors from other namespaces, so it's already broken unfortunately. A more robust mechanism for glibc to use would be nice; not sure it would be worth it only for fstatvfs though.
The code also seems to stop at the first matching mount point. You can have the same device mounted on the same mount point multiple times but with different mount options, e.g.,
$ dd if=/dev/zero of=/var/tmp/ext2 bs=4096 count=16384 $ mkfs.ext2 -F /var/tmp/ext2 $ mount -o loop /var/tmp/ext2 /mnt $ mount -o loop,ro /var/tmp/ext2 /mnt $ tail -n 2 /proc/mounts /dev/loop0 /mnt ext2 rw 0 0 /dev/loop1 /mnt ext2 ro 0 0
The topmost mount point appears last in /proc/mounts, and so unless I am overlooking something else, that's another minor problem.
The third problem, as I already tried to argue several times now, is that the mount points path that /proc/mounts reports may or may not actually exist. That's a problem for glibc, and you should be one of the first to notice and acknowledge that. I gave a chroot example that showed that in the current implementation, you can get pretty random clashes between mounts; there are other cases with lazy unmounts as well.
Quoted 1 line
You're trying to find an excuse to break tings, that seems all there is.
Now what makes you think that??
Andreas
Re: [d_path 0/7] Fixes to d_path: Respin
Quoted 3 lines · Andreas Gruenbacher
The code also seems to stop at the first matching mount point. You can have the same device mounted on the same mount point multiple times but with different mount options, e.g., [...]
You can unfortunately do many stupid things. That's the user's problem. The point is that everything works fine in an environment which does not have such bogus mounts. Namespaces are also the problem of somebody else. The people who came up with them didn't think about the ramifications. None of these problems can be reasonably and reliably fixed with more support from the kernel.
Quoted 3 lines
I gave a chroot example that showed that in the current implementation, you can get pretty random clashes between mounts; there are other cases with lazy unmounts as well.
Irrelevant as well. If you create chroot problems it's your problem.
The fact is that if you have a normal setup the code works fine. All other situations cannot be handled with the current kernel interface.
This does not give anybody the right to say "since the code doesn't always work we can break it completely". That's completely unacceptable.
If you want to improve the situation, do it. Provide a solution for the problems we are having in implementing statvfs. Then we can talk about stopping to use /proc/mounts for statvfs and you can change it in a way which would harm the old implementation. That's *my* view, but I know there will be lots of people who would even object to that. The /proc filesystem is part of the kernel API and cannot be lightly broken.
Re: [d_path 0/7] Fixes to d_path: Respin
Quoted 4 lines
Quoted 3 lines
I gave a chroot example that showed that in the current implementation, you can get pretty random clashes between mounts; there are other cases with lazy unmounts as well.
Quoted 8 lines
Irrelevant as well. If you create chroot problems it's your problem.
The fact is that if you have a normal setup the code works fine. All other situations cannot be handled with the current kernel interface.
This does not give anybody the right to say "since the code doesn't always work we can break it completely". That's completely unacceptable.
I'm not sure I understand the situation completely. What exactly is broken in libc by removing unreachable mounts from /proc/mounts?
Is it the situation when
- file descriptor is opened
- process does chroot
- process does fstatvfs on file descriptor
?
In that case currently fstatvfs() _usually_ gives the correct results, but can give wrong results if mounts paths accidently clash in /proc/mounts?
Also isn't it the case, that fstatvfs() or statvfs() performed within the chroot could also give incorrect result for a _reachable_ mount if it clashes with an unreachable mount?
If this is the case, I would think that removing the unreachable mounts from /proc/mounts, would actually be fixing this second case, which is more likely to be used anyway.
BTW, this patch, or at least a predecessor is in -mm, and it very much feels the Right Thing(tm). The /proc/mounts under a chroot environment actually looks sane, instead of some random crap, that it was previously.
While we should make every effort to keep the kernel interfaces stable, this shouldn't prevent us from fixing bugs. And this one is clearly a bug, even if not a very serious one.
Miklos
Re: [d_path 0/7] Fixes to d_path: Respin
In reply to Ulrich DrepperQuoted 8 lines · Ulrich Drepper
Quoted 3 lines · Andreas Gruenbacher
The code also seems to stop at the first matching mount point. You can have the same device mounted on the same mount point multiple times but with different mount options, e.g., [...]
You can unfortunately do many stupid things. That's the user's problem. The point is that everything works fine in an environment which does not have such bogus mounts.
What I described is a supported feature, nothing more and nothing less. It's also relatively easy to handle this case correctly in glibc, e.g.,
sysdeps/unix/sysv/linux/internal_statvfs.c +4 -2
@@ -139,6 +139,7 @@ __statvfs_getflags (const char *name, inchar *cp = mntbuf.mnt_opts;char *opt; + result =0 ;while ((opt =strsep (&cp,"," )) !=NULL )if (strcmp (opt,"ro" ) ==0 ) result |=ST_RDONLY ;
@@ -157,9 +158,10 @@ __statvfs_getflags (const char *name, inelse if (strcmp (opt,"nodiratime" ) ==0 ) result |=ST_NODIRATIME ; -/* We can stop looking for more entries. */ success = true; -break ; +/* Don't stop looking: the same device may be mounted several +times with different options; in that case, the last entry +is the topmost mount. */ } }/* Maybe the kernel names for the filesystems changed or the
Quoted 5 lines
Quoted 3 lines
I gave a chroot example that showed that in the current implementation, you can get pretty random clashes between mounts; there are other cases with lazy unmounts as well.
Irrelevant as well. If you create chroot problems it's your problem.
There is no way to avoid these problems with chroots; it's not that anybody creates stupid problems on purpose.
The approach I'm proposing fixes these problems. It has a small disadvantage for statvfs / fstatvfs in some situations, which is due to the fact that the kernel doesn't offer a direct interface for querying the mount options of a file descriptor or path, and so glibc has to resort to messing with /proc/mounts. I don't see a nice way of fixing this without introducing [f]statvfs syscalls right now.
This is what POSIX says about statvfs / fstatvfs:
Quoted 2 lines
It is unspecified whether all members of the statvfs structure have meaningful values on all file systems.
In my opinion, the advantage of not reporting bogus pathnames in /proc/mounts by far outweighs the problems is sometimes causes for fstatvfs(). Anyone relying on the information obtained from statvfs / fstatvfs is making false assumptions anyway, and in "normal setups" as you called them, nothing changes for fstatvfs and statvfs.
Andreas
Re: [d_path 0/7] Fixes to d_path: Respin
Quoted 2 lines · Andreas Gruenbacher
What I described is a supported feature, nothing more and nothing less. It's also relatively easy to handle this case correctly in glibc, e.g., [...]
This is only useful if the requirement of an ordered /proc/mounts is part of the kernel ABI. I.e., until somebody specifies (in the sources, in kernel docs, I don't care where exactly) that the entries in /proc/mounts appear in the order in which the mounts happened this change is not better than the current code. I have never found such an assurance.
Quoted 3 lines
This is what POSIX says about statvfs / fstatvfs:
Quoted 2 lines
It is unspecified whether all members of the statvfs structure have meaningful values on all file systems.
Sure, just like POSIX in many other place leaves things unspecified. This does not change the fact that we do a good job now. You try to use this wording to excuse the fact that you want to make the results worse than they are now. That's *not* the intend of this wording.
Quoted 2 lines
In my opinion, the advantage of not reporting bogus pathnames in
/proc/mountsby far outweighs the problems is sometimes causes forfstatvfs().
Hell no. It is never acceptable to deliberately break compatibility. Effects of bugs on the ABI might change but this is not the case here. This is an interface which forever displayed the information in this form and it is correct and useful.
Re: [d_path 0/7] Fixes to d_path: Respin
In reply to Andreas GruenbacherQuoted 8 lines · Andreas Gruenbacher
Quoted 2 lines
It is unspecified whether all members of the statvfs structure have meaningful values on all file systems.
In my opinion, the advantage of not reporting bogus pathnames in
/proc/mountsby far outweighs the problems is sometimes causes forfstatvfs(). Anyone relying on the information obtained from statvfs / fstatvfs is making false assumptions anyway, and in "normal setups" as you called them, nothing changes for fstatvfs and statvfs.
So what about stopping the flaming here and implementing real statvfs/ fstatvfs syscalls instead of these horrible hacks glibc has to do currently? Using our kstatfs infrastructure that should be dirt simple. arch/sparc64/solaris/fs.c already has a template of a statvfs syscall for solaris, although we could probably improve a little on that.
Re: [d_path 0/7] Fixes to d_path: Respin
Quoted 3 lines · Christoph Hellwig
So what about stopping the flaming here and implementing real statvfs/ fstatvfs syscalls instead of these horrible hacks glibc has to do currently?
I could imagine two approaches to that: either add statvfs and fstatvfs syscalls, or use one of the reserved fields in struct statfs, and return the missing flags in there.
Here is a very rough shot at the latter (i386 and x86_64 only so far). One of the uglinesses with that is that all those flags could be cleared at the same time, and so we can't tell whether the field is used from the flags alone and need some other mechanism like an extra bit, or a successive field that is guaranteed to be nonzero. Not sure whether that's acceptable considering the alternative of adding two syscalls and duplicating all the ugly compatibility scaffolding.
struct statvfs also defines a f_favail field, the ``number of file serial numbers available to non-privileged process''. Glibc currently sets this to the same value as f_ffree, the ``total number of free file serial numbers''. We don't seem to have this information, so adding this field wouldn't help immediately.
Andreas
--- fs/compat.c | 13 +++++----- fs/ecryptfs/super.c | 5 +++- fs/nfsd/nfs4xdr.c | 2 - fs/nfsd/vfs.c | 2 - fs/open.c | 53 ++++++++++++++++++++++++++++++++++--------- fs/super.c | 2 - include/asm-generic/statfs.h | 9 ++++--- include/asm-x86_64/compat.h | 3 +- include/asm-x86_64/statfs.h | 9 ++++--- include/linux/fs.h | 2 - include/linux/statfs.h | 13 +++++++++- kernel/acct.c | 2 - 12 files changed, 84 insertions(+), 31 deletions(-)
fs/compat.c +7 -6
@@ -194,7 +194,7 @@ static int put_compat_statfs(struct comp__put_user (kbuf->f_fsid.val[0 ], &ubuf->f_fsid.val[0 ]) ||__put_user (kbuf->f_fsid.val[1 ], &ubuf->f_fsid.val[1 ]) ||__put_user (kbuf->f_frsize, &ubuf->f_frsize) || -__put_user (0 , &ubuf->f_spare[0 ]) || +__put_user (kbuf->f_flag, &ubuf->f_flag) ||__put_user (0 , &ubuf->f_spare[1 ]) ||__put_user (0 , &ubuf->f_spare[2 ]) || __put_user(0, &ubuf->f_spare[3]) ||
@@ -215,7 +215,7 @@ asmlinkage long compat_sys_statfs(const error =user_path_walk (path, &nd);if (!error) {struct kstatfs tmp; - error =vfs_statfs (nd.dentry, &tmp); + error =vfs_statfs (nd.dentry, nd.mnt, &tmp);if (!error) error =put_compat_statfs (buf, &tmp);path_release (&nd);
@@ -233,7 +233,7 @@ asmlinkage long compat_sys_fstatfs(unsig file =fget (fd);if (!file) goto out; - error =vfs_statfs (file->f_path.dentry, &tmp); + error =vfs_statfs (file->f_path.dentry, file->f_path.mnt, &tmp);if (!error) error =put_compat_statfs (buf, &tmp);fput (file);
@@ -267,7 +267,8 @@ static int put_compat_statfs64(struct co__put_user (kbuf->f_namelen, &ubuf->f_namelen) ||__put_user (kbuf->f_fsid.val[0 ], &ubuf->f_fsid.val[0 ]) ||__put_user (kbuf->f_fsid.val[1 ], &ubuf->f_fsid.val[1 ]) || - __put_user(kbuf->f_frsize, &ubuf->f_frsize)) +__put_user (kbuf->f_frsize, &ubuf->f_frsize) || + __put_user(kbuf->f_flag, &ubuf->f_flag)) return -EFAULT;return 0 ; }
@@ -283,7 +284,7 @@ asmlinkage long compat_sys_statfs64(cons error =user_path_walk (path, &nd);if (!error) {struct kstatfs tmp; - error =vfs_statfs (nd.dentry, &tmp); + error =vfs_statfs (nd.dentry, nd.mnt, &tmp);if (!error) error =put_compat_statfs64 (buf, &tmp);path_release (&nd);
@@ -304,7 +305,7 @@ asmlinkage long compat_sys_fstatfs64(uns file =fget (fd);if (!file) goto out; - error =vfs_statfs (file->f_path.dentry, &tmp); + error =vfs_statfs (file->f_path.dentry, file->f_path.mnt, &tmp);if (!error) error =put_compat_statfs64 (buf, &tmp);fput (file);
fs/ecryptfs/super.c +4 -1
@@ -119,7 +119,10 @@ static void ecryptfs_put_super(struct su*/ static int ecryptfs_statfs (struct dentry *dentry,struct kstatfs *buf) { -return vfs_statfs (ecryptfs_dentry_to_lower (dentry), buf); +struct dentry *lower_dentry =ecryptfs_dentry_to_lower (dentry); +struct vfsmount *lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry); + +return vfs_statfs (lower_dentry, lower_mnt, buf); }/**
fs/nfsd/nfs4xdr.c +1 -1
@@ -1457,7 +1457,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, sif ((bmval0 & (FATTR4_WORD0_FILES_FREE |FATTR4_WORD0_FILES_TOTAL )) || (bmval1 & (FATTR4_WORD1_SPACE_AVAIL |FATTR4_WORD1_SPACE_FREE |FATTR4_WORD1_SPACE_TOTAL ))) { - err =vfs_statfs (dentry, &statfs); + err =vfs_statfs (dentry, exp->ex_mnt, &statfs);if (err) goto out_nfserr; }
fs/nfsd/vfs.c +1 -1
@@ -1767,7 +1767,7 @@ __be32 nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) { __be32 err = fh_verify(rqstp, fhp, 0, MAY_NOP); -if (!err &&vfs_statfs (fhp->fh_dentry,stat)) +if (!err &&vfs_statfs (fhp->fh_dentry, fhp->fh_export->ex_mnt, stat)) err = nfserr_io;return err; }
fs/open.c +42 -11
120 lines not on this page · raw message
fs/super.c +1 -1
@@ -542,7 +542,7 @@ asmlinkage long sys_ustat(unsigned dev, s =user_get_super (new_decode_dev (dev));if (s ==NULL ) goto out; - err =vfs_statfs (s->s_root, &sbuf); + err =vfs_statfs (s->s_root,NULL , &sbuf);drop_super (s);if (err) goto out;
include/asm-generic/statfs.h +6 -3
@@ -17,7 +17,8 @@ struct statfs {__kernel_fsid_t f_fsid;__u32 f_namelen;__u32 f_frsize; -__u32 f_spare[5 ]; +__u32 f_flag; +__u32 f_spare[4 ]; };struct statfs64 {
@@ -31,7 +32,8 @@ struct statfs64 {__kernel_fsid_t f_fsid;__u32 f_namelen;__u32 f_frsize; -__u32 f_spare[5 ]; +__u32 f_flag; +__u32 f_spare[4 ]; };struct compat_statfs64 {
@@ -45,7 +47,8 @@ struct compat_statfs64 {__kernel_fsid_t f_fsid;__u32 f_namelen;__u32 f_frsize; - __u32 f_spare[5]; +__u32 f_flag; + __u32 f_spare[4]; };#endif
include/asm-x86_64/compat.h +2 -1
@@ -104,7 +104,8 @@ struct compat_statfs {compat_fsid_t f_fsid;int f_namelen;/* SunOS ignores this field. */ int f_frsize; -int f_spare[5 ]; +int f_flag; +int f_spare[4 ]; };#define COMPAT_RLIM_OLD_INFINITY 0x7fffffff
include/asm-x86_64/statfs.h +6 -3
@@ -24,7 +24,8 @@ struct statfs {__kernel_fsid_t f_fsid;long f_namelen;long f_frsize; -long f_spare[5 ]; +long f_flag; +long f_spare[4 ]; };struct statfs64 {
@@ -38,7 +39,8 @@ struct statfs64 {__kernel_fsid_t f_fsid;long f_namelen;long f_frsize; -long f_spare[5 ]; +long f_flag; +long f_spare[4 ]; };struct compat_statfs64 {
@@ -52,7 +54,8 @@ struct compat_statfs64 {__kernel_fsid_t f_fsid;__u32 f_namelen;__u32 f_frsize; -__u32 f_spare[5 ]; +__u32 f_flag; +__u32 f_spare[4 ]; } __attribute__((packed));#endif
include/linux/fs.h +1 -1
@@ -1408,7 +1408,7 @@ extern struct vfsmount *copy_tree(structextern void mnt_set_mountpoint (struct vfsmount *,struct dentry *,struct vfsmount *); -extern int vfs_statfs (struct dentry *,struct kstatfs *); +extern int vfs_statfs (struct dentry *,struct vfsmount *,struct kstatfs *);/* /sys/fs */ extern struct subsystem fs_subsys;
include/linux/statfs.h +12 -1
@@ -16,7 +16,18 @@ struct kstatfs {__kernel_fsid_t f_fsid;long f_namelen;long f_frsize; - long f_spare[5]; +unsigned long f_flag; + long f_spare[4]; }; +#define ST_RDONLY 1 +#define ST_NOSUID 2 +#define ST_NODEV 4 +#define ST_NOEXEC 8 +#define ST_SYNCHRONOUS 16 +#define ST_MANDLOCK 64 +#define ST_NOATIME 1024 +#define ST_NODIRATIME 2048 +#define ST_IN_USE (1 << 31) +#endif
kernel/acct.c +1 -1
@@ -118,7 +118,7 @@ static int check_free_space(struct filespin_unlock (&acct_globals.lock);/* May block */ -if (vfs_statfs (file->f_path.dentry, &sbuf)) +if (vfs_statfs (file->f_path.dentry, file->f_path.mnt, &sbuf))return res; suspend = sbuf.f_blocks *SUSPEND ; resume = sbuf.f_blocks *RESUME ;
PATCH 1/7 Fix __d_path() for lazy unmounts and make it unambiguous
In reply to Alan CoxFirst, when __d_path() hits a lazily unmounted mount point, it tries to prepend the name of the lazily unmounted dentry to the path name. It gets this wrong, and also overwrites the slash that separates the name from the following pathname component. This patch fixes that; if a process was in directory /foo/bar and /foo got lazily unmounted, the old result was ``foobar'' (note the missing slash), while the new result with this patch is ``foo/bar''.
Second, it isn't always possible to tell from the __d_path() result whether the specified root and rootmnt (i.e., the chroot) was reached. We need an unambiguous result for AppArmor at least though, so we make sure that paths will only start with a slash if the path leads all the way up to the root.
We also add a @fail_deleted argument, which allows to get rid of some of the mess in sys_getcwd().
This patch leaves getcwd() and d_path() as they were before for everything except for bind-mounted directories; for them, it reports ``/foo/bar'' instead of ``foobar'' in the example described above. Subsequent patches propose to make getcwd() fail instead of reporting unreachable paths like this one and hide unreachable mount points from /proc/mounts.
--- fs/dcache.c | 169 ++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 98 insertions(+), 71 deletions(-)
fs/dcache.c +98 -71
@@ -1732,52 +1732,51 @@ shouldnt_be_hashed: }/** -* d_path - return the path of a dentry +* __d_path - return the path of a dentry * @dentry: dentry to report * @vfsmnt: vfsmnt to which the dentry belongs * @root: root dentry * @rootmnt: vfsmnt to which the root dentry belongs * @buffer: buffer to return value in * @buflen: buffer length +* @fail_deleted: what to return for deleted files * -* Convert a dentry into an ASCII path name. If the entry has been deleted +* Convert a dentry into an ASCII path name. If the entry has been deleted, +* then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise, * the string " (deleted)" is appended. Note that this is ambiguous. * -* Returns the buffer or an error code if the path was too long. +* If @dentry is not connected to @root, the path returned will be relative +* (i.e., it will not start with a slash). * -* "buflen" should be positive. Caller holds the dcache_lock. +* Returns the buffer or an error code. */ -static char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, -struct dentry *root,struct vfsmount *rootmnt, -char *buffer,int buflen) -{ -char * end = buffer+buflen; -char * retval; -int namelen; +static char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, +struct dentry *root,struct vfsmount *rootmnt, +char *buffer,int buflen,int fail_deleted) +{ +int namelen, is_slash; + +if (buflen <2 ) +return ERR_PTR (-ENAMETOOLONG ); + buffer += --buflen; + *buffer ='\0' ; - *--end ='\0' ; - buflen--; +spin_lock (&dcache_lock);if (!IS_ROOT (dentry) &&d_unhashed (dentry)) { - buflen -=10 ; - end -=10 ; -if (buflen <0 ) +if (fail_deleted) { + buffer =ERR_PTR (-ENOENT ); + goto out; + } +if (buflen <10 ) goto Elong; -memcpy (end," (deleted)" ,10 ); + buflen -=10 ; + buffer -=10 ; +memcpy (buffer," (deleted)" ,10 ); } - -if (buflen <1 ) - goto Elong; -/* Get '/' right */ - retval = end-1 ; - *retval ='/' ; - -for (;;) { +while (dentry != root || vfsmnt != rootmnt) {struct dentry * parent; -if (dentry == root && vfsmnt == rootmnt) -break ;if (dentry == vfsmnt->mnt_root ||IS_ROOT (dentry)) { -/* Global root? */ spin_lock (&vfsmount_lock);if (vfsmnt->mnt_parent == vfsmnt) {spin_unlock (&vfsmount_lock);
@@ -1791,33 +1790,72 @@ static char * __d_path( struct dentry *d parent = dentry->d_parent;prefetch (parent); namelen = dentry->d_name.len; - buflen -= namelen +1 ; -if (buflen <0 ) +if (buflen < namelen +1 ) goto Elong; - end -= namelen; -memcpy (end, dentry->d_name.name, namelen); - *--end ='/' ; - retval = end; + buflen -= namelen +1 ; + buffer -= namelen; +memcpy (buffer, dentry->d_name.name, namelen); + *--buffer ='/' ; dentry = parent; } +/* Get '/' right. */ +if (*buffer !='/' ) + *--buffer ='/' ; -return retval; +out: +spin_unlock (&dcache_lock); +return buffer; global_root: +/* +* We went past the (vfsmount, dentry) we were looking for and have +* either hit a root dentry, a lazily unmounted dentry, an +* unconnected dentry, or the file is on a pseudo filesystem. +*/ namelen = dentry->d_name.len; - buflen -= namelen; -if (buflen <0 ) + is_slash = (namelen ==1 && *dentry->d_name.name =='/' ); +if (is_slash || (dentry->d_sb->s_flags &MS_NOUSER )) { +/* +* Make sure we won't return a pathname starting with '/'. +* +* Historically, we also glue together the root dentry and +* remaining name for pseudo filesystems like pipefs, which +* have the MS_NOUSER flag set. This results in pathnames +* like "pipe:[439336]". +*/ +if (*buffer =='/' ) { + buffer++; + buflen++; + } +if (is_slash) + goto out; + } +if (buflen < namelen) goto Elong; - retval -= namelen-1 ;/* hit the slash */ -memcpy (retval, dentry->d_name.name, namelen); -return retval; + buffer -= namelen; +memcpy (buffer, dentry->d_name.name, namelen); + goto out; + Elong: -return ERR_PTR (-ENAMETOOLONG ); + buffer =ERR_PTR (-ENAMETOOLONG ); + goto out; +} + +static char *__connect_d_path (char *path,char *buffer) +{ +if (!IS_ERR (path) && *path !='/' ) { +/* Pretend that disconnected paths are hanging off the root. */ +if (path == buffer) + path =ERR_PTR (-ENAMETOOLONG ); +else + *--path ='/' ; + } +return path; }/* write full pathname into buffer and return start of pathname */ -char *d_path (struct dentry *dentry,struct vfsmount *vfsmnt, -char *buf,int buflen) +char *d_path (struct dentry *dentry,struct vfsmount *vfsmnt,char *buf, +int buflen) {char *res;struct vfsmount *rootmnt;
@@ -1827,9 +1865,8 @@ char * d_path(struct dentry *dentry, str rootmnt =mntget (current->fs->rootmnt); root =dget (current->fs->root);read_unlock (¤t->fs->lock); -spin_lock (&dcache_lock); - res =__d_path (dentry, vfsmnt, root, rootmnt, buf, buflen); -spin_unlock (&dcache_lock); + res =__d_path (dentry, vfsmnt, root, rootmnt, buf, buflen,0 ); + res =__connect_d_path (res, buf);dput (root);mntput (rootmnt);return res;
@@ -1855,10 +1892,10 @@ char * d_path(struct dentry *dentry, str*/ asmlinkage long sys_getcwd (char __user *buf,unsigned long size) { -int error; +int error, len;struct vfsmount *pwdmnt, *rootmnt;struct dentry *pwd, *root; -char *page = (char *)__get_free_page (GFP_USER ); +char *page = (char *)__get_free_page (GFP_USER ), *cwd;if (!page)return -ENOMEM ;
@@ -1870,29 +1907,19 @@ asmlinkage long sys_getcwd(char __user * root =dget (current->fs->root);read_unlock (¤t->fs->lock); - error = -ENOENT ; -/* Has the current directory has been unlinked? */ -spin_lock (&dcache_lock); -if (pwd->d_parent == pwd || !d_unhashed (pwd)) { -unsigned long len; -char * cwd; - - cwd =__d_path (pwd, pwdmnt, root, rootmnt, page,PAGE_SIZE ); -spin_unlock (&dcache_lock); - - error =PTR_ERR (cwd); -if (IS_ERR (cwd)) - goto out; - - error = -ERANGE ; - len =PAGE_SIZE + page - cwd; -if (len <= size) { - error = len; -if (copy_to_user (buf, cwd, len)) - error = -EFAULT ; - } - }else -spin_unlock (&dcache_lock); + cwd =__d_path (pwd, pwdmnt, root, rootmnt, page,PAGE_SIZE ,1 ); + cwd =__connect_d_path (cwd, page); + error =PTR_ERR (cwd); +if (IS_ERR (cwd)) + goto out; + + error = -ERANGE ; + len =PAGE_SIZE + page - cwd; +if (len <= size) { + error = len; +if (copy_to_user (buf, cwd, len)) + error = -EFAULT ; + } out:dput (pwd);
Signature · 5 lines
Andreas Gruenbacher <[email protected]> SUSE Labs, SUSE LINUX Products GmbH GF: Markus Rex, HRB 16746 (AG Nuernberg) GPG: AF77 FAD1 1819 D442 400F 4BC8 409A 6903 4FDD EE02
Re: [d_path 1/7] Fix __d_path() for lazy unmounts and make it unambiguous
Quoted 6 lines · On Fri, 20 Apr 2007 01:23:04 +0200 Andreas Gruenbacher <[email protected]> wrote:
First, when
__d_path()hits a lazily unmounted mount point, it tries to prepend the name of the lazily unmounted dentry to the path name. It gets this wrong, and also overwrites the slash that separates the name from the following pathname component. This patch fixes that; if a process was in directory /foo/bar and /foo got lazily unmounted, the old result was ``foobar'' (note the missing slash), while the new result with this patch is ``foo/bar''.
ACK the fix
Quoted 3 lines
of ``foobar'' in the example described above. Subsequent patches propose to make
getcwd()fail instead of reporting unreachable paths like this one and hide unreachable mount points from/proc/mounts.
NAK that change of behaviour on the following patches.
PATCH 2/7 Make d_path() consistent across mount operations
In reply to Alan CoxThe path that __d_path() computes can become slightly inconsistent when it races with mount operations: it grabs the vfsmount_lock when traversing mount points, but immediately drops it again, only to re-grab it when it reaches the next mount point. The result is that the filename computed is not always consisent, and the file may never have had that name. (This is unlikely, but still possible.)
Fix this by grabbing the vfsmount_lock when the first mount point is reached, and holding onto it until the d_cache lookup is completed.
--- fs/dcache.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
fs/dcache.c +8 -6
@@ -1754,7 +1754,7 @@ static char *__d_path(struct dentry *den struct dentry *root, struct vfsmount *rootmnt, char *buffer, int buflen, int fail_deleted) { - int namelen, is_slash; + int namelen, is_slash, vfsmount_locked = 0;if (buflen <2 )return ERR_PTR (-ENAMETOOLONG );
@@ -1777,14 +1777,14 @@ static char *__d_path(struct dentry *denstruct dentry * parent;if (dentry == vfsmnt->mnt_root ||IS_ROOT (dentry)) { -spin_lock (&vfsmount_lock); -if (vfsmnt->mnt_parent == vfsmnt) { -spin_unlock (&vfsmount_lock); - goto global_root; +if (!vfsmount_locked) { +spin_lock (&vfsmount_lock); + vfsmount_locked =1 ; } +if (vfsmnt->mnt_parent == vfsmnt) + goto global_root; dentry = vfsmnt->mnt_mountpoint; vfsmnt = vfsmnt->mnt_parent; -spin_unlock (&vfsmount_lock);continue ; } parent = dentry->d_parent;
@@ -1803,6 +1803,8 @@ static char *__d_path(struct dentry *den *--buffer ='/' ; out: +if (vfsmount_locked) +spin_unlock (&vfsmount_lock);spin_unlock (&dcache_lock);return buffer;
Signature · 5 lines
Andreas Gruenbacher <[email protected]> SUSE Labs, SUSE LINUX Products GmbH GF: Markus Rex, HRB 16746 (AG Nuernberg) GPG: AF77 FAD1 1819 D442 400F 4BC8 409A 6903 4FDD EE02
PATCH 3/7 Add d_namespace_path() to compute namespace relative pathnames
In reply to Alan CoxIn AppArmor, we are interested in pathnames relative to the namespace root. This is the same as d_path() except for the root where the search ends. Add a function for computing the namespace-relative path.
--- fs/dcache.c | 6 +++--- fs/namespace.c | 27 +++++++++++++++++++++++++++ include/linux/dcache.h | 2 ++ include/linux/mount.h | 2 ++ 4 files changed, 34 insertions(+), 3 deletions(-)
fs/dcache.c +3 -3
@@ -1750,9 +1750,9 @@ shouldnt_be_hashed:* * Returns the buffer or an error code. */ -static char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, -struct dentry *root,struct vfsmount *rootmnt, -char *buffer,int buflen,int fail_deleted) +char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, +struct dentry *root,struct vfsmount *rootmnt, +char *buffer,int buflen,int fail_deleted) {int namelen, is_slash, vfsmount_locked =0 ;
fs/namespace.c +27 -0
@@ -1877,3 +1877,30 @@ void __put_mnt_ns(struct mnt_namespace *release_mounts (&umount_list);kfree (ns); } + +char *d_namespace_path (struct dentry *dentry,struct vfsmount *vfsmnt, +char *buf,int buflen) +{ +struct vfsmount *rootmnt, *nsrootmnt =NULL ; +struct dentry *root =NULL ; +char *res; + +read_lock (¤t->fs->lock); + rootmnt =mntget (current->fs->rootmnt); +read_unlock (¤t->fs->lock); +spin_lock (&vfsmount_lock); +if (rootmnt->mnt_ns) + nsrootmnt =mntget (rootmnt->mnt_ns->root); +spin_unlock (&vfsmount_lock); +mntput (rootmnt); +if (nsrootmnt) + root =dget (nsrootmnt->mnt_root); + res =__d_path (dentry, vfsmnt, root, nsrootmnt, buf, buflen,1 ); +dput (root); +mntput (nsrootmnt); +/* Prevent empty path for lazily unmounted filesystems. */ +if (!IS_ERR (res) && *res =='\0' ) + *--res ='.' ; +return res; +} +EXPORT_SYMBOL (d_namespace_path);
include/linux/dcache.h +2 -0
@@ -293,6 +293,8 @@ extern struct dentry * d_hash_and_lookup/* validate "insecure" dentry pointer */ extern int d_validate (struct dentry *,struct dentry *); +extern char *__d_path (struct dentry *,struct vfsmount *,struct dentry *, +struct vfsmount *,char *,int ,int );extern char *d_path (struct dentry *,struct vfsmount *,char *,int );/* Allocation counts.. */
include/linux/mount.h +2 -0
@@ -103,5 +103,7 @@ extern void shrink_submounts(struct vfsmextern spinlock_t vfsmount_lock;extern dev_t name_to_dev_t (char *name); +extern char *d_namespace_path (struct dentry *,struct vfsmount *,char *,int ); +#endif #endif /* _LINUX_MOUNT_H */
Signature · 5 lines
Andreas Gruenbacher <[email protected]> SUSE Labs, SUSE LINUX Products GmbH GF: Markus Rex, HRB 16746 (AG Nuernberg) GPG: AF77 FAD1 1819 D442 400F 4BC8 409A 6903 4FDD EE02
Re: [d_path 3/7] Add d_namespace_path() to compute namespace relative pathnames
Hello.
I've just returned from ELC2007 and
I haven't read all posts in this thread yet,
but I want to comment to this function.
Quoted 3 lines
In AppArmor, we are interested in pathnames relative to the namespace root. This is the same as
d_path()except for the root where the search ends. Add a function for computing the namespace-relative path.
Yes. You came to the same conclusion as TOMOYO Linux does.
http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/fs/realpath.c#L39
TOMOYO Linux uses pathnames relative to the namespace root. You do this using d_path()'s way, but there needs some extensions if you want to use d_namespace_path() for access control/auditing purpose.
In Linux, all characters other than NULL can be used in its pathname.
This means that you can't assume that whitespaces are delimiters.
For example, when you process entries in "Access ..... granted/rejected\n" format (where ..... is a pathname and \n is a carriage return, like "Access /bin/ls granted\n"), an entry "Access /bin/ls granted\nAccess /bin/cat granted\n" can be produced if ..... is "/bin/ls granted\nAccess /bin/cat".
Processing such entry will produce wrong result.
Also, you want wildcards (usually "*") when doing pathname comparison, but there are files that contains wildcards
(for example, "/usr/share/guile/1.6/ice-9/and-let*.scm" in CentOS 4.4). You need to escape so that you can tell whether "*" indicates a literal "*" or a wildcard.
Also, in non-English regions, characters that are out of ASCII printable range are included in its pathname (for example, files created via Samba from Windows client). Some programs can't handle characters that have MSB bit on,
so you may want to represent all characters without using MSB bit.
It may be OK if you use d_namespace_path() for processing a userland's configuration file, but it is not OK if you use it for processing a kernel's configuration file. The kernel has to be able to handle any characters.
So, you may want customized version of d_namespace_path()?
Re: [d_path 3/7] Add d_namespace_path() to compute namespace relative pathnames
Quoted 1 line · Tetsuo Handa
So, you may want customized version of
d_namespace_path()?
No. d_namespace_path() returns valid pathnames, just like d_path() does. Whatever quoting needed can be added to the resulting pathname.
Andreas
PATCH 4/7 Make getcwd() only return valid paths
In reply to Alan CoxMake getcwd() fail with -ENOENT if the current working directory is
disconnected: the process is not asking for some previous name of that directory but for the current name; returning a path meaningless in the context of that process makes no sense.
--- fs/dcache.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/dcache.c +3 -1
@@ -1910,10 +1910,12 @@ asmlinkage long sys_getcwd(char __user *read_unlock (¤t->fs->lock); cwd =__d_path (pwd, pwdmnt, root, rootmnt, page,PAGE_SIZE ,1 ); - cwd =__connect_d_path (cwd, page); error =PTR_ERR (cwd);if (IS_ERR (cwd)) goto out; + error = -ENOENT ; +if (*cwd !='/' ) + goto out; error = -ERANGE ; len =PAGE_SIZE + page - cwd;
Signature · 5 lines
Andreas Gruenbacher <[email protected]> SUSE Labs, SUSE LINUX Products GmbH GF: Markus Rex, HRB 16746 (AG Nuernberg) GPG: AF77 FAD1 1819 D442 400F 4BC8 409A 6903 4FDD EE02
PATCH 5/7 Remove duplicate proc code
In reply to Alan CoxRemove some duplicate code in generating the contents of /proc/mounts and /proc/$pid/mountstats.
--- fs/proc/base.c | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-)
fs/proc/base.c +15 -30
@@ -359,7 +359,8 @@ struct proc_mounts {int event; }; -static int mounts_open (struct inode *inode,struct file *file) +static int __mounts_open (struct inode *inode,struct file *file, +struct seq_operations *seq_ops) {struct task_struct *task =get_proc_task (inode);struct mnt_namespace *ns =NULL ;
@@ -382,7 +383,7 @@ static int mounts_open(struct inode *ino p =kmalloc (sizeof (struct proc_mounts ),GFP_KERNEL );if (p) { file->private_data = &p->m; - ret =seq_open (file, &mounts_op); + ret =seq_open (file, seq_ops);if (!ret) { p->m.private = ns; p->event = ns->event;
@@ -395,17 +396,25 @@ static int mounts_open(struct inode *inoreturn ret; } +static int mounts_open (struct inode *inode,struct file *file) +{ +return __mounts_open (inode, file, &mounts_op); +} +static int mounts_release (struct inode *inode,struct file *file) { -struct seq_file *m = file->private_data; -struct mnt_namespace *ns = m->private; +struct proc_mounts *p = + container_of(file->private_data, struct proc_mounts, m); +struct mnt_namespace *ns = p->m.private; +put_mnt_ns (ns);return seq_release (inode, file); }static unsigned mounts_poll (struct file *file,poll_table *wait) { -struct proc_mounts *p = file->private_data; +struct proc_mounts *p = + container_of(file->private_data, struct proc_mounts, m);struct mnt_namespace *ns = p->m.private;unsigned res =0 ;
@@ -432,31 +441,7 @@ static const struct file_operations procextern struct seq_operations mountstats_op;static int mountstats_open (struct inode *inode,struct file *file) { -int ret =seq_open (file, &mountstats_op); - -if (!ret) { -struct seq_file *m = file->private_data; -struct mnt_namespace *mnt_ns =NULL ; -struct task_struct *task =get_proc_task (inode); - -if (task) { -task_lock (task); -if (task->nsproxy) - mnt_ns = task->nsproxy->mnt_ns; -if (mnt_ns) -get_mnt_ns (mnt_ns); -task_unlock (task); -put_task_struct (task); - } - -if (mnt_ns) - m->private = mnt_ns; -else { -seq_release (inode, file); - ret = -EINVAL ; - } - } -return ret; +return __mounts_open (inode, file, &mountstats_op); }static const struct file_operations proc_mountstats_operations = {
Signature · 5 lines
Andreas Gruenbacher <[email protected]> SUSE Labs, SUSE LINUX Products GmbH GF: Markus Rex, HRB 16746 (AG Nuernberg) GPG: AF77 FAD1 1819 D442 400F 4BC8 409A 6903 4FDD EE02
PATCH 6/7 Filter out disconnected paths from /proc/mounts
In reply to Alan CoxUse d_path() instead of seq_path when generating /proc/mounts and /proc/$id/mountstats, reuse the same buffer for all mounts, and filter out disconnected paths.
This path has no net effect in itself because d_path() so far doesn't distinguish sconnected and disconnected paths yet. The next patch fixes that though; without this patch, the next patch would break /proc/mounts and /proc/$id/mountstats.
There is some disagreement what /proc/mounts should include. Currently it reports all mounts from the current namespace and doesn't include lazy unmounts. This leads to ambiguities with the rootfs (which is an internal mount irrelevant to user-space except in the initrd), and in chroots.
With this and the next patch, /proc/mounts only reports the mounts reachable for the current process, which makes a lot more sense IMO. If the current process is rooted in the namespace root (which it usually is), it will see all mounts except for the rootfs.
--- fs/namespace.c | 23 +++++++++++++++++++++-- fs/proc/base.c | 10 +++++++++- 2 files changed, 30 insertions(+), 3 deletions(-)
fs/namespace.c +21 -2
@@ -348,8 +348,16 @@ static inline void mangle(struct seq_filseq_escape (m, s," \t\n\\" ); } +/* Keep in sync with fs/proc/base.c! */ +struct proc_mounts { +struct seq_file m; +void *page; +int event; +}; +static int show_vfsmnt (struct seq_file *m,void *v) { + void *page = container_of(m, struct proc_mounts, m)->page;struct vfsmount *mnt = v;int err =0 ; static struct proc_fs_info {
@@ -371,10 +379,15 @@ static int show_vfsmnt(struct seq_file * { 0, NULL } };struct proc_fs_info *fs_infop; +char *path; + + path =d_path (mnt->mnt_root, mnt, page,PAGE_SIZE ); +if (IS_ERR (path) || *path !='/' ) +return err;mangle (m, mnt->mnt_devname ? mnt->mnt_devname :"none" );seq_putc (m,' ' ); -seq_path (m, mnt, mnt->mnt_root," \t\n\\" ); +mangle (m, path);seq_putc (m,' ' );mangle (m, mnt->mnt_sb->s_type->name);seq_puts (m, mnt->mnt_sb->s_flags &MS_RDONLY ?" ro" :" rw" );
@@ -401,8 +414,14 @@ struct seq_operations mounts_op = {static int show_vfsstat (struct seq_file *m,void *v) { + void *page = container_of(m, struct proc_mounts, m)->page;struct vfsmount *mnt = v;int err =0 ; +char *path; + + path =d_path (mnt->mnt_root, mnt, page,PAGE_SIZE ); +if (IS_ERR (path) || *path !='/' ) +return err;/* error or path unreachable from chroot */ /* device */ if (mnt->mnt_devname) {
@@ -413,7 +432,7 @@ static int show_vfsstat(struct seq_file/* mount point */ seq_puts (m," mounted on " ); -seq_path (m, mnt, mnt->mnt_root," \t\n\\" ); +mangle (m, path);seq_putc (m,' ' );/* file system type */
fs/proc/base.c +9 -1
@@ -354,8 +354,11 @@ static const struct inode_operations pro };extern struct seq_operations mounts_op; + +/* Keep in sync with fs/namespace.c! */ struct proc_mounts {struct seq_file m; +void *page;int event; };
@@ -383,12 +386,16 @@ static int __mounts_open(struct inode *i p =kmalloc (sizeof (struct proc_mounts ),GFP_KERNEL );if (p) { file->private_data = &p->m; - ret =seq_open (file, seq_ops); + p->page = (void *)__get_free_page (GFP_KERNEL ); +if (p->page) + ret =seq_open (file, seq_ops);if (!ret) { p->m.private = ns; p->event = ns->event;return 0 ; } +if (p->page) +free_page ((unsigned long )p->page);kfree (p); }put_mnt_ns (ns);
@@ -407,6 +414,7 @@ static int mounts_release(struct inode * container_of(file->private_data, struct proc_mounts, m);struct mnt_namespace *ns = p->m.private; +free_page ((unsigned long )p->page);put_mnt_ns (ns);return seq_release (inode, file); }
Signature · 5 lines
Andreas Gruenbacher <[email protected]> SUSE Labs, SUSE LINUX Products GmbH GF: Markus Rex, HRB 16746 (AG Nuernberg) GPG: AF77 FAD1 1819 D442 400F 4BC8 409A 6903 4FDD EE02
Re: [d_path 6/7] Filter out disconnected paths from /proc/mounts
Quoted 11 lines
There is some disagreement what
/proc/mountsshould include. Currently it reports all mounts from the current namespace and doesn't include lazy unmounts. This leads to ambiguities with the rootfs (which is an internal mount irrelevant to user-space except in the initrd), and in chroots.With this and the next patch,
/proc/mountsonly reports the mounts reachable for the current process, which makes a lot more sense IMO. If the current process is rooted in the namespace root (which it usually is), it will see all mounts except for the rootfs.Signed-off-by Andreas Gruenbacher <[email protected]>
This change in behaviour appears to be fine for glibc (except when trying to find the name of a file from a namespace we are not in, which wouldn't have come out right before either)
(but still NAK on the getcwd change)
PATCH 7/7 Distinguish between connected and disconnected paths in d_path()
In reply to Alan CoxChange d_path() so that it will never return a path starting with '/' if
the path doesn't lead up to the chroot directory. Also ensure that the
path returned never is the empty string: this would only occur with a lazily unmounted file system; return "." in that case instead.
--- fs/dcache.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-)
fs/dcache.c +4 -14
@@ -1829,8 +1829,11 @@ global_root: buffer++; buflen++; } -if (is_slash) +if (is_slash) { +if (*buffer =='\0' ) + *--buffer ='.' ; goto out; + } }if (buflen < namelen) goto Elong;
@@ -1843,18 +1846,6 @@ Elong: goto out; } -static char *__connect_d_path (char *path,char *buffer) -{ -if (!IS_ERR (path) && *path !='/' ) { -/* Pretend that disconnected paths are hanging off the root. */ -if (path == buffer) - path =ERR_PTR (-ENAMETOOLONG ); -else - *--path ='/' ; - } -return path; -} -/* write full pathname into buffer and return start of pathname */ char *d_path (struct dentry *dentry,struct vfsmount *vfsmnt,char *buf,int buflen)
@@ -1868,7 +1859,6 @@ char *d_path(struct dentry *dentry, stru root =dget (current->fs->root);read_unlock (¤t->fs->lock); res =__d_path (dentry, vfsmnt, root, rootmnt, buf, buflen,0 ); - res =__connect_d_path (res, buf);dput (root);mntput (rootmnt);return res;
Signature · 5 lines
Andreas Gruenbacher <[email protected]> SUSE Labs, SUSE LINUX Products GmbH GF: Markus Rex, HRB 16746 (AG Nuernberg) GPG: AF77 FAD1 1819 D442 400F 4BC8 409A 6903 4FDD EE02
Re: [AppArmor 31/41] Fix __d_path() for lazy unmounts and make it unambiguous; exclude unreachable mount points from /proc/mounts
In reply to Alan CoxQuoted 14 lines · Alan Cox
Quoted 8 lines
Quoted 2 lines
That is a fairly significant and sudden change to the existing kernel/user interface.
Well, this is not meant for 2.6.21. I hope it is possible to change it in
early 2.6.22; otherwise if we can't fix mistakes from the past we are pretty
doomed.I don't believe the existing behaviour _IS_ a mistake.
Quoted 3 lines
Quoted 2 lines
This is untrue. The process can get there (via fd passing with another task)
Quoted 8 lines
Quoted 5 lines
Process can access file descriptors which are unreachable via path name just
fine indeed, but those fds still don't have a valid path in the context of
that process.Which while problematic to your name based security is just fine to everything else.
The realisation that I feel needs to be made is that fd passing is a legitimate transfer of authority, and attempting to block the usage of this authority (or block the transfer of this authority) would be extremely bad.
One thing however that does need some major attention is the amounth of and the scope of the authority that is being transfered with directory file handles.
PATCH 32/41 Make d_path() consistent across mount operations
In reply to [email protected]Right now, the path that __d_path() computes can become slightly inconsistent when it races with mount operations: it grabs the vfsmount_lock when traversing mount points, but immediately drops it again, only to re-grab it when it reaches the next mount point. The result is that the filename computed is not always consisent, and the file may never have had that name. (This is unlikely, but still possible.)
We can easily fix this by grabbing the vfsmount_lock when the first mount point is reached, and holding onto it until the d_cache lookup is completed.
--- fs/dcache.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
fs/dcache.c +8 -6
@@ -1754,7 +1754,7 @@ static char *__d_path(struct dentry *den struct dentry *root, struct vfsmount *rootmnt, char *buffer, int buflen, int fail_deleted) { - int namelen, is_slash; + int namelen, is_slash, vfsmount_locked = 0;if (buflen <2 )return ERR_PTR (-ENAMETOOLONG );
@@ -1777,14 +1777,14 @@ static char *__d_path(struct dentry *denstruct dentry * parent;if (dentry == vfsmnt->mnt_root ||IS_ROOT (dentry)) { -spin_lock (&vfsmount_lock); -if (vfsmnt->mnt_parent == vfsmnt) { -spin_unlock (&vfsmount_lock); - goto global_root; +if (!vfsmount_locked) { +spin_lock (&vfsmount_lock); + vfsmount_locked =1 ; } +if (vfsmnt->mnt_parent == vfsmnt) + goto global_root; dentry = vfsmnt->mnt_mountpoint; vfsmnt = vfsmnt->mnt_parent; -spin_unlock (&vfsmount_lock);continue ; } parent = dentry->d_parent;
@@ -1803,6 +1803,8 @@ static char *__d_path(struct dentry *den *--buffer ='/' ; out: +if (vfsmount_locked) +spin_unlock (&vfsmount_lock);spin_unlock (&dcache_lock);return buffer;
PATCH 33/41 Add d_namespace_path() to obtain namespace relative pathnames
In reply to [email protected]In AppArmor we are interested in pathnames relative to the namespace root. Except for the root where the search ends, this is the same as d_path(). Add d_namespace_path() for that.
internals.
--- fs/dcache.c | 6 +++--- fs/namespace.c | 22 ++++++++++++++++++++++ include/linux/dcache.h | 2 ++ include/linux/mount.h | 2 ++ 4 files changed, 29 insertions(+), 3 deletions(-)
fs/dcache.c +3 -3
@@ -1750,9 +1750,9 @@ shouldnt_be_hashed:* * Returns the buffer or an error code. */ -static char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, -struct dentry *root,struct vfsmount *rootmnt, -char *buffer,int buflen,int fail_deleted) +char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, +struct dentry *root,struct vfsmount *rootmnt, +char *buffer,int buflen,int fail_deleted) {int namelen, is_slash, vfsmount_locked =0 ;
fs/namespace.c +22 -0
@@ -1896,3 +1896,25 @@ void __put_mnt_ns(struct mnt_namespace *release_mounts (&umount_list);kfree (ns); } + +char *d_namespace_path (struct dentry *dentry,struct vfsmount *vfsmnt, +char *buf,int buflen) +{ +char *res; +struct vfsmount *rootmnt, *nsrootmnt; +struct dentry *root; + +read_lock (¤t->fs->lock); + rootmnt =mntget (current->fs->rootmnt); +read_unlock (¤t->fs->lock); +spin_lock (&vfsmount_lock); + nsrootmnt =mntget (rootmnt->mnt_ns->root); + root =dget (nsrootmnt->mnt_root); +spin_unlock (&vfsmount_lock); +mntput (rootmnt); + res =__d_path (dentry, vfsmnt, root, nsrootmnt, buf, buflen,1 ); +dput (root); +mntput (nsrootmnt); +return res; +} +EXPORT_SYMBOL (d_namespace_path);
include/linux/dcache.h +2 -0
@@ -293,6 +293,8 @@ extern struct dentry * d_hash_and_lookup/* validate "insecure" dentry pointer */ extern int d_validate (struct dentry *,struct dentry *); +extern char *__d_path (struct dentry *,struct vfsmount *,struct dentry *, +struct vfsmount *,char *,int ,int );extern char *d_path (struct dentry *,struct vfsmount *,char *,int );/* Allocation counts.. */
include/linux/mount.h +2 -0
@@ -103,5 +103,7 @@ extern void shrink_submounts(struct vfsmextern spinlock_t vfsmount_lock;extern dev_t name_to_dev_t (char *name); +extern char *d_namespace_path (struct dentry *,struct vfsmount *,char *,int ); +#endif #endif /* _LINUX_MOUNT_H */
Re: [AppArmor 33/41] Add d_namespace_path() to obtain namespace relative pathnames
fs/namespace.c void __put_mnt_ns(struct mnt_namespace * · line 1911
Quoted 12 lines
fs/namespace.c +12
-0+char *d_namespace_path (struct dentry *dentry,struct vfsmount *vfsmnt, +char *buf,int buflen) +{ +char *res; +struct vfsmount *rootmnt, *nsrootmnt; +struct dentry *root; + +read_lock (¤t->fs->lock); + rootmnt =mntget (current->fs->rootmnt); +read_unlock (¤t->fs->lock); +spin_lock (&vfsmount_lock); + nsrootmnt =mntget (rootmnt->mnt_ns->root);
... and when somebody does umount -l on your chroot jail, you get NULL ->mnt_ns. Oops...
PATCH 34/41 Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames.
In reply to [email protected]Struct iattr already contains ia_file since commit cc4e69de from Miklos (which is related to commit befc649c). Use this to pass struct file down the setattr hooks. This allows LSMs to distinguish operations on file descriptors from operations on paths.
--- fs/nfsd/vfs.c | 11 ++++++----- fs/open.c | 16 +++++++++++----- include/linux/fs.h | 3 +++ 3 files changed, 20 insertions(+), 10 deletions(-)
fs/nfsd/vfs.c +6 -5
@@ -383,7 +383,7 @@ static ssize_t nfsd_getxattr(struct dent {ssize_t buflen; - buflen =vfs_getxattr (dentry, mnt, key,NULL ,0 ); + buflen =vfs_getxattr (dentry, mnt, key,NULL ,0 ,NULL );if (buflen <=0 )return buflen;
@@ -391,7 +391,7 @@ static ssize_t nfsd_getxattr(struct dentif (!*buf)return -ENOMEM ; - return vfs_getxattr(dentry, mnt, key, *buf, buflen); + return vfs_getxattr(dentry, mnt, key, *buf, buflen, NULL); }#endif
@@ -417,7 +417,7 @@ set_nfsv4_acl_one(struct dentry *dentry, goto out; } - error =vfs_setxattr (dentry, mnt, key, buf, len,0 ); + error =vfs_setxattr (dentry, mnt, key, buf, len,0 ,NULL ); out:kfree (buf);return error;
@@ -1987,13 +1987,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, iif (size) error =vfs_setxattr (fhp->fh_dentry, fhp->fh_export->ex_mnt, - name, value, size,0 ); + name, value, size,0 ,NULL );else {if (!S_ISDIR (inode->i_mode) && type ==ACL_TYPE_DEFAULT ) error =0 ;else { error =vfs_removexattr (fhp->fh_dentry, - fhp->fh_export->ex_mnt, name); + fhp->fh_export->ex_mnt, name, +NULL );if (error == -ENODATA ) error =0 ; }
fs/open.c +11 -5
@@ -520,6 +520,8 @@ asmlinkage long sys_fchmod(unsigned int mode = inode->i_mode; newattrs.ia_mode = (mode &S_IALLUGO ) | (inode->i_mode & ~S_IALLUGO ); newattrs.ia_valid =ATTR_MODE |ATTR_CTIME ; + newattrs.ia_valid |=ATTR_FILE ; + newattrs.ia_file = file; err =notify_change (dentry, file->f_path.mnt, &newattrs);mutex_unlock (&inode->i_mutex);
@@ -570,7 +572,7 @@ asmlinkage long sys_chmod(const char __u }static int chown_common (struct dentry * dentry,struct vfsmount *mnt, -uid_t user,gid_t group) +uid_t user,gid_t group,struct file *file) {struct inode * inode;int error;
@@ -598,6 +600,10 @@ static int chown_common(struct dentry * }if (!S_ISDIR (inode->i_mode)) newattrs.ia_valid |=ATTR_KILL_SUID |ATTR_KILL_SGID ; +if (file) { + newattrs.ia_file = file; + newattrs.ia_valid |=ATTR_FILE ; + }mutex_lock (&inode->i_mutex); error =notify_change (dentry, mnt, &newattrs);mutex_unlock (&inode->i_mutex);
@@ -613,7 +619,7 @@ asmlinkage long sys_chown(const char __u error =user_path_walk (filename, &nd);if (error) goto out; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );path_release (&nd); out:return error;
@@ -633,7 +639,7 @@ asmlinkage long sys_fchownat(int dfd, co error =__user_walk_fd (dfd, filename, follow, &nd);if (error) goto out; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );path_release (&nd); out:return error;
@@ -647,7 +653,7 @@ asmlinkage long sys_lchown(const char __ error =user_path_walk_link (filename, &nd);if (error) goto out; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );path_release (&nd); out:return error;
@@ -666,7 +672,7 @@ asmlinkage long sys_fchown(unsigned int dentry = file->f_path.dentry;audit_inode (NULL , dentry->d_inode); - error =chown_common (dentry, file->f_path.mnt, user, group); + error =chown_common (dentry, file->f_path.mnt, user, group, file);fput (file); out:return error;
include/linux/fs.h +3 -0
@@ -349,6 +349,9 @@ struct iattr {* Not an attribute, but an auxilary info for filesystems wanting to * implement an ftruncate() like method. NOTE: filesystem should * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL). +* +* The LSM hooks also use this to distinguish operations on a file +* descriptors from operations on pathnames. */ struct file *ia_file; };
PATCH 35/41 Pass struct file down the inode_*xattr security LSM hooks
In reply to [email protected]This allows LSMs to also distinguish between file descriptor and path access for the xattr operations. (The other relevant operations are covered by the setattr hook.)
--- fs/xattr.c | 58 ++++++++++++++++++++++++----------------------- include/linux/security.h | 37 +++++++++++++++++------------ include/linux/xattr.h | 8 +++--- security/commoncap.c | 4 +-- security/dummy.c | 10 ++++---- security/selinux/hooks.c | 10 ++++---- 6 files changed, 70 insertions(+), 57 deletions(-)
fs/xattr.c +30 -28
@@ -71,7 +71,7 @@ xattr_permission(struct inode *inode, coint vfs_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, -void *value,size_t size,int flags) +void *value,size_t size,int flags,struct file *file) {struct inode *inode = dentry->d_inode;int error;
@@ -81,7 +81,7 @@ vfs_setxattr(struct dentry *dentry, strureturn error;mutex_lock (&inode->i_mutex); - error =security_inode_setxattr (dentry, mnt, name, value, size, flags); + error =security_inode_setxattr (dentry, mnt, name, value, size, flags, file);if (error) goto out; error = -EOPNOTSUPP ;
@@ -108,7 +108,7 @@ EXPORT_SYMBOL_GPL(vfs_setxattr);ssize_t vfs_getxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, -void *value,size_t size) +void *value,size_t size,struct file *file) {struct inode *inode = dentry->d_inode;int error;
@@ -117,7 +117,7 @@ vfs_getxattr(struct dentry *dentry, struif (error)return error; - error =security_inode_getxattr (dentry, mnt, name); + error =security_inode_getxattr (dentry, mnt, name, file);if (error)return error;
@@ -145,12 +145,12 @@ EXPORT_SYMBOL_GPL(vfs_getxattr);ssize_t vfs_listxattr (struct dentry *dentry,struct vfsmount *mnt,char *list, -size_t size) +size_t size,struct file *file) {struct inode *inode = dentry->d_inode;ssize_t error; - error =security_inode_listxattr (dentry, mnt); + error =security_inode_listxattr (dentry, mnt, file);if (error)return error; error = -EOPNOTSUPP ;
@@ -166,7 +166,8 @@ vfs_listxattr(struct dentry *dentry, strEXPORT_SYMBOL_GPL (vfs_listxattr);int -vfs_removexattr (struct dentry *dentry,struct vfsmount *mnt,char *name) +vfs_removexattr (struct dentry *dentry,struct vfsmount *mnt,char *name, +struct file *file) {struct inode *inode = dentry->d_inode;int error;
@@ -178,7 +179,7 @@ vfs_removexattr(struct dentry *dentry, sif (error)return error; - error =security_inode_removexattr (dentry, mnt, name); + error =security_inode_removexattr (dentry, mnt, name, file);if (error)return error;
@@ -198,7 +199,7 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);*/ static long setxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name, -void __user *value,size_t size,int flags) +void __user *value,size_t size,int flags,struct file *file) {int error;void *kvalue =NULL ;
@@ -225,7 +226,7 @@ setxattr(struct dentry *dentry, struct v } } - error =vfs_setxattr (dentry, mnt, kname, kvalue, size, flags); + error =vfs_setxattr (dentry, mnt, kname, kvalue, size, flags, file);kfree (kvalue);return error; }
@@ -240,7 +241,7 @@ sys_setxattr(char __user *path, char __u error =user_path_walk (path, &nd);if (error)return error; - error =setxattr (nd.dentry, nd.mnt, name, value, size, flags); + error =setxattr (nd.dentry, nd.mnt, name, value, size, flags,NULL );path_release (&nd);return error; }
@@ -255,7 +256,7 @@ sys_lsetxattr(char __user *path, char __ error =user_path_walk_link (path, &nd);if (error)return error; - error =setxattr (nd.dentry, nd.mnt, name, value, size, flags); + error =setxattr (nd.dentry, nd.mnt, name, value, size, flags,NULL );path_release (&nd);return error; }
@@ -273,7 +274,7 @@ sys_fsetxattr(int fd, char __user *name,return error; dentry = f->f_path.dentry;audit_inode (NULL , dentry->d_inode); - error =setxattr (dentry, f->f_vfsmnt, name, value, size, flags); + error =setxattr (dentry, f->f_vfsmnt, name, value, size, flags, f);fput (f);return error; }
@@ -283,7 +284,7 @@ sys_fsetxattr(int fd, char __user *name,*/ static ssize_t getxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name, -void __user *value,size_t size) +void __user *value,size_t size,struct file *file) {ssize_t error;void *kvalue =NULL ;
@@ -303,7 +304,7 @@ getxattr(struct dentry *dentry, struct vreturn -ENOMEM ; } - error =vfs_getxattr (dentry, mnt, kname, kvalue, size); + error =vfs_getxattr (dentry, mnt, kname, kvalue, size, file);if (error >0 ) {if (size &©_to_user (value, kvalue, error)) error = -EFAULT ;
@@ -326,7 +327,7 @@ sys_getxattr(char __user *path, char __u error =user_path_walk (path, &nd);if (error)return error; - error =getxattr (nd.dentry, nd.mnt, name, value, size); + error =getxattr (nd.dentry, nd.mnt, name, value, size,NULL );path_release (&nd);return error; }
@@ -341,7 +342,7 @@ sys_lgetxattr(char __user *path, char __ error =user_path_walk_link (path, &nd);if (error)return error; - error =getxattr (nd.dentry, nd.mnt, name, value, size); + error =getxattr (nd.dentry, nd.mnt, name, value, size,NULL );path_release (&nd);return error; }
@@ -355,7 +356,7 @@ sys_fgetxattr(int fd, char __user *name, f =fget (fd);if (!f)return error; - error =getxattr (f->f_path.dentry, f->f_path.mnt, name, value, size); + error =getxattr (f->f_path.dentry, f->f_path.mnt, name, value, size, f);fput (f);return error; }
@@ -365,7 +366,7 @@ sys_fgetxattr(int fd, char __user *name,*/ static ssize_t listxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *list, -size_t size) +size_t size,struct file *file) {ssize_t error;char *klist =NULL ;
@@ -378,7 +379,7 @@ listxattr(struct dentry *dentry, structreturn -ENOMEM ; } - error =vfs_listxattr (dentry, mnt, klist, size); + error =vfs_listxattr (dentry, mnt, klist, size, file);if (error >0 ) {if (size &©_to_user (list, klist, error)) error = -EFAULT ;
@@ -400,7 +401,7 @@ sys_listxattr(char __user *path, char __ error =user_path_walk (path, &nd);if (error)return error; - error =listxattr (nd.dentry, nd.mnt, list, size); + error =listxattr (nd.dentry, nd.mnt, list, size,NULL );path_release (&nd);return error; }
@@ -414,7 +415,7 @@ sys_llistxattr(char __user *path, char _ error =user_path_walk_link (path, &nd);if (error)return error; - error =listxattr (nd.dentry, nd.mnt, list, size); + error =listxattr (nd.dentry, nd.mnt, list, size,NULL );path_release (&nd);return error; }
@@ -428,7 +429,7 @@ sys_flistxattr(int fd, char __user *list f =fget (fd);if (!f)return error; - error =listxattr (f->f_path.dentry, f->f_path.mnt, list, size); + error =listxattr (f->f_path.dentry, f->f_path.mnt, list, size, f);fput (f);return error; }
@@ -437,7 +438,8 @@ sys_flistxattr(int fd, char __user *list* Extended attribute REMOVE operations */ static long -removexattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name) +removexattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name, +struct file *file) {int error;char kname[XATTR_NAME_MAX +1 ];
@@ -448,7 +450,7 @@ removexattr(struct dentry *dentry, strucif (error <0 )return error; -return vfs_removexattr (dentry, mnt, kname); +return vfs_removexattr (dentry, mnt, kname, file); }asmlinkage long
@@ -460,7 +462,7 @@ sys_removexattr(char __user *path, char error =user_path_walk (path, &nd);if (error)return error; - error =removexattr (nd.dentry, nd.mnt, name); + error =removexattr (nd.dentry, nd.mnt, name,NULL );path_release (&nd);return error; }
@@ -474,7 +476,7 @@ sys_lremovexattr(char __user *path, char error =user_path_walk_link (path, &nd);if (error)return error; - error =removexattr (nd.dentry, nd.mnt, name); + error =removexattr (nd.dentry, nd.mnt, name,NULL );path_release (&nd);return error; }
@@ -491,7 +493,7 @@ sys_fremovexattr(int fd, char __user *nareturn error; dentry = f->f_path.dentry;audit_inode (NULL , dentry->d_inode); - error =removexattr (dentry, f->f_path.mnt, name); + error =removexattr (dentry, f->f_path.mnt, name, f);fput (f);return error; }
include/linux/security.h +22 -15
@@ -49,8 +49,8 @@ extern void cap_capset_set (struct task_extern int cap_bprm_set_security (struct linux_binprm *bprm);extern void cap_bprm_apply_creds (struct linux_binprm *bprm,int unsafe);extern int cap_bprm_secureexec (struct linux_binprm *bprm); -extern int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags); -extern int cap_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt,char *name); +extern int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags,struct file *file); +extern int cap_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt,char *name,struct file *file);extern int cap_task_post_setuid (uid_t old_ruid,uid_t old_euid,uid_t old_suid,int flags);extern void cap_task_reparent_to_init (struct task_struct *p);extern int cap_syslog (int type);
@@ -1243,16 +1243,18 @@ struct security_operations {int (*inode_getattr) (struct vfsmount *mnt,struct dentry *dentry);void (*inode_delete) (struct inode *inode);int (*inode_setxattr) (struct dentry *dentry,struct vfsmount *mnt, -char *name,void *value,size_t size,int flags); +char *name,void *value,size_t size,int flags, +struct file *file);void (*inode_post_setxattr) (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags);int (*inode_getxattr) (struct dentry *dentry,struct vfsmount *mnt, -char *name); -int (*inode_listxattr) (struct dentry *dentry,struct vfsmount *mnt); +char *name,struct file *file); +int (*inode_listxattr) (struct dentry *dentry,struct vfsmount *mnt, +struct file *file);int (*inode_removexattr) (struct dentry *dentry,struct vfsmount *mnt, -char *name); +char *name,struct file *file);const char *(*inode_xattr_getsuffix) (void );int (*inode_getsecurity)(const struct inode *inode,const char *name,void *buffer,size_t size,int err);int (*inode_setsecurity)(struct inode *inode,const char *name,const void *value,size_t size,int flags);
@@ -1766,12 +1768,13 @@ static inline void security_inode_deletestatic inline int security_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, -void *value,size_t size,int flags) +void *value,size_t size,int flags, +struct file *file) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ;return security_ops->inode_setxattr (dentry, mnt, name, value, size, - flags); + flags, file); } static inline void security_inode_post_setxattr (struct dentry *dentry,
@@ -1781,31 +1784,35 @@ static inline void security_inode_post_s {if (unlikely (IS_PRIVATE (dentry->d_inode)))return ; - security_ops->inode_post_setxattr (dentry, mnt, name, value, size, flags); + security_ops->inode_post_setxattr (dentry, mnt, name, value, size, + flags); }static inline int security_inode_getxattr (struct dentry *dentry, -struct vfsmount *mnt,char *name) +struct vfsmount *mnt,char *name, +struct file *file) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_getxattr (dentry, mnt, name); +return security_ops->inode_getxattr (dentry, mnt, name, file); }static inline int security_inode_listxattr (struct dentry *dentry, -struct vfsmount *mnt) +struct vfsmount *mnt, +struct file *file) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_listxattr (dentry, mnt); +return security_ops->inode_listxattr (dentry, mnt, file); }static inline int security_inode_removexattr (struct dentry *dentry, -struct vfsmount *mnt,char *name) +struct vfsmount *mnt,char *name, +struct file *file) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_removexattr (dentry, mnt, name); +return security_ops->inode_removexattr (dentry, mnt, name, file); }static inline const char *security_inode_xattr_getsuffix (void )
include/linux/xattr.h +4 -4
@@ -47,12 +47,12 @@ struct xattr_handler { };ssize_t vfs_getxattr (struct dentry *,struct vfsmount *,char *,void *, -size_t ); +size_t ,struct file *);ssize_t vfs_listxattr (struct dentry *d,struct vfsmount *,char *list, -size_t size); +size_t size,struct file *);int vfs_setxattr (struct dentry *,struct vfsmount *,char *,void *,size_t , -int ); -int vfs_removexattr (struct dentry *,struct vfsmount *,char *); +int ,struct file *); +int vfs_removexattr (struct dentry *,struct vfsmount *,char *,struct file *);ssize_t generic_getxattr (struct dentry *dentry,const char *name,void *buffer,size_t size);ssize_t generic_listxattr (struct dentry *dentry,char *buffer,size_t buffer_size);
security/commoncap.c +2 -2
@@ -192,7 +192,7 @@ int cap_bprm_secureexec (struct linux_bi }int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, -void *value,size_t size,int flags) +void *value,size_t size,int flags,struct file *file) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
@@ -202,7 +202,7 @@ int cap_inode_setxattr(struct dentry *de }int cap_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, -char *name) +char *name,struct file *file) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
security/dummy.c +6 -4
@@ -352,7 +352,7 @@ static void dummy_inode_delete (struct istatic int dummy_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size, -int flags) +int flags,struct file *file) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
@@ -369,18 +369,20 @@ static void dummy_inode_post_setxattr (s }static int dummy_inode_getxattr (struct dentry *dentry, -struct vfsmount *mnt,char *name) +struct vfsmount *mnt,char *name, +struct file *file) {return 0 ; } -static int dummy_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt) +static int dummy_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt, +struct file *file) {return 0 ; }static int dummy_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, -char *name) +char *name,struct file *file) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
security/selinux/hooks.c +6 -4
@@ -2309,7 +2309,7 @@ static int selinux_inode_getattr(structstatic int selinux_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size, -int flags) +int flags,struct file *file) {struct task_security_struct *tsec = current->security;struct inode *inode = dentry->d_inode;
@@ -2396,18 +2396,20 @@ static void selinux_inode_post_setxattr( }static int selinux_inode_getxattr (struct dentry *dentry,struct vfsmount *mnt, -char *name) +char *name,struct file *file) {return dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); } -static int selinux_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt) +static int selinux_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt, +struct file *file) {return dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); } static int selinux_inode_removexattr (struct dentry *dentry, - struct vfsmount *mnt, char *name) + struct vfsmount *mnt, char *name, + struct file *file) { if (strcmp(name, XATTR_NAME_SELINUX)) { if (!strncmp(name, XATTR_SECURITY_PREFIX,
PATCH 36/41 Export audit subsystem for use by modules
In reply to [email protected]Adds necessary export symbols for audit subsystem routines. Changes audit_log_vformat to be externally visible (analagous to vprintf) Patch is not in mainline -- pending AppArmor code submission to lkml
--- include/linux/audit.h | 5 +++++ kernel/audit.c | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-)
include/linux/audit.h +5 -0
@@ -112,6 +112,8 @@#define AUDIT_LAST_KERN_ANOM_MSG 1799#define AUDIT_ANOM_PROMISCUOUS 1700/* Device changed promiscuous mode */ +#define AUDIT_APPARMOR 1500/* AppArmor audit */ +#define AUDIT_KERNEL 2000/* Asynchronous audit record. NOT A REQUEST. */ /* Rule flags */
@@ -488,6 +490,9 @@ extern void audit_log(struct audit_ __attribute__((format(printf,4,5)));extern struct audit_buffer *audit_log_start (struct audit_context *ctx,gfp_t gfp_mask,int type); +extern void audit_log_vformat (struct audit_buffer *ab, +const char *fmt,va_list args) + __attribute__((format (printf,2 ,0 )));extern void audit_log_format (struct audit_buffer *ab,const char *fmt, ...) __attribute__((format (printf,2 ,3 )));
kernel/audit.c +4 -2
@@ -1054,8 +1054,7 @@ static inline int audit_expand(struct au* will be called a second time. Currently, we assume that a printk * can't format message larger than 1024 bytes, so we don't either. */ -static void audit_log_vformat (struct audit_buffer *ab,const char *fmt, -va_list args) +void audit_log_vformat (struct audit_buffer *ab,const char *fmt,va_list args) {int len, avail;struct sk_buff *skb;
@@ -1311,3 +1310,6 @@ EXPORT_SYMBOL(audit_log_start);EXPORT_SYMBOL (audit_log_end);EXPORT_SYMBOL (audit_log_format);EXPORT_SYMBOL (audit_log); +EXPORT_SYMBOL_GPL (audit_log_vformat); +EXPORT_SYMBOL_GPL (audit_log_untrustedstring); +EXPORT_SYMBOL_GPL (audit_log_d_path);
PATCH 37/41 AppArmor: Main Part
In reply to [email protected]The underlying functions by which the AppArmor LSM hooks are implemented.
--- security/apparmor/main.c | 1322 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1322 insertions(+)
security/apparmor/main.c new file +1322 -0
@@ -0,0 +1,1322 @@ +/* +* Copyright (C) 2002-2007 Novell/SUSE +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation, version 2 of the +* License. +* +* AppArmor Core +*/ + +#include <linux/security.h> +#include <linux/namei.h> +#include <linux/audit.h> +#include <linux/mount.h> +#include <linux/ptrace.h> + +#include "apparmor.h" + +#include "inline.h" + +/* +* Table of capability names: we generate it from capabilities.h. +*/ +static const char *capability_names[] = { +#include "capability_names.h" +}; + +/* NULL complain profile +* +* Used when in complain mode, to emit Permitting messages for non-existant +* profiles and hats. This is necessary because of selective mode, in which +* case we need a complain null_profile and enforce null_profile +* +* The null_complain_profile cannot be statically allocated, because it +* can be associated to files which keep their reference even if apparmor is +* unloaded +*/ +struct aa_profile *null_complain_profile; + +/** +* aa_taskattr_access +* @name: name of the file to check +* +* Check if name matches /proc/self/attr/current, with self resolved +* to the current pid. This file is the usermode iterface for +* changing one's hat. +*/ +static inline int aa_taskattr_access (const char *name) +{ +unsigned long pid; +char *end; + +if (strncmp (name,"/proc/" ,6 ) !=0 ) +return 0 ; + pid =simple_strtoul (name +6 , &end,10 ); +if (pid != current->pid) +return 0 ; +return strcmp (end,"/attr/current" ) ==0 ; +} + +static inline void aa_permerror2result (int perm_result,struct aa_audit *sa) +{ +if (perm_result ==0 ) {/* success */ + sa->result =1 ; + sa->error_code =0 ; + }else {/* -ve internal error code or +ve mask of denied perms */ + sa->result =0 ; + sa->error_code = perm_result; + } +} + +/** +* aa_file_denied - check for @mask access on a file +* @profile: profile to check against +* @name: pathname of file +* @mask: permission mask requested for file +* +* Return %0 on success, or else the permissions in @mask that the +* profile denies. +*/ +static int aa_file_denied (struct aa_profile *profile,const char *name, +int mask) +{ +int perms; + +/* Always allow write access to /proc/self/attr/current. */ +if (mask ==MAY_WRITE &&aa_taskattr_access (name)) +return 0 ; + + perms =aa_match (profile->file_rules, name); + +return (mask & ~perms); +} + +/** +* aa_link_denied - check for permission to link a file +* @profile: profile to check against +* @link: pathname of link being created +* @target: pathname of target to be linked to +* +* Return %0 on success, or else the permissions that the profile denies. +*/ +static int aa_link_denied (struct aa_profile *profile,const char *link, +const char *target) +{ +int l_mode, t_mode; + + l_mode =aa_match (profile->file_rules, link); + t_mode =aa_match (profile->file_rules, target); + +/** +* Link always requires 'l' on the link, a subset of the +* target's 'r', 'w', 'x', and 'm' permissions on the link, and +* if the link has 'x', an exact match of all the execute flags +* ('i', 'u', 'U', 'p', 'P'). +*/ +#define RWXM (MAY_READ | MAY_WRITE | MAY_EXEC | AA_EXEC_MMAP) +if ((l_mode &AA_MAY_LINK ) && + (l_mode &RWXM ) && !(l_mode & ~t_mode &RWXM ) && + (!(l_mode &MAY_EXEC ) || + ((l_mode &AA_EXEC_MODIFIERS ) == (t_mode &AA_EXEC_MODIFIERS ) && + (l_mode &AA_EXEC_UNSAFE ) == (t_mode &AA_EXEC_UNSAFE )))) +return 0 ; +#undef RWXM +/** +* FIXME: There currenly is no way to report which permissions +* we expect in t_mode, so linking could fail even after learning +* the required l_mode. +*/ +return AA_MAY_LINK ; +} + +/** +* aa_get_name - compute the pathname of a file +* @dentry: dentry of the file +* @mnt: vfsmount of the file +* @buffer: buffer that aa_get_name() allocated +* @check: AA_CHECK_DIR is set if the file is a directory +* +* Returns a pointer to the beginning of the pathname (which usually differs +* from the beginning of the buffer), or an error code. +* +* We need @check to indicate whether the file is a directory or not because +* the file may not yet exist, and so we cannot check the inode's file type. +*/ +static char *aa_get_name (struct dentry *dentry,struct vfsmount *mnt, +char **buffer,int check) +{ +char *name; +int is_dir, size =256 ; + + is_dir = (check &AA_CHECK_DIR ) ?1 :0 ; + +for (;;) { +char *buf =kmalloc (size,GFP_KERNEL ); +if (!buf) +return ERR_PTR (-ENOMEM ); + + name =d_namespace_path (dentry, mnt, buf, size - is_dir); +if (!IS_ERR (name)) { +if (name[0 ] !='/' ) { +/* +* This dentry is not connected to the +* namespace root -- reject access. +*/ +kfree (buf); +return ERR_PTR (-ENOENT ); + } +if (is_dir && name[1 ] !='\0' ) { +/* +* Append "/" to the pathname. The root +* directory is a special case; it already +* ends in slash. +*/ + buf[size -2 ] ='/' ; + buf[size -1 ] ='\0' ; + } + + *buffer = buf; +return name; + } +if (PTR_ERR (name) != -ENAMETOOLONG ) +return name; + +kfree (buf); + size <<=1 ; +if (size > apparmor_path_max) +return ERR_PTR (-ENAMETOOLONG ); + } +} + +static inline void aa_put_name_buffer (char *buffer) +{ +kfree (buffer); +} + +/** +* aa_perm_dentry - check if @profile allows @mask for a file +* @profile: profile to check against +* @dentry: dentry of the file +* @mnt: vfsmount o the file +* @sa: audit context +* @mask: requested profile permissions +* @check: kind of check to perform +* +* Returns 0 upon success, or else an error code. +* +* @check indicates the file type, and whether the file was accessed through +* an open file descriptor (AA_CHECK_FD) or not. +*/ +static int aa_perm_dentry (struct aa_profile *profile,struct dentry *dentry, +struct vfsmount *mnt,struct aa_audit *sa,int mask, +int check) +{ +char *buffer =NULL ; +int denied_mask, error; + + sa->name =aa_get_name (dentry, mnt, &buffer, check); + +if (IS_ERR (sa->name)) { +/* +* deleted files are given a pass on permission checks when +* accessed through a file descriptor. +*/ +if (PTR_ERR (sa->name) == -ENOENT && (check &AA_CHECK_FD )) + denied_mask =0 ; +else + denied_mask =PTR_ERR (sa->name); + sa->name =NULL ; + }else { + denied_mask =aa_file_denied (profile, sa->name, mask); + } + +aa_permerror2result (denied_mask, sa); + + error =aa_audit (profile, sa); + +aa_put_name_buffer (buffer); + +return error; +} + +/** +* attach_nullprofile - allocate and attach a null_profile hat to profile +* @profile: profile to attach a null_profile hat to. +* +* Return %0 (success) or error (-%ENOMEM) +*/ +int attach_nullprofile (struct aa_profile *profile) +{ +struct aa_profile *hat =NULL ; +char *hatname =NULL ; + + hat =alloc_aa_profile (); +if (!hat) + goto fail; +if (profile->flags.complain) + hatname =kstrdup ("null-complain-profile" ,GFP_KERNEL ); +else + hatname =kstrdup ("null-profile" ,GFP_KERNEL ); +if (!hatname) + goto fail; + + hat->flags.complain = profile->flags.complain; + hat->name = hatname; + hat->parent = profile; + + profile->null_profile = hat; + +return 0 ; + +fail: +kfree (hatname); +free_aa_profile (hat); + +return -ENOMEM ; +} + +/** +* alloc_null_complain_profile - Allocate the global null_complain_profile. +* +* Return %0 (success) or error (-%ENOMEM) +*/ +int alloc_null_complain_profile (void ) +{ + null_complain_profile =alloc_aa_profile (); +if (!null_complain_profile) + goto fail; + + null_complain_profile->name = +kstrdup ("null-complain-profile" ,GFP_KERNEL ); + +if (!null_complain_profile->name) + goto fail; + + null_complain_profile->flags.complain =1 ; +if (attach_nullprofile (null_complain_profile)) + goto fail; + +return 0 ; + +fail: +/* free_aa_profile is safe for freeing partially constructed objects */ +free_aa_profile (null_complain_profile); + null_complain_profile =NULL ; + +return -ENOMEM ; +} + +/** +* free_null_complain_profile - Free null profiles +*/ +void free_null_complain_profile (void ) +{ +aa_put_profile (null_complain_profile); + null_complain_profile =NULL ; +} + +/** +* aa_audit_message - Log a message to the audit subsystem +* @profile: profile to check against +* @gfp: allocation flags +* @flags: audit flags +* @fmt: varargs fmt +*/ +int aa_audit_message (struct aa_profile *profile,gfp_t gfp,int flags, +const char *fmt, ...) +{ +int ret; +struct aa_audit sa; + + sa.type =AA_AUDITTYPE_MSG ; + sa.name = fmt; +va_start (sa.vaval, fmt); + sa.flags = flags; + sa.gfp_mask = gfp; + sa.error_code =0 ; + sa.result =0 ;/* fake failure: force message to be logged */ + + ret =aa_audit (profile, &sa); + +va_end (sa.vaval); + +return ret; +} + +/** +* aa_audit_syscallreject - Log a syscall rejection to the audit subsystem +* @profile: profile to check against +* @msg: string describing syscall being rejected +* @gfp: memory allocation flags +*/ +int aa_audit_syscallreject (struct aa_profile *profile,gfp_t gfp, +const char *msg) +{ +struct aa_audit sa; + + sa.type =AA_AUDITTYPE_SYSCALL ; + sa.name = msg; + sa.flags =0 ; + sa.gfp_mask = gfp; + sa.error_code =0 ; + sa.result =0 ;/* failure */ + +return aa_audit (profile, &sa); +} + +/** +* aa_audit - Log an audit event to the audit subsystem +* @profile: profile to check against +* @sa: audit event +*/ +int aa_audit (struct aa_profile *profile,const struct aa_audit *sa) +{ +struct audit_buffer *ab =NULL ; +struct audit_context *audit_cxt; + +const char *logcls; +unsigned int flags; +int audit =0 , + complain =0 , + error = -EINVAL , + opspec_error = -EACCES ; + +const gfp_t gfp_mask = sa->gfp_mask; + +WARN_ON (sa->type >=AA_AUDITTYPE__END ); + +/* +* sa->result: 1 success, 0 failure +* sa->error_code: success: 0 +* failure: +ve mask of failed permissions or -ve +* system error +*/ + +if (likely (sa->result)) { +if (likely (!PROFILE_AUDIT (profile))) { +/* nothing to log */ + error =0 ; + goto out; + }else { + audit =1 ; + logcls ="AUDITING" ; + } + }else if (sa->error_code <0 ) { +audit_log (current->audit_context, gfp_mask,AUDIT_APPARMOR , +"Internal error auditing event type %d (error %d)" , + sa->type, sa->error_code); +AA_ERROR ("Internal error auditing event type %d (error %d)\n" , + sa->type, sa->error_code); + error = sa->error_code; + goto out; + }else if (sa->type ==AA_AUDITTYPE_SYSCALL ) { +/* Currently AA_AUDITTYPE_SYSCALL is for rejects only. +* Values set by aa_audit_syscallreject will get us here. +*/ + logcls ="REJECTING" ; + }else { + complain =PROFILE_COMPLAIN (profile); + logcls = complain ?"PERMITTING" :"REJECTING" ; + } + +/* In future extend w/ per-profile flags +* (flags |= sa->profile->flags) +*/ + flags = sa->flags; +if (apparmor_logsyscall) + flags |=AA_AUDITFLAG_AUDITSS_SYSCALL ; + + +/* Force full audit syscall logging regardless of global setting if +* we are rejecting a syscall +*/ +if (sa->type ==AA_AUDITTYPE_SYSCALL ) { + audit_cxt = current->audit_context; + }else { + audit_cxt = (flags &AA_AUDITFLAG_AUDITSS_SYSCALL ) ? + current->audit_context :NULL ; + } + + ab =audit_log_start (audit_cxt, gfp_mask,AUDIT_APPARMOR ); + +if (!ab) { +AA_ERROR ("Unable to log event (%d) to audit subsys\n" , + sa->type); +if (complain) + error =0 ; + goto out; + } + +/* messages get special handling */ +if (sa->type ==AA_AUDITTYPE_MSG ) { +audit_log_vformat (ab, sa->name, sa->vaval); +audit_log_end (ab); + error =0 ; + goto out; + } + +/* log operation */ + +audit_log_format (ab,"%s " , logcls);/* REJECTING/ALLOWING/etc */ + +if (sa->type ==AA_AUDITTYPE_FILE ) { +int perm = audit ? sa->mask : sa->error_code; + +audit_log_format (ab,"%s%s%s%s%s access to %s " , + perm &AA_EXEC_MMAP ?"m" :"" , + perm &MAY_READ ?"r" :"" , + perm &MAY_WRITE ?"w" :"" , + perm &MAY_EXEC ?"x" :"" , + perm &AA_MAY_LINK ?"l" :"" , + sa->name); + + opspec_error = -EPERM ; + + }else if (sa->type ==AA_AUDITTYPE_DIR ) { +audit_log_format (ab,"%s on %s " , sa->operation, sa->name); + + }else if (sa->type ==AA_AUDITTYPE_ATTR ) { +struct iattr *iattr = (struct iattr *)sa->pval; + +audit_log_format (ab, +"attribute (%s%s%s%s%s%s%s) change to %s " , + iattr->ia_valid &ATTR_MODE ?"mode," :"" , + iattr->ia_valid &ATTR_UID ?"uid," :"" , + iattr->ia_valid &ATTR_GID ?"gid," :"" , + iattr->ia_valid &ATTR_SIZE ?"size," :"" , + ((iattr->ia_valid &ATTR_ATIME_SET ) || + (iattr->ia_valid &ATTR_ATIME )) ?"atime," :"" , + ((iattr->ia_valid &ATTR_MTIME_SET ) || + (iattr->ia_valid &ATTR_MTIME )) ?"mtime," :"" , + iattr->ia_valid &ATTR_CTIME ?"ctime," :"" , + sa->name); + + }else if (sa->type ==AA_AUDITTYPE_XATTR ) { +/* FIXME: how are special characters in sa->name escaped? */ +/* FIXME: check if this can be handled on the stack +with an inline varargs function. */ +audit_log_format (ab,"%s on %s " , sa->operation, sa->name); + + }else if (sa->type ==AA_AUDITTYPE_LINK ) { +audit_log_format (ab, +"link access from %s to %s " , + sa->name, + (char *)sa->pval); + + }else if (sa->type ==AA_AUDITTYPE_CAP ) { +audit_log_format (ab, +"access to capability '%s' " , + capability_names[sa->capability]); + + opspec_error = -EPERM ; + }else if (sa->type ==AA_AUDITTYPE_SYSCALL ) { +audit_log_format (ab,"access to syscall '%s' " , sa->name); + + opspec_error = -EPERM ; + }else { +/* -EINVAL -- will WARN_ON above */ + goto out; + } + +audit_log_format (ab,"(%s(%d) profile %s active %s)" , + current->comm, current->pid, + profile->parent->name, profile->name); + +audit_log_end (ab); + +if (complain) + error =0 ; +else + error = sa->result ?0 : opspec_error; + +out: +return error; +} + +/** +* aa_attr - check if attribute change is allowed +* @profile: profile to check against +* @dentry: dentry of the file to check +* @mnt: vfsmount of the file to check +* @iattr: attribute changes requested +*/ +int aa_attr (struct aa_profile *profile,struct dentry *dentry, +struct vfsmount *mnt,struct iattr *iattr) +{ +struct inode *inode = dentry->d_inode; +int error, check; +struct aa_audit sa; + + sa.type =AA_AUDITTYPE_ATTR ; + sa.pval = iattr; + sa.flags =0 ; + sa.gfp_mask =GFP_KERNEL ; + + check =0 ; +if (inode &&S_ISDIR (inode->i_mode)) + check |=AA_CHECK_DIR ; +if (iattr->ia_valid &ATTR_FILE ) + check |=AA_CHECK_FD ; + + error =aa_perm_dentry (profile, dentry, mnt, &sa,MAY_WRITE , check); + +return error; +} + +/** +* aa_perm_xattr - check if xattr attribute change is allowed +* @profile: profile to check against +* @dentry: dentry of the file to check +* @mnt: vfsmount of the file to check +* @operation: xattr operation being done +* @xattr_name: name of xattr to check +* @mask: access mode requested +* @check: kind of check to perform +*/ +int aa_perm_xattr (struct aa_profile *profile,struct dentry *dentry, +struct vfsmount *mnt,const char *operation, +const char *xattr_name,int mask,int check) +{ +struct inode *inode = dentry->d_inode; +int error; +struct aa_audit sa; + + sa.type =AA_AUDITTYPE_XATTR ; + sa.operation = operation; + sa.pval = xattr_name; + sa.flags =0 ; + sa.gfp_mask =GFP_KERNEL ; + +if (inode &&S_ISDIR (inode->i_mode)) + check |=AA_CHECK_DIR ; + + error =aa_perm_dentry (profile, dentry, mnt, &sa, mask, check); + +return error; +} + +/** +* aa_perm - basic apparmor permissions check +* @profile: profile to check against +* @dentry: dentry of the file to check +* @mnt: vfsmount of the file to check +* @mask: access mode requested +* @check: kind of check to perform +* +* Determine if access @mask for the file is authorized by @profile. +* Returns 0 on success, or else an error code. +*/ +int aa_perm (struct aa_profile *profile,struct dentry *dentry, +struct vfsmount *mnt,int mask,int check) +{ +struct aa_audit sa; +int error =0 ; + +if ((check & (AA_CHECK_DIR |AA_CHECK_LEAF )) ==AA_CHECK_DIR ) { +/* +* If checking a non-leaf directory, allow traverse and +* write access: we do not require profile access to +* non-leaf directories in order to traverse them, +* create or remove files in them. We do require +* MAY_WRITE profile access on the actual file or +* directory being created or removed, though. +*/ + mask &= ~(MAY_EXEC |MAY_WRITE ); + } +if (mask ==0 ) + goto out; + + sa.type =AA_AUDITTYPE_FILE ; + sa.mask = mask; + sa.flags =0 ; + sa.gfp_mask =GFP_KERNEL ; + error =aa_perm_dentry (profile, dentry, mnt, &sa, mask, check); + +out: +return error; +} + +/** +* aa_perm_dir +* @profile: profile to check against +* @dentry: dentry of directory to check +* @mnt: vfsmount of directory to check +* @operation: directory operation being performed +* @mask: access mode requested +* +* Determine if directory operation (make/remove) for dentry is authorized +* by @profile. +* Returns 0 on success, or else an error code. +*/ +int aa_perm_dir (struct aa_profile *profile,struct dentry *dentry, +struct vfsmount *mnt,const char *operation,int mask) +{ +struct aa_audit sa; + + sa.type =AA_AUDITTYPE_DIR ; + sa.operation = operation; + sa.flags =0 ; + sa.gfp_mask =GFP_KERNEL ; + +return aa_perm_dentry (profile, dentry, mnt, &sa, mask, +AA_CHECK_DIR |AA_CHECK_LEAF ); +} + +/** +* aa_capability - test permission to use capability +* @cxt: aa_task_context with profile to check against +* @cap: capability to be tested +* +* Look up capability in profile capability set. +* Returns 0 on success, or else an error code. +*/ +int aa_capability (struct aa_task_context *cxt,int cap) +{ +int error =cap_raised (cxt->profile->capabilities, cap) ?0 : -EPERM ; +struct aa_audit sa; + +/* test if cap has alread been logged */ +if (cap_raised (cxt->caps_logged, cap)) { +if (PROFILE_COMPLAIN (cxt->profile)) + error =0 ; +return error; + }else +/* don't worry about rcu replacement of the cxt here. +* caps_logged is a cache to reduce the occurance of +* duplicate messages in the log. The worst that can +* happen is duplicate capability messages shows up in +* the audit log +*/ +cap_raise (cxt->caps_logged, cap); + + sa.type =AA_AUDITTYPE_CAP ; + sa.name =NULL ; + sa.capability = cap; + sa.flags =0 ; + sa.error_code =0 ; + sa.result = !error; + sa.gfp_mask =GFP_ATOMIC ; + + error =aa_audit (cxt->profile, &sa); + +return error; +} + +/* must be used inside rcu_read_lock or task_lock */ +int aa_may_ptrace (struct aa_task_context *cxt,struct aa_profile *tracee) +{ +if (!cxt || cxt->profile == tracee) +return 0 ; +return aa_capability (cxt,CAP_SYS_PTRACE ); +} + +/** +* aa_link - hard link check +* @profile: profile to check against +* @link: dentry of link being created +* @link_mnt: vfsmount of link being created +* @target: dentry of link target +* @target_mnt: vfsmunt of link target +* +* Returns 0 on success, or else an error code. +*/ +int aa_link (struct aa_profile *profile, +struct dentry *link,struct vfsmount *link_mnt, +struct dentry *target,struct vfsmount *target_mnt) +{ +char *name_buffer =NULL , *pval_buffer =NULL ; +int denied_mask = -EPERM , error; +struct aa_audit sa; + + sa.name =aa_get_name (link, link_mnt, &name_buffer,0 ); + sa.pval =aa_get_name (target, target_mnt, &pval_buffer,0 ); + +if (IS_ERR (sa.name)) { + denied_mask =PTR_ERR (sa.name); + sa.name =NULL ; + } +if (IS_ERR (sa.pval)) { + denied_mask =PTR_ERR (sa.pval); + sa.pval =NULL ; + } + +if (sa.name && sa.pval) + denied_mask =aa_link_denied (profile, sa.name, sa.pval); + +aa_permerror2result (denied_mask, &sa); + + sa.type =AA_AUDITTYPE_LINK ; + sa.flags =0 ; + sa.gfp_mask =GFP_KERNEL ; + + error =aa_audit (profile, &sa); + +aa_put_name_buffer (name_buffer); +aa_put_name_buffer (pval_buffer); + +return error; +} + +/******************************* +* Global task related functions +*******************************/ + +/** +* aa_clone - initialize the task context for a new task +* @child: task that is being created +* +* Returns 0 on success, or else an error code. +*/ +int aa_clone (struct task_struct *child) +{ +struct aa_task_context *cxt, *child_cxt; +struct aa_profile *profile; + +if (!aa_task_context (current)) +return 0 ; + child_cxt =aa_alloc_task_context (GFP_KERNEL ); +if (!child_cxt) +return -ENOMEM ; + +repeat: + profile =aa_get_profile (current); +if (profile) { +lock_profile (profile); + cxt =aa_task_context (current); +if (unlikely (profile->isstale || !cxt || + cxt->profile != profile)) { +/** +* Race with profile replacement or removal, or with +* task context removal. +*/ +unlock_profile (profile); +aa_put_profile (profile); + goto repeat; + } + +/* No need to grab the child's task lock here. */ +aa_change_task_context (child, child_cxt, profile, + cxt->hat_magic); +unlock_profile (profile); + +if (APPARMOR_COMPLAIN (child_cxt) && + profile == null_complain_profile) +LOG_HINT (profile,GFP_KERNEL ,HINT_FORK , +"child=%d" , child->pid); +aa_put_profile (profile); + }else +aa_free_task_context (child_cxt); + +return 0 ; +} + +static struct aa_profile * +aa_register_find (struct aa_profile *profile,const char *name,int mandatory, +int complain) +{ +struct aa_profile *new_profile; + +/* Locate new profile */ + new_profile =aa_find_profile (name); +if (new_profile) { +AA_DEBUG ("%s: setting profile %s\n" , + __FUNCTION__, new_profile->name); + }else if (mandatory && profile) { +if (complain) { +LOG_HINT (profile,GFP_KERNEL ,HINT_MANDPROF , +"image '%s'" , name); + profile =aa_dup_profile (null_complain_profile); + }else { +AA_REJECT_MSG (profile,GFP_KERNEL , +"exec(2) of image '%s'. " +"Profile mandatory and not found." , + name); +return ERR_PTR (-EPERM ); + } + }else { +/* Only way we can get into this code is if task +* is unconfined. +*/ +AA_DEBUG ("%s: No profile found for exec image '%s'\n" , + __FUNCTION__, + name); + } +return new_profile; +} + +/** +* aa_register - register a new program +* @bprm: binprm of program being registered +* +* Try to register a new program during execve(). This should give the +* new program a valid aa_task_context if confined. +*/ +int aa_register (struct linux_binprm *bprm) +{ +char *filename, *buffer =NULL ; +struct file *filp = bprm->file; +struct aa_profile *profile, *old_profile, *new_profile =NULL ; +int exec_mode =AA_EXEC_UNSAFE , complain =0 ; + +AA_DEBUG ("%s\n" , __FUNCTION__); + + filename =aa_get_name (filp->f_dentry, filp->f_vfsmnt, &buffer,0 ); +if (IS_ERR (filename)) { +AA_ERROR ("%s: Failed to get filename" , __FUNCTION__); +return -ENOENT ; + } + +repeat: + profile =aa_get_profile (current); +if (profile) { + complain =PROFILE_COMPLAIN (profile); + +/* Confined task, determine what mode inherit, unconfined or +* mandatory to load new profile +*/ + exec_mode =aa_match (profile->file_rules, filename); + +if (exec_mode & (MAY_EXEC |AA_EXEC_MODIFIERS )) { +switch (exec_mode & (MAY_EXEC |AA_EXEC_MODIFIERS )) { +case MAY_EXEC |AA_EXEC_INHERIT : +AA_DEBUG ("%s: INHERIT %s\n" , + __FUNCTION__, + filename); +/* nothing to be done here */ + goto cleanup; + +case MAY_EXEC |AA_EXEC_UNCONFINED : +AA_DEBUG ("%s: UNCONFINED %s\n" , + __FUNCTION__, + filename); + +/* detach current profile */ + new_profile =NULL ; +break ; + +case MAY_EXEC |AA_EXEC_PROFILE : +AA_DEBUG ("%s: PROFILE %s\n" , + __FUNCTION__, + filename); + new_profile =aa_register_find (profile, + filename,1 , + complain); +break ; + +default : +AA_ERROR ("Rejecting exec(2) of image '%s'. " +"Unknown exec qualifier %x " +"(%s (pid %d) profile %s active %s)\n" , + filename, + exec_mode &AA_EXEC_MODIFIERS , + current->comm, current->pid, + profile->parent->name, + profile->name); + new_profile =ERR_PTR (-EPERM ); +break ; + } + + }else if (complain) { +/* There was no entry in calling profile +* describing mode to execute image in. +* Drop into null-profile (disabling secure exec). +*/ + new_profile =aa_dup_profile (null_complain_profile); + exec_mode |=AA_EXEC_UNSAFE ; + }else { +AA_REJECT_MSG (profile,GFP_KERNEL , +"exec(2) of image '%s'. " +"Unable to determine exec qualifier." , + filename); + new_profile =ERR_PTR (-EPERM ); + } + }else { +/* Unconfined task, load profile if it exists */ + new_profile =aa_register_find (NULL , filename,0 ,0 ); +if (new_profile ==NULL ) + goto cleanup; + } + +if (IS_ERR (new_profile)) + goto cleanup; + + old_profile =__aa_replace_profile (current, new_profile,0 ); +if (IS_ERR (old_profile)) { +aa_put_profile (new_profile); +aa_put_profile (profile); +if (PTR_ERR (old_profile) == -ESTALE ) + goto repeat; +if (PTR_ERR (old_profile) == -EPERM ) +AA_REJECT_MSG (profile,GFP_KERNEL , +"exec(2) of image '%s'. " +"Unable to change profile, ptraced by %d." , + filename, current->parent->pid); + new_profile = old_profile; + goto cleanup; + } +aa_put_profile (old_profile); +aa_put_profile (profile); + +/* Handle confined exec. +* Can be at this point for the following reasons: +* 1. unconfined switching to confined +* 2. confined switching to different confinement +* 3. confined switching to unconfined +* +* Cases 2 and 3 are marked as requiring secure exec +* (unless policy specified "unsafe exec") +*/ +if (!(exec_mode &AA_EXEC_UNSAFE )) { +unsigned long bprm_flags; + + bprm_flags =AA_SECURE_EXEC_NEEDED ; + bprm->security = (void *) + ((unsigned long )bprm->security | bprm_flags); + } + +if (complain && new_profile == null_complain_profile) +LOG_HINT (new_profile,GFP_ATOMIC ,HINT_CHGPROF , +"" ); + +cleanup: +aa_put_name_buffer (buffer); +if (IS_ERR (new_profile)) +return PTR_ERR (new_profile); +aa_put_profile (new_profile); +return 0 ; +} + +/** +* aa_release - release a task context +* @task: task being released +* +* This is called after a task has exited and the parent has reaped it. +*/ +void aa_release (struct task_struct *task) +{ +struct aa_task_context *cxt; +struct aa_profile *profile; +/* +* While the task context is still on a profile's task context +* list, another process could replace the profile under us, +* leaving us with a locked profile that is no longer attached +* to this task. So after locking the profile, we check that +* the profile is still attached. The profile lock is +* sufficient to prevent the replacement race so we do not lock +* the task. +* +* lock_dep reports a false 'possible irq lock inversion dependency' +* between the profile lock and the task_lock. +* +* We also avoid taking the task_lock here because lock_dep +* would report another false {softirq-on-W} potential irq_lock +* inversion. +* +* If the task does not have a profile attached we are safe; +* nothing can race with us at this point. +*/ + +repeat: + profile =aa_get_profile (task); +if (profile) { +lock_profile (profile); + cxt =aa_task_context (task); +if (unlikely (!cxt || cxt->profile != profile)) { +unlock_profile (profile); +aa_put_profile (profile); + goto repeat; + } +aa_change_task_context (task,NULL ,NULL ,0 ); +unlock_profile (profile); +aa_put_profile (profile); + } +} + +/** +* do_change_hat - actually switch hats +* @hat_name: name of hat to switch to +* @new_cxt: new aa_task_context to use on profile change +* @hat_magic: new magic value to use +* +* Switch to a new hat. Returns %0 on success, error otherwise. +*/ +static int do_change_hat (const char *hat_name, +struct aa_task_context *new_cxt,u64 hat_magic) +{ +struct aa_task_context *cxt =aa_task_context (current); +struct aa_profile *sub; +int error =0 ; + +/* +* Note: the profile and sub-profiles cannot go away under us here; +* no need to grab an additional reference count. +*/ + sub =__aa_find_profile (hat_name, &cxt->profile->parent->sub); + +if ((current->ptrace &PT_PTRACED ) &&aa_may_ptrace (cxt, sub)) +return -EPERM ; + +if (sub) { +/* change hat */ +aa_change_task_context (current, new_cxt, sub, hat_magic); + }else { +struct aa_profile *profile = cxt->profile; + +if (APPARMOR_COMPLAIN (cxt)) { +LOG_HINT (profile,GFP_ATOMIC ,HINT_UNKNOWN_HAT , +"%s" , hat_name); + }else { +AA_DEBUG ("%s: Unknown hatname '%s'. " +"Changing to NULL profile " +"(%s(%d) profile %s active %s)\n" , + __FUNCTION__, + hat_name, + current->comm, current->pid, + profile->parent->name, + profile->name); + error = -EACCES ; + } +/* +* Switch to the NULL profile: it grants no accesses, so in +* learning mode all accesses will get logged, and in enforce +* mode all accesses will be denied. +* +* In learning mode, this allows us to learn about new hats. +*/ +aa_change_task_context (current, new_cxt, + cxt->profile->null_profile, hat_magic); + } + +return error; +} + +/** +* aa_change_hat - change hat to/from subprofile +* @hat_name: hat to change to +* @hat_magic: magic cookie to validate the hat change +* +* Change to new @hat_name, and store the @hat_magic in the current task +* context. If the new @hat_name is %NULL and the @hat_magic matches that +* stored in the current task context and is not 0, return to the top level +* profile. +* Returns %0 on success, error otherwise. +*/ +int aa_change_hat (const char *hat_name,u64 hat_magic) +{ +struct aa_task_context *cxt, *new_cxt; +struct aa_profile *profile =NULL ; +int error =0 ; + +/* Dump out above debugging in WARN mode if we are in AUDIT mode */ +if (APPARMOR_AUDIT (aa_task_context (current))) { +AA_WARN (GFP_KERNEL ,"change_hat %s, 0x%llx (pid %d)" , + hat_name ? hat_name :"NULL" , + hat_magic, current->pid); + } + + new_cxt =aa_alloc_task_context (GFP_KERNEL ); +if (!new_cxt) +return -ENOMEM ; + + cxt =lock_task_and_profiles (current,NULL ); +if (!cxt) { +/* An unconfined process cannot change_hat(). */ + error = -EPERM ; + goto out; + } + +/* No need to get reference count: we do not sleep. */ + profile = cxt->profile; + +/* check to see if the confined process has any hats. */ +if (list_empty (&profile->parent->sub) && !PROFILE_COMPLAIN (profile)) { + error = -ECHILD ; + goto out; + } + +if (profile == profile->parent) { +/* We are in the parent profile. */ +if (hat_name) { +AA_DEBUG ("%s: switching to %s, 0x%llx\n" , + __FUNCTION__, + hat_name, + hat_magic); + error =do_change_hat (hat_name, new_cxt, hat_magic); + } + }else { +/* +* We are in a child profile. +* +* Check to make sure magic is same as what was passed when +* we switched into this profile. Handle special casing of +* NULL magic which confines task to subprofile and prohibits +* further change_hats. +*/ +if (hat_magic && hat_magic == cxt->hat_magic) { +if (!hat_name) { +/* Return from subprofile back to parent. */ +aa_change_task_context (current, new_cxt, + profile->parent,0 ); + }else { +/* +* Change to another (sibling) profile, and +* stick with the same hat_magic. +*/ + error =do_change_hat (hat_name, new_cxt, + cxt->hat_magic); + } + }else if (cxt->hat_magic) { +AA_ERROR ("KILLING process %s(%d) " +"Invalid change_hat() magic# 0x%llx " +"(hatname %s profile %s active %s)\n" , + current->comm, current->pid, + hat_magic, + hat_name ? hat_name :"NULL" , + profile->parent->name, + profile->name); + +/* terminate current process */ + (void )send_sig_info (SIGKILL ,NULL , current); + }else {/* cxt->hat_magic == 0 */ +AA_ERROR ("KILLING process %s(%d) " +"Task was confined to current subprofile " +"(profile %s active %s)\n" , + current->comm, current->pid, + profile->parent->name, + profile->name); + +/* terminate current process */ + (void )send_sig_info (SIGKILL ,NULL , current); + } + + } + +out: +if (aa_task_context (current) != new_cxt) +aa_free_task_context (new_cxt); +task_unlock (current); +unlock_profile (profile); +return error; +} + +/** +* __aa_replace_profile - replace a task's profile +* @task: task to switch the profile of +* @profile: profile to switch to +* @hat_magic: magic cookie to switch to +* +* Returns a handle to the previous profile upon success, or else an +* error code. +*/ +struct aa_profile *__aa_replace_profile (struct task_struct *task, +struct aa_profile *profile, +u32 hat_magic) +{ +struct aa_task_context *cxt, *new_cxt =NULL ; +struct aa_profile *old_profile =NULL ; + +if (profile) { + new_cxt =aa_alloc_task_context (GFP_KERNEL ); +if (!new_cxt) +return ERR_PTR (-ENOMEM ); + } + + cxt =lock_task_and_profiles (task, profile); +if (unlikely (profile && profile->isstale)) { +task_unlock (task); +unlock_both_profiles (profile, cxt ? cxt->profile :NULL ); +aa_free_task_context (new_cxt); +return ERR_PTR (-ESTALE ); + } + +if ((current->ptrace &PT_PTRACED ) &&aa_may_ptrace (cxt, profile)) { +task_unlock (task); +unlock_both_profiles (profile, cxt ? cxt->profile :NULL ); +aa_free_task_context (new_cxt); +return ERR_PTR (-EPERM ); + } + +if (cxt) { + old_profile =aa_dup_profile (cxt->profile); +aa_change_task_context (task, new_cxt, profile, cxt->hat_magic); + }else +aa_change_task_context (task, new_cxt, profile,0 ); + +task_unlock (task); +unlock_both_profiles (profile, old_profile); +return old_profile; +} + +/** +* lock_task_and_profile - lock the task and confining profiles and @profile +* @task - task to lock +* @profile - extra profile to lock in addition to the current profile +* +* Handle the spinning on locking to make sure the task context and +* profile are consistent once all locks are aquired. +* +* return the aa_task_context currently confining the task. The task lock +* will be held whether or not the task is confined. +*/ +struct aa_task_context * +lock_task_and_profiles (struct task_struct *task,struct aa_profile *profile) +{ +struct aa_task_context *cxt; +struct aa_profile *old_profile =NULL ; + +rcu_read_lock (); +repeat: + cxt =aa_task_context (task); +if (cxt) + old_profile = cxt->profile; +lock_both_profiles (profile, old_profile); +task_lock (task); + +/* check for race with profile transition, replacement or removal */ +if (unlikely (cxt !=aa_task_context (task))) { +task_unlock (task); +unlock_both_profiles (profile, old_profile); + goto repeat; + } +rcu_read_unlock (); +return cxt; +} + +static void free_aa_task_context_rcu_callback (struct rcu_head *head) +{ +struct aa_task_context *cxt; + + cxt = container_of(head, struct aa_task_context, rcu); +aa_free_task_context (cxt); +} + +/** +* aa_change_task_context - switch a task to use a new context and profile +* @task: task that is having its task context changed +* @new_cxt: new task context to use after the switch +* @profile: new profile to use after the switch +* @hat_magic: hat value to switch to (0 for no hat) +*/ +void aa_change_task_context (struct task_struct *task, +struct aa_task_context *new_cxt, +struct aa_profile *profile,u64 hat_magic) +{ +struct aa_task_context *old_cxt =aa_task_context (task); + +if (old_cxt) { +list_del_init (&old_cxt->list); +call_rcu (&old_cxt->rcu, free_aa_task_context_rcu_callback); + } +if (new_cxt) { +/* clear the caps_logged cache, so that new profile/hat has +* chance to emit its own set of cap messages */ + new_cxt->caps_logged =CAP_EMPTY_SET ; + new_cxt->hat_magic = hat_magic; + new_cxt->task = task; + new_cxt->profile =aa_dup_profile (profile); +list_move (&new_cxt->list, &profile->parent->task_contexts); + } +rcu_assign_pointer (task->security, new_cxt); +}
Re: [AppArmor 37/41] AppArmor: Main Part
Quoted 7 lines
+ *
aa_taskattr_access
+ * @name: name of the file to check
+ *
+ * Check if name matches/proc/self/attr/current, with self resolved
+ * to the current pid. This file is the usermode iterface for
+ * changing one's hat.
+ */
security/apparmor/main.c line 55
Quoted 7 lines
security/apparmor/main.c new file +7
-0+static inline int aa_taskattr_access (const char *name) +{ +unsigned long pid; +char *end; + +if (strncmp (name,"/proc/" ,6 ) !=0 ) +return 0 ;
The proc file system may not be mounted at /proc. There are environments where this is done for good reason (eg not wanting the /proc info exposed to a low trust environment). Another is when FUSE is providing an arbitrated proc either by merging across clusters or by removing stuff.
security/apparmor/main.c line 89
Quoted 8 lines
security/apparmor/main.c new file +8
-0+static int aa_file_denied (struct aa_profile *profile,const char *name, +int mask) +{ +int perms; + +/* Always allow write access to /proc/self/attr/current. */ +if (mask ==MAY_WRITE &&aa_taskattr_access (name)) +return 0 ;
Why can't this be done in the profile itself to avoid kernel special case uglies and inflexibility ?
security/apparmor/main.c line 227
Quoted 2 lines
security/apparmor/main.c new file +2
-0+if (PTR_ERR (sa->name) == -ENOENT && (check &AA_CHECK_FD )) + denied_mask =0 ;
Now there is an interesting question. Is PTR_ERR() safe for kernel pointers on all platforms or just for user ones ?
Re: [AppArmor 37/41] AppArmor: Main Part
Quoted 7 lines · Alan Cox
The proc file system may not be mounted at /proc. There are environments where this is done for good reason (eg not wanting the /proc info exposed to a low trust environment). Another is when FUSE is providing an arbitrated proc either by merging across clusters or by removing stuff. [...]
Why can't this be done in the profile itself to avoid kernel special case uglies and inflexibility ?
Good points. I'm in fact not sure how this could have been missed, and indeed it makes more sense to put this in profiles.
Thanks,
Andreas
Re: [AppArmor 37/41] AppArmor: Main Part
In reply to Alan CoxQuoted 5 lines · Alan Cox
security/apparmor/main.c line 227
Quoted 2 lines
security/apparmor/main.c new file +2
-0+if (PTR_ERR (sa->name) == -ENOENT && (check &AA_CHECK_FD )) + denied_mask =0 ;Now there is an interesting question. Is
PTR_ERR()safe for kernel pointers on all platforms or just for user ones ?
It's used for kernel pointers all over the place and mmap also mixes user addresses with -Exxx, so it's definitely supposed to work. I'm not sure how exactly the topmost page is kept from getting mapped.
Andreas
Re: [AppArmor 37/41] AppArmor: Main Part
Quoted 10 lines · Andreas Gruenbacher
Quoted 5 lines · Alan Cox
security/apparmor/main.c line 227
Quoted 2 lines
security/apparmor/main.c new file +2
-0+if (PTR_ERR (sa->name) == -ENOENT && (check &AA_CHECK_FD )) + denied_mask =0 ;Now there is an interesting question. Is
PTR_ERR()safe for kernel pointers on all platforms or just for user ones ?It's used for kernel pointers all over the place and mmap also mixes user addresses with -Exxx, so it's definitely supposed to work. I'm not sure how exactly the topmost page is kept from getting mapped.
Yeah, the comments indicate it was first used for dentries.
I wonder if it shouldn't be using the NULL page instead? (ie 0-4095)
Signature · 1 line
SUSE Labs, Novell Inc.