From owner-svn-src-stable@FreeBSD.ORG Tue Oct 29 18:36:46 2013 Return-Path: Delivered-To: svn-src-stable@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id 925E517F; Tue, 29 Oct 2013 18:36:46 +0000 (UTC) (envelope-from bdrewery@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 6F72D2C71; Tue, 29 Oct 2013 18:36:46 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r9TIakaV082771; Tue, 29 Oct 2013 18:36:46 GMT (envelope-from bdrewery@svn.freebsd.org) Received: (from bdrewery@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r9TIaiXQ082763; Tue, 29 Oct 2013 18:36:44 GMT (envelope-from bdrewery@svn.freebsd.org) Message-Id: <201310291836.r9TIaiXQ082763@svn.freebsd.org> From: Bryan Drewery Date: Tue, 29 Oct 2013 18:36:44 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r257353 - in stable/10: etc etc/keys etc/mtree etc/pkg share/man/man7 usr.sbin/pkg X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for all the -stable branches of the src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 29 Oct 2013 18:36:46 -0000 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 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 + * Copyright (c) 2013 Bryan Drewery * 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 + * Copyright (c) 2013 Bryan Drewery * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,10 +29,15 @@ __FBSDID("$FreeBSD$"); #include +#include +#include +#include #include +#define _WITH_GETLINE #include #include +#include #include #include #include @@ -43,10 +49,37 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include + +#include +#include #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 ***