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