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