xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/validate.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
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.39 2010/08/15 16:36:24 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 += (unsigned)length;
123 
124 	return (int)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 			hexdump(stderr, "v4 hash", sig->info.v4_hashed,
208 					sig->info.v4_hashlen);
209 		}
210 		hash.add(&hash, sig->info.v4_hashed, (unsigned)sig->info.v4_hashlen);
211 		trailer[0] = 0x04;	/* version */
212 		trailer[1] = 0xFF;
213 		hashedlen = (unsigned)sig->info.v4_hashlen;
214 		trailer[2] = (uint8_t)(hashedlen >> 24);
215 		trailer[3] = (uint8_t)(hashedlen >> 16);
216 		trailer[4] = (uint8_t)(hashedlen >> 8);
217 		trailer[5] = (uint8_t)(hashedlen);
218 		hash.add(&hash, trailer, 6);
219 		break;
220 
221 	default:
222 		(void) fprintf(stderr, "Invalid signature version %d\n",
223 				sig->info.version);
224 		return 0;
225 	}
226 
227 	n = hash.finish(&hash, hashout);
228 	if (__ops_get_debug_level(__FILE__)) {
229 		hexdump(stdout, "hash out", hashout, n);
230 	}
231 	return __ops_check_sig(hashout, n, sig, signer);
232 }
233 
234 __ops_cb_ret_t
235 __ops_validate_key_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
236 {
237 	const __ops_contents_t	 *content = &pkt->u;
238 	const __ops_key_t	 *signer;
239 	validate_key_cb_t	 *key;
240 	__ops_pubkey_t		 *sigkey;
241 	__ops_error_t		**errors;
242 	__ops_io_t		 *io;
243 	unsigned		  from;
244 	unsigned		  valid = 0;
245 
246 	io = cbinfo->io;
247 	if (__ops_get_debug_level(__FILE__)) {
248 		(void) fprintf(io->errs, "%s\n",
249 				__ops_show_packet_tag(pkt->tag));
250 	}
251 	key = __ops_callback_arg(cbinfo);
252 	errors = __ops_callback_errors(cbinfo);
253 	switch (pkt->tag) {
254 	case OPS_PTAG_CT_PUBLIC_KEY:
255 		if (key->pubkey.version != 0) {
256 			(void) fprintf(io->errs,
257 				"__ops_validate_key_cb: version bad\n");
258 			return OPS_FINISHED;
259 		}
260 		key->pubkey = content->pubkey;
261 		return OPS_KEEP_MEMORY;
262 
263 	case OPS_PTAG_CT_PUBLIC_SUBKEY:
264 		if (key->subkey.version) {
265 			__ops_pubkey_free(&key->subkey);
266 		}
267 		key->subkey = content->pubkey;
268 		return OPS_KEEP_MEMORY;
269 
270 	case OPS_PTAG_CT_SECRET_KEY:
271 		key->seckey = content->seckey;
272 		key->pubkey = key->seckey.pubkey;
273 		return OPS_KEEP_MEMORY;
274 
275 	case OPS_PTAG_CT_USER_ID:
276 		if (key->userid) {
277 			__ops_userid_free(&key->userid);
278 		}
279 		key->userid = content->userid;
280 		key->last_seen = ID;
281 		return OPS_KEEP_MEMORY;
282 
283 	case OPS_PTAG_CT_USER_ATTR:
284 		if (content->userattr.len == 0) {
285 			(void) fprintf(io->errs,
286 			"__ops_validate_key_cb: user attribute length 0");
287 			return OPS_FINISHED;
288 		}
289 		(void) fprintf(io->outs, "user attribute, length=%d\n",
290 			(int) content->userattr.len);
291 		if (key->userattr.len) {
292 			__ops_data_free(&key->userattr);
293 		}
294 		key->userattr = content->userattr;
295 		key->last_seen = ATTRIBUTE;
296 		return OPS_KEEP_MEMORY;
297 
298 	case OPS_PTAG_CT_SIGNATURE:	/* V3 sigs */
299 	case OPS_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
300 		from = 0;
301 		signer = __ops_getkeybyid(io, key->keyring,
302 					 content->sig.info.signer_id,
303 					 &from, &sigkey);
304 		if (!signer) {
305 			if (!add_sig_to_list(&content->sig.info,
306 				&key->result->unknown_sigs,
307 				&key->result->unknownc)) {
308 					(void) fprintf(io->errs,
309 					"__ops_validate_key_cb: user attribute length 0");
310 					return OPS_FINISHED;
311 			}
312 			break;
313 		}
314 		if (sigkey == &signer->enckey) {
315 			(void) fprintf(io->errs,
316 				"WARNING: signature made with encryption key\n");
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 			break;
351 
352 		case OPS_SIG_DIRECT:
353 			valid = __ops_check_direct_sig(&key->pubkey,
354 				&content->sig,
355 				__ops_get_pubkey(signer),
356 				key->reader->key->packets[
357 					key->reader->packet].raw);
358 			break;
359 
360 		case OPS_SIG_STANDALONE:
361 		case OPS_SIG_PRIMARY:
362 		case OPS_SIG_REV_KEY:
363 		case OPS_SIG_REV_SUBKEY:
364 		case OPS_SIG_TIMESTAMP:
365 		case OPS_SIG_3RD_PARTY:
366 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
367 				"Sig Verification type 0x%02x not done yet\n",
368 				content->sig.info.type);
369 			break;
370 
371 		default:
372 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
373 				    "Unexpected signature type 0x%02x\n",
374 				    	content->sig.info.type);
375 		}
376 
377 		if (valid) {
378 			if (!add_sig_to_list(&content->sig.info,
379 				&key->result->valid_sigs,
380 				&key->result->validc)) {
381 				OPS_ERROR(errors, OPS_E_UNIMPLEMENTED,
382 				    "Can't add good sig to list\n");
383 			}
384 		} else {
385 			OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE, "Bad Sig");
386 			if (!add_sig_to_list(&content->sig.info,
387 				&key->result->invalid_sigs,
388 				&key->result->invalidc)) {
389 				OPS_ERROR(errors, OPS_E_UNIMPLEMENTED,
390 				    "Can't add good sig to list\n");
391 			}
392 		}
393 		break;
394 
395 		/* ignore these */
396 	case OPS_PARSER_PTAG:
397 	case OPS_PTAG_CT_SIGNATURE_HEADER:
398 	case OPS_PARSER_PACKET_END:
399 		break;
400 
401 	case OPS_GET_PASSPHRASE:
402 		if (key->getpassphrase) {
403 			return key->getpassphrase(pkt, cbinfo);
404 		}
405 		break;
406 
407 	case OPS_PTAG_CT_TRUST:
408 		/* 1 byte for level (depth), 1 byte for trust amount */
409 		printf("trust dump\n");
410 		printf("Got trust\n");
411 		//hexdump(stdout, (const uint8_t *)content->trust.data, 10, " ");
412 		//hexdump(stdout, (const uint8_t *)&content->ss_trust, 2, " ");
413 		//printf("Trust level %d, amount %d\n", key->trust.level, key->trust.amount);
414 		break;
415 
416 	default:
417 		(void) fprintf(stderr, "unexpected tag=0x%x\n", pkt->tag);
418 		return OPS_FINISHED;
419 	}
420 	return OPS_RELEASE_MEMORY;
421 }
422 
423 __ops_cb_ret_t
424 validate_data_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
425 {
426 	const __ops_contents_t	 *content = &pkt->u;
427 	const __ops_key_t	 *signer;
428 	validate_data_cb_t	 *data;
429 	__ops_pubkey_t		 *sigkey;
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 			hexdump(io->outs, "hashed data", content->sig.info.v4_hashed,
476 					content->sig.info.v4_hashlen);
477 			hexdump(io->outs, "signer id", content->sig.info.signer_id,
478 				sizeof(content->sig.info.signer_id));
479 		}
480 		from = 0;
481 		signer = __ops_getkeybyid(io, data->keyring,
482 					 content->sig.info.signer_id, &from, &sigkey);
483 		if (!signer) {
484 			OPS_ERROR(errors, OPS_E_V_UNKNOWN_SIGNER,
485 					"Unknown Signer");
486 			if (!add_sig_to_list(&content->sig.info,
487 					&data->result->unknown_sigs,
488 					&data->result->unknownc)) {
489 				OPS_ERROR(errors, OPS_E_V_UNKNOWN_SIGNER,
490 					"Can't add unknown sig to list");
491 			}
492 			break;
493 		}
494 		if (sigkey == &signer->enckey) {
495 			(void) fprintf(io->errs,
496 				"WARNING: signature made with encryption key\n");
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 				hexdump(stderr, "sig dump", (const uint8_t *)(const void *)&content->sig,
519 					sizeof(content->sig));
520 			}
521 			valid = check_binary_sig(__ops_mem_data(data->mem),
522 					(const unsigned)__ops_mem_len(data->mem),
523 					&content->sig,
524 					__ops_get_pubkey(signer));
525 			break;
526 
527 		default:
528 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
529 				    "No Sig Verification type 0x%02x yet\n",
530 				    content->sig.info.type);
531 			break;
532 
533 		}
534 
535 		if (valid) {
536 			if (!add_sig_to_list(&content->sig.info,
537 					&data->result->valid_sigs,
538 					&data->result->validc)) {
539 				OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE,
540 					"Can't add good sig to list");
541 			}
542 		} else {
543 			OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE,
544 					"Bad Signature");
545 			if (!add_sig_to_list(&content->sig.info,
546 					&data->result->invalid_sigs,
547 					&data->result->invalidc)) {
548 				OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE,
549 					"Can't add good sig to list");
550 			}
551 		}
552 		break;
553 
554 		/* ignore these */
555 	case OPS_PARSER_PTAG:
556 	case OPS_PTAG_CT_SIGNATURE_HEADER:
557 	case OPS_PTAG_CT_ARMOUR_HEADER:
558 	case OPS_PTAG_CT_ARMOUR_TRAILER:
559 	case OPS_PTAG_CT_1_PASS_SIG:
560 		break;
561 
562 	case OPS_PARSER_PACKET_END:
563 		break;
564 
565 	default:
566 		OPS_ERROR(errors, OPS_E_V_NO_SIGNATURE, "No signature");
567 		break;
568 	}
569 	return OPS_RELEASE_MEMORY;
570 }
571 
572 static void
573 keydata_destroyer(__ops_reader_t *readinfo)
574 {
575 	free(__ops_reader_get_arg(readinfo));
576 }
577 
578 void
579 __ops_keydata_reader_set(__ops_stream_t *stream, const __ops_key_t *key)
580 {
581 	validate_reader_t *data;
582 
583 	if ((data = calloc(1, sizeof(*data))) == NULL) {
584 		(void) fprintf(stderr, "__ops_keydata_reader_set: bad alloc\n");
585 	} else {
586 		data->key = key;
587 		data->packet = 0;
588 		data->offset = 0;
589 		__ops_reader_set(stream, keydata_reader, keydata_destroyer, data);
590 	}
591 }
592 
593 static char *
594 fmtsecs(int64_t n, char *buf, size_t size)
595 {
596 	if (n > 365 * 24 * 60 * 60) {
597 		n /= (365 * 24 * 60 * 60);
598 		(void) snprintf(buf, size, "%" PRId64 " year%s", n, (n == 1) ? "" : "s");
599 		return buf;
600 	}
601 	if (n > 30 * 24 * 60 * 60) {
602 		n /= (30 * 24 * 60 * 60);
603 		(void) snprintf(buf, size, "%" PRId64 " month%s", n, (n == 1) ? "" : "s");
604 		return buf;
605 	}
606 	if (n > 24 * 60 * 60) {
607 		n /= (24 * 60 * 60);
608 		(void) snprintf(buf, size, "%" PRId64 " day%s", n, (n == 1) ? "" : "s");
609 		return buf;
610 	}
611 	if (n > 60 * 60) {
612 		n /= (60 * 60);
613 		(void) snprintf(buf, size, "%" PRId64 " hour%s", n, (n == 1) ? "" : "s");
614 		return buf;
615 	}
616 	if (n > 60) {
617 		n /= 60;
618 		(void) snprintf(buf, size, "%" PRId64 " minute%s", n, (n == 1) ? "" : "s");
619 		return buf;
620 	}
621 	(void) snprintf(buf, size, "%" PRId64 " second%s", n, (n == 1) ? "" : "s");
622 	return buf;
623 }
624 
625 /**
626  * \ingroup HighLevel_Verify
627  * \brief Indicicates whether any errors were found
628  * \param result Validation result to check
629  * \return 0 if any invalid signatures or unknown signers
630  	or no valid signatures; else 1
631  */
632 static unsigned
633 validate_result_status(FILE *errs, const char *f, __ops_validation_t *val)
634 {
635 	time_t	now;
636 	time_t	t;
637 	char	buf[128];
638 
639 	now = time(NULL);
640 	if (now < val->birthtime) {
641 		/* signature is not valid yet! */
642 		if (f) {
643 			(void) fprintf(errs, "\"%s\": ", f);
644 		} else {
645 			(void) fprintf(errs, "memory ");
646 		}
647 		(void) fprintf(errs,
648 			"signature not valid until %.24s (%s)\n",
649 			ctime(&val->birthtime),
650 			fmtsecs((int64_t)(val->birthtime - now), buf, sizeof(buf)));
651 		return 0;
652 	}
653 	if (val->duration != 0 && now > val->birthtime + val->duration) {
654 		/* signature has expired */
655 		t = val->duration + val->birthtime;
656 		if (f) {
657 			(void) fprintf(errs, "\"%s\": ", f);
658 		} else {
659 			(void) fprintf(errs, "memory ");
660 		}
661 		(void) fprintf(errs,
662 			"signature not valid after %.24s (%s ago)\n",
663 			ctime(&t),
664 			fmtsecs((int64_t)(now - t), buf, sizeof(buf)));
665 		return 0;
666 	}
667 	return val->validc && !val->invalidc && !val->unknownc;
668 }
669 
670 /**
671  * \ingroup HighLevel_Verify
672  * \brief Validate all signatures on a single key against the given keyring
673  * \param result Where to put the result
674  * \param key Key to validate
675  * \param keyring Keyring to use for validation
676  * \param cb_get_passphrase Callback to use to get passphrase
677  * \return 1 if all signatures OK; else 0
678  * \note It is the caller's responsiblity to free result after use.
679  * \sa __ops_validate_result_free()
680  */
681 unsigned
682 __ops_validate_key_sigs(__ops_validation_t *result,
683 	const __ops_key_t *key,
684 	const __ops_keyring_t *keyring,
685 	__ops_cb_ret_t cb_get_passphrase(const __ops_packet_t *,
686 						__ops_cbdata_t *))
687 {
688 	__ops_stream_t	*stream;
689 	validate_key_cb_t	 keysigs;
690 	const int		 printerrors = 1;
691 
692 	(void) memset(&keysigs, 0x0, sizeof(keysigs));
693 	keysigs.result = result;
694 	keysigs.getpassphrase = cb_get_passphrase;
695 
696 	stream = __ops_new(sizeof(*stream));
697 	/* __ops_parse_options(&opt,OPS_PTAG_CT_SIGNATURE,OPS_PARSE_PARSED); */
698 
699 	keysigs.keyring = keyring;
700 
701 	__ops_set_callback(stream, __ops_validate_key_cb, &keysigs);
702 	stream->readinfo.accumulate = 1;
703 	__ops_keydata_reader_set(stream, key);
704 
705 	/* Note: Coverity incorrectly reports an error that keysigs.reader */
706 	/* is never used. */
707 	keysigs.reader = stream->readinfo.arg;
708 
709 	__ops_parse(stream, !printerrors);
710 
711 	__ops_pubkey_free(&keysigs.pubkey);
712 	if (keysigs.subkey.version) {
713 		__ops_pubkey_free(&keysigs.subkey);
714 	}
715 	__ops_userid_free(&keysigs.userid);
716 	__ops_data_free(&keysigs.userattr);
717 
718 	__ops_stream_delete(stream);
719 
720 	return (!result->invalidc && !result->unknownc && result->validc);
721 }
722 
723 /**
724    \ingroup HighLevel_Verify
725    \param result Where to put the result
726    \param ring Keyring to use
727    \param cb_get_passphrase Callback to use to get passphrase
728    \note It is the caller's responsibility to free result after use.
729    \sa __ops_validate_result_free()
730 */
731 unsigned
732 __ops_validate_all_sigs(__ops_validation_t *result,
733 	    const __ops_keyring_t *ring,
734 	    __ops_cb_ret_t cb_get_passphrase(const __ops_packet_t *,
735 	    					__ops_cbdata_t *))
736 {
737 	unsigned	n;
738 
739 	(void) memset(result, 0x0, sizeof(*result));
740 	for (n = 0; n < ring->keyc; ++n) {
741 		__ops_validate_key_sigs(result, &ring->keys[n], ring,
742 				cb_get_passphrase);
743 	}
744 	return validate_result_status(stderr, "keyring", result);
745 }
746 
747 /**
748    \ingroup HighLevel_Verify
749    \brief Frees validation result and associated memory
750    \param result Struct to be freed
751    \note Must be called after validation functions
752 */
753 void
754 __ops_validate_result_free(__ops_validation_t *result)
755 {
756 	if (result != NULL) {
757 		if (result->valid_sigs) {
758 			free_sig_info(result->valid_sigs);
759 		}
760 		if (result->invalid_sigs) {
761 			free_sig_info(result->invalid_sigs);
762 		}
763 		if (result->unknown_sigs) {
764 			free_sig_info(result->unknown_sigs);
765 		}
766 		free(result);
767 		/* result = NULL; - XXX unnecessary */
768 	}
769 }
770 
771 /**
772    \ingroup HighLevel_Verify
773    \brief Verifies the signatures in a signed file
774    \param result Where to put the result
775    \param filename Name of file to be validated
776    \param armoured Treat file as armoured, if set
777    \param keyring Keyring to use
778    \return 1 if signatures validate successfully;
779    	0 if signatures fail or there are no signatures
780    \note After verification, result holds the details of all keys which
781    have passed, failed and not been recognised.
782    \note It is the caller's responsiblity to call
783    	__ops_validate_result_free(result) after use.
784 */
785 unsigned
786 __ops_validate_file(__ops_io_t *io,
787 			__ops_validation_t *result,
788 			const char *infile,
789 			const char *outfile,
790 			const int user_says_armoured,
791 			const __ops_keyring_t *keyring)
792 {
793 	validate_data_cb_t	 validation;
794 	__ops_stream_t		*parse = NULL;
795 	struct stat		 st;
796 	const char		*signame;
797 	const int		 printerrors = 1;
798 	unsigned		 ret;
799 	char			 f[MAXPATHLEN];
800 	char			*dataname;
801 	int			 realarmour;
802 	int			 outfd = 0;
803 	int			 infd;
804 	int			 cc;
805 
806 	if (stat(infile, &st) < 0) {
807 		(void) fprintf(io->errs,
808 			"__ops_validate_file: can't open '%s'\n", infile);
809 		return 0;
810 	}
811 	realarmour = user_says_armoured;
812 	dataname = NULL;
813 	signame = NULL;
814 	cc = snprintf(f, sizeof(f), "%s", infile);
815 	if (strcmp(&f[cc - 4], ".sig") == 0) {
816 		/* we've been given a sigfile as infile */
817 		f[cc - 4] = 0x0;
818 		/* set dataname to name of file which was signed */
819 		dataname = f;
820 		signame = infile;
821 	} else if (strcmp(&f[cc - 4], ".asc") == 0) {
822 		/* we've been given an armored sigfile as infile */
823 		f[cc - 4] = 0x0;
824 		/* set dataname to name of file which was signed */
825 		dataname = f;
826 		signame = infile;
827 		realarmour = 1;
828 	} else {
829 		signame = infile;
830 	}
831 	(void) memset(&validation, 0x0, sizeof(validation));
832 	infd = __ops_setup_file_read(io, &parse, signame, &validation,
833 				validate_data_cb, 1);
834 	if (infd < 0) {
835 		return 0;
836 	}
837 
838 	if (dataname) {
839 		validation.detachname = netpgp_strdup(dataname);
840 	}
841 
842 	/* Set verification reader and handling options */
843 	validation.result = result;
844 	validation.keyring = keyring;
845 	validation.mem = __ops_memory_new();
846 	__ops_memory_init(validation.mem, 128);
847 	/* Note: Coverity incorrectly reports an error that validation.reader */
848 	/* is never used. */
849 	validation.reader = parse->readinfo.arg;
850 
851 	if (realarmour) {
852 		__ops_reader_push_dearmour(parse);
853 	}
854 
855 	/* Do the verification */
856 	__ops_parse(parse, !printerrors);
857 
858 	/* Tidy up */
859 	if (realarmour) {
860 		__ops_reader_pop_dearmour(parse);
861 	}
862 	__ops_teardown_file_read(parse, infd);
863 
864 	ret = validate_result_status(io->errs, infile, result);
865 
866 	/* this is triggered only for --cat output */
867 	if (outfile) {
868 		/* need to send validated output somewhere */
869 		if (strcmp(outfile, "-") == 0) {
870 			outfd = STDOUT_FILENO;
871 		} else {
872 			outfd = open(outfile, O_WRONLY | O_CREAT, 0666);
873 		}
874 		if (outfd < 0) {
875 			/* even if the signature was good, we can't
876 			* write the file, so send back a bad return
877 			* code */
878 			ret = 0;
879 		} else if (validate_result_status(io->errs, infile, result)) {
880 			unsigned	 len;
881 			char		*cp;
882 			int		 i;
883 
884 			len = (unsigned)__ops_mem_len(validation.mem);
885 			cp = __ops_mem_data(validation.mem);
886 			for (i = 0 ; i < (int)len ; i += cc) {
887 				cc = (int)write(outfd, &cp[i], (unsigned)(len - i));
888 				if (cc < 0) {
889 					(void) fprintf(io->errs,
890 						"netpgp: short write\n");
891 					ret = 0;
892 					break;
893 				}
894 			}
895 			if (strcmp(outfile, "-") != 0) {
896 				(void) close(outfd);
897 			}
898 		}
899 	}
900 	__ops_memory_free(validation.mem);
901 	return ret;
902 }
903 
904 /**
905    \ingroup HighLevel_Verify
906    \brief Verifies the signatures in a __ops_memory_t struct
907    \param result Where to put the result
908    \param mem Memory to be validated
909    \param user_says_armoured Treat data as armoured, if set
910    \param keyring Keyring to use
911    \return 1 if signature validates successfully; 0 if not
912    \note After verification, result holds the details of all keys which
913    have passed, failed and not been recognised.
914    \note It is the caller's responsiblity to call
915    	__ops_validate_result_free(result) after use.
916 */
917 
918 unsigned
919 __ops_validate_mem(__ops_io_t *io,
920 			__ops_validation_t *result,
921 			__ops_memory_t *mem,
922 			__ops_memory_t **cat,
923 			const int user_says_armoured,
924 			const __ops_keyring_t *keyring)
925 {
926 	validate_data_cb_t	 validation;
927 	__ops_stream_t		*stream = NULL;
928 	const int		 printerrors = 1;
929 	int			 realarmour;
930 
931 	__ops_setup_memory_read(io, &stream, mem, &validation, validate_data_cb, 1);
932 	/* Set verification reader and handling options */
933 	(void) memset(&validation, 0x0, sizeof(validation));
934 	validation.result = result;
935 	validation.keyring = keyring;
936 	validation.mem = __ops_memory_new();
937 	__ops_memory_init(validation.mem, 128);
938 	/* Note: Coverity incorrectly reports an error that validation.reader */
939 	/* is never used. */
940 	validation.reader = stream->readinfo.arg;
941 
942 	if ((realarmour = user_says_armoured) != 0 ||
943 	    strncmp(__ops_mem_data(mem),
944 	    		"-----BEGIN PGP MESSAGE-----", 27) == 0) {
945 		realarmour = 1;
946 	}
947 	if (realarmour) {
948 		__ops_reader_push_dearmour(stream);
949 	}
950 
951 	/* Do the verification */
952 	__ops_parse(stream, !printerrors);
953 
954 	/* Tidy up */
955 	if (realarmour) {
956 		__ops_reader_pop_dearmour(stream);
957 	}
958 	__ops_teardown_memory_read(stream, mem);
959 
960 	/* this is triggered only for --cat output */
961 	if (cat) {
962 		/* need to send validated output somewhere */
963 		*cat = validation.mem;
964 	} else {
965 		__ops_memory_free(validation.mem);
966 	}
967 
968 	return validate_result_status(io->errs, NULL, result);
969 }
970