Blame SOURCES/0020-try-to-say-why-something-fails.patch

360211
From 81583146602bba96728fa7544c8e856b32c22ee4 Mon Sep 17 00:00:00 2001
360211
From: Peter Jones <pjones@redhat.com>
360211
Date: Tue, 25 Apr 2017 17:01:13 -0400
360211
Subject: [PATCH 20/29] try to say why something fails
360211
360211
Signed-off-by: Peter Jones <pjones@redhat.com>
360211
---
360211
 src/certdb.c             |  15 ++-
360211
 src/certdb.h             |   2 +-
360211
 src/pesigcheck.c         | 244 ++++++++++++++++++++++++++++++++++++++++++-----
360211
 src/pesigcheck_context.h |   1 +
360211
 4 files changed, 233 insertions(+), 29 deletions(-)
360211
360211
diff --git a/src/certdb.c b/src/certdb.c
360211
index 1078a8a..fae80af 100644
360211
--- a/src/certdb.c
360211
+++ b/src/certdb.c
360211
@@ -205,7 +205,7 @@ typedef db_status (*checkfn)(pesigcheck_context *ctx, SECItem *sig,
360211
 
360211
 static db_status
360211
 check_db(db_specifier which, pesigcheck_context *ctx, checkfn check,
360211
-	 void *data, ssize_t datalen)
360211
+	 void *data, ssize_t datalen, SECItem *match)
360211
 {
360211
 	SECItem pkcs7sig, sig;
360211
 	dblist *dbl = which == DB ? ctx->db : ctx->dbx;
360211
@@ -241,8 +241,12 @@ check_db(db_specifier which, pesigcheck_context *ctx, checkfn check,
360211
 				found = check(ctx, &sig,
360211
 					      &certlist->SignatureType,
360211
 					      &pkcs7sig);
360211
-				if (found == FOUND)
360211
+				if (found == FOUND) {
360211
+					if (match)
360211
+						memcpy(match, &sig,
360211
+						       sizeof(sig));
360211
 					return FOUND;
360211
+				}
360211
 				cert = (EFI_SIGNATURE_DATA *)((uint8_t *)cert +
360211
 				        certlist->SignatureSize);
360211
 			}
360211
@@ -280,7 +284,7 @@ check_hash(pesigcheck_context *ctx, SECItem *sig, efi_guid_t *sigtype,
360211
 db_status
360211
 check_db_hash(db_specifier which, pesigcheck_context *ctx)
360211
 {
360211
-	return check_db(which, ctx, check_hash, NULL, 0);
360211
+	return check_db(which, ctx, check_hash, NULL, 0, NULL);
360211
 }
360211
 
360211
 static void
360211
@@ -459,7 +463,8 @@ out:
360211
 }
360211
 
360211
 db_status
360211
-check_db_cert(db_specifier which, pesigcheck_context *ctx, void *data, ssize_t datalen)
360211
+check_db_cert(db_specifier which, pesigcheck_context *ctx,
360211
+	      void *data, ssize_t datalen, SECItem *match)
360211
 {
360211
-	return check_db(which, ctx, check_cert, data, datalen);
360211
+	return check_db(which, ctx, check_cert, data, datalen, match);
360211
 }
360211
diff --git a/src/certdb.h b/src/certdb.h
360211
index ccf3c87..8402299 100644
360211
--- a/src/certdb.h
360211
+++ b/src/certdb.h
360211
@@ -43,7 +43,7 @@ typedef struct {
360211
 
360211
 extern db_status check_db_hash(db_specifier which, pesigcheck_context *ctx);
360211
 extern db_status check_db_cert(db_specifier which, pesigcheck_context *ctx,
360211
-				void *data, ssize_t datalen);
360211
+				void *data, ssize_t datalen, SECItem *match);
360211
 
360211
 extern void init_cert_db(pesigcheck_context *ctx, int use_system_dbs);
360211
 extern int add_cert_db(pesigcheck_context *ctx, const char *filename);
360211
diff --git a/src/pesigcheck.c b/src/pesigcheck.c
360211
index d7be542..c8e1086 100644
360211
--- a/src/pesigcheck.c
360211
+++ b/src/pesigcheck.c
360211
@@ -17,7 +17,9 @@
360211
  * Author(s): Peter Jones <pjones@redhat.com>
360211
  */
360211
 
360211
+#include <err.h>
360211
 #include <fcntl.h>
360211
+#include <stdbool.h>
360211
 #include <stdio.h>
360211
 #include <stdlib.h>
360211
 #include <string.h>
360211
@@ -88,7 +90,8 @@ check_inputs(pesigcheck_context *ctx)
360211
 }
360211
 
360211
 static int
360211
-cert_matches_digest(pesigcheck_context *ctx, void *data, ssize_t datalen)
360211
+cert_matches_digest(pesigcheck_context *ctx, void *data, ssize_t datalen,
360211
+		    SECItem *digest_out)
360211
 {
360211
 	SECItem sig, *pe_digest, *content;
360211
 	uint8_t *digest;
360211
@@ -109,6 +112,12 @@ cert_matches_digest(pesigcheck_context *ctx, void *data, ssize_t datalen)
360211
 	pe_digest = ctx->cms_ctx->digests[0].pe_digest;
360211
 	content = cinfo->content.signedData->contentInfo.content.data;
360211
 	digest = content->data + content->len - pe_digest->len;
360211
+	if (digest_out) {
360211
+		digest_out->data = malloc(pe_digest->len);
360211
+		digest_out->len = pe_digest->len;
360211
+		digest_out->type = pe_digest->type;
360211
+		memcpy(digest_out->data, digest, pe_digest->len);
360211
+	}
360211
 	if (memcmp(pe_digest->data, digest, pe_digest->len) != 0)
360211
 		goto out;
360211
 
360211
@@ -120,22 +129,149 @@ out:
360211
 	return ret;
360211
 }
360211
 
360211
+struct reason {
360211
+	enum {
360211
+		WHITELISTED = 0,
360211
+		INVALID = 1,
360211
+		BLACKLISTED = 2,
360211
+		NO_WHITELIST = 3,
360211
+	} reason;
360211
+	enum {
360211
+		NONE = 0,
360211
+		DIGEST = 1,
360211
+		SIGNATURE = 2,
360211
+	} type;
360211
+	union {
360211
+		struct {
360211
+			SECItem digest;
360211
+		};
360211
+		struct {
360211
+			SECItem sig;
360211
+			SECItem db_cert;
360211
+		};
360211
+	};
360211
+};
360211
+
360211
+static void
360211
+print_digest(SECItem *digest)
360211
+{
360211
+	char buf[digest->len * 2 + 2];
360211
+
360211
+	for (unsigned int i = 0; i < digest->len; i++)
360211
+		snprintf(buf + i * 2, digest->len * 2, "%02x",
360211
+			 digest->data[i]);
360211
+	buf[digest->len * 2] = '\0';
360211
+	printf("%s\n", buf);
360211
+}
360211
+
360211
+static void
360211
+print_certificate(SECItem *cert)
360211
+{
360211
+	printf("put a breakpoint at %s:%d\n", __FILE__, __LINE__);
360211
+	printf("cert: %p\n", cert);
360211
+}
360211
+
360211
+static void
360211
+print_signatures(SECItem *database_cert, SECItem *signature)
360211
+{
360211
+	printf("put a breakpoint at %s:%d\n", __FILE__, __LINE__);
360211
+	print_certificate(database_cert);
360211
+	print_certificate(signature);
360211
+}
360211
+
360211
+static void
360211
+print_reason(struct reason *reason)
360211
+{
360211
+	switch (reason->reason) {
360211
+	case WHITELISTED:
360211
+		printf("Whitelist entry: ");
360211
+		if (reason->type == DIGEST)
360211
+			print_digest(&reason->digest);
360211
+		else if (reason->type == SIGNATURE)
360211
+			print_signatures(&reason->sig, &reason->db_cert);
360211
+		else
360211
+			errx(1, "Unknown data type %d\n", reason->type);
360211
+		break;
360211
+	case INVALID:
360211
+		if (reason->type == DIGEST) {
360211
+			printf("Invalid digest: ");
360211
+			print_digest(&reason->digest);
360211
+		} else if (reason->type == SIGNATURE) {
360211
+			printf("Invalid signature: ");
360211
+			print_signatures(&reason->sig, &reason->db_cert);
360211
+		} else {
360211
+			errx(1, "Unknown data type %d\n", reason->type);
360211
+		}
360211
+		break;
360211
+	case BLACKLISTED:
360211
+		if (reason->type == DIGEST) {
360211
+			printf("Invalid digest: ");
360211
+			print_digest(&reason->digest);
360211
+		} else if (reason->type == SIGNATURE) {
360211
+			printf("Invalid signature: ");
360211
+			print_signatures(&reason->sig, &reason->db_cert);
360211
+		} else {
360211
+			errx(1, "Unknown data type %d\n", reason->type);
360211
+		}
360211
+		break;
360211
+	case NO_WHITELIST:
360211
+		if (reason->type == NONE)
360211
+			printf("No matching whitelist entry.\n");
360211
+		else
360211
+			errx(1, "Invalid data type %d\n", reason->type);
360211
+		break;
360211
+	default:
360211
+		errx(1, "Unknown reason type %d\n", reason->reason);
360211
+		break;
360211
+	}
360211
+}
360211
+
360211
+static void
360211
+get_digest(pesigcheck_context *ctx, SECItem *digest)
360211
+{
360211
+	struct cms_context *cms = ctx->cms_ctx;
360211
+	struct digest *cms_digest = &cms->digests[cms->selected_digest];
360211
+
360211
+	memcpy(digest, cms_digest->pe_digest, sizeof (*digest));
360211
+}
360211
+
360211
 static int
360211
-check_signature(pesigcheck_context *ctx)
360211
+check_signature(pesigcheck_context *ctx, int *nreasons,
360211
+		struct reason **reasons)
360211
 {
360211
-	int has_valid_cert = 0;
360211
-	int has_invalid_cert = 0;
360211
+	bool has_valid_cert = false;
360211
+	bool is_invalid = false;
360211
+	struct reason *reasonps = NULL, *reason;
360211
+	int num_reasons = 16;
360211
+	int nreason = 0;
360211
 	int rc = 0;
360211
+	int ret = -1;
360211
 
360211
 	cert_iter iter;
360211
 
360211
+	reasonps = calloc(sizeof(struct reason), 512);
360211
+	if (!reasonps)
360211
+		err(1, "check_signature");
360211
+
360211
 	generate_digest(ctx->cms_ctx, ctx->inpe, 1);
360211
 
360211
-	if (check_db_hash(DBX, ctx) == FOUND)
360211
-		return -1;
360211
+	if (check_db_hash(DBX, ctx) == FOUND) {
360211
+		reason = &reasonps[nreason];
360211
+		reason->reason = BLACKLISTED;
360211
+		reason->type = DIGEST;
360211
+		get_digest(ctx, &reason->digest);
360211
+		reason += 1;
360211
+		is_invalid = true;
360211
+	}
360211
 
360211
-	if (check_db_hash(DB, ctx) == FOUND)
360211
-		has_valid_cert = 1;
360211
+	if (check_db_hash(DB, ctx) == FOUND) {
360211
+		reason = &reasonps[nreason];
360211
+		reason->reason = WHITELISTED;
360211
+		reason->type = DIGEST;
360211
+		get_digest(ctx, &reason->digest);
360211
+		nreason += 1;
360211
+		has_valid_cert = true;
360211
+	}
360211
 
360211
 	rc = cert_iter_init(&iter, ctx->inpe);
360211
 	if (rc < 0)
360211
@@ -145,32 +281,81 @@ check_signature(pesigcheck_context *ctx)
360211
 	ssize_t datalen;
360211
 
360211
 	while (1) {
360211
+		/*
360211
+		 * Make sure we always have enough for this iteration of the
360211
+		 * loop, plus one "NO_WHITELIST" entry at the end.
360211
+		 */
360211
+		if (nreason >= num_reasons - 4) {
360211
+			struct reason *new_reasons;
360211
+
360211
+			num_reasons += 16;
360211
+
360211
+			new_reasons = calloc(sizeof(struct reason), num_reasons);
360211
+			if (!new_reasons)
360211
+				err(1, "check_signature");
360211
+			reasonps = new_reasons;
360211
+		}
360211
+
360211
 		rc = next_cert(&iter, &data, &datalen);
360211
 		if (rc <= 0)
360211
 			break;
360211
 
360211
-		if (cert_matches_digest(ctx, data, datalen) < 0) {
360211
-			has_invalid_cert = 1;
360211
-			break;
360211
+		reason = &reasonps[nreason];
360211
+		if (cert_matches_digest(ctx, data, datalen,
360211
+					&reason->digest) < 0) {
360211
+			reason->reason = INVALID;
360211
+			reason->type = DIGEST;
360211
+			nreason += 1;
360211
+			is_invalid = true;
360211
 		}
360211
 
360211
-		if (check_db_cert(DBX, ctx, data, datalen) == FOUND) {
360211
-			has_invalid_cert = 1;
360211
-			break;
360211
+		reason = &reasonps[nreason];
360211
+		if (check_db_cert(DBX, ctx, data, datalen,
360211
+				  &reason->db_cert) == FOUND) {
360211
+			reason->reason = INVALID;
360211
+			reason->type = SIGNATURE;
360211
+			reason->sig.data = data;
360211
+			reason->sig.len = datalen;
360211
+			reason->type = siBuffer;
360211
+			nreason += 1;
360211
+			is_invalid = true;
360211
 		}
360211
 
360211
-		if (check_db_cert(DB, ctx, data, datalen) == FOUND)
360211
-			has_valid_cert = 1;
360211
+		reason = &reasonps[nreason];
360211
+		if (check_db_cert(DB, ctx, data, datalen,
360211
+				  &reason->db_cert) == FOUND) {
360211
+			reason->reason = WHITELISTED;
360211
+			reason->type = SIGNATURE;
360211
+			reason->sig.data = data;
360211
+			reason->sig.len = datalen;
360211
+			reason->type = siBuffer;
360211
+			nreason += 1;
360211
+			has_valid_cert = true;
360211
+		}
360211
 	}
360211
 
360211
 err:
360211
-	if (has_invalid_cert)
360211
-		return -1;
360211
+	if (has_valid_cert != true) {
360211
+		if (is_invalid != true) {
360211
+			reason = &reasonps[nreason];
360211
+			reason->reason = NO_WHITELIST;
360211
+			reason->type = NONE;
360211
+			nreason += 1;
360211
+		}
360211
+		is_invalid = true;
360211
+	}
360211
 
360211
-	if (has_valid_cert)
360211
-		return 0;
360211
+	if (is_invalid == false)
360211
+		ret = 0;
360211
 
360211
-	return -1;
360211
+	if (nreasons && reasons) {
360211
+		*nreasons = nreason;
360211
+		*reasons = reasonps;
360211
+	} else {
360211
+		free(reasonps);
360211
+	}
360211
+
360211
+	return ret;
360211
 }
360211
 
360211
 void
360211
@@ -204,6 +389,9 @@ main(int argc, char *argv[])
360211
 
360211
 	pesigcheck_context ctx, *ctxp = &ctx;
360211
 
360211
+	struct reason *reasons = NULL;
360211
+	int nreasons = 0;
360211
+
360211
 	char *dbfile = NULL;
360211
 	char *dbxfile = NULL;
360211
 	char *certfile = NULL;
360211
@@ -242,6 +430,12 @@ main(int argc, char *argv[])
360211
 		 .arg = &ctx.quiet,
360211
 		 .val = 1,
360211
 		 .descrip = "return only; no text output." },
360211
+		{.longName = "verbose",
360211
+		 .shortName = 'v',
360211
+		 .argInfo = POPT_BIT_SET,
360211
+		 .arg = &ctx.verbose,
360211
+		 .val = 1,
360211
+		 .descrip = "print reasons for success and failure." },
360211
 		{.longName = "no-system-db",
360211
 		 .shortName = 'n',
360211
 		 .argInfo = POPT_ARG_INT,
360211
@@ -308,12 +502,16 @@ main(int argc, char *argv[])
360211
 		exit(1);
360211
 	}
360211
 
360211
-	rc = check_signature(ctxp);
360211
+	rc = check_signature(ctxp, &nreasons, &reasons);
360211
 
360211
-	close_input(ctxp);
360211
+	if (!ctx.quiet && ctx.verbose) {
360211
+		for (int i = 0; i < nreasons; i++)
360211
+			print_reason(&reasons[i]);
360211
+	}
360211
 	if (!ctx.quiet)
360211
 		printf("pesigcheck: \"%s\" is %s.\n", ctx.infile,
360211
 			rc >= 0 ? "valid" : "invalid");
360211
+	close_input(ctxp);
360211
 	pesigcheck_context_fini(&ctx;;
360211
 
360211
 	NSS_Shutdown();
360211
diff --git a/src/pesigcheck_context.h b/src/pesigcheck_context.h
360211
index 7b5cc89..aec415e 100644
360211
--- a/src/pesigcheck_context.h
360211
+++ b/src/pesigcheck_context.h
360211
@@ -61,6 +61,7 @@ typedef struct pesigcheck_context {
360211
 	Pe *inpe;
360211
 
360211
 	int quiet;
360211
+	int verbose;
360211
 
360211
 	hashlist *hashes;
360211
 
360211
-- 
360211
2.13.4
360211