~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/net/sunrpc/svcauth_des.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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  */
 26 struct 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  */
 48 static int                      in_keycall = 0;
 49 
 50 #define FAIL(err) \
 51         { if (data) put_cred(data);                     \
 52           *authp = rpc_autherr_##err;                   \
 53           return;                                       \
 54         }
 55 
 56 void
 57 svcauth_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;
192 garbage:
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  */
202 static struct des_cred *
203 get_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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.