Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * linux/fs/nfs/nfs4namespace.c
0004  *
0005  * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
0006  * - Modified by David Howells <dhowells@redhat.com>
0007  *
0008  * NFSv4 namespace
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/dcache.h>
0013 #include <linux/mount.h>
0014 #include <linux/namei.h>
0015 #include <linux/nfs_fs.h>
0016 #include <linux/nfs_mount.h>
0017 #include <linux/slab.h>
0018 #include <linux/string.h>
0019 #include <linux/sunrpc/clnt.h>
0020 #include <linux/sunrpc/addr.h>
0021 #include <linux/vfs.h>
0022 #include <linux/inet.h>
0023 #include "internal.h"
0024 #include "nfs4_fs.h"
0025 #include "nfs.h"
0026 #include "dns_resolve.h"
0027 
0028 #define NFSDBG_FACILITY     NFSDBG_VFS
0029 
0030 /*
0031  * Work out the length that an NFSv4 path would render to as a standard posix
0032  * path, with a leading slash but no terminating slash.
0033  */
0034 static ssize_t nfs4_pathname_len(const struct nfs4_pathname *pathname)
0035 {
0036     ssize_t len = 0;
0037     int i;
0038 
0039     for (i = 0; i < pathname->ncomponents; i++) {
0040         const struct nfs4_string *component = &pathname->components[i];
0041 
0042         if (component->len > NAME_MAX)
0043             goto too_long;
0044         len += 1 + component->len; /* Adding "/foo" */
0045         if (len > PATH_MAX)
0046             goto too_long;
0047     }
0048     return len;
0049 
0050 too_long:
0051     return -ENAMETOOLONG;
0052 }
0053 
0054 /*
0055  * Convert the NFSv4 pathname components into a standard posix path.
0056  */
0057 static char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
0058                   unsigned short *_len)
0059 {
0060     ssize_t len;
0061     char *buf, *p;
0062     int i;
0063 
0064     len = nfs4_pathname_len(pathname);
0065     if (len < 0)
0066         return ERR_PTR(len);
0067     *_len = len;
0068 
0069     p = buf = kmalloc(len + 1, GFP_KERNEL);
0070     if (!buf)
0071         return ERR_PTR(-ENOMEM);
0072 
0073     for (i = 0; i < pathname->ncomponents; i++) {
0074         const struct nfs4_string *component = &pathname->components[i];
0075 
0076         *p++ = '/';
0077         memcpy(p, component->data, component->len);
0078         p += component->len;
0079     }
0080 
0081     *p = 0;
0082     return buf;
0083 }
0084 
0085 /*
0086  * return the path component of "<server>:<path>"
0087  *  nfspath - the "<server>:<path>" string
0088  *  end - one past the last char that could contain "<server>:"
0089  * returns NULL on failure
0090  */
0091 static char *nfs_path_component(const char *nfspath, const char *end)
0092 {
0093     char *p;
0094 
0095     if (*nfspath == '[') {
0096         /* parse [] escaped IPv6 addrs */
0097         p = strchr(nfspath, ']');
0098         if (p != NULL && ++p < end && *p == ':')
0099             return p + 1;
0100     } else {
0101         /* otherwise split on first colon */
0102         p = strchr(nfspath, ':');
0103         if (p != NULL && p < end)
0104             return p + 1;
0105     }
0106     return NULL;
0107 }
0108 
0109 /*
0110  * Determine the mount path as a string
0111  */
0112 static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
0113 {
0114     char *limit;
0115     char *path = nfs_path(&limit, dentry, buffer, buflen,
0116                   NFS_PATH_CANONICAL);
0117     if (!IS_ERR(path)) {
0118         char *path_component = nfs_path_component(path, limit);
0119         if (path_component)
0120             return path_component;
0121     }
0122     return path;
0123 }
0124 
0125 /*
0126  * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
0127  * believe to be the server path to this dentry
0128  */
0129 static int nfs4_validate_fspath(struct dentry *dentry,
0130                 const struct nfs4_fs_locations *locations,
0131                 struct nfs_fs_context *ctx)
0132 {
0133     const char *path;
0134     char *fs_path;
0135     unsigned short len;
0136     char *buf;
0137     int n;
0138 
0139     buf = kmalloc(4096, GFP_KERNEL);
0140     if (!buf)
0141         return -ENOMEM;
0142 
0143     path = nfs4_path(dentry, buf, 4096);
0144     if (IS_ERR(path)) {
0145         kfree(buf);
0146         return PTR_ERR(path);
0147     }
0148 
0149     fs_path = nfs4_pathname_string(&locations->fs_path, &len);
0150     if (IS_ERR(fs_path)) {
0151         kfree(buf);
0152         return PTR_ERR(fs_path);
0153     }
0154 
0155     n = strncmp(path, fs_path, len);
0156     kfree(buf);
0157     kfree(fs_path);
0158     if (n != 0) {
0159         dprintk("%s: path %s does not begin with fsroot %s\n",
0160             __func__, path, ctx->nfs_server.export_path);
0161         return -ENOENT;
0162     }
0163 
0164     return 0;
0165 }
0166 
0167 size_t nfs_parse_server_name(char *string, size_t len, struct sockaddr *sa,
0168                  size_t salen, struct net *net, int port)
0169 {
0170     ssize_t ret;
0171 
0172     ret = rpc_pton(net, string, len, sa, salen);
0173     if (ret == 0) {
0174         ret = rpc_uaddr2sockaddr(net, string, len, sa, salen);
0175         if (ret == 0) {
0176             ret = nfs_dns_resolve_name(net, string, len, sa, salen);
0177             if (ret < 0)
0178                 ret = 0;
0179         }
0180     } else if (port) {
0181         rpc_set_port(sa, port);
0182     }
0183     return ret;
0184 }
0185 
0186 /**
0187  * nfs_find_best_sec - Find a security mechanism supported locally
0188  * @clnt: pointer to rpc_clnt
0189  * @server: NFS server struct
0190  * @flavors: List of security tuples returned by SECINFO procedure
0191  *
0192  * Return an rpc client that uses the first security mechanism in
0193  * "flavors" that is locally supported.  The "flavors" array
0194  * is searched in the order returned from the server, per RFC 3530
0195  * recommendation and each flavor is checked for membership in the
0196  * sec= mount option list if it exists.
0197  *
0198  * Return -EPERM if no matching flavor is found in the array.
0199  *
0200  * Please call rpc_shutdown_client() when you are done with this rpc client.
0201  *
0202  */
0203 static struct rpc_clnt *nfs_find_best_sec(struct rpc_clnt *clnt,
0204                       struct nfs_server *server,
0205                       struct nfs4_secinfo_flavors *flavors)
0206 {
0207     rpc_authflavor_t pflavor;
0208     struct nfs4_secinfo4 *secinfo;
0209     unsigned int i;
0210 
0211     for (i = 0; i < flavors->num_flavors; i++) {
0212         secinfo = &flavors->flavors[i];
0213 
0214         switch (secinfo->flavor) {
0215         case RPC_AUTH_NULL:
0216         case RPC_AUTH_UNIX:
0217         case RPC_AUTH_GSS:
0218             pflavor = rpcauth_get_pseudoflavor(secinfo->flavor,
0219                             &secinfo->flavor_info);
0220             /* does the pseudoflavor match a sec= mount opt? */
0221             if (pflavor != RPC_AUTH_MAXFLAVOR &&
0222                 nfs_auth_info_match(&server->auth_info, pflavor)) {
0223                 struct rpc_clnt *new;
0224                 struct rpc_cred *cred;
0225 
0226                 /* Cloning creates an rpc_auth for the flavor */
0227                 new = rpc_clone_client_set_auth(clnt, pflavor);
0228                 if (IS_ERR(new))
0229                     continue;
0230                 /**
0231                 * Check that the user actually can use the
0232                 * flavor. This is mostly for RPC_AUTH_GSS
0233                 * where cr_init obtains a gss context
0234                 */
0235                 cred = rpcauth_lookupcred(new->cl_auth, 0);
0236                 if (IS_ERR(cred)) {
0237                     rpc_shutdown_client(new);
0238                     continue;
0239                 }
0240                 put_rpccred(cred);
0241                 return new;
0242             }
0243         }
0244     }
0245     return ERR_PTR(-EPERM);
0246 }
0247 
0248 /**
0249  * nfs4_negotiate_security - in response to an NFS4ERR_WRONGSEC on lookup,
0250  * return an rpc_clnt that uses the best available security flavor with
0251  * respect to the secinfo flavor list and the sec= mount options.
0252  *
0253  * @clnt: RPC client to clone
0254  * @inode: directory inode
0255  * @name: lookup name
0256  *
0257  * Please call rpc_shutdown_client() when you are done with this rpc client.
0258  */
0259 struct rpc_clnt *
0260 nfs4_negotiate_security(struct rpc_clnt *clnt, struct inode *inode,
0261                     const struct qstr *name)
0262 {
0263     struct page *page;
0264     struct nfs4_secinfo_flavors *flavors;
0265     struct rpc_clnt *new;
0266     int err;
0267 
0268     page = alloc_page(GFP_KERNEL);
0269     if (!page)
0270         return ERR_PTR(-ENOMEM);
0271 
0272     flavors = page_address(page);
0273 
0274     err = nfs4_proc_secinfo(inode, name, flavors);
0275     if (err < 0) {
0276         new = ERR_PTR(err);
0277         goto out;
0278     }
0279 
0280     new = nfs_find_best_sec(clnt, NFS_SERVER(inode), flavors);
0281 
0282 out:
0283     put_page(page);
0284     return new;
0285 }
0286 
0287 static int try_location(struct fs_context *fc,
0288             const struct nfs4_fs_location *location)
0289 {
0290     struct nfs_fs_context *ctx = nfs_fc2context(fc);
0291     unsigned int len, s;
0292     char *export_path, *source, *p;
0293     int ret = -ENOENT;
0294 
0295     /* Allocate a buffer big enough to hold any of the hostnames plus a
0296      * terminating char and also a buffer big enough to hold the hostname
0297      * plus a colon plus the path.
0298      */
0299     len = 0;
0300     for (s = 0; s < location->nservers; s++) {
0301         const struct nfs4_string *buf = &location->servers[s];
0302         if (buf->len > len)
0303             len = buf->len;
0304     }
0305 
0306     kfree(ctx->nfs_server.hostname);
0307     ctx->nfs_server.hostname = kmalloc(len + 1, GFP_KERNEL);
0308     if (!ctx->nfs_server.hostname)
0309         return -ENOMEM;
0310 
0311     export_path = nfs4_pathname_string(&location->rootpath,
0312                        &ctx->nfs_server.export_path_len);
0313     if (IS_ERR(export_path))
0314         return PTR_ERR(export_path);
0315 
0316     kfree(ctx->nfs_server.export_path);
0317     ctx->nfs_server.export_path = export_path;
0318 
0319     source = kmalloc(len + 1 + ctx->nfs_server.export_path_len + 1,
0320              GFP_KERNEL);
0321     if (!source)
0322         return -ENOMEM;
0323 
0324     kfree(fc->source);
0325     fc->source = source;
0326     for (s = 0; s < location->nservers; s++) {
0327         const struct nfs4_string *buf = &location->servers[s];
0328 
0329         if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
0330             continue;
0331 
0332         ctx->nfs_server.addrlen =
0333             nfs_parse_server_name(buf->data, buf->len,
0334                           &ctx->nfs_server.address,
0335                           sizeof(ctx->nfs_server._address),
0336                           fc->net_ns, 0);
0337         if (ctx->nfs_server.addrlen == 0)
0338             continue;
0339 
0340         rpc_set_port(&ctx->nfs_server.address, NFS_PORT);
0341 
0342         memcpy(ctx->nfs_server.hostname, buf->data, buf->len);
0343         ctx->nfs_server.hostname[buf->len] = '\0';
0344 
0345         p = source;
0346         memcpy(p, buf->data, buf->len);
0347         p += buf->len;
0348         *p++ = ':';
0349         memcpy(p, ctx->nfs_server.export_path, ctx->nfs_server.export_path_len);
0350         p += ctx->nfs_server.export_path_len;
0351         *p = 0;
0352 
0353         ret = nfs4_get_referral_tree(fc);
0354         if (ret == 0)
0355             return 0;
0356     }
0357 
0358     return ret;
0359 }
0360 
0361 /**
0362  * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
0363  * @fc: pointer to struct nfs_fs_context
0364  * @locations: array of NFSv4 server location information
0365  *
0366  */
0367 static int nfs_follow_referral(struct fs_context *fc,
0368                    const struct nfs4_fs_locations *locations)
0369 {
0370     struct nfs_fs_context *ctx = nfs_fc2context(fc);
0371     int loc, error;
0372 
0373     if (locations == NULL || locations->nlocations <= 0)
0374         return -ENOENT;
0375 
0376     dprintk("%s: referral at %pd2\n", __func__, ctx->clone_data.dentry);
0377 
0378     /* Ensure fs path is a prefix of current dentry path */
0379     error = nfs4_validate_fspath(ctx->clone_data.dentry, locations, ctx);
0380     if (error < 0)
0381         return error;
0382 
0383     error = -ENOENT;
0384     for (loc = 0; loc < locations->nlocations; loc++) {
0385         const struct nfs4_fs_location *location = &locations->locations[loc];
0386 
0387         if (location == NULL || location->nservers <= 0 ||
0388             location->rootpath.ncomponents == 0)
0389             continue;
0390 
0391         error = try_location(fc, location);
0392         if (error == 0)
0393             return 0;
0394     }
0395 
0396     return error;
0397 }
0398 
0399 /*
0400  * nfs_do_refmount - handle crossing a referral on server
0401  * @dentry - dentry of referral
0402  *
0403  */
0404 static int nfs_do_refmount(struct fs_context *fc, struct rpc_clnt *client)
0405 {
0406     struct nfs_fs_context *ctx = nfs_fc2context(fc);
0407     struct dentry *dentry, *parent;
0408     struct nfs4_fs_locations *fs_locations = NULL;
0409     struct page *page;
0410     int err = -ENOMEM;
0411 
0412     /* BUG_ON(IS_ROOT(dentry)); */
0413     page = alloc_page(GFP_KERNEL);
0414     if (!page)
0415         return -ENOMEM;
0416 
0417     fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
0418     if (!fs_locations)
0419         goto out_free;
0420     fs_locations->fattr = nfs_alloc_fattr();
0421     if (!fs_locations->fattr)
0422         goto out_free_2;
0423 
0424     /* Get locations */
0425     dentry = ctx->clone_data.dentry;
0426     parent = dget_parent(dentry);
0427     dprintk("%s: getting locations for %pd2\n",
0428         __func__, dentry);
0429 
0430     err = nfs4_proc_fs_locations(client, d_inode(parent), &dentry->d_name, fs_locations, page);
0431     dput(parent);
0432     if (err != 0)
0433         goto out_free_3;
0434 
0435     err = -ENOENT;
0436     if (fs_locations->nlocations <= 0 ||
0437         fs_locations->fs_path.ncomponents <= 0)
0438         goto out_free_3;
0439 
0440     err = nfs_follow_referral(fc, fs_locations);
0441 out_free_3:
0442     kfree(fs_locations->fattr);
0443 out_free_2:
0444     kfree(fs_locations);
0445 out_free:
0446     __free_page(page);
0447     return err;
0448 }
0449 
0450 int nfs4_submount(struct fs_context *fc, struct nfs_server *server)
0451 {
0452     struct nfs_fs_context *ctx = nfs_fc2context(fc);
0453     struct dentry *dentry = ctx->clone_data.dentry;
0454     struct dentry *parent = dget_parent(dentry);
0455     struct inode *dir = d_inode(parent);
0456     struct rpc_clnt *client;
0457     int ret;
0458 
0459     /* Look it up again to get its attributes and sec flavor */
0460     client = nfs4_proc_lookup_mountpoint(dir, dentry, ctx->mntfh,
0461                          ctx->clone_data.fattr);
0462     dput(parent);
0463     if (IS_ERR(client))
0464         return PTR_ERR(client);
0465 
0466     ctx->selected_flavor = client->cl_auth->au_flavor;
0467     if (ctx->clone_data.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
0468         ret = nfs_do_refmount(fc, client);
0469     } else {
0470         ret = nfs_do_submount(fc);
0471     }
0472 
0473     rpc_shutdown_client(client);
0474     return ret;
0475 }
0476 
0477 /*
0478  * Try one location from the fs_locations array.
0479  *
0480  * Returns zero on success, or a negative errno value.
0481  */
0482 static int nfs4_try_replacing_one_location(struct nfs_server *server,
0483         char *page, char *page2,
0484         const struct nfs4_fs_location *location)
0485 {
0486     const size_t addr_bufsize = sizeof(struct sockaddr_storage);
0487     struct net *net = rpc_net_ns(server->client);
0488     struct sockaddr *sap;
0489     unsigned int s;
0490     size_t salen;
0491     int error;
0492 
0493     sap = kmalloc(addr_bufsize, GFP_KERNEL);
0494     if (sap == NULL)
0495         return -ENOMEM;
0496 
0497     error = -ENOENT;
0498     for (s = 0; s < location->nservers; s++) {
0499         const struct nfs4_string *buf = &location->servers[s];
0500         char *hostname;
0501 
0502         if (buf->len <= 0 || buf->len > PAGE_SIZE)
0503             continue;
0504 
0505         if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len) != NULL)
0506             continue;
0507 
0508         salen = nfs_parse_server_name(buf->data, buf->len,
0509                         sap, addr_bufsize, net, 0);
0510         if (salen == 0)
0511             continue;
0512         rpc_set_port(sap, NFS_PORT);
0513 
0514         error = -ENOMEM;
0515         hostname = kmemdup_nul(buf->data, buf->len, GFP_KERNEL);
0516         if (hostname == NULL)
0517             break;
0518 
0519         error = nfs4_update_server(server, hostname, sap, salen, net);
0520         kfree(hostname);
0521         if (error == 0)
0522             break;
0523     }
0524 
0525     kfree(sap);
0526     return error;
0527 }
0528 
0529 /**
0530  * nfs4_replace_transport - set up transport to destination server
0531  *
0532  * @server: export being migrated
0533  * @locations: fs_locations array
0534  *
0535  * Returns zero on success, or a negative errno value.
0536  *
0537  * The client tries all the entries in the "locations" array, in the
0538  * order returned by the server, until one works or the end of the
0539  * array is reached.
0540  */
0541 int nfs4_replace_transport(struct nfs_server *server,
0542                const struct nfs4_fs_locations *locations)
0543 {
0544     char *page = NULL, *page2 = NULL;
0545     int loc, error;
0546 
0547     error = -ENOENT;
0548     if (locations == NULL || locations->nlocations <= 0)
0549         goto out;
0550 
0551     error = -ENOMEM;
0552     page = (char *) __get_free_page(GFP_USER);
0553     if (!page)
0554         goto out;
0555     page2 = (char *) __get_free_page(GFP_USER);
0556     if (!page2)
0557         goto out;
0558 
0559     for (loc = 0; loc < locations->nlocations; loc++) {
0560         const struct nfs4_fs_location *location =
0561                         &locations->locations[loc];
0562 
0563         if (location == NULL || location->nservers <= 0 ||
0564             location->rootpath.ncomponents == 0)
0565             continue;
0566 
0567         error = nfs4_try_replacing_one_location(server, page,
0568                             page2, location);
0569         if (error == 0)
0570             break;
0571     }
0572 
0573 out:
0574     free_page((unsigned long)page);
0575     free_page((unsigned long)page2);
0576     return error;
0577 }