xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/validate.c (revision 62a8debe1dc62962e18a1c918def78666141273b)
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.31 2010/03/05 16:01:10 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(void *dest, size_t length, __ops_error_t **errors,
94 	       __ops_reader_t *readinfo,
95 	       __ops_cbdata_t *cbinfo)
96 {
97 	validate_reader_t *reader = __ops_reader_get_arg(readinfo);
98 
99 	__OPS_USED(errors);
100 	__OPS_USED(cbinfo);
101 	if (reader->offset == reader->key->packets[reader->packet].length) {
102 		reader->packet += 1;
103 		reader->offset = 0;
104 	}
105 	if (reader->packet == reader->key->packetc) {
106 		return 0;
107 	}
108 
109 	/*
110 	 * we should never be asked to cross a packet boundary in a single
111 	 * read
112 	 */
113 	if (reader->key->packets[reader->packet].length <
114 			reader->offset + length) {
115 		(void) fprintf(stderr, "keydata_reader: weird length\n");
116 		return 0;
117 	}
118 
119 	(void) memcpy(dest,
120 		&reader->key->packets[reader->packet].raw[reader->offset],
121 		length);
122 	reader->offset += length;
123 
124 	return length;
125 }
126 
127 static void
128 free_sig_info(__ops_sig_info_t *sig)
129 {
130 	free(sig->v4_hashed);
131 	free(sig);
132 }
133 
134 static void
135 copy_sig_info(__ops_sig_info_t *dst, const __ops_sig_info_t *src)
136 {
137 	(void) memcpy(dst, src, sizeof(*src));
138 	if ((dst->v4_hashed = calloc(1, src->v4_hashlen)) == NULL) {
139 		(void) fprintf(stderr, "copy_sig_info: bad alloc\n");
140 	} else {
141 		(void) memcpy(dst->v4_hashed, src->v4_hashed, src->v4_hashlen);
142 	}
143 }
144 
145 static int
146 add_sig_to_list(const __ops_sig_info_t *sig, __ops_sig_info_t **sigs,
147 			unsigned *count)
148 {
149 	__ops_sig_info_t	*newsigs;
150 
151 	if (*count == 0) {
152 		newsigs = calloc(*count + 1, sizeof(__ops_sig_info_t));
153 	} else {
154 		newsigs = realloc(*sigs,
155 				(*count + 1) * sizeof(__ops_sig_info_t));
156 	}
157 	if (newsigs == NULL) {
158 		(void) fprintf(stderr, "add_sig_to_list: alloc failure\n");
159 		return 0;
160 	}
161 	*sigs = newsigs;
162 	copy_sig_info(&(*sigs)[*count], sig);
163 	*count += 1;
164 	return 1;
165 }
166 
167 /*
168 The hash value is calculated by the following method:
169 + hash the data using the given digest algorithm
170 + hash the hash value onto the end
171 + hash the trailer - 6 bytes
172   [OPS_V4][0xff][len >> 24][len >> 16][len >> 8][len & 0xff]
173 to give the final hash value that is checked against the one in the signature
174 */
175 
176 /* Does the signed hash match the given hash? */
177 unsigned
178 check_binary_sig(const uint8_t *data,
179 		const unsigned len,
180 		const __ops_sig_t *sig,
181 		const __ops_pubkey_t *signer)
182 {
183 	unsigned    hashedlen;
184 	__ops_hash_t	hash;
185 	unsigned	n;
186 	uint8_t		hashout[OPS_MAX_HASH_SIZE];
187 	uint8_t		trailer[6];
188 
189 	__ops_hash_any(&hash, sig->info.hash_alg);
190 	if (!hash.init(&hash)) {
191 		(void) fprintf(stderr, "check_binary_sig: bad hash init\n");
192 		return 0;
193 	}
194 	hash.add(&hash, data, len);
195 	switch (sig->info.version) {
196 	case OPS_V3:
197 		trailer[0] = sig->info.type;
198 		trailer[1] = (unsigned)(sig->info.birthtime) >> 24;
199 		trailer[2] = (unsigned)(sig->info.birthtime) >> 16;
200 		trailer[3] = (unsigned)(sig->info.birthtime) >> 8;
201 		trailer[4] = (uint8_t)(sig->info.birthtime);
202 		hash.add(&hash, trailer, 5);
203 		break;
204 
205 	case OPS_V4:
206 		if (__ops_get_debug_level(__FILE__)) {
207 			(void) fprintf(stderr, "v4_hashlen %zu\n",
208 					sig->info.v4_hashlen);
209 			hexdump(stderr, sig->info.v4_hashed,
210 					sig->info.v4_hashlen, " ");
211 			(void) fprintf(stderr, "\n");
212 		}
213 		hash.add(&hash, sig->info.v4_hashed, sig->info.v4_hashlen);
214 		trailer[0] = 0x04;	/* version */
215 		trailer[1] = 0xFF;
216 		hashedlen = sig->info.v4_hashlen;
217 		trailer[2] = hashedlen >> 24;
218 		trailer[3] = hashedlen >> 16;
219 		trailer[4] = hashedlen >> 8;
220 		trailer[5] = hashedlen;
221 		hash.add(&hash, trailer, 6);
222 		break;
223 
224 	default:
225 		(void) fprintf(stderr, "Invalid signature version %d\n",
226 				sig->info.version);
227 		return 0;
228 	}
229 
230 	n = hash.finish(&hash, hashout);
231 	if (__ops_get_debug_level(__FILE__)) {
232 		printf("check_binary_sig: hash length %" PRIsize "u\n",
233 			hash.size);
234 		hexdump(stdout, hashout, n, "");
235 	}
236 	return __ops_check_sig(hashout, n, sig, signer);
237 }
238 
239 __ops_cb_ret_t
240 __ops_validate_key_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
241 {
242 	const __ops_contents_t	 *content = &pkt->u;
243 	const __ops_key_t	 *signer;
244 	validate_key_cb_t	 *key;
245 	__ops_error_t		**errors;
246 	__ops_io_t		 *io;
247 	unsigned		  from;
248 	unsigned		  valid = 0;
249 
250 	io = cbinfo->io;
251 	if (__ops_get_debug_level(__FILE__)) {
252 		(void) fprintf(io->errs, "%s\n",
253 				__ops_show_packet_tag(pkt->tag));
254 	}
255 	key = __ops_callback_arg(cbinfo);
256 	errors = __ops_callback_errors(cbinfo);
257 	switch (pkt->tag) {
258 	case OPS_PTAG_CT_PUBLIC_KEY:
259 		if (key->pubkey.version != 0) {
260 			(void) fprintf(io->errs,
261 				"__ops_validate_key_cb: version bad\n");
262 			return OPS_FINISHED;
263 		}
264 		key->pubkey = content->pubkey;
265 		return OPS_KEEP_MEMORY;
266 
267 	case OPS_PTAG_CT_PUBLIC_SUBKEY:
268 		if (key->subkey.version) {
269 			__ops_pubkey_free(&key->subkey);
270 		}
271 		key->subkey = content->pubkey;
272 		return OPS_KEEP_MEMORY;
273 
274 	case OPS_PTAG_CT_SECRET_KEY:
275 		key->seckey = content->seckey;
276 		key->pubkey = key->seckey.pubkey;
277 		return OPS_KEEP_MEMORY;
278 
279 	case OPS_PTAG_CT_USER_ID:
280 		if (key->userid.userid) {
281 			__ops_userid_free(&key->userid);
282 		}
283 		key->userid = content->userid;
284 		key->last_seen = ID;
285 		return OPS_KEEP_MEMORY;
286 
287 	case OPS_PTAG_CT_USER_ATTR:
288 		if (content->userattr.data.len == 0) {
289 			(void) fprintf(io->errs,
290 			"__ops_validate_key_cb: user attribute length 0");
291 			return OPS_FINISHED;
292 		}
293 		(void) fprintf(io->outs, "user attribute, length=%d\n",
294 			(int) content->userattr.data.len);
295 		if (key->userattr.data.len) {
296 			__ops_userattr_free(&key->userattr);
297 		}
298 		key->userattr = content->userattr;
299 		key->last_seen = ATTRIBUTE;
300 		return OPS_KEEP_MEMORY;
301 
302 	case OPS_PTAG_CT_SIGNATURE:	/* V3 sigs */
303 	case OPS_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
304 		from = 0;
305 		signer = __ops_getkeybyid(io, key->keyring,
306 					 content->sig.info.signer_id,
307 					 &from);
308 		if (!signer) {
309 			if (!add_sig_to_list(&content->sig.info,
310 				&key->result->unknown_sigs,
311 				&key->result->unknownc)) {
312 					(void) fprintf(io->errs,
313 					"__ops_validate_key_cb: user attribute length 0");
314 					return OPS_FINISHED;
315 			}
316 			break;
317 		}
318 		switch (content->sig.info.type) {
319 		case OPS_CERT_GENERIC:
320 		case OPS_CERT_PERSONA:
321 		case OPS_CERT_CASUAL:
322 		case OPS_CERT_POSITIVE:
323 		case OPS_SIG_REV_CERT:
324 			valid = (key->last_seen == ID) ?
325 			    __ops_check_useridcert_sig(&key->pubkey,
326 					&key->userid,
327 					&content->sig,
328 					__ops_get_pubkey(signer),
329 					key->reader->key->packets[
330 						key->reader->packet].raw) :
331 			    __ops_check_userattrcert_sig(&key->pubkey,
332 					&key->userattr,
333 					&content->sig,
334 				       __ops_get_pubkey(signer),
335 					key->reader->key->packets[
336 						key->reader->packet].raw);
337 			break;
338 
339 		case OPS_SIG_SUBKEY:
340 			/*
341 			 * XXX: we should also check that the signer is the
342 			 * key we are validating, I think.
343 			 */
344 			valid = __ops_check_subkey_sig(&key->pubkey,
345 				&key->subkey,
346 				&content->sig,
347 				__ops_get_pubkey(signer),
348 				key->reader->key->packets[
349 					key->reader->packet].raw);
350 			/* XXX - agc - put subkey logic here */
351 			break;
352 
353 		case OPS_SIG_DIRECT:
354 			valid = __ops_check_direct_sig(&key->pubkey,
355 				&content->sig,
356 				__ops_get_pubkey(signer),
357 				key->reader->key->packets[
358 					key->reader->packet].raw);
359 			break;
360 
361 		case OPS_SIG_STANDALONE:
362 		case OPS_SIG_PRIMARY:
363 		case OPS_SIG_REV_KEY:
364 		case OPS_SIG_REV_SUBKEY:
365 		case OPS_SIG_TIMESTAMP:
366 		case OPS_SIG_3RD_PARTY:
367 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
368 				"Sig Verification type 0x%02x not done yet\n",
369 				content->sig.info.type);
370 			break;
371 
372 		default:
373 			OPS_ERROR_1(errors, OPS_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 				OPS_ERROR(errors, OPS_E_UNIMPLEMENTED,
383 				    "Can't add good sig to list\n");
384 			}
385 		} else {
386 			OPS_ERROR(errors, OPS_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 				OPS_ERROR(errors, OPS_E_UNIMPLEMENTED,
391 				    "Can't add good sig to list\n");
392 			}
393 		}
394 		break;
395 
396 		/* ignore these */
397 	case OPS_PARSER_PTAG:
398 	case OPS_PTAG_CT_SIGNATURE_HEADER:
399 	case OPS_PARSER_PACKET_END:
400 		break;
401 
402 	case OPS_GET_PASSPHRASE:
403 		if (key->getpassphrase) {
404 			return key->getpassphrase(pkt, cbinfo);
405 		}
406 		break;
407 
408 	case OPS_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 OPS_FINISHED;
420 	}
421 	return OPS_RELEASE_MEMORY;
422 }
423 
424 __ops_cb_ret_t
425 validate_data_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
426 {
427 	const __ops_contents_t	 *content = &pkt->u;
428 	const __ops_key_t	 *signer;
429 	validate_data_cb_t	 *data;
430 	__ops_error_t		**errors;
431 	__ops_io_t		 *io;
432 	unsigned		  from;
433 	unsigned		  valid = 0;
434 
435 	io = cbinfo->io;
436 	if (__ops_get_debug_level(__FILE__)) {
437 		(void) fprintf(io->errs, "validate_data_cb: %s\n",
438 				__ops_show_packet_tag(pkt->tag));
439 	}
440 	data = __ops_callback_arg(cbinfo);
441 	errors = __ops_callback_errors(cbinfo);
442 	switch (pkt->tag) {
443 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER:
444 		/*
445 		 * ignore - this gives us the "Armor Header" line "Hash:
446 		 * SHA1" or similar
447 		 */
448 		break;
449 
450 	case OPS_PTAG_CT_LITDATA_HEADER:
451 		/* ignore */
452 		break;
453 
454 	case OPS_PTAG_CT_LITDATA_BODY:
455 		data->data.litdata_body = content->litdata_body;
456 		data->type = LITDATA;
457 		__ops_memory_add(data->mem, data->data.litdata_body.data,
458 				       data->data.litdata_body.length);
459 		return OPS_KEEP_MEMORY;
460 
461 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY:
462 		data->data.cleartext_body = content->cleartext_body;
463 		data->type = SIGNED_CLEARTEXT;
464 		__ops_memory_add(data->mem, data->data.litdata_body.data,
465 			       data->data.litdata_body.length);
466 		return OPS_KEEP_MEMORY;
467 
468 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER:
469 		/* this gives us an __ops_hash_t struct */
470 		break;
471 
472 	case OPS_PTAG_CT_SIGNATURE:	/* V3 sigs */
473 	case OPS_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
474 		if (__ops_get_debug_level(__FILE__)) {
475 			(void) fprintf(io->outs, "\n*** hashed data:\n");
476 			hexdump(io->outs, content->sig.info.v4_hashed,
477 					content->sig.info.v4_hashlen, " ");
478 			(void) fprintf(io->outs, "\ntype=%02x signer_id=",
479 					content->sig.info.type);
480 			hexdump(io->outs, content->sig.info.signer_id,
481 				sizeof(content->sig.info.signer_id), "");
482 			(void) fprintf(io->outs, "\n");
483 		}
484 		from = 0;
485 		signer = __ops_getkeybyid(io, data->keyring,
486 					 content->sig.info.signer_id, &from);
487 		if (!signer) {
488 			OPS_ERROR(errors, OPS_E_V_UNKNOWN_SIGNER,
489 					"Unknown Signer");
490 			if (!add_sig_to_list(&content->sig.info,
491 					&data->result->unknown_sigs,
492 					&data->result->unknownc)) {
493 				OPS_ERROR(errors, OPS_E_V_UNKNOWN_SIGNER,
494 					"Can't add unknown sig to list");
495 			}
496 			break;
497 		}
498 		if (content->sig.info.birthtime_set) {
499 			data->result->birthtime = content->sig.info.birthtime;
500 		}
501 		if (content->sig.info.duration_set) {
502 			data->result->duration = content->sig.info.duration;
503 		}
504 		switch (content->sig.info.type) {
505 		case OPS_SIG_BINARY:
506 		case OPS_SIG_TEXT:
507 			if (__ops_mem_len(data->mem) == 0 &&
508 			    data->detachname) {
509 				/* check we have seen some data */
510 				/* if not, need to read from detached name */
511 				(void) fprintf(io->outs,
512 				"netpgp: assuming signed data in \"%s\"\n",
513 					data->detachname);
514 				data->mem = __ops_memory_new();
515 				__ops_mem_readfile(data->mem, data->detachname);
516 			}
517 			if (__ops_get_debug_level(__FILE__)) {
518 				(void) fprintf(stderr, "about to check_binary_sig, dump of sig:\n");
519 				hexdump(stderr, (const uint8_t *)(const void *)&content->sig,
520 					sizeof(content->sig), "");
521 			}
522 			valid = check_binary_sig(__ops_mem_data(data->mem),
523 					__ops_mem_len(data->mem),
524 					&content->sig,
525 					__ops_get_pubkey(signer));
526 			break;
527 
528 		default:
529 			OPS_ERROR_1(errors, OPS_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 				OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE,
541 					"Can't add good sig to list");
542 			}
543 		} else {
544 			OPS_ERROR(errors, OPS_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 				OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE,
550 					"Can't add good sig to list");
551 			}
552 		}
553 		break;
554 
555 		/* ignore these */
556 	case OPS_PARSER_PTAG:
557 	case OPS_PTAG_CT_SIGNATURE_HEADER:
558 	case OPS_PTAG_CT_ARMOUR_HEADER:
559 	case OPS_PTAG_CT_ARMOUR_TRAILER:
560 	case OPS_PTAG_CT_1_PASS_SIG:
561 		break;
562 
563 	case OPS_PARSER_PACKET_END:
564 		break;
565 
566 	default:
567 		OPS_ERROR(errors, OPS_E_V_NO_SIGNATURE, "No signature");
568 		break;
569 	}
570 	return OPS_RELEASE_MEMORY;
571 }
572 
573 static void
574 keydata_destroyer(__ops_reader_t *readinfo)
575 {
576 	free(__ops_reader_get_arg(readinfo));
577 }
578 
579 void
580 __ops_keydata_reader_set(__ops_stream_t *stream, const __ops_key_t *key)
581 {
582 	validate_reader_t *data;
583 
584 	if ((data = calloc(1, sizeof(*data))) == NULL) {
585 		(void) fprintf(stderr, "__ops_keydata_reader_set: bad alloc\n");
586 	} else {
587 		data->key = key;
588 		data->packet = 0;
589 		data->offset = 0;
590 		__ops_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, __ops_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 __ops_validate_result_free()
681  */
682 unsigned
683 __ops_validate_key_sigs(__ops_validation_t *result,
684 	const __ops_key_t *key,
685 	const __ops_keyring_t *keyring,
686 	__ops_cb_ret_t cb_get_passphrase(const __ops_packet_t *,
687 						__ops_cbdata_t *))
688 {
689 	__ops_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 = __ops_new(sizeof(*stream));
698 	/* __ops_parse_options(&opt,OPS_PTAG_CT_SIGNATURE,OPS_PARSE_PARSED); */
699 
700 	keysigs.keyring = keyring;
701 
702 	__ops_set_callback(stream, __ops_validate_key_cb, &keysigs);
703 	stream->readinfo.accumulate = 1;
704 	__ops_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 	__ops_parse(stream, !printerrors);
711 
712 	__ops_pubkey_free(&keysigs.pubkey);
713 	if (keysigs.subkey.version) {
714 		__ops_pubkey_free(&keysigs.subkey);
715 	}
716 	__ops_userid_free(&keysigs.userid);
717 	__ops_userattr_free(&keysigs.userattr);
718 
719 	__ops_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 __ops_validate_result_free()
731 */
732 unsigned
733 __ops_validate_all_sigs(__ops_validation_t *result,
734 	    const __ops_keyring_t *ring,
735 	    __ops_cb_ret_t cb_get_passphrase(const __ops_packet_t *,
736 	    					__ops_cbdata_t *))
737 {
738 	unsigned	n;
739 
740 	(void) memset(result, 0x0, sizeof(*result));
741 	for (n = 0; n < ring->keyc; ++n) {
742 		__ops_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 __ops_validate_result_free(__ops_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    	__ops_validate_result_free(result) after use.
785 */
786 unsigned
787 __ops_validate_file(__ops_io_t *io,
788 			__ops_validation_t *result,
789 			const char *infile,
790 			const char *outfile,
791 			const int user_says_armoured,
792 			const __ops_keyring_t *keyring)
793 {
794 	validate_data_cb_t	 validation;
795 	__ops_stream_t		*parse = NULL;
796 	struct stat		 st;
797 	const int		 printerrors = 1;
798 	unsigned		 ret;
799 	int64_t		 	 sigsize;
800 	char			 origfile[MAXPATHLEN];
801 	char			*detachname;
802 	int			 realarmour;
803 	int			 outfd = 0;
804 	int			 infd;
805 	int			 cc;
806 
807 #define SIG_OVERHEAD	284 /* XXX - depends on sig size? */
808 
809 	realarmour = user_says_armoured;
810 	if (stat(infile, &st) < 0) {
811 		(void) fprintf(io->errs, "can't validate \"%s\"\n", infile);
812 		return 0;
813 	}
814 	sigsize = st.st_size;
815 	detachname = NULL;
816 	cc = snprintf(origfile, sizeof(origfile), "%s", infile);
817 	if (strcmp(&origfile[cc - 4], ".sig") == 0) {
818 		origfile[cc - 4] = 0x0;
819 		if (stat(origfile, &st) == 0 &&
820 		    st.st_size > sigsize - SIG_OVERHEAD) {
821 			detachname = netpgp_strdup(origfile);
822 		}
823 	}
824 	if (strcmp(&origfile[cc - 4], ".asc") == 0) {
825 		realarmour = 1;
826 	}
827 
828 	(void) memset(&validation, 0x0, sizeof(validation));
829 
830 	infd = __ops_setup_file_read(io, &parse, infile, &validation,
831 				validate_data_cb, 1);
832 	if (infd < 0) {
833 		free(detachname);
834 		return 0;
835 	}
836 
837 	validation.detachname = detachname;
838 
839 	/* Set verification reader and handling options */
840 	validation.result = result;
841 	validation.keyring = keyring;
842 	validation.mem = __ops_memory_new();
843 	__ops_memory_init(validation.mem, 128);
844 	/* Note: Coverity incorrectly reports an error that validation.reader */
845 	/* is never used. */
846 	validation.reader = parse->readinfo.arg;
847 
848 	if (realarmour) {
849 		__ops_reader_push_dearmour(parse);
850 	}
851 
852 	/* Do the verification */
853 	__ops_parse(parse, !printerrors);
854 
855 	/* Tidy up */
856 	if (realarmour) {
857 		__ops_reader_pop_dearmour(parse);
858 	}
859 	__ops_teardown_file_read(parse, infd);
860 
861 	ret = validate_result_status(io->errs, infile, result);
862 
863 	/* this is triggered only for --cat output */
864 	if (outfile) {
865 		/* need to send validated output somewhere */
866 		if (strcmp(outfile, "-") == 0) {
867 			outfd = STDOUT_FILENO;
868 		} else {
869 			outfd = open(outfile, O_WRONLY | O_CREAT, 0666);
870 		}
871 		if (outfd < 0) {
872 			/* even if the signature was good, we can't
873 			* write the file, so send back a bad return
874 			* code */
875 			ret = 0;
876 		} else if (validate_result_status(io->errs, infile, result)) {
877 			unsigned	 len;
878 			char		*cp;
879 			int		 i;
880 
881 			len = __ops_mem_len(validation.mem);
882 			cp = __ops_mem_data(validation.mem);
883 			for (i = 0 ; i < (int)len ; i += cc) {
884 				cc = write(outfd, &cp[i], len - i);
885 				if (cc < 0) {
886 					(void) fprintf(io->errs,
887 						"netpgp: short write\n");
888 					ret = 0;
889 					break;
890 				}
891 			}
892 			if (strcmp(outfile, "-") != 0) {
893 				(void) close(outfd);
894 			}
895 		}
896 	}
897 	__ops_memory_free(validation.mem);
898 	return ret;
899 }
900 
901 /**
902    \ingroup HighLevel_Verify
903    \brief Verifies the signatures in a __ops_memory_t struct
904    \param result Where to put the result
905    \param mem Memory to be validated
906    \param user_says_armoured Treat data as armoured, if set
907    \param keyring Keyring to use
908    \return 1 if signature validates successfully; 0 if not
909    \note After verification, result holds the details of all keys which
910    have passed, failed and not been recognised.
911    \note It is the caller's responsiblity to call
912    	__ops_validate_result_free(result) after use.
913 */
914 
915 unsigned
916 __ops_validate_mem(__ops_io_t *io,
917 			__ops_validation_t *result,
918 			__ops_memory_t *mem,
919 			__ops_memory_t **cat,
920 			const int user_says_armoured,
921 			const __ops_keyring_t *keyring)
922 {
923 	validate_data_cb_t	 validation;
924 	__ops_stream_t		*stream = NULL;
925 	const int		 printerrors = 1;
926 	int			 realarmour;
927 
928 	__ops_setup_memory_read(io, &stream, mem, &validation, validate_data_cb, 1);
929 	/* Set verification reader and handling options */
930 	(void) memset(&validation, 0x0, sizeof(validation));
931 	validation.result = result;
932 	validation.keyring = keyring;
933 	validation.mem = __ops_memory_new();
934 	__ops_memory_init(validation.mem, 128);
935 	/* Note: Coverity incorrectly reports an error that validation.reader */
936 	/* is never used. */
937 	validation.reader = stream->readinfo.arg;
938 
939 	if ((realarmour = user_says_armoured) != 0 ||
940 	    strncmp(__ops_mem_data(mem),
941 	    		"-----BEGIN PGP MESSAGE-----", 27) == 0) {
942 		realarmour = 1;
943 	}
944 	if (realarmour) {
945 		__ops_reader_push_dearmour(stream);
946 	}
947 
948 	/* Do the verification */
949 	__ops_parse(stream, !printerrors);
950 
951 	/* Tidy up */
952 	if (realarmour) {
953 		__ops_reader_pop_dearmour(stream);
954 	}
955 	__ops_teardown_memory_read(stream, mem);
956 
957 	/* this is triggered only for --cat output */
958 	if (cat) {
959 		/* need to send validated output somewhere */
960 		*cat = validation.mem;
961 	} else {
962 		__ops_memory_free(validation.mem);
963 	}
964 
965 	return validate_result_status(io->errs, NULL, result);
966 }
967