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