xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/validate.c (revision 1ca06f9c9235889e2ff6dc77279d01d151d70a9a)
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.24 2009/12/07 16:17:17 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 char *data,
95 		const unsigned len,
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, 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		  from;
232 	unsigned		  valid = 0;
233 
234 	io = cbinfo->io;
235 	if (__ops_get_debug_level(__FILE__)) {
236 		(void) fprintf(io->errs, "%s\n",
237 				__ops_show_packet_tag(pkt->tag));
238 	}
239 	key = __ops_callback_arg(cbinfo);
240 	errors = __ops_callback_errors(cbinfo);
241 	switch (pkt->tag) {
242 	case OPS_PTAG_CT_PUBLIC_KEY:
243 		if (key->pubkey.version != 0) {
244 			(void) fprintf(io->errs,
245 				"__ops_validate_key_cb: version bad\n");
246 			return OPS_FINISHED;
247 		}
248 		key->pubkey = content->pubkey;
249 		return OPS_KEEP_MEMORY;
250 
251 	case OPS_PTAG_CT_PUBLIC_SUBKEY:
252 		if (key->subkey.version) {
253 			__ops_pubkey_free(&key->subkey);
254 		}
255 		key->subkey = content->pubkey;
256 		return OPS_KEEP_MEMORY;
257 
258 	case OPS_PTAG_CT_SECRET_KEY:
259 		key->seckey = content->seckey;
260 		key->pubkey = key->seckey.pubkey;
261 		return OPS_KEEP_MEMORY;
262 
263 	case OPS_PTAG_CT_USER_ID:
264 		if (key->userid.userid) {
265 			__ops_userid_free(&key->userid);
266 		}
267 		key->userid = content->userid;
268 		key->last_seen = ID;
269 		return OPS_KEEP_MEMORY;
270 
271 	case OPS_PTAG_CT_USER_ATTR:
272 		if (content->userattr.data.len == 0) {
273 			(void) fprintf(io->errs,
274 			"__ops_validate_key_cb: user attribute length 0");
275 			return OPS_FINISHED;
276 		}
277 		(void) fprintf(io->outs, "user attribute, length=%d\n",
278 			(int) content->userattr.data.len);
279 		if (key->userattr.data.len) {
280 			__ops_userattr_free(&key->userattr);
281 		}
282 		key->userattr = content->userattr;
283 		key->last_seen = ATTRIBUTE;
284 		return OPS_KEEP_MEMORY;
285 
286 	case OPS_PTAG_CT_SIGNATURE:	/* V3 sigs */
287 	case OPS_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
288 		from = 0;
289 		signer = __ops_getkeybyid(io, key->keyring,
290 					 content->sig.info.signer_id,
291 					 &from);
292 		if (!signer) {
293 			if (!add_sig_to_list(&content->sig.info,
294 				&key->result->unknown_sigs,
295 				&key->result->unknownc)) {
296 					(void) fprintf(io->errs,
297 					"__ops_validate_key_cb: user attribute length 0");
298 					return OPS_FINISHED;
299 			}
300 			break;
301 		}
302 		switch (content->sig.info.type) {
303 		case OPS_CERT_GENERIC:
304 		case OPS_CERT_PERSONA:
305 		case OPS_CERT_CASUAL:
306 		case OPS_CERT_POSITIVE:
307 		case OPS_SIG_REV_CERT:
308 			valid = (key->last_seen == ID) ?
309 			    __ops_check_useridcert_sig(&key->pubkey,
310 					&key->userid,
311 					&content->sig,
312 					__ops_get_pubkey(signer),
313 					key->reader->key->packets[
314 						key->reader->packet].raw) :
315 			    __ops_check_userattrcert_sig(&key->pubkey,
316 					&key->userattr,
317 					&content->sig,
318 				       __ops_get_pubkey(signer),
319 					key->reader->key->packets[
320 						key->reader->packet].raw);
321 			break;
322 
323 		case OPS_SIG_SUBKEY:
324 			/*
325 			 * XXX: we should also check that the signer is the
326 			 * key we are validating, I think.
327 			 */
328 			valid = __ops_check_subkey_sig(&key->pubkey,
329 				&key->subkey,
330 				&content->sig,
331 				__ops_get_pubkey(signer),
332 				key->reader->key->packets[
333 					key->reader->packet].raw);
334 			break;
335 
336 		case OPS_SIG_DIRECT:
337 			valid = __ops_check_direct_sig(&key->pubkey,
338 				&content->sig,
339 				__ops_get_pubkey(signer),
340 				key->reader->key->packets[
341 					key->reader->packet].raw);
342 			break;
343 
344 		case OPS_SIG_STANDALONE:
345 		case OPS_SIG_PRIMARY:
346 		case OPS_SIG_REV_KEY:
347 		case OPS_SIG_REV_SUBKEY:
348 		case OPS_SIG_TIMESTAMP:
349 		case OPS_SIG_3RD_PARTY:
350 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
351 				"Sig Verification type 0x%02x not done yet\n",
352 				content->sig.info.type);
353 			break;
354 
355 		default:
356 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
357 				    "Unexpected signature type 0x%02x\n",
358 				    	content->sig.info.type);
359 		}
360 
361 		if (valid) {
362 			if (!add_sig_to_list(&content->sig.info,
363 				&key->result->valid_sigs,
364 				&key->result->validc)) {
365 				OPS_ERROR(errors, OPS_E_UNIMPLEMENTED,
366 				    "Can't add good sig to list\n");
367 			}
368 		} else {
369 			OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE, "Bad Sig");
370 			if (!add_sig_to_list(&content->sig.info,
371 				&key->result->invalid_sigs,
372 				&key->result->invalidc)) {
373 				OPS_ERROR(errors, OPS_E_UNIMPLEMENTED,
374 				    "Can't add good sig to list\n");
375 			}
376 		}
377 		break;
378 
379 		/* ignore these */
380 	case OPS_PARSER_PTAG:
381 	case OPS_PTAG_CT_SIGNATURE_HEADER:
382 	case OPS_PARSER_PACKET_END:
383 		break;
384 
385 	case OPS_GET_PASSPHRASE:
386 		if (key->getpassphrase) {
387 			return key->getpassphrase(pkt, cbinfo);
388 		}
389 		break;
390 
391 	default:
392 		(void) fprintf(stderr, "unexpected tag=0x%x\n", pkt->tag);
393 		return OPS_FINISHED;
394 	}
395 	return OPS_RELEASE_MEMORY;
396 }
397 
398 __ops_cb_ret_t
399 validate_data_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
400 {
401 	const __ops_contents_t	 *content = &pkt->u;
402 	const __ops_key_t	 *signer;
403 	validate_data_cb_t	 *data;
404 	__ops_error_t		**errors;
405 	__ops_io_t		 *io;
406 	unsigned		  from;
407 	unsigned		  valid = 0;
408 
409 	io = cbinfo->io;
410 	if (__ops_get_debug_level(__FILE__)) {
411 		(void) fprintf(io->errs, "validate_data_cb: %s\n",
412 				__ops_show_packet_tag(pkt->tag));
413 	}
414 	data = __ops_callback_arg(cbinfo);
415 	errors = __ops_callback_errors(cbinfo);
416 	switch (pkt->tag) {
417 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER:
418 		/*
419 		 * ignore - this gives us the "Armor Header" line "Hash:
420 		 * SHA1" or similar
421 		 */
422 		break;
423 
424 	case OPS_PTAG_CT_LITDATA_HEADER:
425 		/* ignore */
426 		break;
427 
428 	case OPS_PTAG_CT_LITDATA_BODY:
429 		data->data.litdata_body = content->litdata_body;
430 		data->type = LITDATA;
431 		__ops_memory_add(data->mem, data->data.litdata_body.data,
432 				       data->data.litdata_body.length);
433 		return OPS_KEEP_MEMORY;
434 
435 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY:
436 		data->data.cleartext_body = content->cleartext_body;
437 		data->type = SIGNED_CLEARTEXT;
438 		__ops_memory_add(data->mem, data->data.litdata_body.data,
439 			       data->data.litdata_body.length);
440 		return OPS_KEEP_MEMORY;
441 
442 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER:
443 		/* this gives us an __ops_hash_t struct */
444 		break;
445 
446 	case OPS_PTAG_CT_SIGNATURE:	/* V3 sigs */
447 	case OPS_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
448 		if (__ops_get_debug_level(__FILE__)) {
449 			(void) fprintf(io->outs, "\n*** hashed data:\n");
450 			hexdump(io->outs, content->sig.info.v4_hashed,
451 					content->sig.info.v4_hashlen, " ");
452 			(void) fprintf(io->outs, "\n");
453 			(void) fprintf(io->outs, "type=%02x signer_id=",
454 					content->sig.info.type);
455 			hexdump(io->outs, content->sig.info.signer_id,
456 				sizeof(content->sig.info.signer_id), "");
457 			(void) fprintf(io->outs, "\n");
458 		}
459 		from = 0;
460 		signer = __ops_getkeybyid(io, data->keyring,
461 					 content->sig.info.signer_id, &from);
462 		if (!signer) {
463 			OPS_ERROR(errors, OPS_E_V_UNKNOWN_SIGNER,
464 					"Unknown Signer");
465 			if (!add_sig_to_list(&content->sig.info,
466 					&data->result->unknown_sigs,
467 					&data->result->unknownc)) {
468 				OPS_ERROR(errors, OPS_E_V_UNKNOWN_SIGNER,
469 					"Can't add unknown sig to list");
470 			}
471 			break;
472 		}
473 		switch (content->sig.info.type) {
474 		case OPS_SIG_BINARY:
475 		case OPS_SIG_TEXT:
476 			if (__ops_mem_len(data->mem) == 0 &&
477 			    data->detachname) {
478 				/* check we have seen some data */
479 				/* if not, need to read from detached name */
480 				(void) fprintf(io->outs,
481 				"netpgp: assuming signed data in \"%s\"\n",
482 					data->detachname);
483 				data->mem = __ops_memory_new();
484 				__ops_mem_readfile(data->mem, data->detachname);
485 			}
486 			valid = check_binary_sig(__ops_mem_data(data->mem),
487 					__ops_mem_len(data->mem),
488 					&content->sig,
489 					__ops_get_pubkey(signer));
490 			break;
491 
492 		default:
493 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
494 				    "No Sig Verification type 0x%02x yet\n",
495 				    content->sig.info.type);
496 			break;
497 
498 		}
499 
500 		if (valid) {
501 			if (!add_sig_to_list(&content->sig.info,
502 					&data->result->valid_sigs,
503 					&data->result->validc)) {
504 				OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE,
505 					"Can't add good sig to list");
506 			}
507 		} else {
508 			OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE,
509 					"Bad Signature");
510 			if (!add_sig_to_list(&content->sig.info,
511 					&data->result->invalid_sigs,
512 					&data->result->invalidc)) {
513 				OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE,
514 					"Can't add good sig to list");
515 			}
516 		}
517 		break;
518 
519 		/* ignore these */
520 	case OPS_PARSER_PTAG:
521 	case OPS_PTAG_CT_SIGNATURE_HEADER:
522 	case OPS_PTAG_CT_ARMOUR_HEADER:
523 	case OPS_PTAG_CT_ARMOUR_TRAILER:
524 	case OPS_PTAG_CT_1_PASS_SIG:
525 		break;
526 
527 	case OPS_PARSER_PACKET_END:
528 		break;
529 
530 	default:
531 		OPS_ERROR(errors, OPS_E_V_NO_SIGNATURE, "No signature");
532 		break;
533 	}
534 	return OPS_RELEASE_MEMORY;
535 }
536 
537 static void
538 keydata_destroyer(__ops_reader_t *readinfo)
539 {
540 	free(__ops_reader_get_arg(readinfo));
541 }
542 
543 void
544 __ops_keydata_reader_set(__ops_stream_t *stream, const __ops_key_t *key)
545 {
546 	validate_reader_t *data;
547 
548 	if ((data = calloc(1, sizeof(*data))) == NULL) {
549 		(void) fprintf(stderr, "__ops_keydata_reader_set: bad alloc\n");
550 	} else {
551 		data->key = key;
552 		data->packet = 0;
553 		data->offset = 0;
554 		__ops_reader_set(stream, keydata_reader, keydata_destroyer, data);
555 	}
556 }
557 
558 /**
559  * \ingroup HighLevel_Verify
560  * \brief Indicicates whether any errors were found
561  * \param result Validation result to check
562  * \return 0 if any invalid signatures or unknown signers
563  	or no valid signatures; else 1
564  */
565 static unsigned
566 validate_result_status(__ops_validation_t *val)
567 {
568 	return val->validc && !val->invalidc && !val->unknownc;
569 }
570 
571 /**
572  * \ingroup HighLevel_Verify
573  * \brief Validate all signatures on a single key against the given keyring
574  * \param result Where to put the result
575  * \param key Key to validate
576  * \param keyring Keyring to use for validation
577  * \param cb_get_passphrase Callback to use to get passphrase
578  * \return 1 if all signatures OK; else 0
579  * \note It is the caller's responsiblity to free result after use.
580  * \sa __ops_validate_result_free()
581  */
582 unsigned
583 __ops_validate_key_sigs(__ops_validation_t *result,
584 	const __ops_key_t *key,
585 	const __ops_keyring_t *keyring,
586 	__ops_cb_ret_t cb_get_passphrase(const __ops_packet_t *,
587 						__ops_cbdata_t *))
588 {
589 	__ops_stream_t	*stream;
590 	validate_key_cb_t	 keysigs;
591 	const int		 printerrors = 1;
592 
593 	(void) memset(&keysigs, 0x0, sizeof(keysigs));
594 	keysigs.result = result;
595 	keysigs.getpassphrase = cb_get_passphrase;
596 
597 	stream = __ops_new(sizeof(*stream));
598 	/* __ops_parse_options(&opt,OPS_PTAG_CT_SIGNATURE,OPS_PARSE_PARSED); */
599 
600 	keysigs.keyring = keyring;
601 
602 	__ops_set_callback(stream, __ops_validate_key_cb, &keysigs);
603 	stream->readinfo.accumulate = 1;
604 	__ops_keydata_reader_set(stream, key);
605 
606 	/* Note: Coverity incorrectly reports an error that keysigs.reader */
607 	/* is never used. */
608 	keysigs.reader = stream->readinfo.arg;
609 
610 	__ops_parse(stream, !printerrors);
611 
612 	__ops_pubkey_free(&keysigs.pubkey);
613 	if (keysigs.subkey.version) {
614 		__ops_pubkey_free(&keysigs.subkey);
615 	}
616 	__ops_userid_free(&keysigs.userid);
617 	__ops_userattr_free(&keysigs.userattr);
618 
619 	__ops_stream_delete(stream);
620 
621 	return (!result->invalidc && !result->unknownc && result->validc);
622 }
623 
624 /**
625    \ingroup HighLevel_Verify
626    \param result Where to put the result
627    \param ring Keyring to use
628    \param cb_get_passphrase Callback to use to get passphrase
629    \note It is the caller's responsibility to free result after use.
630    \sa __ops_validate_result_free()
631 */
632 unsigned
633 __ops_validate_all_sigs(__ops_validation_t *result,
634 	    const __ops_keyring_t *ring,
635 	    __ops_cb_ret_t cb_get_passphrase(const __ops_packet_t *,
636 	    					__ops_cbdata_t *))
637 {
638 	unsigned	n;
639 
640 	(void) memset(result, 0x0, sizeof(*result));
641 	for (n = 0; n < ring->keyc; ++n) {
642 		__ops_validate_key_sigs(result, &ring->keys[n], ring,
643 				cb_get_passphrase);
644 	}
645 	return validate_result_status(result);
646 }
647 
648 /**
649    \ingroup HighLevel_Verify
650    \brief Frees validation result and associated memory
651    \param result Struct to be freed
652    \note Must be called after validation functions
653 */
654 void
655 __ops_validate_result_free(__ops_validation_t *result)
656 {
657 	if (result != NULL) {
658 		if (result->valid_sigs) {
659 			free_sig_info(result->valid_sigs);
660 		}
661 		if (result->invalid_sigs) {
662 			free_sig_info(result->invalid_sigs);
663 		}
664 		if (result->unknown_sigs) {
665 			free_sig_info(result->unknown_sigs);
666 		}
667 		free(result);
668 		/* result = NULL; - XXX unnecessary */
669 	}
670 }
671 
672 /**
673    \ingroup HighLevel_Verify
674    \brief Verifies the signatures in a signed file
675    \param result Where to put the result
676    \param filename Name of file to be validated
677    \param armoured Treat file as armoured, if set
678    \param keyring Keyring to use
679    \return 1 if signatures validate successfully;
680    	0 if signatures fail or there are no signatures
681    \note After verification, result holds the details of all keys which
682    have passed, failed and not been recognised.
683    \note It is the caller's responsiblity to call
684    	__ops_validate_result_free(result) after use.
685 */
686 unsigned
687 __ops_validate_file(__ops_io_t *io,
688 			__ops_validation_t *result,
689 			const char *infile,
690 			const char *outfile,
691 			const int armoured,
692 			const __ops_keyring_t *keyring)
693 {
694 	validate_data_cb_t	 validation;
695 	__ops_stream_t		*parse = NULL;
696 	struct stat		 st;
697 	const int		 printerrors = 1;
698 	unsigned		 ret;
699 	int64_t		 	 sigsize;
700 	char			 origfile[MAXPATHLEN];
701 	char			*detachname;
702 	int			 realarmour;
703 	int			 outfd = 0;
704 	int			 infd;
705 	int			 cc;
706 
707 #define SIG_OVERHEAD	284 /* XXX - depends on sig size? */
708 
709 	realarmour = armoured;
710 	if (stat(infile, &st) < 0) {
711 		(void) fprintf(io->errs, "can't validate \"%s\"\n", infile);
712 		return 0;
713 	}
714 	sigsize = st.st_size;
715 	detachname = NULL;
716 	cc = snprintf(origfile, sizeof(origfile), "%s", infile);
717 	if (strcmp(&origfile[cc - 4], ".sig") == 0) {
718 		origfile[cc - 4] = 0x0;
719 		if (stat(origfile, &st) == 0 &&
720 		    st.st_size > sigsize - SIG_OVERHEAD) {
721 			detachname = strdup(origfile);
722 		}
723 	}
724 	if (strcmp(&origfile[cc - 4], ".asc") == 0) {
725 		realarmour = 1;
726 	}
727 
728 	(void) memset(&validation, 0x0, sizeof(validation));
729 
730 	infd = __ops_setup_file_read(io, &parse, infile, &validation,
731 				validate_data_cb, 1);
732 	if (infd < 0) {
733 		return 0;
734 	}
735 
736 	validation.detachname = detachname;
737 
738 	/* Set verification reader and handling options */
739 	validation.result = result;
740 	validation.keyring = keyring;
741 	validation.mem = __ops_memory_new();
742 	__ops_memory_init(validation.mem, 128);
743 	/* Note: Coverity incorrectly reports an error that validation.reader */
744 	/* is never used. */
745 	validation.reader = parse->readinfo.arg;
746 
747 	if (realarmour) {
748 		__ops_reader_push_dearmour(parse);
749 	}
750 
751 	/* Do the verification */
752 	__ops_parse(parse, !printerrors);
753 
754 	/* Tidy up */
755 	if (realarmour) {
756 		__ops_reader_pop_dearmour(parse);
757 	}
758 	__ops_teardown_file_read(parse, infd);
759 
760 	ret = validate_result_status(result);
761 
762 	/* this is triggered only for --cat output */
763 	if (outfile) {
764 		/* need to send validated output somewhere */
765 		if (strcmp(outfile, "-") == 0) {
766 			outfd = STDOUT_FILENO;
767 		} else {
768 			outfd = open(outfile, O_WRONLY | O_CREAT, 0666);
769 		}
770 		if (outfd < 0) {
771 			/* even if the signature was good, we can't
772 			* write the file, so send back a bad return
773 			* code */
774 			ret = 0;
775 		} else if (validate_result_status(result)) {
776 			unsigned	 len;
777 			char		*cp;
778 			int		 i;
779 
780 			len = __ops_mem_len(validation.mem);
781 			cp = __ops_mem_data(validation.mem);
782 			for (i = 0 ; i < (int)len ; i += cc) {
783 				cc = write(outfd, &cp[i], len - i);
784 				if (cc < 0) {
785 					(void) fprintf(io->errs,
786 						"netpgp: short write\n");
787 					ret = 0;
788 					break;
789 				}
790 			}
791 			if (strcmp(outfile, "-") != 0) {
792 				(void) close(outfd);
793 			}
794 		}
795 	}
796 	__ops_memory_free(validation.mem);
797 	return ret;
798 }
799 
800 /**
801    \ingroup HighLevel_Verify
802    \brief Verifies the signatures in a __ops_memory_t struct
803    \param result Where to put the result
804    \param mem Memory to be validated
805    \param armoured Treat data as armoured, if set
806    \param keyring Keyring to use
807    \return 1 if signature validates successfully; 0 if not
808    \note After verification, result holds the details of all keys which
809    have passed, failed and not been recognised.
810    \note It is the caller's responsiblity to call
811    	__ops_validate_result_free(result) after use.
812 */
813 
814 unsigned
815 __ops_validate_mem(__ops_io_t *io,
816 			__ops_validation_t *result,
817 			__ops_memory_t *mem,
818 			const int armoured,
819 			const __ops_keyring_t *keyring)
820 {
821 	validate_data_cb_t	 validation;
822 	__ops_stream_t	*stream = NULL;
823 	const int		 printerrors = 1;
824 
825 	__ops_setup_memory_read(io, &stream, mem, &validation, validate_data_cb, 1);
826 	/* Set verification reader and handling options */
827 	(void) memset(&validation, 0x0, sizeof(validation));
828 	validation.result = result;
829 	validation.keyring = keyring;
830 	validation.mem = __ops_memory_new();
831 	__ops_memory_init(validation.mem, 128);
832 	/* Note: Coverity incorrectly reports an error that validation.reader */
833 	/* is never used. */
834 	validation.reader = stream->readinfo.arg;
835 
836 	if (armoured) {
837 		__ops_reader_push_dearmour(stream);
838 	}
839 
840 	/* Do the verification */
841 	__ops_parse(stream, !printerrors);
842 
843 	/* Tidy up */
844 	if (armoured) {
845 		__ops_reader_pop_dearmour(stream);
846 	}
847 	__ops_teardown_memory_read(stream, mem);
848 	__ops_memory_free(validation.mem);
849 
850 	return validate_result_status(result);
851 }
852