linux-old/net/sunrpc/svcauth_des.c
<<
>>
Prefs
   1/*
   2 * linux/net/sunrpc/svcauth_des.c
   3 *
   4 * Server-side AUTH_DES handling.
   5 * 
   6 * Copyright (C) 1996, 1997 Olaf Kirch <okir@monad.swb.de>
   7 */
   8
   9#include <linux/types.h>
  10#include <linux/sched.h>
  11#include <linux/sunrpc/types.h>
  12#include <linux/sunrpc/xdr.h>
  13#include <linux/sunrpc/svcauth.h>
  14#include <linux/sunrpc/svcsock.h>
  15
  16#define RPCDBG_FACILITY RPCDBG_AUTH
  17
  18/*
  19 * DES cedential cache.
  20 * The cache is indexed by fullname/key to allow for multiple sessions
  21 * by the same user from different hosts.
  22 * It would be tempting to use the client's IP address rather than the
  23 * conversation key as an index, but that could become problematic for
  24 * multi-homed hosts that distribute traffic across their interfaces.
  25 */
  26struct des_cred {
  27        struct des_cred *       dc_next;
  28        char *                  dc_fullname;
  29        u32                     dc_nickname;
  30        des_cblock              dc_key;         /* conversation key */
  31        des_cblock              dc_xkey;        /* encrypted conv. key */
  32        des_key_schedule        dc_keysched;
  33};
  34
  35#define ADN_FULLNAME            0
  36#define ADN_NICKNAME            1
  37
  38/*
  39 * The default slack allowed when checking for replayed credentials
  40 * (in milliseconds).
  41 */
  42#define DES_REPLAY_SLACK        2000
  43
  44/*
  45 * Make sure we don't place more than one call to the key server at
  46 * a time.
  47 */
  48static int                      in_keycall = 0;
  49
  50#define FAIL(err) \
  51        { if (data) put_cred(data);                     \
  52          *authp = rpc_autherr_##err;                   \
  53          return;                                       \
  54        }
  55
  56void
  57svcauth_des(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
  58{
  59        struct svc_buf  *argp = &rqstp->rq_argbuf;
  60        struct svc_buf  *resp = &rqstp->rq_resbuf;
  61        struct svc_cred *cred = &rqstp->rq_cred;
  62        struct des_cred *data = NULL;
  63        u32             cryptkey[2];
  64        u32             cryptbuf[4];
  65        u32             *p = argp->buf;
  66        int             len   = argp->len, slen, i;
  67
  68        *authp = rpc_auth_ok;
  69
  70        if ((argp->len -= 3) < 0) {
  71                *statp = rpc_garbage_args;
  72                return;
  73        }
  74
  75        p++;                                    /* skip length field */
  76        namekind = ntohl(*p++);                 /* fullname/nickname */
  77
  78        /* Get the credentials */
  79        if (namekind == ADN_NICKNAME) {
  80                /* If we can't find the cached session key, initiate a
  81                 * new session. */
  82                if (!(data = get_cred_bynick(*p++)))
  83                        FAIL(rejectedcred);
  84        } else if (namekind == ADN_FULLNAME) {
  85                p = xdr_decode_string(p, &fullname, &len, RPC_MAXNETNAMELEN);
  86                if (p == NULL)
  87                        FAIL(badcred);
  88                cryptkey[0] = *p++;             /* get the encrypted key */
  89                cryptkey[1] = *p++;
  90                cryptbuf[2] = *p++;             /* get the encrypted window */
  91        } else {
  92                FAIL(badcred);
  93        }
  94
  95        /* If we're just updating the key, silently discard the request. */
  96        if (data && data->dc_locked) {
  97                *authp = rpc_autherr_dropit;
  98                _put_cred(data);        /* release but don't unlock */
  99                return;
 100        }
 101
 102        /* Get the verifier flavor and length */
 103        if (ntohl(*p++) != RPC_AUTH_DES && ntohl(*p++) != 12)
 104                FAIL(badverf);
 105
 106        cryptbuf[0] = *p++;                     /* encrypted time stamp */
 107        cryptbuf[1] = *p++;
 108        cryptbuf[3] = *p++;                     /* 0 or window - 1 */
 109
 110        if (namekind == ADN_NICKNAME) {
 111                status = des_ecb_encrypt((des_block *) cryptbuf,
 112                                         (des_block *) cryptbuf,
 113                                         data->dc_keysched, DES_DECRYPT);
 114        } else {
 115                /* We first have to decrypt the new session key and
 116                 * fill in the UNIX creds. */
 117                if (!(data = get_cred_byname(rqstp, authp, fullname, cryptkey)))
 118                        return;
 119                status = des_cbc_encrypt((des_cblock *) cryptbuf,
 120                                         (des_cblock *) cryptbuf, 16,
 121                                         data->dc_keysched,
 122                                         (des_cblock *) &ivec,
 123                                         DES_DECRYPT);
 124        }
 125        if (status) {
 126                printk("svcauth_des: DES decryption failed (status %d)\n",
 127                                status);
 128                FAIL(badverf);
 129        }
 130
 131        /* Now check the whole lot */
 132        if (namekind == ADN_FULLNAME) {
 133                unsigned long   winverf;
 134
 135                data->dc_window = ntohl(cryptbuf[2]);
 136                winverf = ntohl(cryptbuf[2]);
 137                if (window != winverf - 1) {
 138                        printk("svcauth_des: bad window verifier!\n");
 139                        FAIL(badverf);
 140                }
 141        }
 142
 143        /* XDR the decrypted timestamp */
 144        cryptbuf[0] = ntohl(cryptbuf[0]);
 145        cryptbuf[1] = ntohl(cryptbuf[1]);
 146        if (cryptbuf[1] > 1000000) {
 147                dprintk("svcauth_des: bad usec value %u\n", cryptbuf[1]);
 148                if (namekind == ADN_NICKNAME)
 149                        FAIL(rejectedverf);
 150                FAIL(badverf);
 151        }
 152        
 153        /*
 154         * Check for replayed credentials. We must allow for reordering
 155         * of requests by the network, and the OS scheduler, hence we
 156         * cannot expect timestamps to be increasing monotonically.
 157         * This opens a small security hole, therefore the replay_slack
 158         * value shouldn't be too large.
 159         */
 160        if ((delta = cryptbuf[0] - data->dc_timestamp[0]) <= 0) {
 161                switch (delta) {
 162                case -1:        
 163                        delta = -1000000;
 164                case 0:
 165                        delta += cryptbuf[1] - data->dc_timestamp[1];
 166                        break;
 167                default:
 168                        delta = -1000000;
 169                }
 170                if (delta < DES_REPLAY_SLACK)
 171                        FAIL(rejectedverf);
 172#ifdef STRICT_REPLAY_CHECKS
 173                /* TODO: compare time stamp to last five timestamps cached
 174                 * and reject (drop?) request if a match is found. */
 175#endif
 176        }
 177
 178        now = xtime;
 179        now.tv_secs -= data->dc_window;
 180        if (now.tv_secs < cryptbuf[0] ||
 181            (now.tv_secs == cryptbuf[0] && now.tv_usec < cryptbuf[1]))
 182                FAIL(rejectedverf);
 183
 184        /* Okay, we're done. Update the lot */
 185        if (namekind == ADN_FULLNAME)
 186                data->dc_valid = 1;
 187        data->dc_timestamp[0] = cryptbuf[0];
 188        data->dc_timestamp[1] = cryptbuf[1];
 189
 190        put_cred(data);
 191        return;
 192garbage:
 193        *statp = rpc_garbage_args;
 194        return;
 195}
 196
 197/*
 198 * Call the keyserver to obtain the decrypted conversation key and
 199 * UNIX creds. We use a Linux-specific keycall extension that does
 200 * both things in one go.
 201 */
 202static struct des_cred *
 203get_cred_byname(struct svc_rqst *rqstp, u32 *authp, char *fullname, u32 *cryptkey)
 204{
 205        static int      in_keycall = 0;
 206        struct des_cred *cred;
 207
 208        if (in_keycall) {
 209                *authp = rpc_autherr_dropit;
 210                return NULL;
 211        }
 212        in_keycall = 1;
 213        in_keycall = 0;
 214        return cred;
 215}
 216
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.