Index: usr.sbin/pkg/config.c =================================================================== --- usr.sbin/pkg/config.c (revision 287854) +++ usr.sbin/pkg/config.c (working copy) @@ -131,6 +131,15 @@ static struct config_entry c[] = { false, true, }, + [PUBKEY] = { + PKG_CONFIG_STRING, + "PUBKEY", + NULL, + NULL, + NULL, + false, + false + } }; static const char * @@ -347,6 +356,8 @@ config_parse(ucl_object_t *obj, pkg_conf_file_t co 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)) Index: usr.sbin/pkg/config.h =================================================================== --- usr.sbin/pkg/config.h (revision 287854) +++ usr.sbin/pkg/config.h (working copy) @@ -40,6 +40,7 @@ typedef enum { SIGNATURE_TYPE, FINGERPRINTS, REPOS_DIR, + PUBKEY, CONFIG_SIZE } pkg_config_key; Index: usr.sbin/pkg/pkg.c =================================================================== --- usr.sbin/pkg/pkg.c (revision 287854) +++ usr.sbin/pkg/pkg.c (working copy) @@ -47,7 +47,6 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include #include #include @@ -66,6 +65,11 @@ struct sig_cert { bool trusted; }; +struct pubkey { + unsigned char *sig; + int siglen; +}; + typedef enum { HASH_UNKNOWN, HASH_SHA256, @@ -176,14 +180,11 @@ fetch_to_fd(const char *url, char *path) /* To store _https._tcp. + hostname + \0 */ int fd; int retry, max_retry; - off_t done, r; - time_t now, last; + ssize_t r; char buf[10240]; char zone[MAXHOSTNAMELEN + 13]; static const char *mirror_type = NULL; - done = 0; - last = 0; max_retry = 3; current = mirrors = NULL; remote = NULL; @@ -233,19 +234,16 @@ fetch_to_fd(const char *url, char *path) } } - while (done < st.size) { - if ((r = fread(buf, 1, sizeof(buf), remote)) < 1) - break; - + while ((r = fread(buf, 1, sizeof(buf), remote)) > 0) { if (write(fd, buf, r) != r) { warn("write()"); goto fetchfail; } + } - done += r; - now = time(NULL); - if (now > last || done == st.size) - last = now; + if (r != 0) { + warn("An error occurred while fetching pkg(8)"); + goto fetchfail; } if (ferror(remote)) @@ -480,6 +478,29 @@ cleanup: } static RSA * +load_rsa_public_key_file(const char *file) +{ + RSA *rsa = NULL; + BIO *bp; + char errbuf[1024]; + + bp = BIO_new_file(file, "r"); + if (!bp) + errx(EXIT_FAILURE, "Unable to read %s", file); + + if (!PEM_read_bio_RSA_PUBKEY(bp, &rsa, NULL, NULL)) { + warn("error reading public key: %s", + ERR_error_string(ERR_get_error(), errbuf)); + BIO_free(bp); + return (NULL); + } + + BIO_free(bp); + + return (rsa); +} + +static RSA * load_rsa_public_key_buf(unsigned char *cert, int certlen) { RSA *rsa = NULL; @@ -499,8 +520,8 @@ load_rsa_public_key_buf(unsigned char *cert, int c static bool -rsa_verify_cert(int fd, unsigned char *key, int keylen, - unsigned char *sig, int siglen) +rsa_verify_cert(int fd, const char *sigfile, unsigned char *key, + int keylen, unsigned char *sig, int siglen) { char sha256[SHA256_DIGEST_LENGTH *2 +1]; char hash[SHA256_DIGEST_LENGTH]; @@ -517,7 +538,11 @@ static bool sha256_buf_bin(sha256, strlen(sha256), hash); - rsa = load_rsa_public_key_buf(key, keylen); + if (sigfile != NULL) { + rsa = load_rsa_public_key_file(sigfile); + } else { + rsa = load_rsa_public_key_buf(key, keylen); + } if (rsa == NULL) return (false); ret = RSA_verify(NID_sha256, hash, sizeof(hash), sig, siglen, rsa); @@ -532,6 +557,35 @@ static bool return (true); } +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; @@ -605,6 +659,45 @@ parse_cert(int fd) { } 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) { struct fingerprint_list *trusted, *revoked; @@ -682,7 +775,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) { printf("failed\n"); fprintf(stderr, "Signature is not valid\n"); @@ -750,24 +843,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) @@ -841,21 +952,37 @@ bootstrap_pkg_local(const char *pkgpath, bool forc } 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)