diff --git a/usr.sbin/pkg/Makefile b/usr.sbin/pkg/Makefile index c7ea68973041..2fa0dbb053ad 100644 --- a/usr.sbin/pkg/Makefile +++ b/usr.sbin/pkg/Makefile @@ -1,31 +1,34 @@ PACKAGE= pkg-bootstrap _BRANCH!= ${MAKE} -C ${SRCTOP}/release -V BRANCH BRANCH?= ${_BRANCH} .if ${BRANCH:MCURRENT} != "" PKGCONFBRANCH?= latest .else . if ${BRANCH:MBETA*} || ${BRANCH:MRC*} || ${BRANCH:MRELEASE*} PKGCONFBRANCH?= quarterly . else . if ${MACHINE} != "amd64" && ${MACHINE} != "i386" && ${MACHINE} != "arm64" PKGCONFBRANCH?= quarterly . else PKGCONFBRANCH?= latest . endif . endif .endif PKGCONF?= FreeBSD.conf.${PKGCONFBRANCH} CONFS= ${PKGCONF} CONFSNAME_${PKGCONF}= ${PKGCONF:C/\.conf.+$/.conf/} CONFSDIR= /etc/pkg CONFSMODE= 644 PROG= pkg -SRCS= pkg.c rsa.c dns_utils.c config.c hash.c +SRCS= pkg.c rsa.c dns_utils.c config.c ecc.c hash.c MAN= pkg.7 CFLAGS+=-I${SRCTOP}/contrib/libucl/include .PATH: ${SRCTOP}/contrib/libucl/include -LIBADD= archive fetch ucl crypto ssl util md +LIBADD= archive der fetch pkgecc ucl crypto ssl util md + +CFLAGS+=-I${SRCTOP}/contrib/libder/libder +CFLAGS+=-I${SRCTOP}/crypto/libecc/include .include diff --git a/usr.sbin/pkg/ecc.c b/usr.sbin/pkg/ecc.c new file mode 100644 index 000000000000..01ce020bdba0 --- /dev/null +++ b/usr.sbin/pkg/ecc.c @@ -0,0 +1,606 @@ +/*- + * Copyright (c) 2011-2013 Baptiste Daroussin + * Copyright (c) 2011-2012 Julien Laffaye + * All rights reserved. + * Copyright (c) 2021 Kyle Evans + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define WITH_STDLIB +#include +#undef WITH_STDLIB + +#include "pkg.h" +#include "hash.h" + +/* libpkg shim */ +#define STREQ(l, r) (strcmp(l, r) == 0) + +struct ecc_sign_ctx { + struct pkgsign_ctx sctx; + ec_params params; + ec_key_pair keypair; + ec_alg_type sig_alg; + hash_alg_type sig_hash; + bool loaded; +}; + +/* Grab the ossl context from a pkgsign_ctx. */ +#define ECC_CCTX(c) (__containerof(c, const struct ecc_sign_ctx, sctx)) +#define ECC_CTX(c) (__containerof(c, struct ecc_sign_ctx, sctx)) + +#define PUBKEY_UNCOMPRESSED 0x04 + +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +static const uint8_t oid_ecpubkey[] = \ + { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 }; + +static const uint8_t oid_secp[] = \ + { 0x2b, 0x81, 0x04, 0x00 }; +static const uint8_t oid_secp256k1[] = \ + { 0x2b, 0x81, 0x04, 0x00, 0x0a }; +static const uint8_t oid_brainpoolP[] = \ + { 0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01 }; + +#define ENTRY(name, params) { #name, sizeof(#name) - 1, params } +static const struct pkgkey_map_entry { + const char *name; + size_t namesz; + const ec_str_params *params; +} pkgkey_map[] = { + ENTRY(WEI25519, &wei25519_str_params), + ENTRY(SECP256K1, &secp256k1_str_params), + ENTRY(SECP384R1, &secp384r1_str_params), + ENTRY(SECP512R1, &secp521r1_str_params), + ENTRY(BRAINPOOLP256R1, &brainpoolp256r1_str_params), + ENTRY(BRAINPOOLP256T1, &brainpoolp256t1_str_params), + ENTRY(BRAINPOOLP320R1, &brainpoolp320r1_str_params), + ENTRY(BRAINPOOLP320T1, &brainpoolp320t1_str_params), + ENTRY(BRAINPOOLP384R1, &brainpoolp384r1_str_params), + ENTRY(BRAINPOOLP384T1, &brainpoolp384t1_str_params), + ENTRY(BRAINPOOLP512R1, &brainpoolp512r1_str_params), + ENTRY(BRAINPOOLP512T1, &brainpoolp512t1_str_params), +}; + +static const char pkgkey_app[] = "pkg"; +static const char pkgkey_signer[] = "ecc"; + +static const ec_str_params * +ecc_pkgkey_params(const uint8_t *curve, size_t curvesz) +{ + const struct pkgkey_map_entry *entry; + + for (size_t i = 0; i < nitems(pkgkey_map); i++) { + entry = &pkgkey_map[i]; + if (curvesz != entry->namesz) + continue; + if (memcmp(curve, entry->name, curvesz) == 0) + return (entry->params); + } + + return (NULL); +} + +static int +ecc_read_pkgkey(struct libder_object *root, ec_params *params, int public, + uint8_t *rawkey, size_t *rawlen) +{ + struct libder_object *obj; + const uint8_t *data; + const ec_str_params *sparams; + size_t datasz; + int ret; + + if (libder_obj_type_simple(root) != BT_SEQUENCE) + return (1); + + /* Application */ + obj = libder_obj_child(root, 0); + if (obj == NULL || libder_obj_type_simple(obj) != BT_UTF8STRING) + return (1); + data = libder_obj_data(obj, &datasz); + if (datasz != sizeof(pkgkey_app) - 1 || + memcmp(data, pkgkey_app, datasz) != 0) + return (1); + + /* Version */ + obj = libder_obj_child(root, 1); + if (obj == NULL || libder_obj_type_simple(obj) != BT_INTEGER) + return (1); + data = libder_obj_data(obj, &datasz); + if (datasz != 1 || *data != 1 /* XXX */) + return (1); + + /* Signer */ + obj = libder_obj_child(root, 2); + if (obj == NULL || libder_obj_type_simple(obj) != BT_UTF8STRING) + return (1); + data = libder_obj_data(obj, &datasz); + if (datasz != sizeof(pkgkey_signer) - 1 || + memcmp(data, pkgkey_signer, datasz) != 0) + return (1); + + /* KeyType (curve) */ + obj = libder_obj_child(root, 3); + if (obj == NULL || libder_obj_type_simple(obj) != BT_UTF8STRING) + return (1); + data = libder_obj_data(obj, &datasz); + sparams = ecc_pkgkey_params(data, datasz); + if (sparams == NULL) + return (1); + + ret = import_params(params, sparams); + if (ret != 0) + return (1); + + /* Public? */ + obj = libder_obj_child(root, 4); + if (obj == NULL || libder_obj_type_simple(obj) != BT_BOOLEAN) + return (1); + data = libder_obj_data(obj, &datasz); + if (datasz != 1 || !data[0] != !public) + return (1); + + /* Key */ + obj = libder_obj_child(root, 5); + if (obj == NULL || libder_obj_type_simple(obj) != BT_BITSTRING) + return (1); + data = libder_obj_data(obj, &datasz); + if (datasz <= 2 || data[0] != 0 || data[1] != PUBKEY_UNCOMPRESSED) + return (1); + + data += 2; + datasz -= 2; + + if (datasz > *rawlen) + return (1); + + + memcpy(rawkey, data, datasz); + *rawlen = datasz; + + return (0); +} + +static int +ecc_extract_signature(const uint8_t *sig, size_t siglen, uint8_t *rawsig, + size_t rawlen) +{ + struct libder_ctx *ctx; + struct libder_object *obj, *root; + const uint8_t *sigdata; + size_t compsz, datasz, sigoff; + int rc; + + ctx = libder_open(); + if (ctx == NULL) + return (1); + + rc = 1; + root = libder_read(ctx, sig, &siglen); + if (root == NULL || libder_obj_type_simple(root) != BT_SEQUENCE) + goto out; + + /* Descend into the sequence's payload, extract both numbers. */ + compsz = rawlen / 2; + sigoff = 0; + for (int i = 0; i < 2; i++) { + obj = libder_obj_child(root, i); + if (libder_obj_type_simple(obj) != BT_INTEGER) + goto out; + + sigdata = libder_obj_data(obj, &datasz); + if (datasz < 2 || datasz > compsz + 1) + goto out; + + /* + * We may see an extra lead byte if our high bit of the first + * byte was set, since these numbers are positive by definition. + */ + if (sigdata[0] == 0 && (sigdata[1] & 0x80) != 0) { + sigdata++; + datasz--; + } + + /* Sanity check: don't overflow the output. */ + if (sigoff + datasz > rawlen) + goto out; + + /* Padding to the significant end if we're too small. */ + if (datasz < compsz) { + memset(&rawsig[sigoff], 0, compsz - datasz); + sigoff += compsz - datasz; + } + + memcpy(&rawsig[sigoff], sigdata, datasz); + sigoff += datasz; + } + + /* Sanity check: must have exactly the required # of signature bits. */ + rc = (sigoff == rawlen) ? 0 : 1; + +out: + libder_obj_free(root); + libder_close(ctx); + return (rc); +} + +static int +ecc_extract_pubkey_string(const uint8_t *data, size_t datalen, uint8_t *rawkey, + size_t *rawlen) +{ + uint8_t prefix, usebit; + + if (datalen <= 2) + return (1); + + usebit = *data++; + datalen--; + + if (usebit != 0) + return (1); + + prefix = *data++; + datalen--; + + if (prefix != PUBKEY_UNCOMPRESSED) + return (1); + + if (datalen > *rawlen) + return (1); + + memcpy(rawkey, data, datalen); + *rawlen = datalen; + + return (0); +} + +static int +ecc_extract_key_params(const uint8_t *oid, size_t oidlen, + ec_params *rawparams) +{ + int ret; + + if (oidlen >= sizeof(oid_secp) && + memcmp(oid, oid_secp, sizeof(oid_secp)) >= 0) { + oid += sizeof(oid_secp); + oidlen -= sizeof(oid_secp); + + if (oidlen != 1) + return (1); + + ret = -1; + switch (*oid) { + case 0x0a: /* secp256k1 */ + ret = import_params(rawparams, &secp256k1_str_params); + break; + case 0x22: /* secp384r1 */ + ret = import_params(rawparams, &secp384r1_str_params); + break; + case 0x23: /* secp521r1 */ + ret = import_params(rawparams, &secp521r1_str_params); + break; + default: + return (1); + } + + if (ret == 0) + return (0); + return (1); + } + + if (oidlen >= sizeof(oid_brainpoolP) && + memcmp(oid, oid_brainpoolP, sizeof(oid_brainpoolP)) >= 0) { + oid += sizeof(oid_brainpoolP); + oidlen -= sizeof(oid_brainpoolP); + + if (oidlen != 1) + return (1); + + ret = -1; + switch (*oid) { + case 0x07: /* brainpoolP256r1 */ + ret = import_params(rawparams, &brainpoolp256r1_str_params); + break; + case 0x08: /* brainpoolP256t1 */ + ret = import_params(rawparams, &brainpoolp256t1_str_params); + break; + case 0x09: /* brainpoolP320r1 */ + ret = import_params(rawparams, &brainpoolp320r1_str_params); + break; + case 0x0a: /* brainpoolP320t1 */ + ret = import_params(rawparams, &brainpoolp320t1_str_params); + break; + case 0x0b: /* brainpoolP384r1 */ + ret = import_params(rawparams, &brainpoolp384r1_str_params); + break; + case 0x0c: /* brainpoolP384t1 */ + ret = import_params(rawparams, &brainpoolp384t1_str_params); + break; + case 0x0d: /* brainpoolP512r1 */ + ret = import_params(rawparams, &brainpoolp512r1_str_params); + break; + case 0x0e: /* brainpoolP512t1 */ + ret = import_params(rawparams, &brainpoolp512t1_str_params); + break; + default: + return (1); + } + + if (ret == 0) + return (0); + return (1); + } + +#ifdef ECC_DEBUG + for (size_t i = 0; i < oidlen; i++) { + fprintf(stderr, "%.02x ", oid[i]); + } + + fprintf(stderr, "\n"); +#endif + + return (1); +} + +/* + * On entry, *rawparams should point to an ec_params that we can import the + * key parameters to. We'll either do that, or we'll set it to NULL if we could + * not deduce the curve. + */ +static int +ecc_extract_pubkey(FILE *keyfp, const uint8_t *key, size_t keylen, + uint8_t *rawkey, size_t *rawlen, ec_params *rawparams) +{ + const uint8_t *oidp; + struct libder_ctx *ctx; + struct libder_object *keydata, *oid, *params, *root; + size_t oidsz; + int rc; + + ctx = libder_open(); + if (ctx == NULL) + return (1); + + rc = 1; + assert((keyfp != NULL) ^ (key != NULL)); + if (keyfp != NULL) { + root = libder_read_file(ctx, keyfp, &keylen); + } else { + root = libder_read(ctx, key, &keylen); + } + + if (root == NULL || libder_obj_type_simple(root) != BT_SEQUENCE) + goto out; + + params = libder_obj_child(root, 0); + + if (params == NULL) { + goto out; + } else if (libder_obj_type_simple(params) != BT_SEQUENCE) { + rc = ecc_read_pkgkey(root, rawparams, 1, rawkey, rawlen); + goto out; + } + + /* Is a sequence */ + keydata = libder_obj_child(root, 1); + if (keydata == NULL || libder_obj_type_simple(keydata) != BT_BITSTRING) + goto out; + + /* Key type */ + oid = libder_obj_child(params, 0); + if (oid == NULL || libder_obj_type_simple(oid) != BT_OID) + goto out; + + oidp = libder_obj_data(oid, &oidsz); + if (oidsz != sizeof(oid_ecpubkey) || + memcmp(oidp, oid_ecpubkey, oidsz) != 0) + return (1); + + /* Curve */ + oid = libder_obj_child(params, 1); + if (oid == NULL || libder_obj_type_simple(oid) != BT_OID) + goto out; + + oidp = libder_obj_data(oid, &oidsz); + if (ecc_extract_key_params(oidp, oidsz, rawparams) != 0) + goto out; + + /* Finally, peel off the key material */ + key = libder_obj_data(keydata, &keylen); + if (ecc_extract_pubkey_string(key, keylen, rawkey, rawlen) != 0) + goto out; + + rc = 0; +out: + libder_obj_free(root); + libder_close(ctx); + return (rc); +} + +struct ecc_verify_cbdata { + const struct pkgsign_ctx *sctx; + FILE *keyfp; + const unsigned char *key; + size_t keylen; + unsigned char *sig; + size_t siglen; +}; + +static int +ecc_verify_internal(struct ecc_verify_cbdata *cbdata, const uint8_t *hash, + size_t hashsz) +{ + ec_pub_key pubkey; + ec_params derparams; + const struct ecc_sign_ctx *keyinfo = ECC_CCTX(cbdata->sctx); + uint8_t keybuf[EC_PUB_KEY_MAX_SIZE]; + uint8_t rawsig[EC_MAX_SIGLEN]; + size_t keysz; + int ret; + uint8_t ecsiglen; + + keysz = MIN(sizeof(keybuf), cbdata->keylen / 2); + + keysz = sizeof(keybuf); + if (ecc_extract_pubkey(cbdata->keyfp, cbdata->key, cbdata->keylen, + keybuf, &keysz, &derparams) != 0) { + warnx("failed to parse key"); + return (1); + } + + ret = ec_get_sig_len(&derparams, keyinfo->sig_alg, keyinfo->sig_hash, + &ecsiglen); + if (ret != 0) + return (1); + + /* + * Signatures are DER-encoded, whether by OpenSSL or pkg. + */ + if (ecc_extract_signature(cbdata->sig, cbdata->siglen, + rawsig, ecsiglen) != 0) { + warnx("failed to decode signature"); + return (1); + } + + ret = ec_pub_key_import_from_aff_buf(&pubkey, &derparams, + keybuf, keysz, keyinfo->sig_alg); + if (ret != 0) { + warnx("failed to import key"); + return (1); + } + + ret = ec_verify(rawsig, ecsiglen, &pubkey, hash, hashsz, keyinfo->sig_alg, + keyinfo->sig_hash, NULL, 0); + if (ret != 0) { + warnx("failed to verify signature"); + return (1); + } + + return (0); +} + +static bool +ecc_verify_data(const struct pkgsign_ctx *sctx, + const char *data, size_t datasz, const char *sigfile, + const unsigned char *key, int keylen, + unsigned char *sig, int siglen) +{ + int ret; + struct ecc_verify_cbdata cbdata; + + ret = 1; + + if (sigfile != NULL) { + cbdata.keyfp = fopen(sigfile, "r"); + if (cbdata.keyfp == NULL) { + warn("fopen: %s", sigfile); + return (false); + } + } else { + cbdata.keyfp = NULL; + cbdata.key = key; + cbdata.keylen = keylen; + } + + cbdata.sctx = sctx; + cbdata.sig = sig; + cbdata.siglen = siglen; + + ret = ecc_verify_internal(&cbdata, data, datasz); + + if (cbdata.keyfp != NULL) + fclose(cbdata.keyfp); + + return (ret == 0); +} + +static bool +ecc_verify_cert(const struct pkgsign_ctx *sctx, int fd, + const char *sigfile, const unsigned char *key, int keylen, + unsigned char *sig, int siglen) +{ + bool ret; + char *sha256; + + ret = false; + if (lseek(fd, 0, SEEK_SET) == -1) { + warn("lseek"); + return (false); + } + + if ((sha256 = sha256_fd(fd)) != NULL) { + ret = ecc_verify_data(sctx, sha256, strlen(sha256), sigfile, key, + keylen, sig, siglen); + free(sha256); + } + + return (ret); +} + +static int +ecc_new(const char *name __unused, struct pkgsign_ctx *sctx) +{ + struct ecc_sign_ctx *keyinfo = ECC_CTX(sctx); + int ret; + + ret = 1; + if (STREQ(name, "ecc") || STREQ(name, "eddsa")) { + keyinfo->sig_alg = EDDSA25519; + keyinfo->sig_hash = SHA512; + ret = import_params(&keyinfo->params, &wei25519_str_params); + } else if (STREQ(name, "ecdsa")) { + keyinfo->sig_alg = ECDSA; + keyinfo->sig_hash = SHA256; + ret = import_params(&keyinfo->params, &secp256k1_str_params); + } + + if (ret != 0) + return (1); + + return (0); +} + +const struct pkgsign_ops pkgsign_ecc = { + .pkgsign_ctx_size = sizeof(struct ecc_sign_ctx), + .pkgsign_new = ecc_new, + .pkgsign_verify_cert = ecc_verify_cert, + .pkgsign_verify_data = ecc_verify_data, +}; diff --git a/usr.sbin/pkg/pkg.c b/usr.sbin/pkg/pkg.c index db15e7b6ab22..54884dbe0b23 100644 --- a/usr.sbin/pkg/pkg.c +++ b/usr.sbin/pkg/pkg.c @@ -1,1211 +1,1290 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2012-2014 Baptiste Daroussin * Copyright (c) 2013 Bryan Drewery * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pkg.h" #include "dns_utils.h" #include "config.h" #include "hash.h" +#define PKGSIGN_MARKER "$PKGSIGN:" + static const struct pkgsign_impl { const char *pi_name; const struct pkgsign_ops *pi_ops; } pkgsign_builtins[] = { { .pi_name = "rsa", .pi_ops = &pkgsign_rsa, }, + { + .pi_name = "ecc", + .pi_ops = &pkgsign_ecc, + }, + { + .pi_name = "ecdsa", + .pi_ops = &pkgsign_ecc, + }, + { + .pi_name = "eddsa", + .pi_ops = &pkgsign_ecc, + }, }; typedef enum { HASH_UNKNOWN, HASH_SHA256, } hash_t; struct fingerprint { hash_t type; char *name; char hash[BUFSIZ]; STAILQ_ENTRY(fingerprint) next; }; static const char *bootstrap_names [] = { "pkg.pkg", "pkg.txz", NULL }; STAILQ_HEAD(fingerprint_list, fingerprint); static int debug; static int pkgsign_new(const char *name, struct pkgsign_ctx **ctx) { const struct pkgsign_impl *impl; const struct pkgsign_ops *ops; struct pkgsign_ctx *nctx; size_t ctx_size; int ret; assert(*ctx == NULL); ops = NULL; for (size_t i = 0; i < nitems(pkgsign_builtins); i++) { impl = &pkgsign_builtins[i]; if (strcmp(name, impl->pi_name) == 0) { ops = impl->pi_ops; break; } } if (ops == NULL) return (ENOENT); ctx_size = ops->pkgsign_ctx_size; if (ctx_size == 0) ctx_size = sizeof(*nctx); assert(ctx_size >= sizeof(*nctx)); nctx = calloc(1, ctx_size); if (nctx == NULL) err(EXIT_FAILURE, "calloc"); nctx->impl = impl; ret = 0; if (ops->pkgsign_new != NULL) ret = (*ops->pkgsign_new)(name, nctx); if (ret != 0) { free(nctx); return (ret); } *ctx = nctx; return (0); } static bool pkgsign_verify_cert(const struct pkgsign_ctx *ctx, int fd, const char *sigfile, const unsigned char *key, int keylen, unsigned char *sig, int siglen) { return ((*ctx->impl->pi_ops->pkgsign_verify_cert)(ctx, fd, sigfile, key, keylen, sig, siglen)); } static bool pkgsign_verify_data(const struct pkgsign_ctx *ctx, const char *data, size_t datasz, const char *sigfile, const unsigned char *key, int keylen, unsigned char *sig, int siglen) { return ((*ctx->impl->pi_ops->pkgsign_verify_data)(ctx, data, datasz, sigfile, key, keylen, sig, siglen)); } static int extract_pkg_static(int fd, char *p, int sz) { struct archive *a; struct archive_entry *ae; char *end; int ret, r; ret = -1; a = archive_read_new(); if (a == NULL) { warn("archive_read_new"); return (ret); } archive_read_support_filter_all(a); archive_read_support_format_tar(a); if (lseek(fd, 0, 0) == -1) { warn("lseek"); goto cleanup; } if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) { warnx("archive_read_open_fd: %s", archive_error_string(a)); goto cleanup; } ae = NULL; while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) { end = strrchr(archive_entry_pathname(ae), '/'); if (end == NULL) continue; if (strcmp(end, "/pkg-static") == 0) { r = archive_read_extract(a, ae, ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR); strlcpy(p, archive_entry_pathname(ae), sz); break; } } if (r == ARCHIVE_OK) ret = 0; else warnx("failed to extract pkg-static: %s", archive_error_string(a)); cleanup: archive_read_free(a); return (ret); } static int install_pkg_static(const char *path, const char *pkgpath, bool force) { int pstat; pid_t pid; switch ((pid = fork())) { case -1: return (-1); case 0: if (force) execl(path, "pkg-static", "add", "-f", pkgpath, (char *)NULL); else execl(path, "pkg-static", "add", pkgpath, (char *)NULL); _exit(1); default: break; } while (waitpid(pid, &pstat, 0) == -1) if (errno != EINTR) return (-1); if (WEXITSTATUS(pstat)) return (WEXITSTATUS(pstat)); else if (WIFSIGNALED(pstat)) return (128 & (WTERMSIG(pstat))); return (pstat); } static int fetch_to_fd(const char *url, char *path, const char *fetchOpts) { struct url *u; struct dns_srvinfo *mirrors, *current; struct url_stat st; FILE *remote; /* To store _https._tcp. + hostname + \0 */ int fd; int retry, max_retry; ssize_t r; char buf[10240]; char zone[MAXHOSTNAMELEN + 13]; static const char *mirror_type = NULL; max_retry = 3; current = mirrors = NULL; remote = NULL; if (mirror_type == NULL && config_string(MIRROR_TYPE, &mirror_type) != 0) { warnx("No MIRROR_TYPE defined"); return (-1); } if ((fd = mkstemp(path)) == -1) { warn("mkstemp()"); return (-1); } retry = max_retry; if ((u = fetchParseURL(url)) == NULL) { warn("fetchParseURL('%s')", url); return (-1); } while (remote == NULL) { if (retry == max_retry) { if (strcmp(u->scheme, "file") != 0 && strcasecmp(mirror_type, "srv") == 0) { snprintf(zone, sizeof(zone), "_%s._tcp.%s", u->scheme, u->host); mirrors = dns_getsrvinfo(zone); current = mirrors; } } if (mirrors != NULL) { strlcpy(u->host, current->host, sizeof(u->host)); u->port = current->port; } remote = fetchXGet(u, &st, fetchOpts); if (remote == NULL) { --retry; if (retry <= 0) goto fetchfail; if (mirrors != NULL) { current = current->next; if (current == NULL) current = mirrors; } } } while ((r = fread(buf, 1, sizeof(buf), remote)) > 0) { if (write(fd, buf, r) != r) { warn("write()"); goto fetchfail; } } if (r != 0) { warn("An error occurred while fetching pkg(8)"); goto fetchfail; } if (ferror(remote)) goto fetchfail; 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(ucl_object_t *obj) { const ucl_object_t *cur; ucl_object_iter_t it = NULL; const char *function, *fp, *key; struct fingerprint *f; hash_t fct = HASH_UNKNOWN; function = fp = NULL; while ((cur = ucl_iterate_object(obj, &it, true))) { key = ucl_object_key(cur); if (cur->type != UCL_STRING) continue; if (strcasecmp(key, "function") == 0) { function = ucl_object_tostring(cur); continue; } if (strcasecmp(key, "fingerprint") == 0) { fp = ucl_object_tostring(cur); continue; } } if (fp == NULL || function == NULL) return (NULL); if (strcasecmp(function, "sha256") == 0) fct = HASH_SHA256; if (fct == HASH_UNKNOWN) { warnx("Unsupported hashing function: %s", 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, *tmp; STAILQ_FOREACH_SAFE(fingerprint, list, next, tmp) { free(fingerprint->name); free(fingerprint); } free(list); } static struct fingerprint * load_fingerprint(const char *dir, const char *filename) { ucl_object_t *obj = NULL; struct ucl_parser *p = NULL; struct fingerprint *f; char path[MAXPATHLEN]; f = NULL; snprintf(path, MAXPATHLEN, "%s/%s", dir, filename); p = ucl_parser_new(0); if (!ucl_parser_add_file(p, path)) { warnx("%s: %s", path, ucl_parser_get_error(p)); ucl_parser_free(p); return (NULL); } obj = ucl_parser_get_object(p); if (obj->type == UCL_OBJECT) f = parse_fingerprint(obj); if (f != NULL) f->name = strdup(filename); ucl_object_unref(obj); ucl_parser_free(p); 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) { free(fingerprints); 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); } char * pkg_read_fd(int fd, size_t *osz) { char *obuf; char buf[4096]; FILE *fp; ssize_t r; obuf = NULL; *osz = 0; fp = open_memstream(&obuf, osz); if (fp == NULL) err(EXIT_FAILURE, "open_memstream()"); while ((r = read(fd, buf, sizeof(buf))) >0) { fwrite(buf, 1, r, fp); } if (ferror(fp)) errx(EXIT_FAILURE, "reading file"); fclose(fp); return (obuf); } +/* + * Returns a copy of the signature type stored on the heap, and advances *bufp + * past the type. + */ +static char * +parse_sigtype(char **bufp, size_t *bufszp) +{ + char *buf = *bufp; + char *endp; + char *sigtype; + size_t bufsz = *bufszp; + + if (bufsz <= sizeof(PKGSIGN_MARKER) - 1 || + strncmp(buf, PKGSIGN_MARKER, sizeof(PKGSIGN_MARKER) - 1) != 0) + goto dflt; + + buf += sizeof(PKGSIGN_MARKER) - 1; + endp = strchr(buf, '$'); + if (endp == NULL) + goto dflt; + + sigtype = strndup(buf, endp - buf); + *bufp = endp + 1; + *bufszp -= *bufp - buf; + + return (sigtype); +dflt: + return (strdup("rsa")); +} + static struct pubkey * read_pubkey(int fd) { struct pubkey *pk; - char *sigb; + char *osigb, *sigb, *sigtype; size_t sigsz; if (lseek(fd, 0, 0) == -1) { warn("lseek"); return (NULL); } - sigb = pkg_read_fd(fd, &sigsz); + osigb = sigb = pkg_read_fd(fd, &sigsz); + sigtype = parse_sigtype(&sigb, &sigsz); pk = calloc(1, sizeof(struct pubkey)); pk->siglen = sigsz; pk->sig = calloc(1, pk->siglen); memcpy(pk->sig, sigb, pk->siglen); - free(sigb); + pk->sigtype = sigtype; + free(osigb); return (pk); } static struct sig_cert * parse_cert(int fd) { int my_fd; struct sig_cert *sc; - FILE *fp, *sigfp, *certfp, *tmpfp; + FILE *fp, *sigfp, *certfp, *tmpfp, *typefp; char *line; - char *sig, *cert; - size_t linecap, sigsz, certsz; + char *sig, *cert, *type; + size_t linecap, sigsz, certsz, typesz; ssize_t linelen; + bool end_seen; sc = NULL; line = NULL; linecap = 0; - sig = cert = NULL; - sigfp = certfp = tmpfp = NULL; + sig = cert = type = NULL; + sigfp = certfp = tmpfp = typefp = NULL; 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); } - sigsz = certsz = 0; + sigsz = certsz = typesz = 0; sigfp = open_memstream(&sig, &sigsz); if (sigfp == NULL) err(EXIT_FAILURE, "open_memstream()"); certfp = open_memstream(&cert, &certsz); if (certfp == NULL) err(EXIT_FAILURE, "open_memstream()"); + typefp = open_memstream(&type, &typesz); + if (typefp == NULL) + err(EXIT_FAILURE, "open_memstream()"); + end_seen = false; while ((linelen = getline(&line, &linecap, fp)) > 0) { if (strcmp(line, "SIGNATURE\n") == 0) { tmpfp = sigfp; continue; + } else if (strcmp(line, "TYPE\n") == 0) { + tmpfp = typefp; + continue; } else if (strcmp(line, "CERT\n") == 0) { tmpfp = certfp; continue; } else if (strcmp(line, "END\n") == 0) { + end_seen = true; break; } if (tmpfp != NULL) fwrite(line, 1, linelen, tmpfp); } fclose(fp); fclose(sigfp); fclose(certfp); + fclose(typefp); sc = calloc(1, sizeof(struct sig_cert)); sc->siglen = sigsz -1; /* Trim out unrelated trailing newline */ sc->sig = sig; + if (typesz == 0) { + sc->type = strdup("rsa"); + free(type); + } else { + assert(type[typesz - 1] == '\n'); + type[typesz - 1] = '\0'; + sc->type = type; + } + + /* + * cert could be DER-encoded rather than PEM, so strip off any trailing + * END marker if we ran over it. + */ + if (!end_seen && certsz > 4 && + strcmp(&cert[certsz - 4], "END\n") == 0) + certsz -= 4; sc->certlen = certsz; sc->cert = cert; return (sc); } static bool verify_pubsignature(int fd_pkg, int fd_sig) { struct pubkey *pk; const char *pubkey; char *data; struct pkgsign_ctx *sctx; size_t datasz; bool ret; pk = NULL; pubkey = NULL; sctx = NULL; data = NULL; ret = false; if (config_string(PUBKEY, &pubkey) != 0) { warnx("No CONFIG_PUBKEY defined"); goto cleanup; } if ((pk = read_pubkey(fd_sig)) == NULL) { warnx("Error reading signature"); goto cleanup; } if (lseek(fd_pkg, 0, SEEK_SET) == -1) { warn("lseek"); goto cleanup; } - /* Future types shouldn't do this. */ - if ((data = sha256_fd(fd_pkg)) == NULL) { - warnx("Error creating SHA256 hash for package"); - goto cleanup; - } + if (strcmp(pk->sigtype, "rsa") == 0) { + /* Future types shouldn't do this. */ + if ((data = sha256_fd(fd_pkg)) == NULL) { + warnx("Error creating SHA256 hash for package"); + goto cleanup; + } - datasz = strlen(data); + datasz = strlen(data); + } else { + if ((data = pkg_read_fd(fd_pkg, &datasz)) == NULL) { + warnx("Failed to read package data"); + goto cleanup; + } + } - if (pkgsign_new("rsa", &sctx) != 0) { - warnx("Failed to fetch 'rsa' signer"); + if (pkgsign_new(pk->sigtype, &sctx) != 0) { + warnx("Failed to fetch '%s' signer", pk->sigtype); goto cleanup; } /* Verify the signature. */ printf("Verifying signature with public key %s... ", pubkey); if (pkgsign_verify_data(sctx, data, datasz, pubkey, NULL, 0, pk->sig, pk->siglen) == false) { fprintf(stderr, "Signature is not valid\n"); goto cleanup; } ret = true; cleanup: free(data); if (pk) { free(pk->sig); free(pk); } return (ret); } static bool verify_signature(int fd_pkg, int fd_sig) { struct fingerprint_list *trusted, *revoked; struct fingerprint *fingerprint; struct sig_cert *sc; struct pkgsign_ctx *sctx; bool ret; int trusted_count, revoked_count; const char *fingerprints; char path[MAXPATHLEN]; char *hash; hash = NULL; sc = NULL; sctx = 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 */ hash = sha256_buf(sc->cert, sc->certlen); /* 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; } - if (pkgsign_new("rsa", &sctx) != 0) { + if (pkgsign_new(sc->type, &sctx) != 0) { fprintf(stderr, "Failed to fetch 'rsa' signer\n"); goto cleanup; } /* Verify the signature. */ printf("Verifying signature with trusted certificate %s... ", sc->name); if (pkgsign_verify_cert(sctx, fd_pkg, NULL, sc->cert, sc->certlen, sc->sig, sc->siglen) == false) { fprintf(stderr, "Signature is not valid\n"); goto cleanup; } ret = true; cleanup: free(hash); if (trusted) free_fingerprint_list(trusted); if (revoked) free_fingerprint_list(revoked); if (sc) { free(sc->cert); free(sc->sig); free(sc->name); free(sc); } return (ret); } static int bootstrap_pkg(bool force, const char *fetchOpts) { int fd_pkg, fd_sig; int ret; char url[MAXPATHLEN]; char tmppkg[MAXPATHLEN]; char tmpsig[MAXPATHLEN]; const char *packagesite; const char *signature_type; char pkgstatic[MAXPATHLEN]; const char *bootstrap_name; fd_sig = -1; ret = -1; 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); for (int j = 0; bootstrap_names[j] != NULL; j++) { bootstrap_name = bootstrap_names[j]; snprintf(url, MAXPATHLEN, "%s/Latest/%s", packagesite, bootstrap_name); snprintf(tmppkg, MAXPATHLEN, "%s/%s.XXXXXX", getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP, bootstrap_name); if ((fd_pkg = fetch_to_fd(url, tmppkg, fetchOpts)) != -1) break; bootstrap_name = NULL; } if (bootstrap_name == NULL) goto fetchfail; if (signature_type != NULL && strcasecmp(signature_type, "NONE") != 0) { if (strcasecmp(signature_type, "FINGERPRINTS") == 0) { snprintf(tmpsig, MAXPATHLEN, "%s/%s.sig.XXXXXX", getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP, bootstrap_name); snprintf(url, MAXPATHLEN, "%s/Latest/%s.sig", packagesite, bootstrap_name); if ((fd_sig = fetch_to_fd(url, tmpsig, fetchOpts)) == -1) { fprintf(stderr, "Signature for pkg not " "available.\n"); goto fetchfail; } if (verify_signature(fd_pkg, fd_sig) == false) goto cleanup; } else if (strcasecmp(signature_type, "PUBKEY") == 0) { snprintf(tmpsig, MAXPATHLEN, "%s/%s.pubkeysig.XXXXXX", getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP, bootstrap_name); snprintf(url, MAXPATHLEN, "%s/Latest/%s.pubkeysig", packagesite, bootstrap_name); if ((fd_sig = fetch_to_fd(url, tmpsig, fetchOpts)) == -1) { fprintf(stderr, "Signature for pkg not " "available.\n"); goto fetchfail; } if (verify_pubsignature(fd_pkg, fd_sig) == false) goto cleanup; } else { warnx("Signature type %s is not supported for " "bootstrapping.", signature_type); goto cleanup; } } if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0) ret = install_pkg_static(pkgstatic, tmppkg, force); goto cleanup; fetchfail: for (int j = 0; bootstrap_names[j] != NULL; j++) { warnx("Attempted to fetch %s/Latest/%s", packagesite, bootstrap_names[j]); } warnx("Error: %s", fetchLastErrString); if (fetchLastErrCode == FETCH_RESOLV) { fprintf(stderr, "Address resolution failed for %s.\n", packagesite); fprintf(stderr, "Consider changing PACKAGESITE.\n"); } else { 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 (fd_sig != -1) { close(fd_sig); unlink(tmpsig); } if (fd_pkg != -1) { close(fd_pkg); unlink(tmppkg); } return (ret); } static const char confirmation_message[] = "The package management tool is not yet installed on your system.\n" "Do you want to fetch and install it now? [y/N]: "; static const char non_interactive_message[] = "The package management tool is not yet installed on your system.\n" "Please set ASSUME_ALWAYS_YES=yes environment variable to be able to bootstrap " "in non-interactive (stdin not being a tty)\n"; static const char args_bootstrap_message[] = "Too many arguments\n" "Usage: pkg [-4|-6] bootstrap [-f] [-y]\n"; static const char args_add_message[] = "Too many arguments\n" "Usage: pkg add [-f] [-y] {pkg.pkg}\n"; static int pkg_query_yes_no(void) { int ret, c; fflush(stdout); c = getchar(); if (c == 'y' || c == 'Y') ret = 1; else ret = 0; while (c != '\n' && c != EOF) c = getchar(); return (ret); } static int bootstrap_pkg_local(const char *pkgpath, bool force) { 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"); goto cleanup; } if (signature_type != NULL && strcasecmp(signature_type, "NONE") != 0) { if (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; } else if (strcasecmp(signature_type, "PUBKEY") == 0) { snprintf(path, sizeof(path), "%s.pubkeysig", pkgpath); if ((fd_sig = open(path, O_RDONLY)) == -1) { fprintf(stderr, "Signature for pkg not " "available.\n"); goto cleanup; } if (verify_pubsignature(fd_pkg, fd_sig) == false) goto cleanup; } else { warnx("Signature type %s is not supported for " "bootstrapping.", signature_type); goto cleanup; } } if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0) ret = install_pkg_static(pkgstatic, pkgpath, force); cleanup: close(fd_pkg); if (fd_sig != -1) close(fd_sig); return (ret); } #define PKG_NAME "pkg" #define PKG_DEVEL_NAME PKG_NAME "-devel" #define PKG_PKG PKG_NAME "." static bool pkg_is_pkg_pkg(const char *pkg) { char *vstart, *basename; size_t namelen; /* Strip path. */ if ((basename = strrchr(pkg, '/')) != NULL) pkg = basename + 1; /* * Chop off the final "-" (version delimiter) and check the name that * precedes it. If we didn't have a version delimiter, it must be the * pkg.$archive short form but we'll check it anyways. pkg-devel short * form will look like a pkg archive with 'devel' version, but that's * OK. We otherwise assumed that non-pkg packages will always have a * version component. */ vstart = strrchr(pkg, '-'); if (vstart == NULL) { return (strlen(pkg) > sizeof(PKG_PKG) - 1 && strncmp(pkg, PKG_PKG, sizeof(PKG_PKG) - 1) == 0); } namelen = vstart - pkg; if (namelen == sizeof(PKG_NAME) - 1 && strncmp(pkg, PKG_NAME, sizeof(PKG_NAME) - 1) == 0) return (true); if (namelen == sizeof(PKG_DEVEL_NAME) - 1 && strncmp(pkg, PKG_DEVEL_NAME, sizeof(PKG_DEVEL_NAME) - 1) == 0) return (true); return (false); } int main(int argc, char *argv[]) { char pkgpath[MAXPATHLEN]; const char *pkgarg, *repo_name; bool activation_test, add_pkg, bootstrap_only, force, yes; signed char ch; const char *fetchOpts; char *command; activation_test = false; add_pkg = false; bootstrap_only = false; command = NULL; fetchOpts = ""; force = false; pkgarg = NULL; repo_name = NULL; yes = false; struct option longopts[] = { { "debug", no_argument, NULL, 'd' }, { "force", no_argument, NULL, 'f' }, { "only-ipv4", no_argument, NULL, '4' }, { "only-ipv6", no_argument, NULL, '6' }, { "yes", no_argument, NULL, 'y' }, { NULL, 0, NULL, 0 }, }; snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", getlocalbase()); while ((ch = getopt_long(argc, argv, "-:dfr::yN46", longopts, NULL)) != -1) { switch (ch) { case 'd': debug++; break; case 'f': force = true; break; case 'N': activation_test = true; break; case 'y': yes = true; break; case '4': fetchOpts = "4"; break; case '6': fetchOpts = "6"; break; case 'r': /* * The repository can only be specified for an explicit * bootstrap request at this time, so that we don't * confuse the user if they're trying to use a verb that * has some other conflicting meaning but we need to * bootstrap. * * For that reason, we specify that -r has an optional * argument above and process the next index ourselves. * This is mostly significant because getopt(3) will * otherwise eat the next argument, which could be * something we need to try and make sense of. * * At worst this gets us false positives that we ignore * in other contexts, and we have to do a little fudging * in order to support separating -r from the reponame * with a space since it's not actually optional in * the bootstrap/add sense. */ if (add_pkg || bootstrap_only) { if (optarg != NULL) { repo_name = optarg; } else if (optind < argc) { repo_name = argv[optind]; } if (repo_name == NULL || *repo_name == '\0') { fprintf(stderr, "Must specify a repository with -r!\n"); exit(EXIT_FAILURE); } if (optarg == NULL) { /* Advance past repo name. */ optreset = 1; optind++; } } break; case 1: // Non-option arguments, first one is the command if (command == NULL) { command = argv[optind-1]; if (strcmp(command, "add") == 0) { add_pkg = true; } else if (strcmp(command, "bootstrap") == 0) { bootstrap_only = true; } } // bootstrap doesn't accept other arguments else if (bootstrap_only) { fprintf(stderr, args_bootstrap_message); exit(EXIT_FAILURE); } else if (add_pkg && pkgarg != NULL) { /* * Additional arguments also means it's not a * local bootstrap request. */ add_pkg = false; } else if (add_pkg) { /* * If it's not a request for pkg or pkg-devel, * then we must assume they were trying to * install some other local package and we * should try to bootstrap from the repo. */ if (!pkg_is_pkg_pkg(argv[optind-1])) { add_pkg = false; } else { pkgarg = argv[optind-1]; } } break; default: break; } } if (debug > 1) fetchDebug = 1; if ((bootstrap_only && force) || access(pkgpath, X_OK) == -1) { /* * To allow 'pkg -N' to be used as a reliable test for whether * a system is configured to use pkg, don't bootstrap pkg * when that option is passed. */ if (activation_test) errx(EXIT_FAILURE, "pkg is not installed"); config_init(repo_name); if (add_pkg) { if (pkgarg == NULL) { fprintf(stderr, "Path to pkg.pkg required\n"); exit(EXIT_FAILURE); } if (access(pkgarg, R_OK) == -1) { fprintf(stderr, "No such file: %s\n", pkgarg); exit(EXIT_FAILURE); } if (bootstrap_pkg_local(pkgarg, force) != 0) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } /* * Do not ask for confirmation if either of stdin or stdout is * not tty. Check the environment to see if user has answer * tucked in there already. */ if (!yes) config_bool(ASSUME_ALWAYS_YES, &yes); if (!yes) { if (!isatty(fileno(stdin))) { fprintf(stderr, non_interactive_message); exit(EXIT_FAILURE); } printf("%s", confirmation_message); if (pkg_query_yes_no() == 0) exit(EXIT_FAILURE); } if (bootstrap_pkg(force, fetchOpts) != 0) exit(EXIT_FAILURE); config_finish(); if (bootstrap_only) exit(EXIT_SUCCESS); } else if (bootstrap_only) { printf("pkg already bootstrapped at %s\n", pkgpath); exit(EXIT_SUCCESS); } execv(pkgpath, argv); /* NOT REACHED */ return (EXIT_FAILURE); } diff --git a/usr.sbin/pkg/pkg.h b/usr.sbin/pkg/pkg.h index b9fe9b5fa566..f74f97ce795b 100644 --- a/usr.sbin/pkg/pkg.h +++ b/usr.sbin/pkg/pkg.h @@ -1,72 +1,75 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2012-2014 Baptiste Daroussin * Copyright (c) 2013 Bryan Drewery * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _PKG_H #define _PKG_H #include struct pkgsign_ctx { const struct pkgsign_impl *impl; }; /* Tentatively won't be needing to free any state, all allocated in the ctx. */ typedef int pkgsign_new_cb(const char *, struct pkgsign_ctx *); typedef bool pkgsign_verify_cert_cb(const struct pkgsign_ctx *, int, const char *, const unsigned char *, int, unsigned char *, int); typedef bool pkgsign_verify_data_cb(const struct pkgsign_ctx *, const char *, size_t, const char *, const unsigned char *, int, unsigned char *, int); struct pkgsign_ops { size_t pkgsign_ctx_size; pkgsign_new_cb *pkgsign_new; pkgsign_verify_cert_cb *pkgsign_verify_cert; pkgsign_verify_data_cb *pkgsign_verify_data; }; +extern const struct pkgsign_ops pkgsign_ecc; extern const struct pkgsign_ops pkgsign_rsa; struct sig_cert { char *name; + char *type; unsigned char *sig; int siglen; unsigned char *cert; int certlen; bool trusted; }; struct pubkey { + char *sigtype; unsigned char *sig; int siglen; }; char *pkg_read_fd(int fd, size_t *osz); #endif /* _PKG_H */