*** empty log message ***
[mmondor.git] / mmsoftware / mmlib / mmrc4util.c
CommitLineData
e6a00e2b 1/* $Id: mmrc4util.c,v 1.10 2004/04/02 18:06:46 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");
e6a00e2b 55MMRCSID("$Id: mmrc4util.c,v 1.10 2004/04/02 18:06:46 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).
150 * We use 4.2BSD random() which yields better results than ANSI/POSIX
151 * rand() as the PRNG. Hmm mmap(2) did not seem to work with
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().
199 */
200void rc4_create(RC4_KEY **key, const void *data, size_t len)
201{
202 if (key != NULL && *key == NULL) {
203 if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL)
204 RC4_set_key(*key, (int)len, (const unsigned char *)data);
205 }
206}
207
208
f152b535
MM
209/* Optionally duplicates a key context, automatically allocating safe memory
210 * for the new one. Optionally duplicates the supplied key data also.
211 */
212void rc4_copy(RC4_KEY **key, RC4_KEY *fromkey, void **data,
213 const void *fromdata, size_t size)
14545a99
MM
214{
215 if (key != NULL && *key == NULL && fromkey != NULL) {
216 if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL)
217 mm_memcpy(*key, fromkey, sizeof(RC4_KEY));
218 }
f152b535
MM
219 if (data != NULL && *data == NULL && fromdata != NULL && size > 0) {
220 if ((*data = secure_malloc(size)) != NULL)
221 mm_memcpy(*data, fromdata, size);
222 }
14545a99
MM
223}
224
225
290898d3
MM
226/* Wrapper functions for the mmfd library, internally using RC4 cipher if any
227 * is set for the current session. Moreover, because the cipher key is hard
14545a99 228 * to guess, but that random data would still normally result in unexpected
290898d3 229 * results at successful decryption, we use the advantage of the cipher to also
14545a99
MM
230 * obscure the necessary data header which we add, consisting of a checksum
231 * of the data followed by the length of the block for which the checksum
232 * accounts. This allows protection against an eavesdropper attack which could
233 * use TCP sequence prediction or sniffing to input spoofed TCP packets into
234 * the stream, which would result in unexpected data at input. This way, the
235 * stream's security does not only rely on the clear text TCP/IP headers to
236 * prevent connection hijacking. The checksum/length header is of course,
237 * also encrypted, and thus extremely difficult to forge for an attacker,
238 * since the checksum is calculated from the data before it was encrypted.
239 */
240
241
982c912c
MM
242ssize_t rc4_read(RC4_KEY *key, fdbuf *fdb, void *cryptbuf, void *buf,
243 size_t size)
14545a99
MM
244{
245 ssize_t ret = 0;
982c912c 246 int fd = fdb->fd;
14545a99
MM
247
248 if (size > 0) {
249 if (key != NULL) {
250 u_int32_t header[2];
251
252 /* Obtain header which rc4_write() setup for us. This assumes
253 * that we generally write and read blocks of a similar size at
254 * once. This is what actually happens with buffering. If we
255 * attempt to read a larger block than the actual block which was
256 * written, we only read the data which the header accounted for,
257 * so that the next read operation would still obtain the header
258 * of the next written block.
259 */
982c912c 260 if (rc4_bread(fd, cryptbuf, sizeof(u_int32_t) * 2) ==
14545a99 261 sizeof(u_int32_t) * 2) {
982c912c 262 RC4(key, sizeof(u_int32_t) * 2, cryptbuf, (void *)header);
14545a99
MM
263 *header = (u_int32_t)ntohl(*header);
264 header[1] = (u_int32_t)ntohl(header[1]);
265 /* Verify size */
266 if (header[1] <= size) {
267 size = header[1];
982c912c 268 if ((ret = rc4_bread(fd, cryptbuf, size)) == size) {
14545a99
MM
269 size_t i;
270 u_int32_t csum;
271 const unsigned char *b;
272
982c912c 273 RC4(key, size, cryptbuf, buf);
14545a99
MM
274 /* Verify checksum */
275 for (b = buf, csum = 0, i = 0; i < size; i++)
276 csum += b[i];
277 if (csum != *header) {
6a34c407
MM
278 DPRINTF("rc4_read",
279 "Invalid hdr checksum (c = %u, hc = %u)",
280 csum, &header);
14545a99
MM
281 ret = -1;
282 }
283 }
284 } else {
6a34c407
MM
285 DPRINTF("rc4_read",
286 "Invalid hdr size (s = %u, hs = %u)",
287 size, header[1]);
14545a99
MM
288 ret = -1;
289 }
290 }
291 if (ret > 0) {
292 /* Update fdb context for extraneous bytes */
293 fdb->context.rbytes += sizeof(u_int32_t) * 2;
294 if (fdb->gcontext != NULL)
295 fdb->gcontext->rbytes += sizeof(u_int32_t) * 2;
296 }
297 } else
298 ret = read(fd, buf, size);
299 }
300
301 return ret;
302}
303
304
982c912c 305ssize_t rc4_write(RC4_KEY *key, fdbuf *fdb, void *cryptbuf, const void *buf,
14545a99
MM
306 size_t size)
307{
308 ssize_t ret = 0;
982c912c 309 int fd = fdb->fd;
14545a99
MM
310
311 if (size > 0) {
312 if (key != NULL) {
313 u_int32_t header[2];
314 size_t i;
315 const unsigned char *b;
316
317 /* Calculate a checksum for the block, and write out the serialized
318 * checksum with block length for rc4_read().
319 */
320 for (b = buf, *header = 0, i = 0; i < size; i++)
321 *header += b[i];
322 *header = (u_int32_t)htonl(*header);
323 header[1] = (u_int32_t)htonl(size);
982c912c
MM
324 RC4(key, sizeof(u_int32_t) * 2, (void *)header, cryptbuf);
325 if (rc4_bwrite(fd, cryptbuf, sizeof(u_int32_t) * 2) !=
14545a99
MM
326 sizeof(u_int32_t) * 2) {
327 DPRINTF("rc4_write", "Error writing header!");
328 ret = -1;
329 } else {
330 /* Write out actual block */
982c912c
MM
331 RC4(key, size, buf, cryptbuf);
332 ret = rc4_bwrite(fd, cryptbuf, size);
14545a99
MM
333 }
334 if (ret > 0) {
335 /* Update fdb context for extraneous bytes */
336 fdb->context.wbytes += sizeof(u_int32_t) * 2;
337 if (fdb->gcontext != NULL)
338 fdb->gcontext->wbytes += sizeof(u_int32_t) * 2;
339 }
340 } else
341 ret = write(fd, buf, size);
342 }
343
344 return ret;
345}
346
347
348/* These functions are used so that block read and write operations are sure
349 * to be synchronized. We know that poll(2) first returned with success for
350 * the following direction, so we can start with the actual operation, and
351 * then loop as required polling again. Moreover, these ignore the EINTR
352 * events. Of course, an important timer which needs to break the operation
353 * will be able to do so by taking direct action such as sending SIGTERM,
354 * calling exit(3), etc. Other timers should normally set flags for the
355 * main state swicher loop to verify for.
356 */
357
358
359static ssize_t rc4_bread(int fd, void *buf, size_t size)
360{
361 unsigned char *b;
362 ssize_t cur;
363 struct pollfd fds[] = {
364 {-1, POLLIN | POLLERR | POLLHUP | POLLNVAL, 0}
365 };
366
367 fds[0].fd = fd;
368 for (b = buf, cur = 0; ;) {
369 ssize_t ret;
370 int sel = 0;
371
e6f6121b
MM
372 while ((ret = read(fd, b, size - cur)) == -1 && errno == EINTR) ;
373 if (ret > 0) {
14545a99
MM
374 cur += ret;
375 b += ret;
376 } else {
377 cur = ret;
378 break;
379 }
380 if (cur < size) {
381 while (!(sel & POLLIN)) {
382 while ((sel = poll(fds, 1, -1)) == -1 && errno == EINTR) ;
383 if (sel != 1)
384 return -1;
385 sel = fds[0].revents;
386 if (sel & POLLERR || sel & POLLHUP || sel & POLLNVAL)
387 return -1;
388 }
389 } else
390 break;
391 }
392
393 return cur;
394}
395
396
397static ssize_t rc4_bwrite(int fd, const void *buf, size_t size)
398{
399 const unsigned char *b;
400 ssize_t cur;
401 struct pollfd fds[] = {
402 {-1, POLLOUT | POLLERR | POLLHUP | POLLNVAL, 0}
403 };
404
405 fds[0].fd = fd;
406 for (b = buf, cur = 0; ;) {
407 ssize_t ret;
408 int sel = 0;
409
653e669e 410 if ((ret = write(fd, b, size - cur)) > 0) {
14545a99
MM
411 cur += ret;
412 b += ret;
413 } else {
414 cur = ret;
415 break;
416 }
417 if (cur < size) {
418 while (!(sel & POLLOUT)) {
419 while ((sel = poll(fds, 1, -1)) == -1 && errno == EINTR) ;
420 if (sel != 1)
421 return -1;
422 sel = fds[0].revents;
423 if (sel & POLLERR || sel & POLLHUP || sel & POLLNVAL)
424 return -1;
425 }
426 } else
427 break;
428 }
429
430 return cur;
431}