xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/validate.c (revision 88fcb00c0357f2d7c1774f86a352637bfda96184)
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.43 2010/11/11 00:58:04 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 static int
93 keydata_reader(pgp_stream_t *stream, void *dest, size_t length, pgp_error_t **errors,
94 	       pgp_reader_t *readinfo,
95 	       pgp_cbdata_t *cbinfo)
96 {
97 	validate_reader_t *reader = pgp_reader_get_arg(readinfo);
98 
99 	__PGP_USED(stream);
100 	__PGP_USED(errors);
101 	__PGP_USED(cbinfo);
102 	if (reader->offset == reader->key->packets[reader->packet].length) {
103 		reader->packet += 1;
104 		reader->offset = 0;
105 	}
106 	if (reader->packet == reader->key->packetc) {
107 		return 0;
108 	}
109 
110 	/*
111 	 * we should never be asked to cross a packet boundary in a single
112 	 * read
113 	 */
114 	if (reader->key->packets[reader->packet].length <
115 			reader->offset + length) {
116 		(void) fprintf(stderr, "keydata_reader: weird length\n");
117 		return 0;
118 	}
119 
120 	(void) memcpy(dest,
121 		&reader->key->packets[reader->packet].raw[reader->offset],
122 		length);
123 	reader->offset += (unsigned)length;
124 
125 	return (int)length;
126 }
127 
128 static void
129 free_sig_info(pgp_sig_info_t *sig)
130 {
131 	free(sig->v4_hashed);
132 	free(sig);
133 }
134 
135 static void
136 copy_sig_info(pgp_sig_info_t *dst, const pgp_sig_info_t *src)
137 {
138 	(void) memcpy(dst, src, sizeof(*src));
139 	if ((dst->v4_hashed = calloc(1, src->v4_hashlen)) == NULL) {
140 		(void) fprintf(stderr, "copy_sig_info: bad alloc\n");
141 	} else {
142 		(void) memcpy(dst->v4_hashed, src->v4_hashed, src->v4_hashlen);
143 	}
144 }
145 
146 static int
147 add_sig_to_list(const pgp_sig_info_t *sig, pgp_sig_info_t **sigs,
148 			unsigned *count)
149 {
150 	pgp_sig_info_t	*newsigs;
151 
152 	if (*count == 0) {
153 		newsigs = calloc(*count + 1, sizeof(pgp_sig_info_t));
154 	} else {
155 		newsigs = realloc(*sigs,
156 				(*count + 1) * sizeof(pgp_sig_info_t));
157 	}
158 	if (newsigs == NULL) {
159 		(void) fprintf(stderr, "add_sig_to_list: alloc failure\n");
160 		return 0;
161 	}
162 	*sigs = newsigs;
163 	copy_sig_info(&(*sigs)[*count], sig);
164 	*count += 1;
165 	return 1;
166 }
167 
168 /*
169 The hash value is calculated by the following method:
170 + hash the data using the given digest algorithm
171 + hash the hash value onto the end
172 + hash the trailer - 6 bytes
173   [PGP_V4][0xff][len >> 24][len >> 16][len >> 8][len & 0xff]
174 to give the final hash value that is checked against the one in the signature
175 */
176 
177 /* Does the signed hash match the given hash? */
178 unsigned
179 check_binary_sig(const uint8_t *data,
180 		const unsigned len,
181 		const pgp_sig_t *sig,
182 		const pgp_pubkey_t *signer)
183 {
184 	unsigned    hashedlen;
185 	pgp_hash_t	hash;
186 	unsigned	n;
187 	uint8_t		hashout[PGP_MAX_HASH_SIZE];
188 	uint8_t		trailer[6];
189 
190 	pgp_hash_any(&hash, sig->info.hash_alg);
191 	if (!hash.init(&hash)) {
192 		(void) fprintf(stderr, "check_binary_sig: bad hash init\n");
193 		return 0;
194 	}
195 	hash.add(&hash, data, len);
196 	switch (sig->info.version) {
197 	case PGP_V3:
198 		trailer[0] = sig->info.type;
199 		trailer[1] = (unsigned)(sig->info.birthtime) >> 24;
200 		trailer[2] = (unsigned)(sig->info.birthtime) >> 16;
201 		trailer[3] = (unsigned)(sig->info.birthtime) >> 8;
202 		trailer[4] = (uint8_t)(sig->info.birthtime);
203 		hash.add(&hash, trailer, 5);
204 		break;
205 
206 	case PGP_V4:
207 		if (pgp_get_debug_level(__FILE__)) {
208 			hexdump(stderr, "v4 hash", sig->info.v4_hashed,
209 					sig->info.v4_hashlen);
210 		}
211 		hash.add(&hash, sig->info.v4_hashed, (unsigned)sig->info.v4_hashlen);
212 		trailer[0] = 0x04;	/* version */
213 		trailer[1] = 0xFF;
214 		hashedlen = (unsigned)sig->info.v4_hashlen;
215 		trailer[2] = (uint8_t)(hashedlen >> 24);
216 		trailer[3] = (uint8_t)(hashedlen >> 16);
217 		trailer[4] = (uint8_t)(hashedlen >> 8);
218 		trailer[5] = (uint8_t)(hashedlen);
219 		hash.add(&hash, trailer, 6);
220 		break;
221 
222 	default:
223 		(void) fprintf(stderr, "Invalid signature version %d\n",
224 				sig->info.version);
225 		return 0;
226 	}
227 
228 	n = hash.finish(&hash, hashout);
229 	if (pgp_get_debug_level(__FILE__)) {
230 		hexdump(stdout, "hash out", hashout, n);
231 	}
232 	return pgp_check_sig(hashout, n, sig, signer);
233 }
234 
235 pgp_cb_ret_t
236 pgp_validate_key_cb(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
237 {
238 	const pgp_contents_t	 *content = &pkt->u;
239 	const pgp_key_t	 *signer;
240 	validate_key_cb_t	 *key;
241 	pgp_pubkey_t		 *sigkey;
242 	pgp_error_t		**errors;
243 	pgp_io_t		 *io;
244 	unsigned		  from;
245 	unsigned		  valid = 0;
246 
247 	io = cbinfo->io;
248 	if (pgp_get_debug_level(__FILE__)) {
249 		(void) fprintf(io->errs, "%s\n",
250 				pgp_show_packet_tag(pkt->tag));
251 	}
252 	key = pgp_callback_arg(cbinfo);
253 	errors = pgp_callback_errors(cbinfo);
254 	switch (pkt->tag) {
255 	case PGP_PTAG_CT_PUBLIC_KEY:
256 		if (key->pubkey.version != 0) {
257 			(void) fprintf(io->errs,
258 				"pgp_validate_key_cb: version bad\n");
259 			return PGP_FINISHED;
260 		}
261 		key->pubkey = content->pubkey;
262 		return PGP_KEEP_MEMORY;
263 
264 	case PGP_PTAG_CT_PUBLIC_SUBKEY:
265 		if (key->subkey.version) {
266 			pgp_pubkey_free(&key->subkey);
267 		}
268 		key->subkey = content->pubkey;
269 		return PGP_KEEP_MEMORY;
270 
271 	case PGP_PTAG_CT_SECRET_KEY:
272 		key->seckey = content->seckey;
273 		key->pubkey = key->seckey.pubkey;
274 		return PGP_KEEP_MEMORY;
275 
276 	case PGP_PTAG_CT_USER_ID:
277 		if (key->userid) {
278 			pgp_userid_free(&key->userid);
279 		}
280 		key->userid = content->userid;
281 		key->last_seen = ID;
282 		return PGP_KEEP_MEMORY;
283 
284 	case PGP_PTAG_CT_USER_ATTR:
285 		if (content->userattr.len == 0) {
286 			(void) fprintf(io->errs,
287 			"pgp_validate_key_cb: user attribute length 0");
288 			return PGP_FINISHED;
289 		}
290 		(void) fprintf(io->outs, "user attribute, length=%d\n",
291 			(int) content->userattr.len);
292 		if (key->userattr.len) {
293 			pgp_data_free(&key->userattr);
294 		}
295 		key->userattr = content->userattr;
296 		key->last_seen = ATTRIBUTE;
297 		return PGP_KEEP_MEMORY;
298 
299 	case PGP_PTAG_CT_SIGNATURE:	/* V3 sigs */
300 	case PGP_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
301 		from = 0;
302 		signer = pgp_getkeybyid(io, key->keyring,
303 					 content->sig.info.signer_id,
304 					 &from, &sigkey);
305 		if (!signer) {
306 			if (!add_sig_to_list(&content->sig.info,
307 				&key->result->unknown_sigs,
308 				&key->result->unknownc)) {
309 					(void) fprintf(io->errs,
310 					"pgp_validate_key_cb: user attribute length 0");
311 					return PGP_FINISHED;
312 			}
313 			break;
314 		}
315 		if (sigkey == &signer->enckey) {
316 			(void) fprintf(io->errs,
317 				"WARNING: signature made with encryption key\n");
318 		}
319 		switch (content->sig.info.type) {
320 		case PGP_CERT_GENERIC:
321 		case PGP_CERT_PERSONA:
322 		case PGP_CERT_CASUAL:
323 		case PGP_CERT_POSITIVE:
324 		case PGP_SIG_REV_CERT:
325 			valid = (key->last_seen == ID) ?
326 			    pgp_check_useridcert_sig(&key->pubkey,
327 					key->userid,
328 					&content->sig,
329 					pgp_get_pubkey(signer),
330 					key->reader->key->packets[
331 						key->reader->packet].raw) :
332 			    pgp_check_userattrcert_sig(&key->pubkey,
333 					&key->userattr,
334 					&content->sig,
335 				       pgp_get_pubkey(signer),
336 					key->reader->key->packets[
337 						key->reader->packet].raw);
338 			break;
339 
340 		case PGP_SIG_SUBKEY:
341 			/*
342 			 * XXX: we should also check that the signer is the
343 			 * key we are validating, I think.
344 			 */
345 			valid = pgp_check_subkey_sig(&key->pubkey,
346 				&key->subkey,
347 				&content->sig,
348 				pgp_get_pubkey(signer),
349 				key->reader->key->packets[
350 					key->reader->packet].raw);
351 			break;
352 
353 		case PGP_SIG_DIRECT:
354 			valid = pgp_check_direct_sig(&key->pubkey,
355 				&content->sig,
356 				pgp_get_pubkey(signer),
357 				key->reader->key->packets[
358 					key->reader->packet].raw);
359 			break;
360 
361 		case PGP_SIG_STANDALONE:
362 		case PGP_SIG_PRIMARY:
363 		case PGP_SIG_REV_KEY:
364 		case PGP_SIG_REV_SUBKEY:
365 		case PGP_SIG_TIMESTAMP:
366 		case PGP_SIG_3RD_PARTY:
367 			PGP_ERROR_1(errors, PGP_E_UNIMPLEMENTED,
368 				"Sig Verification type 0x%02x not done yet\n",
369 				content->sig.info.type);
370 			break;
371 
372 		default:
373 			PGP_ERROR_1(errors, PGP_E_UNIMPLEMENTED,
374 				    "Unexpected signature type 0x%02x\n",
375 				    	content->sig.info.type);
376 		}
377 
378 		if (valid) {
379 			if (!add_sig_to_list(&content->sig.info,
380 				&key->result->valid_sigs,
381 				&key->result->validc)) {
382 				PGP_ERROR(errors, PGP_E_UNIMPLEMENTED,
383 				    "Can't add good sig to list\n");
384 			}
385 		} else {
386 			PGP_ERROR(errors, PGP_E_V_BAD_SIGNATURE, "Bad Sig");
387 			if (!add_sig_to_list(&content->sig.info,
388 				&key->result->invalid_sigs,
389 				&key->result->invalidc)) {
390 				PGP_ERROR(errors, PGP_E_UNIMPLEMENTED,
391 				    "Can't add good sig to list\n");
392 			}
393 		}
394 		break;
395 
396 		/* ignore these */
397 	case PGP_PARSER_PTAG:
398 	case PGP_PTAG_CT_SIGNATURE_HEADER:
399 	case PGP_PARSER_PACKET_END:
400 		break;
401 
402 	case PGP_GET_PASSPHRASE:
403 		if (key->getpassphrase) {
404 			return key->getpassphrase(pkt, cbinfo);
405 		}
406 		break;
407 
408 	case PGP_PTAG_CT_TRUST:
409 		/* 1 byte for level (depth), 1 byte for trust amount */
410 		printf("trust dump\n");
411 		printf("Got trust\n");
412 		//hexdump(stdout, (const uint8_t *)content->trust.data, 10, " ");
413 		//hexdump(stdout, (const uint8_t *)&content->ss_trust, 2, " ");
414 		//printf("Trust level %d, amount %d\n", key->trust.level, key->trust.amount);
415 		break;
416 
417 	default:
418 		(void) fprintf(stderr, "unexpected tag=0x%x\n", pkt->tag);
419 		return PGP_FINISHED;
420 	}
421 	return PGP_RELEASE_MEMORY;
422 }
423 
424 pgp_cb_ret_t
425 validate_data_cb(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
426 {
427 	const pgp_contents_t	 *content = &pkt->u;
428 	const pgp_key_t	 *signer;
429 	validate_data_cb_t	 *data;
430 	pgp_pubkey_t		 *sigkey;
431 	pgp_error_t		**errors;
432 	pgp_io_t		 *io;
433 	unsigned		  from;
434 	unsigned		  valid = 0;
435 
436 	io = cbinfo->io;
437 	if (pgp_get_debug_level(__FILE__)) {
438 		(void) fprintf(io->errs, "validate_data_cb: %s\n",
439 				pgp_show_packet_tag(pkt->tag));
440 	}
441 	data = pgp_callback_arg(cbinfo);
442 	errors = pgp_callback_errors(cbinfo);
443 	switch (pkt->tag) {
444 	case PGP_PTAG_CT_SIGNED_CLEARTEXT_HEADER:
445 		/*
446 		 * ignore - this gives us the "Armor Header" line "Hash:
447 		 * SHA1" or similar
448 		 */
449 		break;
450 
451 	case PGP_PTAG_CT_LITDATA_HEADER:
452 		/* ignore */
453 		break;
454 
455 	case PGP_PTAG_CT_LITDATA_BODY:
456 		data->data.litdata_body = content->litdata_body;
457 		data->type = LITDATA;
458 		pgp_memory_add(data->mem, data->data.litdata_body.data,
459 				       data->data.litdata_body.length);
460 		return PGP_KEEP_MEMORY;
461 
462 	case PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY:
463 		data->data.cleartext_body = content->cleartext_body;
464 		data->type = SIGNED_CLEARTEXT;
465 		pgp_memory_add(data->mem, data->data.cleartext_body.data,
466 			       data->data.cleartext_body.length);
467 		return PGP_KEEP_MEMORY;
468 
469 	case PGP_PTAG_CT_SIGNED_CLEARTEXT_TRAILER:
470 		/* this gives us an pgp_hash_t struct */
471 		break;
472 
473 	case PGP_PTAG_CT_SIGNATURE:	/* V3 sigs */
474 	case PGP_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
475 		if (pgp_get_debug_level(__FILE__)) {
476 			hexdump(io->outs, "hashed data", content->sig.info.v4_hashed,
477 					content->sig.info.v4_hashlen);
478 			hexdump(io->outs, "signer id", content->sig.info.signer_id,
479 				sizeof(content->sig.info.signer_id));
480 		}
481 		from = 0;
482 		signer = pgp_getkeybyid(io, data->keyring,
483 					 content->sig.info.signer_id, &from, &sigkey);
484 		if (!signer) {
485 			PGP_ERROR(errors, PGP_E_V_UNKNOWN_SIGNER,
486 					"Unknown Signer");
487 			if (!add_sig_to_list(&content->sig.info,
488 					&data->result->unknown_sigs,
489 					&data->result->unknownc)) {
490 				PGP_ERROR(errors, PGP_E_V_UNKNOWN_SIGNER,
491 					"Can't add unknown sig to list");
492 			}
493 			break;
494 		}
495 		if (sigkey == &signer->enckey) {
496 			(void) fprintf(io->errs,
497 				"WARNING: signature made with encryption key\n");
498 		}
499 		if (content->sig.info.birthtime_set) {
500 			data->result->birthtime = content->sig.info.birthtime;
501 		}
502 		if (content->sig.info.duration_set) {
503 			data->result->duration = content->sig.info.duration;
504 		}
505 		switch (content->sig.info.type) {
506 		case PGP_SIG_BINARY:
507 		case PGP_SIG_TEXT:
508 			if (pgp_mem_len(data->mem) == 0 &&
509 			    data->detachname) {
510 				/* check we have seen some data */
511 				/* if not, need to read from detached name */
512 				(void) fprintf(io->errs,
513 				"netpgp: assuming signed data in \"%s\"\n",
514 					data->detachname);
515 				data->mem = pgp_memory_new();
516 				pgp_mem_readfile(data->mem, data->detachname);
517 			}
518 			if (pgp_get_debug_level(__FILE__)) {
519 				hexdump(stderr, "sig dump", (const uint8_t *)(const void *)&content->sig,
520 					sizeof(content->sig));
521 			}
522 			valid = check_binary_sig(pgp_mem_data(data->mem),
523 					(const unsigned)pgp_mem_len(data->mem),
524 					&content->sig,
525 					pgp_get_pubkey(signer));
526 			break;
527 
528 		default:
529 			PGP_ERROR_1(errors, PGP_E_UNIMPLEMENTED,
530 				    "No Sig Verification type 0x%02x yet\n",
531 				    content->sig.info.type);
532 			break;
533 
534 		}
535 
536 		if (valid) {
537 			if (!add_sig_to_list(&content->sig.info,
538 					&data->result->valid_sigs,
539 					&data->result->validc)) {
540 				PGP_ERROR(errors, PGP_E_V_BAD_SIGNATURE,
541 					"Can't add good sig to list");
542 			}
543 		} else {
544 			PGP_ERROR(errors, PGP_E_V_BAD_SIGNATURE,
545 					"Bad Signature");
546 			if (!add_sig_to_list(&content->sig.info,
547 					&data->result->invalid_sigs,
548 					&data->result->invalidc)) {
549 				PGP_ERROR(errors, PGP_E_V_BAD_SIGNATURE,
550 					"Can't add good sig to list");
551 			}
552 		}
553 		break;
554 
555 		/* ignore these */
556 	case PGP_PARSER_PTAG:
557 	case PGP_PTAG_CT_SIGNATURE_HEADER:
558 	case PGP_PTAG_CT_ARMOUR_HEADER:
559 	case PGP_PTAG_CT_ARMOUR_TRAILER:
560 	case PGP_PTAG_CT_1_PASS_SIG:
561 		break;
562 
563 	case PGP_PARSER_PACKET_END:
564 		break;
565 
566 	default:
567 		PGP_ERROR(errors, PGP_E_V_NO_SIGNATURE, "No signature");
568 		break;
569 	}
570 	return PGP_RELEASE_MEMORY;
571 }
572 
573 static void
574 keydata_destroyer(pgp_reader_t *readinfo)
575 {
576 	free(pgp_reader_get_arg(readinfo));
577 }
578 
579 void
580 pgp_keydata_reader_set(pgp_stream_t *stream, const pgp_key_t *key)
581 {
582 	validate_reader_t *data;
583 
584 	if ((data = calloc(1, sizeof(*data))) == NULL) {
585 		(void) fprintf(stderr, "pgp_keydata_reader_set: bad alloc\n");
586 	} else {
587 		data->key = key;
588 		data->packet = 0;
589 		data->offset = 0;
590 		pgp_reader_set(stream, keydata_reader, keydata_destroyer, data);
591 	}
592 }
593 
594 static char *
595 fmtsecs(int64_t n, char *buf, size_t size)
596 {
597 	if (n > 365 * 24 * 60 * 60) {
598 		n /= (365 * 24 * 60 * 60);
599 		(void) snprintf(buf, size, "%" PRId64 " year%s", n, (n == 1) ? "" : "s");
600 		return buf;
601 	}
602 	if (n > 30 * 24 * 60 * 60) {
603 		n /= (30 * 24 * 60 * 60);
604 		(void) snprintf(buf, size, "%" PRId64 " month%s", n, (n == 1) ? "" : "s");
605 		return buf;
606 	}
607 	if (n > 24 * 60 * 60) {
608 		n /= (24 * 60 * 60);
609 		(void) snprintf(buf, size, "%" PRId64 " day%s", n, (n == 1) ? "" : "s");
610 		return buf;
611 	}
612 	if (n > 60 * 60) {
613 		n /= (60 * 60);
614 		(void) snprintf(buf, size, "%" PRId64 " hour%s", n, (n == 1) ? "" : "s");
615 		return buf;
616 	}
617 	if (n > 60) {
618 		n /= 60;
619 		(void) snprintf(buf, size, "%" PRId64 " minute%s", n, (n == 1) ? "" : "s");
620 		return buf;
621 	}
622 	(void) snprintf(buf, size, "%" PRId64 " second%s", n, (n == 1) ? "" : "s");
623 	return buf;
624 }
625 
626 /**
627  * \ingroup HighLevel_Verify
628  * \brief Indicicates whether any errors were found
629  * \param result Validation result to check
630  * \return 0 if any invalid signatures or unknown signers
631  	or no valid signatures; else 1
632  */
633 static unsigned
634 validate_result_status(FILE *errs, const char *f, pgp_validation_t *val)
635 {
636 	time_t	now;
637 	time_t	t;
638 	char	buf[128];
639 
640 	now = time(NULL);
641 	if (now < val->birthtime) {
642 		/* signature is not valid yet! */
643 		if (f) {
644 			(void) fprintf(errs, "\"%s\": ", f);
645 		} else {
646 			(void) fprintf(errs, "memory ");
647 		}
648 		(void) fprintf(errs,
649 			"signature not valid until %.24s (%s)\n",
650 			ctime(&val->birthtime),
651 			fmtsecs((int64_t)(val->birthtime - now), buf, sizeof(buf)));
652 		return 0;
653 	}
654 	if (val->duration != 0 && now > val->birthtime + val->duration) {
655 		/* signature has expired */
656 		t = val->duration + val->birthtime;
657 		if (f) {
658 			(void) fprintf(errs, "\"%s\": ", f);
659 		} else {
660 			(void) fprintf(errs, "memory ");
661 		}
662 		(void) fprintf(errs,
663 			"signature not valid after %.24s (%s ago)\n",
664 			ctime(&t),
665 			fmtsecs((int64_t)(now - t), buf, sizeof(buf)));
666 		return 0;
667 	}
668 	return val->validc && !val->invalidc && !val->unknownc;
669 }
670 
671 /**
672  * \ingroup HighLevel_Verify
673  * \brief Validate all signatures on a single key against the given keyring
674  * \param result Where to put the result
675  * \param key Key to validate
676  * \param keyring Keyring to use for validation
677  * \param cb_get_passphrase Callback to use to get passphrase
678  * \return 1 if all signatures OK; else 0
679  * \note It is the caller's responsiblity to free result after use.
680  * \sa pgp_validate_result_free()
681  */
682 unsigned
683 pgp_validate_key_sigs(pgp_validation_t *result,
684 	const pgp_key_t *key,
685 	const pgp_keyring_t *keyring,
686 	pgp_cb_ret_t cb_get_passphrase(const pgp_packet_t *,
687 						pgp_cbdata_t *))
688 {
689 	pgp_stream_t	*stream;
690 	validate_key_cb_t	 keysigs;
691 	const int		 printerrors = 1;
692 
693 	(void) memset(&keysigs, 0x0, sizeof(keysigs));
694 	keysigs.result = result;
695 	keysigs.getpassphrase = cb_get_passphrase;
696 
697 	stream = pgp_new(sizeof(*stream));
698 	/* pgp_parse_options(&opt,PGP_PTAG_CT_SIGNATURE,PGP_PARSE_PARSED); */
699 
700 	keysigs.keyring = keyring;
701 
702 	pgp_set_callback(stream, pgp_validate_key_cb, &keysigs);
703 	stream->readinfo.accumulate = 1;
704 	pgp_keydata_reader_set(stream, key);
705 
706 	/* Note: Coverity incorrectly reports an error that keysigs.reader */
707 	/* is never used. */
708 	keysigs.reader = stream->readinfo.arg;
709 
710 	pgp_parse(stream, !printerrors);
711 
712 	pgp_pubkey_free(&keysigs.pubkey);
713 	if (keysigs.subkey.version) {
714 		pgp_pubkey_free(&keysigs.subkey);
715 	}
716 	pgp_userid_free(&keysigs.userid);
717 	pgp_data_free(&keysigs.userattr);
718 
719 	pgp_stream_delete(stream);
720 
721 	return (!result->invalidc && !result->unknownc && result->validc);
722 }
723 
724 /**
725    \ingroup HighLevel_Verify
726    \param result Where to put the result
727    \param ring Keyring to use
728    \param cb_get_passphrase Callback to use to get passphrase
729    \note It is the caller's responsibility to free result after use.
730    \sa pgp_validate_result_free()
731 */
732 unsigned
733 pgp_validate_all_sigs(pgp_validation_t *result,
734 	    const pgp_keyring_t *ring,
735 	    pgp_cb_ret_t cb_get_passphrase(const pgp_packet_t *,
736 	    					pgp_cbdata_t *))
737 {
738 	unsigned	n;
739 
740 	(void) memset(result, 0x0, sizeof(*result));
741 	for (n = 0; n < ring->keyc; ++n) {
742 		pgp_validate_key_sigs(result, &ring->keys[n], ring,
743 				cb_get_passphrase);
744 	}
745 	return validate_result_status(stderr, "keyring", result);
746 }
747 
748 /**
749    \ingroup HighLevel_Verify
750    \brief Frees validation result and associated memory
751    \param result Struct to be freed
752    \note Must be called after validation functions
753 */
754 void
755 pgp_validate_result_free(pgp_validation_t *result)
756 {
757 	if (result != NULL) {
758 		if (result->valid_sigs) {
759 			free_sig_info(result->valid_sigs);
760 		}
761 		if (result->invalid_sigs) {
762 			free_sig_info(result->invalid_sigs);
763 		}
764 		if (result->unknown_sigs) {
765 			free_sig_info(result->unknown_sigs);
766 		}
767 		free(result);
768 		/* result = NULL; - XXX unnecessary */
769 	}
770 }
771 
772 /**
773    \ingroup HighLevel_Verify
774    \brief Verifies the signatures in a signed file
775    \param result Where to put the result
776    \param filename Name of file to be validated
777    \param armoured Treat file as armoured, if set
778    \param keyring Keyring to use
779    \return 1 if signatures validate successfully;
780    	0 if signatures fail or there are no signatures
781    \note After verification, result holds the details of all keys which
782    have passed, failed and not been recognised.
783    \note It is the caller's responsiblity to call
784    	pgp_validate_result_free(result) after use.
785 */
786 unsigned
787 pgp_validate_file(pgp_io_t *io,
788 			pgp_validation_t *result,
789 			const char *infile,
790 			const char *outfile,
791 			const int user_says_armoured,
792 			const pgp_keyring_t *keyring)
793 {
794 	validate_data_cb_t	 validation;
795 	pgp_stream_t		*parse = NULL;
796 	struct stat		 st;
797 	const char		*signame;
798 	const int		 printerrors = 1;
799 	unsigned		 ret;
800 	char			 f[MAXPATHLEN];
801 	char			*dataname;
802 	int			 realarmour;
803 	int			 outfd = 0;
804 	int			 infd;
805 	int			 cc;
806 
807 	if (stat(infile, &st) < 0) {
808 		(void) fprintf(io->errs,
809 			"pgp_validate_file: can't open '%s'\n", infile);
810 		return 0;
811 	}
812 	realarmour = user_says_armoured;
813 	dataname = NULL;
814 	signame = NULL;
815 	cc = snprintf(f, sizeof(f), "%s", infile);
816 	if (strcmp(&f[cc - 4], ".sig") == 0) {
817 		/* we've been given a sigfile as infile */
818 		f[cc - 4] = 0x0;
819 		/* set dataname to name of file which was signed */
820 		dataname = f;
821 		signame = infile;
822 	} else if (strcmp(&f[cc - 4], ".asc") == 0) {
823 		/* we've been given an armored sigfile as infile */
824 		f[cc - 4] = 0x0;
825 		/* set dataname to name of file which was signed */
826 		dataname = f;
827 		signame = infile;
828 		realarmour = 1;
829 	} else {
830 		signame = infile;
831 	}
832 	(void) memset(&validation, 0x0, sizeof(validation));
833 	infd = pgp_setup_file_read(io, &parse, signame, &validation,
834 				validate_data_cb, 1);
835 	if (infd < 0) {
836 		return 0;
837 	}
838 
839 	if (dataname) {
840 		validation.detachname = netpgp_strdup(dataname);
841 	}
842 
843 	/* Set verification reader and handling options */
844 	validation.result = result;
845 	validation.keyring = keyring;
846 	validation.mem = pgp_memory_new();
847 	pgp_memory_init(validation.mem, 128);
848 	/* Note: Coverity incorrectly reports an error that validation.reader */
849 	/* is never used. */
850 	validation.reader = parse->readinfo.arg;
851 
852 	if (realarmour) {
853 		pgp_reader_push_dearmour(parse);
854 	}
855 
856 	/* Do the verification */
857 	pgp_parse(parse, !printerrors);
858 
859 	/* Tidy up */
860 	if (realarmour) {
861 		pgp_reader_pop_dearmour(parse);
862 	}
863 	pgp_teardown_file_read(parse, infd);
864 
865 	ret = validate_result_status(io->errs, infile, result);
866 
867 	/* this is triggered only for --cat output */
868 	if (outfile) {
869 		/* need to send validated output somewhere */
870 		if (strcmp(outfile, "-") == 0) {
871 			outfd = STDOUT_FILENO;
872 		} else {
873 			outfd = open(outfile, O_WRONLY | O_CREAT, 0666);
874 		}
875 		if (outfd < 0) {
876 			/* even if the signature was good, we can't
877 			* write the file, so send back a bad return
878 			* code */
879 			ret = 0;
880 		} else if (validate_result_status(io->errs, infile, result)) {
881 			unsigned	 len;
882 			char		*cp;
883 			int		 i;
884 
885 			len = (unsigned)pgp_mem_len(validation.mem);
886 			cp = pgp_mem_data(validation.mem);
887 			for (i = 0 ; i < (int)len ; i += cc) {
888 				cc = (int)write(outfd, &cp[i], (unsigned)(len - i));
889 				if (cc < 0) {
890 					(void) fprintf(io->errs,
891 						"netpgp: short write\n");
892 					ret = 0;
893 					break;
894 				}
895 			}
896 			if (strcmp(outfile, "-") != 0) {
897 				(void) close(outfd);
898 			}
899 		}
900 	}
901 	pgp_memory_free(validation.mem);
902 	return ret;
903 }
904 
905 /**
906    \ingroup HighLevel_Verify
907    \brief Verifies the signatures in a pgp_memory_t struct
908    \param result Where to put the result
909    \param mem Memory to be validated
910    \param user_says_armoured Treat data as armoured, if set
911    \param keyring Keyring to use
912    \return 1 if signature validates successfully; 0 if not
913    \note After verification, result holds the details of all keys which
914    have passed, failed and not been recognised.
915    \note It is the caller's responsiblity to call
916    	pgp_validate_result_free(result) after use.
917 */
918 
919 unsigned
920 pgp_validate_mem(pgp_io_t *io,
921 			pgp_validation_t *result,
922 			pgp_memory_t *mem,
923 			pgp_memory_t **cat,
924 			const int user_says_armoured,
925 			const pgp_keyring_t *keyring)
926 {
927 	validate_data_cb_t	 validation;
928 	pgp_stream_t		*stream = NULL;
929 	const int		 printerrors = 1;
930 	int			 realarmour;
931 
932 	pgp_setup_memory_read(io, &stream, mem, &validation, validate_data_cb, 1);
933 	/* Set verification reader and handling options */
934 	(void) memset(&validation, 0x0, sizeof(validation));
935 	validation.result = result;
936 	validation.keyring = keyring;
937 	validation.mem = pgp_memory_new();
938 	pgp_memory_init(validation.mem, 128);
939 	/* Note: Coverity incorrectly reports an error that validation.reader */
940 	/* is never used. */
941 	validation.reader = stream->readinfo.arg;
942 
943 	if ((realarmour = user_says_armoured) != 0 ||
944 	    strncmp(pgp_mem_data(mem),
945 	    		"-----BEGIN PGP MESSAGE-----", 27) == 0) {
946 		realarmour = 1;
947 	}
948 	if (realarmour) {
949 		pgp_reader_push_dearmour(stream);
950 	}
951 
952 	/* Do the verification */
953 	pgp_parse(stream, !printerrors);
954 
955 	/* Tidy up */
956 	if (realarmour) {
957 		pgp_reader_pop_dearmour(stream);
958 	}
959 	pgp_teardown_memory_read(stream, mem);
960 
961 	/* this is triggered only for --cat output */
962 	if (cat) {
963 		/* need to send validated output somewhere */
964 		*cat = validation.mem;
965 	} else {
966 		pgp_memory_free(validation.mem);
967 	}
968 
969 	return validate_result_status(io->errs, NULL, result);
970 }
971