xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/validate.c (revision c8da0e5fefd3800856b306200a18b2315c7fbb9f)
1 /*-
2  * Copyright (c) 2009 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Alistair Crooks (agc@NetBSD.org)
7  *
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.
16  *
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.
28  */
29 /*
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.
35  *
36  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
37  * use this file except in compliance with the License.
38  *
39  * You may obtain a copy of the License at
40  *     http://www.apache.org/licenses/LICENSE-2.0
41  *
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.
45  *
46  * See the License for the specific language governing permissions and
47  * limitations under the License.
48  */
49 #include "config.h"
50 
51 #include <sys/types.h>
52 #include <sys/param.h>
53 #include <sys/stat.h>
54 
55 #include <string.h>
56 #include <stdio.h>
57 
58 #include "packet-parse.h"
59 #include "packet-show.h"
60 #include "keyring.h"
61 #include "signature.h"
62 #include "netpgpsdk.h"
63 #include "readerwriter.h"
64 #include "netpgpdefs.h"
65 #include "memory.h"
66 #include "keyring_local.h"
67 #include "parse_local.h"
68 #include "validate.h"
69 
70 
71 /* Does the signed hash match the given hash? */
72 static          bool
73 check_binary_sig(const unsigned len,
74 		       const unsigned char *data,
75 		       const __ops_sig_t *sig,
76 		    const __ops_pubkey_t *signer)
77 {
78 	unsigned char   hashout[OPS_MAX_HASH_SIZE];
79 	unsigned char   trailer[6];
80 	unsigned int    hashedlen;
81 	__ops_hash_t	hash;
82 	unsigned	n = 0;
83 
84 	__OPS_USED(signer);
85 	__ops_hash_any(&hash, sig->info.hash_alg);
86 	hash.init(&hash);
87 	hash.add(&hash, data, len);
88 	switch (sig->info.version) {
89 	case OPS_V3:
90 		trailer[0] = sig->info.type;
91 		trailer[1] = (unsigned)(sig->info.birthtime) >> 24;
92 		trailer[2] = (unsigned)(sig->info.birthtime) >> 16;
93 		trailer[3] = (unsigned)(sig->info.birthtime) >> 8;
94 		trailer[4] = (unsigned char)(sig->info.birthtime);
95 		hash.add(&hash, &trailer[0], 5);
96 		break;
97 
98 	case OPS_V4:
99 		hash.add(&hash, sig->info.v4_hashed_data,
100 				sig->info.v4_hashed_data_length);
101 		trailer[0] = 0x04;	/* version */
102 		trailer[1] = 0xFF;
103 		hashedlen = sig->info.v4_hashed_data_length;
104 		trailer[2] = hashedlen >> 24;
105 		trailer[3] = hashedlen >> 16;
106 		trailer[4] = hashedlen >> 8;
107 		trailer[5] = hashedlen;
108 		hash.add(&hash, &trailer[0], 6);
109 		break;
110 
111 	default:
112 		fprintf(stderr, "Invalid signature version %d\n",
113 				sig->info.version);
114 		return false;
115 	}
116 
117 	n = hash.finish(&hash, hashout);
118 	if (__ops_get_debug_level(__FILE__)) {
119 		printf("check_binary_sig: hash length %" PRIsize "u\n",
120 			hash.size);
121 	}
122 	return __ops_check_sig(hashout, n, sig, signer);
123 }
124 
125 static int
126 keydata_reader(void *dest, size_t length, __ops_error_t ** errors,
127 	       __ops_reader_t * readinfo,
128 	       __ops_callback_data_t * cbinfo)
129 {
130 	validate_reader_t *reader = __ops_reader_get_arg(readinfo);
131 
132 	__OPS_USED(errors);
133 	__OPS_USED(cbinfo);
134 	if (reader->offset == reader->key->packets[reader->packet].length) {
135 		reader->packet += 1;
136 		reader->offset = 0;
137 	}
138 	if (reader->packet == reader->key->npackets) {
139 		return 0;
140 	}
141 
142 	/*
143 	 * we should never be asked to cross a packet boundary in a single
144 	 * read
145 	 */
146 	if (reader->key->packets[reader->packet].length <
147 			reader->offset + length) {
148 		(void) fprintf(stderr, "keydata_reader: weird length\n");
149 		return 0;
150 	}
151 
152 	(void) memcpy(dest,
153 		&reader->key->packets[reader->packet].raw[reader->offset],
154 		length);
155 	reader->offset += length;
156 
157 	return length;
158 }
159 
160 static void
161 free_sig_info(__ops_sig_info_t * sig)
162 {
163 	(void) free(sig->v4_hashed_data);
164 	(void) free(sig);
165 }
166 
167 static void
168 copy_sig_info(__ops_sig_info_t * dst, const __ops_sig_info_t * src)
169 {
170 	(void) memcpy(dst, src, sizeof(*src));
171 	dst->v4_hashed_data = calloc(1, src->v4_hashed_data_length);
172 	(void) memcpy(dst->v4_hashed_data, src->v4_hashed_data,
173 		src->v4_hashed_data_length);
174 }
175 
176 static void
177 add_sig_to_list(const __ops_sig_info_t *sig, __ops_sig_info_t **sigs,
178 			unsigned *count)
179 {
180 	if (*count == 0) {
181 		*sigs = calloc(*count + 1, sizeof(__ops_sig_info_t));
182 	} else {
183 		*sigs = realloc(*sigs,
184 				(*count + 1) * sizeof(__ops_sig_info_t));
185 	}
186 	copy_sig_info(&(*sigs)[*count], sig);
187 	*count += 1;
188 }
189 
190 
191 __ops_parse_cb_return_t
192 __ops_validate_key_cb(const __ops_packet_t *pkt, __ops_callback_data_t *cbinfo)
193 {
194 	const __ops_parser_content_union_t *content = &pkt->u;
195 	const __ops_keydata_t	*signer;
196 	validate_key_cb_t	*key = __ops_parse_cb_get_arg(cbinfo);
197 	__ops_error_t		**errors = __ops_parse_cb_get_errors(cbinfo);
198 	bool			 valid = false;
199 
200 	if (__ops_get_debug_level(__FILE__)) {
201 		printf("%s\n", __ops_show_packet_tag(pkt->tag));
202 	}
203 
204 	switch (pkt->tag) {
205 	case OPS_PTAG_CT_PUBLIC_KEY:
206 		if (key->pubkey.version != 0) {
207 			(void) fprintf(stderr,
208 				"__ops_validate_key_cb: version bad\n");
209 			return OPS_FINISHED;
210 		}
211 		key->pubkey = content->pubkey;
212 		return OPS_KEEP_MEMORY;
213 
214 	case OPS_PTAG_CT_PUBLIC_SUBKEY:
215 		if (key->subkey.version)
216 			__ops_pubkey_free(&key->subkey);
217 		key->subkey = content->pubkey;
218 		return OPS_KEEP_MEMORY;
219 
220 	case OPS_PTAG_CT_SECRET_KEY:
221 		key->seckey = content->seckey;
222 		key->pubkey = key->seckey.pubkey;
223 		return OPS_KEEP_MEMORY;
224 
225 	case OPS_PTAG_CT_USER_ID:
226 		if (key->user_id.user_id)
227 			__ops_user_id_free(&key->user_id);
228 		key->user_id = content->user_id;
229 		key->last_seen = ID;
230 		return OPS_KEEP_MEMORY;
231 
232 	case OPS_PTAG_CT_USER_ATTRIBUTE:
233 		if (content->user_attribute.data.len == 0) {
234 			(void) fprintf(stderr,
235 			"__ops_validate_key_cb: user attribute length 0");
236 			return OPS_FINISHED;
237 		}
238 		printf("user attribute, length=%d\n",
239 			(int) content->user_attribute.data.len);
240 		if (key->user_attribute.data.len)
241 			__ops_user_attribute_free(&key->user_attribute);
242 		key->user_attribute = content->user_attribute;
243 		key->last_seen = ATTRIBUTE;
244 		return OPS_KEEP_MEMORY;
245 
246 	case OPS_PTAG_CT_SIGNATURE:	/* V3 sigs */
247 	case OPS_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
248 
249 		signer = __ops_keyring_find_key_by_id(key->keyring,
250 					 content->sig.info.signer_id);
251 		if (!signer) {
252 			add_sig_to_list(&content->sig.info,
253 					&key->result->unknown_sigs,
254 					&key->result->unknownc);
255 			break;
256 		}
257 		switch (content->sig.info.type) {
258 		case OPS_CERT_GENERIC:
259 		case OPS_CERT_PERSONA:
260 		case OPS_CERT_CASUAL:
261 		case OPS_CERT_POSITIVE:
262 		case OPS_SIG_REV_CERT:
263 			valid = (key->last_seen == ID) ?
264 			    __ops_check_useridcert_sig(&key->pubkey,
265 					&key->user_id,
266 					&content->sig,
267 					__ops_get_pubkey(signer),
268 				key->rarg->key->packets[key->rarg->packet].raw):
269 			    __ops_check_userattrcert_sig(&key->pubkey,
270 					&key->user_attribute,
271 					&content->sig,
272 				       __ops_get_pubkey(signer),
273 				key->rarg->key->packets[key->rarg->packet].raw);
274 			break;
275 
276 		case OPS_SIG_SUBKEY:
277 			/*
278 			 * XXX: we should also check that the signer is the
279 			 * key we are validating, I think.
280 			 */
281 			valid = __ops_check_subkey_sig(&key->pubkey, &key->subkey,
282 				&content->sig,
283 				__ops_get_pubkey(signer),
284 				key->rarg->key->packets[key->rarg->packet].raw);
285 			break;
286 
287 		case OPS_SIG_DIRECT:
288 			valid = __ops_check_direct_sig(&key->pubkey,
289 				&content->sig,
290 				__ops_get_pubkey(signer),
291 				key->rarg->key->packets[key->rarg->packet].raw);
292 			break;
293 
294 		case OPS_SIG_STANDALONE:
295 		case OPS_SIG_PRIMARY:
296 		case OPS_SIG_REV_KEY:
297 		case OPS_SIG_REV_SUBKEY:
298 		case OPS_SIG_TIMESTAMP:
299 		case OPS_SIG_3RD_PARTY:
300 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
301 				"Sig Verification type 0x%02x not done yet\n",
302 				content->sig.info.type);
303 			break;
304 
305 		default:
306 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
307 				    "Unexpected signature type 0x%02x\n",
308 				    	content->sig.info.type);
309 		}
310 
311 		if (valid) {
312 			add_sig_to_list(&content->sig.info,
313 				&key->result->valid_sigs,
314 				&key->result->validc);
315 		} else {
316 			OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE, "Bad Sig");
317 			add_sig_to_list(&content->sig.info,
318 					&key->result->invalid_sigs,
319 					&key->result->invalidc);
320 		}
321 		break;
322 
323 		/* ignore these */
324 	case OPS_PARSER_PTAG:
325 	case OPS_PTAG_CT_SIGNATURE_HEADER:
326 	case OPS_PARSER_PACKET_END:
327 		break;
328 
329 	case OPS_PARSER_CMD_GET_SK_PASSPHRASE:
330 		if (key->cb_get_passphrase) {
331 			return key->cb_get_passphrase(pkt, cbinfo);
332 		}
333 		break;
334 
335 	default:
336 		(void) fprintf(stderr, "unexpected tag=0x%x\n", pkt->tag);
337 		return OPS_FINISHED;
338 	}
339 	return OPS_RELEASE_MEMORY;
340 }
341 
342 __ops_parse_cb_return_t
343 validate_data_cb(const __ops_packet_t * pkt,
344 			__ops_callback_data_t * cbinfo)
345 {
346 	const __ops_parser_content_union_t *content = &pkt->u;
347 	const __ops_keydata_t	*signer;
348 	validate_data_cb_t	*data = __ops_parse_cb_get_arg(cbinfo);
349 	__ops_error_t		**errors = __ops_parse_cb_get_errors(cbinfo);
350 	bool			 valid = false;
351 
352 	if (__ops_get_debug_level(__FILE__)) {
353 		printf("validate_data_cb: %s\n",
354 			__ops_show_packet_tag(pkt->tag));
355 	}
356 	switch (pkt->tag) {
357 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER:
358 		/*
359 		 * ignore - this gives us the "Armor Header" line "Hash:
360 		 * SHA1" or similar
361 		 */
362 		break;
363 
364 	case OPS_PTAG_CT_LITERAL_DATA_HEADER:
365 		/* ignore */
366 		break;
367 
368 	case OPS_PTAG_CT_LITERAL_DATA_BODY:
369 		data->data.litdata_body = content->litdata_body;
370 		data->use = LITERAL_DATA;
371 		__ops_memory_add(data->mem, data->data.litdata_body.data,
372 			       data->data.litdata_body.length);
373 		return OPS_KEEP_MEMORY;
374 
375 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY:
376 		data->data.cleartext_body =
377 				content->cleartext_body;
378 		data->use = SIGNED_CLEARTEXT;
379 		__ops_memory_add(data->mem, data->data.litdata_body.data,
380 			       data->data.litdata_body.length);
381 		return OPS_KEEP_MEMORY;
382 
383 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER:
384 		/* this gives us an __ops_hash_t struct */
385 		break;
386 
387 	case OPS_PTAG_CT_SIGNATURE:	/* V3 sigs */
388 	case OPS_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
389 		if (__ops_get_debug_level(__FILE__)) {
390 			unsigned i = 0;
391 
392 			printf("\n*** hashed data:\n");
393 			for (i = 0;
394 			     i < content->sig.info.v4_hashed_data_length;
395 			     i++) {
396 				printf("0x%02x ",
397 				content->sig.info.v4_hashed_data[i]);
398 			}
399 			printf("\n");
400 			printf("  type=%02x signer_id=",
401 				content->sig.info.type);
402 			hexdump(content->sig.info.signer_id,
403 				sizeof(content->sig.info.signer_id), "");
404 			printf("\n");
405 		}
406 		signer = __ops_keyring_find_key_by_id(data->keyring,
407 					 content->sig.info.signer_id);
408 		if (!signer) {
409 			OPS_ERROR(errors, OPS_E_V_UNKNOWN_SIGNER,
410 					"Unknown Signer");
411 			add_sig_to_list(&content->sig.info,
412 					&data->result->unknown_sigs,
413 					&data->result->unknownc);
414 			break;
415 		}
416 		switch (content->sig.info.type) {
417 		case OPS_SIG_BINARY:
418 		case OPS_SIG_TEXT:
419 			valid = check_binary_sig(
420 					__ops_memory_get_length(data->mem),
421 					__ops_memory_get_data(data->mem),
422 					&content->sig,
423 					__ops_get_pubkey(signer));
424 			break;
425 
426 		default:
427 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
428 				    "No Sig Verification type 0x%02x yet\n",
429 				    content->sig.info.type);
430 			break;
431 
432 		}
433 
434 		__ops_memory_free(data->mem);
435 
436 		if (valid) {
437 			add_sig_to_list(&content->sig.info,
438 					&data->result->valid_sigs,
439 					&data->result->validc);
440 		} else {
441 			OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE,
442 					"Bad Signature");
443 			add_sig_to_list(&content->sig.info,
444 					&data->result->invalid_sigs,
445 					&data->result->invalidc);
446 		}
447 		break;
448 
449 		/* ignore these */
450 	case OPS_PARSER_PTAG:
451 	case OPS_PTAG_CT_SIGNATURE_HEADER:
452 	case OPS_PTAG_CT_ARMOUR_HEADER:
453 	case OPS_PTAG_CT_ARMOUR_TRAILER:
454 	case OPS_PTAG_CT_ONE_PASS_SIGNATURE:
455 	case OPS_PARSER_PACKET_END:
456 		break;
457 
458 	default:
459 		OPS_ERROR(errors, OPS_E_V_NO_SIGNATURE, "No signature");
460 		break;
461 	}
462 	return OPS_RELEASE_MEMORY;
463 }
464 
465 static void
466 keydata_destroyer(__ops_reader_t * readinfo)
467 {
468 	free(__ops_reader_get_arg(readinfo));
469 }
470 
471 void
472 __ops_keydata_reader_set(__ops_parseinfo_t *pinfo,
473 			const __ops_keydata_t *key)
474 {
475 	validate_reader_t *data = calloc(1, sizeof(*data));
476 
477 	data->key = key;
478 	data->packet = 0;
479 	data->offset = 0;
480 
481 	__ops_reader_set(pinfo, keydata_reader, keydata_destroyer, data);
482 }
483 
484 /**
485  * \ingroup HighLevel_Verify
486  * \brief Indicicates whether any errors were found
487  * \param result Validation result to check
488  * \return false if any invalid signatures or unknown signers
489  	or no valid signatures; else true
490  */
491 static bool
492 validate_result_status(__ops_validation_t *val)
493 {
494 	return val->validc && !val->invalidc && !val->unknownc;
495 }
496 
497 /**
498  * \ingroup HighLevel_Verify
499  * \brief Validate all signatures on a single key against the given keyring
500  * \param result Where to put the result
501  * \param key Key to validate
502  * \param keyring Keyring to use for validation
503  * \param cb_get_passphrase Callback to use to get passphrase
504  * \return true if all signatures OK; else false
505  * \note It is the caller's responsiblity to free result after use.
506  * \sa __ops_validate_result_free()
507  */
508 bool
509 __ops_validate_key_sigs(__ops_validation_t * result,
510 	const __ops_keydata_t * key,
511 	const __ops_keyring_t * keyring,
512 	__ops_parse_cb_return_t cb_get_passphrase(const __ops_packet_t *,
513 						__ops_callback_data_t *))
514 {
515 	__ops_parseinfo_t	*pinfo;
516 	validate_key_cb_t	 carg;
517 
518 	(void) memset(&carg, 0x0, sizeof(carg));
519 	carg.result = result;
520 	carg.cb_get_passphrase = cb_get_passphrase;
521 
522 	pinfo = __ops_parseinfo_new();
523 	/* __ops_parse_options(&opt,OPS_PTAG_CT_SIGNATURE,OPS_PARSE_PARSED); */
524 
525 	carg.keyring = keyring;
526 
527 	__ops_parse_cb_set(pinfo, __ops_validate_key_cb, &carg);
528 	pinfo->readinfo.accumulate = true;
529 	__ops_keydata_reader_set(pinfo, key);
530 
531 	/* Note: Coverity incorrectly reports an error that carg.rarg */
532 	/* is never used. */
533 	carg.rarg = pinfo->readinfo.arg;
534 
535 	__ops_parse(pinfo, 0);
536 
537 	__ops_pubkey_free(&carg.pubkey);
538 	if (carg.subkey.version) {
539 		__ops_pubkey_free(&carg.subkey);
540 	}
541 	__ops_user_id_free(&carg.user_id);
542 	__ops_user_attribute_free(&carg.user_attribute);
543 
544 	__ops_parseinfo_delete(pinfo);
545 
546 	return (!result->invalidc && !result->unknownc && result->validc);
547 }
548 
549 /**
550    \ingroup HighLevel_Verify
551    \param result Where to put the result
552    \param ring Keyring to use
553    \param cb_get_passphrase Callback to use to get passphrase
554    \note It is the caller's responsibility to free result after use.
555    \sa __ops_validate_result_free()
556 */
557 bool
558 __ops_validate_all_sigs(__ops_validation_t *result,
559 	    const __ops_keyring_t *ring,
560 	    __ops_parse_cb_return_t cb_get_passphrase(const __ops_packet_t *,
561 	    					__ops_callback_data_t *))
562 {
563 	int	n;
564 
565 	(void) memset(result, 0x0, sizeof(*result));
566 	for (n = 0; n < ring->nkeys; ++n) {
567 		__ops_validate_key_sigs(result, &ring->keys[n], ring,
568 				cb_get_passphrase);
569 	}
570 	return validate_result_status(result);
571 }
572 
573 /**
574    \ingroup HighLevel_Verify
575    \brief Frees validation result and associated memory
576    \param result Struct to be freed
577    \note Must be called after validation functions
578 */
579 void
580 __ops_validate_result_free(__ops_validation_t *result)
581 {
582 	if (result != NULL) {
583 		if (result->valid_sigs) {
584 			free_sig_info(result->valid_sigs);
585 		}
586 		if (result->invalid_sigs) {
587 			free_sig_info(result->invalid_sigs);
588 		}
589 		if (result->unknown_sigs) {
590 			free_sig_info(result->unknown_sigs);
591 		}
592 		(void) free(result);
593 		result = NULL;
594 	}
595 }
596 
597 /**
598    \ingroup HighLevel_Verify
599    \brief Verifies the signatures in a signed file
600    \param result Where to put the result
601    \param filename Name of file to be validated
602    \param armoured Treat file as armoured, if set
603    \param keyring Keyring to use
604    \return true if signatures validate successfully;
605    	false if signatures fail or there are no signatures
606    \note After verification, result holds the details of all keys which
607    have passed, failed and not been recognised.
608    \note It is the caller's responsiblity to call
609    	__ops_validate_result_free(result) after use.
610 */
611 bool
612 __ops_validate_file(__ops_validation_t *result,
613 			const char *infile,
614 			const char *outfile,
615 			const int armoured,
616 			const __ops_keyring_t *keyring)
617 {
618 	__ops_parseinfo_t	*parse = NULL;
619 	validate_data_cb_t	 validation;
620 	struct stat		 st;
621 	int64_t		 	 origsize;
622 	char			*filename;
623 	char			 origfile[MAXPATHLEN];
624 	int			 fd_out = 0;
625 	int			 fd_in;
626 	int			 cc;
627 
628 #define SIG_OVERHEAD	284 /* XXX - depends on sig size? */
629 
630 	if (stat(infile, &st) < 0) {
631 		(void) fprintf(stderr, "can't validate \"%s\"\n", infile);
632 		return false;
633 	}
634 	origsize = st.st_size;
635 	cc = snprintf(origfile, sizeof(origfile), "%s", infile);
636 	if (strcmp(&origfile[cc - 4], ".sig") == 0) {
637 		origfile[cc - 4] = 0x0;
638 		if (stat(origfile, &st) == 0 &&
639 		    st.st_size - SIG_OVERHEAD < origsize) {
640 			parse->synthlit = strdup(origfile);
641 		}
642 	}
643 
644 	fd_in = __ops_setup_file_read(&parse, infile, &validation,
645 				validate_data_cb, true);
646 	if (fd_in < 0) {
647 		return false;
648 	}
649 
650 	/* setup output filename */
651 	filename = NULL;
652 	if (outfile) {
653 		if (strcmp(outfile, "-") == 0) {
654 			outfile = NULL;
655 		}
656 		fd_out = __ops_setup_file_write(&parse->cbinfo.cinfo,
657 						NULL, false);
658 		if (fd_out < 0) {
659 			__ops_teardown_file_read(parse, fd_in);
660 			return false;
661 		}
662 	}
663 
664 	/* Set verification reader and handling options */
665 	(void) memset(&validation, 0x0, sizeof(validation));
666 	validation.result = result;
667 	validation.keyring = keyring;
668 	validation.mem = __ops_memory_new();
669 	__ops_memory_init(validation.mem, 128);
670 	/* Note: Coverity incorrectly reports an error that carg.rarg */
671 	/* is never used. */
672 	validation.rarg = parse->readinfo.arg;
673 
674 	if (armoured) {
675 		__ops_reader_push_dearmour(parse);
676 	}
677 
678 	/* Do the verification */
679 	__ops_parse(parse, 0);
680 
681 	if (__ops_get_debug_level(__FILE__)) {
682 		printf("valid=%d, invalid=%d, unknown=%d\n",
683 		       result->validc,
684 		       result->invalidc,
685 		       result->unknownc);
686 	}
687 
688 	/* Tidy up */
689 	if (armoured) {
690 		__ops_reader_pop_dearmour(parse);
691 	}
692 	__ops_teardown_file_read(parse, fd_in);
693 
694 	return validate_result_status(result);
695 }
696 
697 /**
698    \ingroup HighLevel_Verify
699    \brief Verifies the signatures in a __ops_memory_t struct
700    \param result Where to put the result
701    \param mem Memory to be validated
702    \param armoured Treat data as armoured, if set
703    \param keyring Keyring to use
704    \return true if signature validates successfully; false if not
705    \note After verification, result holds the details of all keys which
706    have passed, failed and not been recognised.
707    \note It is the caller's responsiblity to call
708    	__ops_validate_result_free(result) after use.
709 */
710 
711 bool
712 __ops_validate_mem(__ops_validation_t *result, __ops_memory_t *mem,
713 			const int armoured, const __ops_keyring_t *keyring)
714 {
715 	__ops_parseinfo_t	*pinfo = NULL;
716 	validate_data_cb_t	 validation;
717 
718 	__ops_setup_memory_read(&pinfo, mem, &validation, validate_data_cb,
719 			true);
720 
721 	/* Set verification reader and handling options */
722 	(void) memset(&validation, 0x0, sizeof(validation));
723 	validation.result = result;
724 	validation.keyring = keyring;
725 	validation.mem = __ops_memory_new();
726 	__ops_memory_init(validation.mem, 128);
727 	/* Note: Coverity incorrectly reports an error that carg.rarg */
728 	/* is never used. */
729 	validation.rarg = pinfo->readinfo.arg;
730 
731 	if (armoured) {
732 		__ops_reader_push_dearmour(pinfo);
733 	}
734 
735 	/* Do the verification */
736 	__ops_parse(pinfo, 0);
737 	if (__ops_get_debug_level(__FILE__)) {
738 		printf("valid=%d, invalid=%d, unknown=%d\n",
739 		       result->validc,
740 		       result->invalidc,
741 		       result->unknownc);
742 	}
743 
744 	/* Tidy up */
745 	if (armoured) {
746 		__ops_reader_pop_dearmour(pinfo);
747 	}
748 	__ops_teardown_memory_read(pinfo, mem);
749 
750 	return validate_result_status(result);
751 }
752