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
svcauth_des(struct svc_rqst * rqstp,u32 * statp,u32 * authp)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 *
get_cred_byname(struct svc_rqst * rqstp,u32 * authp,char * fullname,u32 * cryptkey)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