File: /usr/src/linux/net/sunrpc/svcauth_des.c

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