svn commit: r257353 - in stable/10: etc etc/keys etc/mtree etc/pkg share/man/man7 usr.sbin/pkg

Bryan Drewery bdrewery at FreeBSD.org
Tue Oct 29 18:36:46 UTC 2013


Author: bdrewery (ports committer)
Date: Tue Oct 29 18:36:44 2013
New Revision: 257353
URL: http://svnweb.freebsd.org/changeset/base/257353

Log:
  MFC: r256770,r257142,r257145,r257146,r257147,r257148,
       r257149,r257150,r257158,r257159,r257164,r257168,
       r257193
  
    - Support checking signature for pkg bootstrap from remote
      and for 'pkg add ./pkg.txz'
  
    - Be verbose on where pkg is being bootstrapped from.
  
    - Add support for reading configuration files from /etc/pkg.
      For now only /etc/pkg/FreeBSD.conf is supported.
  
    - Add test package signing key fingerprint into /etc/keys/pkg/trusted.
  
    - Disable fingerprint checking by default for now as the official
      packages are not yet signed.
  
  Approved by:	bapt
  Approved by:	re (glebius)

Added:
  stable/10/etc/keys/
     - copied from r257150, head/etc/keys/
  stable/10/etc/pkg/
     - copied from r257145, head/etc/pkg/
Modified:
  stable/10/etc/Makefile
  stable/10/etc/mtree/BSD.root.dist
  stable/10/etc/pkg/FreeBSD.conf
  stable/10/share/man/man7/hier.7
  stable/10/usr.sbin/pkg/Makefile
  stable/10/usr.sbin/pkg/config.c
  stable/10/usr.sbin/pkg/config.h
  stable/10/usr.sbin/pkg/pkg.c
Directory Properties:
  stable/10/etc/   (props changed)
  stable/10/share/man/man7/   (props changed)
  stable/10/usr.sbin/pkg/   (props changed)

Modified: stable/10/etc/Makefile
==============================================================================
--- stable/10/etc/Makefile	Tue Oct 29 18:13:04 2013	(r257352)
+++ stable/10/etc/Makefile	Tue Oct 29 18:36:44 2013	(r257353)
@@ -221,7 +221,11 @@ distribution:
 	${_+_}cd ${.CURDIR}/defaults; ${MAKE} install
 	${_+_}cd ${.CURDIR}/devd; ${MAKE} install
 	${_+_}cd ${.CURDIR}/gss; ${MAKE} install
+	${_+_}cd ${.CURDIR}/keys; ${MAKE} install
 	${_+_}cd ${.CURDIR}/periodic; ${MAKE} install
+.if ${MK_PKGBOOTSTRAP} != "no"
+	${_+_}cd ${.CURDIR}/pkg; ${MAKE} install
+.endif
 	${_+_}cd ${.CURDIR}/rc.d; ${MAKE} install
 	${_+_}cd ${.CURDIR}/../gnu/usr.bin/send-pr; ${MAKE} etc-gnats-freefall
 	${_+_}cd ${.CURDIR}/../share/termcap; ${MAKE} etc-termcap

Modified: stable/10/etc/mtree/BSD.root.dist
==============================================================================
--- stable/10/etc/mtree/BSD.root.dist	Tue Oct 29 18:13:04 2013	(r257352)
+++ stable/10/etc/mtree/BSD.root.dist	Tue Oct 29 18:36:44 2013	(r257353)
@@ -34,6 +34,14 @@
         ..
         gss
         ..
+        keys
+            pkg
+                revoked
+                ..
+                trusted
+                ..
+            ..
+        ..
         mail
         ..
         mtree
@@ -52,6 +60,8 @@
             weekly
             ..
         ..
+        pkg
+        ..
         ppp
         ..
         rc.d

Modified: stable/10/etc/pkg/FreeBSD.conf
==============================================================================
--- head/etc/pkg/FreeBSD.conf	Sat Oct 26 03:31:05 2013	(r257145)
+++ stable/10/etc/pkg/FreeBSD.conf	Tue Oct 29 18:36:44 2013	(r257353)
@@ -2,5 +2,7 @@
 FreeBSD: {
   url: "pkg+http://pkg.freebsd.org/${ABI}/latest",
   mirror_type: "srv",
+  signature_type: "none",
+  fingerprints: "/etc/keys/pkg",
   enabled: "yes"
 }

Modified: stable/10/share/man/man7/hier.7
==============================================================================
--- stable/10/share/man/man7/hier.7	Tue Oct 29 18:13:04 2013	(r257352)
+++ stable/10/share/man/man7/hier.7	Tue Oct 29 18:36:44 2013	(r257353)
@@ -32,7 +32,7 @@
 .\"	@(#)hier.7	8.1 (Berkeley) 6/5/93
 .\" $FreeBSD$
 .\"
-.Dd January 21, 2010
+.Dd October 23, 2013
 .Dt HIER 7
 .Os
 .Sh NAME
@@ -94,6 +94,15 @@ bluetooth configuration files
 gnats configuration files;
 see
 .Xr send-pr 1
+.It Pa keys/
+known trusted and revoked keys.
+.Pp
+.Bl -tag -width ".Pa keys/pkg/" -compact
+.It Pa keys/pkg/
+fingerprints for
+.Xr pkg 8
+.El
+.Pp
 .It Pa localtime
 local timezone information;
 see

Modified: stable/10/usr.sbin/pkg/Makefile
==============================================================================
--- stable/10/usr.sbin/pkg/Makefile	Tue Oct 29 18:13:04 2013	(r257352)
+++ stable/10/usr.sbin/pkg/Makefile	Tue Oct 29 18:36:44 2013	(r257353)
@@ -6,8 +6,9 @@ SRCS=	pkg.c dns_utils.c config.c
 NO_MAN=	yes
 CFLAGS+=-I${.CURDIR}/../../contrib/libyaml/include
 .PATH:	${.CURDIR}/../../contrib/libyaml/include
-DPADD=	${LIBARCHIVE} ${LIBELF} ${LIBFETCH} ${LIBYAML} ${LIBSBUF}
-LDADD=	-larchive -lelf -lfetch -lyaml -lsbuf
+DPADD=	${LIBARCHIVE} ${LIBELF} ${LIBFETCH} ${LIBYAML} ${LIBSBUF} ${LIBSSL} \
+	${LIBCRYPTO}
+LDADD=	-larchive -lelf -lfetch -lyaml -lsbuf -lssl -lcrypto
 USEPRIVATELIB=	yaml
 
 .include <bsd.prog.mk>

Modified: stable/10/usr.sbin/pkg/config.c
==============================================================================
--- stable/10/usr.sbin/pkg/config.c	Tue Oct 29 18:13:04 2013	(r257352)
+++ stable/10/usr.sbin/pkg/config.c	Tue Oct 29 18:36:44 2013	(r257353)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2013 Baptiste Daroussin <bapt at FreeBSD.org>
+ * Copyright (c) 2013 Bryan Drewery <bdrewery at FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -86,7 +87,21 @@ static struct config_entry c[] = {
 		"NO",
 		NULL,
 		false,
-	}
+	},
+	[SIGNATURE_TYPE] = {
+		PKG_CONFIG_STRING,
+		"SIGNATURE_TYPE",
+		NULL,
+		NULL,
+		false,
+	},
+	[FINGERPRINTS] = {
+		PKG_CONFIG_STRING,
+		"FINGERPRINTS",
+		NULL,
+		NULL,
+		false,
+	},
 };
 
 static const char *
@@ -460,7 +475,7 @@ subst_packagesite(const char *abi)
 }
 
 static void
-config_parse(yaml_document_t *doc, yaml_node_t *node)
+config_parse(yaml_document_t *doc, yaml_node_t *node, pkg_conf_file_t conftype)
 {
 	yaml_node_pair_t *pair;
 	yaml_node_t *key, *val;
@@ -495,15 +510,39 @@ config_parse(yaml_document_t *doc, yaml_
 		}
 
 		sbuf_clear(buf);
-		for (j = 0; j < strlen(key->data.scalar.value); ++j)
-			sbuf_putc(buf, toupper(key->data.scalar.value[j]));
 
-		sbuf_finish(buf);
+		if (conftype == CONFFILE_PKG) {
+			for (j = 0; j < strlen(key->data.scalar.value); ++j)
+				sbuf_putc(buf,
+				    toupper(key->data.scalar.value[j]));
+			sbuf_finish(buf);
+		} else if (conftype == CONFFILE_REPO) {
+			/* The CONFFILE_REPO type is more restrictive. Only
+			   parse known elements. */
+			if (strcasecmp(key->data.scalar.value, "url") == 0)
+				sbuf_cpy(buf, "PACKAGESITE");
+			else if (strcasecmp(key->data.scalar.value,
+			    "mirror_type") == 0)
+				sbuf_cpy(buf, "MIRROR_TYPE");
+			else if (strcasecmp(key->data.scalar.value,
+			    "signature_type") == 0)
+				sbuf_cpy(buf, "SIGNATURE_TYPE");
+			else if (strcasecmp(key->data.scalar.value,
+			    "fingerprints") == 0)
+				sbuf_cpy(buf, "FINGERPRINTS");
+			else { /* Skip unknown entries for future use. */
+				++pair;
+				continue;
+			}
+			sbuf_finish(buf);
+		}
+
 		for (i = 0; i < CONFIG_SIZE; i++) {
 			if (strcmp(sbuf_data(buf), c[i].key) == 0)
 				break;
 		}
 
+		/* Silently skip unknown keys to be future compatible. */
 		if (i == CONFIG_SIZE) {
 			++pair;
 			continue;
@@ -522,13 +561,80 @@ config_parse(yaml_document_t *doc, yaml_
 	sbuf_delete(buf);
 }
 
-int
-config_init(void)
+/*-
+ * Parse new repo style configs in style:
+ * Name:
+ *   URL:
+ *   MIRROR_TYPE:
+ * etc...
+ */
+static void
+parse_repo_file(yaml_document_t *doc, yaml_node_t *node)
+{
+	yaml_node_pair_t *pair;
+
+	pair = node->data.mapping.pairs.start;
+	while (pair < node->data.mapping.pairs.top) {
+		yaml_node_t *key = yaml_document_get_node(doc, pair->key);
+		yaml_node_t *val = yaml_document_get_node(doc, pair->value);
+
+		if (key->data.scalar.length <= 0) {
+			++pair;
+			continue;
+		}
+
+		if (val->type != YAML_MAPPING_NODE) {
+			++pair;
+			continue;
+		}
+
+		config_parse(doc, val, CONFFILE_REPO);
+		++pair;
+	}
+}
+
+
+static int
+read_conf_file(const char *confpath, pkg_conf_file_t conftype)
 {
 	FILE *fp;
 	yaml_parser_t parser;
 	yaml_document_t doc;
 	yaml_node_t *node;
+
+	if ((fp = fopen(confpath, "r")) == NULL) {
+		if (errno != ENOENT)
+			err(EXIT_FAILURE, "Unable to open configuration "
+			    "file %s", confpath);
+		/* no configuration present */
+		return (1);
+	}
+
+	yaml_parser_initialize(&parser);
+	yaml_parser_set_input_file(&parser, fp);
+	yaml_parser_load(&parser, &doc);
+
+	node = yaml_document_get_root_node(&doc);
+
+	if (node == NULL || node->type != YAML_MAPPING_NODE)
+		warnx("Invalid configuration format, ignoring the "
+		    "configuration file %s", confpath);
+	else {
+		if (conftype == CONFFILE_PKG)
+			config_parse(&doc, node, conftype);
+		else if (conftype == CONFFILE_REPO)
+			parse_repo_file(&doc, node);
+	}
+
+	yaml_document_delete(&doc);
+	yaml_parser_delete(&parser);
+
+	return (0);
+}
+
+int
+config_init(void)
+{
 	const char *val;
 	int i;
 	const char *localbase;
@@ -544,37 +650,23 @@ config_init(void)
 	}
 
 	localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
-	snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase);
+	snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf",
+	    localbase);
 
-	if ((fp = fopen(confpath, "r")) == NULL) {
-		if (errno != ENOENT)
-			err(EXIT_FAILURE, "Unable to open configuration file %s", confpath);
-		/* no configuration present */
+	if (access(confpath, F_OK) == 0 && read_conf_file(confpath,
+	    CONFFILE_PKG))
 		goto finalize;
-	}
 
-	yaml_parser_initialize(&parser);
-	yaml_parser_set_input_file(&parser, fp);
-	yaml_parser_load(&parser, &doc);
-
-	node = yaml_document_get_root_node(&doc);
-
-	if (node != NULL) {
-		if (node->type != YAML_MAPPING_NODE)
-			warnx("Invalid configuration format, ignoring the configuration file");
-		else
-			config_parse(&doc, node);
-	} else {
-		warnx("Invalid configuration format, ignoring the configuration file");
-	}
-
-	yaml_document_delete(&doc);
-	yaml_parser_delete(&parser);
+	snprintf(confpath, sizeof(confpath), "/etc/pkg/FreeBSD.conf");
+	if (access(confpath, F_OK) == 0 && read_conf_file(confpath,
+	    CONFFILE_REPO))
+		goto finalize;
 
 finalize:
 	if (c[ABI].val == NULL && c[ABI].value == NULL) {
 		if (pkg_get_myabi(abi, BUFSIZ) != 0)
-			errx(EXIT_FAILURE, "Failed to determine the system ABI");
+			errx(EXIT_FAILURE, "Failed to determine the system "
+			    "ABI");
 		c[ABI].val = abi;
 	}
 

Modified: stable/10/usr.sbin/pkg/config.h
==============================================================================
--- stable/10/usr.sbin/pkg/config.h	Tue Oct 29 18:13:04 2013	(r257352)
+++ stable/10/usr.sbin/pkg/config.h	Tue Oct 29 18:36:44 2013	(r257353)
@@ -37,6 +37,8 @@ typedef enum {
 	ABI,
 	MIRROR_TYPE,
 	ASSUME_ALWAYS_YES,
+	SIGNATURE_TYPE,
+	FINGERPRINTS,
 	CONFIG_SIZE
 } pkg_config_key;
 
@@ -45,6 +47,11 @@ typedef enum {
 	PKG_CONFIG_BOOL,
 } pkg_config_t;
 
+typedef enum {
+	CONFFILE_PKG=0,
+	CONFFILE_REPO,
+} pkg_conf_file_t;
+
 int config_init(void);
 void config_finish(void);
 int config_string(pkg_config_key, const char **);

Modified: stable/10/usr.sbin/pkg/pkg.c
==============================================================================
--- stable/10/usr.sbin/pkg/pkg.c	Tue Oct 29 18:13:04 2013	(r257352)
+++ stable/10/usr.sbin/pkg/pkg.c	Tue Oct 29 18:36:44 2013	(r257353)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2012-2013 Baptiste Daroussin <bapt at FreeBSD.org>
+ * Copyright (c) 2013 Bryan Drewery <bdrewery at FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,10 +29,15 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/sbuf.h>
 #include <sys/wait.h>
 
+#define _WITH_GETLINE
 #include <archive.h>
 #include <archive_entry.h>
+#include <dirent.h>
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -43,10 +49,37 @@ __FBSDID("$FreeBSD$");
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include <yaml.h>
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
 
 #include "dns_utils.h"
 #include "config.h"
 
+struct sig_cert {
+	char *name;
+	unsigned char *sig;
+	int siglen;
+	unsigned char *cert;
+	int certlen;
+	bool trusted;
+};
+
+typedef enum {
+       HASH_UNKNOWN,
+       HASH_SHA256,
+} hash_t;
+
+struct fingerprint {
+       hash_t type;
+       char *name;
+       char hash[BUFSIZ];
+       STAILQ_ENTRY(fingerprint) next;
+};
+
+STAILQ_HEAD(fingerprint_list, fingerprint);
+
 static int
 extract_pkg_static(int fd, char *p, int sz)
 {
@@ -102,7 +135,7 @@ cleanup:
 }
 
 static int
-install_pkg_static(char *path, char *pkgpath)
+install_pkg_static(const char *path, const char *pkgpath)
 {
 	int pstat;
 	pid_t pid;
@@ -129,58 +162,34 @@ install_pkg_static(char *path, char *pkg
 }
 
 static int
-bootstrap_pkg(void)
+fetch_to_fd(const char *url, char *path)
 {
 	struct url *u;
-	FILE *remote;
-	FILE *config;
-	char *site;
 	struct dns_srvinfo *mirrors, *current;
-	/* To store _https._tcp. + hostname + \0 */
-	char zone[MAXHOSTNAMELEN + 13];
-	char url[MAXPATHLEN];
-	char conf[MAXPATHLEN];
-	char tmppkg[MAXPATHLEN];
-	const char *packagesite, *mirror_type;
-	char buf[10240];
-	char pkgstatic[MAXPATHLEN];
-	int fd, retry, ret, max_retry;
 	struct url_stat st;
+	FILE *remote;
+	/* To store _https._tcp. + hostname + \0 */
+	int fd;
+	int retry, max_retry;
 	off_t done, r;
-	time_t now;
-	time_t last;
+	time_t now, last;
+	char buf[10240];
+	char zone[MAXHOSTNAMELEN + 13];
+	static const char *mirror_type = NULL;
 
 	done = 0;
 	last = 0;
 	max_retry = 3;
-	ret = -1;
-	remote = NULL;
-	config = NULL;
 	current = mirrors = NULL;
+	remote = NULL;
 
-	printf("Bootstrapping pkg please wait\n");
-
-	if (config_string(PACKAGESITE, &packagesite) != 0) {
-		warnx("No PACKAGESITE defined");
-		return (-1);
-	}
-	if (config_string(MIRROR_TYPE, &mirror_type) != 0) {
+	if (mirror_type == NULL && config_string(MIRROR_TYPE, &mirror_type)
+	    != 0) {
 		warnx("No MIRROR_TYPE defined");
 		return (-1);
 	}
 
-	/* Support pkg+http:// for PACKAGESITE which is the new format
-	   in 1.2 to avoid confusion on why http://pkg.FreeBSD.org has
-	   no A record. */
-	if (strncmp(URL_SCHEME_PREFIX, packagesite,
-	    strlen(URL_SCHEME_PREFIX)) == 0)
-		packagesite += strlen(URL_SCHEME_PREFIX);
-	snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", packagesite);
-
-	snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX",
-	    getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP);
-
-	if ((fd = mkstemp(tmppkg)) == -1) {
+	if ((fd = mkstemp(path)) == -1) {
 		warn("mkstemp()");
 		return (-1);
 	}
@@ -228,7 +237,7 @@ bootstrap_pkg(void)
 
 		if (write(fd, buf, r) != r) {
 			warn("write()");
-			goto cleanup;
+			goto fetchfail;
 		}
 
 		done += r;
@@ -240,7 +249,558 @@ bootstrap_pkg(void)
 	if (ferror(remote))
 		goto fetchfail;
 
-	if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0)
+	goto cleanup;
+
+fetchfail:
+	if (fd != -1) {
+		close(fd);
+		fd = -1;
+		unlink(path);
+	}
+
+cleanup:
+	if (remote != NULL)
+		fclose(remote);
+
+	return fd;
+}
+
+static struct fingerprint *
+parse_fingerprint(yaml_document_t *doc, yaml_node_t *node)
+{
+	yaml_node_pair_t *pair;
+	yaml_char_t *function, *fp;
+	struct fingerprint *f;
+	hash_t fct = HASH_UNKNOWN;
+
+	function = fp = NULL;
+
+	pair = node->data.mapping.pairs.start;
+	while (pair < node->data.mapping.pairs.top) {
+		yaml_node_t *key = yaml_document_get_node(doc, pair->key);
+		yaml_node_t *val = yaml_document_get_node(doc, pair->value);
+
+		if (key->data.scalar.length <= 0) {
+			++pair;
+			continue;
+		}
+
+		if (val->type != YAML_SCALAR_NODE) {
+			++pair;
+			continue;
+		}
+
+		if (strcasecmp(key->data.scalar.value, "function") == 0)
+			function = val->data.scalar.value;
+		else if (strcasecmp(key->data.scalar.value, "fingerprint")
+		    == 0)
+			fp = val->data.scalar.value;
+
+		++pair;
+		continue;
+	}
+
+	if (fp == NULL || function == NULL)
+		return (NULL);
+
+	if (strcasecmp(function, "sha256") == 0)
+		fct = HASH_SHA256;
+
+	if (fct == HASH_UNKNOWN) {
+		fprintf(stderr, "Unsupported hashing function: %s\n", function);
+		return (NULL);
+	}
+
+	f = calloc(1, sizeof(struct fingerprint));
+	f->type = fct;
+	strlcpy(f->hash, fp, sizeof(f->hash));
+
+	return (f);
+}
+
+static void
+free_fingerprint_list(struct fingerprint_list* list)
+{
+	struct fingerprint* fingerprint;
+
+	STAILQ_FOREACH(fingerprint, list, next) {
+		if (fingerprint->name)
+			free(fingerprint->name);
+		free(fingerprint);
+	}
+	free(list);
+}
+
+static struct fingerprint *
+load_fingerprint(const char *dir, const char *filename)
+{
+	yaml_parser_t parser;
+	yaml_document_t doc;
+	yaml_node_t *node;
+	FILE *fp;
+	struct fingerprint *f;
+	char path[MAXPATHLEN];
+
+	f = NULL;
+
+	snprintf(path, MAXPATHLEN, "%s/%s", dir, filename);
+
+	if ((fp = fopen(path, "r")) == NULL)
+		return (NULL);
+
+	yaml_parser_initialize(&parser);
+	yaml_parser_set_input_file(&parser, fp);
+	yaml_parser_load(&parser, &doc);
+
+	node = yaml_document_get_root_node(&doc);
+	if (node == NULL || node->type != YAML_MAPPING_NODE)
+		goto out;
+
+	f = parse_fingerprint(&doc, node);
+	f->name = strdup(filename);
+
+out:
+	yaml_document_delete(&doc);
+	yaml_parser_delete(&parser);
+	fclose(fp);
+
+	return (f);
+}
+
+static struct fingerprint_list *
+load_fingerprints(const char *path, int *count)
+{
+	DIR *d;
+	struct dirent *ent;
+	struct fingerprint *finger;
+	struct fingerprint_list *fingerprints;
+
+	*count = 0;
+
+	fingerprints = calloc(1, sizeof(struct fingerprint_list));
+	if (fingerprints == NULL)
+		return (NULL);
+	STAILQ_INIT(fingerprints);
+
+	if ((d = opendir(path)) == NULL)
+		return (NULL);
+
+	while ((ent = readdir(d))) {
+		if (strcmp(ent->d_name, ".") == 0 ||
+		    strcmp(ent->d_name, "..") == 0)
+			continue;
+		finger = load_fingerprint(path, ent->d_name);
+		if (finger != NULL) {
+			STAILQ_INSERT_TAIL(fingerprints, finger, next);
+			++(*count);
+		}
+	}
+
+	closedir(d);
+
+	return (fingerprints);
+}
+
+static void
+sha256_hash(unsigned char hash[SHA256_DIGEST_LENGTH],
+    char out[SHA256_DIGEST_LENGTH * 2 + 1])
+{
+	int i;
+
+	for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
+		sprintf(out + (i * 2), "%02x", hash[i]);
+
+	out[SHA256_DIGEST_LENGTH * 2] = '\0';
+}
+
+static void
+sha256_buf(char *buf, size_t len, char out[SHA256_DIGEST_LENGTH * 2 + 1])
+{
+	unsigned char hash[SHA256_DIGEST_LENGTH];
+	SHA256_CTX sha256;
+
+	out[0] = '\0';
+
+	SHA256_Init(&sha256);
+	SHA256_Update(&sha256, buf, len);
+	SHA256_Final(hash, &sha256);
+	sha256_hash(hash, out);
+}
+
+static int
+sha256_fd(int fd, char out[SHA256_DIGEST_LENGTH * 2 + 1])
+{
+	int my_fd;
+	FILE *fp;
+	char buffer[BUFSIZ];
+	unsigned char hash[SHA256_DIGEST_LENGTH];
+	size_t r;
+	int ret;
+	SHA256_CTX sha256;
+
+	my_fd = -1;
+	fp = NULL;
+	r = 0;
+	ret = 1;
+
+	out[0] = '\0';
+
+	/* Duplicate the fd so that fclose(3) does not close it. */
+	if ((my_fd = dup(fd)) == -1) {
+		warnx("dup");
+		goto cleanup;
+	}
+
+	if ((fp = fdopen(my_fd, "rb")) == NULL) {
+		warnx("fdopen");
+		goto cleanup;
+	}
+
+	SHA256_Init(&sha256);
+
+	while ((r = fread(buffer, 1, BUFSIZ, fp)) > 0)
+		SHA256_Update(&sha256, buffer, r);
+
+	if (ferror(fp) != 0) {
+		warnx("fread");
+		goto cleanup;
+	}
+
+	SHA256_Final(hash, &sha256);
+	sha256_hash(hash, out);
+	ret = 0;
+
+cleanup:
+	if (fp != NULL)
+		fclose(fp);
+	else if (my_fd != -1)
+		close(my_fd);
+	(void)lseek(fd, 0, SEEK_SET);
+
+	return (ret);
+}
+
+static EVP_PKEY *
+load_public_key_buf(const unsigned char *cert, int certlen)
+{
+	EVP_PKEY *pkey;
+	BIO *bp;
+	char errbuf[1024];
+
+	bp = BIO_new_mem_buf(__DECONST(void *, cert), certlen);
+
+	if ((pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL)) == NULL)
+		warnx("%s", ERR_error_string(ERR_get_error(), errbuf));
+
+	BIO_free(bp);
+
+	return (pkey);
+}
+
+static bool
+rsa_verify_cert(int fd, const unsigned char *key, int keylen,
+    unsigned char *sig, int siglen)
+{
+	EVP_MD_CTX *mdctx;
+	EVP_PKEY *pkey;
+	char sha256[(SHA256_DIGEST_LENGTH * 2) + 2];
+	char errbuf[1024];
+	bool ret;
+
+	pkey = NULL;
+	mdctx = NULL;
+	ret = false;
+
+	/* Compute SHA256 of the package. */
+	if (lseek(fd, 0, 0) == -1) {
+		warn("lseek");
+		goto cleanup;
+	}
+	if ((sha256_fd(fd, sha256)) == -1) {
+		warnx("Error creating SHA256 hash for package");
+		goto cleanup;
+	}
+
+	if ((pkey = load_public_key_buf(key, keylen)) == NULL) {
+		warnx("Error reading public key");
+		goto cleanup;
+	}
+
+	/* Verify signature of the SHA256(pkg) is valid. */
+	if ((mdctx = EVP_MD_CTX_create()) == NULL) {
+		warnx("%s", ERR_error_string(ERR_get_error(), errbuf));
+		goto error;
+	}
+
+	if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pkey) != 1) {
+		warnx("%s", ERR_error_string(ERR_get_error(), errbuf));
+		goto error;
+	}
+	if (EVP_DigestVerifyUpdate(mdctx, sha256, strlen(sha256)) != 1) {
+		warnx("%s", ERR_error_string(ERR_get_error(), errbuf));
+		goto error;
+	}
+
+	if (EVP_DigestVerifyFinal(mdctx, sig, siglen) != 1) {
+		warnx("%s", ERR_error_string(ERR_get_error(), errbuf));
+		goto error;
+	}
+
+	ret = true;
+	printf("done\n");
+	goto cleanup;
+
+error:
+	printf("failed\n");
+
+cleanup:
+	if (pkey)
+		EVP_PKEY_free(pkey);
+	if (mdctx)
+		EVP_MD_CTX_destroy(mdctx);
+	ERR_free_strings();
+
+	return (ret);
+}
+
+static struct sig_cert *
+parse_cert(int fd) {
+	int my_fd;
+	struct sig_cert *sc;
+	FILE *fp;
+	struct sbuf *buf, *sig, *cert;
+	char *line;
+	size_t linecap;
+	ssize_t linelen;
+
+	buf = NULL;
+	my_fd = -1;
+	sc = NULL;
+	line = NULL;
+	linecap = 0;
+
+	if (lseek(fd, 0, 0) == -1) {
+		warn("lseek");
+		return (NULL);
+	}
+
+	/* Duplicate the fd so that fclose(3) does not close it. */
+	if ((my_fd = dup(fd)) == -1) {
+		warnx("dup");
+		return (NULL);
+	}
+
+	if ((fp = fdopen(my_fd, "rb")) == NULL) {
+		warn("fdopen");
+		close(my_fd);
+		return (NULL);
+	}
+
+	sig = sbuf_new_auto();
+	cert = sbuf_new_auto();
+
+	while ((linelen = getline(&line, &linecap, fp)) > 0) {
+		if (strcmp(line, "SIGNATURE\n") == 0) {
+			buf = sig;
+			continue;
+		} else if (strcmp(line, "CERT\n") == 0) {
+			buf = cert;
+			continue;
+		} else if (strcmp(line, "END\n") == 0) {
+			break;
+		}
+		if (buf != NULL)
+			sbuf_bcat(buf, line, linelen);
+	}
+
+	fclose(fp);
+
+	/* Trim out unrelated trailing newline */
+	sbuf_setpos(sig, sbuf_len(sig) - 1);
+
+	sbuf_finish(sig);
+	sbuf_finish(cert);
+
+	sc = calloc(1, sizeof(struct sig_cert));
+	sc->siglen = sbuf_len(sig);
+	sc->sig = calloc(1, sc->siglen);
+	memcpy(sc->sig, sbuf_data(sig), sc->siglen);
+
+	sc->certlen = sbuf_len(cert);
+	sc->cert = strdup(sbuf_data(cert));
+
+	sbuf_delete(sig);
+	sbuf_delete(cert);
+
+	return (sc);
+}
+
+static bool
+verify_signature(int fd_pkg, int fd_sig)
+{
+	struct fingerprint_list *trusted, *revoked;
+	struct fingerprint *fingerprint;
+	struct sig_cert *sc;
+	bool ret;
+	int trusted_count, revoked_count;
+	const char *fingerprints;
+	char path[MAXPATHLEN];
+	char hash[SHA256_DIGEST_LENGTH * 2 + 1];
+
+	sc = NULL;
+	trusted = revoked = NULL;
+	ret = false;
+
+	/* Read and parse fingerprints. */
+	if (config_string(FINGERPRINTS, &fingerprints) != 0) {
+		warnx("No CONFIG_FINGERPRINTS defined");
+		goto cleanup;
+	}
+
+	snprintf(path, MAXPATHLEN, "%s/trusted", fingerprints);
+	if ((trusted = load_fingerprints(path, &trusted_count)) == NULL) {
+		warnx("Error loading trusted certificates");
+		goto cleanup;
+	}
+
+	if (trusted_count == 0 || trusted == NULL) {
+		fprintf(stderr, "No trusted certificates found.\n");
+		goto cleanup;
+	}
+
+	snprintf(path, MAXPATHLEN, "%s/revoked", fingerprints);
+	if ((revoked = load_fingerprints(path, &revoked_count)) == NULL) {
+		warnx("Error loading revoked certificates");
+		goto cleanup;
+	}
+
+	/* Read certificate and signature in. */
+	if ((sc = parse_cert(fd_sig)) == NULL) {
+		warnx("Error parsing certificate");
+		goto cleanup;
+	}
+	/* Explicitly mark as non-trusted until proven otherwise. */
+	sc->trusted = false;
+
+	/* Parse signature and pubkey out of the certificate */
+	sha256_buf(sc->cert, sc->certlen, hash);
+
+	/* Check if this hash is revoked */
+	if (revoked != NULL) {
+		STAILQ_FOREACH(fingerprint, revoked, next) {
+			if (strcasecmp(fingerprint->hash, hash) == 0) {
+				fprintf(stderr, "The package was signed with "
+				    "revoked certificate %s\n",
+				    fingerprint->name);
+				goto cleanup;
+			}
+		}
+	}
+
+	STAILQ_FOREACH(fingerprint, trusted, next) {
+		if (strcasecmp(fingerprint->hash, hash) == 0) {
+			sc->trusted = true;
+			sc->name = strdup(fingerprint->name);
+			break;
+		}
+	}
+
+	if (sc->trusted == false) {
+		fprintf(stderr, "No trusted fingerprint found matching "
+		    "package's certificate\n");
+		goto cleanup;
+	}
+
+	/* Verify the signature. */
+	printf("Verifying signature with trusted certificate %s... ", sc->name);
+	if (rsa_verify_cert(fd_pkg, sc->cert, sc->certlen, sc->sig,
+	    sc->siglen) == false) {
+		fprintf(stderr, "Signature is not valid\n");
+		goto cleanup;
+	}
+
+	ret = true;
+
+cleanup:
+	if (trusted)
+		free_fingerprint_list(trusted);
+	if (revoked)
+		free_fingerprint_list(revoked);
+	if (sc) {
+		if (sc->cert)
+			free(sc->cert);
+		if (sc->sig)
+			free(sc->sig);
+		if (sc->name)
+			free(sc->name);
+		free(sc);
+	}
+
+	return (ret);
+}
+
+static int
+bootstrap_pkg(void)
+{
+	FILE *config;
+	int fd_pkg, fd_sig;
+	int ret;
+	char *site;
+	char url[MAXPATHLEN];
+	char conf[MAXPATHLEN];
+	char tmppkg[MAXPATHLEN];
+	char tmpsig[MAXPATHLEN];
+	const char *packagesite;
+	const char *signature_type;
+	char pkgstatic[MAXPATHLEN];
+
+	fd_sig = -1;
+	ret = -1;
+	config = NULL;
+
+	if (config_string(PACKAGESITE, &packagesite) != 0) {
+		warnx("No PACKAGESITE defined");
+		return (-1);
+	}

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list