summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authorbdrewery <bdrewery@FreeBSD.org>2013-10-29 18:36:44 +0000
committerbdrewery <bdrewery@FreeBSD.org>2013-10-29 18:36:44 +0000
commit7046958142fcbc1e0f447f73cf6049ffc395b5ca (patch)
treef40ea8ace6b95a21e03808d27d810254044c9a8f /usr.sbin
parent2d60d1851a80cfae2603a0ed8a5b19372d8b77ba (diff)
downloadFreeBSD-src-7046958142fcbc1e0f447f73cf6049ffc395b5ca.zip
FreeBSD-src-7046958142fcbc1e0f447f73cf6049ffc395b5ca.tar.gz
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)
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/pkg/Makefile5
-rw-r--r--usr.sbin/pkg/config.c154
-rw-r--r--usr.sbin/pkg/config.h7
-rw-r--r--usr.sbin/pkg/pkg.c711
4 files changed, 788 insertions, 89 deletions
diff --git a/usr.sbin/pkg/Makefile b/usr.sbin/pkg/Makefile
index 38d82ba..9cf78cd 100644
--- a/usr.sbin/pkg/Makefile
+++ b/usr.sbin/pkg/Makefile
@@ -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>
diff --git a/usr.sbin/pkg/config.c b/usr.sbin/pkg/config.c
index 6a6b27d..affcee9 100644
--- a/usr.sbin/pkg/config.c
+++ b/usr.sbin/pkg/config.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2013 Baptiste Daroussin <bapt@FreeBSD.org>
+ * Copyright (c) 2013 Bryan Drewery <bdrewery@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_node_t *node)
}
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_node_t *node)
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;
}
diff --git a/usr.sbin/pkg/config.h b/usr.sbin/pkg/config.h
index c3d877e..72d2ec0 100644
--- a/usr.sbin/pkg/config.h
+++ b/usr.sbin/pkg/config.h
@@ -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 **);
diff --git a/usr.sbin/pkg/pkg.c b/usr.sbin/pkg/pkg.c
index 0064e26..4e9bb94 100644
--- a/usr.sbin/pkg/pkg.c
+++ b/usr.sbin/pkg/pkg.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2012-2013 Baptiste Daroussin <bapt@FreeBSD.org>
+ * Copyright (c) 2013 Bryan Drewery <bdrewery@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 *pkgpath)
}
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);
+ }
+
+ if (config_string(SIGNATURE_TYPE, &signature_type) != 0) {
+ warnx("Error looking up SIGNATURE_TYPE");
+ return (-1);
+ }
+
+ printf("Bootstrapping pkg from %s, please wait...\n", packagesite);
+
+ /* 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_pkg = fetch_to_fd(url, tmppkg)) == -1)
+ goto fetchfail;
+
+ if (signature_type != NULL &&
+ strcasecmp(signature_type, "FINGERPRINTS") == 0) {
+ snprintf(tmpsig, MAXPATHLEN, "%s/pkg.txz.sig.XXXXXX",
+ getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP);
+ snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz.sig",
+ packagesite);
+
+ if ((fd_sig = fetch_to_fd(url, tmpsig)) == -1) {
+ fprintf(stderr, "Signature for pkg not available.\n");
+ goto fetchfail;
+ }
+
+ if (verify_signature(fd_pkg, fd_sig) == false)
+ goto cleanup;
+ }
+
+ if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0)
ret = install_pkg_static(pkgstatic, tmppkg);
snprintf(conf, MAXPATHLEN, "%s/etc/pkg.conf",
@@ -267,13 +827,17 @@ bootstrap_pkg(void)
fetchfail:
warnx("Error fetching %s: %s", url, fetchLastErrString);
- fprintf(stderr, "A pre-built version of pkg could not be found for your system.\n");
- fprintf(stderr, "Consider changing PACKAGESITE or installing it from ports: 'ports-mgmt/pkg'.\n");
+ fprintf(stderr, "A pre-built version of pkg could not be found for "
+ "your system.\n");
+ fprintf(stderr, "Consider changing PACKAGESITE or installing it from "
+ "ports: 'ports-mgmt/pkg'.\n");
cleanup:
- if (remote != NULL)
- fclose(remote);
- close(fd);
+ if (fd_sig != -1) {
+ close(fd_sig);
+ unlink(tmpsig);
+ }
+ close(fd_pkg);
unlink(tmppkg);
return (ret);
@@ -301,13 +865,54 @@ pkg_query_yes_no(void)
return (ret);
}
+static int
+bootstrap_pkg_local(const char *pkgpath)
+{
+ char path[MAXPATHLEN];
+ char pkgstatic[MAXPATHLEN];
+ const char *signature_type;
+ int fd_pkg, fd_sig, ret;
+
+ fd_sig = -1;
+ ret = -1;
+
+ fd_pkg = open(pkgpath, O_RDONLY);
+ if (fd_pkg == -1)
+ err(EXIT_FAILURE, "Unable to open %s", pkgpath);
+
+ if (config_string(SIGNATURE_TYPE, &signature_type) != 0) {
+ warnx("Error looking up SIGNATURE_TYPE");
+ return (-1);
+ }
+ if (signature_type != NULL &&
+ strcasecmp(signature_type, "FINGERPRINTS") == 0) {
+ snprintf(path, sizeof(path), "%s.sig", pkgpath);
+
+ if ((fd_sig = open(path, O_RDONLY)) == -1) {
+ fprintf(stderr, "Signature for pkg not available.\n");
+ goto cleanup;
+ }
+
+ if (verify_signature(fd_pkg, fd_sig) == false)
+ goto cleanup;
+ }
+
+ if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0)
+ ret = install_pkg_static(pkgstatic, pkgpath);
+
+cleanup:
+ close(fd_pkg);
+ if (fd_sig != -1)
+ close(fd_sig);
+
+ return (ret);
+}
+
int
main(__unused int argc, char *argv[])
{
char pkgpath[MAXPATHLEN];
- char pkgstatic[MAXPATHLEN];
bool yes = false;
- int fd, ret;
snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg",
getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
@@ -321,16 +926,11 @@ main(__unused int argc, char *argv[])
if (argv[1] != NULL && strcmp(argv[1], "-N") == 0)
errx(EXIT_FAILURE, "pkg is not installed");
+ config_init();
+
if (argc > 2 && strcmp(argv[1], "add") == 0 &&
access(argv[2], R_OK) == 0) {
- fd = open(argv[2], O_RDONLY);
- if (fd == -1)
- err(EXIT_FAILURE, "Unable to open %s", argv[2]);
-
- if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0)
- ret = install_pkg_static(pkgstatic, argv[2]);
- close(fd);
- if (ret != 0)
+ if (bootstrap_pkg_local(argv[2]) != 0)
exit(EXIT_FAILURE);
exit(EXIT_SUCCESS);
}
@@ -339,7 +939,6 @@ main(__unused int argc, char *argv[])
* not tty. Check the environment to see if user has answer
* tucked in there already.
*/
- config_init();
config_bool(ASSUME_ALWAYS_YES, &yes);
if (!yes) {
printf("%s", confirmation_message);
OpenPOWER on IntegriCloud