1 /*
2  * MISC - Miscellaneous utility functions
3  *
4  * Author:
5  * Emile van Bergen, emile@evbergen.xs4all.nl
6  *
7  * Permission to redistribute an original or modified version of this program
8  * in source, intermediate or object code form is hereby granted exclusively
9  * under the terms of the GNU General Public License, version 2. Please see the
10  * file COPYING for details, or refer to http://www.gnu.org/copyleft/gpl.html.
11  *
12  * History:
13  * 2001/06/25 - EvB - Moved hex and memrchr here from lang_vm.c
14  * 2002/04/07 - EvB - Added encryption/decryption functions as used in
15  * 		      RFC 2868 (tunneling attrs) and RFC 2548 (MS VSAs)
16  * 2002/02/23 - EvB - Added encrypt_attr_pap
17  * 2005/06/01 - EvB - Added hmac_md5, moved get_random_data here from radclient
18  */
19 
20 char misc_id[] = "MISC - Copyright (C) 2001 Emile van Bergen.";
21 
22 
23  /*
24   * INCLUDES & DEFINES
25   */
26 
27 
28 #include <string.h>		/* For memset / memcpy / strlen */
29 #include <unistd.h>		/* For open */
30 #include <stdlib.h>		/* For rand */
31 #include <fcntl.h>		/* For O_RDONLY */
32 
33 #include <constants.h>
34 #include <metadata.h>		/* For putord / getord */
35 #include <debug.h>
36 #include <md5.h>
37 
38 #include <misc.h>
39 
40 
41 /*
42  * FUNCTIONS
43  */
44 
45 
memrchr9(char * s,int c,ssize_t len)46 char *memrchr9(char *s, int c, ssize_t len)
47 {
48 	char *ret;
49 
50 	if (len && s) {
51 		for(ret = s + len - 1; ret >= s; ret--)
52 			if (*ret == c) return ret;
53 	}
54 	return 0;
55 }
56 
57 
hex(char * buf,const char * src,ssize_t len)58 void hex(char *buf, const char *src, ssize_t len)
59 {
60 	static char *hextbl = "0123456789abcdef";
61 
62 	for( ; len > 0; len--) {
63 		*buf++ = hextbl[(*src >> 4) & 0xf];
64 		*buf++ = hextbl[*src++ & 0xf];
65 	}
66 }
67 
68 
69 /* Get reasonably good random data */
70 
71 #define RANDOM_DEV	"/dev/urandom"
72 
get_random_data(char * p,ssize_t len)73 void get_random_data(char *p, ssize_t len)
74 {
75 #ifdef RANDOM_DEV
76 	int fd;
77 	ssize_t n;
78 
79 	if ((fd = open(RANDOM_DEV, O_RDONLY)) != -1 &&
80 	    (n = read(fd, p, len)) == len &&
81 	    close(fd) != -1) return;
82 
83 	msg(F_MISC, L_NOTICE, "Warning: No data from " RANDOM_DEV ", using rand()\n");
84 #endif
85 	while(len-- > 0) *p++ = (char)(256.0 * rand() / (RAND_MAX + 1.0));
86 }
87 
88 
89 /*
90  * Encrypt/decrypt string attributes, User-Password (PAP) style (RFC2865)
91  */
92 
93 
94 /* These functions allow encryption/decryption in place, so it's possible to
95    set dst to src and dstlen pointing to srclen. Note that dstlen in no way
96    limits the number of bytes output; in case of encryption that's always
97    srclen rounded up to an even multiple of 16, and in case of decryption
98    it's srclen or less, srclen - 15 minimum. */
99 
100 
encrypt_attr_pap(char * src,META_ORD srclen,char * dst,META_ORD * dstlen,char * sec,META_ORD seclen,char * auth,META_ORD authlen)101 void encrypt_attr_pap(char *src, META_ORD srclen,
102 		      char *dst, META_ORD *dstlen,
103 		      char *sec, META_ORD seclen,
104 		      char *auth, META_ORD authlen)
105 {
106 	char *i, *o, *p, md5buf[16], *m;
107 	int l, chunks, n, retlen;
108 	md5_state_t md5ctx;
109 
110 	/* Use request authenticator after secret to hash first chunk */
111 
112 	p = auth; l = authlen;
113 
114 	/* Initialise */
115 
116 	i = src; o = dst; retlen = 0;
117 
118 	/* Process full chunks at beginning, if any */
119 
120 	for(chunks = srclen >> 4; chunks > 0; chunks--) {
121 
122 		md5_init(&md5ctx);
123 		md5_append(&md5ctx, sec, seclen);
124 		md5_append(&md5ctx, p, l);
125 		md5_finish(&md5ctx, md5buf);
126 
127 		p = o;
128 		l = 16;
129 		for(m = md5buf, n = l; n > 0; n--)
130 			*o++ = *i++ ^ *m++;
131 		retlen += l;
132 	}
133 
134 	/* Process partial chunk at end, if any */
135 
136 	if (srclen & 15) {
137 
138 		md5_init(&md5ctx);
139 		md5_append(&md5ctx, sec, seclen);
140 		md5_append(&md5ctx, p, l);
141 		md5_finish(&md5ctx, md5buf);
142 
143 		for(m = md5buf, n = srclen & 15; n > 0; n--)
144 			*o++ = *i++ ^ *m++;
145 		memcpy(o, m, 16 - (srclen & 15));   /* 0 ^ m = m after all */
146 		retlen += 16;
147 	}
148 
149 	*dstlen = retlen;
150 }
151 
152 
decrypt_attr_pap(char * src,META_ORD srclen,char * dst,META_ORD * dstlen,char * sec,META_ORD seclen,char * auth,META_ORD authlen)153 void decrypt_attr_pap(char *src, META_ORD srclen,
154 		      char *dst, META_ORD *dstlen,
155 		      char *sec, META_ORD seclen,
156 		      char *auth, META_ORD authlen)
157 {
158 	char *i, *o, md5buf[16], *m;
159 	int n, retlen;
160 	md5_state_t md5ctx;
161 
162 	/* Initialise. Round down source length to multiple of 16 and
163 	 * exit if not at least one chunk. */
164 
165 	srclen &= ~0xf;
166 	if (!srclen) { *dstlen = 0; return; }
167 	i = src + srclen; o = dst + srclen; retlen = 0;
168 
169 	/* Process chunks beyond first one, if any, using md5(sec . prev)
170 	 * Everything happens in reverse to allow decrypting in place */
171 
172 	while (i >= src + 32) {
173 
174 		md5_init(&md5ctx);
175 		md5_append(&md5ctx, sec, seclen);
176 		md5_append(&md5ctx, i - 32, 16);
177 		md5_finish(&md5ctx, md5buf);
178 
179 		for(n = 16, m = md5buf + n; n > 0; n--) *--o = *--i ^ *--m;
180 		retlen += 16;
181 	}
182 
183 	/* Process first chunk using md5(sec . auth) */
184 
185 	md5_init(&md5ctx);
186 	md5_append(&md5ctx, sec, seclen);
187 	md5_append(&md5ctx, auth, authlen);
188 	md5_finish(&md5ctx, md5buf);
189 
190 	for(n = 16, m = md5buf + n; n > 0; n--) *--o = *--i ^ *--m;
191 	retlen += 16;
192 
193 	*dstlen = retlen;
194 }
195 
196 /*
197  * Encrypt/decrypt string attributes, style 1 (RFC2868)
198  *
199  * See RFC 2868, section 3.5 for details. Currently probably indeed
200  * only useful for Tunnel-Password, but why make it a special case.
201  * It's optimized a little for speed, but it could probably be better.
202  *
203  * Both operations are done in place; for encryption, there must be
204  * enough *extra* room for 16 - (cleartextlen + 1) % 16 + 2 bytes.
205  * To be safe, you could simply have 18 bytes extra at all times.
206  * The maximum cleartext length is limited to 256 bytes, so 274 bytes
207  * is the maximum that can ever get output.
208  */
209 
210 #define	CLEAR_STRING_LEN	256 	/* The RFC says it is */
211 #define AUTHENTICATOR_LEN	16	/* The RFC says it is */
212 #define	MD5_LEN			16	/* The algorithm specifies it */
213 #define	SALT_LEN		2	/* The RFC says it is */
214 
215 
encrypt_attr_style_1(char * text,META_ORD * len,char * secret,char * reqauth)216 void encrypt_attr_style_1(char *text, META_ORD *len, char *secret,char *reqauth)
217 {
218 	char clear_buf[CLEAR_STRING_LEN];
219 	char work_buf[C_MAX_SECRSIZE + AUTHENTICATOR_LEN + SALT_LEN];
220 	char digest[MD5_LEN];
221 	char *i,*o;
222 	unsigned short salt;
223 	int clear_len;
224 	int work_len;
225 	int secret_len;
226 	int n;
227 
228 	/* Create the string we'll actually be processing by copying up to 255
229 	   bytes of original cleartext, padding it with zeroes to a multiple of
230 	   16 bytes and inserting a length octet in front. */
231 
232 	/* Limit length */
233 	clear_len = *len;
234 	if (clear_len > CLEAR_STRING_LEN - 1) clear_len = CLEAR_STRING_LEN - 1;
235 
236 	/* Write the 'limited original' length byte and copy the buffer */
237 	*clear_buf = clear_len;
238 	memcpy(clear_buf + 1, text, clear_len);
239 
240 	/* From now on, the length byte is included with the byte count */
241 	clear_len++;
242 
243 	/* Pad the string to a multiple of 1 chunk */
244 	if (clear_len % MD5_LEN) {
245 		memset(clear_buf+clear_len, 0, MD5_LEN - (clear_len % MD5_LEN));
246 	}
247 
248 	/* Define input and number of chunks to process */
249 	i = clear_buf;
250 	clear_len = (clear_len + (MD5_LEN - 1)) / MD5_LEN;
251 
252 	/* Define output and its starting length */
253 	o = text;
254 	*len = sizeof(salt);
255 
256 	/*
257 	 * Fill in salt. Must be unique per attribute that uses it in the same
258 	 * packet, and the most significant bit must be set - see RFC 2868.
259 	 * We use the memory adddress of the text xor-ed with the longword
260 	 * from the request authenticator.
261 	 */
262 	salt = ( ((long)text ^ *(long *)reqauth) & 0xffff ) | 0x8000;
263 	putord(o, sizeof(salt), salt); o += sizeof(salt);
264 
265 	/* Create a first working buffer to calc the MD5 hash over */
266 	secret_len = strlen(secret);	/* already limited by read_clients */
267 	memcpy(work_buf, secret, secret_len);
268 	memcpy(work_buf + secret_len, reqauth, AUTHENTICATOR_LEN);
269 	putord(work_buf + secret_len + AUTHENTICATOR_LEN, sizeof(salt), salt);
270 	work_len = secret_len + AUTHENTICATOR_LEN + sizeof(salt);
271 
272 	for( ; clear_len; clear_len--) {
273 
274 		/* Get the digest */
275 		md5(digest, work_buf, work_len);
276 
277 		/* Xor the clear text to get the output chunk and next buffer */
278 		for(n = 0; n < MD5_LEN; n++) {
279 			*(work_buf + secret_len + n) = *o++ = *i++ ^ digest[n];
280 		}
281 
282 		/* This is the size of the next working buffer */
283 		work_len = secret_len + MD5_LEN;
284 
285 		/* Increment the output length */
286 		*len += MD5_LEN;
287 	}
288 }
289 
290 
decrypt_attr_style_1(char * text,META_ORD * len,char * secret,char * reqauth)291 int decrypt_attr_style_1(char *text, META_ORD *len, char *secret, char *reqauth)
292 {
293 	char work_buf[C_MAX_SECRSIZE + AUTHENTICATOR_LEN + SALT_LEN];
294 	char digest[MD5_LEN];
295 	char *i,*o;
296 	unsigned short salt;
297 	int chunks;
298 	int secret_len;
299 	int n;
300 
301 	/* Check the length; the salt and at least one chunk must be present,
302 	   and partial chunks are not allowed. */
303 	if (((*len - SALT_LEN) < MD5_LEN) ||
304 	    ((*len - SALT_LEN) % MD5_LEN)) {
305 		msg(F_MISC, L_ERR, "decrypt_attr_style_1: bogus cyphertext "
306 				   "length %d\n", *len);
307 		return -1;
308 	}
309 
310 	/* Define input */
311 	i = text;
312 	chunks = ((*len - SALT_LEN) + (MD5_LEN - 1)) / MD5_LEN;
313 
314 	/* Define output - it's done in place, overwriting the salt! */
315 	o = text;
316 
317 	/* Get the salt from the input and check it */
318 	salt = getord(i, sizeof(salt)); i += sizeof(salt);
319 	if (!(salt & 0x8000)) {
320 		msg(F_MISC, L_ERR, "decrypt_attr_style_1: bogus salt 0x%04x\n",
321 		    salt);
322 		return -1;
323 	}
324 
325 	/* Create a first working buffer to calculate the MD5 hash over */
326 	secret_len = strlen(secret);	/* already limited by read_clients */
327 	memcpy(work_buf, secret, secret_len);
328 	memcpy(work_buf + secret_len, reqauth, AUTHENTICATOR_LEN);
329 	putord(work_buf + secret_len + AUTHENTICATOR_LEN, sizeof(salt), salt);
330 
331 	/* Calculate the digest */
332 	md5(digest, work_buf, secret_len+AUTHENTICATOR_LEN+sizeof(salt));
333 
334 	/* Process the first byte: the cleartext length */
335 	*(work_buf + secret_len + 0) = *i;
336 	*len = *i++ ^ digest[0];
337 
338 	if ((*len > CLEAR_STRING_LEN) ||
339 	    (*len > MD5_LEN * chunks) ||
340 	    (*len < MD5_LEN * (chunks - 1))) {
341 		msg(F_MISC, L_ERR, "decrypt_attr_style_1: bogus decrypted "
342 				   "length %d\n", *len);
343 		return -1;
344 	}
345 
346 	/* Process the rest of the first chunk */
347 	for(n = 1; n < MD5_LEN; n++) {
348 		*(work_buf + secret_len + n) = *i;
349 		*o++ = *i++ ^ digest[n];
350 	}
351 
352 	/* One full chunk is already done. */
353 	chunks--;
354 
355 	/* Loop over the rest of the chunks */
356 	for( ; chunks > 0; chunks--) {
357 
358 		/* Get the digest */
359 		md5(digest, work_buf, secret_len + MD5_LEN);
360 
361 		/* Fill the work buffer with the next cyphertext chunk and
362 		   xor the it with the digest to get the output segment */
363 		for(n = 0; n < MD5_LEN; n++) {
364 			*(work_buf + secret_len + n) = *i;
365 			*o++ = *i++ ^ digest[n];
366 		}
367 	}
368 
369 	return 0;
370 }
371 
372 
373 /* Compute HMAC using MD5 as a hash */
374 
hmac_md5(char * out,char * in,META_ORD inl,char * key,META_ORD keyl)375 void hmac_md5(char *out, char *in, META_ORD inl, char *key, META_ORD keyl)
376 {
377 	static unsigned char ipad[64], opad[64], inner[16];
378 	unsigned char *ip = ipad, *op = opad;
379 	md5_state_t mds;
380 
381 	if (keyl > 64) {
382 		md5_init(&mds);
383 		md5_append(&mds, key, keyl);
384 		md5_finish(&mds, inner);
385 		key = inner; keyl = 16;
386 	}
387 	memset(ipad, 0x36, 64); memset(opad, 0x5c, 64);
388 	while(keyl--) { *ip++ ^= *key; *op++ ^= *key++; }
389 	md5_init(&mds);
390 	md5_append(&mds, ipad, 64);
391 	md5_append(&mds, (unsigned char *)in, inl);
392 	md5_finish(&mds, inner);
393 	md5_init(&mds);
394 	md5_append(&mds, opad, 64);
395 	md5_append(&mds, inner, 16);
396 	md5_finish(&mds, out);
397 }
398 
399