* [RFC PATCH] CIFS posix acl permission checking
@ 2010-03-01 15:15 Jon Severinsson
2010-03-01 17:03 ` Matthew Wilcox
0 siblings, 1 reply; 6+ messages in thread
From: Jon Severinsson @ 2010-03-01 15:15 UTC (permalink / raw)
To: linux-fsdevel; +Cc: linux-kernel
[-- Attachment #1.1: Type: text/plain, Size: 1174 bytes --]
Hello everyone
Firstly, please note that I'm new to the kernel community, so if I'm doing
something wrong, just please politely point it out to me and I'll try to fix
it, or at the very least not do it again. Also please bear in mind that
English isn't my native language.
Anyway, I recently realised that while the CIFS file system driver in Linux
does supports posix acl retrieval and modification using the getfacl and
setfacl command line tools, it does not use acl for client side permission
checking. On the server side Samba does consult the acl, but that doesn't
really help when cifs.ko never even asks the server, due to the users only
source of permission being from an acl.
I'm attaching a first attempt at implementing it. I have tested it, but only on
a single setup, so I can give no guarantees to its portability. Also please
note that this is my first kernel patch, so if I'm doing something wrong, such
as not following some coding standard, please enlighten me and I'll gladly fix
it.
I only subscribed to the linux-fsdevel list, so please include it in any
response you might send.
Best Regards
Jon Severinsson
[-- Attachment #1.2: cifs-acl-permission-check-v1.patch --]
[-- Type: text/x-patch, Size: 5213 bytes --]
commit fab1872fcbb8a5cdb85d7e7a88ecf0cad99d1c80
Author: Jon Severinsson <jon@severinsson.net>
Date: Mon Mar 1 15:49:40 2010 +0100
[CIFS] Adds support for permision checking vs. posix acl.
CIFS already supports getting and setting acls through getfacl and setfacl, but
prior to this patch, any acls was ignored when doing permission checking.
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 29f1da7..0605e11 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -284,7 +284,7 @@ static int cifs_permission(struct inode *inode, int mask)
on the client (above and beyond ACL on servers) for
servers which do not support setting and viewing mode bits,
so allowing client to check permissions is useful */
- return generic_permission(inode, mask, NULL);
+ return generic_permission(inode, mask, inode->i_op->check_acl);
}
static struct kmem_cache *cifs_inode_cachep;
@@ -702,6 +702,9 @@ const struct inode_operations cifs_dir_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
@@ -716,6 +719,9 @@ const struct inode_operations cifs_file_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
@@ -732,6 +738,9 @@ const struct inode_operations cifs_symlink_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index ac2b24c..6409a83 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -102,11 +102,15 @@ extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
int buflen);
extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
const char *symname);
+
+/* Functions related to extended attributes */
extern int cifs_removexattr(struct dentry *, const char *);
extern int cifs_setxattr(struct dentry *, const char *, const void *,
size_t, int);
extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
+extern int cifs_check_acl(struct inode *inode, int mask);
+
extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_CIFS_EXPERIMENTAL
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
index a75afa3..a851ba1 100644
--- a/fs/cifs/xattr.c
+++ b/fs/cifs/xattr.c
@@ -374,3 +374,74 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
#endif
return rc;
}
+
+int cifs_check_acl(struct inode *inode, int mask)
+{
+ int rc = -EOPNOTSUPP;
+#ifdef CONFIG_CIFS_XATTR
+#ifdef CONFIG_CIFS_POSIX
+ struct dentry *dentry = NULL;
+ size_t buf_size;
+ void *ea_value = NULL;
+ ssize_t ea_size;
+ struct posix_acl *acl = NULL;
+
+ /* CIFS gets acl from server by path, and thus needs a dentry rather than
+ an inode. Note that the path of each dentry will point to the same inode
+ on the backing fs at the server, so their acls will be the same, and it
+ doesn't matter which one we pick, so just pick the fist. */
+ if (list_empty(&inode->i_dentry))
+ dentry = list_first_entry(&inode->i_dentry, struct dentry, d_alias);
+
+ /* If we didn't get an dentry for the inode, something went terribly wrong.
+ All we can do at this point is to return an error. */
+ if (!dentry || IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ /* Try to fit the extended attribute corresponding to the posix acl in 4k
+ memory. 4k was chosen because it always fits in a single page, and is
+ the maximum on a default ext2/3/4 backing fs. */
+ buf_size = 4096;
+ ea_value = kmalloc(buf_size, GFP_KERNEL);
+ if (!ea_value) {
+ rc = -EAGAIN;
+ goto check_acl_exit;
+ }
+ ea_size = cifs_getxattr(dentry, POSIX_ACL_XATTR_ACCESS, ea_value, buf_size);
+
+ /* If 4k wasn't enough, try 64k, the maximum on any current backing fs. */
+ if (ea_size == -ERANGE) {
+ kfree(ea_value);
+ buf_size = 65536;
+ ea_value = kmalloc(buf_size, GFP_KERNEL);
+ if (!ea_value) {
+ rc = -EAGAIN;
+ goto check_acl_exit;
+ }
+ ea_size = cifs_getxattr(dentry, POSIX_ACL_XATTR_ACCESS, ea_value, buf_size);
+ }
+
+ /* If we didn't get any extended attribute, set the error code and exit */
+ if (ea_size <= 0) {
+ rc = -EAGAIN;
+ if (ea_size == -EOPNOTSUPP || ea_size == -EIO || ea_size == -ENOTDIR || ea_size == -ENOENT || ea_size == -EFAULT || ea_size == -EACCES)
+ rc = ea_size;
+ goto check_acl_exit;
+ }
+
+ /* Set the appropriate return value. Adapted from ext4. */
+ acl = posix_acl_from_xattr(ea_value, ea_size);
+ if (IS_ERR(acl))
+ rc = PTR_ERR(acl);
+ else if (acl)
+ rc = posix_acl_permission(inode, acl, mask);
+ else
+ rc = -EAGAIN;
+
+check_acl_exit:
+ posix_acl_release(acl);
+ kfree(ea_value);
+#endif /* CONFIG_CIFS_POSIX */
+#endif /* CONFIG_CIFS_XATTR */
+ return rc;
+}
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [RFC PATCH] CIFS posix acl permission checking
@ 2010-03-01 16:00 Jon Severinsson
0 siblings, 0 replies; 6+ messages in thread
From: Jon Severinsson @ 2010-03-01 16:00 UTC (permalink / raw)
To: linux-fsdevel; +Cc: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 1149 bytes --]
Hello everyone
Firstly, please note that I'm new to the kernel community, so if I'm doing
something wrong, just please politely point it out to me and I'll try to fix
it, or at the very least not do it again. Also please bear in mind that
English isn't my native language.
Anyway, I recently realised that while the CIFS file system driver in Linux
does supports posix acl retrieval and modification using the getfacl and
setfacl command line tools, it does not use acl for client side permission
checking. On the server side Samba does consult the acl, but that doesn't
really help when cifs.ko never even asks the server, due to the users only
source of permission being from an acl.
I'm attaching a first attempt at implementing it. I have tested it, but only on
a single setup, so I can give no guarantees to its portability. Also please
note that this is my first kernel patch, so if I'm doing something wrong, such
as not following some coding standard, please enlighten me and I'll gladly fix
it.
I only subscribed to the linux-fsdevel list, so please include it in any
response you might send.
Best Regards
Jon Severinsson
[-- Attachment #2: cifs-acl-permission-check-v1.patch --]
[-- Type: text/x-patch, Size: 5213 bytes --]
commit fab1872fcbb8a5cdb85d7e7a88ecf0cad99d1c80
Author: Jon Severinsson <jon@severinsson.net>
Date: Mon Mar 1 15:49:40 2010 +0100
[CIFS] Adds support for permision checking vs. posix acl.
CIFS already supports getting and setting acls through getfacl and setfacl, but
prior to this patch, any acls was ignored when doing permission checking.
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 29f1da7..0605e11 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -284,7 +284,7 @@ static int cifs_permission(struct inode *inode, int mask)
on the client (above and beyond ACL on servers) for
servers which do not support setting and viewing mode bits,
so allowing client to check permissions is useful */
- return generic_permission(inode, mask, NULL);
+ return generic_permission(inode, mask, inode->i_op->check_acl);
}
static struct kmem_cache *cifs_inode_cachep;
@@ -702,6 +702,9 @@ const struct inode_operations cifs_dir_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
@@ -716,6 +719,9 @@ const struct inode_operations cifs_file_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
@@ -732,6 +738,9 @@ const struct inode_operations cifs_symlink_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index ac2b24c..6409a83 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -102,11 +102,15 @@ extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
int buflen);
extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
const char *symname);
+
+/* Functions related to extended attributes */
extern int cifs_removexattr(struct dentry *, const char *);
extern int cifs_setxattr(struct dentry *, const char *, const void *,
size_t, int);
extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
+extern int cifs_check_acl(struct inode *inode, int mask);
+
extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_CIFS_EXPERIMENTAL
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
index a75afa3..a851ba1 100644
--- a/fs/cifs/xattr.c
+++ b/fs/cifs/xattr.c
@@ -374,3 +374,74 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
#endif
return rc;
}
+
+int cifs_check_acl(struct inode *inode, int mask)
+{
+ int rc = -EOPNOTSUPP;
+#ifdef CONFIG_CIFS_XATTR
+#ifdef CONFIG_CIFS_POSIX
+ struct dentry *dentry = NULL;
+ size_t buf_size;
+ void *ea_value = NULL;
+ ssize_t ea_size;
+ struct posix_acl *acl = NULL;
+
+ /* CIFS gets acl from server by path, and thus needs a dentry rather than
+ an inode. Note that the path of each dentry will point to the same inode
+ on the backing fs at the server, so their acls will be the same, and it
+ doesn't matter which one we pick, so just pick the fist. */
+ if (list_empty(&inode->i_dentry))
+ dentry = list_first_entry(&inode->i_dentry, struct dentry, d_alias);
+
+ /* If we didn't get an dentry for the inode, something went terribly wrong.
+ All we can do at this point is to return an error. */
+ if (!dentry || IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ /* Try to fit the extended attribute corresponding to the posix acl in 4k
+ memory. 4k was chosen because it always fits in a single page, and is
+ the maximum on a default ext2/3/4 backing fs. */
+ buf_size = 4096;
+ ea_value = kmalloc(buf_size, GFP_KERNEL);
+ if (!ea_value) {
+ rc = -EAGAIN;
+ goto check_acl_exit;
+ }
+ ea_size = cifs_getxattr(dentry, POSIX_ACL_XATTR_ACCESS, ea_value, buf_size);
+
+ /* If 4k wasn't enough, try 64k, the maximum on any current backing fs. */
+ if (ea_size == -ERANGE) {
+ kfree(ea_value);
+ buf_size = 65536;
+ ea_value = kmalloc(buf_size, GFP_KERNEL);
+ if (!ea_value) {
+ rc = -EAGAIN;
+ goto check_acl_exit;
+ }
+ ea_size = cifs_getxattr(dentry, POSIX_ACL_XATTR_ACCESS, ea_value, buf_size);
+ }
+
+ /* If we didn't get any extended attribute, set the error code and exit */
+ if (ea_size <= 0) {
+ rc = -EAGAIN;
+ if (ea_size == -EOPNOTSUPP || ea_size == -EIO || ea_size == -ENOTDIR || ea_size == -ENOENT || ea_size == -EFAULT || ea_size == -EACCES)
+ rc = ea_size;
+ goto check_acl_exit;
+ }
+
+ /* Set the appropriate return value. Adapted from ext4. */
+ acl = posix_acl_from_xattr(ea_value, ea_size);
+ if (IS_ERR(acl))
+ rc = PTR_ERR(acl);
+ else if (acl)
+ rc = posix_acl_permission(inode, acl, mask);
+ else
+ rc = -EAGAIN;
+
+check_acl_exit:
+ posix_acl_release(acl);
+ kfree(ea_value);
+#endif /* CONFIG_CIFS_POSIX */
+#endif /* CONFIG_CIFS_XATTR */
+ return rc;
+}
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] CIFS posix acl permission checking
2010-03-01 15:15 [RFC PATCH] CIFS posix acl permission checking Jon Severinsson
@ 2010-03-01 17:03 ` Matthew Wilcox
2010-03-01 18:33 ` Jon Severinsson
0 siblings, 1 reply; 6+ messages in thread
From: Matthew Wilcox @ 2010-03-01 17:03 UTC (permalink / raw)
To: Jon Severinsson; +Cc: linux-fsdevel, linux-kernel, linux-cifs-client
On Mon, Mar 01, 2010 at 04:15:10PM +0100, Jon Severinsson wrote:
> Hello everyone
>
> Firstly, please note that I'm new to the kernel community, so if I'm doing
> something wrong, just please politely point it out to me and I'll try to fix
> it, or at the very least not do it again. Also please bear in mind that
> English isn't my native language.
Okey dokey, here's some minor mistakes you made.
- Generally a good idea to cc the CIFS development list for CIFS patches.
I'm sure they'll notice it here, but just to be on the safe side, I've
added them to the cc.
- You've included ifdefs around the check_acl entry in inode_operations,
*and* inside the definition of cifs_check_acl. You only need to do one
or the other, and opinion is divided on which is better.
(further comments inline)
> Anyway, I recently realised that while the CIFS file system driver in Linux
> does supports posix acl retrieval and modification using the getfacl and
> setfacl command line tools, it does not use acl for client side permission
> checking. On the server side Samba does consult the acl, but that doesn't
> really help when cifs.ko never even asks the server, due to the users only
> source of permission being from an acl.
>
> I'm attaching a first attempt at implementing it. I have tested it, but only on
> a single setup, so I can give no guarantees to its portability. Also please
> note that this is my first kernel patch, so if I'm doing something wrong, such
> as not following some coding standard, please enlighten me and I'll gladly fix
> it.
>
> I only subscribed to the linux-fsdevel list, so please include it in any
> response you might send.
>
> Best Regards
> Jon Severinsson
> commit fab1872fcbb8a5cdb85d7e7a88ecf0cad99d1c80
> Author: Jon Severinsson <jon@severinsson.net>
> Date: Mon Mar 1 15:49:40 2010 +0100
>
> [CIFS] Adds support for permision checking vs. posix acl.
>
> CIFS already supports getting and setting acls through getfacl and setfacl, but
> prior to this patch, any acls was ignored when doing permission checking.
>
> diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
> index 29f1da7..0605e11 100644
> --- a/fs/cifs/cifsfs.c
> +++ b/fs/cifs/cifsfs.c
> @@ -284,7 +284,7 @@ static int cifs_permission(struct inode *inode, int mask)
> on the client (above and beyond ACL on servers) for
> servers which do not support setting and viewing mode bits,
> so allowing client to check permissions is useful */
> - return generic_permission(inode, mask, NULL);
> + return generic_permission(inode, mask, inode->i_op->check_acl);
> }
>
> static struct kmem_cache *cifs_inode_cachep;
> @@ -702,6 +702,9 @@ const struct inode_operations cifs_dir_inode_ops = {
> .getxattr = cifs_getxattr,
> .listxattr = cifs_listxattr,
> .removexattr = cifs_removexattr,
> +#ifdef CONFIG_CIFS_POSIX
> + .check_acl = cifs_check_acl,
> +#endif
> #endif
> };
>
> @@ -716,6 +719,9 @@ const struct inode_operations cifs_file_inode_ops = {
> .getxattr = cifs_getxattr,
> .listxattr = cifs_listxattr,
> .removexattr = cifs_removexattr,
> +#ifdef CONFIG_CIFS_POSIX
> + .check_acl = cifs_check_acl,
> +#endif
> #endif
> };
>
> @@ -732,6 +738,9 @@ const struct inode_operations cifs_symlink_inode_ops = {
> .getxattr = cifs_getxattr,
> .listxattr = cifs_listxattr,
> .removexattr = cifs_removexattr,
> +#ifdef CONFIG_CIFS_POSIX
> + .check_acl = cifs_check_acl,
> +#endif
> #endif
> };
>
> diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
> index ac2b24c..6409a83 100644
> --- a/fs/cifs/cifsfs.h
> +++ b/fs/cifs/cifsfs.h
> @@ -102,11 +102,15 @@ extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
> int buflen);
> extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
> const char *symname);
> +
> +/* Functions related to extended attributes */
> extern int cifs_removexattr(struct dentry *, const char *);
> extern int cifs_setxattr(struct dentry *, const char *, const void *,
> size_t, int);
> extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
> extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
> +extern int cifs_check_acl(struct inode *inode, int mask);
> +
> extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
>
> #ifdef CONFIG_CIFS_EXPERIMENTAL
> diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
> index a75afa3..a851ba1 100644
> --- a/fs/cifs/xattr.c
> +++ b/fs/cifs/xattr.c
> @@ -374,3 +374,74 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
> #endif
> return rc;
> }
> +
> +int cifs_check_acl(struct inode *inode, int mask)
> +{
> + int rc = -EOPNOTSUPP;
> +#ifdef CONFIG_CIFS_XATTR
> +#ifdef CONFIG_CIFS_POSIX
> + struct dentry *dentry = NULL;
> + size_t buf_size;
> + void *ea_value = NULL;
I don't think you need to initialise ea_value, do you?
> + ssize_t ea_size;
> + struct posix_acl *acl = NULL;
> +
> + /* CIFS gets acl from server by path, and thus needs a dentry rather than
> + an inode. Note that the path of each dentry will point to the same inode
> + on the backing fs at the server, so their acls will be the same, and it
> + doesn't matter which one we pick, so just pick the fist. */
> + if (list_empty(&inode->i_dentry))
> + dentry = list_first_entry(&inode->i_dentry, struct dentry, d_alias);
> +
> + /* If we didn't get an dentry for the inode, something went terribly wrong.
> + All we can do at this point is to return an error. */
> + if (!dentry || IS_ERR(dentry))
> + return PTR_ERR(dentry);
I don't think dentry can possibly be IS_ERR here, and returning
PTR_ERR(NULL) doesn't do what you think it does. Also, you've got the sense
of list_empty backwards. I think what you meant to do here was:
if (!list_empty(&inode->i_dentry))
dentry = list_first_entry(&inode->i_dentry, struct dentry,
d_alias);
else
return -EINVAL;
> + /* Try to fit the extended attribute corresponding to the posix acl in 4k
> + memory. 4k was chosen because it always fits in a single page, and is
> + the maximum on a default ext2/3/4 backing fs. */
> + buf_size = 4096;
> + ea_value = kmalloc(buf_size, GFP_KERNEL);
> + if (!ea_value) {
> + rc = -EAGAIN;
> + goto check_acl_exit;
> + }
> + ea_size = cifs_getxattr(dentry, POSIX_ACL_XATTR_ACCESS, ea_value, buf_size);
> +
> + /* If 4k wasn't enough, try 64k, the maximum on any current backing fs. */
> + if (ea_size == -ERANGE) {
> + kfree(ea_value);
> + buf_size = 65536;
> + ea_value = kmalloc(buf_size, GFP_KERNEL);
> + if (!ea_value) {
> + rc = -EAGAIN;
> + goto check_acl_exit;
> + }
> + ea_size = cifs_getxattr(dentry, POSIX_ACL_XATTR_ACCESS, ea_value, buf_size);
> + }
> +
> + /* If we didn't get any extended attribute, set the error code and exit */
> + if (ea_size <= 0) {
> + rc = -EAGAIN;
> + if (ea_size == -EOPNOTSUPP || ea_size == -EIO || ea_size == -ENOTDIR || ea_size == -ENOENT || ea_size == -EFAULT || ea_size == -EACCES)
> + rc = ea_size;
> + goto check_acl_exit;
> + }
> +
> + /* Set the appropriate return value. Adapted from ext4. */
> + acl = posix_acl_from_xattr(ea_value, ea_size);
> + if (IS_ERR(acl))
> + rc = PTR_ERR(acl);
> + else if (acl)
> + rc = posix_acl_permission(inode, acl, mask);
> + else
> + rc = -EAGAIN;
> +
> +check_acl_exit:
> + posix_acl_release(acl);
> + kfree(ea_value);
> +#endif /* CONFIG_CIFS_POSIX */
> +#endif /* CONFIG_CIFS_XATTR */
> + return rc;
> +}
--
Matthew Wilcox Intel Open Source Technology Centre
"Bill, look, we understand that you're interested in selling us this
operating system, but compare it to ours. We can't possibly take such
a retrograde step."
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] CIFS posix acl permission checking
2010-03-01 17:03 ` Matthew Wilcox
@ 2010-03-01 18:33 ` Jon Severinsson
0 siblings, 0 replies; 6+ messages in thread
From: Jon Severinsson @ 2010-03-01 18:33 UTC (permalink / raw)
To: linux-fsdevel; +Cc: linux-kernel, linux-cifs-client
[-- Attachment #1: Type: Text/Plain, Size: 4586 bytes --]
On Monday 01 March 2010 18:03:49, Matthew Wilcox wrote:
> On Mon, Mar 01, 2010 at 04:15:10PM +0100, Jon Severinsson wrote:
>> Firstly, please note that I'm new to the kernel community, so if I'm
>> doing something wrong, just please politely point it out to me and I'll
>> try to fix it, or at the very least not do it again. Also please bear in
>> mind that English isn't my native language.
>
> Okey dokey, here's some minor mistakes you made.
Thank you.
> - Generally a good idea to cc the CIFS development list for CIFS patches.
> I'm sure they'll notice it here, but just to be on the safe side, I've
> added them to the cc.
Sure thing. I used linux-fsdevel as I didn't find anything more specific on
vger.kernel.org, didn't think to look at lists.samba.org.
> - You've included ifdefs around the check_acl entry in inode_operations,
> *and* inside the definition of cifs_check_acl. You only need to do one
> or the other, and opinion is divided on which is better.
While I did recognize the redundancy, I decided to follow the same convention
the other functions in xattr.c did, and include ifdefs at both locations.
I also considered the possible reasons for the existing functions to do both,
and and came up with two reasons. The first simply being the paradigm of
defensive programming, always check before doing a call, but never assume that
the check has been done before being called. The second one is that of
performance. The ifdefs has to be in cifs_check_acl to protect against other
callers (while this patch doesn't introduce any, it doesn't prevent further
patches from adding them), and not including the ifdefs in inode_operations
would mean a completely useless function call on every permission check when a
feature was turned off at compile time. The second one is a micro-optimization
I
don't really care fore, but defensive programming I do respect.
With this in mind, what do you recommend, double protection, breaking
convention or changing the existing code?
>> --- a/fs/cifs/xattr.c
>> +++ b/fs/cifs/xattr.c
>> @@ -374,3 +374,74 @@ ssize_t cifs_listxattr(struct dentry *direntry, char
>> *data, size_t buf_size) #endif
>> return rc;
>> }
>> +
>> +int cifs_check_acl(struct inode *inode, int mask)
>> +{
>> + int rc = -EOPNOTSUPP;
>> +#ifdef CONFIG_CIFS_XATTR
>> +#ifdef CONFIG_CIFS_POSIX
>> + struct dentry *dentry = NULL;
>> + size_t buf_size;
>> + void *ea_value = NULL;
>> + ssize_t ea_size;
>> + struct posix_acl *acl = NULL;
>
> I don't think you need to initialise ea_value, do you?
While currently correct, I find it a good idea to immediately null any pointer
that is freed in the exit section. Otherwise it is very easy to forget to do
that the day someone adds a goto prior to the first assignment, and not nulling
then can have unintended consequences in unrelated code. That being said, if
you say that the kernel community frowns upon that kind of defensive
programming I will definitely remove it.
With your suggested improvement below, there is no need to NULL dentry though,
so I'll remove that.
>> + /* CIFS gets acl from server by path, and thus needs a dentry rather
>> than
>> + an inode. Note that the path of each dentry will point to the
>> same inode
>> + on the backing fs at the server, so their acls will be
>> the same, and it
>> + doesn't matter which one we pick, so just pick the
>> fist. */
>> + if (list_empty(&inode->i_dentry))
>> + dentry = list_first_entry(&inode->i_dentry, struct dentry,
>> d_alias);
>> +
>> + /* If we didn't get an dentry for the inode, something went terribly
>> wrong.
>> + All we can do at this point is to return an error. */
>> + if (!dentry || IS_ERR(dentry))
>> + return PTR_ERR(dentry);
>
> I don't think dentry can possibly be IS_ERR here, and returning
> PTR_ERR(NULL) doesn't do what you think it does. Also, you've got the
> sense of list_empty backwards. I think what you meant to do here was:
>
> if (!list_empty(&inode->i_dentry))
> dentry = list_first_entry(&inode->i_dentry, struct dentry,
> d_alias);
> else
> return -EINVAL;
Sure, this makes more sense. Originally I used d_find_alias(inode), but later
thought it was better to use the information in the inode struct directly.
I never really understood how list.h lists was supposed to work, but stopped
when cut-and-paste got it working for me. Probably a bad idea...
Attaching an updated patch, mostly for the convenience of anyone only
following linux-cifs-client.
And thanks again for your input.
Best Regards
Jon Severinsson
[-- Attachment #2: cifs-acl-permission-check-v2.patch --]
[-- Type: text/x-patch, Size: 4882 bytes --]
commit fa0b9415cda17b31966542101bc4ceb0c97c87cb
Author: Jon Severinsson <jon@severinsson.net>
Date: Mon Mar 1 19:24:30 2010 +0100
[CIFS] Adds support for permision checking vs. posix acl.
CIFS already supports getting and setting acls through getfacl and setfacl, but
prior to this patch, any acls was ignored when doing permission checking.
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 29f1da7..0605e11 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -284,7 +284,7 @@ static int cifs_permission(struct inode *inode, int mask)
on the client (above and beyond ACL on servers) for
servers which do not support setting and viewing mode bits,
so allowing client to check permissions is useful */
- return generic_permission(inode, mask, NULL);
+ return generic_permission(inode, mask, inode->i_op->check_acl);
}
static struct kmem_cache *cifs_inode_cachep;
@@ -702,6 +702,9 @@ const struct inode_operations cifs_dir_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
@@ -716,6 +719,9 @@ const struct inode_operations cifs_file_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
@@ -732,6 +738,9 @@ const struct inode_operations cifs_symlink_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index ac2b24c..6409a83 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -102,11 +102,15 @@ extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
int buflen);
extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
const char *symname);
+
+/* Functions related to extended attributes */
extern int cifs_removexattr(struct dentry *, const char *);
extern int cifs_setxattr(struct dentry *, const char *, const void *,
size_t, int);
extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
+extern int cifs_check_acl(struct inode *inode, int mask);
+
extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_CIFS_EXPERIMENTAL
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
index a75afa3..a07633b 100644
--- a/fs/cifs/xattr.c
+++ b/fs/cifs/xattr.c
@@ -374,3 +374,71 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
#endif
return rc;
}
+
+int cifs_check_acl(struct inode *inode, int mask)
+{
+ int rc = -EOPNOTSUPP;
+#ifdef CONFIG_CIFS_XATTR
+#ifdef CONFIG_CIFS_POSIX
+ struct dentry *dentry;
+ size_t buf_size;
+ void *ea_value = NULL;
+ ssize_t ea_size;
+ struct posix_acl *acl = NULL;
+
+ /* CIFS gets acl from server by path, and thus needs a dentry rather than
+ an inode. Note that the path of each dentry will point to the same inode
+ on the backing fs at the server, so their acls will be the same, and it
+ doesn't matter which one we pick, so just pick the fist. */
+ if (!list_empty(&inode->i_dentry))
+ dentry = list_first_entry(&inode->i_dentry, struct dentry, d_alias);
+ else
+ return -EINVAL;
+
+ /* Try to fit the extended attribute corresponding to the posix acl in 4k
+ memory. 4k was chosen because it always fits in a single page, and is
+ the maximum on a default ext2/3/4 backing fs. */
+ buf_size = 4096;
+ ea_value = kmalloc(buf_size, GFP_KERNEL);
+ if (!ea_value) {
+ rc = -EAGAIN;
+ goto check_acl_exit;
+ }
+ ea_size = cifs_getxattr(dentry, POSIX_ACL_XATTR_ACCESS, ea_value, buf_size);
+
+ /* If 4k wasn't enough, try 64k, the maximum on any current backing fs. */
+ if (ea_size == -ERANGE) {
+ kfree(ea_value);
+ buf_size = 65536;
+ ea_value = kmalloc(buf_size, GFP_KERNEL);
+ if (!ea_value) {
+ rc = -EAGAIN;
+ goto check_acl_exit;
+ }
+ ea_size = cifs_getxattr(dentry, POSIX_ACL_XATTR_ACCESS, ea_value, buf_size);
+ }
+
+ /* If we didn't get any extended attribute, set the error code and exit */
+ if (ea_size <= 0) {
+ rc = -EAGAIN;
+ if (ea_size == -EOPNOTSUPP || ea_size == -EIO || ea_size == -ENOTDIR || ea_size == -ENOENT || ea_size == -EFAULT || ea_size == -EACCES)
+ rc = ea_size;
+ goto check_acl_exit;
+ }
+
+ /* Set the appropriate return value. Adapted from ext4. */
+ acl = posix_acl_from_xattr(ea_value, ea_size);
+ if (IS_ERR(acl))
+ rc = PTR_ERR(acl);
+ else if (acl)
+ rc = posix_acl_permission(inode, acl, mask);
+ else
+ rc = -EAGAIN;
+
+check_acl_exit:
+ posix_acl_release(acl);
+ kfree(ea_value);
+#endif /* CONFIG_CIFS_POSIX */
+#endif /* CONFIG_CIFS_XATTR */
+ return rc;
+}
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [RFC PATCH] CIFS posix acl permission checking
@ 2010-03-04 10:50 Jon Severinsson
2010-03-04 13:44 ` [linux-cifs-client] " simo
0 siblings, 1 reply; 6+ messages in thread
From: Jon Severinsson @ 2010-03-04 10:50 UTC (permalink / raw)
To: linux-cifs-client; +Cc: linux-fsdevel, linux-kernel
[-- Attachment #1: Type: Text/Plain, Size: 2980 bytes --]
Hello
Early this weak I sent a patch implementing posix acl permission checking in
the linux cifs filesystem module. Unfortunately I only sent it to linux-fsdev
as I was unaware of the linux-cifs-client list. I later tried to submit it to
linux-cifs-client as well, but my message seems to have been lost in the
moderation queue, so I subscribed and am trying again.
I don't believe my patch is perfect, but I think it's a good start, and would
like some comments from more experienced cifs developers to be able to get it
into shape for inclusion in the kernel.
I did get some comments from Matthew Wilcox at linux-fsdev, but unfortunately
he never followed up on my response, so I'm including some unresolved
questions I still have, as well as attaching the patch for further comments.
Best Regards
Jon Severinsson
On Monday 01 March 2010 19:33:41, Jon Severinsson wrote:
> On Monday 01 March 2010 18:03:49, Matthew Wilcox wrote:
>> You've included ifdefs around the check_acl entry in inode_operations,
>> *and* inside the definition of cifs_check_acl. You only need to do
>> one or the other, and opinion is divided on which is better.
>
> While I did recognize the redundancy, I decided to follow the same
> convention the other functions in xattr.c did, and include ifdefs at both
> locations.
>
> I also considered the possible reasons for the existing functions to do
> both, and and came up with two reasons. The first simply being the paradigm
> of defensive programming, always check before doing a call, but never assume
> that the check has been done before being called. The second one is that of
> performance. The ifdefs has to be in cifs_check_acl to protect against other
> callers (while this patch doesn't introduce any, it doesn't prevent further
> patches from adding them), and not including the ifdefs in inode_operations
> would mean a completely useless function call when a feature was turned off
> at compile time. The second one is a micro-optimization I don't really care
> fore, but defensive programming I do respect.
>
> With this in mind, what do you recommend, double protection, breaking
> convention or changing the existing code?
>
>>> +int cifs_check_acl(struct inode *inode, int mask)
>>> +{
>>> + int rc = -EOPNOTSUPP;
>>> +#ifdef CONFIG_CIFS_XATTR
>>> +#ifdef CONFIG_CIFS_POSIX
>>> + struct dentry *dentry;
>>> + size_t buf_size;
>>> + void *ea_value = NULL;
>>> + ssize_t ea_size;
>>> + struct posix_acl *acl = NULL;
>>
>> I don't think you need to initialise ea_value, do you?
>
> While currently correct, I find it a good idea to immediately null any
> pointer that is freed in the exit section. Otherwise it is very easy to
> forget to do that the day someone adds a goto prior to the first
> assignment, and not nulling then can have unintended consequences in
> unrelated code. That being said, if you say that the kernel community
> frowns upon that kind of defensive programming I will definitely remove
> it.
[-- Attachment #2: cifs-acl-permission-check-v2.patch --]
[-- Type: text/x-patch, Size: 4882 bytes --]
commit fa0b9415cda17b31966542101bc4ceb0c97c87cb
Author: Jon Severinsson <jon@severinsson.net>
Date: Mon Mar 1 19:24:30 2010 +0100
[CIFS] Adds support for permision checking vs. posix acl.
CIFS already supports getting and setting acls through getfacl and setfacl, but
prior to this patch, any acls was ignored when doing permission checking.
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 29f1da7..0605e11 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -284,7 +284,7 @@ static int cifs_permission(struct inode *inode, int mask)
on the client (above and beyond ACL on servers) for
servers which do not support setting and viewing mode bits,
so allowing client to check permissions is useful */
- return generic_permission(inode, mask, NULL);
+ return generic_permission(inode, mask, inode->i_op->check_acl);
}
static struct kmem_cache *cifs_inode_cachep;
@@ -702,6 +702,9 @@ const struct inode_operations cifs_dir_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
@@ -716,6 +719,9 @@ const struct inode_operations cifs_file_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
@@ -732,6 +738,9 @@ const struct inode_operations cifs_symlink_inode_ops = {
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
+#ifdef CONFIG_CIFS_POSIX
+ .check_acl = cifs_check_acl,
+#endif
#endif
};
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index ac2b24c..6409a83 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -102,11 +102,15 @@ extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
int buflen);
extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
const char *symname);
+
+/* Functions related to extended attributes */
extern int cifs_removexattr(struct dentry *, const char *);
extern int cifs_setxattr(struct dentry *, const char *, const void *,
size_t, int);
extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
+extern int cifs_check_acl(struct inode *inode, int mask);
+
extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_CIFS_EXPERIMENTAL
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
index a75afa3..a07633b 100644
--- a/fs/cifs/xattr.c
+++ b/fs/cifs/xattr.c
@@ -374,3 +374,71 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
#endif
return rc;
}
+
+int cifs_check_acl(struct inode *inode, int mask)
+{
+ int rc = -EOPNOTSUPP;
+#ifdef CONFIG_CIFS_XATTR
+#ifdef CONFIG_CIFS_POSIX
+ struct dentry *dentry;
+ size_t buf_size;
+ void *ea_value = NULL;
+ ssize_t ea_size;
+ struct posix_acl *acl = NULL;
+
+ /* CIFS gets acl from server by path, and thus needs a dentry rather than
+ an inode. Note that the path of each dentry will point to the same inode
+ on the backing fs at the server, so their acls will be the same, and it
+ doesn't matter which one we pick, so just pick the fist. */
+ if (!list_empty(&inode->i_dentry))
+ dentry = list_first_entry(&inode->i_dentry, struct dentry, d_alias);
+ else
+ return -EINVAL;
+
+ /* Try to fit the extended attribute corresponding to the posix acl in 4k
+ memory. 4k was chosen because it always fits in a single page, and is
+ the maximum on a default ext2/3/4 backing fs. */
+ buf_size = 4096;
+ ea_value = kmalloc(buf_size, GFP_KERNEL);
+ if (!ea_value) {
+ rc = -EAGAIN;
+ goto check_acl_exit;
+ }
+ ea_size = cifs_getxattr(dentry, POSIX_ACL_XATTR_ACCESS, ea_value, buf_size);
+
+ /* If 4k wasn't enough, try 64k, the maximum on any current backing fs. */
+ if (ea_size == -ERANGE) {
+ kfree(ea_value);
+ buf_size = 65536;
+ ea_value = kmalloc(buf_size, GFP_KERNEL);
+ if (!ea_value) {
+ rc = -EAGAIN;
+ goto check_acl_exit;
+ }
+ ea_size = cifs_getxattr(dentry, POSIX_ACL_XATTR_ACCESS, ea_value, buf_size);
+ }
+
+ /* If we didn't get any extended attribute, set the error code and exit */
+ if (ea_size <= 0) {
+ rc = -EAGAIN;
+ if (ea_size == -EOPNOTSUPP || ea_size == -EIO || ea_size == -ENOTDIR || ea_size == -ENOENT || ea_size == -EFAULT || ea_size == -EACCES)
+ rc = ea_size;
+ goto check_acl_exit;
+ }
+
+ /* Set the appropriate return value. Adapted from ext4. */
+ acl = posix_acl_from_xattr(ea_value, ea_size);
+ if (IS_ERR(acl))
+ rc = PTR_ERR(acl);
+ else if (acl)
+ rc = posix_acl_permission(inode, acl, mask);
+ else
+ rc = -EAGAIN;
+
+check_acl_exit:
+ posix_acl_release(acl);
+ kfree(ea_value);
+#endif /* CONFIG_CIFS_POSIX */
+#endif /* CONFIG_CIFS_XATTR */
+ return rc;
+}
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] CIFS posix acl permission checking
2010-03-04 13:44 ` [linux-cifs-client] " simo
@ 2010-03-04 15:21 ` Jon Severinsson
0 siblings, 0 replies; 6+ messages in thread
From: Jon Severinsson @ 2010-03-04 15:21 UTC (permalink / raw)
To: linux-cifs-client; +Cc: linux-fsdevel, linux-kernel
Den Thursday 04 March 2010 14:44:22 skrev du:
> although you did a good job with the code itself, I have to say that I
> think the approach is just wrong. Checking ACLs on the client is simply
> the wrong way to go. It is just racy and it is not authoritative anyway.
The problem with this approach is that the server checks the permissions using
the credentials of the user doing the mount, not the user trying to access the
file. This leaves a big gaping security hole on any multiuser system, and even
on single user systems you sill loose any benefits from privilege separation
(such as running system daemons as an unprivileged user).
So while it is possible to do it that way (by including the noperm mount
option when mounting), it is generally a bad idea to do so.
As you are going to do permission checking on the client side anyway, I do not
see the problem consulting the acl when doing so. As not consulting the acl
only restricts access to files I'm supposed to have access to (and indeed has
access to if using smbclient), it only results in me being forced to use the
uid, gid, file_mode, and/or dir_mode mount options, at witch point I can't see
whether I have access or not other than by trying to do the access, as it will
always look as if I have access.
> It is like trying to look up a path using a cached directory listing and
> then try to open by inode. It simply doesn't work, by the time you do
> that, things may have been completely changed on the server.
I'm afraid I'm not sure exactly what you mean by this section, could you
please clarify.
> And we are not counting the problem that with CIFS (and samba in
> particular) the client have no way to know what are the real credentials
> assigned to the session and that the client may have no idea what the
> users and groups in the ACL are (if client and server do not use exactly
> the same user database).
Of course this only works as intended if the client and server shares the same
user database. In my case that is through ldap, but could as well be nis,
winbind or some other similar nss module. However, that is the case already,
without any acl, as UNIX permission bits have exactly the same problem. If you
are mounting something from a server with a different user database, you'll
need to use the uid, gid, file_mode, and/or dir_mode mount options, but that is
no different from the situation without this patch.
> The right way is to let the server enforce ACLs, and use multisessions
> mounts if multiple users are involved. Time would be better spent
> working in that direction IMO.
If by multisession mounts you mean something like nfs, where the client tells
the server what user is trying to do the access, that can only work if either
the server blindly trusts the client (as in nfs3) or with some complex
cryptographic token infrastructure (as in nfs4 with kerberos authentication).
The first case is simply not very secure, and a cryptographic token
infrastructure is needlessly complex. If someone were to work on making
setting up a cryptographic token infrastructure simpler I would love that, but
I'm not even remotely qualified to do so, and in the meantime the current samba
infrastructure fulfils all my need, except that the cifs module erroneously
refuses me the rights I'm granted through an acl.
> Simo.
Regards
Jon Severinsson
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2010-03-04 15:21 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-03-01 15:15 [RFC PATCH] CIFS posix acl permission checking Jon Severinsson
2010-03-01 17:03 ` Matthew Wilcox
2010-03-01 18:33 ` Jon Severinsson
-- strict thread matches above, loose matches on Subject: below --
2010-03-01 16:00 Jon Severinsson
2010-03-04 10:50 Jon Severinsson
2010-03-04 13:44 ` [linux-cifs-client] " simo
2010-03-04 15:21 ` Jon Severinsson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox