Message ID | 201003041150.08341.jon@severinsson.net |
---|---|
State | New |
Headers | show |
On Thu, 2010-03-04 at 11:50 +0100, Jon Severinsson wrote: > 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. Hi Jon, 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. 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. 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). 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. Simo.
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
On Thu, 2010-03-04 at 16:21 +0100, Jon Severinsson wrote: > 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). Letting a different user access the mount point *is* a security violation in itself. The CIFS security model lies in per user sessions. The right way to fix the problem is multi-session mounts. Allowing a different user to use a user session is a violation of the security model of CIFS. Trying to put a bandaid on a gross policy and security violation is not really useful :) > 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, This is the problem, you shouldn't. CIFS is not NFS, you are abusing your access credentials simply because they are stored in the kernel. > 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. Sorry, but just piling up errors on top of other errors does not really make the situation better. > > 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. Yes, the current situation is not right either, and definitely need fixing. > > 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). No, you cannot "tell" the server who you are like in NFS, this is the main point. CIFS has a security model that is different from (old) NFS. In CIFS each session is authenticated, that's why you should have multi-session if you want to access the same mount point as different users. Like NFSv4 you should authenticate each user first, create it's own session and then let him in, or deny access completely (or degrade access to the guest user if the remote server allow you so). > The first case is simply not very secure, The first case is simply not possible with CIFS. > and a cryptographic token infrastructure is needlessly complex. Yet there is no real alternative, short of changing the CIFS model to allow the server to trust the client machine. > 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. We already have the cifs.upcall thing, which is the prerequisite to get multi-session work at least with kerberos. And it can be easily extended to allow also to use NTLM cached credentials. That is a goal that would be better pursuing. Simo.
On Thu, 4 Mar 2010 11:50:04 +0100 Jon Severinsson <jon@severinsson.net> wrote: > 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 > (cc'ing Michael Adam since he's been working on a similar patch) I've looked over the patch and it's pretty good for a first attempt. There are a few minor problems with it, but it's a reasonable first pass. The issues I have are with the approach -- you've stepped into a bit of a minefield with regards to CIFS' design. The issues that Simo brings up however are quite valid. Let me just state outright that permission checking on the client is a completely bogus concept. There are a couple of problems with this approach: 1) Someone could change the permissions on the server between the time you check them and when you do your operation. Yes, I know that CIFS already does this for permission bits, but that's dumb too. 2) Ownership matters. This patch will have no effect on how files get created. They still get created with the owner set to the user who's credentials were used, and you can't change them afterward (since users can't "chown" files to other users). Now, it is possible to mount a samba server with root's credentials. Then you can use the "setuids" mount option to make chown's work and you *might* get files created the way you intend. I really, really do not recommend this though -- it's a bad idea to allow any user to share root's server credentials. I'm convinced, after working on it for more than 3 years that the *only* proper fix for the nightmare that is CIFS permissions is to make CIFS use proper credentials in the first place. CIFS is currently completely broken in this regard -- it's designed such that all accesses to the mount use the same set of credentials, and that's just plain wrong. Fixing this entails establishing new SMB sessions on the fly whenever a user wants to do an on the wire operation. Obviously, we can't prompt for passwords for this, so we need to limit this to krb5 creds or come up with a way for people to stash credentials for the kernel to use for this purpose. I'm about 1/3 of the way into a first draft of a patchset to do this, but it won't be fixed anytime soon and may not be suitable for CIFS with SMB2 getting development focus now. Now, all of that said...I don't have a specific issue with adding a patch to do this. I think this approach is completely ass-backwards and broken, but we do already attempt to enforce permission bits on the client currently. Adding checks for POSIX acl's don't make this (much) worse. The one thing I don't think we want though is to turn this on by default since it means an extra round trip to the server every time you want to check permissions. This needs to be "switchable" somehow -- possibly via mount option (maybe -o checkposixacls or something?) > 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. */ ^^^ comments should follow kernel coding styles. Be sure to run your patch through scripts/checkpatch.pl too. > + if (!list_empty(&inode->i_dentry)) > + dentry = list_first_entry(&inode->i_dentry, struct dentry, d_alias); ^^^^ you should probably dget this dentry to take a reference to it and then put it when you're finished with it. It could vanish out of the cache before you have a chance to use it otherwise. > + 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; > +} The rest of the patch looks reasonable but I agree with Simo that all of our time would be better spent working to make CIFS use proper credentials on the wire.
On Thu, Mar 04, 2010 at 10:51:53AM -0500, simo wrote: > > Letting a different user access the mount point *is* a security > violation in itself. The CIFS security model lies in per user sessions. > The right way to fix the problem is multi-session mounts. Allowing a > different user to use a user session is a violation of the security > model of CIFS. Multi-session mounts are the only sane fix. This is what Windows does in their redirectory (when a process with different credentials traverses into a mount point a new sessionsetup is done to get remote credentials). Jeremy.
Jeff Layton wrote: > On Thu, 4 Mar 2010 11:50:04 +0100 > Jon Severinsson <jon@severinsson.net> wrote: > > > 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. > > > > ... > > (cc'ing Michael Adam since he's been working on a similar patch) > > I've looked over the patch and it's pretty good for a first attempt. > There are a few minor problems with it, but it's a reasonable first > pass. > > The issues I have are with the approach -- you've stepped into a bit of > a minefield with regards to CIFS' design. The issues that Simo brings up > however are quite valid. Let me just state outright that permission > checking on the client is a completely bogus concept. There are a > couple of problems with this approach: > > 1) Someone could change the permissions on the server between the time > you check them and when you do your operation. Yes, I know that CIFS > already does this for permission bits, but that's dumb too. > > 2) Ownership matters. This patch will have no effect on how files get > created. They still get created with the owner set to the user who's > credentials were used, and you can't change them afterward (since users > can't "chown" files to other users). Now, it is possible to mount a > samba server with root's credentials. Then you can use the "setuids" > mount option to make chown's work and you *might* get files created the > way you intend. I really, really do not recommend this though -- it's a > bad idea to allow any user to share root's server credentials. I completely agree that client side checking is bogus. And as already mentioned in a different mail, the "setuids" option seems to be broken currently (in my test cases at least). > I'm convinced, after working on it for more than 3 years that the *only* > proper fix for the nightmare that is CIFS permissions is to make CIFS > use proper credentials in the first place. CIFS is currently completely > broken in this regard -- it's designed such that all accesses to the > mount use the same set of credentials, and that's just plain wrong. > > Fixing this entails establishing new SMB sessions on the fly whenever a > user wants to do an on the wire operation. Obviously, we can't prompt > for passwords for this, so we need to limit this to krb5 creds or come > up with a way for people to stash credentials for the kernel to use for > this purpose. So this method of "stashing" creds would usually involve some kind of upcall to e.g. query the winbindd credential cache or some static text file, right? When discussing this with Volker today, he had a different idea: One could implement a trans2 impersonate call in samba (as a new call in the unix extensions) that could be used to transfer the session established by the privileged user (root, say) to a different user specified as an argument to the call -- without the need to give credentials! Then this call could be used in the multi user mount scenario: when uid 1000 accesse the cifs mount then the root-dispatcher mount would create a new session initially as root and issue an impersonate call to user 1000 directly afterwards. Wouldn't that be something worth considering? > I'm about 1/3 of the way into a first draft of a patchset > to do this, but it won't be fixed anytime soon and may not be suitable > for CIFS with SMB2 getting development focus now. Sorry, what do you mean? Is SMB2 somehow contradictory to multi session mounts? Cheers - Michael > Now, all of that said...I don't have a specific issue with adding a > patch to do this. I think this approach is completely ass-backwards and > broken, but we do already attempt to enforce permission bits on the > client currently. Adding checks for POSIX acl's don't make this (much) > worse. > > The one thing I don't think we want though is to turn this on by > default since it means an extra round trip to the server every time > you want to check permissions. This needs to be "switchable" somehow -- > possibly via mount option (maybe -o checkposixacls or something?) > > > 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. */ > > ^^^ comments should follow kernel coding styles. Be sure to run your > patch through scripts/checkpatch.pl too. > > > + if (!list_empty(&inode->i_dentry)) > > + dentry = list_first_entry(&inode->i_dentry, struct dentry, d_alias); > > ^^^^ you should probably dget this dentry to take a > reference to it and then put it when you're finished > with it. It could vanish out of the cache before you > have a chance to use it otherwise. > > > + 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; > > +} > > The rest of the patch looks reasonable but I agree with Simo that all > of our time would be better spent working to make CIFS use proper > credentials on the wire. > > -- > Jeff Layton <jlayton@samba.org>
On Thu, 11 Mar 2010 23:45:29 +0100 Michael Adam <obnox@samba.org> wrote: > Jeff Layton wrote: > > On Thu, 4 Mar 2010 11:50:04 +0100 > > Jon Severinsson <jon@severinsson.net> wrote: > > > > > 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. > > > > > > ... > > > > (cc'ing Michael Adam since he's been working on a similar patch) > > > > I've looked over the patch and it's pretty good for a first attempt. > > There are a few minor problems with it, but it's a reasonable first > > pass. > > > > The issues I have are with the approach -- you've stepped into a bit of > > a minefield with regards to CIFS' design. The issues that Simo brings up > > however are quite valid. Let me just state outright that permission > > checking on the client is a completely bogus concept. There are a > > couple of problems with this approach: > > > > 1) Someone could change the permissions on the server between the time > > you check them and when you do your operation. Yes, I know that CIFS > > already does this for permission bits, but that's dumb too. > > > > 2) Ownership matters. This patch will have no effect on how files get > > created. They still get created with the owner set to the user who's > > credentials were used, and you can't change them afterward (since users > > can't "chown" files to other users). Now, it is possible to mount a > > samba server with root's credentials. Then you can use the "setuids" > > mount option to make chown's work and you *might* get files created the > > way you intend. I really, really do not recommend this though -- it's a > > bad idea to allow any user to share root's server credentials. > > I completely agree that client side checking is bogus. > And as already mentioned in a different mail, the "setuids" > option seems to be broken currently (in my test cases at least). > Ok, some investigation as to the cause there might be helpful. That said, the whole idea of mounting using root's creds seems a bit dangerous to me. I can't point to a specific attack vector that employs mounting with root creds, but it "smells" like bad practice. > > I'm convinced, after working on it for more than 3 years that the *only* > > proper fix for the nightmare that is CIFS permissions is to make CIFS > > use proper credentials in the first place. CIFS is currently completely > > broken in this regard -- it's designed such that all accesses to the > > mount use the same set of credentials, and that's just plain wrong. > > > > Fixing this entails establishing new SMB sessions on the fly whenever a > > user wants to do an on the wire operation. Obviously, we can't prompt > > for passwords for this, so we need to limit this to krb5 creds or come > > up with a way for people to stash credentials for the kernel to use for > > this purpose. > > So this method of "stashing" creds would usually involve some > kind of upcall to e.g. query the winbindd credential cache or > some static text file, right? > Actually, I was more envisioning using the kernel keyring to stash a username, and password or NTLM hash. The kernel can then scoop those up as needed without needing to upcall. You could do this per-user or per-session too. Eventually we want to overhaul the krb5 code to do less in userspace too. See the keyctl_* manpages for details on that facility. > When discussing this with Volker today, he had a different idea: > One could implement a trans2 impersonate call in samba (as a new > call in the unix extensions) that could be used to transfer the > session established by the privileged user (root, say) to a > different user specified as an argument to the call -- without > the need to give credentials! Then this call could be used in > the multi user mount scenario: when uid 1000 accesse the cifs > mount then the root-dispatcher mount would create a new session > initially as root and issue an impersonate call to user 1000 > directly afterwards. > > Wouldn't that be something worth considering? > I've only just read this, and haven't thought about it fully, but at first glance that looks like the wrong way to go about fixing this. For one thing, this is a samba only extension. For another, you're still allowing "credential sharing" to some degree and that scheme encourages people to mount using root's credentials. The bottom line (IMO) is that any scheme that has multiple users sharing credentials on a single SMB session is just a bad solution. Credentials exist for a reason and really shouldn't be shared. > > I'm about 1/3 of the way into a first draft of a patchset > > to do this, but it won't be fixed anytime soon and may not be suitable > > for CIFS with SMB2 getting development focus now. > > Sorry, what do you mean? Is SMB2 somehow contradictory to multi > session mounts? > No, but the SMB2 code is a completely separate project however. What there is of it today consists largely of code and functionality that is cut-and-pasted from CIFS. Any fixes that gets implemented in CIFS will need to be essentially duplicated for SMB2.
On Thu, Mar 11, 2010 at 11:45:29PM +0100, Michael Adam wrote: > > When discussing this with Volker today, he had a different idea: > One could implement a trans2 impersonate call in samba (as a new > call in the unix extensions) that could be used to transfer the > session established by the privileged user (root, say) to a > different user specified as an argument to the call -- without > the need to give credentials! Then this call could be used in > the multi user mount scenario: when uid 1000 accesse the cifs > mount then the root-dispatcher mount would create a new session > initially as root and issue an impersonate call to user 1000 > directly afterwards. > > Wouldn't that be something worth considering? This world work, but protocol cleanliness-wise it's *really* horrible :-). Jeremy.
Jeremy Allison wrote: > On Thu, Mar 11, 2010 at 11:45:29PM +0100, Michael Adam wrote: > > > > When discussing this with Volker today, he had a different idea: > > One could implement a trans2 impersonate call in samba (as a new > > call in the unix extensions) that could be used to transfer the > > session established by the privileged user (root, say) to a > > different user specified as an argument to the call -- without > > the need to give credentials! Then this call could be used in > > the multi user mount scenario: when uid 1000 accesse the cifs > > mount then the root-dispatcher mount would create a new session > > initially as root and issue an impersonate call to user 1000 > > directly afterwards. > > > > Wouldn't that be something worth considering? > > This world work, but protocol cleanliness-wise it's > *really* horrible :-). Agreed. :-)
On Fri, Mar 12, 2010 at 09:09:11AM +0100, Michael Adam wrote: > > > Wouldn't that be something worth considering? > > > > This world work, but protocol cleanliness-wise it's > > *really* horrible :-). > > Agreed. :-) But what's the alternative? Let NFS go and do that piece better forever? :-) Volker
On Fri, 12 Mar 2010 09:17:34 +0100 Volker Lendecke <Volker.Lendecke@SerNet.DE> wrote: > On Fri, Mar 12, 2010 at 09:09:11AM +0100, Michael Adam wrote: > > > > Wouldn't that be something worth considering? > > > > > > This world work, but protocol cleanliness-wise it's > > > *really* horrible :-). > > > > Agreed. :-) > > But what's the alternative? Let NFS go and do that piece > better forever? :-) > > Volker Establish sessions as needed, based on a user's own credentials and have the kernel use that session/tcon combinaton instead of those established at mount time. My goal is to have a prototype of this to present at SambaXP, but I may have an initial set of patches in the next few weeks.
On Fri, Mar 12, 2010 at 07:18:32AM -0500, Jeff Layton wrote: > > But what's the alternative? Let NFS go and do that piece > > better forever? :-) > > > > Volker > > Establish sessions as needed, based on a user's own credentials and > have the kernel use that session/tcon combinaton instead of those > established at mount time. My goal is to have a prototype of this to > present at SambaXP, but I may have an initial set of patches in the > next few weeks. Ok, then we rule out batch machines where there are no user credentials. NFS does this fine. I know this is REALLY ugly, but I have customers who need this. If you have a good solution for that problem, I would really be happy to hear this. Something like constrained delegation in Kerberos to me sounds pretty much like the exact same hack in a different place. Volker
On Fri, 2010-03-12 at 13:23 +0100, Volker Lendecke wrote: > On Fri, Mar 12, 2010 at 07:18:32AM -0500, Jeff Layton wrote: > > > But what's the alternative? Let NFS go and do that piece > > > better forever? :-) > > > > > > Volker > > > > Establish sessions as needed, based on a user's own credentials and > > have the kernel use that session/tcon combinaton instead of those > > established at mount time. My goal is to have a prototype of this to > > present at SambaXP, but I may have an initial set of patches in the > > next few weeks. > > Ok, then we rule out batch machines where there are no user > credentials. NFS does this fine. I know this is REALLY ugly, > but I have customers who need this. If you have a good > solution for that problem, I would really be happy to hear > this. Something like constrained delegation in Kerberos to > me sounds pretty much like the exact same hack in a > different place. The solution in those case is probably S4U2PROXY, or NFS. Simo.
On Fri, Mar 12, 2010 at 07:35:42AM -0500, simo wrote: > > Ok, then we rule out batch machines where there are no user > > credentials. NFS does this fine. I know this is REALLY ugly, > > but I have customers who need this. If you have a good > > solution for that problem, I would really be happy to hear > > this. Something like constrained delegation in Kerberos to > > me sounds pretty much like the exact same hack in a > > different place. > > The solution in those case is probably S4U2PROXY, or NFS. The reason why my customer wants to get away from NFS is the 16 groups limit. Different question: Why is s4u2proxy more secure than allowing "su - <user>" over cifs? Volker
On Fri, 2010-03-12 at 13:50 +0100, Volker Lendecke wrote: > On Fri, Mar 12, 2010 at 07:35:42AM -0500, simo wrote: > > > Ok, then we rule out batch machines where there are no user > > > credentials. NFS does this fine. I know this is REALLY ugly, > > > but I have customers who need this. If you have a good > > > solution for that problem, I would really be happy to hear > > > this. Something like constrained delegation in Kerberos to > > > me sounds pretty much like the exact same hack in a > > > different place. > > > > The solution in those case is probably S4U2PROXY, or NFS. > > The reason why my customer wants to get away from NFS is the > 16 groups limit. Different question: Why is s4u2proxy more > secure than allowing "su - <user>" over cifs? Because you can control at the KDC level which tickets the server is allowed to get. And without giving out user credentials or even root credentials. And because this way you don't change the security model. Simo.
On Fri, Mar 12, 2010 at 07:58:03AM -0500, simo wrote: > > The reason why my customer wants to get away from NFS is the > > 16 groups limit. Different question: Why is s4u2proxy more > > secure than allowing "su - <user>" over cifs? > > Because you can control at the KDC level which tickets the server is > allowed to get. And without giving out user credentials or even root > credentials. And because this way you don't change the security model. Ok. Which KDCs support this? Volker
On Fri, 2010-03-12 at 14:03 +0100, Volker Lendecke wrote: > On Fri, Mar 12, 2010 at 07:58:03AM -0500, simo wrote: > > > The reason why my customer wants to get away from NFS is the > > > 16 groups limit. Different question: Why is s4u2proxy more > > > secure than allowing "su - <user>" over cifs? > > > > Because you can control at the KDC level which tickets the server is > > allowed to get. And without giving out user credentials or even root > > credentials. And because this way you don't change the security model. > > Ok. Which KDCs support this? AD and MIT 1.8, although I the latter one may require so work to make the delegation easier to manage. Simo.
On Fri, Mar 12, 2010 at 01:23:01PM +0100, Volker Lendecke wrote: > On Fri, Mar 12, 2010 at 07:18:32AM -0500, Jeff Layton wrote: > > > But what's the alternative? Let NFS go and do that piece > > > better forever? :-) > > > > > > Volker > > > > Establish sessions as needed, based on a user's own credentials and > > have the kernel use that session/tcon combinaton instead of those > > established at mount time. My goal is to have a prototype of this to > > present at SambaXP, but I may have an initial set of patches in the > > next few weeks. > > Ok, then we rule out batch machines where there are no user > credentials. NFS does this fine. I know this is REALLY ugly, > but I have customers who need this. If you have a good > solution for that problem, I would really be happy to hear > this. Something like constrained delegation in Kerberos to > me sounds pretty much like the exact same hack in a > different place. If you want to code this up in the server, I have no objections. Sort of a passwordless "sessionsetup" that returns a new vuid. I think it should be a Samba-only extension though. Too ugly to propose to the wider unix-extension community :-). Jeremy.
On Fri, 12 Mar 2010 13:23:01 +0100 Volker Lendecke <Volker.Lendecke@SerNet.DE> wrote: > On Fri, Mar 12, 2010 at 07:18:32AM -0500, Jeff Layton wrote: > > > But what's the alternative? Let NFS go and do that piece > > > better forever? :-) > > > > > > Volker > > > > Establish sessions as needed, based on a user's own credentials and > > have the kernel use that session/tcon combinaton instead of those > > established at mount time. My goal is to have a prototype of this to > > present at SambaXP, but I may have an initial set of patches in the > > next few weeks. > > Ok, then we rule out batch machines where there are no user > credentials. NFS does this fine. I know this is REALLY ugly, > but I have customers who need this. If you have a good > solution for that problem, I would really be happy to hear > this. Something like constrained delegation in Kerberos to > me sounds pretty much like the exact same hack in a > different place. > I'm not familiar with constrained delegation, so I'll have to read up on that before I can comment on it. If you want to do something like this, why bother with a privileged session at all? Why not just add a new authentication method for CIFS (under GSSAPI maybe?) that works like AUTH_SYS but without the arbitrary group list limit? The server could be configured then to permit this auth scheme only for certain clients. ...or once I (or someone else) get multisession mounts working, you could generate a krb5 keytab per uid, put them in a well-defined location and then teach cifs.upcall how to find them. That seems like a reasonable idea that doesn't involve extending the protocol. For that matter, they could do something similar with NFS (teach rpc.gssd how to find user keytabs). Now, all that said -- like Jeremy I wouldn't have a specific objection to adding what you suggest, but for one thing: CIFS is already a god-awful mess when it comes to handling permissions. There are a ton of options that affect how it deals with permissions. Many of them conflict and the effect of their union is poorly understood. It's a maintenance and support *nightmare*. Adding this means adding more options and yet another set of behavior, and that worries me.
2010/3/12 Jeff Layton <jlayton@samba.org>: > > CIFS is already a god-awful mess when it comes to handling permissions. > There are a ton of options that affect how it deals with permissions. > Many of them conflict and the effect of their union is poorly > understood. It's a maintenance and support *nightmare*. Adding this > means adding more options and yet another set of behavior, and that > worries me. > I do not know how difficult it is to maintain, but sometimes it's a good idea to rewrite code. Maybe you already have thought about that, but is it an (again a) option? Stef
On Tue, 30 Mar 2010 14:44:42 +0200 Stef Bon <stefbon@gmail.com> wrote: > 2010/3/12 Jeff Layton <jlayton@samba.org>: > > > > CIFS is already a god-awful mess when it comes to handling permissions. > > There are a ton of options that affect how it deals with permissions. > > Many of them conflict and the effect of their union is poorly > > understood. It's a maintenance and support *nightmare*. Adding this > > means adding more options and yet another set of behavior, and that > > worries me. > > > > I do not know how difficult it is to maintain, but sometimes it's a > good idea to rewrite code. > Maybe you already have thought about that, but is it an (again a) option? > > Stef > What are you proposing to rewrite?
I do not something specific, but I read the discussion and you metioning the cifs code is very hard to understand and to maintain when it comes to handling permissions. Sometimes it's a good idea to rewrite. That's all. Stef Bon 2010/3/30 Jeff Layton <jlayton@samba.org>: > On Tue, 30 Mar 2010 14:44:42 +0200 > Stef Bon <stefbon@gmail.com> wrote: > >> 2010/3/12 Jeff Layton <jlayton@samba.org>: >> > >> > CIFS is already a god-awful mess when it comes to handling permissions. >> > There are a ton of options that affect how it deals with permissions. >> > Many of them conflict and the effect of their union is poorly >> > understood. It's a maintenance and support *nightmare*. Adding this >> > means adding more options and yet another set of behavior, and that >> > worries me. >> > >> >> I do not know how difficult it is to maintain, but sometimes it's a >> good idea to rewrite code. >> Maybe you already have thought about that, but is it an (again a) option? >> >> Stef >> > > What are you proposing to rewrite? > -- > Jeff Layton <jlayton@samba.org> >
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; +}