linux-bk/fs/nfs/callback.c
<<
>>
Prefs
   1/*
   2 * linux/fs/nfs/callback.c
   3 *
   4 * Copyright (C) 2004 Trond Myklebust
   5 *
   6 * NFSv4 callback handling
   7 */
   8
   9#include <linux/config.h>
  10#include <linux/completion.h>
  11#include <linux/ip.h>
  12#include <linux/module.h>
  13#include <linux/smp_lock.h>
  14#include <linux/sunrpc/svc.h>
  15#include <linux/sunrpc/svcsock.h>
  16#include <linux/nfs_fs.h>
  17#include "callback.h"
  18
  19#define NFSDBG_FACILITY NFSDBG_CALLBACK
  20
  21struct nfs_callback_data {
  22        unsigned int users;
  23        struct svc_serv *serv;
  24        pid_t pid;
  25        struct completion started;
  26        struct completion stopped;
  27};
  28
  29static struct nfs_callback_data nfs_callback_info;
  30static DECLARE_MUTEX(nfs_callback_sema);
  31static struct svc_program nfs4_callback_program;
  32
  33unsigned short nfs_callback_tcpport;
  34
  35/*
  36 * This is the callback kernel thread.
  37 */
  38static void nfs_callback_svc(struct svc_rqst *rqstp)
  39{
  40        struct svc_serv *serv = rqstp->rq_server;
  41        int err;
  42
  43        __module_get(THIS_MODULE);
  44        lock_kernel();
  45
  46        nfs_callback_info.pid = current->pid;
  47        daemonize("nfsv4-svc");
  48        /* Process request with signals blocked, but allow SIGKILL.  */
  49        allow_signal(SIGKILL);
  50
  51        complete(&nfs_callback_info.started);
  52
  53        while (nfs_callback_info.users != 0 || !signalled()) {
  54                /*
  55                 * Listen for a request on the socket
  56                 */
  57                err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT);
  58                if (err == -EAGAIN || err == -EINTR)
  59                        continue;
  60                if (err < 0) {
  61                        printk(KERN_WARNING
  62                                        "%s: terminating on error %d\n",
  63                                        __FUNCTION__, -err);
  64                        break;
  65                }
  66                dprintk("%s: request from %u.%u.%u.%u\n", __FUNCTION__,
  67                                NIPQUAD(rqstp->rq_addr.sin_addr.s_addr));
  68                svc_process(serv, rqstp);
  69        }
  70
  71        nfs_callback_info.pid = 0;
  72        complete(&nfs_callback_info.stopped);
  73        unlock_kernel();
  74        module_put_and_exit(0);
  75}
  76
  77/*
  78 * Bring up the server process if it is not already up.
  79 */
  80int nfs_callback_up(void)
  81{
  82        struct svc_serv *serv;
  83        struct svc_sock *svsk;
  84        int ret = 0;
  85
  86        lock_kernel();
  87        down(&nfs_callback_sema);
  88        if (nfs_callback_info.users++ || nfs_callback_info.pid != 0)
  89                goto out;
  90        init_completion(&nfs_callback_info.started);
  91        init_completion(&nfs_callback_info.stopped);
  92        serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE);
  93        ret = -ENOMEM;
  94        if (!serv)
  95                goto out_err;
  96        /* FIXME: We don't want to register this socket with the portmapper */
  97        ret = svc_makesock(serv, IPPROTO_TCP, 0);
  98        if (ret < 0)
  99                goto out_destroy;
 100        if (!list_empty(&serv->sv_permsocks)) {
 101                svsk = list_entry(serv->sv_permsocks.next,
 102                                struct svc_sock, sk_list);
 103                nfs_callback_tcpport = ntohs(inet_sk(svsk->sk_sk)->sport);
 104                dprintk ("Callback port = 0x%x\n", nfs_callback_tcpport);
 105        } else
 106                BUG();
 107        ret = svc_create_thread(nfs_callback_svc, serv);
 108        if (ret < 0)
 109                goto out_destroy;
 110        nfs_callback_info.serv = serv;
 111        wait_for_completion(&nfs_callback_info.started);
 112out:
 113        up(&nfs_callback_sema);
 114        unlock_kernel();
 115        return ret;
 116out_destroy:
 117        svc_destroy(serv);
 118out_err:
 119        nfs_callback_info.users--;
 120        goto out;
 121}
 122
 123/*
 124 * Kill the server process if it is not already up.
 125 */
 126int nfs_callback_down(void)
 127{
 128        int ret = 0;
 129
 130        lock_kernel();
 131        down(&nfs_callback_sema);
 132        if (--nfs_callback_info.users || nfs_callback_info.pid == 0)
 133                goto out;
 134        kill_proc(nfs_callback_info.pid, SIGKILL, 1);
 135        wait_for_completion(&nfs_callback_info.stopped);
 136out:
 137        up(&nfs_callback_sema);
 138        unlock_kernel();
 139        return ret;
 140}
 141
 142/*
 143 * AUTH_NULL authentication
 144 */
 145static int nfs_callback_null_accept(struct svc_rqst *rqstp, u32 *authp)
 146{
 147        struct kvec    *argv = &rqstp->rq_arg.head[0];
 148        struct kvec    *resv = &rqstp->rq_res.head[0];
 149
 150        if (argv->iov_len < 3*4)
 151                return SVC_GARBAGE;
 152
 153        if (svc_getu32(argv) != 0) {
 154                dprintk("svc: bad null cred\n");
 155                *authp = rpc_autherr_badcred;
 156                return SVC_DENIED;
 157        }
 158        if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {
 159                dprintk("svc: bad null verf\n");
 160                 *authp = rpc_autherr_badverf;
 161                 return SVC_DENIED;
 162        }
 163
 164        /* Signal that mapping to nobody uid/gid is required */
 165        rqstp->rq_cred.cr_uid = (uid_t) -1;
 166        rqstp->rq_cred.cr_gid = (gid_t) -1;
 167        rqstp->rq_cred.cr_group_info = groups_alloc(0);
 168        if (rqstp->rq_cred.cr_group_info == NULL)
 169                return SVC_DROP; /* kmalloc failure - client must retry */
 170
 171        /* Put NULL verifier */
 172        svc_putu32(resv, RPC_AUTH_NULL);
 173        svc_putu32(resv, 0);
 174        dprintk("%s: success, returning %d!\n", __FUNCTION__, SVC_OK);
 175        return SVC_OK;
 176}
 177
 178static int nfs_callback_null_release(struct svc_rqst *rqstp)
 179{
 180        if (rqstp->rq_cred.cr_group_info)
 181                put_group_info(rqstp->rq_cred.cr_group_info);
 182        rqstp->rq_cred.cr_group_info = NULL;
 183        return 0; /* don't drop */
 184}
 185
 186static struct auth_ops nfs_callback_auth_null = {
 187        .name = "null",
 188        .flavour = RPC_AUTH_NULL,
 189        .accept = nfs_callback_null_accept,
 190        .release = nfs_callback_null_release,
 191};
 192
 193/*
 194 * AUTH_SYS authentication
 195 */
 196static int nfs_callback_unix_accept(struct svc_rqst *rqstp, u32 *authp)
 197{
 198        struct kvec    *argv = &rqstp->rq_arg.head[0];
 199        struct kvec    *resv = &rqstp->rq_res.head[0];
 200        struct svc_cred *cred = &rqstp->rq_cred;
 201        u32 slen, i;
 202        int len = argv->iov_len;
 203
 204        dprintk("%s: start\n", __FUNCTION__);
 205        cred->cr_group_info = NULL;
 206        rqstp->rq_client = NULL;
 207        if ((len -= 3*4) < 0)
 208                return SVC_GARBAGE;
 209
 210        /* Get length, time stamp and machine name */
 211        svc_getu32(argv);
 212        svc_getu32(argv);
 213        slen = XDR_QUADLEN(ntohl(svc_getu32(argv)));
 214        if (slen > 64 || (len -= (slen + 3)*4) < 0)
 215                goto badcred;
 216        argv->iov_base = (void*)((u32*)argv->iov_base + slen);
 217        argv->iov_len -= slen*4;
 218
 219        cred->cr_uid = ntohl(svc_getu32(argv));
 220        cred->cr_gid = ntohl(svc_getu32(argv));
 221        slen = ntohl(svc_getu32(argv));
 222        if (slen > 16 || (len -= (slen + 2)*4) < 0)
 223                goto badcred;
 224        cred->cr_group_info = groups_alloc(slen);
 225        if (cred->cr_group_info == NULL)
 226                return SVC_DROP;
 227        for (i = 0; i < slen; i++)
 228                GROUP_AT(cred->cr_group_info, i) = ntohl(svc_getu32(argv));
 229
 230        if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {
 231                *authp = rpc_autherr_badverf;
 232                return SVC_DENIED;
 233        }
 234        /* Put NULL verifier */
 235        svc_putu32(resv, RPC_AUTH_NULL);
 236        svc_putu32(resv, 0);
 237        dprintk("%s: success, returning %d!\n", __FUNCTION__, SVC_OK);
 238        return SVC_OK;
 239badcred:
 240        *authp = rpc_autherr_badcred;
 241        return SVC_DENIED;
 242}
 243
 244static int nfs_callback_unix_release(struct svc_rqst *rqstp)
 245{
 246        if (rqstp->rq_cred.cr_group_info)
 247                put_group_info(rqstp->rq_cred.cr_group_info);
 248        rqstp->rq_cred.cr_group_info = NULL;
 249        return 0;
 250}
 251
 252static struct auth_ops nfs_callback_auth_unix = {
 253        .name = "unix",
 254        .flavour = RPC_AUTH_UNIX,
 255        .accept = nfs_callback_unix_accept,
 256        .release = nfs_callback_unix_release,
 257};
 258
 259/*
 260 * Hook the authentication protocol
 261 */
 262static int nfs_callback_auth(struct svc_rqst *rqstp, u32 *authp)
 263{
 264        struct in_addr *addr = &rqstp->rq_addr.sin_addr;
 265        struct nfs4_client *clp;
 266        struct kvec *argv = &rqstp->rq_arg.head[0];
 267        int flavour;
 268        int retval;
 269
 270        /* Don't talk to strangers */
 271        clp = nfs4_find_client(addr);
 272        if (clp == NULL)
 273                return SVC_DROP;
 274        dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr));
 275        nfs4_put_client(clp);
 276        flavour = ntohl(svc_getu32(argv));
 277        switch(flavour) {
 278                case RPC_AUTH_NULL:
 279                        if (rqstp->rq_proc != CB_NULL) {
 280                                *authp = rpc_autherr_tooweak;
 281                                retval = SVC_DENIED;
 282                                break;
 283                        }
 284                        rqstp->rq_authop = &nfs_callback_auth_null;
 285                        retval = nfs_callback_null_accept(rqstp, authp);
 286                        break;
 287                case RPC_AUTH_UNIX:
 288                        /* Eat the authentication flavour */
 289                        rqstp->rq_authop = &nfs_callback_auth_unix;
 290                        retval = nfs_callback_unix_accept(rqstp, authp);
 291                        break;
 292                default:
 293                        /* FIXME: need to add RPCSEC_GSS upcalls */
 294#if 0
 295                        svc_ungetu32(argv);
 296                        retval = svc_authenticate(rqstp, authp);
 297#else
 298                        *authp = rpc_autherr_rejectedcred;
 299                        retval = SVC_DENIED;
 300#endif
 301        }
 302        dprintk("%s: flavour %d returning error %d\n", __FUNCTION__, flavour, retval);
 303        return retval;
 304}
 305
 306/*
 307 * Define NFS4 callback program
 308 */
 309extern struct svc_version nfs4_callback_version1;
 310
 311static struct svc_version *nfs4_callback_version[] = {
 312        [1] = &nfs4_callback_version1,
 313};
 314
 315static struct svc_stat nfs4_callback_stats;
 316
 317static struct svc_program nfs4_callback_program = {
 318        .pg_prog = NFS4_CALLBACK,                       /* RPC service number */
 319        .pg_nvers = ARRAY_SIZE(nfs4_callback_version),  /* Number of entries */
 320        .pg_vers = nfs4_callback_version,               /* version table */
 321        .pg_name = "NFSv4 callback",                    /* service name */
 322        .pg_class = "nfs",                              /* authentication class */
 323        .pg_stats = &nfs4_callback_stats,
 324        .pg_authenticate = nfs_callback_auth,
 325};
 326
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.