/src/strongswan/src/libstrongswan/utils/chunk.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2008-2019 Tobias Brunner |
3 | | * Copyright (C) 2023 Andreas Steffen |
4 | | * Copyright (C) 2005-2006 Martin Willi |
5 | | * Copyright (C) 2005 Jan Hutter |
6 | | * |
7 | | * Copyright (C) secunet Security Networks AG |
8 | | * |
9 | | * This program is free software; you can redistribute it and/or modify it |
10 | | * under the terms of the GNU General Public License as published by the |
11 | | * Free Software Foundation; either version 2 of the License, or (at your |
12 | | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
13 | | * |
14 | | * This program is distributed in the hope that it will be useful, but |
15 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
16 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
17 | | * for more details. |
18 | | */ |
19 | | |
20 | | #include <stdio.h> |
21 | | #include <sys/types.h> |
22 | | #include <sys/stat.h> |
23 | | #ifdef HAVE_MMAP |
24 | | # include <sys/mman.h> |
25 | | #endif |
26 | | #include <fcntl.h> |
27 | | #include <unistd.h> |
28 | | #include <errno.h> |
29 | | #include <ctype.h> |
30 | | #include <time.h> |
31 | | |
32 | | #include "chunk.h" |
33 | | |
34 | | /** |
35 | | * Empty chunk. |
36 | | */ |
37 | | chunk_t chunk_empty = { NULL, 0 }; |
38 | | |
39 | | /** |
40 | | * Described in header. |
41 | | */ |
42 | | chunk_t chunk_create_clone(u_char *ptr, chunk_t chunk) |
43 | 38.8k | { |
44 | 38.8k | chunk_t clone = chunk_empty; |
45 | | |
46 | 38.8k | if (chunk.ptr && chunk.len > 0) |
47 | 37.8k | { |
48 | 37.8k | clone.ptr = ptr; |
49 | 37.8k | clone.len = chunk.len; |
50 | 37.8k | memcpy(clone.ptr, chunk.ptr, chunk.len); |
51 | 37.8k | } |
52 | | |
53 | 38.8k | return clone; |
54 | 38.8k | } |
55 | | |
56 | | /** |
57 | | * Described in header. |
58 | | */ |
59 | | size_t chunk_length(const char* mode, ...) |
60 | 0 | { |
61 | 0 | va_list chunks; |
62 | 0 | size_t length = 0; |
63 | |
|
64 | 0 | va_start(chunks, mode); |
65 | 0 | while (TRUE) |
66 | 0 | { |
67 | 0 | switch (*mode++) |
68 | 0 | { |
69 | 0 | case 'm': |
70 | 0 | case 'c': |
71 | 0 | case 's': |
72 | 0 | { |
73 | 0 | chunk_t ch = va_arg(chunks, chunk_t); |
74 | 0 | length += ch.len; |
75 | 0 | continue; |
76 | 0 | } |
77 | 0 | default: |
78 | 0 | break; |
79 | 0 | } |
80 | 0 | break; |
81 | 0 | } |
82 | 0 | va_end(chunks); |
83 | 0 | return length; |
84 | 0 | } |
85 | | |
86 | | /** |
87 | | * Described in header. |
88 | | */ |
89 | | chunk_t chunk_create_cat(u_char *ptr, const char* mode, ...) |
90 | 0 | { |
91 | 0 | va_list chunks; |
92 | 0 | chunk_t construct = chunk_create(ptr, 0); |
93 | |
|
94 | 0 | va_start(chunks, mode); |
95 | 0 | while (TRUE) |
96 | 0 | { |
97 | 0 | bool free_chunk = FALSE, clear_chunk = FALSE; |
98 | 0 | chunk_t ch; |
99 | |
|
100 | 0 | switch (*mode++) |
101 | 0 | { |
102 | 0 | case 's': |
103 | 0 | clear_chunk = TRUE; |
104 | | /* FALL */ |
105 | 0 | case 'm': |
106 | 0 | free_chunk = TRUE; |
107 | | /* FALL */ |
108 | 0 | case 'c': |
109 | 0 | ch = va_arg(chunks, chunk_t); |
110 | 0 | memcpy(ptr, ch.ptr, ch.len); |
111 | 0 | ptr += ch.len; |
112 | 0 | construct.len += ch.len; |
113 | 0 | if (clear_chunk) |
114 | 0 | { |
115 | 0 | chunk_clear(&ch); |
116 | 0 | } |
117 | 0 | else if (free_chunk) |
118 | 0 | { |
119 | 0 | free(ch.ptr); |
120 | 0 | } |
121 | 0 | continue; |
122 | 0 | default: |
123 | 0 | break; |
124 | 0 | } |
125 | 0 | break; |
126 | 0 | } |
127 | 0 | va_end(chunks); |
128 | |
|
129 | 0 | return construct; |
130 | 0 | } |
131 | | |
132 | | /** |
133 | | * Described in header. |
134 | | */ |
135 | | void chunk_split(chunk_t chunk, const char *mode, ...) |
136 | 0 | { |
137 | 0 | va_list chunks; |
138 | 0 | u_int len; |
139 | 0 | chunk_t *ch; |
140 | |
|
141 | 0 | va_start(chunks, mode); |
142 | 0 | while (TRUE) |
143 | 0 | { |
144 | 0 | if (*mode == '\0') |
145 | 0 | { |
146 | 0 | break; |
147 | 0 | } |
148 | 0 | len = va_arg(chunks, u_int); |
149 | 0 | ch = va_arg(chunks, chunk_t*); |
150 | | /* a null chunk means skip len bytes */ |
151 | 0 | if (ch == NULL) |
152 | 0 | { |
153 | 0 | chunk = chunk_skip(chunk, len); |
154 | 0 | continue; |
155 | 0 | } |
156 | 0 | switch (*mode++) |
157 | 0 | { |
158 | 0 | case 'm': |
159 | 0 | { |
160 | 0 | ch->len = min(chunk.len, len); |
161 | 0 | if (ch->len) |
162 | 0 | { |
163 | 0 | ch->ptr = chunk.ptr; |
164 | 0 | } |
165 | 0 | else |
166 | 0 | { |
167 | 0 | ch->ptr = NULL; |
168 | 0 | } |
169 | 0 | chunk = chunk_skip(chunk, ch->len); |
170 | 0 | continue; |
171 | 0 | } |
172 | 0 | case 'a': |
173 | 0 | { |
174 | 0 | ch->len = min(chunk.len, len); |
175 | 0 | if (ch->len) |
176 | 0 | { |
177 | 0 | ch->ptr = malloc(ch->len); |
178 | 0 | memcpy(ch->ptr, chunk.ptr, ch->len); |
179 | 0 | } |
180 | 0 | else |
181 | 0 | { |
182 | 0 | ch->ptr = NULL; |
183 | 0 | } |
184 | 0 | chunk = chunk_skip(chunk, ch->len); |
185 | 0 | continue; |
186 | 0 | } |
187 | 0 | case 'c': |
188 | 0 | { |
189 | 0 | ch->len = min(ch->len, chunk.len); |
190 | 0 | ch->len = min(ch->len, len); |
191 | 0 | if (ch->len) |
192 | 0 | { |
193 | 0 | memcpy(ch->ptr, chunk.ptr, ch->len); |
194 | 0 | } |
195 | 0 | else |
196 | 0 | { |
197 | 0 | ch->ptr = NULL; |
198 | 0 | } |
199 | 0 | chunk = chunk_skip(chunk, ch->len); |
200 | 0 | continue; |
201 | 0 | } |
202 | 0 | default: |
203 | 0 | break; |
204 | 0 | } |
205 | 0 | break; |
206 | 0 | } |
207 | 0 | va_end(chunks); |
208 | 0 | } |
209 | | |
210 | | /** |
211 | | * Described in header. |
212 | | */ |
213 | | bool chunk_write(chunk_t chunk, char *path, mode_t mask, bool force) |
214 | 0 | { |
215 | 0 | mode_t oldmask; |
216 | 0 | FILE *fd; |
217 | 0 | bool good = FALSE; |
218 | 0 | int tmp = 0; |
219 | |
|
220 | 0 | if (!force && access(path, F_OK) == 0) |
221 | 0 | { |
222 | 0 | errno = EEXIST; |
223 | 0 | return FALSE; |
224 | 0 | } |
225 | 0 | oldmask = umask(mask); |
226 | 0 | fd = fopen(path, |
227 | | #ifdef WIN32 |
228 | | "wb" |
229 | | #else |
230 | 0 | "w" |
231 | 0 | #endif |
232 | 0 | ); |
233 | |
|
234 | 0 | if (fd) |
235 | 0 | { |
236 | 0 | if (fwrite(chunk.ptr, sizeof(u_char), chunk.len, fd) == chunk.len) |
237 | 0 | { |
238 | 0 | good = TRUE; |
239 | 0 | } |
240 | 0 | else |
241 | 0 | { |
242 | 0 | tmp = errno; |
243 | 0 | } |
244 | 0 | fclose(fd); |
245 | 0 | } |
246 | 0 | else |
247 | 0 | { |
248 | 0 | tmp = errno; |
249 | 0 | } |
250 | 0 | umask(oldmask); |
251 | 0 | errno = tmp; |
252 | 0 | return good; |
253 | 0 | } |
254 | | |
255 | | /** |
256 | | * Described in header. |
257 | | */ |
258 | | bool chunk_from_fd(int fd, chunk_t *out) |
259 | 0 | { |
260 | 0 | struct stat sb; |
261 | 0 | char *buf, *tmp; |
262 | 0 | ssize_t len, total = 0, bufsize; |
263 | |
|
264 | 0 | if (fstat(fd, &sb) == 0 && S_ISREG(sb.st_mode)) |
265 | 0 | { |
266 | 0 | bufsize = sb.st_size; |
267 | 0 | } |
268 | 0 | else |
269 | 0 | { |
270 | 0 | bufsize = 256; |
271 | 0 | } |
272 | 0 | buf = malloc(bufsize); |
273 | 0 | if (!buf) |
274 | 0 | { /* for huge files */ |
275 | 0 | return FALSE; |
276 | 0 | } |
277 | | |
278 | 0 | while (TRUE) |
279 | 0 | { |
280 | 0 | len = read(fd, buf + total, bufsize - total); |
281 | | #ifdef WIN32 |
282 | | if (len == -1 && errno == EBADF) |
283 | | { /* operating on a Winsock socket? */ |
284 | | len = recv(fd, buf + total, bufsize - total, 0); |
285 | | } |
286 | | #endif |
287 | 0 | if (len < 0) |
288 | 0 | { |
289 | 0 | free(buf); |
290 | 0 | return FALSE; |
291 | 0 | } |
292 | 0 | if (len == 0) |
293 | 0 | { |
294 | 0 | break; |
295 | 0 | } |
296 | 0 | total += len; |
297 | 0 | if (total == bufsize) |
298 | 0 | { |
299 | 0 | bufsize *= 2; |
300 | 0 | tmp = realloc(buf, bufsize); |
301 | 0 | if (!tmp) |
302 | 0 | { |
303 | 0 | free(buf); |
304 | 0 | return FALSE; |
305 | 0 | } |
306 | 0 | buf = tmp; |
307 | 0 | } |
308 | 0 | } |
309 | 0 | if (total == 0) |
310 | 0 | { |
311 | 0 | free(buf); |
312 | 0 | buf = NULL; |
313 | 0 | } |
314 | 0 | else if (total < bufsize) |
315 | 0 | { |
316 | 0 | buf = realloc(buf, total); |
317 | 0 | } |
318 | 0 | *out = chunk_create(buf, total); |
319 | 0 | return TRUE; |
320 | 0 | } |
321 | | |
322 | | /** |
323 | | * Implementation for mmap()ed chunks |
324 | | */ |
325 | | typedef struct { |
326 | | /* public chunk interface */ |
327 | | chunk_t public; |
328 | | /* FD of open file */ |
329 | | int fd; |
330 | | /* mmap() address */ |
331 | | void *map; |
332 | | /* size of map */ |
333 | | size_t len; |
334 | | /* do we write? */ |
335 | | bool wr; |
336 | | } mmaped_chunk_t; |
337 | | |
338 | | /** |
339 | | * See header. |
340 | | */ |
341 | | chunk_t *chunk_map(char *path, bool wr) |
342 | 0 | { |
343 | 0 | mmaped_chunk_t *chunk; |
344 | 0 | struct stat sb; |
345 | 0 | int tmp, flags; |
346 | |
|
347 | 0 | flags = wr ? O_RDWR : O_RDONLY; |
348 | | #ifdef WIN32 |
349 | | flags |= O_BINARY; |
350 | | #endif |
351 | |
|
352 | 0 | INIT(chunk, |
353 | 0 | .fd = open(path, flags), |
354 | 0 | .wr = wr, |
355 | 0 | ); |
356 | |
|
357 | 0 | if (chunk->fd == -1) |
358 | 0 | { |
359 | 0 | free(chunk); |
360 | 0 | return NULL; |
361 | 0 | } |
362 | 0 | if (fstat(chunk->fd, &sb) == -1) |
363 | 0 | { |
364 | 0 | tmp = errno; |
365 | 0 | chunk_unmap(&chunk->public); |
366 | 0 | errno = tmp; |
367 | 0 | return NULL; |
368 | 0 | } |
369 | 0 | #ifdef HAVE_MMAP |
370 | 0 | chunk->len = sb.st_size; |
371 | | /* map non-empty files only, as mmap() complains otherwise */ |
372 | 0 | if (chunk->len) |
373 | 0 | { |
374 | | /* in read-only mode, we allow writes, but don't sync to disk */ |
375 | 0 | chunk->map = mmap(NULL, chunk->len, PROT_READ | PROT_WRITE, |
376 | 0 | wr ? MAP_SHARED : MAP_PRIVATE, chunk->fd, 0); |
377 | 0 | if (chunk->map == MAP_FAILED) |
378 | 0 | { |
379 | 0 | tmp = errno; |
380 | 0 | chunk_unmap(&chunk->public); |
381 | 0 | errno = tmp; |
382 | 0 | return NULL; |
383 | 0 | } |
384 | 0 | } |
385 | 0 | chunk->public = chunk_create(chunk->map, chunk->len); |
386 | | #else /* !HAVE_MMAP */ |
387 | | if (!chunk_from_fd(chunk->fd, &chunk->public)) |
388 | | { |
389 | | tmp = errno; |
390 | | chunk_unmap(&chunk->public); |
391 | | errno = tmp; |
392 | | return NULL; |
393 | | } |
394 | | chunk->map = chunk->public.ptr; |
395 | | chunk->len = chunk->public.len; |
396 | | #endif /* !HAVE_MMAP */ |
397 | 0 | return &chunk->public; |
398 | 0 | } |
399 | | |
400 | | /** |
401 | | * Unmap the given chunk and optionally clear it |
402 | | */ |
403 | | static bool chunk_unmap_internal(chunk_t *public, bool clear) |
404 | 0 | { |
405 | 0 | mmaped_chunk_t *chunk; |
406 | 0 | bool ret = FALSE; |
407 | 0 | int tmp = 0; |
408 | |
|
409 | 0 | chunk = (mmaped_chunk_t*)public; |
410 | 0 | #ifdef HAVE_MMAP |
411 | 0 | if (chunk->map && chunk->map != MAP_FAILED) |
412 | 0 | { |
413 | 0 | if (!chunk->wr && clear) |
414 | 0 | { |
415 | 0 | memwipe(chunk->map, chunk->len); |
416 | 0 | } |
417 | 0 | ret = munmap(chunk->map, chunk->len) == 0; |
418 | 0 | tmp = errno; |
419 | 0 | } |
420 | | #else /* !HAVE_MMAP */ |
421 | | if (chunk->wr) |
422 | | { |
423 | | if (lseek(chunk->fd, 0, SEEK_SET) != -1) |
424 | | { |
425 | | int len, total = 0; |
426 | | |
427 | | ret = TRUE; |
428 | | while (total < chunk->len) |
429 | | { |
430 | | len = write(chunk->fd, chunk->map + total, chunk->len - total); |
431 | | if (len <= 0) |
432 | | { |
433 | | ret = FALSE; |
434 | | break; |
435 | | } |
436 | | total += len; |
437 | | } |
438 | | } |
439 | | tmp = errno; |
440 | | } |
441 | | else |
442 | | { |
443 | | ret = TRUE; |
444 | | } |
445 | | if (clear) |
446 | | { |
447 | | memwipe(chunk->map, chunk->len); |
448 | | } |
449 | | free(chunk->map); |
450 | | #endif /* !HAVE_MMAP */ |
451 | 0 | close(chunk->fd); |
452 | 0 | free(chunk); |
453 | 0 | errno = tmp; |
454 | |
|
455 | 0 | return ret; |
456 | 0 | } |
457 | | |
458 | | /* |
459 | | * Described in header |
460 | | */ |
461 | | bool chunk_unmap(chunk_t *public) |
462 | 0 | { |
463 | 0 | return chunk_unmap_internal(public, FALSE); |
464 | 0 | } |
465 | | |
466 | | /* |
467 | | * Described in header |
468 | | */ |
469 | | bool chunk_unmap_clear(chunk_t *public) |
470 | 0 | { |
471 | 0 | return chunk_unmap_internal(public, TRUE); |
472 | 0 | } |
473 | | |
474 | | /** hex conversion digits */ |
475 | | static char hexdig_upper[] = "0123456789ABCDEF"; |
476 | | static char hexdig_lower[] = "0123456789abcdef"; |
477 | | |
478 | | /** |
479 | | * Described in header. |
480 | | */ |
481 | | chunk_t chunk_to_hex(chunk_t chunk, char *buf, bool uppercase) |
482 | 0 | { |
483 | 0 | int i, len; |
484 | 0 | char *hexdig = hexdig_lower; |
485 | |
|
486 | 0 | if (uppercase) |
487 | 0 | { |
488 | 0 | hexdig = hexdig_upper; |
489 | 0 | } |
490 | |
|
491 | 0 | len = chunk.len * 2; |
492 | 0 | if (!buf) |
493 | 0 | { |
494 | 0 | buf = malloc(len + 1); |
495 | 0 | } |
496 | 0 | buf[len] = '\0'; |
497 | |
|
498 | 0 | for (i = 0; i < chunk.len; i++) |
499 | 0 | { |
500 | 0 | buf[i*2] = hexdig[(chunk.ptr[i] >> 4) & 0xF]; |
501 | 0 | buf[i*2+1] = hexdig[(chunk.ptr[i] ) & 0xF]; |
502 | 0 | } |
503 | 0 | return chunk_create(buf, len); |
504 | 0 | } |
505 | | |
506 | | /** |
507 | | * convert a single hex character to its binary value |
508 | | */ |
509 | | static char hex2bin(char hex) |
510 | 43.9k | { |
511 | 43.9k | switch (hex) |
512 | 43.9k | { |
513 | 11.3k | case '0' ... '9': |
514 | 11.3k | return hex - '0'; |
515 | 12.5k | case 'A' ... 'F': |
516 | 12.5k | return hex - 'A' + 10; |
517 | 5.50k | case 'a' ... 'f': |
518 | 5.50k | return hex - 'a' + 10; |
519 | 14.5k | default: |
520 | 14.5k | return 0; |
521 | 43.9k | } |
522 | 43.9k | } |
523 | | |
524 | | /** |
525 | | * Described in header. |
526 | | */ |
527 | | chunk_t chunk_from_hex(chunk_t hex, char *buf) |
528 | 3.95k | { |
529 | 3.95k | int i, len; |
530 | 3.95k | u_char *ptr; |
531 | 3.95k | bool odd = FALSE; |
532 | | |
533 | | /* skip an optional 0x prefix */ |
534 | 3.95k | if (hex.len > 1 && hex.ptr[1] == 'x' && hex.ptr[0] == '0') |
535 | 765 | { |
536 | 765 | hex = chunk_skip(hex, 2); |
537 | 765 | } |
538 | | |
539 | | /* subtract the number of optional ':' separation characters */ |
540 | 3.95k | len = hex.len; |
541 | 3.95k | ptr = hex.ptr; |
542 | 48.8k | for (i = 0; i < hex.len; i++) |
543 | 44.9k | { |
544 | 44.9k | if (*ptr++ == ':') |
545 | 935 | { |
546 | 935 | len--; |
547 | 935 | } |
548 | 44.9k | } |
549 | | |
550 | | /* compute the number of binary bytes */ |
551 | 3.95k | if (len % 2) |
552 | 2.24k | { |
553 | 2.24k | odd = TRUE; |
554 | 2.24k | len++; |
555 | 2.24k | } |
556 | 3.95k | len /= 2; |
557 | | |
558 | | /* allocate buffer memory unless provided by caller */ |
559 | 3.95k | if (!buf) |
560 | 0 | { |
561 | 0 | buf = malloc(len); |
562 | 0 | } |
563 | | |
564 | | /* buffer is filled from the right */ |
565 | 3.95k | memset(buf, 0, len); |
566 | 3.95k | hex.ptr += hex.len; |
567 | | |
568 | 27.0k | for (i = len - 1; i >= 0; i--) |
569 | 23.1k | { |
570 | | /* skip separation characters */ |
571 | 23.1k | if (*(--hex.ptr) == ':') |
572 | 284 | { |
573 | 284 | --hex.ptr; |
574 | 284 | } |
575 | 23.1k | buf[i] = hex2bin(*hex.ptr); |
576 | 23.1k | if (i > 0 || !odd) |
577 | 20.8k | { |
578 | 20.8k | buf[i] |= hex2bin(*(--hex.ptr)) << 4; |
579 | 20.8k | } |
580 | 23.1k | } |
581 | 3.95k | return chunk_create(buf, len); |
582 | 3.95k | } |
583 | | |
584 | | /** base 64 conversion digits */ |
585 | | static char b64digits[] = |
586 | | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
587 | | |
588 | | /** |
589 | | * Described in header. |
590 | | */ |
591 | | chunk_t chunk_to_base64(chunk_t chunk, char *buf) |
592 | 0 | { |
593 | 0 | int i, len; |
594 | 0 | char *pos; |
595 | |
|
596 | 0 | len = chunk.len + ((3 - chunk.len % 3) % 3); |
597 | 0 | if (!buf) |
598 | 0 | { |
599 | 0 | buf = malloc(len * 4 / 3 + 1); |
600 | 0 | } |
601 | 0 | pos = buf; |
602 | 0 | for (i = 0; i < len; i+=3) |
603 | 0 | { |
604 | 0 | *pos++ = b64digits[chunk.ptr[i] >> 2]; |
605 | 0 | if (i+1 >= chunk.len) |
606 | 0 | { |
607 | 0 | *pos++ = b64digits[(chunk.ptr[i] & 0x03) << 4]; |
608 | 0 | *pos++ = '='; |
609 | 0 | *pos++ = '='; |
610 | 0 | break; |
611 | 0 | } |
612 | 0 | *pos++ = b64digits[((chunk.ptr[i] & 0x03) << 4) | (chunk.ptr[i+1] >> 4)]; |
613 | 0 | if (i+2 >= chunk.len) |
614 | 0 | { |
615 | 0 | *pos++ = b64digits[(chunk.ptr[i+1] & 0x0F) << 2]; |
616 | 0 | *pos++ = '='; |
617 | 0 | break; |
618 | 0 | } |
619 | 0 | *pos++ = b64digits[((chunk.ptr[i+1] & 0x0F) << 2) | (chunk.ptr[i+2] >> 6)]; |
620 | 0 | *pos++ = b64digits[chunk.ptr[i+2] & 0x3F]; |
621 | 0 | } |
622 | 0 | *pos = '\0'; |
623 | 0 | return chunk_create(buf, len * 4 / 3); |
624 | 0 | } |
625 | | |
626 | | /** |
627 | | * convert a base 64 digit to its binary form (inversion of b64digits array) |
628 | | */ |
629 | | static int b642bin(char b64) |
630 | 13.3M | { |
631 | 13.3M | switch (b64) |
632 | 13.3M | { |
633 | 3.80M | case 'A' ... 'Z': |
634 | 3.80M | return b64 - 'A'; |
635 | 4.68M | case 'a' ... 'z': |
636 | 4.68M | return ('Z' - 'A' + 1) + b64 - 'a'; |
637 | 612k | case '0' ... '9': |
638 | 612k | return ('Z' - 'A' + 1) + ('z' - 'a' + 1) + b64 - '0'; |
639 | 3.26k | case '+': |
640 | 28.7k | case '-': |
641 | 28.7k | return 62; |
642 | 6.92k | case '/': |
643 | 10.0k | case '_': |
644 | 10.0k | return 63; |
645 | 6.43k | case '=': |
646 | 6.43k | return 0; |
647 | 4.20M | default: |
648 | 4.20M | return -1; |
649 | 13.3M | } |
650 | 13.3M | } |
651 | | |
652 | | /** |
653 | | * Described in header. |
654 | | */ |
655 | | chunk_t chunk_from_base64(chunk_t base64, char *buf) |
656 | 79.5k | { |
657 | 79.5k | u_char *pos, byte[4]; |
658 | 79.5k | int i, j, len, outlen; |
659 | | |
660 | 79.5k | len = base64.len / 4 * 3; |
661 | 79.5k | if (!buf) |
662 | 0 | { |
663 | 0 | buf = malloc(len); |
664 | 0 | } |
665 | 79.5k | pos = base64.ptr; |
666 | 79.5k | outlen = 0; |
667 | 3.41M | for (i = 0; i < len; i+=3) |
668 | 3.33M | { |
669 | 3.33M | outlen += 3; |
670 | 16.6M | for (j = 0; j < 4; j++) |
671 | 13.3M | { |
672 | 13.3M | if (*pos == '=' && outlen > 0) |
673 | 5.74k | { |
674 | 5.74k | outlen--; |
675 | 5.74k | } |
676 | 13.3M | byte[j] = b642bin(*pos++); |
677 | 13.3M | } |
678 | 3.33M | buf[i] = (byte[0] << 2) | (byte[1] >> 4); |
679 | 3.33M | buf[i+1] = (byte[1] << 4) | (byte[2] >> 2); |
680 | 3.33M | buf[i+2] = (byte[2] << 6) | (byte[3]); |
681 | 3.33M | } |
682 | 79.5k | return chunk_create(buf, outlen); |
683 | 79.5k | } |
684 | | |
685 | | /** base 32 conversion digits */ |
686 | | static char b32digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; |
687 | | |
688 | | /** |
689 | | * Described in header. |
690 | | */ |
691 | | chunk_t chunk_to_base32(chunk_t chunk, char *buf) |
692 | 0 | { |
693 | 0 | int i, len; |
694 | 0 | char *pos; |
695 | |
|
696 | 0 | len = chunk.len + ((5 - chunk.len % 5) % 5); |
697 | 0 | if (!buf) |
698 | 0 | { |
699 | 0 | buf = malloc(len * 8 / 5 + 1); |
700 | 0 | } |
701 | 0 | pos = buf; |
702 | 0 | for (i = 0; i < len; i+=5) |
703 | 0 | { |
704 | 0 | *pos++ = b32digits[chunk.ptr[i] >> 3]; |
705 | 0 | if (i+1 >= chunk.len) |
706 | 0 | { |
707 | 0 | *pos++ = b32digits[(chunk.ptr[i] & 0x07) << 2]; |
708 | 0 | memset(pos, '=', 6); |
709 | 0 | pos += 6; |
710 | 0 | break; |
711 | 0 | } |
712 | 0 | *pos++ = b32digits[((chunk.ptr[i] & 0x07) << 2) | |
713 | 0 | (chunk.ptr[i+1] >> 6)]; |
714 | 0 | *pos++ = b32digits[(chunk.ptr[i+1] & 0x3E) >> 1]; |
715 | 0 | if (i+2 >= chunk.len) |
716 | 0 | { |
717 | 0 | *pos++ = b32digits[(chunk.ptr[i+1] & 0x01) << 4]; |
718 | 0 | memset(pos, '=', 4); |
719 | 0 | pos += 4; |
720 | 0 | break; |
721 | 0 | } |
722 | 0 | *pos++ = b32digits[((chunk.ptr[i+1] & 0x01) << 4) | |
723 | 0 | (chunk.ptr[i+2] >> 4)]; |
724 | 0 | if (i+3 >= chunk.len) |
725 | 0 | { |
726 | 0 | *pos++ = b32digits[(chunk.ptr[i+2] & 0x0F) << 1]; |
727 | 0 | memset(pos, '=', 3); |
728 | 0 | pos += 3; |
729 | 0 | break; |
730 | 0 | } |
731 | 0 | *pos++ = b32digits[((chunk.ptr[i+2] & 0x0F) << 1) | |
732 | 0 | (chunk.ptr[i+3] >> 7)]; |
733 | 0 | *pos++ = b32digits[(chunk.ptr[i+3] & 0x7F) >> 2]; |
734 | 0 | if (i+4 >= chunk.len) |
735 | 0 | { |
736 | 0 | *pos++ = b32digits[(chunk.ptr[i+3] & 0x03) << 3]; |
737 | 0 | *pos++ = '='; |
738 | 0 | break; |
739 | 0 | } |
740 | 0 | *pos++ = b32digits[((chunk.ptr[i+3] & 0x03) << 3) | |
741 | 0 | (chunk.ptr[i+4] >> 5)]; |
742 | 0 | *pos++ = b32digits[chunk.ptr[i+4] & 0x1F]; |
743 | 0 | } |
744 | 0 | *pos = '\0'; |
745 | 0 | return chunk_create(buf, len * 8 / 5); |
746 | 0 | } |
747 | | |
748 | | /** |
749 | | * Described in header. |
750 | | */ |
751 | | chunk_t chunk_to_dec(chunk_t chunk, char *buf) |
752 | 0 | { |
753 | 0 | int len, i, i_buf, i_bin = 0; |
754 | 0 | uint16_t remainder; |
755 | 0 | chunk_t bin; |
756 | | |
757 | | /* Determine the number of needed decimal digits: |
758 | | * 10^len > 2^(8*chunk.len) => |
759 | | * len > log(256) * chunk.len => |
760 | | * len > 2.4083 * chunk.len |
761 | | */ |
762 | 0 | len = (int)(2.4083 * (double)chunk.len) + 1; |
763 | |
|
764 | 0 | if (!buf) |
765 | 0 | { |
766 | 0 | buf = malloc(len + 1); |
767 | 0 | } |
768 | 0 | i_buf = len; |
769 | 0 | buf[i_buf] = '\0'; |
770 | 0 | bin = chunk_clone(chunk); |
771 | 0 | while (i_bin < bin.len) |
772 | 0 | { |
773 | 0 | remainder = 0; |
774 | 0 | for (i = i_bin; i < bin.len; i++) |
775 | 0 | { |
776 | 0 | remainder = bin.ptr[i] + (remainder << 8); |
777 | 0 | if (remainder < 10) |
778 | 0 | { |
779 | 0 | remainder = bin.ptr[i]; |
780 | 0 | bin.ptr[i] = 0; |
781 | 0 | if (i == i_bin) |
782 | 0 | { |
783 | 0 | i_bin++; |
784 | 0 | } |
785 | 0 | } |
786 | 0 | else |
787 | 0 | { |
788 | 0 | bin.ptr[i] = remainder / 10; |
789 | 0 | remainder %= 10; |
790 | 0 | } |
791 | 0 | } |
792 | 0 | if (i_buf > 0) |
793 | 0 | { |
794 | 0 | buf[--i_buf] = 0x30 + remainder; |
795 | 0 | } |
796 | 0 | } |
797 | 0 | chunk_free(&bin); |
798 | | |
799 | | /* align decimal number to the start of the string */ |
800 | 0 | if (i_buf > 0) |
801 | 0 | { |
802 | 0 | len -= i_buf; |
803 | |
|
804 | 0 | for (i = 0; i <= len; i++) |
805 | 0 | { |
806 | 0 | buf[i] = buf[i + i_buf]; |
807 | 0 | } |
808 | 0 | } |
809 | |
|
810 | 0 | return chunk_create(buf, len); |
811 | 0 | } |
812 | | |
813 | | /** |
814 | | * Described in header. |
815 | | */ |
816 | | int chunk_compare(chunk_t a, chunk_t b) |
817 | 0 | { |
818 | 0 | int compare_len = a.len - b.len; |
819 | 0 | int len = (compare_len < 0)? a.len : b.len; |
820 | |
|
821 | 0 | if (compare_len != 0 || len == 0) |
822 | 0 | { |
823 | 0 | return compare_len; |
824 | 0 | } |
825 | 0 | return memcmp(a.ptr, b.ptr, len); |
826 | 0 | }; |
827 | | |
828 | | |
829 | | /** |
830 | | * Described in header. |
831 | | */ |
832 | | bool chunk_increment(chunk_t chunk) |
833 | 0 | { |
834 | 0 | int i; |
835 | |
|
836 | 0 | for (i = chunk.len - 1; i >= 0; i--) |
837 | 0 | { |
838 | 0 | if (++chunk.ptr[i] != 0) |
839 | 0 | { |
840 | 0 | return FALSE; |
841 | 0 | } |
842 | 0 | } |
843 | 0 | return TRUE; |
844 | 0 | } |
845 | | |
846 | | /* |
847 | | * Described in header |
848 | | */ |
849 | | chunk_t chunk_copy_pad(chunk_t dst, chunk_t src, u_char chr) |
850 | 0 | { |
851 | 0 | if (dst.ptr) |
852 | 0 | { |
853 | 0 | if (dst.len > src.len) |
854 | 0 | { |
855 | 0 | memcpy(dst.ptr + dst.len - src.len, src.ptr, src.len); |
856 | 0 | memset(dst.ptr, chr, dst.len - src.len); |
857 | 0 | } |
858 | 0 | else |
859 | 0 | { |
860 | 0 | memcpy(dst.ptr, src.ptr + src.len - dst.len, dst.len); |
861 | 0 | } |
862 | 0 | } |
863 | 0 | return dst; |
864 | 0 | } |
865 | | |
866 | | /** |
867 | | * Remove non-printable characters from a chunk. |
868 | | */ |
869 | | bool chunk_printable(chunk_t chunk, chunk_t *sane, char replace) |
870 | 318 | { |
871 | 318 | bool printable = TRUE; |
872 | 318 | int i; |
873 | | |
874 | 318 | if (sane) |
875 | 318 | { |
876 | 318 | *sane = chunk_clone(chunk); |
877 | 318 | } |
878 | 1.18k | for (i = 0; i < chunk.len; i++) |
879 | 863 | { |
880 | 863 | if (!isprint(chunk.ptr[i])) |
881 | 548 | { |
882 | 548 | if (sane) |
883 | 548 | { |
884 | 548 | sane->ptr[i] = replace; |
885 | 548 | } |
886 | 548 | printable = FALSE; |
887 | 548 | } |
888 | 863 | } |
889 | 318 | return printable; |
890 | 318 | } |
891 | | |
892 | | /** |
893 | | * Helper functions for chunk_mac() |
894 | | */ |
895 | | static inline uint64_t sipget(u_char *in) |
896 | 9.53M | { |
897 | 9.53M | uint64_t v = 0; |
898 | 9.53M | int i; |
899 | | |
900 | 85.8M | for (i = 0; i < 64; i += 8, ++in) |
901 | 76.2M | { |
902 | 76.2M | v |= ((uint64_t)*in) << i; |
903 | 76.2M | } |
904 | 9.53M | return v; |
905 | 9.53M | } |
906 | | |
907 | | static inline uint64_t siprotate(uint64_t v, int shift) |
908 | 200M | { |
909 | 200M | return (v << shift) | (v >> (64 - shift)); |
910 | 200M | } |
911 | | |
912 | | static inline void sipround(uint64_t *v0, uint64_t *v1, uint64_t *v2, |
913 | | uint64_t *v3) |
914 | 33.3M | { |
915 | 33.3M | *v0 += *v1; |
916 | 33.3M | *v1 = siprotate(*v1, 13); |
917 | 33.3M | *v1 ^= *v0; |
918 | 33.3M | *v0 = siprotate(*v0, 32); |
919 | | |
920 | 33.3M | *v2 += *v3; |
921 | 33.3M | *v3 = siprotate(*v3, 16); |
922 | 33.3M | *v3 ^= *v2; |
923 | | |
924 | 33.3M | *v2 += *v1; |
925 | 33.3M | *v1 = siprotate(*v1, 17); |
926 | 33.3M | *v1 ^= *v2; |
927 | 33.3M | *v2 = siprotate(*v2, 32); |
928 | | |
929 | 33.3M | *v0 += *v3; |
930 | 33.3M | *v3 = siprotate(*v3, 21); |
931 | 33.3M | *v3 ^= *v0; |
932 | 33.3M | } |
933 | | |
934 | | static inline void sipcompress(uint64_t *v0, uint64_t *v1, uint64_t *v2, |
935 | | uint64_t *v3, uint64_t m) |
936 | 7.13M | { |
937 | 7.13M | *v3 ^= m; |
938 | 7.13M | sipround(v0, v1, v2, v3); |
939 | 7.13M | sipround(v0, v1, v2, v3); |
940 | 7.13M | *v0 ^= m; |
941 | 7.13M | } |
942 | | |
943 | | static inline uint64_t siplast(size_t len, u_char *pos) |
944 | 4.76M | { |
945 | 4.76M | uint64_t b; |
946 | 4.76M | int rem = len & 7; |
947 | | |
948 | 4.76M | b = ((uint64_t)len) << 56; |
949 | 4.76M | switch (rem) |
950 | 4.76M | { |
951 | 0 | case 7: |
952 | 0 | b |= ((uint64_t)pos[6]) << 48; |
953 | 3.93k | case 6: |
954 | 3.93k | b |= ((uint64_t)pos[5]) << 40; |
955 | 7.86k | case 5: |
956 | 7.86k | b |= ((uint64_t)pos[4]) << 32; |
957 | 4.56M | case 4: |
958 | 4.56M | b |= ((uint64_t)pos[3]) << 24; |
959 | 4.57M | case 3: |
960 | 4.57M | b |= ((uint64_t)pos[2]) << 16; |
961 | 4.57M | case 2: |
962 | 4.57M | b |= ((uint64_t)pos[1]) << 8; |
963 | 4.57M | case 1: |
964 | 4.57M | b |= ((uint64_t)pos[0]); |
965 | 4.57M | break; |
966 | 188k | case 0: |
967 | 188k | break; |
968 | 4.76M | } |
969 | 4.76M | return b; |
970 | 4.76M | } |
971 | | |
972 | | /** |
973 | | * Calculate SipHash-2-4 with an optional first block given as argument. |
974 | | */ |
975 | | static uint64_t chunk_mac_inc(chunk_t chunk, u_char *key, uint64_t m) |
976 | 4.76M | { |
977 | 4.76M | uint64_t v0, v1, v2, v3, k0, k1; |
978 | 4.76M | size_t len = chunk.len; |
979 | 4.76M | u_char *pos = chunk.ptr, *end; |
980 | | |
981 | 4.76M | end = chunk.ptr + len - (len % 8); |
982 | | |
983 | 4.76M | k0 = sipget(key); |
984 | 4.76M | k1 = sipget(key + 8); |
985 | | |
986 | 4.76M | v0 = k0 ^ 0x736f6d6570736575ULL; |
987 | 4.76M | v1 = k1 ^ 0x646f72616e646f6dULL; |
988 | 4.76M | v2 = k0 ^ 0x6c7967656e657261ULL; |
989 | 4.76M | v3 = k1 ^ 0x7465646279746573ULL; |
990 | | |
991 | 4.76M | if (m) |
992 | 2.36M | { |
993 | 2.36M | sipcompress(&v0, &v1, &v2, &v3, m); |
994 | 2.36M | } |
995 | | |
996 | | /* compression with c = 2 */ |
997 | 4.76M | for (; pos != end; pos += 8) |
998 | 8 | { |
999 | 8 | m = sipget(pos); |
1000 | 8 | sipcompress(&v0, &v1, &v2, &v3, m); |
1001 | 8 | } |
1002 | 4.76M | sipcompress(&v0, &v1, &v2, &v3, siplast(len, pos)); |
1003 | | |
1004 | | /* finalization with d = 4 */ |
1005 | 4.76M | v2 ^= 0xff; |
1006 | 4.76M | sipround(&v0, &v1, &v2, &v3); |
1007 | 4.76M | sipround(&v0, &v1, &v2, &v3); |
1008 | 4.76M | sipround(&v0, &v1, &v2, &v3); |
1009 | 4.76M | sipround(&v0, &v1, &v2, &v3); |
1010 | 4.76M | return v0 ^ v1 ^ v2 ^ v3; |
1011 | 4.76M | } |
1012 | | |
1013 | | /** |
1014 | | * Described in header. |
1015 | | */ |
1016 | | uint64_t chunk_mac(chunk_t chunk, u_char *key) |
1017 | 2.39M | { |
1018 | 2.39M | return chunk_mac_inc(chunk, key, 0); |
1019 | 2.39M | } |
1020 | | |
1021 | | /** |
1022 | | * Secret key allocated randomly with chunk_hash_seed(). |
1023 | | */ |
1024 | | static u_char hash_key[16] = {}; |
1025 | | |
1026 | | /** |
1027 | | * Static key used in case predictable hash values are required. |
1028 | | */ |
1029 | | static u_char static_key[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
1030 | | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; |
1031 | | |
1032 | | /** |
1033 | | * See header |
1034 | | */ |
1035 | | void chunk_hash_seed() |
1036 | 3.92k | { |
1037 | 3.92k | static bool seeded = FALSE; |
1038 | 3.92k | ssize_t len; |
1039 | 3.92k | size_t done = 0; |
1040 | 3.92k | int fd; |
1041 | | |
1042 | 3.92k | if (seeded) |
1043 | 3.92k | { |
1044 | | /* just once to have the same seed during the whole process lifetimes */ |
1045 | 3.92k | return; |
1046 | 3.92k | } |
1047 | | |
1048 | 2 | fd = open("/dev/urandom", O_RDONLY); |
1049 | 2 | if (fd >= 0) |
1050 | 2 | { |
1051 | 4 | while (done < sizeof(hash_key)) |
1052 | 2 | { |
1053 | 2 | len = read(fd, hash_key + done, sizeof(hash_key) - done); |
1054 | 2 | if (len < 0) |
1055 | 0 | { |
1056 | 0 | break; |
1057 | 0 | } |
1058 | 2 | done += len; |
1059 | 2 | } |
1060 | 2 | close(fd); |
1061 | 2 | } |
1062 | | /* on error we use random() to generate the key (better than nothing) */ |
1063 | 2 | if (done < sizeof(hash_key)) |
1064 | 0 | { |
1065 | 0 | srandom(time(NULL) + getpid()); |
1066 | 0 | for (; done < sizeof(hash_key); done++) |
1067 | 0 | { |
1068 | 0 | hash_key[done] = (u_char)random(); |
1069 | 0 | } |
1070 | 0 | } |
1071 | 2 | seeded = TRUE; |
1072 | 2 | } |
1073 | | |
1074 | | /** |
1075 | | * Described in header. |
1076 | | */ |
1077 | | uint32_t chunk_hash_inc(chunk_t chunk, uint32_t hash) |
1078 | 2.36M | { |
1079 | | /* we could use a mac of the previous hash, but this is faster */ |
1080 | 2.36M | return chunk_mac_inc(chunk, hash_key, ((uint64_t)hash) << 32 | hash); |
1081 | 2.36M | } |
1082 | | |
1083 | | /** |
1084 | | * Described in header. |
1085 | | */ |
1086 | | uint32_t chunk_hash(chunk_t chunk) |
1087 | 2.39M | { |
1088 | 2.39M | return chunk_mac(chunk, hash_key); |
1089 | 2.39M | } |
1090 | | |
1091 | | /* |
1092 | | * Described in header. |
1093 | | */ |
1094 | | uint32_t chunk_hash_ptr(chunk_t *chunk) |
1095 | 0 | { |
1096 | 0 | return chunk_hash(*chunk); |
1097 | 0 | } |
1098 | | |
1099 | | /** |
1100 | | * Described in header. |
1101 | | */ |
1102 | | uint32_t chunk_hash_static_inc(chunk_t chunk, uint32_t hash) |
1103 | 0 | { /* we could use a mac of the previous hash, but this is faster */ |
1104 | 0 | return chunk_mac_inc(chunk, static_key, ((uint64_t)hash) << 32 | hash); |
1105 | 0 | } |
1106 | | |
1107 | | /** |
1108 | | * Described in header. |
1109 | | */ |
1110 | | uint32_t chunk_hash_static(chunk_t chunk) |
1111 | 0 | { |
1112 | 0 | return chunk_mac(chunk, static_key); |
1113 | 0 | } |
1114 | | |
1115 | | /** |
1116 | | * Described in header. |
1117 | | */ |
1118 | | uint16_t chunk_internet_checksum_inc(chunk_t data, uint16_t checksum) |
1119 | 0 | { |
1120 | 0 | uint32_t sum = ntohs((uint16_t)~checksum); |
1121 | |
|
1122 | 0 | while (data.len > 1) |
1123 | 0 | { |
1124 | 0 | sum += untoh16(data.ptr); |
1125 | 0 | data = chunk_skip(data, 2); |
1126 | 0 | } |
1127 | 0 | if (data.len) |
1128 | 0 | { |
1129 | 0 | sum += (uint16_t)*data.ptr << 8; |
1130 | 0 | } |
1131 | 0 | while (sum >> 16) |
1132 | 0 | { |
1133 | 0 | sum = (sum & 0xffff) + (sum >> 16); |
1134 | 0 | } |
1135 | 0 | return htons(~sum); |
1136 | 0 | } |
1137 | | |
1138 | | /** |
1139 | | * Described in header. |
1140 | | */ |
1141 | | uint16_t chunk_internet_checksum(chunk_t data) |
1142 | 0 | { |
1143 | 0 | return chunk_internet_checksum_inc(data, 0xffff); |
1144 | 0 | } |
1145 | | |
1146 | | /** |
1147 | | * Described in header. |
1148 | | */ |
1149 | | int chunk_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, |
1150 | | const void *const *args) |
1151 | 0 | { |
1152 | 0 | chunk_t *chunk = *((chunk_t**)(args[0])); |
1153 | 0 | bool first = TRUE; |
1154 | 0 | chunk_t copy = *chunk; |
1155 | 0 | int written = 0; |
1156 | |
|
1157 | 0 | if (!spec->hash && !spec->plus) |
1158 | 0 | { |
1159 | 0 | u_int chunk_len = chunk->len; |
1160 | 0 | const void *new_args[] = {&chunk->ptr, &chunk_len}; |
1161 | 0 | return mem_printf_hook(data, spec, new_args); |
1162 | 0 | } |
1163 | | |
1164 | 0 | while (copy.len > 0) |
1165 | 0 | { |
1166 | 0 | if (first) |
1167 | 0 | { |
1168 | 0 | first = FALSE; |
1169 | 0 | } |
1170 | 0 | else if (!spec->plus) |
1171 | 0 | { |
1172 | 0 | written += print_in_hook(data, ":"); |
1173 | 0 | } |
1174 | 0 | written += print_in_hook(data, "%02x", *copy.ptr++); |
1175 | 0 | copy.len--; |
1176 | 0 | } |
1177 | 0 | return written; |
1178 | 0 | } |