*** empty log message ***
[mmondor.git] / mmsoftware / mmlib / mmrc4util.c
CommitLineData
a9bcfbda 1/* $Id: mmrc4util.c,v 1.11 2004/05/02 11:24:20 mmondor Exp $ */
14545a99
MM
2
3/*
d4aaa170 4 * Copyright (C) 2003-2004, Matthew Mondor
14545a99
MM
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Matthew Mondor.
18 * 4. The name of Matthew Mondor may not be used to endorse or promote
19 * products derived from this software without specific prior written
20 * permission.
21 * 5. Redistribution of source code may not be released under the terms of
22 * any GNU Public License derivate.
23 *
24 * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36
37
38#include <sys/types.h>
39#include <sys/mman.h>
40#include <unistd.h>
41#include <fcntl.h>
42#include <stdlib.h>
43#include <errno.h>
44
45#include <mmrc4util.h>
46#include <mmfd.h>
47#include <mmheap.h>
48#include <mmstring.h>
49#include <mmlog.h>
50
51
52
d4aaa170 53MMCOPYRIGHT("@(#) Copyright (c) 2003-2004\n\
14545a99 54\tMatthew Mondor. All rights reserved.\n");
a9bcfbda 55MMRCSID("$Id: mmrc4util.c,v 1.11 2004/05/02 11:24:20 mmondor Exp $");
14545a99
MM
56
57
58
59static bool init(void);
60static ssize_t rc4_bread(int, void *, size_t);
61static ssize_t rc4_bwrite(int, const void *, size_t);
62
63
64
65static size_t pagesize = 0;
66
67
68
69static bool init(void)
70{
71 bool ok = FALSE;
72
73 if (pagesize == 0) {
74 if ((pagesize = (size_t)sysconf(_SC_PAGESIZE)) != -1)
75 ok = TRUE;
76 } else
77 ok = TRUE;
78
79 return ok;
80}
81
82
83/* Safely load a private key from the specified file, in a way which will
84 * prevent the key contents from being swapped out to the swap device.
85 */
86void rc4_load(RC4_KEY **key, void **data, const char *file, size_t size)
87{
88 void *mem;
89 int fd;
90 size_t rsize;
91
6648da34
MM
92 if (key == NULL)
93 return;
94
14545a99
MM
95 *key = NULL;
96 if (!init())
97 return;
98
99 rsize = (size_t)BALIGN_CEIL(size, pagesize);
100
101 if (file == NULL) {
102 DPRINTF("rc4_load", "file == NULL");
103 return;
104 }
105
106 if ((fd = open(file, O_RDONLY)) != -1) {
e6a00e2b
MM
107 if ((mem = mmap(NULL, rsize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd,
108 0)) != MAP_FAILED) {
14545a99
MM
109 if (mlock(mem, rsize) == 0) {
110 close(fd);
111 fd = -1;
112 if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL)
113 RC4_set_key(*key, size, mem);
114 else
115 DPRINTF("rc4_load", "secure_malloc(%d)", sizeof(RC4_KEY));
116 if (data != NULL) {
117 if ((*data = secure_malloc(size)) != NULL)
118 mm_memcpy(*data, mem, size);
119 else
120 DPRINTF("rc4_load", "secure_malloc(%d)", (int)size);
121 }
e6a00e2b 122 mm_memclr(mem, rsize);
14545a99
MM
123 munlock(mem, rsize);
124 } else
125 DPRINTF("rc4_load", "mlock(%p, %d)", mem, (int)rsize);
126 munmap(mem, rsize);
127 } else
128 DPRINTF("rc4_load", "mmap(%p, %d)", mem, (int)rsize);
129 if (fd != -1)
130 close(fd);
131 } else
132 DPRINTF("rc4_load", "open(%s)", file);
133}
134
135
136/* Creates a random block of memory, and returns both the block and associated
137 * rc4 key. This system also uses safe memory.
138 */
139void rc4_random(RC4_KEY **key, void **data, size_t size)
140{
141 int fd, i, i2;
142 int rndsize = size / 16;
143 unsigned long seed;
144 long *rnd;
145
146 *key = *data = NULL;
147
148 /* We seed the random generator using an unsigned long value from the
149 * "/dev/urandom" device at every 128 bits (16 bytes, generally 4 longs).
a9bcfbda
MM
150 * We use 4.2BSD random(3) which yields better results than ANSI/POSIX
151 * rand(3) as the PRNG. Hmm mmap(2) did not seem to work with
14545a99
MM
152 * "/dev/urandom" for some reason, so we just call read(2) as needed now.
153 */
154 if ((fd = open("/dev/urandom", O_RDONLY)) != -1) {
155 if (data != NULL && (*data = secure_malloc(size)) != NULL) {
156 rnd = *data;
157 for (i = 0; i < rndsize; i++) {
158 if (read(fd, &seed, sizeof(unsigned long)) ==
159 sizeof(unsigned long))
160 srandom(seed);
161 else
162 DPRINTF("rc4_random", "read(/dev/urandom)");
163 for (i2 = 0; i2 < (16 / sizeof(long)); i2++)
164 *rnd++ = random();
165 }
166 if (key != NULL) {
167 if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL)
168 RC4_set_key(*key, size,
169 (const unsigned char *)*data);
170 else
171 DPRINTF("rc4_random", "secure_malloc(key)");
172 }
173 } else
174 DPRINTF("rc4_random", "secure_malloc(data)");
175 close(fd);
176 } else
177 DPRINTF("rc4_random", "open(/dev/urandom)");
178}
179
180
181/* Frees an rc4 key, and/or the associated random buffer. Actually only
182 * provided as an elegant API around secure_free().
183 */
184void rc4_free(RC4_KEY **key, void **data)
185{
186 if (key != NULL && *key != NULL) {
187 secure_free(*key);
188 *key = NULL;
189 }
190 if (data != NULL && *data != NULL) {
191 secure_free(*data);
192 *data = NULL;
193 }
194}
195
196
197/* Frontend to RC4_set_key() which allocates the key memory using
198 * secure_malloc().
a9bcfbda
MM
199 *
200 * XXX For better performance it would be nice to use mmpool(3) on a block
201 * of memory allocated using secure_malloc(). However, this requires knowing
202 * the maximum number of expected concurrent sessions so that the buffer be
203 * pre-allocated large enough. Of course, keys would need to be zeroed before
204 * being freed using pool_free() as well... To know how much memory to
205 * allocate, the application could call rc4_init(), possibly, with required
206 * number of maximum keys. This would also obsolete our init() function.
14545a99
MM
207 */
208void rc4_create(RC4_KEY **key, const void *data, size_t len)
209{
210 if (key != NULL && *key == NULL) {
211 if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL)
212 RC4_set_key(*key, (int)len, (const unsigned char *)data);
213 }
214}
215
216
f152b535
MM
217/* Optionally duplicates a key context, automatically allocating safe memory
218 * for the new one. Optionally duplicates the supplied key data also.
219 */
220void rc4_copy(RC4_KEY **key, RC4_KEY *fromkey, void **data,
221 const void *fromdata, size_t size)
14545a99
MM
222{
223 if (key != NULL && *key == NULL && fromkey != NULL) {
224 if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL)
225 mm_memcpy(*key, fromkey, sizeof(RC4_KEY));
226 }
f152b535
MM
227 if (data != NULL && *data == NULL && fromdata != NULL && size > 0) {
228 if ((*data = secure_malloc(size)) != NULL)
229 mm_memcpy(*data, fromdata, size);
230 }
14545a99
MM
231}
232
233
290898d3
MM
234/* Wrapper functions for the mmfd library, internally using RC4 cipher if any
235 * is set for the current session. Moreover, because the cipher key is hard
14545a99 236 * to guess, but that random data would still normally result in unexpected
290898d3 237 * results at successful decryption, we use the advantage of the cipher to also
14545a99
MM
238 * obscure the necessary data header which we add, consisting of a checksum
239 * of the data followed by the length of the block for which the checksum
240 * accounts. This allows protection against an eavesdropper attack which could
241 * use TCP sequence prediction or sniffing to input spoofed TCP packets into
242 * the stream, which would result in unexpected data at input. This way, the
243 * stream's security does not only rely on the clear text TCP/IP headers to
244 * prevent connection hijacking. The checksum/length header is of course,
a9bcfbda
MM
245 * also encrypted, and thus difficult to forge for an attacker, since the
246 * checksum is calculated from the data before it was encrypted.
247 *
248 * XXX We only use a simple checksum, but we encode it using RC4 as well.
249 * We could have used harder to crack checksum or hash function but it would
250 * be considerably slower than the current one. CRC32 or even MD5 or SHA1 could
251 * also be used.
14545a99
MM
252 */
253
254
982c912c
MM
255ssize_t rc4_read(RC4_KEY *key, fdbuf *fdb, void *cryptbuf, void *buf,
256 size_t size)
14545a99
MM
257{
258 ssize_t ret = 0;
982c912c 259 int fd = fdb->fd;
14545a99
MM
260
261 if (size > 0) {
262 if (key != NULL) {
263 u_int32_t header[2];
264
265 /* Obtain header which rc4_write() setup for us. This assumes
266 * that we generally write and read blocks of a similar size at
267 * once. This is what actually happens with buffering. If we
268 * attempt to read a larger block than the actual block which was
269 * written, we only read the data which the header accounted for,
270 * so that the next read operation would still obtain the header
271 * of the next written block.
272 */
982c912c 273 if (rc4_bread(fd, cryptbuf, sizeof(u_int32_t) * 2) ==
14545a99 274 sizeof(u_int32_t) * 2) {
982c912c 275 RC4(key, sizeof(u_int32_t) * 2, cryptbuf, (void *)header);
14545a99
MM
276 *header = (u_int32_t)ntohl(*header);
277 header[1] = (u_int32_t)ntohl(header[1]);
278 /* Verify size */
279 if (header[1] <= size) {
280 size = header[1];
982c912c 281 if ((ret = rc4_bread(fd, cryptbuf, size)) == size) {
14545a99
MM
282 size_t i;
283 u_int32_t csum;
284 const unsigned char *b;
285
982c912c 286 RC4(key, size, cryptbuf, buf);
14545a99
MM
287 /* Verify checksum */
288 for (b = buf, csum = 0, i = 0; i < size; i++)
289 csum += b[i];
290 if (csum != *header) {
6a34c407
MM
291 DPRINTF("rc4_read",
292 "Invalid hdr checksum (c = %u, hc = %u)",
293 csum, &header);
14545a99
MM
294 ret = -1;
295 }
296 }
297 } else {
6a34c407
MM
298 DPRINTF("rc4_read",
299 "Invalid hdr size (s = %u, hs = %u)",
300 size, header[1]);
14545a99
MM
301 ret = -1;
302 }
303 }
304 if (ret > 0) {
305 /* Update fdb context for extraneous bytes */
306 fdb->context.rbytes += sizeof(u_int32_t) * 2;
307 if (fdb->gcontext != NULL)
308 fdb->gcontext->rbytes += sizeof(u_int32_t) * 2;
309 }
310 } else
311 ret = read(fd, buf, size);
312 }
313
314 return ret;
315}
316
317
982c912c 318ssize_t rc4_write(RC4_KEY *key, fdbuf *fdb, void *cryptbuf, const void *buf,
14545a99
MM
319 size_t size)
320{
321 ssize_t ret = 0;
982c912c 322 int fd = fdb->fd;
14545a99
MM
323
324 if (size > 0) {
325 if (key != NULL) {
326 u_int32_t header[2];
327 size_t i;
328 const unsigned char *b;
329
330 /* Calculate a checksum for the block, and write out the serialized
331 * checksum with block length for rc4_read().
332 */
333 for (b = buf, *header = 0, i = 0; i < size; i++)
334 *header += b[i];
335 *header = (u_int32_t)htonl(*header);
336 header[1] = (u_int32_t)htonl(size);
982c912c
MM
337 RC4(key, sizeof(u_int32_t) * 2, (void *)header, cryptbuf);
338 if (rc4_bwrite(fd, cryptbuf, sizeof(u_int32_t) * 2) !=
14545a99
MM
339 sizeof(u_int32_t) * 2) {
340 DPRINTF("rc4_write", "Error writing header!");
341 ret = -1;
342 } else {
343 /* Write out actual block */
982c912c
MM
344 RC4(key, size, buf, cryptbuf);
345 ret = rc4_bwrite(fd, cryptbuf, size);
14545a99
MM
346 }
347 if (ret > 0) {
348 /* Update fdb context for extraneous bytes */
349 fdb->context.wbytes += sizeof(u_int32_t) * 2;
350 if (fdb->gcontext != NULL)
351 fdb->gcontext->wbytes += sizeof(u_int32_t) * 2;
352 }
353 } else
354 ret = write(fd, buf, size);
355 }
356
357 return ret;
358}
359
360
361/* These functions are used so that block read and write operations are sure
362 * to be synchronized. We know that poll(2) first returned with success for
363 * the following direction, so we can start with the actual operation, and
364 * then loop as required polling again. Moreover, these ignore the EINTR
365 * events. Of course, an important timer which needs to break the operation
366 * will be able to do so by taking direct action such as sending SIGTERM,
367 * calling exit(3), etc. Other timers should normally set flags for the
368 * main state swicher loop to verify for.
369 */
370
371
372static ssize_t rc4_bread(int fd, void *buf, size_t size)
373{
374 unsigned char *b;
375 ssize_t cur;
376 struct pollfd fds[] = {
377 {-1, POLLIN | POLLERR | POLLHUP | POLLNVAL, 0}
378 };
379
380 fds[0].fd = fd;
381 for (b = buf, cur = 0; ;) {
382 ssize_t ret;
383 int sel = 0;
384
e6f6121b
MM
385 while ((ret = read(fd, b, size - cur)) == -1 && errno == EINTR) ;
386 if (ret > 0) {
14545a99
MM
387 cur += ret;
388 b += ret;
389 } else {
390 cur = ret;
391 break;
392 }
393 if (cur < size) {
394 while (!(sel & POLLIN)) {
395 while ((sel = poll(fds, 1, -1)) == -1 && errno == EINTR) ;
396 if (sel != 1)
397 return -1;
398 sel = fds[0].revents;
399 if (sel & POLLERR || sel & POLLHUP || sel & POLLNVAL)
400 return -1;
401 }
402 } else
403 break;
404 }
405
406 return cur;
407}
408
409
410static ssize_t rc4_bwrite(int fd, const void *buf, size_t size)
411{
412 const unsigned char *b;
413 ssize_t cur;
414 struct pollfd fds[] = {
415 {-1, POLLOUT | POLLERR | POLLHUP | POLLNVAL, 0}
416 };
417
418 fds[0].fd = fd;
419 for (b = buf, cur = 0; ;) {
420 ssize_t ret;
421 int sel = 0;
422
653e669e 423 if ((ret = write(fd, b, size - cur)) > 0) {
14545a99
MM
424 cur += ret;
425 b += ret;
426 } else {
427 cur = ret;
428 break;
429 }
430 if (cur < size) {
431 while (!(sel & POLLOUT)) {
432 while ((sel = poll(fds, 1, -1)) == -1 && errno == EINTR) ;
433 if (sel != 1)
434 return -1;
435 sel = fds[0].revents;
436 if (sel & POLLERR || sel & POLLHUP || sel & POLLNVAL)
437 return -1;
438 }
439 } else
440 break;
441 }
442
443 return cur;
444}