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