Fixed a bug where rc4_read() and rc4_write() would not account for
[mmondor.git] / mmsoftware / mmlib / mmrc4util.c
CommitLineData
653e669e 1/* $Id: mmrc4util.c,v 1.5 2003/08/11 13:40:10 mmondor Exp $ */
14545a99
MM
2
3/*
4 * Copyright (C) 2003, Matthew Mondor
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
53MMCOPYRIGHT("@(#) Copyright (c) 2003\n\
54\tMatthew Mondor. All rights reserved.\n");
653e669e 55MMRCSID("$Id: mmrc4util.c,v 1.5 2003/08/11 13:40:10 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) {
107 if ((mem = mmap(NULL, rsize, PROT_READ, MAP_PRIVATE, fd, 0))
108 != MAP_FAILED) {
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 }
122 munlock(mem, rsize);
123 } else
124 DPRINTF("rc4_load", "mlock(%p, %d)", mem, (int)rsize);
125 munmap(mem, rsize);
126 } else
127 DPRINTF("rc4_load", "mmap(%p, %d)", mem, (int)rsize);
128 if (fd != -1)
129 close(fd);
130 } else
131 DPRINTF("rc4_load", "open(%s)", file);
132}
133
134
135/* Creates a random block of memory, and returns both the block and associated
136 * rc4 key. This system also uses safe memory.
137 */
138void rc4_random(RC4_KEY **key, void **data, size_t size)
139{
140 int fd, i, i2;
141 int rndsize = size / 16;
142 unsigned long seed;
143 long *rnd;
144
145 *key = *data = NULL;
146
147 /* We seed the random generator using an unsigned long value from the
148 * "/dev/urandom" device at every 128 bits (16 bytes, generally 4 longs).
149 * We use 4.2BSD random() which yields better results than ANSI/POSIX
150 * rand() as the PRNG. Hmm mmap(2) did not seem to work with
151 * "/dev/urandom" for some reason, so we just call read(2) as needed now.
152 */
153 if ((fd = open("/dev/urandom", O_RDONLY)) != -1) {
154 if (data != NULL && (*data = secure_malloc(size)) != NULL) {
155 rnd = *data;
156 for (i = 0; i < rndsize; i++) {
157 if (read(fd, &seed, sizeof(unsigned long)) ==
158 sizeof(unsigned long))
159 srandom(seed);
160 else
161 DPRINTF("rc4_random", "read(/dev/urandom)");
162 for (i2 = 0; i2 < (16 / sizeof(long)); i2++)
163 *rnd++ = random();
164 }
165 if (key != NULL) {
166 if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL)
167 RC4_set_key(*key, size,
168 (const unsigned char *)*data);
169 else
170 DPRINTF("rc4_random", "secure_malloc(key)");
171 }
172 } else
173 DPRINTF("rc4_random", "secure_malloc(data)");
174 close(fd);
175 } else
176 DPRINTF("rc4_random", "open(/dev/urandom)");
177}
178
179
180/* Frees an rc4 key, and/or the associated random buffer. Actually only
181 * provided as an elegant API around secure_free().
182 */
183void rc4_free(RC4_KEY **key, void **data)
184{
185 if (key != NULL && *key != NULL) {
186 secure_free(*key);
187 *key = NULL;
188 }
189 if (data != NULL && *data != NULL) {
190 secure_free(*data);
191 *data = NULL;
192 }
193}
194
195
196/* Frontend to RC4_set_key() which allocates the key memory using
197 * secure_malloc().
198 */
199void rc4_create(RC4_KEY **key, const void *data, size_t len)
200{
201 if (key != NULL && *key == NULL) {
202 if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL)
203 RC4_set_key(*key, (int)len, (const unsigned char *)data);
204 }
205}
206
207
f152b535
MM
208/* Optionally duplicates a key context, automatically allocating safe memory
209 * for the new one. Optionally duplicates the supplied key data also.
210 */
211void rc4_copy(RC4_KEY **key, RC4_KEY *fromkey, void **data,
212 const void *fromdata, size_t size)
14545a99
MM
213{
214 if (key != NULL && *key == NULL && fromkey != NULL) {
215 if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL)
216 mm_memcpy(*key, fromkey, sizeof(RC4_KEY));
217 }
f152b535
MM
218 if (data != NULL && *data == NULL && fromdata != NULL && size > 0) {
219 if ((*data = secure_malloc(size)) != NULL)
220 mm_memcpy(*data, fromdata, size);
221 }
14545a99
MM
222}
223
224
290898d3
MM
225/* Wrapper functions for the mmfd library, internally using RC4 cipher if any
226 * is set for the current session. Moreover, because the cipher key is hard
14545a99 227 * to guess, but that random data would still normally result in unexpected
290898d3 228 * results at successful decryption, we use the advantage of the cipher to also
14545a99
MM
229 * obscure the necessary data header which we add, consisting of a checksum
230 * of the data followed by the length of the block for which the checksum
231 * accounts. This allows protection against an eavesdropper attack which could
232 * use TCP sequence prediction or sniffing to input spoofed TCP packets into
233 * the stream, which would result in unexpected data at input. This way, the
234 * stream's security does not only rely on the clear text TCP/IP headers to
235 * prevent connection hijacking. The checksum/length header is of course,
236 * also encrypted, and thus extremely difficult to forge for an attacker,
237 * since the checksum is calculated from the data before it was encrypted.
238 */
239
240
241ssize_t rc4_read(RC4_KEY *key, fdbuf *fdb, int fd, void *buf, size_t size)
242{
243 ssize_t ret = 0;
244
245 if (size > 0) {
246 if (key != NULL) {
247 u_int32_t header[2];
248
249 /* Obtain header which rc4_write() setup for us. This assumes
250 * that we generally write and read blocks of a similar size at
251 * once. This is what actually happens with buffering. If we
252 * attempt to read a larger block than the actual block which was
253 * written, we only read the data which the header accounted for,
254 * so that the next read operation would still obtain the header
255 * of the next written block.
256 */
257 if (rc4_bread(fd, header, sizeof(u_int32_t) * 2) ==
258 sizeof(u_int32_t) * 2) {
259 RC4(key, sizeof(u_int32_t) * 2, (void *)header,
260 (void *)header);
261 *header = (u_int32_t)ntohl(*header);
262 header[1] = (u_int32_t)ntohl(header[1]);
263 /* Verify size */
264 if (header[1] <= size) {
265 size = header[1];
266 if ((ret = rc4_bread(fd, buf, size)) == size) {
267 size_t i;
268 u_int32_t csum;
269 const unsigned char *b;
270
271 RC4(key, size, buf, buf);
272 /* Verify checksum */
273 for (b = buf, csum = 0, i = 0; i < size; i++)
274 csum += b[i];
275 if (csum != *header) {
276 DPRINTF("rc4_read", "Invalid header checksum!");
277 ret = -1;
278 }
279 }
280 } else {
281 DPRINTF("rc4_read", "Invalid header size!");
282 ret = -1;
283 }
284 }
285 if (ret > 0) {
286 /* Update fdb context for extraneous bytes */
287 fdb->context.rbytes += sizeof(u_int32_t) * 2;
288 if (fdb->gcontext != NULL)
289 fdb->gcontext->rbytes += sizeof(u_int32_t) * 2;
290 }
291 } else
292 ret = read(fd, buf, size);
293 }
294
295 return ret;
296}
297
298
299ssize_t rc4_write(RC4_KEY *key, fdbuf *fdb, int fd, const void *buf,
300 size_t size)
301{
302 ssize_t ret = 0;
303
304 if (size > 0) {
305 if (key != NULL) {
306 u_int32_t header[2];
307 size_t i;
308 const unsigned char *b;
309
310 /* Calculate a checksum for the block, and write out the serialized
311 * checksum with block length for rc4_read().
312 */
313 for (b = buf, *header = 0, i = 0; i < size; i++)
314 *header += b[i];
315 *header = (u_int32_t)htonl(*header);
316 header[1] = (u_int32_t)htonl(size);
317 RC4(key, sizeof(u_int32_t) * 2, (void *)header, (void *)header);
318 if (rc4_bwrite(fd, header, sizeof(u_int32_t) * 2) !=
319 sizeof(u_int32_t) * 2) {
320 DPRINTF("rc4_write", "Error writing header!");
321 ret = -1;
322 } else {
323 /* Write out actual block */
324 RC4(key, size, buf, (void *)buf);
325 ret = rc4_bwrite(fd, buf, size);
326 }
327 if (ret > 0) {
328 /* Update fdb context for extraneous bytes */
329 fdb->context.wbytes += sizeof(u_int32_t) * 2;
330 if (fdb->gcontext != NULL)
331 fdb->gcontext->wbytes += sizeof(u_int32_t) * 2;
332 }
333 } else
334 ret = write(fd, buf, size);
335 }
336
337 return ret;
338}
339
340
341/* These functions are used so that block read and write operations are sure
342 * to be synchronized. We know that poll(2) first returned with success for
343 * the following direction, so we can start with the actual operation, and
344 * then loop as required polling again. Moreover, these ignore the EINTR
345 * events. Of course, an important timer which needs to break the operation
346 * will be able to do so by taking direct action such as sending SIGTERM,
347 * calling exit(3), etc. Other timers should normally set flags for the
348 * main state swicher loop to verify for.
349 */
350
351
352static ssize_t rc4_bread(int fd, void *buf, size_t size)
353{
354 unsigned char *b;
355 ssize_t cur;
356 struct pollfd fds[] = {
357 {-1, POLLIN | POLLERR | POLLHUP | POLLNVAL, 0}
358 };
359
360 fds[0].fd = fd;
361 for (b = buf, cur = 0; ;) {
362 ssize_t ret;
363 int sel = 0;
364
653e669e 365 if ((ret = read(fd, b, size - cur)) > 0) {
14545a99
MM
366 cur += ret;
367 b += ret;
368 } else {
369 cur = ret;
370 break;
371 }
372 if (cur < size) {
373 while (!(sel & POLLIN)) {
374 while ((sel = poll(fds, 1, -1)) == -1 && errno == EINTR) ;
375 if (sel != 1)
376 return -1;
377 sel = fds[0].revents;
378 if (sel & POLLERR || sel & POLLHUP || sel & POLLNVAL)
379 return -1;
380 }
381 } else
382 break;
383 }
384
385 return cur;
386}
387
388
389static ssize_t rc4_bwrite(int fd, const void *buf, size_t size)
390{
391 const unsigned char *b;
392 ssize_t cur;
393 struct pollfd fds[] = {
394 {-1, POLLOUT | POLLERR | POLLHUP | POLLNVAL, 0}
395 };
396
397 fds[0].fd = fd;
398 for (b = buf, cur = 0; ;) {
399 ssize_t ret;
400 int sel = 0;
401
653e669e 402 if ((ret = write(fd, b, size - cur)) > 0) {
14545a99
MM
403 cur += ret;
404 b += ret;
405 } else {
406 cur = ret;
407 break;
408 }
409 if (cur < size) {
410 while (!(sel & POLLOUT)) {
411 while ((sel = poll(fds, 1, -1)) == -1 && errno == EINTR) ;
412 if (sel != 1)
413 return -1;
414 sel = fds[0].revents;
415 if (sel & POLLERR || sel & POLLHUP || sel & POLLNVAL)
416 return -1;
417 }
418 } else
419 break;
420 }
421
422 return cur;
423}