diff options
author | Andy Adamson <andros@netapp.com> | 2014-06-12 15:02:32 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2014-06-24 18:46:58 -0400 |
commit | 66b068604903849c5dee3842eb72564064c64c72 (patch) | |
tree | 8a5d1d0c052adadc3c2533bccf09d9e52868ab0b /fs | |
parent | 8445cd3528b21da77c41ad1372a944ef94d7516f (diff) |
NFSv4: test SECINFO RPC_AUTH_GSS pseudoflavors for support
Fix nfs4_negotiate_security to create an rpc_clnt used to test each SECINFO
returned pseudoflavor. Check credential creation (and gss_context creation)
which is important for RPC_AUTH_GSS pseudoflavors which can fail for multiple
reasons including mis-configuration.
Don't call nfs4_negotiate in nfs4_submount as it was just called by
nfs4_proc_lookup_mountpoint (nfs4_proc_lookup_common)
Signed-off-by: Andy Adamson <andros@netapp.com>
[Trond: fix corrupt return value from nfs_find_best_sec()]
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/nfs4_fs.h | 2 | ||||
-rw-r--r-- | fs/nfs/nfs4namespace.c | 98 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 2 |
3 files changed, 57 insertions, 45 deletions
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index f63cb87cd730..ba2affa51941 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
@@ -230,7 +230,7 @@ int nfs_atomic_open(struct inode *, struct dentry *, struct file *, | |||
230 | extern struct file_system_type nfs4_fs_type; | 230 | extern struct file_system_type nfs4_fs_type; |
231 | 231 | ||
232 | /* nfs4namespace.c */ | 232 | /* nfs4namespace.c */ |
233 | struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *, struct inode *, struct qstr *); | 233 | struct rpc_clnt *nfs4_negotiate_security(struct rpc_clnt *, struct inode *, struct qstr *); |
234 | struct vfsmount *nfs4_submount(struct nfs_server *, struct dentry *, | 234 | struct vfsmount *nfs4_submount(struct nfs_server *, struct dentry *, |
235 | struct nfs_fh *, struct nfs_fattr *); | 235 | struct nfs_fh *, struct nfs_fattr *); |
236 | int nfs4_replace_transport(struct nfs_server *server, | 236 | int nfs4_replace_transport(struct nfs_server *server, |
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index fd4dcb67cd15..3d83cb1fdc70 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c | |||
@@ -139,17 +139,22 @@ static size_t nfs_parse_server_name(char *string, size_t len, | |||
139 | * @server: NFS server struct | 139 | * @server: NFS server struct |
140 | * @flavors: List of security tuples returned by SECINFO procedure | 140 | * @flavors: List of security tuples returned by SECINFO procedure |
141 | * | 141 | * |
142 | * Return the pseudoflavor of the first security mechanism in | 142 | * Return an rpc client that uses the first security mechanism in |
143 | * "flavors" that is locally supported. The "flavors" array | 143 | * "flavors" that is locally supported. The "flavors" array |
144 | * is searched in the order returned from the server, per RFC 3530 | 144 | * is searched in the order returned from the server, per RFC 3530 |
145 | * recommendation. | 145 | * recommendation and each flavor is checked for membership in the |
146 | * sec= mount option list if it exists. | ||
146 | * | 147 | * |
147 | * Return -EPERM if no matching flavor is found in the array. | 148 | * Return -EPERM if no matching flavor is found in the array. |
149 | * | ||
150 | * Please call rpc_shutdown_client() when you are done with this rpc client. | ||
151 | * | ||
148 | */ | 152 | */ |
149 | static rpc_authflavor_t nfs_find_best_sec(struct nfs_server *server, | 153 | static struct rpc_clnt *nfs_find_best_sec(struct rpc_clnt *clnt, |
154 | struct nfs_server *server, | ||
150 | struct nfs4_secinfo_flavors *flavors) | 155 | struct nfs4_secinfo_flavors *flavors) |
151 | { | 156 | { |
152 | rpc_authflavor_t pseudoflavor; | 157 | rpc_authflavor_t pflavor; |
153 | struct nfs4_secinfo4 *secinfo; | 158 | struct nfs4_secinfo4 *secinfo; |
154 | unsigned int i; | 159 | unsigned int i; |
155 | 160 | ||
@@ -160,58 +165,73 @@ static rpc_authflavor_t nfs_find_best_sec(struct nfs_server *server, | |||
160 | case RPC_AUTH_NULL: | 165 | case RPC_AUTH_NULL: |
161 | case RPC_AUTH_UNIX: | 166 | case RPC_AUTH_UNIX: |
162 | case RPC_AUTH_GSS: | 167 | case RPC_AUTH_GSS: |
163 | pseudoflavor = rpcauth_get_pseudoflavor(secinfo->flavor, | 168 | pflavor = rpcauth_get_pseudoflavor(secinfo->flavor, |
164 | &secinfo->flavor_info); | 169 | &secinfo->flavor_info); |
165 | /* make sure pseudoflavor matches sec= mount opt */ | 170 | /* does the pseudoflavor match a sec= mount opt? */ |
166 | if (pseudoflavor != RPC_AUTH_MAXFLAVOR && | 171 | if (pflavor != RPC_AUTH_MAXFLAVOR && |
167 | nfs_auth_info_match(&server->auth_info, | 172 | nfs_auth_info_match(&server->auth_info, pflavor)) { |
168 | pseudoflavor)) | 173 | struct rpc_clnt *new; |
169 | return pseudoflavor; | 174 | struct rpc_cred *cred; |
170 | break; | 175 | |
176 | /* Cloning creates an rpc_auth for the flavor */ | ||
177 | new = rpc_clone_client_set_auth(clnt, pflavor); | ||
178 | if (IS_ERR(new)) | ||
179 | continue; | ||
180 | /** | ||
181 | * Check that the user actually can use the | ||
182 | * flavor. This is mostly for RPC_AUTH_GSS | ||
183 | * where cr_init obtains a gss context | ||
184 | */ | ||
185 | cred = rpcauth_lookupcred(new->cl_auth, 0); | ||
186 | if (IS_ERR(cred)) { | ||
187 | rpc_shutdown_client(new); | ||
188 | continue; | ||
189 | } | ||
190 | put_rpccred(cred); | ||
191 | return new; | ||
192 | } | ||
171 | } | 193 | } |
172 | } | 194 | } |
173 | 195 | return ERR_PTR(-EPERM); | |
174 | return -EPERM; | ||
175 | } | 196 | } |
176 | 197 | ||
177 | static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name) | 198 | /** |
199 | * nfs4_negotiate_security - in response to an NFS4ERR_WRONGSEC on lookup, | ||
200 | * return an rpc_clnt that uses the best available security flavor with | ||
201 | * respect to the secinfo flavor list and the sec= mount options. | ||
202 | * | ||
203 | * @clnt: RPC client to clone | ||
204 | * @inode: directory inode | ||
205 | * @name: lookup name | ||
206 | * | ||
207 | * Please call rpc_shutdown_client() when you are done with this rpc client. | ||
208 | */ | ||
209 | struct rpc_clnt * | ||
210 | nfs4_negotiate_security(struct rpc_clnt *clnt, struct inode *inode, | ||
211 | struct qstr *name) | ||
178 | { | 212 | { |
179 | struct page *page; | 213 | struct page *page; |
180 | struct nfs4_secinfo_flavors *flavors; | 214 | struct nfs4_secinfo_flavors *flavors; |
181 | rpc_authflavor_t flavor; | 215 | struct rpc_clnt *new; |
182 | int err; | 216 | int err; |
183 | 217 | ||
184 | page = alloc_page(GFP_KERNEL); | 218 | page = alloc_page(GFP_KERNEL); |
185 | if (!page) | 219 | if (!page) |
186 | return -ENOMEM; | 220 | return ERR_PTR(-ENOMEM); |
221 | |||
187 | flavors = page_address(page); | 222 | flavors = page_address(page); |
188 | 223 | ||
189 | err = nfs4_proc_secinfo(inode, name, flavors); | 224 | err = nfs4_proc_secinfo(inode, name, flavors); |
190 | if (err < 0) { | 225 | if (err < 0) { |
191 | flavor = err; | 226 | new = ERR_PTR(err); |
192 | goto out; | 227 | goto out; |
193 | } | 228 | } |
194 | 229 | ||
195 | flavor = nfs_find_best_sec(NFS_SERVER(inode), flavors); | 230 | new = nfs_find_best_sec(clnt, NFS_SERVER(inode), flavors); |
196 | 231 | ||
197 | out: | 232 | out: |
198 | put_page(page); | 233 | put_page(page); |
199 | return flavor; | 234 | return new; |
200 | } | ||
201 | |||
202 | /* | ||
203 | * Please call rpc_shutdown_client() when you are done with this client. | ||
204 | */ | ||
205 | struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *inode, | ||
206 | struct qstr *name) | ||
207 | { | ||
208 | rpc_authflavor_t flavor; | ||
209 | |||
210 | flavor = nfs4_negotiate_security(inode, name); | ||
211 | if ((int)flavor < 0) | ||
212 | return ERR_PTR((int)flavor); | ||
213 | |||
214 | return rpc_clone_client_set_auth(clnt, flavor); | ||
215 | } | 235 | } |
216 | 236 | ||
217 | static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, | 237 | static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, |
@@ -394,14 +414,6 @@ struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry, | |||
394 | 414 | ||
395 | if (client->cl_auth->au_flavor != flavor) | 415 | if (client->cl_auth->au_flavor != flavor) |
396 | flavor = client->cl_auth->au_flavor; | 416 | flavor = client->cl_auth->au_flavor; |
397 | else { | ||
398 | rpc_authflavor_t new = nfs4_negotiate_security(dir, name); | ||
399 | if ((int)new < 0) { | ||
400 | mnt = ERR_PTR((int)new); | ||
401 | goto out; | ||
402 | } | ||
403 | flavor = new; | ||
404 | } | ||
405 | mnt = nfs_do_submount(dentry, fh, fattr, flavor); | 417 | mnt = nfs_do_submount(dentry, fh, fattr, flavor); |
406 | out: | 418 | out: |
407 | rpc_shutdown_client(client); | 419 | rpc_shutdown_client(client); |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 285ad5334018..4bf3d97cc5a0 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -3247,7 +3247,7 @@ static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir, | |||
3247 | err = -EPERM; | 3247 | err = -EPERM; |
3248 | if (client != *clnt) | 3248 | if (client != *clnt) |
3249 | goto out; | 3249 | goto out; |
3250 | client = nfs4_create_sec_client(client, dir, name); | 3250 | client = nfs4_negotiate_security(client, dir, name); |
3251 | if (IS_ERR(client)) | 3251 | if (IS_ERR(client)) |
3252 | return PTR_ERR(client); | 3252 | return PTR_ERR(client); |
3253 | 3253 | ||