Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / crypto / external / bsd / netpgp / dist / src / lib / reader.c
blob18dc7f9765d6f515bb885a14548aba106cb59084
1 /*-
2 * Copyright (c) 2009 The NetBSD Foundation, Inc.
3 * All rights reserved.
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Alistair Crooks (agc@NetBSD.org)
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
30 * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
31 * All rights reserved.
32 * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
33 * their moral rights under the UK Copyright Design and Patents Act 1988 to
34 * be recorded as the authors of this copyright work.
36 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
37 * use this file except in compliance with the License.
39 * You may obtain a copy of the License at
40 * http://www.apache.org/licenses/LICENSE-2.0
42 * Unless required by applicable law or agreed to in writing, software
43 * distributed under the License is distributed on an "AS IS" BASIS,
44 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
46 * See the License for the specific language governing permissions and
47 * limitations under the License.
49 #include "config.h"
51 #ifdef HAVE_SYS_CDEFS_H
52 #include <sys/cdefs.h>
53 #endif
55 #if defined(__NetBSD__)
56 __COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
57 __RCSID("$NetBSD: reader.c,v 1.26 2009/12/05 07:08:19 agc Exp $");
58 #endif
60 #include <sys/types.h>
61 #include <sys/stat.h>
63 #ifdef HAVE_SYS_MMAN_H
64 #include <sys/mman.h>
65 #endif
67 #ifdef HAVE_SYS_PARAM_H
68 #include <sys/param.h>
69 #endif
71 #ifdef HAVE_FCNTL_H
72 #include <fcntl.h>
73 #endif
75 #ifdef HAVE_UNISTD_H
76 #include <unistd.h>
77 #endif
79 #ifdef HAVE_DIRECT_H
80 #include <direct.h>
81 #endif
83 #ifdef HAVE_INTTYPES_H
84 #include <inttypes.h>
85 #endif
87 #ifdef HAVE_OPENSSL_IDEA_H
88 #include <openssl/cast.h>
89 #endif
91 #ifdef HAVE_OPENSSL_IDEA_H
92 #include <openssl/idea.h>
93 #endif
95 #ifdef HAVE_OPENSSL_AES_H
96 #include <openssl/aes.h>
97 #endif
99 #ifdef HAVE_OPENSSL_DES_H
100 #include <openssl/des.h>
101 #endif
103 #include <string.h>
104 #include <stdlib.h>
105 #include <stdio.h>
107 #ifdef HAVE_TERMIOS_H
108 #include <termios.h>
109 #endif
111 #ifdef HAVE_ERRNO_H
112 #include <errno.h>
113 #endif
115 #ifdef HAVE_UNISTD_H
116 #include <unistd.h>
117 #endif
119 #ifdef HAVE_LIMITS_H
120 #include <limits.h>
121 #endif
123 #include "errors.h"
124 #include "crypto.h"
125 #include "create.h"
126 #include "signature.h"
127 #include "packet.h"
128 #include "packet-parse.h"
129 #include "packet-show.h"
130 #include "packet.h"
131 #include "keyring.h"
132 #include "readerwriter.h"
133 #include "netpgpdefs.h"
134 #include "netpgpdigest.h"
137 /* get a pass phrase from the user */
139 __ops_getpassphrase(void *in, char *phrase, size_t size)
141 char *p;
143 if (in == NULL) {
144 while ((p = getpass("netpgp passphrase: ")) == NULL) {
146 (void) snprintf(phrase, size, "%s", p);
147 } else {
148 if (fgets(phrase, (int)size, in) == NULL) {
149 return 0;
151 phrase[strlen(phrase) - 1] = 0x0;
153 return 1;
157 * \ingroup Internal_Readers_Generic
158 * \brief Starts reader stack
159 * \param stream Parse settings
160 * \param reader Reader to use
161 * \param destroyer Destroyer to use
162 * \param vp Reader-specific arg
164 void
165 __ops_reader_set(__ops_stream_t *stream,
166 __ops_reader_func_t *reader,
167 __ops_reader_destroyer_t *destroyer,
168 void *vp)
170 stream->readinfo.reader = reader;
171 stream->readinfo.destroyer = destroyer;
172 stream->readinfo.arg = vp;
176 * \ingroup Internal_Readers_Generic
177 * \brief Adds to reader stack
178 * \param stream Parse settings
179 * \param reader Reader to use
180 * \param destroyer Reader's destroyer
181 * \param vp Reader-specific arg
183 void
184 __ops_reader_push(__ops_stream_t *stream,
185 __ops_reader_func_t *reader,
186 __ops_reader_destroyer_t *destroyer,
187 void *vp)
189 __ops_reader_t *readinfo;
191 if ((readinfo = calloc(1, sizeof(*readinfo))) == NULL) {
192 (void) fprintf(stderr, "__ops_reader_push: bad alloc\n");
193 } else {
194 *readinfo = stream->readinfo;
195 (void) memset(&stream->readinfo, 0x0, sizeof(stream->readinfo));
196 stream->readinfo.next = readinfo;
197 stream->readinfo.parent = stream;
199 /* should copy accumulate flags from other reader? RW */
200 stream->readinfo.accumulate = readinfo->accumulate;
202 __ops_reader_set(stream, reader, destroyer, vp);
207 * \ingroup Internal_Readers_Generic
208 * \brief Removes from reader stack
209 * \param stream Parse settings
211 void
212 __ops_reader_pop(__ops_stream_t *stream)
214 __ops_reader_t *next = stream->readinfo.next;
216 stream->readinfo = *next;
217 free(next);
221 * \ingroup Internal_Readers_Generic
222 * \brief Gets arg from reader
223 * \param readinfo Reader info
224 * \return Pointer to reader info's arg
226 void *
227 __ops_reader_get_arg(__ops_reader_t *readinfo)
229 return readinfo->arg;
232 /**************************************************************************/
234 #define CRC24_POLY 0x1864cfbL
236 enum {
237 NONE = 0,
238 BEGIN_PGP_MESSAGE,
239 BEGIN_PGP_PUBLIC_KEY_BLOCK,
240 BEGIN_PGP_PRIVATE_KEY_BLOCK,
241 BEGIN_PGP_MULTI,
242 BEGIN_PGP_SIGNATURE,
244 END_PGP_MESSAGE,
245 END_PGP_PUBLIC_KEY_BLOCK,
246 END_PGP_PRIVATE_KEY_BLOCK,
247 END_PGP_MULTI,
248 END_PGP_SIGNATURE,
250 BEGIN_PGP_SIGNED_MESSAGE
254 * \struct dearmour_t
256 typedef struct {
257 enum {
258 OUTSIDE_BLOCK = 0,
259 BASE64,
260 AT_TRAILER_NAME
261 } state;
262 int lastseen;
263 __ops_stream_t *parse_info;
264 unsigned seen_nl:1;
265 unsigned prev_nl:1;
266 unsigned allow_headers_without_gap:1;
267 /* !< allow headers in armoured data that are
268 * not separated from the data by a blank line
269 * */
270 unsigned allow_no_gap:1;
271 /* !< allow no blank line at the start of
272 * armoured data */
273 unsigned allow_trailing_whitespace:1;
274 /* !< allow armoured stuff to have trailing
275 * whitespace where we wouldn't strictly expect
276 * it */
277 /* it is an error to get a cleartext message without a sig */
278 unsigned expect_sig:1;
279 unsigned got_sig:1;
280 /* base64 stuff */
281 unsigned buffered;
282 unsigned char buffer[3];
283 unsigned eof64;
284 unsigned long checksum;
285 unsigned long read_checksum;
286 /* unarmoured text blocks */
287 unsigned char unarmoured[NETPGP_BUFSIZ];
288 size_t unarmoredc;
289 /* pushed back data (stored backwards) */
290 unsigned char *pushback;
291 unsigned pushbackc;
292 /* armoured block headers */
293 __ops_headers_t headers;
294 } dearmour_t;
296 static void
297 push_back(dearmour_t *dearmour, const unsigned char *buf,
298 unsigned length)
300 unsigned n;
302 if (dearmour->pushback) {
303 (void) fprintf(stderr, "push_back: already pushed back\n");
304 } else if ((dearmour->pushback = calloc(1, length)) == NULL) {
305 (void) fprintf(stderr, "push_back: bad alloc\n");
306 } else {
307 for (n = 0; n < length; ++n) {
308 dearmour->pushback[n] = buf[(length - n) - 1];
310 dearmour->pushbackc = length;
314 /* this struct holds a textual header line */
315 typedef struct headerline_t {
316 const char *s; /* the header line */
317 size_t len; /* its length */
318 int type; /* the defined type */
319 } headerline_t;
321 static headerline_t headerlines[] = {
322 { "BEGIN PGP MESSAGE", 17, BEGIN_PGP_MESSAGE },
323 { "BEGIN PGP PUBLIC KEY BLOCK", 26, BEGIN_PGP_PUBLIC_KEY_BLOCK },
324 { "BEGIN PGP PRIVATE KEY BLOCK",27, BEGIN_PGP_PRIVATE_KEY_BLOCK },
325 { "BEGIN PGP MESSAGE, PART ", 25, BEGIN_PGP_MULTI },
326 { "BEGIN PGP SIGNATURE", 19, BEGIN_PGP_SIGNATURE },
328 { "END PGP MESSAGE", 15, END_PGP_MESSAGE },
329 { "END PGP PUBLIC KEY BLOCK", 24, END_PGP_PUBLIC_KEY_BLOCK },
330 { "END PGP PRIVATE KEY BLOCK", 25, END_PGP_PRIVATE_KEY_BLOCK },
331 { "END PGP MESSAGE, PART ", 22, END_PGP_MULTI },
332 { "END PGP SIGNATURE", 17, END_PGP_SIGNATURE },
334 { "BEGIN PGP SIGNED MESSAGE", 24, BEGIN_PGP_SIGNED_MESSAGE },
336 { NULL, 0, -1 }
339 /* search through the table of header lines */
340 static int
341 findheaderline(char *headerline)
343 headerline_t *hp;
345 for (hp = headerlines ; hp->s ; hp++) {
346 if (strncmp(headerline, hp->s, hp->len) == 0) {
347 break;
350 return hp->type;
353 static int
354 set_lastseen_headerline(dearmour_t *dearmour, char *hdr, __ops_error_t **errors)
356 int lastseen;
357 int prev;
359 prev = dearmour->lastseen;
360 if ((lastseen = findheaderline(hdr)) == -1) {
361 OPS_ERROR_1(errors, OPS_E_R_BAD_FORMAT,
362 "Unrecognised Header Line %s", hdr);
363 return 0;
365 dearmour->lastseen = lastseen;
366 if (__ops_get_debug_level(__FILE__)) {
367 printf("set header: hdr=%s, dearmour->lastseen=%d, prev=%d\n",
368 hdr, dearmour->lastseen, prev);
370 switch (dearmour->lastseen) {
371 case NONE:
372 OPS_ERROR_1(errors, OPS_E_R_BAD_FORMAT,
373 "Unrecognised last seen Header Line %s", hdr);
374 break;
376 case END_PGP_MESSAGE:
377 if (prev != BEGIN_PGP_MESSAGE) {
378 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
379 "Got END PGP MESSAGE, but not after BEGIN");
381 break;
383 case END_PGP_PUBLIC_KEY_BLOCK:
384 if (prev != BEGIN_PGP_PUBLIC_KEY_BLOCK) {
385 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
386 "Got END PGP PUBLIC KEY BLOCK, but not after BEGIN");
388 break;
390 case END_PGP_PRIVATE_KEY_BLOCK:
391 if (prev != BEGIN_PGP_PRIVATE_KEY_BLOCK) {
392 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
393 "Got END PGP PRIVATE KEY BLOCK, but not after BEGIN");
395 break;
397 case BEGIN_PGP_MULTI:
398 case END_PGP_MULTI:
399 OPS_ERROR(errors, OPS_E_R_UNSUPPORTED,
400 "Multi-part messages are not yet supported");
401 break;
403 case END_PGP_SIGNATURE:
404 if (prev != BEGIN_PGP_SIGNATURE) {
405 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
406 "Got END PGP SIGNATURE, but not after BEGIN");
408 break;
410 case BEGIN_PGP_MESSAGE:
411 case BEGIN_PGP_PUBLIC_KEY_BLOCK:
412 case BEGIN_PGP_PRIVATE_KEY_BLOCK:
413 case BEGIN_PGP_SIGNATURE:
414 case BEGIN_PGP_SIGNED_MESSAGE:
415 break;
417 return 1;
420 static int
421 read_char(dearmour_t *dearmour,
422 __ops_error_t **errors,
423 __ops_reader_t *readinfo,
424 __ops_cbdata_t *cbinfo,
425 unsigned skip)
427 unsigned char c;
429 do {
430 if (dearmour->pushbackc) {
431 c = dearmour->pushback[--dearmour->pushbackc];
432 if (dearmour->pushbackc == 0) {
433 free(dearmour->pushback);
434 dearmour->pushback = NULL;
436 } else if (__ops_stacked_read(&c, 1, errors, readinfo,
437 cbinfo) != 1) {
438 return -1;
440 } while (skip && c == '\r');
441 dearmour->prev_nl = dearmour->seen_nl;
442 dearmour->seen_nl = c == '\n';
443 return c;
446 static int
447 eat_whitespace(int first,
448 dearmour_t *dearmour,
449 __ops_error_t **errors,
450 __ops_reader_t *readinfo,
451 __ops_cbdata_t *cbinfo,
452 unsigned skip)
454 int c = first;
456 while (c == ' ' || c == '\t') {
457 c = read_char(dearmour, errors, readinfo, cbinfo, skip);
459 return c;
462 static int
463 read_and_eat_whitespace(dearmour_t *dearmour,
464 __ops_error_t **errors,
465 __ops_reader_t *readinfo,
466 __ops_cbdata_t *cbinfo,
467 unsigned skip)
469 int c;
471 do {
472 c = read_char(dearmour, errors, readinfo, cbinfo, skip);
473 } while (c == ' ' || c == '\t');
474 return c;
477 static void
478 flush(dearmour_t *dearmour, __ops_cbdata_t *cbinfo)
480 __ops_packet_t content;
482 if (dearmour->unarmoredc > 0) {
483 content.u.unarmoured_text.data = dearmour->unarmoured;
484 content.u.unarmoured_text.length = dearmour->unarmoredc;
485 CALLBACK(OPS_PTAG_CT_UNARMOURED_TEXT, cbinfo, &content);
486 dearmour->unarmoredc = 0;
490 static int
491 unarmoured_read_char(dearmour_t *dearmour,
492 __ops_error_t **errors,
493 __ops_reader_t *readinfo,
494 __ops_cbdata_t *cbinfo,
495 unsigned skip)
497 int c;
499 do {
500 c = read_char(dearmour, errors, readinfo, cbinfo, 0);
501 if (c < 0) {
502 return c;
504 dearmour->unarmoured[dearmour->unarmoredc++] = c;
505 if (dearmour->unarmoredc == sizeof(dearmour->unarmoured)) {
506 flush(dearmour, cbinfo);
508 } while (skip && c == '\r');
509 return c;
513 * \param headers
514 * \param key
516 * \return header value if found, otherwise NULL
518 static const char *
519 __ops_find_header(__ops_headers_t *headers, const char *key)
521 unsigned n;
523 for (n = 0; n < headers->headerc; ++n) {
524 if (strcmp(headers->headers[n].key, key) == 0) {
525 return headers->headers[n].value;
528 return NULL;
532 * \param dest
533 * \param src
535 static void
536 __ops_dup_headers(__ops_headers_t *dest, const __ops_headers_t *src)
538 unsigned n;
540 if ((dest->headers = calloc(src->headerc, sizeof(*dest->headers))) == NULL) {
541 (void) fprintf(stderr, "__ops_dup_headers: bad alloc\n");
542 } else {
543 dest->headerc = src->headerc;
544 for (n = 0; n < src->headerc; ++n) {
545 dest->headers[n].key = strdup(src->headers[n].key);
546 dest->headers[n].value = strdup(src->headers[n].value);
552 * Note that this skips CRs so implementations always see just straight LFs
553 * as line terminators
555 static int
556 process_dash_escaped(dearmour_t *dearmour,
557 __ops_error_t **errors,
558 __ops_reader_t *readinfo,
559 __ops_cbdata_t *cbinfo)
561 __ops_packet_t content;
562 __ops_packet_t content2;
563 __ops_cleartext_body_t *body = &content.u.cleartext_body;
564 __ops_cleartext_trailer_t *trailer = &content2.u.cleartext_trailer;
565 const char *hashstr;
566 __ops_hash_t *hash;
567 int total;
569 if ((hash = calloc(1, sizeof(*hash))) == NULL) {
570 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
571 "process_dash_escaped: bad alloc");
572 return -1;
574 hashstr = __ops_find_header(&dearmour->headers, "Hash");
575 if (hashstr) {
576 __ops_hash_alg_t alg;
578 alg = __ops_str_to_hash_alg(hashstr);
579 if (!__ops_is_hash_alg_supported(&alg)) {
580 free(hash);
581 OPS_ERROR_1(errors, OPS_E_R_BAD_FORMAT,
582 "Unsupported hash algorithm '%s'", hashstr);
583 return -1;
585 if (alg == OPS_HASH_UNKNOWN) {
586 free(hash);
587 OPS_ERROR_1(errors, OPS_E_R_BAD_FORMAT,
588 "Unknown hash algorithm '%s'", hashstr);
589 return -1;
591 __ops_hash_any(hash, alg);
592 } else {
593 __ops_hash_md5(hash);
596 if (!hash->init(hash)) {
597 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
598 "can't initialise hash");
599 return -1;
602 body->length = 0;
603 total = 0;
604 for (;;) {
605 int c;
606 unsigned count;
608 c = read_char(dearmour, errors, readinfo, cbinfo, 1);
609 if (c < 0) {
610 return -1;
612 if (dearmour->prev_nl && c == '-') {
613 if ((c = read_char(dearmour, errors, readinfo, cbinfo,
614 0)) < 0) {
615 return -1;
617 if (c != ' ') {
618 /* then this had better be a trailer! */
619 if (c != '-') {
620 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
621 "Bad dash-escaping");
623 for (count = 2; count < 5; ++count) {
624 if ((c = read_char(dearmour, errors,
625 readinfo, cbinfo, 0)) < 0) {
626 return -1;
628 if (c != '-') {
629 OPS_ERROR(errors,
630 OPS_E_R_BAD_FORMAT,
631 "Bad dash-escaping (2)");
634 dearmour->state = AT_TRAILER_NAME;
635 break;
637 /* otherwise we read the next character */
638 if ((c = read_char(dearmour, errors, readinfo, cbinfo,
639 0)) < 0) {
640 return -1;
643 if (c == '\n' && body->length) {
644 if (memchr(body->data + 1, '\n', body->length - 1)
645 != NULL) {
646 (void) fprintf(stderr,
647 "process_dash_escaped: newline found\n");
648 return -1;
650 if (body->data[0] == '\n') {
651 hash->add(hash, (const unsigned char *)"\r", 1);
653 hash->add(hash, body->data, body->length);
654 if (__ops_get_debug_level(__FILE__)) {
655 fprintf(stderr, "Got body:\n%s\n", body->data);
657 CALLBACK(OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY, cbinfo,
658 &content);
659 body->length = 0;
661 body->data[body->length++] = c;
662 total += 1;
663 if (body->length == sizeof(body->data)) {
664 if (__ops_get_debug_level(__FILE__)) {
665 (void) fprintf(stderr, "Got body (2):\n%s\n",
666 body->data);
668 CALLBACK(OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY, cbinfo,
669 &content);
670 body->length = 0;
673 if (body->data[0] != '\n') {
674 (void) fprintf(stderr,
675 "process_dash_escaped: no newline in body data\n");
676 return -1;
678 if (body->length != 1) {
679 (void) fprintf(stderr,
680 "process_dash_escaped: bad body length\n");
681 return -1;
684 /* don't send that one character, because it's part of the trailer */
685 trailer->hash = hash;
686 CALLBACK(OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER, cbinfo, &content2);
687 return total;
690 static int
691 add_header(dearmour_t *dearmour, const char *key, const char *value)
693 int n;
696 * Check that the header is valid
698 if (strcmp(key, "Version") == 0 ||
699 strcmp(key, "Comment") == 0 ||
700 strcmp(key, "MessageID") == 0 ||
701 strcmp(key, "Hash") == 0 ||
702 strcmp(key, "Charset") == 0) {
703 n = dearmour->headers.headerc;
704 dearmour->headers.headers = realloc(dearmour->headers.headers,
705 (n + 1) * sizeof(*dearmour->headers.headers));
706 if (dearmour->headers.headers == NULL) {
707 (void) fprintf(stderr, "add_header: bad alloc\n");
708 return 0;
710 dearmour->headers.headers[n].key = strdup(key);
711 dearmour->headers.headers[n].value = strdup(value);
712 dearmour->headers.headerc = n + 1;
713 return 1;
715 return 0;
718 /* \todo what does a return value of 0 indicate? 1 is good, -1 is bad */
719 static int
720 parse_headers(dearmour_t *dearmour, __ops_error_t **errors,
721 __ops_reader_t * readinfo, __ops_cbdata_t * cbinfo)
723 unsigned nbuf;
724 unsigned size;
725 unsigned first = 1;
726 char *buf;
727 int ret = 1;
729 nbuf = 0;
730 size = 80;
731 if ((buf = calloc(1, size)) == NULL) {
732 (void) fprintf(stderr, "parse_headers: bad calloc\n");
733 return -1;
735 for (;;) {
736 int c;
738 if ((c = read_char(dearmour, errors, readinfo, cbinfo, 1)) < 0) {
739 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT, "Unexpected EOF");
740 ret = -1;
741 break;
743 if (c == '\n') {
744 char *s;
746 if (nbuf == 0) {
747 break;
750 if (nbuf >= size) {
751 (void) fprintf(stderr,
752 "parse_headers: bad size\n");
753 return -1;
755 buf[nbuf] = '\0';
757 if ((s = strchr(buf, ':')) == NULL) {
758 if (!first && !dearmour->allow_headers_without_gap) {
760 * then we have seriously malformed
761 * armour
763 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT, "No colon in armour header");
764 ret = -1;
765 break;
766 } else {
767 if (first &&
768 !(dearmour->allow_headers_without_gap || dearmour->allow_no_gap)) {
769 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT, "No colon in armour header (2)");
771 * then we have a nasty
772 * armoured block with no
773 * headers, not even a blank
774 * line.
776 buf[nbuf] = '\n';
777 push_back(dearmour, (unsigned char *) buf, nbuf + 1);
778 ret = -1;
779 break;
782 } else {
783 *s = '\0';
784 if (s[1] != ' ') {
785 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT, "No space in armour header");
786 ret = -1;
787 goto end;
789 if (!add_header(dearmour, buf, s + 2)) {
790 OPS_ERROR_1(errors, OPS_E_R_BAD_FORMAT, "Invalid header %s", buf);
791 ret = -1;
792 goto end;
794 nbuf = 0;
796 first = 0;
797 } else {
798 if (size <= nbuf + 1) {
799 size += size + 80;
800 buf = realloc(buf, size);
801 if (buf == NULL) {
802 (void) fprintf(stderr, "bad alloc\n");
803 ret = -1;
804 goto end;
807 buf[nbuf++] = c;
811 end:
812 free(buf);
814 return ret;
817 static int
818 read4(dearmour_t *dearmour, __ops_error_t **errors,
819 __ops_reader_t *readinfo, __ops_cbdata_t *cbinfo,
820 int *pc, unsigned *pn, unsigned long *pl)
822 int n, c;
823 unsigned long l = 0;
825 for (n = 0; n < 4; ++n) {
826 c = read_char(dearmour, errors, readinfo, cbinfo, 1);
827 if (c < 0) {
828 dearmour->eof64 = 1;
829 return -1;
831 if (c == '-' || c == '=') {
832 break;
834 l <<= 6;
835 if (c >= 'A' && c <= 'Z') {
836 l += (unsigned long)(c - 'A');
837 } else if (c >= 'a' && c <= 'z') {
838 l += (unsigned long)(c - 'a') + 26;
839 } else if (c >= '0' && c <= '9') {
840 l += (unsigned long)(c - '0') + 52;
841 } else if (c == '+') {
842 l += 62;
843 } else if (c == '/') {
844 l += 63;
845 } else {
846 --n;
847 l >>= 6;
851 *pc = c;
852 *pn = n;
853 *pl = l;
855 return 4;
858 unsigned
859 __ops_crc24(unsigned checksum, unsigned char c)
861 unsigned i;
863 checksum ^= c << 16;
864 for (i = 0; i < 8; i++) {
865 checksum <<= 1;
866 if (checksum & 0x1000000)
867 checksum ^= CRC24_POLY;
869 return checksum & 0xffffffL;
872 static int
873 decode64(dearmour_t *dearmour, __ops_error_t **errors,
874 __ops_reader_t *readinfo, __ops_cbdata_t *cbinfo)
876 unsigned n;
877 int n2;
878 unsigned long l;
879 int c;
880 int ret;
882 if (dearmour->buffered) {
883 (void) fprintf(stderr, "decode64: bad dearmour->buffered\n");
884 return 0;
887 ret = read4(dearmour, errors, readinfo, cbinfo, &c, &n, &l);
888 if (ret < 0) {
889 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT, "Badly formed base64");
890 return 0;
892 if (n == 3) {
893 if (c != '=') {
894 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
895 "Badly terminated base64 (2)");
896 return 0;
898 dearmour->buffered = 2;
899 dearmour->eof64 = 1;
900 l >>= 2;
901 } else if (n == 2) {
902 if (c != '=') {
903 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
904 "Badly terminated base64 (3)");
905 return 0;
907 dearmour->buffered = 1;
908 dearmour->eof64 = 1;
909 l >>= 4;
910 c = read_char(dearmour, errors, readinfo, cbinfo, 0);
911 if (c != '=') {
912 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
913 "Badly terminated base64");
914 return 0;
916 } else if (n == 0) {
917 if (!dearmour->prev_nl || c != '=') {
918 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
919 "Badly terminated base64 (4)");
920 return 0;
922 dearmour->buffered = 0;
923 } else {
924 if (n != 4) {
925 (void) fprintf(stderr,
926 "decode64: bad n (!= 4)\n");
927 return 0;
929 dearmour->buffered = 3;
930 if (c == '-' || c == '=') {
931 (void) fprintf(stderr, "decode64: bad c\n");
932 return 0;
936 if (dearmour->buffered < 3 && dearmour->buffered > 0) {
937 /* then we saw padding */
938 if (c != '=') {
939 (void) fprintf(stderr, "decode64: bad c (=)\n");
940 return 0;
942 c = read_and_eat_whitespace(dearmour, errors, readinfo, cbinfo,
944 if (c != '\n') {
945 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
946 "No newline at base64 end");
947 return 0;
949 c = read_char(dearmour, errors, readinfo, cbinfo, 0);
950 if (c != '=') {
951 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
952 "No checksum at base64 end");
953 return 0;
956 if (c == '=') {
957 /* now we are at the checksum */
958 ret = read4(dearmour, errors, readinfo, cbinfo, &c, &n,
959 &dearmour->read_checksum);
960 if (ret < 0 || n != 4) {
961 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
962 "Error in checksum");
963 return 0;
965 c = read_char(dearmour, errors, readinfo, cbinfo, 1);
966 if (dearmour->allow_trailing_whitespace)
967 c = eat_whitespace(c, dearmour, errors, readinfo, cbinfo,
969 if (c != '\n') {
970 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
971 "Badly terminated checksum");
972 return 0;
974 c = read_char(dearmour, errors, readinfo, cbinfo, 0);
975 if (c != '-') {
976 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
977 "Bad base64 trailer (2)");
978 return 0;
981 if (c == '-') {
982 for (n = 0; n < 4; ++n)
983 if (read_char(dearmour, errors, readinfo, cbinfo,
984 0) != '-') {
985 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
986 "Bad base64 trailer");
987 return 0;
989 dearmour->eof64 = 1;
990 } else {
991 if (!dearmour->buffered) {
992 (void) fprintf(stderr, "decode64: not buffered\n");
993 return 0;
997 for (n = 0; n < dearmour->buffered; ++n) {
998 dearmour->buffer[n] = (unsigned char)l;
999 l >>= 8;
1002 for (n2 = dearmour->buffered - 1; n2 >= 0; --n2)
1003 dearmour->checksum = __ops_crc24((unsigned)dearmour->checksum,
1004 dearmour->buffer[n2]);
1006 if (dearmour->eof64 && dearmour->read_checksum != dearmour->checksum) {
1007 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT, "Checksum mismatch");
1008 return 0;
1010 return 1;
1013 static void
1014 base64(dearmour_t *dearmour)
1016 dearmour->state = BASE64;
1017 dearmour->checksum = CRC24_INIT;
1018 dearmour->eof64 = 0;
1019 dearmour->buffered = 0;
1022 /* This reader is rather strange in that it can generate callbacks for */
1023 /* content - this is because plaintext is not encapsulated in PGP */
1024 /* packets... it also calls back for the text between the blocks. */
1026 static int
1027 armoured_data_reader(void *dest_, size_t length, __ops_error_t **errors,
1028 __ops_reader_t *readinfo,
1029 __ops_cbdata_t *cbinfo)
1031 __ops_packet_t content;
1032 unsigned char *dest = dest_;
1033 dearmour_t *dearmour;
1034 unsigned first;
1035 int saved;
1036 int ret;
1038 dearmour = __ops_reader_get_arg(readinfo);
1039 saved = length;
1040 if (dearmour->eof64 && !dearmour->buffered) {
1041 if (dearmour->state != OUTSIDE_BLOCK &&
1042 dearmour->state != AT_TRAILER_NAME) {
1043 (void) fprintf(stderr,
1044 "armoured_data_reader: bad dearmour state\n");
1045 return 0;
1049 while (length > 0) {
1050 unsigned count;
1051 unsigned n;
1052 char buf[1024];
1053 int c;
1055 flush(dearmour, cbinfo);
1056 switch (dearmour->state) {
1057 case OUTSIDE_BLOCK:
1059 * This code returns EOF rather than EARLY_EOF
1060 * because if we don't see a header line at all, then
1061 * it is just an EOF (and not a BLOCK_END)
1063 while (!dearmour->seen_nl) {
1064 if ((c = unarmoured_read_char(dearmour, errors,
1065 readinfo, cbinfo, 1)) < 0) {
1066 return 0;
1071 * flush at this point so we definitely have room for
1072 * the header, and so we can easily erase it from the
1073 * buffer
1075 flush(dearmour, cbinfo);
1076 /* Find and consume the 5 leading '-' */
1077 for (count = 0; count < 5; ++count) {
1078 if ((c = unarmoured_read_char(dearmour, errors,
1079 readinfo, cbinfo, 0)) < 0) {
1080 return 0;
1082 if (c != '-') {
1083 goto reloop;
1087 /* Now find the block type */
1088 for (n = 0; n < sizeof(buf) - 1;) {
1089 if ((c = unarmoured_read_char(dearmour, errors,
1090 readinfo, cbinfo, 0)) < 0) {
1091 return 0;
1093 if (c == '-') {
1094 goto got_minus;
1096 buf[n++] = c;
1098 /* then I guess this wasn't a proper header */
1099 break;
1101 got_minus:
1102 buf[n] = '\0';
1104 /* Consume trailing '-' */
1105 for (count = 1; count < 5; ++count) {
1106 if ((c = unarmoured_read_char(dearmour, errors,
1107 readinfo, cbinfo, 0)) < 0) {
1108 return 0;
1110 if (c != '-') {
1111 /* wasn't a header after all */
1112 goto reloop;
1116 /* Consume final NL */
1117 if ((c = unarmoured_read_char(dearmour, errors, readinfo,
1118 cbinfo, 1)) < 0) {
1119 return 0;
1121 if (dearmour->allow_trailing_whitespace) {
1122 if ((c = eat_whitespace(c, dearmour, errors,
1123 readinfo, cbinfo, 1)) < 0) {
1124 return 0;
1127 if (c != '\n') {
1128 /* wasn't a header line after all */
1129 break;
1133 * Now we've seen the header, scrub it from the
1134 * buffer
1136 dearmour->unarmoredc = 0;
1139 * But now we've seen a header line, then errors are
1140 * EARLY_EOF
1142 if ((ret = parse_headers(dearmour, errors, readinfo,
1143 cbinfo)) <= 0) {
1144 return -1;
1147 if (!set_lastseen_headerline(dearmour, buf, errors)) {
1148 return -1;
1151 if (strcmp(buf, "BEGIN PGP SIGNED MESSAGE") == 0) {
1152 __ops_dup_headers(
1153 &content.u.cleartext_head.headers,
1154 &dearmour->headers);
1155 CALLBACK(OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER,
1156 cbinfo,
1157 &content);
1158 ret = process_dash_escaped(dearmour, errors,
1159 readinfo, cbinfo);
1160 if (ret <= 0) {
1161 return ret;
1163 } else {
1164 /* XXX Flexelint - Assigning address of auto variable 'buf' to outer
1165 scope symbol 'content'*/
1166 content.u.armour_header.type = buf;
1167 content.u.armour_header.headers =
1168 dearmour->headers;
1169 (void) memset(&dearmour->headers, 0x0,
1170 sizeof(dearmour->headers));
1171 CALLBACK(OPS_PTAG_CT_ARMOUR_HEADER, cbinfo,
1172 &content);
1173 base64(dearmour);
1175 break;
1177 case BASE64:
1178 first = 1;
1179 while (length > 0) {
1180 if (!dearmour->buffered) {
1181 if (!dearmour->eof64) {
1182 ret = decode64(dearmour,
1183 errors, readinfo, cbinfo);
1184 if (ret <= 0) {
1185 return ret;
1188 if (!dearmour->buffered) {
1189 if (!dearmour->eof64) {
1190 (void) fprintf(stderr,
1191 "armoured_data_reader: bad dearmour eof64\n");
1192 return 0;
1194 if (first) {
1195 dearmour->state =
1196 AT_TRAILER_NAME;
1197 goto reloop;
1199 return -1;
1202 if (!dearmour->buffered) {
1203 (void) fprintf(stderr,
1204 "armoured_data_reader: bad dearmour buffered\n");
1205 return 0;
1207 *dest = dearmour->buffer[--dearmour->buffered];
1208 ++dest;
1209 --length;
1210 first = 0;
1212 if (dearmour->eof64 && !dearmour->buffered) {
1213 dearmour->state = AT_TRAILER_NAME;
1215 break;
1217 case AT_TRAILER_NAME:
1218 for (n = 0; n < sizeof(buf) - 1;) {
1219 if ((c = read_char(dearmour, errors, readinfo,
1220 cbinfo, 0)) < 0) {
1221 return -1;
1223 if (c == '-') {
1224 goto got_minus2;
1226 buf[n++] = c;
1228 /* then I guess this wasn't a proper trailer */
1229 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
1230 "Bad ASCII armour trailer");
1231 break;
1233 got_minus2:
1234 buf[n] = '\0';
1236 if (!set_lastseen_headerline(dearmour, buf, errors)) {
1237 return -1;
1240 /* Consume trailing '-' */
1241 for (count = 1; count < 5; ++count) {
1242 if ((c = read_char(dearmour, errors, readinfo,
1243 cbinfo, 0)) < 0) {
1244 return -1;
1246 if (c != '-') {
1247 /* wasn't a trailer after all */
1248 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
1249 "Bad ASCII armour trailer (2)");
1253 /* Consume final NL */
1254 if ((c = read_char(dearmour, errors, readinfo, cbinfo,
1255 1)) < 0) {
1256 return -1;
1258 if (dearmour->allow_trailing_whitespace) {
1259 if ((c = eat_whitespace(c, dearmour, errors,
1260 readinfo, cbinfo, 1)) < 0) {
1261 return 0;
1264 if (c != '\n') {
1265 /* wasn't a trailer line after all */
1266 OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,
1267 "Bad ASCII armour trailer (3)");
1270 if (strncmp(buf, "BEGIN ", 6) == 0) {
1271 if (!set_lastseen_headerline(dearmour, buf,
1272 errors)) {
1273 return -1;
1275 if ((ret = parse_headers(dearmour, errors,
1276 readinfo, cbinfo)) <= 0) {
1277 return ret;
1279 content.u.armour_header.type = buf;
1280 content.u.armour_header.headers =
1281 dearmour->headers;
1282 (void) memset(&dearmour->headers, 0x0,
1283 sizeof(dearmour->headers));
1284 CALLBACK(OPS_PTAG_CT_ARMOUR_HEADER, cbinfo,
1285 &content);
1286 base64(dearmour);
1287 } else {
1288 content.u.armour_trailer.type = buf;
1289 CALLBACK(OPS_PTAG_CT_ARMOUR_TRAILER, cbinfo,
1290 &content);
1291 dearmour->state = OUTSIDE_BLOCK;
1293 break;
1295 reloop:
1296 continue;
1299 return saved;
1302 static void
1303 armoured_data_destroyer(__ops_reader_t *readinfo)
1305 free(__ops_reader_get_arg(readinfo));
1309 * \ingroup Core_Readers_Armour
1310 * \brief Pushes dearmouring reader onto stack
1311 * \param parse_info Usual structure containing information about to how to do the parse
1312 * \sa __ops_reader_pop_dearmour()
1314 void
1315 __ops_reader_push_dearmour(__ops_stream_t *parse_info)
1317 * This function originally had these params to cater for packets which
1318 * didn't strictly match the RFC. The initial 0.5 release is only going to
1319 * support strict checking. If it becomes desirable to support loose checking
1320 * of armoured packets and these params are reinstated, parse_headers() must
1321 * be fixed so that these flags work correctly.
1323 * // Allow headers in armoured data that are not separated from the data by a
1324 * blank line unsigned without_gap,
1326 * // Allow no blank line at the start of armoured data unsigned no_gap,
1328 * //Allow armoured data to have trailing whitespace where we strictly would not
1329 * expect it unsigned trailing_whitespace
1332 dearmour_t *dearmour;
1334 if ((dearmour = calloc(1, sizeof(*dearmour))) == NULL) {
1335 (void) fprintf(stderr, "__ops_reader_push_dearmour: bad alloc\n");
1336 } else {
1337 dearmour->seen_nl = 1;
1339 dearmour->allow_headers_without_gap=without_gap;
1340 dearmour->allow_no_gap=no_gap;
1341 dearmour->allow_trailing_whitespace=trailing_whitespace;
1343 dearmour->expect_sig = 0;
1344 dearmour->got_sig = 0;
1346 __ops_reader_push(parse_info, armoured_data_reader,
1347 armoured_data_destroyer, dearmour);
1352 * \ingroup Core_Readers_Armour
1353 * \brief Pops dearmour reader from stock
1354 * \param stream
1355 * \sa __ops_reader_push_dearmour()
1357 void
1358 __ops_reader_pop_dearmour(__ops_stream_t *stream)
1360 dearmour_t *dearmour;
1362 dearmour = __ops_reader_get_arg(__ops_readinfo(stream));
1363 free(dearmour);
1364 __ops_reader_pop(stream);
1367 /**************************************************************************/
1369 /* this is actually used for *decrypting* */
1370 typedef struct {
1371 unsigned char decrypted[1024 * 15];
1372 size_t c;
1373 size_t off;
1374 __ops_crypt_t *decrypt;
1375 __ops_region_t *region;
1376 unsigned prevplain:1;
1377 } encrypted_t;
1379 static int
1380 encrypted_data_reader(void *dest,
1381 size_t length,
1382 __ops_error_t **errors,
1383 __ops_reader_t *readinfo,
1384 __ops_cbdata_t *cbinfo)
1386 encrypted_t *encrypted;
1387 char *cdest;
1388 int saved;
1390 encrypted = __ops_reader_get_arg(readinfo);
1391 saved = length;
1393 * V3 MPIs have the count plain and the cipher is reset after each
1394 * count
1396 if (encrypted->prevplain && !readinfo->parent->reading_mpi_len) {
1397 if (!readinfo->parent->reading_v3_secret) {
1398 (void) fprintf(stderr,
1399 "encrypted_data_reader: bad v3 secret\n");
1400 return -1;
1402 encrypted->decrypt->decrypt_resync(encrypted->decrypt);
1403 encrypted->prevplain = 0;
1404 } else if (readinfo->parent->reading_v3_secret &&
1405 readinfo->parent->reading_mpi_len) {
1406 encrypted->prevplain = 1;
1408 while (length > 0) {
1409 if (encrypted->c) {
1410 unsigned n;
1413 * if we are reading v3 we should never read
1414 * more than we're asked for */
1415 if (length < encrypted->c &&
1416 (readinfo->parent->reading_v3_secret ||
1417 readinfo->parent->exact_read)) {
1418 (void) fprintf(stderr,
1419 "encrypted_data_reader: bad v3 read\n");
1420 return 0;
1422 n = MIN(length, encrypted->c);
1423 (void) memcpy(dest,
1424 encrypted->decrypted + encrypted->off, n);
1425 encrypted->c -= n;
1426 encrypted->off += n;
1427 length -= n;
1428 cdest = dest;
1429 cdest += n;
1430 dest = cdest;
1431 } else {
1432 unsigned n = encrypted->region->length;
1433 unsigned char buffer[1024];
1435 if (!n) {
1436 return -1;
1438 if (!encrypted->region->indeterminate) {
1439 n -= encrypted->region->readc;
1440 if (n == 0) {
1441 return saved - length;
1443 if (n > sizeof(buffer)) {
1444 n = sizeof(buffer);
1446 } else {
1447 n = sizeof(buffer);
1451 * we can only read as much as we're asked for
1452 * in v3 keys because they're partially
1453 * unencrypted! */
1454 if ((readinfo->parent->reading_v3_secret ||
1455 readinfo->parent->exact_read) && n > length) {
1456 n = length;
1459 if (!__ops_stacked_limited_read(buffer, n,
1460 encrypted->region, errors, readinfo, cbinfo)) {
1461 return -1;
1463 if (!readinfo->parent->reading_v3_secret ||
1464 !readinfo->parent->reading_mpi_len) {
1465 encrypted->c =
1466 __ops_decrypt_se_ip(encrypted->decrypt,
1467 encrypted->decrypted, buffer, n);
1469 if (__ops_get_debug_level(__FILE__)) {
1470 int i;
1472 (void) fprintf(stderr,
1473 "READING:\nencrypted: ");
1474 for (i = 0; i < 16; i++) {
1475 (void) fprintf(stderr,
1476 "%2x ", buffer[i]);
1478 (void) fprintf(stderr, "\ndecrypted: ");
1479 for (i = 0; i < 16; i++) {
1480 (void) fprintf(stderr, "%2x ",
1481 encrypted->decrypted[i]);
1483 (void) fprintf(stderr, "\n");
1485 } else {
1486 (void) memcpy(
1487 &encrypted->decrypted[encrypted->off], buffer, n);
1488 encrypted->c = n;
1491 if (encrypted->c == 0) {
1492 (void) fprintf(stderr,
1493 "encrypted_data_reader: 0 decrypted count\n");
1494 return 0;
1497 encrypted->off = 0;
1501 return saved;
1504 static void
1505 encrypted_data_destroyer(__ops_reader_t *readinfo)
1507 free(__ops_reader_get_arg(readinfo));
1511 * \ingroup Core_Readers_SE
1512 * \brief Pushes decryption reader onto stack
1513 * \sa __ops_reader_pop_decrypt()
1515 void
1516 __ops_reader_push_decrypt(__ops_stream_t *stream, __ops_crypt_t *decrypt,
1517 __ops_region_t *region)
1519 encrypted_t *encrypted;
1521 if ((encrypted = calloc(1, sizeof(*encrypted))) == NULL) {
1522 (void) fprintf(stderr, "__ops_reader_push_decrypted: bad alloc\n");
1523 } else {
1524 encrypted->decrypt = decrypt;
1525 encrypted->region = region;
1526 __ops_decrypt_init(encrypted->decrypt);
1527 __ops_reader_push(stream, encrypted_data_reader,
1528 encrypted_data_destroyer, encrypted);
1533 * \ingroup Core_Readers_Encrypted
1534 * \brief Pops decryption reader from stack
1535 * \sa __ops_reader_push_decrypt()
1537 void
1538 __ops_reader_pop_decrypt(__ops_stream_t *stream)
1540 encrypted_t *encrypted;
1542 encrypted = __ops_reader_get_arg(__ops_readinfo(stream));
1543 encrypted->decrypt->decrypt_finish(encrypted->decrypt);
1544 free(encrypted);
1545 __ops_reader_pop(stream);
1548 /**************************************************************************/
1550 typedef struct {
1551 /* boolean: 0 once we've done the preamble/MDC checks */
1552 /* and are reading from the plaintext */
1553 int passed_checks;
1554 unsigned char *plaintext;
1555 size_t plaintext_available;
1556 size_t plaintext_offset;
1557 __ops_region_t *region;
1558 __ops_crypt_t *decrypt;
1559 } decrypt_se_ip_t;
1562 Gets entire SE_IP data packet.
1563 Verifies leading preamble
1564 Verifies trailing MDC packet
1565 Then passes up plaintext as requested
1567 static int
1568 se_ip_data_reader(void *dest_,
1569 size_t len,
1570 __ops_error_t **errors,
1571 __ops_reader_t *readinfo,
1572 __ops_cbdata_t *cbinfo)
1574 decrypt_se_ip_t *se_ip;
1575 __ops_region_t decrypted_region;
1576 unsigned int n = 0;
1578 se_ip = __ops_reader_get_arg(readinfo);
1579 if (!se_ip->passed_checks) {
1580 unsigned char *buf = NULL;
1581 unsigned char hashed[OPS_SHA1_HASH_SIZE];
1582 unsigned char *preamble;
1583 unsigned char *plaintext;
1584 unsigned char *mdc;
1585 unsigned char *mdc_hash;
1586 __ops_hash_t hash;
1587 size_t b;
1588 size_t sz_preamble;
1589 size_t sz_mdc_hash;
1590 size_t sz_mdc;
1591 size_t sz_plaintext;
1593 __ops_hash_any(&hash, OPS_HASH_SHA1);
1594 if (!hash.init(&hash)) {
1595 (void) fprintf(stderr,
1596 "se_ip_data_reader: can't init hash\n");
1597 return -1;
1600 __ops_init_subregion(&decrypted_region, NULL);
1601 decrypted_region.length =
1602 se_ip->region->length - se_ip->region->readc;
1603 if ((buf = calloc(1, decrypted_region.length)) == NULL) {
1604 (void) fprintf(stderr, "se_ip_data_reader: bad alloc\n");
1605 return -1;
1608 /* read entire SE IP packet */
1609 if (!__ops_stacked_limited_read(buf, decrypted_region.length,
1610 &decrypted_region, errors, readinfo, cbinfo)) {
1611 free(buf);
1612 return -1;
1614 if (__ops_get_debug_level(__FILE__)) {
1615 unsigned i;
1617 fprintf(stderr, "\n\nentire SE IP packet (len=%d):\n",
1618 decrypted_region.length);
1619 for (i = 0; i < decrypted_region.length; i++) {
1620 fprintf(stderr, "0x%02x ", buf[i]);
1621 if (!((i + 1) % 8))
1622 fprintf(stderr, "\n");
1624 fprintf(stderr, "\n\n");
1626 /* verify leading preamble */
1628 if (__ops_get_debug_level(__FILE__)) {
1629 unsigned i;
1631 fprintf(stderr, "\npreamble: ");
1632 for (i = 0; i < se_ip->decrypt->blocksize + 2; i++)
1633 fprintf(stderr, " 0x%02x", buf[i]);
1634 fprintf(stderr, "\n");
1636 b = se_ip->decrypt->blocksize;
1637 if (buf[b - 2] != buf[b] || buf[b - 1] != buf[b + 1]) {
1638 fprintf(stderr,
1639 "Bad symmetric decrypt (%02x%02x vs %02x%02x)\n",
1640 buf[b - 2], buf[b - 1], buf[b], buf[b + 1]);
1641 OPS_ERROR(errors, OPS_E_PROTO_BAD_SYMMETRIC_DECRYPT,
1642 "Bad symmetric decrypt when parsing SE IP packet");
1643 free(buf);
1644 return -1;
1646 /* Verify trailing MDC hash */
1648 sz_preamble = se_ip->decrypt->blocksize + 2;
1649 sz_mdc_hash = OPS_SHA1_HASH_SIZE;
1650 sz_mdc = 1 + 1 + sz_mdc_hash;
1651 sz_plaintext = (decrypted_region.length - sz_preamble) - sz_mdc;
1653 preamble = buf;
1654 plaintext = buf + sz_preamble;
1655 mdc = plaintext + sz_plaintext;
1656 mdc_hash = mdc + 2;
1658 if (__ops_get_debug_level(__FILE__)) {
1659 unsigned i;
1661 fprintf(stderr, "\nplaintext (len=%" PRIsize "u): ",
1662 sz_plaintext);
1663 for (i = 0; i < sz_plaintext; i++)
1664 fprintf(stderr, " 0x%02x", plaintext[i]);
1665 fprintf(stderr, "\n");
1667 fprintf(stderr, "\nmdc (len=%" PRIsize "u): ", sz_mdc);
1668 for (i = 0; i < sz_mdc; i++)
1669 fprintf(stderr, " 0x%02x", mdc[i]);
1670 fprintf(stderr, "\n");
1672 __ops_calc_mdc_hash(preamble, sz_preamble, plaintext,
1673 sz_plaintext, hashed);
1675 if (memcmp(mdc_hash, hashed, OPS_SHA1_HASH_SIZE) != 0) {
1676 OPS_ERROR(errors, OPS_E_V_BAD_HASH,
1677 "Bad hash in MDC packet");
1678 free(buf);
1679 return 0;
1681 /* all done with the checks */
1682 /* now can start reading from the plaintext */
1683 if (se_ip->plaintext) {
1684 (void) fprintf(stderr,
1685 "se_ip_data_reader: bad plaintext\n");
1686 return 0;
1688 if ((se_ip->plaintext = calloc(1, sz_plaintext)) == NULL) {
1689 (void) fprintf(stderr,
1690 "se_ip_data_reader: bad alloc\n");
1691 return 0;
1693 memcpy(se_ip->plaintext, plaintext, sz_plaintext);
1694 se_ip->plaintext_available = sz_plaintext;
1696 se_ip->passed_checks = 1;
1698 free(buf);
1700 n = len;
1701 if (n > se_ip->plaintext_available) {
1702 n = se_ip->plaintext_available;
1705 memcpy(dest_, se_ip->plaintext + se_ip->plaintext_offset, n);
1706 se_ip->plaintext_available -= n;
1707 se_ip->plaintext_offset += n;
1708 /* len -= n; - not used at all, for info only */
1710 return n;
1713 static void
1714 se_ip_data_destroyer(__ops_reader_t *readinfo)
1716 decrypt_se_ip_t *se_ip;
1718 se_ip = __ops_reader_get_arg(readinfo);
1719 free(se_ip->plaintext);
1720 free(se_ip);
1724 \ingroup Internal_Readers_SEIP
1726 void
1727 __ops_reader_push_se_ip_data(__ops_stream_t *stream, __ops_crypt_t *decrypt,
1728 __ops_region_t * region)
1730 decrypt_se_ip_t *se_ip;
1732 if ((se_ip = calloc(1, sizeof(*se_ip))) == NULL) {
1733 (void) fprintf(stderr, "__ops_reader_push_se_ip_data: bad alloc\n");
1734 } else {
1735 se_ip->region = region;
1736 se_ip->decrypt = decrypt;
1737 __ops_reader_push(stream, se_ip_data_reader, se_ip_data_destroyer,
1738 se_ip);
1743 \ingroup Internal_Readers_SEIP
1745 void
1746 __ops_reader_pop_se_ip_data(__ops_stream_t *stream)
1749 * decrypt_se_ip_t
1750 * *se_ip=__ops_reader_get_arg(__ops_readinfo(stream));
1752 /* free(se_ip); */
1753 __ops_reader_pop(stream);
1756 /**************************************************************************/
1758 /** Arguments for reader_fd
1760 typedef struct mmap_reader_t {
1761 void *mem; /* memory mapped file */
1762 uint64_t size; /* size of file */
1763 uint64_t offset; /* current offset in file */
1764 int fd; /* file descriptor */
1765 } mmap_reader_t;
1769 * \ingroup Core_Readers
1771 * __ops_reader_fd() attempts to read up to "plength" bytes from the file
1772 * descriptor in "parse_info" into the buffer starting at "dest" using the
1773 * rules contained in "flags"
1775 * \param dest Pointer to previously allocated buffer
1776 * \param plength Number of bytes to try to read
1777 * \param flags Rules about reading to use
1778 * \param readinfo Reader info
1779 * \param cbinfo Callback info
1781 * \return n Number of bytes read
1783 * OPS_R_EARLY_EOF and OPS_R_ERROR push errors on the stack
1785 static int
1786 fd_reader(void *dest, size_t length, __ops_error_t **errors,
1787 __ops_reader_t *readinfo, __ops_cbdata_t *cbinfo)
1789 mmap_reader_t *reader = __ops_reader_get_arg(readinfo);
1790 int n = read(reader->fd, dest, length);
1792 __OPS_USED(cbinfo);
1793 if (n == 0)
1794 return 0;
1795 if (n < 0) {
1796 OPS_SYSTEM_ERROR_1(errors, OPS_E_R_READ_FAILED, "read",
1797 "file descriptor %d", reader->fd);
1798 return -1;
1800 return n;
1803 static void
1804 reader_fd_destroyer(__ops_reader_t *readinfo)
1806 free(__ops_reader_get_arg(readinfo));
1810 \ingroup Core_Readers_First
1811 \brief Starts stack with file reader
1814 void
1815 __ops_reader_set_fd(__ops_stream_t *stream, int fd)
1817 mmap_reader_t *reader;
1819 if ((reader = calloc(1, sizeof(*reader))) == NULL) {
1820 (void) fprintf(stderr, "__ops_reader_set_fd: bad alloc\n");
1821 } else {
1822 reader->fd = fd;
1823 __ops_reader_set(stream, fd_reader, reader_fd_destroyer, reader);
1827 /**************************************************************************/
1829 typedef struct {
1830 const unsigned char *buffer;
1831 size_t length;
1832 size_t offset;
1833 } reader_mem_t;
1835 static int
1836 mem_reader(void *dest, size_t length, __ops_error_t **errors,
1837 __ops_reader_t *readinfo, __ops_cbdata_t *cbinfo)
1839 reader_mem_t *reader = __ops_reader_get_arg(readinfo);
1840 unsigned n;
1842 __OPS_USED(cbinfo);
1843 __OPS_USED(errors);
1845 if (reader->offset + length > reader->length)
1846 n = reader->length - reader->offset;
1847 else
1848 n = length;
1850 if (n == 0)
1851 return 0;
1853 memcpy(dest, reader->buffer + reader->offset, n);
1854 reader->offset += n;
1856 return n;
1859 static void
1860 mem_destroyer(__ops_reader_t *readinfo)
1862 free(__ops_reader_get_arg(readinfo));
1866 \ingroup Core_Readers_First
1867 \brief Starts stack with memory reader
1870 void
1871 __ops_reader_set_memory(__ops_stream_t *stream, const void *buffer,
1872 size_t length)
1874 reader_mem_t *mem;
1876 if ((mem = calloc(1, sizeof(*mem))) == NULL) {
1877 (void) fprintf(stderr, "__ops_reader_set_memory: bad alloc\n");
1878 } else {
1879 mem->buffer = buffer;
1880 mem->length = length;
1881 mem->offset = 0;
1882 __ops_reader_set(stream, mem_reader, mem_destroyer, mem);
1886 /**************************************************************************/
1889 \ingroup Core_Writers
1890 \brief Create and initialise output and mem; Set for writing to mem
1891 \param output Address where new output pointer will be set
1892 \param mem Address when new mem pointer will be set
1893 \param bufsz Initial buffer size (will automatically be increased when necessary)
1894 \note It is the caller's responsiblity to free output and mem.
1895 \sa __ops_teardown_memory_write()
1897 void
1898 __ops_setup_memory_write(__ops_output_t **output, __ops_memory_t **mem, size_t bufsz)
1901 * initialise needed structures for writing to memory
1904 *output = __ops_output_new();
1905 *mem = __ops_memory_new();
1907 __ops_memory_init(*mem, bufsz);
1909 __ops_writer_set_memory(*output, *mem);
1913 \ingroup Core_Writers
1914 \brief Closes writer and frees output and mem
1915 \param output
1916 \param mem
1917 \sa __ops_setup_memory_write()
1919 void
1920 __ops_teardown_memory_write(__ops_output_t *output, __ops_memory_t *mem)
1922 __ops_writer_close(output);/* new */
1923 __ops_output_delete(output);
1924 __ops_memory_free(mem);
1928 \ingroup Core_Readers
1929 \brief Create parse_info and sets to read from memory
1930 \param stream Address where new parse_info will be set
1931 \param mem Memory to read from
1932 \param arg Reader-specific arg
1933 \param callback Callback to use with reader
1934 \param accumulate Set if we need to accumulate as we read. (Usually 0 unless doing signature verification)
1935 \note It is the caller's responsiblity to free parse_info
1936 \sa __ops_teardown_memory_read()
1938 void
1939 __ops_setup_memory_read(__ops_io_t *io,
1940 __ops_stream_t **stream,
1941 __ops_memory_t *mem,
1942 void *vp,
1943 __ops_cb_ret_t callback(const __ops_packet_t *,
1944 __ops_cbdata_t *),
1945 unsigned accumulate)
1947 *stream = __ops_new(sizeof(**stream));
1948 (*stream)->io = (*stream)->cbinfo.io = io;
1949 __ops_set_callback(*stream, callback, vp);
1950 __ops_reader_set_memory(*stream,
1951 __ops_mem_data(mem),
1952 __ops_mem_len(mem));
1953 if (accumulate) {
1954 (*stream)->readinfo.accumulate = 1;
1959 \ingroup Core_Readers
1960 \brief Frees stream and mem
1961 \param stream
1962 \param mem
1963 \sa __ops_setup_memory_read()
1965 void
1966 __ops_teardown_memory_read(__ops_stream_t *stream, __ops_memory_t *mem)
1968 __ops_stream_delete(stream);
1969 __ops_memory_free(mem);
1973 \ingroup Core_Writers
1974 \brief Create and initialise output and mem; Set for writing to file
1975 \param output Address where new output pointer will be set
1976 \param filename File to write to
1977 \param allow_overwrite Allows file to be overwritten, if set.
1978 \return Newly-opened file descriptor
1979 \note It is the caller's responsiblity to free output and to close fd.
1980 \sa __ops_teardown_file_write()
1982 int
1983 __ops_setup_file_write(__ops_output_t **output, const char *filename,
1984 unsigned allow_overwrite)
1986 int fd = 0;
1987 int flags = 0;
1990 * initialise needed structures for writing to file
1992 if (filename == NULL) {
1993 /* write to stdout */
1994 fd = STDOUT_FILENO;
1995 } else {
1996 flags = O_WRONLY | O_CREAT;
1997 if (allow_overwrite)
1998 flags |= O_TRUNC;
1999 else
2000 flags |= O_EXCL;
2001 #ifdef O_BINARY
2002 flags |= O_BINARY;
2003 #endif
2004 fd = open(filename, flags, 0600);
2005 if (fd < 0) {
2006 perror(filename);
2007 return fd;
2010 *output = __ops_output_new();
2011 __ops_writer_set_fd(*output, fd);
2012 return fd;
2016 \ingroup Core_Writers
2017 \brief Closes writer, frees info, closes fd
2018 \param output
2019 \param fd
2021 void
2022 __ops_teardown_file_write(__ops_output_t *output, int fd)
2024 __ops_writer_close(output);
2025 close(fd);
2026 __ops_output_delete(output);
2030 \ingroup Core_Writers
2031 \brief As __ops_setup_file_write, but appends to file
2033 int
2034 __ops_setup_file_append(__ops_output_t **output, const char *filename)
2036 int fd;
2038 * initialise needed structures for writing to file
2041 #ifdef O_BINARY
2042 fd = open(filename, O_WRONLY | O_APPEND | O_BINARY, 0600);
2043 #else
2044 fd = open(filename, O_WRONLY | O_APPEND, 0600);
2045 #endif
2046 if (fd < 0) {
2047 perror(filename);
2048 return fd;
2050 *output = __ops_output_new();
2052 __ops_writer_set_fd(*output, fd);
2054 return fd;
2058 \ingroup Core_Writers
2059 \brief As __ops_teardown_file_write()
2061 void
2062 __ops_teardown_file_append(__ops_output_t *output, int fd)
2064 __ops_teardown_file_write(output, fd);
2068 \ingroup Core_Readers
2069 \brief Creates parse_info, opens file, and sets to read from file
2070 \param stream Address where new parse_info will be set
2071 \param filename Name of file to read
2072 \param vp Reader-specific arg
2073 \param callback Callback to use when reading
2074 \param accumulate Set if we need to accumulate as we read. (Usually 0 unless doing signature verification)
2075 \note It is the caller's responsiblity to free parse_info and to close fd
2076 \sa __ops_teardown_file_read()
2078 int
2079 __ops_setup_file_read(__ops_io_t *io,
2080 __ops_stream_t **stream,
2081 const char *filename,
2082 void *vp,
2083 __ops_cb_ret_t callback(const __ops_packet_t *,
2084 __ops_cbdata_t *),
2085 unsigned accumulate)
2087 int fd;
2089 #ifdef O_BINARY
2090 fd = open(filename, O_RDONLY | O_BINARY);
2091 #else
2092 fd = open(filename, O_RDONLY);
2093 #endif
2094 if (fd < 0) {
2095 (void) fprintf(io->errs, "can't open \"%s\"\n", filename);
2096 return fd;
2098 *stream = __ops_new(sizeof(**stream));
2099 (*stream)->io = (*stream)->cbinfo.io = io;
2100 __ops_set_callback(*stream, callback, vp);
2101 #ifdef USE_MMAP_FOR_FILES
2102 __ops_reader_set_mmap(*stream, fd);
2103 #else
2104 __ops_reader_set_fd(*stream, fd);
2105 #endif
2106 if (accumulate) {
2107 (*stream)->readinfo.accumulate = 1;
2109 return fd;
2113 \ingroup Core_Readers
2114 \brief Frees stream and closes fd
2115 \param stream
2116 \param fd
2117 \sa __ops_setup_file_read()
2119 void
2120 __ops_teardown_file_read(__ops_stream_t *stream, int fd)
2122 close(fd);
2123 __ops_stream_delete(stream);
2126 __ops_cb_ret_t
2127 litdata_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
2129 const __ops_contents_t *content = &pkt->u;
2131 if (__ops_get_debug_level(__FILE__)) {
2132 printf("litdata_cb: ");
2133 __ops_print_packet(pkt);
2135 /* Read data from packet into static buffer */
2136 switch (pkt->tag) {
2137 case OPS_PTAG_CT_LITDATA_BODY:
2138 /* if writer enabled, use it */
2139 if (cbinfo->output) {
2140 if (__ops_get_debug_level(__FILE__)) {
2141 printf("litdata_cb: length is %u\n",
2142 content->litdata_body.length);
2144 __ops_write(cbinfo->output,
2145 content->litdata_body.data,
2146 content->litdata_body.length);
2148 break;
2150 case OPS_PTAG_CT_LITDATA_HEADER:
2151 /* ignore */
2152 break;
2154 default:
2155 break;
2158 return OPS_RELEASE_MEMORY;
2161 __ops_cb_ret_t
2162 pk_sesskey_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
2164 const __ops_contents_t *content = &pkt->u;
2165 unsigned from;
2166 __ops_io_t *io;
2168 io = cbinfo->io;
2169 if (__ops_get_debug_level(__FILE__)) {
2170 __ops_print_packet(pkt);
2172 /* Read data from packet into static buffer */
2173 switch (pkt->tag) {
2174 case OPS_PTAG_CT_PK_SESSION_KEY:
2175 if (__ops_get_debug_level(__FILE__)) {
2176 printf("OPS_PTAG_CT_PK_SESSION_KEY\n");
2178 if (!cbinfo->cryptinfo.keyring) {
2179 (void) fprintf(io->errs,
2180 "pk_sesskey_cb: bad keyring\n");
2181 return (__ops_cb_ret_t)0;
2183 from = 0;
2184 cbinfo->cryptinfo.keydata =
2185 __ops_getkeybyid(io, cbinfo->cryptinfo.keyring,
2186 content->pk_sesskey.key_id, &from);
2187 if (!cbinfo->cryptinfo.keydata) {
2188 break;
2190 break;
2192 default:
2193 break;
2196 return OPS_RELEASE_MEMORY;
2200 \ingroup Core_Callbacks
2202 \brief Callback to get secret key, decrypting if necessary.
2204 @verbatim
2205 This callback does the following:
2206 * finds the session key in the keyring
2207 * gets a passphrase if required
2208 * decrypts the secret key, if necessary
2209 * sets the seckey in the content struct
2210 @endverbatim
2213 __ops_cb_ret_t
2214 get_seckey_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
2216 const __ops_contents_t *content = &pkt->u;
2217 const __ops_seckey_t *secret;
2218 const __ops_key_t *keypair;
2219 unsigned from;
2220 __ops_io_t *io;
2222 io = cbinfo->io;
2223 if (__ops_get_debug_level(__FILE__)) {
2224 __ops_print_packet(pkt);
2226 switch (pkt->tag) {
2227 case OPS_GET_SECKEY:
2228 from = 0;
2229 cbinfo->cryptinfo.keydata =
2230 __ops_getkeybyid(io, cbinfo->cryptinfo.keyring,
2231 content->get_seckey.pk_sesskey->key_id,
2232 &from);
2233 if (!cbinfo->cryptinfo.keydata ||
2234 !__ops_is_key_secret(cbinfo->cryptinfo.keydata)) {
2235 return (__ops_cb_ret_t)0;
2238 keypair = cbinfo->cryptinfo.keydata;
2239 do {
2240 /* print out the user id */
2241 __ops_print_keydata(io, keypair, "pub", &keypair->key.pubkey);
2242 /* now decrypt key */
2243 secret = __ops_decrypt_seckey(keypair);
2244 if (secret == NULL) {
2245 (void) fprintf(io->errs, "Bad passphrase\n");
2247 } while (secret == NULL);
2248 *content->get_seckey.seckey = secret;
2249 break;
2251 default:
2252 break;
2255 return OPS_RELEASE_MEMORY;
2259 \ingroup HighLevel_Callbacks
2260 \brief Callback to use when you need to prompt user for passphrase
2261 \param contents
2262 \param cbinfo
2264 __ops_cb_ret_t
2265 get_passphrase_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
2267 const __ops_contents_t *content = &pkt->u;
2268 __ops_io_t *io;
2270 io = cbinfo->io;
2271 if (__ops_get_debug_level(__FILE__)) {
2272 __ops_print_packet(pkt);
2274 if (cbinfo->cryptinfo.keydata == NULL) {
2275 (void) fprintf(io->errs, "get_passphrase_cb: NULL keydata\n");
2276 } else {
2277 __ops_print_keydata(io, cbinfo->cryptinfo.keydata, "pub",
2278 &cbinfo->cryptinfo.keydata->key.pubkey);
2280 switch (pkt->tag) {
2281 case OPS_GET_PASSPHRASE:
2282 *(content->skey_passphrase.passphrase) =
2283 strdup(getpass("netpgp passphrase: "));
2284 return OPS_KEEP_MEMORY;
2285 default:
2286 break;
2288 return OPS_RELEASE_MEMORY;
2291 unsigned
2292 __ops_reader_set_accumulate(__ops_stream_t *stream, unsigned state)
2294 return stream->readinfo.accumulate = state;
2297 /**************************************************************************/
2299 static int
2300 hash_reader(void *dest,
2301 size_t length,
2302 __ops_error_t **errors,
2303 __ops_reader_t *readinfo,
2304 __ops_cbdata_t *cbinfo)
2306 __ops_hash_t *hash = __ops_reader_get_arg(readinfo);
2307 int r;
2309 r = __ops_stacked_read(dest, length, errors, readinfo, cbinfo);
2310 if (r <= 0) {
2311 return r;
2313 hash->add(hash, dest, (unsigned)r);
2314 return r;
2318 \ingroup Internal_Readers_Hash
2319 \brief Push hashed data reader on stack
2321 void
2322 __ops_reader_push_hash(__ops_stream_t *stream, __ops_hash_t *hash)
2324 if (!hash->init(hash)) {
2325 (void) fprintf(stderr, "__ops_reader_push_hash: can't init hash\n");
2326 /* just continue and die */
2327 /* XXX - agc - no way to return failure */
2329 __ops_reader_push(stream, hash_reader, NULL, hash);
2333 \ingroup Internal_Readers_Hash
2334 \brief Pop hashed data reader from stack
2336 void
2337 __ops_reader_pop_hash(__ops_stream_t *stream)
2339 __ops_reader_pop(stream);
2342 /* read memory from the previously mmap-ed file */
2343 static int
2344 mmap_reader(void *dest, size_t length, __ops_error_t **errors,
2345 __ops_reader_t *readinfo, __ops_cbdata_t *cbinfo)
2347 mmap_reader_t *mem = __ops_reader_get_arg(readinfo);
2348 unsigned n;
2349 char *cmem = mem->mem;
2351 __OPS_USED(errors);
2352 __OPS_USED(cbinfo);
2353 n = MIN(length, (unsigned)(mem->size - mem->offset));
2354 if (n > 0) {
2355 (void) memcpy(dest, &cmem[(int)mem->offset], (unsigned)n);
2356 mem->offset += n;
2358 return n;
2361 /* tear down the mmap, close the fd */
2362 static void
2363 mmap_destroyer(__ops_reader_t *readinfo)
2365 mmap_reader_t *mem = __ops_reader_get_arg(readinfo);
2367 (void) munmap(mem->mem, (unsigned)mem->size);
2368 (void) close(mem->fd);
2369 free(__ops_reader_get_arg(readinfo));
2372 /* set up the file to use mmap-ed memory if available, file IO otherwise */
2373 void
2374 __ops_reader_set_mmap(__ops_stream_t *stream, int fd)
2376 mmap_reader_t *mem;
2377 struct stat st;
2379 if (fstat(fd, &st) != 0) {
2380 (void) fprintf(stderr, "__ops_reader_set_mmap: can't fstat\n");
2381 } else if ((mem = calloc(1, sizeof(*mem))) == NULL) {
2382 (void) fprintf(stderr, "__ops_reader_set_mmap: bad alloc\n");
2383 } else {
2384 mem->size = (uint64_t)st.st_size;
2385 mem->offset = 0;
2386 mem->fd = fd;
2387 mem->mem = mmap(NULL, (size_t)st.st_size, PROT_READ,
2388 MAP_PRIVATE | MAP_FILE, fd, 0);
2389 if (mem->mem == MAP_FAILED) {
2390 __ops_reader_set(stream, fd_reader, reader_fd_destroyer,
2391 mem);
2392 } else {
2393 __ops_reader_set(stream, mmap_reader, mmap_destroyer,
2394 mem);