summaryrefslogtreecommitdiffstats
path: root/usr.sbin/pkg/pkg.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/pkg/pkg.c')
-rw-r--r--usr.sbin/pkg/pkg.c1109
1 files changed, 1109 insertions, 0 deletions
diff --git a/usr.sbin/pkg/pkg.c b/usr.sbin/pkg/pkg.c
new file mode 100644
index 0000000..bd3076c
--- /dev/null
+++ b/usr.sbin/pkg/pkg.c
@@ -0,0 +1,1109 @@
+/*-
+ * Copyright (c) 2012-2014 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
+ * 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 <sys/cdefs.h>
+__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>
+#include <fetch.h>
+#include <paths.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ucl.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;
+};
+
+struct pubkey {
+ unsigned char *sig;
+ int siglen;
+};
+
+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)
+{
+ 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)
+{
+ 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, "");
+ if (remote == NULL) {
+ --retry;
+ if (retry <= 0)
+ goto fetchfail;
+ if (mirrors == NULL) {
+ sleep(1);
+ } else {
+ 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);
+}
+
+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_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)
+{
+ 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 char *sigfile, 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;
+
+ SSL_load_error_strings();
+
+ /* 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 (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. */
+ 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 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;
+ 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_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;
+ 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, NULL, 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) {
+ free(sc->cert);
+ free(sc->sig);
+ free(sc->name);
+ free(sc);
+ }
+
+ return (ret);
+}
+
+static int
+bootstrap_pkg(bool force)
+{
+ 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];
+
+ 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);
+ 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, "NONE") != 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;
+ }
+ }
+
+ if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0)
+ ret = install_pkg_static(pkgstatic, tmppkg, force);
+
+ goto cleanup;
+
+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");
+
+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 int
+pkg_query_yes_no(void)
+{
+ int ret, c;
+
+ 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);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char pkgpath[MAXPATHLEN];
+ const char *pkgarg;
+ bool bootstrap_only, force, yes;
+
+ bootstrap_only = false;
+ force = false;
+ pkgarg = NULL;
+ yes = false;
+
+ snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg",
+ getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
+
+ if (argc > 1 && strcmp(argv[1], "bootstrap") == 0) {
+ bootstrap_only = true;
+ if (argc == 3 && strcmp(argv[2], "-f") == 0)
+ force = true;
+ }
+
+ 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 argument is given as argv[1].
+ */
+ if (argv[1] != NULL && strcmp(argv[1], "-N") == 0)
+ errx(EXIT_FAILURE, "pkg is not installed");
+
+ config_init();
+
+ if (argc > 1 && strcmp(argv[1], "add") == 0) {
+ if (argc > 2 && strcmp(argv[2], "-f") == 0) {
+ force = true;
+ pkgarg = argv[3];
+ } else
+ pkgarg = argv[2];
+ if (pkgarg == NULL) {
+ fprintf(stderr, "Path to pkg.txz 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.
+ */
+ 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) != 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);
+}
OpenPOWER on IntegriCloud