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