Implement pubkey support for the bootstrap

Note that to not interfer with finger print it expects a signature on pkg itself
which is named pkg.txz.pubkeysign

To genrate it:
echo -n "$(sha256 -q pkg.txz)" | openssl dgst -sha256 -sign /thekey \
    -binary -out ./pkg.txz.pubkeysig

Note the "echo -n" which prevent signing the '\n' one would get otherwise

PR:		202622
MFC after:	1 week
This commit is contained in:
bapt 2015-09-08 21:25:36 +00:00
parent 61177b5cd0
commit 57a6813e1e
4 changed files with 182 additions and 35 deletions

View File

@ -6,6 +6,6 @@ MAN= pkg.7
CFLAGS+=-I${.CURDIR}/../../contrib/libucl/include
.PATH: ${.CURDIR}/../../contrib/libucl/include
LIBADD= archive fetch ucl sbuf crypto
LIBADD= archive fetch ucl sbuf crypto ssl
.include <bsd.prog.mk>

View File

@ -131,6 +131,15 @@ static struct config_entry c[] = {
false,
true,
},
[PUBKEY] = {
PKG_CONFIG_STRING,
"PUBKEY",
NULL,
NULL,
NULL,
false,
false
}
};
static int
@ -231,6 +240,8 @@ config_parse(const ucl_object_t *obj, pkg_conf_file_t conftype)
sbuf_cpy(buf, "SIGNATURE_TYPE");
else if (strcasecmp(key, "fingerprints") == 0)
sbuf_cpy(buf, "FINGERPRINTS");
else if (strcasecmp(key, "pubkey") == 0)
sbuf_cpy(buf, "PUBKEY");
else if (strcasecmp(key, "enabled") == 0) {
if ((cur->type != UCL_BOOLEAN) ||
!ucl_object_toboolean(cur))

View File

@ -40,6 +40,7 @@ typedef enum {
SIGNATURE_TYPE,
FINGERPRINTS,
REPOS_DIR,
PUBKEY,
CONFIG_SIZE
} pkg_config_key;

View File

@ -65,6 +65,11 @@ struct sig_cert {
bool trusted;
};
struct pubkey {
unsigned char *sig;
int siglen;
};
typedef enum {
HASH_UNKNOWN,
HASH_SHA256,
@ -469,6 +474,25 @@ cleanup:
return (ret);
}
static EVP_PKEY *
load_public_key_file(const char *file)
{
EVP_PKEY *pkey;
BIO *bp;
char errbuf[1024];
bp = BIO_new_file(file, "r");
if (!bp)
errx(EXIT_FAILURE, "Unable to read %s", file);
if ((pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL)) == NULL)
warnx("ici: %s", ERR_error_string(ERR_get_error(), errbuf));
BIO_free(bp);
return (pkey);
}
static EVP_PKEY *
load_public_key_buf(const unsigned char *cert, int certlen)
{
@ -487,8 +511,8 @@ load_public_key_buf(const unsigned char *cert, int certlen)
}
static bool
rsa_verify_cert(int fd, const unsigned char *key, int keylen,
unsigned char *sig, int siglen)
rsa_verify_cert(int fd, const char *sigfile, const unsigned char *key,
int keylen, unsigned char *sig, int siglen)
{
EVP_MD_CTX *mdctx;
EVP_PKEY *pkey;
@ -500,6 +524,8 @@ rsa_verify_cert(int fd, const unsigned char *key, int keylen,
mdctx = NULL;
ret = false;
SSL_load_error_strings();
/* Compute SHA256 of the package. */
if (lseek(fd, 0, 0) == -1) {
warn("lseek");
@ -510,9 +536,16 @@ rsa_verify_cert(int fd, const unsigned char *key, int keylen,
goto cleanup;
}
if ((pkey = load_public_key_buf(key, keylen)) == NULL) {
warnx("Error reading public key");
goto cleanup;
if (sigfile != NULL) {
if ((pkey = load_public_key_file(sigfile)) == NULL) {
warnx("Error reading public key");
goto cleanup;
}
} else {
if ((pkey = load_public_key_buf(key, keylen)) == NULL) {
warnx("Error reading public key");
goto cleanup;
}
}
/* Verify signature of the SHA256(pkg) is valid. */
@ -522,16 +555,16 @@ rsa_verify_cert(int fd, const unsigned char *key, int keylen,
}
if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pkey) != 1) {
warnx("%s", ERR_error_string(ERR_get_error(), errbuf));
warnx("la %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));
warnx("ici: %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));
warnx("merde %s", ERR_error_string(ERR_get_error(), errbuf));
goto error;
}
@ -552,6 +585,35 @@ cleanup:
return (ret);
}
static struct pubkey *
read_pubkey(int fd)
{
struct pubkey *pk;
struct sbuf *sig;
char buf[4096];
int r;
if (lseek(fd, 0, 0) == -1) {
warn("lseek");
return (NULL);
}
sig = sbuf_new_auto();
while ((r = read(fd, buf, sizeof(buf))) >0) {
sbuf_bcat(sig, buf, r);
}
sbuf_finish(sig);
pk = calloc(1, sizeof(struct pubkey));
pk->siglen = sbuf_len(sig);
pk->sig = calloc(1, pk->siglen);
memcpy(pk->sig, sbuf_data(sig), pk->siglen);
sbuf_delete(sig);
return (pk);
}
static struct sig_cert *
parse_cert(int fd) {
int my_fd;
@ -624,6 +686,45 @@ parse_cert(int fd) {
return (sc);
}
static bool
verify_pubsignature(int fd_pkg, int fd_sig)
{
struct pubkey *pk;
const char *pubkey;
bool ret;
pk = NULL;
pubkey = 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;
}
/* Verify the signature. */
printf("Verifying signature with public key %s... ", pubkey);
if (rsa_verify_cert(fd_pkg, pubkey, NULL, 0, pk->sig,
pk->siglen) == false) {
fprintf(stderr, "Signature is not valid\n");
goto cleanup;
}
ret = true;
cleanup:
if (pk) {
free(pk->sig);
free(pk);
}
return (ret);
}
static bool
verify_signature(int fd_pkg, int fd_sig)
{
@ -702,7 +803,7 @@ verify_signature(int fd_pkg, int fd_sig)
/* Verify the signature. */
printf("Verifying signature with trusted certificate %s... ", sc->name);
if (rsa_verify_cert(fd_pkg, sc->cert, sc->certlen, sc->sig,
if (rsa_verify_cert(fd_pkg, NULL, sc->cert, sc->certlen, sc->sig,
sc->siglen) == false) {
fprintf(stderr, "Signature is not valid\n");
goto cleanup;
@ -768,24 +869,42 @@ bootstrap_pkg(bool force)
if (signature_type != NULL &&
strcasecmp(signature_type, "NONE") != 0) {
if (strcasecmp(signature_type, "FINGERPRINTS") != 0) {
if (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;
} else if (strcasecmp(signature_type, "PUBKEY") == 0) {
snprintf(tmpsig, MAXPATHLEN,
"%s/pkg.txz.pubkeysig.XXXXXX",
getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP);
snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz.pubkeysig",
packagesite);
if ((fd_sig = fetch_to_fd(url, tmpsig)) == -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;
}
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)
@ -862,21 +981,37 @@ bootstrap_pkg_local(const char *pkgpath, bool force)
}
if (signature_type != NULL &&
strcasecmp(signature_type, "NONE") != 0) {
if (strcasecmp(signature_type, "FINGERPRINTS") != 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;
}
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)