linux/fs/nfs/callback.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * linux/fs/nfs/callback.c
   4 *
   5 * Copyright (C) 2004 Trond Myklebust
   6 *
   7 * NFSv4 callback handling
   8 */
   9
  10#include <linux/completion.h>
  11#include <linux/ip.h>
  12#include <linux/module.h>
  13#include <linux/sched/signal.h>
  14#include <linux/sunrpc/svc.h>
  15#include <linux/sunrpc/svcsock.h>
  16#include <linux/nfs_fs.h>
  17#include <linux/errno.h>
  18#include <linux/mutex.h>
  19#include <linux/freezer.h>
  20#include <linux/kthread.h>
  21#include <linux/sunrpc/svcauth_gss.h>
  22#include <linux/sunrpc/bc_xprt.h>
  23
  24#include <net/inet_sock.h>
  25
  26#include "nfs4_fs.h"
  27#include "callback.h"
  28#include "internal.h"
  29#include "netns.h"
  30
  31#define NFSDBG_FACILITY NFSDBG_CALLBACK
  32
  33struct nfs_callback_data {
  34        unsigned int users;
  35        struct svc_serv *serv;
  36};
  37
  38static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
  39static DEFINE_MUTEX(nfs_callback_mutex);
  40static struct svc_program nfs4_callback_program;
  41
  42static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
  43{
  44        const struct cred *cred = current_cred();
  45        int ret;
  46        struct nfs_net *nn = net_generic(net, nfs_net_id);
  47
  48        ret = svc_create_xprt(serv, "tcp", net, PF_INET,
  49                                nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
  50                                cred);
  51        if (ret <= 0)
  52                goto out_err;
  53        nn->nfs_callback_tcpport = ret;
  54        dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
  55                nn->nfs_callback_tcpport, PF_INET, net->ns.inum);
  56
  57        ret = svc_create_xprt(serv, "tcp", net, PF_INET6,
  58                                nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
  59                                cred);
  60        if (ret > 0) {
  61                nn->nfs_callback_tcpport6 = ret;
  62                dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
  63                        nn->nfs_callback_tcpport6, PF_INET6, net->ns.inum);
  64        } else if (ret != -EAFNOSUPPORT)
  65                goto out_err;
  66        return 0;
  67
  68out_err:
  69        return (ret) ? ret : -ENOMEM;
  70}
  71
  72/*
  73 * This is the NFSv4 callback kernel thread.
  74 */
  75static int
  76nfs4_callback_svc(void *vrqstp)
  77{
  78        int err;
  79        struct svc_rqst *rqstp = vrqstp;
  80
  81        set_freezable();
  82
  83        while (!kthread_freezable_should_stop(NULL)) {
  84
  85                if (signal_pending(current))
  86                        flush_signals(current);
  87                /*
  88                 * Listen for a request on the socket
  89                 */
  90                err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
  91                if (err == -EAGAIN || err == -EINTR)
  92                        continue;
  93                svc_process(rqstp);
  94        }
  95        svc_exit_thread(rqstp);
  96        module_put_and_exit(0);
  97        return 0;
  98}
  99
 100#if defined(CONFIG_NFS_V4_1)
 101/*
 102 * The callback service for NFSv4.1 callbacks
 103 */
 104static int
 105nfs41_callback_svc(void *vrqstp)
 106{
 107        struct svc_rqst *rqstp = vrqstp;
 108        struct svc_serv *serv = rqstp->rq_server;
 109        struct rpc_rqst *req;
 110        int error;
 111        DEFINE_WAIT(wq);
 112
 113        set_freezable();
 114
 115        while (!kthread_freezable_should_stop(NULL)) {
 116
 117                if (signal_pending(current))
 118                        flush_signals(current);
 119
 120                prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
 121                spin_lock_bh(&serv->sv_cb_lock);
 122                if (!list_empty(&serv->sv_cb_list)) {
 123                        req = list_first_entry(&serv->sv_cb_list,
 124                                        struct rpc_rqst, rq_bc_list);
 125                        list_del(&req->rq_bc_list);
 126                        spin_unlock_bh(&serv->sv_cb_lock);
 127                        finish_wait(&serv->sv_cb_waitq, &wq);
 128                        dprintk("Invoking bc_svc_process()\n");
 129                        error = bc_svc_process(serv, req, rqstp);
 130                        dprintk("bc_svc_process() returned w/ error code= %d\n",
 131                                error);
 132                } else {
 133                        spin_unlock_bh(&serv->sv_cb_lock);
 134                        if (!kthread_should_stop())
 135                                schedule();
 136                        finish_wait(&serv->sv_cb_waitq, &wq);
 137                }
 138        }
 139        svc_exit_thread(rqstp);
 140        module_put_and_exit(0);
 141        return 0;
 142}
 143
 144static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 145                struct svc_serv *serv)
 146{
 147        if (minorversion)
 148                /*
 149                 * Save the svc_serv in the transport so that it can
 150                 * be referenced when the session backchannel is initialized
 151                 */
 152                xprt->bc_serv = serv;
 153}
 154#else
 155static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 156                struct svc_serv *serv)
 157{
 158}
 159#endif /* CONFIG_NFS_V4_1 */
 160
 161static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
 162                                  struct svc_serv *serv)
 163{
 164        int nrservs = nfs_callback_nr_threads;
 165        int ret;
 166
 167        nfs_callback_bc_serv(minorversion, xprt, serv);
 168
 169        if (nrservs < NFS4_MIN_NR_CALLBACK_THREADS)
 170                nrservs = NFS4_MIN_NR_CALLBACK_THREADS;
 171
 172        if (serv->sv_nrthreads-1 == nrservs)
 173                return 0;
 174
 175        ret = serv->sv_ops->svo_setup(serv, NULL, nrservs);
 176        if (ret) {
 177                serv->sv_ops->svo_setup(serv, NULL, 0);
 178                return ret;
 179        }
 180        dprintk("nfs_callback_up: service started\n");
 181        return 0;
 182}
 183
 184static void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struct net *net)
 185{
 186        struct nfs_net *nn = net_generic(net, nfs_net_id);
 187
 188        if (--nn->cb_users[minorversion])
 189                return;
 190
 191        dprintk("NFS: destroy per-net callback data; net=%x\n", net->ns.inum);
 192        svc_shutdown_net(serv, net);
 193}
 194
 195static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
 196                               struct net *net, struct rpc_xprt *xprt)
 197{
 198        struct nfs_net *nn = net_generic(net, nfs_net_id);
 199        int ret;
 200
 201        if (nn->cb_users[minorversion]++)
 202                return 0;
 203
 204        dprintk("NFS: create per-net callback data; net=%x\n", net->ns.inum);
 205
 206        ret = svc_bind(serv, net);
 207        if (ret < 0) {
 208                printk(KERN_WARNING "NFS: bind callback service failed\n");
 209                goto err_bind;
 210        }
 211
 212        ret = 0;
 213        if (!IS_ENABLED(CONFIG_NFS_V4_1) || minorversion == 0)
 214                ret = nfs4_callback_up_net(serv, net);
 215        else if (xprt->ops->bc_setup)
 216                set_bc_enabled(serv);
 217        else
 218                ret = -EPROTONOSUPPORT;
 219
 220        if (ret < 0) {
 221                printk(KERN_ERR "NFS: callback service start failed\n");
 222                goto err_socks;
 223        }
 224        return 0;
 225
 226err_socks:
 227        svc_rpcb_cleanup(serv, net);
 228err_bind:
 229        nn->cb_users[minorversion]--;
 230        dprintk("NFS: Couldn't create callback socket: err = %d; "
 231                        "net = %x\n", ret, net->ns.inum);
 232        return ret;
 233}
 234
 235static const struct svc_serv_ops nfs40_cb_sv_ops = {
 236        .svo_function           = nfs4_callback_svc,
 237        .svo_enqueue_xprt       = svc_xprt_do_enqueue,
 238        .svo_setup              = svc_set_num_threads_sync,
 239        .svo_module             = THIS_MODULE,
 240};
 241#if defined(CONFIG_NFS_V4_1)
 242static const struct svc_serv_ops nfs41_cb_sv_ops = {
 243        .svo_function           = nfs41_callback_svc,
 244        .svo_enqueue_xprt       = svc_xprt_do_enqueue,
 245        .svo_setup              = svc_set_num_threads_sync,
 246        .svo_module             = THIS_MODULE,
 247};
 248
 249static const struct svc_serv_ops *nfs4_cb_sv_ops[] = {
 250        [0] = &nfs40_cb_sv_ops,
 251        [1] = &nfs41_cb_sv_ops,
 252};
 253#else
 254static const struct svc_serv_ops *nfs4_cb_sv_ops[] = {
 255        [0] = &nfs40_cb_sv_ops,
 256        [1] = NULL,
 257};
 258#endif
 259
 260static struct svc_serv *nfs_callback_create_svc(int minorversion)
 261{
 262        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 263        const struct svc_serv_ops *sv_ops;
 264        struct svc_serv *serv;
 265
 266        /*
 267         * Check whether we're already up and running.
 268         */
 269        if (cb_info->serv) {
 270                /*
 271                 * Note: increase service usage, because later in case of error
 272                 * svc_destroy() will be called.
 273                 */
 274                svc_get(cb_info->serv);
 275                return cb_info->serv;
 276        }
 277
 278        switch (minorversion) {
 279        case 0:
 280                sv_ops = nfs4_cb_sv_ops[0];
 281                break;
 282        default:
 283                sv_ops = nfs4_cb_sv_ops[1];
 284        }
 285
 286        if (sv_ops == NULL)
 287                return ERR_PTR(-ENOTSUPP);
 288
 289        /*
 290         * Sanity check: if there's no task,
 291         * we should be the first user ...
 292         */
 293        if (cb_info->users)
 294                printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
 295                        cb_info->users);
 296
 297        serv = svc_create_pooled(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, sv_ops);
 298        if (!serv) {
 299                printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
 300                return ERR_PTR(-ENOMEM);
 301        }
 302        cb_info->serv = serv;
 303        /* As there is only one thread we need to over-ride the
 304         * default maximum of 80 connections
 305         */
 306        serv->sv_maxconn = 1024;
 307        dprintk("nfs_callback_create_svc: service created\n");
 308        return serv;
 309}
 310
 311/*
 312 * Bring up the callback thread if it is not already up.
 313 */
 314int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
 315{
 316        struct svc_serv *serv;
 317        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 318        int ret;
 319        struct net *net = xprt->xprt_net;
 320
 321        mutex_lock(&nfs_callback_mutex);
 322
 323        serv = nfs_callback_create_svc(minorversion);
 324        if (IS_ERR(serv)) {
 325                ret = PTR_ERR(serv);
 326                goto err_create;
 327        }
 328
 329        ret = nfs_callback_up_net(minorversion, serv, net, xprt);
 330        if (ret < 0)
 331                goto err_net;
 332
 333        ret = nfs_callback_start_svc(minorversion, xprt, serv);
 334        if (ret < 0)
 335                goto err_start;
 336
 337        cb_info->users++;
 338        /*
 339         * svc_create creates the svc_serv with sv_nrthreads == 1, and then
 340         * svc_prepare_thread increments that. So we need to call svc_destroy
 341         * on both success and failure so that the refcount is 1 when the
 342         * thread exits.
 343         */
 344err_net:
 345        if (!cb_info->users)
 346                cb_info->serv = NULL;
 347        svc_destroy(serv);
 348err_create:
 349        mutex_unlock(&nfs_callback_mutex);
 350        return ret;
 351
 352err_start:
 353        nfs_callback_down_net(minorversion, serv, net);
 354        dprintk("NFS: Couldn't create server thread; err = %d\n", ret);
 355        goto err_net;
 356}
 357
 358/*
 359 * Kill the callback thread if it's no longer being used.
 360 */
 361void nfs_callback_down(int minorversion, struct net *net)
 362{
 363        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 364        struct svc_serv *serv;
 365
 366        mutex_lock(&nfs_callback_mutex);
 367        serv = cb_info->serv;
 368        nfs_callback_down_net(minorversion, serv, net);
 369        cb_info->users--;
 370        if (cb_info->users == 0) {
 371                svc_get(serv);
 372                serv->sv_ops->svo_setup(serv, NULL, 0);
 373                svc_destroy(serv);
 374                dprintk("nfs_callback_down: service destroyed\n");
 375                cb_info->serv = NULL;
 376        }
 377        mutex_unlock(&nfs_callback_mutex);
 378}
 379
 380/* Boolean check of RPC_AUTH_GSS principal */
 381int
 382check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp)
 383{
 384        char *p = rqstp->rq_cred.cr_principal;
 385
 386        if (rqstp->rq_authop->flavour != RPC_AUTH_GSS)
 387                return 1;
 388
 389        /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */
 390        if (clp->cl_minorversion != 0)
 391                return 0;
 392        /*
 393         * It might just be a normal user principal, in which case
 394         * userspace won't bother to tell us the name at all.
 395         */
 396        if (p == NULL)
 397                return 0;
 398
 399        /*
 400         * Did we get the acceptor from userland during the SETCLIENID
 401         * negotiation?
 402         */
 403        if (clp->cl_acceptor)
 404                return !strcmp(p, clp->cl_acceptor);
 405
 406        /*
 407         * Otherwise try to verify it using the cl_hostname. Note that this
 408         * doesn't work if a non-canonical hostname was used in the devname.
 409         */
 410
 411        /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
 412
 413        if (memcmp(p, "nfs@", 4) != 0)
 414                return 0;
 415        p += 4;
 416        if (strcmp(p, clp->cl_hostname) != 0)
 417                return 0;
 418        return 1;
 419}
 420
 421/*
 422 * pg_authenticate method for nfsv4 callback threads.
 423 *
 424 * The authflavor has been negotiated, so an incorrect flavor is a server
 425 * bug. Deny packets with incorrect authflavor.
 426 *
 427 * All other checking done after NFS decoding where the nfs_client can be
 428 * found in nfs4_callback_compound
 429 */
 430static int nfs_callback_authenticate(struct svc_rqst *rqstp)
 431{
 432        switch (rqstp->rq_authop->flavour) {
 433        case RPC_AUTH_NULL:
 434                if (rqstp->rq_proc != CB_NULL)
 435                        return SVC_DENIED;
 436                break;
 437        case RPC_AUTH_GSS:
 438                /* No RPC_AUTH_GSS support yet in NFSv4.1 */
 439                 if (svc_is_backchannel(rqstp))
 440                        return SVC_DENIED;
 441        }
 442        return SVC_OK;
 443}
 444
 445/*
 446 * Define NFS4 callback program
 447 */
 448static const struct svc_version *nfs4_callback_version[] = {
 449        [1] = &nfs4_callback_version1,
 450        [4] = &nfs4_callback_version4,
 451};
 452
 453static struct svc_stat nfs4_callback_stats;
 454
 455static struct svc_program nfs4_callback_program = {
 456        .pg_prog = NFS4_CALLBACK,                       /* RPC service number */
 457        .pg_nvers = ARRAY_SIZE(nfs4_callback_version),  /* Number of entries */
 458        .pg_vers = nfs4_callback_version,               /* version table */
 459        .pg_name = "NFSv4 callback",                    /* service name */
 460        .pg_class = "nfs",                              /* authentication class */
 461        .pg_stats = &nfs4_callback_stats,
 462        .pg_authenticate = nfs_callback_authenticate,
 463        .pg_init_request = svc_generic_init_request,
 464        .pg_rpcbind_set = svc_generic_rpcbind_set,
 465};
 466