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