xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/validate.c (revision 7f21db1c0118155e0dd40b75182e30c589d9f63e)
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.28 2010/02/08 17:19:12 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 unsigned char *data,
179 		const unsigned len,
180 		const __ops_sig_t *sig,
181 		const __ops_pubkey_t *signer)
182 {
183 	unsigned char   hashout[OPS_MAX_HASH_SIZE];
184 	unsigned char   trailer[6];
185 	unsigned int    hashedlen;
186 	__ops_hash_t	hash;
187 	unsigned	n;
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] = (unsigned char)(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 unsigned char *)content->trust.data, 10, " ");
413 		//hexdump(stdout, (const unsigned char *)&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 unsigned char *)(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 		(void) snprintf(buf, size, "%" PRId64 " years", n / (365 * 24 * 60 * 60));
599 		return buf;
600 	}
601 	if (n > 30 * 24 * 60 * 60) {
602 		(void) snprintf(buf, size, "%" PRId64 " months", n / (30 * 24 * 60 * 60));
603 		return buf;
604 	}
605 	if (n > 24 * 60 * 60) {
606 		(void) snprintf(buf, size, "%" PRId64 " days", n / (24 * 60 * 60));
607 		return buf;
608 	}
609 	if (n > 60 * 60) {
610 		(void) snprintf(buf, size, "%" PRId64 " hours", n / (60 * 60));
611 		return buf;
612 	}
613 	if (n > 60) {
614 		(void) snprintf(buf, size, "%" PRId64 " minutes", n / 60);
615 		return buf;
616 	}
617 	(void) snprintf(buf, size, "%" PRId64 " seconds", n);
618 	return buf;
619 }
620 
621 /**
622  * \ingroup HighLevel_Verify
623  * \brief Indicicates whether any errors were found
624  * \param result Validation result to check
625  * \return 0 if any invalid signatures or unknown signers
626  	or no valid signatures; else 1
627  */
628 static unsigned
629 validate_result_status(FILE *errs, const char *f, __ops_validation_t *val)
630 {
631 	time_t	now;
632 	time_t	t;
633 	char	buf[128];
634 
635 	now = time(NULL);
636 	if (now < val->birthtime) {
637 		/* signature is not valid yet! */
638 		if (f) {
639 			(void) fprintf(errs, "\"%s\": ", f);
640 		} else {
641 			(void) fprintf(errs, "memory ");
642 		}
643 		(void) fprintf(errs,
644 			"signature not valid until %.24s (%s)\n",
645 			ctime(&val->birthtime),
646 			fmtsecs((int64_t)(val->birthtime - now), buf, sizeof(buf)));
647 		return 0;
648 	}
649 	if (val->duration != 0 && now > val->birthtime + val->duration) {
650 		/* signature has expired */
651 		t = val->duration + val->birthtime;
652 		if (f) {
653 			(void) fprintf(errs, "\"%s\": ", f);
654 		} else {
655 			(void) fprintf(errs, "memory ");
656 		}
657 		(void) fprintf(errs,
658 			"signature not valid after %.24s (%s ago)\n",
659 			ctime(&t),
660 			fmtsecs((int64_t)(now - t), buf, sizeof(buf)));
661 		return 0;
662 	}
663 	return val->validc && !val->invalidc && !val->unknownc;
664 }
665 
666 /**
667  * \ingroup HighLevel_Verify
668  * \brief Validate all signatures on a single key against the given keyring
669  * \param result Where to put the result
670  * \param key Key to validate
671  * \param keyring Keyring to use for validation
672  * \param cb_get_passphrase Callback to use to get passphrase
673  * \return 1 if all signatures OK; else 0
674  * \note It is the caller's responsiblity to free result after use.
675  * \sa __ops_validate_result_free()
676  */
677 unsigned
678 __ops_validate_key_sigs(__ops_validation_t *result,
679 	const __ops_key_t *key,
680 	const __ops_keyring_t *keyring,
681 	__ops_cb_ret_t cb_get_passphrase(const __ops_packet_t *,
682 						__ops_cbdata_t *))
683 {
684 	__ops_stream_t	*stream;
685 	validate_key_cb_t	 keysigs;
686 	const int		 printerrors = 1;
687 
688 	(void) memset(&keysigs, 0x0, sizeof(keysigs));
689 	keysigs.result = result;
690 	keysigs.getpassphrase = cb_get_passphrase;
691 
692 	stream = __ops_new(sizeof(*stream));
693 	/* __ops_parse_options(&opt,OPS_PTAG_CT_SIGNATURE,OPS_PARSE_PARSED); */
694 
695 	keysigs.keyring = keyring;
696 
697 	__ops_set_callback(stream, __ops_validate_key_cb, &keysigs);
698 	stream->readinfo.accumulate = 1;
699 	__ops_keydata_reader_set(stream, key);
700 
701 	/* Note: Coverity incorrectly reports an error that keysigs.reader */
702 	/* is never used. */
703 	keysigs.reader = stream->readinfo.arg;
704 
705 	__ops_parse(stream, !printerrors);
706 
707 	__ops_pubkey_free(&keysigs.pubkey);
708 	if (keysigs.subkey.version) {
709 		__ops_pubkey_free(&keysigs.subkey);
710 	}
711 	__ops_userid_free(&keysigs.userid);
712 	__ops_userattr_free(&keysigs.userattr);
713 
714 	__ops_stream_delete(stream);
715 
716 	return (!result->invalidc && !result->unknownc && result->validc);
717 }
718 
719 /**
720    \ingroup HighLevel_Verify
721    \param result Where to put the result
722    \param ring Keyring to use
723    \param cb_get_passphrase Callback to use to get passphrase
724    \note It is the caller's responsibility to free result after use.
725    \sa __ops_validate_result_free()
726 */
727 unsigned
728 __ops_validate_all_sigs(__ops_validation_t *result,
729 	    const __ops_keyring_t *ring,
730 	    __ops_cb_ret_t cb_get_passphrase(const __ops_packet_t *,
731 	    					__ops_cbdata_t *))
732 {
733 	unsigned	n;
734 
735 	(void) memset(result, 0x0, sizeof(*result));
736 	for (n = 0; n < ring->keyc; ++n) {
737 		__ops_validate_key_sigs(result, &ring->keys[n], ring,
738 				cb_get_passphrase);
739 	}
740 	return validate_result_status(stderr, "keyring", result);
741 }
742 
743 /**
744    \ingroup HighLevel_Verify
745    \brief Frees validation result and associated memory
746    \param result Struct to be freed
747    \note Must be called after validation functions
748 */
749 void
750 __ops_validate_result_free(__ops_validation_t *result)
751 {
752 	if (result != NULL) {
753 		if (result->valid_sigs) {
754 			free_sig_info(result->valid_sigs);
755 		}
756 		if (result->invalid_sigs) {
757 			free_sig_info(result->invalid_sigs);
758 		}
759 		if (result->unknown_sigs) {
760 			free_sig_info(result->unknown_sigs);
761 		}
762 		free(result);
763 		/* result = NULL; - XXX unnecessary */
764 	}
765 }
766 
767 /**
768    \ingroup HighLevel_Verify
769    \brief Verifies the signatures in a signed file
770    \param result Where to put the result
771    \param filename Name of file to be validated
772    \param armoured Treat file as armoured, if set
773    \param keyring Keyring to use
774    \return 1 if signatures validate successfully;
775    	0 if signatures fail or there are no signatures
776    \note After verification, result holds the details of all keys which
777    have passed, failed and not been recognised.
778    \note It is the caller's responsiblity to call
779    	__ops_validate_result_free(result) after use.
780 */
781 unsigned
782 __ops_validate_file(__ops_io_t *io,
783 			__ops_validation_t *result,
784 			const char *infile,
785 			const char *outfile,
786 			const int armoured,
787 			const __ops_keyring_t *keyring)
788 {
789 	validate_data_cb_t	 validation;
790 	__ops_stream_t		*parse = NULL;
791 	struct stat		 st;
792 	const int		 printerrors = 1;
793 	unsigned		 ret;
794 	int64_t		 	 sigsize;
795 	char			 origfile[MAXPATHLEN];
796 	char			*detachname;
797 	int			 realarmour;
798 	int			 outfd = 0;
799 	int			 infd;
800 	int			 cc;
801 
802 #define SIG_OVERHEAD	284 /* XXX - depends on sig size? */
803 
804 	realarmour = armoured;
805 	if (stat(infile, &st) < 0) {
806 		(void) fprintf(io->errs, "can't validate \"%s\"\n", infile);
807 		return 0;
808 	}
809 	sigsize = st.st_size;
810 	detachname = NULL;
811 	cc = snprintf(origfile, sizeof(origfile), "%s", infile);
812 	if (strcmp(&origfile[cc - 4], ".sig") == 0) {
813 		origfile[cc - 4] = 0x0;
814 		if (stat(origfile, &st) == 0 &&
815 		    st.st_size > sigsize - SIG_OVERHEAD) {
816 			detachname = strdup(origfile);
817 		}
818 	}
819 	if (strcmp(&origfile[cc - 4], ".asc") == 0) {
820 		realarmour = 1;
821 	}
822 
823 	(void) memset(&validation, 0x0, sizeof(validation));
824 
825 	infd = __ops_setup_file_read(io, &parse, infile, &validation,
826 				validate_data_cb, 1);
827 	if (infd < 0) {
828 		return 0;
829 	}
830 
831 	validation.detachname = detachname;
832 
833 	/* Set verification reader and handling options */
834 	validation.result = result;
835 	validation.keyring = keyring;
836 	validation.mem = __ops_memory_new();
837 	__ops_memory_init(validation.mem, 128);
838 	/* Note: Coverity incorrectly reports an error that validation.reader */
839 	/* is never used. */
840 	validation.reader = parse->readinfo.arg;
841 
842 	if (realarmour) {
843 		__ops_reader_push_dearmour(parse);
844 	}
845 
846 	/* Do the verification */
847 	__ops_parse(parse, !printerrors);
848 
849 	/* Tidy up */
850 	if (realarmour) {
851 		__ops_reader_pop_dearmour(parse);
852 	}
853 	__ops_teardown_file_read(parse, infd);
854 
855 	ret = validate_result_status(io->errs, infile, result);
856 
857 	/* this is triggered only for --cat output */
858 	if (outfile) {
859 		/* need to send validated output somewhere */
860 		if (strcmp(outfile, "-") == 0) {
861 			outfd = STDOUT_FILENO;
862 		} else {
863 			outfd = open(outfile, O_WRONLY | O_CREAT, 0666);
864 		}
865 		if (outfd < 0) {
866 			/* even if the signature was good, we can't
867 			* write the file, so send back a bad return
868 			* code */
869 			ret = 0;
870 		} else if (validate_result_status(io->errs, infile, result)) {
871 			unsigned	 len;
872 			char		*cp;
873 			int		 i;
874 
875 			len = __ops_mem_len(validation.mem);
876 			cp = __ops_mem_data(validation.mem);
877 			for (i = 0 ; i < (int)len ; i += cc) {
878 				cc = write(outfd, &cp[i], len - i);
879 				if (cc < 0) {
880 					(void) fprintf(io->errs,
881 						"netpgp: short write\n");
882 					ret = 0;
883 					break;
884 				}
885 			}
886 			if (strcmp(outfile, "-") != 0) {
887 				(void) close(outfd);
888 			}
889 		}
890 	}
891 	__ops_memory_free(validation.mem);
892 	return ret;
893 }
894 
895 /**
896    \ingroup HighLevel_Verify
897    \brief Verifies the signatures in a __ops_memory_t struct
898    \param result Where to put the result
899    \param mem Memory to be validated
900    \param armoured Treat data as armoured, if set
901    \param keyring Keyring to use
902    \return 1 if signature validates successfully; 0 if not
903    \note After verification, result holds the details of all keys which
904    have passed, failed and not been recognised.
905    \note It is the caller's responsiblity to call
906    	__ops_validate_result_free(result) after use.
907 */
908 
909 unsigned
910 __ops_validate_mem(__ops_io_t *io,
911 			__ops_validation_t *result,
912 			__ops_memory_t *mem,
913 			__ops_memory_t **cat,
914 			const int armoured,
915 			const __ops_keyring_t *keyring)
916 {
917 	validate_data_cb_t	 validation;
918 	__ops_stream_t		*stream = NULL;
919 	const int		 printerrors = 1;
920 
921 	__ops_setup_memory_read(io, &stream, mem, &validation, validate_data_cb, 1);
922 	/* Set verification reader and handling options */
923 	(void) memset(&validation, 0x0, sizeof(validation));
924 	validation.result = result;
925 	validation.keyring = keyring;
926 	validation.mem = __ops_memory_new();
927 	__ops_memory_init(validation.mem, 128);
928 	/* Note: Coverity incorrectly reports an error that validation.reader */
929 	/* is never used. */
930 	validation.reader = stream->readinfo.arg;
931 
932 	if (armoured) {
933 		__ops_reader_push_dearmour(stream);
934 	}
935 
936 	/* Do the verification */
937 	__ops_parse(stream, !printerrors);
938 
939 	/* Tidy up */
940 	if (armoured) {
941 		__ops_reader_pop_dearmour(stream);
942 	}
943 	__ops_teardown_memory_read(stream, mem);
944 
945 	/* this is triggered only for --cat output */
946 	if (*cat) {
947 		/* need to send validated output somewhere */
948 		*cat = validation.mem;
949 	} else {
950 		__ops_memory_free(validation.mem);
951 	}
952 
953 	return validate_result_status(io->errs, NULL, result);
954 }
955