pkg.c revision 287873
1189251Ssam/*- 2189251Ssam * Copyright (c) 2012-2014 Baptiste Daroussin <bapt@FreeBSD.org> 3189251Ssam * Copyright (c) 2013 Bryan Drewery <bdrewery@FreeBSD.org> 4189251Ssam * All rights reserved. 5252726Srpaulo * 6252726Srpaulo * Redistribution and use in source and binary forms, with or without 7189251Ssam * modification, are permitted provided that the following conditions 8189251Ssam * are met: 9189251Ssam * 1. Redistributions of source code must retain the above copyright 10189251Ssam * notice, this list of conditions and the following disclaimer. 11189251Ssam * 2. Redistributions in binary form must reproduce the above copyright 12189251Ssam * notice, this list of conditions and the following disclaimer in the 13189251Ssam * documentation and/or other materials provided with the distribution. 14189251Ssam * 15189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16281806Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18189251Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24189251Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25189251Ssam * SUCH DAMAGE. 26189251Ssam */ 27189251Ssam 28189251Ssam#include <sys/cdefs.h> 29281806Srpaulo__FBSDID("$FreeBSD: releng/10.1/usr.sbin/pkg/pkg.c 287873 2015-09-16 21:00:21Z delphij $"); 30189251Ssam 31281806Srpaulo#include <sys/param.h> 32189251Ssam#include <sys/queue.h> 33189251Ssam#include <sys/types.h> 34189251Ssam#include <sys/sbuf.h> 35189251Ssam#include <sys/wait.h> 36189251Ssam 37189251Ssam#define _WITH_GETLINE 38189251Ssam#include <archive.h> 39189251Ssam#include <archive_entry.h> 40189251Ssam#include <dirent.h> 41189251Ssam#include <err.h> 42189251Ssam#include <errno.h> 43189251Ssam#include <fcntl.h> 44189251Ssam#include <fetch.h> 45189251Ssam#include <paths.h> 46189251Ssam#include <stdbool.h> 47189251Ssam#include <stdlib.h> 48189251Ssam#include <stdio.h> 49189251Ssam#include <string.h> 50189251Ssam#include <time.h> 51189251Ssam#include <unistd.h> 52189251Ssam#include <ucl.h> 53189251Ssam 54189251Ssam#include <openssl/err.h> 55189251Ssam#include <openssl/ssl.h> 56189251Ssam 57189251Ssam#include "dns_utils.h" 58189251Ssam#include "config.h" 59189251Ssam 60189251Ssamstruct sig_cert { 61189251Ssam char *name; 62189251Ssam unsigned char *sig; 63189251Ssam int siglen; 64189251Ssam unsigned char *cert; 65189251Ssam int certlen; 66189251Ssam bool trusted; 67189251Ssam}; 68189251Ssam 69189251Ssamstruct pubkey { 70189251Ssam unsigned char *sig; 71189251Ssam int siglen; 72189251Ssam}; 73189251Ssam 74189251Ssamtypedef enum { 75189251Ssam HASH_UNKNOWN, 76189251Ssam HASH_SHA256, 77189251Ssam} hash_t; 78189251Ssam 79189251Ssamstruct fingerprint { 80189251Ssam hash_t type; 81189251Ssam char *name; 82189251Ssam char hash[BUFSIZ]; 83189251Ssam STAILQ_ENTRY(fingerprint) next; 84189251Ssam}; 85189251Ssam 86189251SsamSTAILQ_HEAD(fingerprint_list, fingerprint); 87189251Ssam 88189251Ssamstatic int 89189251Ssamextract_pkg_static(int fd, char *p, int sz) 90189251Ssam{ 91189251Ssam struct archive *a; 92189251Ssam struct archive_entry *ae; 93189251Ssam char *end; 94189251Ssam int ret, r; 95189251Ssam 96189251Ssam ret = -1; 97189251Ssam a = archive_read_new(); 98189251Ssam if (a == NULL) { 99189251Ssam warn("archive_read_new"); 100189251Ssam return (ret); 101189251Ssam } 102189251Ssam archive_read_support_filter_all(a); 103189251Ssam archive_read_support_format_tar(a); 104189251Ssam 105189251Ssam if (lseek(fd, 0, 0) == -1) { 106189251Ssam warn("lseek"); 107337817Scy goto cleanup; 108189251Ssam } 109189251Ssam 110189251Ssam if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) { 111189251Ssam warnx("archive_read_open_fd: %s", archive_error_string(a)); 112189251Ssam goto cleanup; 113189251Ssam } 114189251Ssam 115189251Ssam ae = NULL; 116189251Ssam while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) { 117189251Ssam end = strrchr(archive_entry_pathname(ae), '/'); 118189251Ssam if (end == NULL) 119189251Ssam continue; 120189251Ssam 121189251Ssam if (strcmp(end, "/pkg-static") == 0) { 122189251Ssam r = archive_read_extract(a, ae, 123189251Ssam ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | 124189251Ssam ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL | 125189251Ssam ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR); 126189251Ssam strlcpy(p, archive_entry_pathname(ae), sz); 127189251Ssam break; 128189251Ssam } 129346981Scy } 130346981Scy 131189251Ssam if (r == ARCHIVE_OK) 132189251Ssam ret = 0; 133189251Ssam else 134189251Ssam warnx("failed to extract pkg-static: %s", 135189251Ssam archive_error_string(a)); 136189251Ssam 137189251Ssamcleanup: 138189251Ssam archive_read_free(a); 139337817Scy return (ret); 140189251Ssam 141189251Ssam} 142189251Ssam 143189251Ssamstatic int 144189251Ssaminstall_pkg_static(const char *path, const char *pkgpath, bool force) 145189251Ssam{ 146189251Ssam int pstat; 147189251Ssam pid_t pid; 148189251Ssam 149189251Ssam switch ((pid = fork())) { 150189251Ssam case -1: 151189251Ssam return (-1); 152189251Ssam case 0: 153189251Ssam if (force) 154189251Ssam execl(path, "pkg-static", "add", "-f", pkgpath, 155189251Ssam (char *)NULL); 156189251Ssam else 157189251Ssam execl(path, "pkg-static", "add", pkgpath, 158189251Ssam (char *)NULL); 159189251Ssam _exit(1); 160189251Ssam default: 161189251Ssam break; 162189251Ssam } 163189251Ssam 164189251Ssam while (waitpid(pid, &pstat, 0) == -1) 165189251Ssam if (errno != EINTR) 166189251Ssam return (-1); 167189251Ssam 168189251Ssam if (WEXITSTATUS(pstat)) 169189251Ssam return (WEXITSTATUS(pstat)); 170189251Ssam else if (WIFSIGNALED(pstat)) 171189251Ssam return (128 & (WTERMSIG(pstat))); 172189251Ssam return (pstat); 173189251Ssam} 174189251Ssam 175189251Ssamstatic int 176189251Ssamfetch_to_fd(const char *url, char *path) 177189251Ssam{ 178189251Ssam struct url *u; 179189251Ssam struct dns_srvinfo *mirrors, *current; 180189251Ssam struct url_stat st; 181189251Ssam FILE *remote; 182189251Ssam /* To store _https._tcp. + hostname + \0 */ 183189251Ssam int fd; 184337817Scy int retry, max_retry; 185189251Ssam off_t done, r; 186189251Ssam time_t now, last; 187189251Ssam char buf[10240]; 188189251Ssam char zone[MAXHOSTNAMELEN + 13]; 189189251Ssam static const char *mirror_type = NULL; 190189251Ssam 191189251Ssam done = 0; 192189251Ssam last = 0; 193189251Ssam max_retry = 3; 194189251Ssam current = mirrors = NULL; 195189251Ssam remote = NULL; 196189251Ssam 197189251Ssam if (mirror_type == NULL && config_string(MIRROR_TYPE, &mirror_type) 198189251Ssam != 0) { 199189251Ssam warnx("No MIRROR_TYPE defined"); 200189251Ssam return (-1); 201189251Ssam } 202189251Ssam 203189251Ssam if ((fd = mkstemp(path)) == -1) { 204337817Scy warn("mkstemp()"); 205337817Scy return (-1); 206189251Ssam } 207189251Ssam 208189251Ssam retry = max_retry; 209189251Ssam 210189251Ssam u = fetchParseURL(url); 211189251Ssam while (remote == NULL) { 212189251Ssam if (retry == max_retry) { 213189251Ssam if (strcmp(u->scheme, "file") != 0 && 214337817Scy strcasecmp(mirror_type, "srv") == 0) { 215337817Scy snprintf(zone, sizeof(zone), 216337817Scy "_%s._tcp.%s", u->scheme, u->host); 217189251Ssam mirrors = dns_getsrvinfo(zone); 218189251Ssam current = mirrors; 219189251Ssam } 220189251Ssam } 221189251Ssam 222189251Ssam if (mirrors != NULL) { 223189251Ssam strlcpy(u->host, current->host, sizeof(u->host)); 224189251Ssam u->port = current->port; 225337817Scy } 226189251Ssam 227189251Ssam remote = fetchXGet(u, &st, ""); 228189251Ssam if (remote == NULL) { 229189251Ssam --retry; 230189251Ssam if (retry <= 0) 231189251Ssam goto fetchfail; 232189251Ssam if (mirrors == NULL) { 233189251Ssam sleep(1); 234189251Ssam } else { 235189251Ssam current = current->next; 236189251Ssam if (current == NULL) 237189251Ssam current = mirrors; 238189251Ssam } 239189251Ssam } 240189251Ssam } 241189251Ssam 242189251Ssam if (remote == NULL) 243189251Ssam goto fetchfail; 244189251Ssam 245189251Ssam while (done < st.size) { 246189251Ssam if ((r = fread(buf, 1, sizeof(buf), remote)) < 1) 247189251Ssam break; 248189251Ssam 249189251Ssam if (write(fd, buf, r) != r) { 250189251Ssam warn("write()"); 251189251Ssam goto fetchfail; 252189251Ssam } 253189251Ssam 254189251Ssam done += r; 255189251Ssam now = time(NULL); 256189251Ssam if (now > last || done == st.size) 257189251Ssam last = now; 258189251Ssam } 259189251Ssam 260189251Ssam if (ferror(remote)) 261189251Ssam goto fetchfail; 262189251Ssam 263189251Ssam goto cleanup; 264189251Ssam 265189251Ssamfetchfail: 266189251Ssam if (fd != -1) { 267189251Ssam close(fd); 268189251Ssam fd = -1; 269189251Ssam unlink(path); 270189251Ssam } 271189251Ssam 272189251Ssamcleanup: 273189251Ssam if (remote != NULL) 274189251Ssam fclose(remote); 275189251Ssam 276189251Ssam return fd; 277189251Ssam} 278189251Ssam 279189251Ssamstatic struct fingerprint * 280189251Ssamparse_fingerprint(ucl_object_t *obj) 281189251Ssam{ 282189251Ssam const ucl_object_t *cur; 283189251Ssam ucl_object_iter_t it = NULL; 284189251Ssam const char *function, *fp, *key; 285189251Ssam struct fingerprint *f; 286189251Ssam hash_t fct = HASH_UNKNOWN; 287189251Ssam 288189251Ssam function = fp = NULL; 289189251Ssam 290189251Ssam while ((cur = ucl_iterate_object(obj, &it, true))) { 291189251Ssam key = ucl_object_key(cur); 292189251Ssam if (cur->type != UCL_STRING) 293189251Ssam continue; 294189251Ssam if (strcasecmp(key, "function") == 0) { 295189251Ssam function = ucl_object_tostring(cur); 296189251Ssam continue; 297189251Ssam } 298189251Ssam if (strcasecmp(key, "fingerprint") == 0) { 299189251Ssam fp = ucl_object_tostring(cur); 300189251Ssam continue; 301189251Ssam } 302189251Ssam } 303189251Ssam 304189251Ssam if (fp == NULL || function == NULL) 305189251Ssam return (NULL); 306189251Ssam 307189251Ssam if (strcasecmp(function, "sha256") == 0) 308189251Ssam fct = HASH_SHA256; 309189251Ssam 310189251Ssam if (fct == HASH_UNKNOWN) { 311189251Ssam warnx("Unsupported hashing function: %s", function); 312189251Ssam return (NULL); 313189251Ssam } 314189251Ssam 315189251Ssam f = calloc(1, sizeof(struct fingerprint)); 316189251Ssam f->type = fct; 317189251Ssam strlcpy(f->hash, fp, sizeof(f->hash)); 318189251Ssam 319189251Ssam return (f); 320189251Ssam} 321189251Ssam 322189251Ssamstatic void 323189251Ssamfree_fingerprint_list(struct fingerprint_list* list) 324189251Ssam{ 325189251Ssam struct fingerprint *fingerprint, *tmp; 326189251Ssam 327189251Ssam STAILQ_FOREACH_SAFE(fingerprint, list, next, tmp) { 328189251Ssam if (fingerprint->name) 329189251Ssam free(fingerprint->name); 330189251Ssam free(fingerprint); 331189251Ssam } 332189251Ssam free(list); 333189251Ssam} 334189251Ssam 335189251Ssamstatic struct fingerprint * 336189251Ssamload_fingerprint(const char *dir, const char *filename) 337189251Ssam{ 338189251Ssam ucl_object_t *obj = NULL; 339189251Ssam struct ucl_parser *p = NULL; 340189251Ssam struct fingerprint *f; 341189251Ssam char path[MAXPATHLEN]; 342189251Ssam 343189251Ssam f = NULL; 344189251Ssam 345189251Ssam snprintf(path, MAXPATHLEN, "%s/%s", dir, filename); 346189251Ssam 347189251Ssam p = ucl_parser_new(0); 348189251Ssam if (!ucl_parser_add_file(p, path)) { 349189251Ssam warnx("%s: %s", path, ucl_parser_get_error(p)); 350189251Ssam ucl_parser_free(p); 351189251Ssam return (NULL); 352189251Ssam } 353189251Ssam 354189251Ssam obj = ucl_parser_get_object(p); 355189251Ssam 356189251Ssam if (obj->type == UCL_OBJECT) 357189251Ssam f = parse_fingerprint(obj); 358189251Ssam 359189251Ssam if (f != NULL) 360189251Ssam f->name = strdup(filename); 361189251Ssam 362189251Ssam ucl_object_unref(obj); 363189251Ssam ucl_parser_free(p); 364189251Ssam 365189251Ssam return (f); 366189251Ssam} 367189251Ssam 368189251Ssamstatic struct fingerprint_list * 369189251Ssamload_fingerprints(const char *path, int *count) 370189251Ssam{ 371189251Ssam DIR *d; 372189251Ssam struct dirent *ent; 373189251Ssam struct fingerprint *finger; 374189251Ssam struct fingerprint_list *fingerprints; 375189251Ssam 376189251Ssam *count = 0; 377189251Ssam 378189251Ssam fingerprints = calloc(1, sizeof(struct fingerprint_list)); 379189251Ssam if (fingerprints == NULL) 380189251Ssam return (NULL); 381189251Ssam STAILQ_INIT(fingerprints); 382189251Ssam 383189251Ssam if ((d = opendir(path)) == NULL) 384189251Ssam return (NULL); 385189251Ssam 386189251Ssam while ((ent = readdir(d))) { 387189251Ssam if (strcmp(ent->d_name, ".") == 0 || 388189251Ssam strcmp(ent->d_name, "..") == 0) 389189251Ssam continue; 390189251Ssam finger = load_fingerprint(path, ent->d_name); 391189251Ssam if (finger != NULL) { 392189251Ssam STAILQ_INSERT_TAIL(fingerprints, finger, next); 393189251Ssam ++(*count); 394189251Ssam } 395189251Ssam } 396189251Ssam 397189251Ssam closedir(d); 398189251Ssam 399189251Ssam return (fingerprints); 400189251Ssam} 401189251Ssam 402189251Ssamstatic void 403189251Ssamsha256_hash(unsigned char hash[SHA256_DIGEST_LENGTH], 404189251Ssam char out[SHA256_DIGEST_LENGTH * 2 + 1]) 405189251Ssam{ 406189251Ssam int i; 407189251Ssam 408189251Ssam for (i = 0; i < SHA256_DIGEST_LENGTH; i++) 409189251Ssam sprintf(out + (i * 2), "%02x", hash[i]); 410189251Ssam 411189251Ssam out[SHA256_DIGEST_LENGTH * 2] = '\0'; 412189251Ssam} 413189251Ssam 414189251Ssamstatic void 415189251Ssamsha256_buf(char *buf, size_t len, char out[SHA256_DIGEST_LENGTH * 2 + 1]) 416189251Ssam{ 417189251Ssam unsigned char hash[SHA256_DIGEST_LENGTH]; 418189251Ssam SHA256_CTX sha256; 419189251Ssam 420189251Ssam out[0] = '\0'; 421189251Ssam 422189251Ssam SHA256_Init(&sha256); 423189251Ssam SHA256_Update(&sha256, buf, len); 424189251Ssam SHA256_Final(hash, &sha256); 425189251Ssam sha256_hash(hash, out); 426189251Ssam} 427189251Ssam 428189251Ssamstatic int 429189251Ssamsha256_fd(int fd, char out[SHA256_DIGEST_LENGTH * 2 + 1]) 430189251Ssam{ 431189251Ssam int my_fd; 432189251Ssam FILE *fp; 433189251Ssam char buffer[BUFSIZ]; 434189251Ssam unsigned char hash[SHA256_DIGEST_LENGTH]; 435189251Ssam size_t r; 436189251Ssam int ret; 437189251Ssam SHA256_CTX sha256; 438189251Ssam 439189251Ssam my_fd = -1; 440189251Ssam fp = NULL; 441189251Ssam r = 0; 442189251Ssam ret = 1; 443189251Ssam 444189251Ssam out[0] = '\0'; 445189251Ssam 446189251Ssam /* Duplicate the fd so that fclose(3) does not close it. */ 447189251Ssam if ((my_fd = dup(fd)) == -1) { 448189251Ssam warnx("dup"); 449189251Ssam goto cleanup; 450189251Ssam } 451189251Ssam 452189251Ssam if ((fp = fdopen(my_fd, "rb")) == NULL) { 453189251Ssam warnx("fdopen"); 454189251Ssam goto cleanup; 455189251Ssam } 456189251Ssam 457189251Ssam SHA256_Init(&sha256); 458189251Ssam 459189251Ssam while ((r = fread(buffer, 1, BUFSIZ, fp)) > 0) 460189251Ssam SHA256_Update(&sha256, buffer, r); 461189251Ssam 462189251Ssam if (ferror(fp) != 0) { 463189251Ssam warnx("fread"); 464189251Ssam goto cleanup; 465189251Ssam } 466189251Ssam 467189251Ssam SHA256_Final(hash, &sha256); 468189251Ssam sha256_hash(hash, out); 469189251Ssam ret = 0; 470189251Ssam 471189251Ssamcleanup: 472189251Ssam if (fp != NULL) 473189251Ssam fclose(fp); 474189251Ssam else if (my_fd != -1) 475189251Ssam close(my_fd); 476189251Ssam (void)lseek(fd, 0, SEEK_SET); 477189251Ssam 478189251Ssam return (ret); 479189251Ssam} 480189251Ssam 481189251Ssamstatic EVP_PKEY * 482189251Ssamload_public_key_file(const char *file) 483189251Ssam{ 484189251Ssam EVP_PKEY *pkey; 485189251Ssam BIO *bp; 486189251Ssam char errbuf[1024]; 487189251Ssam 488189251Ssam bp = BIO_new_file(file, "r"); 489189251Ssam if (!bp) 490189251Ssam errx(EXIT_FAILURE, "Unable to read %s", file); 491189251Ssam 492189251Ssam if ((pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL)) == NULL) 493189251Ssam warnx("ici: %s", ERR_error_string(ERR_get_error(), errbuf)); 494189251Ssam 495189251Ssam BIO_free(bp); 496189251Ssam 497189251Ssam return (pkey); 498189251Ssam} 499189251Ssam 500189251Ssamstatic EVP_PKEY * 501189251Ssamload_public_key_buf(const unsigned char *cert, int certlen) 502189251Ssam{ 503189251Ssam EVP_PKEY *pkey; 504189251Ssam BIO *bp; 505189251Ssam char errbuf[1024]; 506189251Ssam 507189251Ssam bp = BIO_new_mem_buf(__DECONST(void *, cert), certlen); 508189251Ssam 509189251Ssam if ((pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL)) == NULL) 510189251Ssam warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); 511189251Ssam 512189251Ssam BIO_free(bp); 513189251Ssam 514189251Ssam return (pkey); 515189251Ssam} 516189251Ssam 517189251Ssamstatic bool 518189251Ssamrsa_verify_cert(int fd, const char *sigfile, const unsigned char *key, 519189251Ssam int keylen, unsigned char *sig, int siglen) 520189251Ssam{ 521189251Ssam EVP_MD_CTX *mdctx; 522189251Ssam EVP_PKEY *pkey; 523189251Ssam char sha256[(SHA256_DIGEST_LENGTH * 2) + 2]; 524189251Ssam char errbuf[1024]; 525189251Ssam bool ret; 526189251Ssam 527189251Ssam pkey = NULL; 528189251Ssam mdctx = NULL; 529189251Ssam ret = false; 530189251Ssam 531189251Ssam SSL_load_error_strings(); 532189251Ssam 533189251Ssam /* Compute SHA256 of the package. */ 534189251Ssam if (lseek(fd, 0, 0) == -1) { 535189251Ssam warn("lseek"); 536189251Ssam goto cleanup; 537189251Ssam } 538189251Ssam if ((sha256_fd(fd, sha256)) == -1) { 539189251Ssam warnx("Error creating SHA256 hash for package"); 540189251Ssam goto cleanup; 541189251Ssam } 542189251Ssam 543189251Ssam if (sigfile != NULL) { 544189251Ssam if ((pkey = load_public_key_file(sigfile)) == NULL) { 545189251Ssam warnx("Error reading public key"); 546189251Ssam goto cleanup; 547189251Ssam } 548189251Ssam } else { 549189251Ssam if ((pkey = load_public_key_buf(key, keylen)) == NULL) { 550189251Ssam warnx("Error reading public key"); 551189251Ssam goto cleanup; 552189251Ssam } 553189251Ssam } 554189251Ssam 555189251Ssam /* Verify signature of the SHA256(pkg) is valid. */ 556189251Ssam if ((mdctx = EVP_MD_CTX_create()) == NULL) { 557189251Ssam warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); 558189251Ssam goto error; 559189251Ssam } 560189251Ssam 561189251Ssam if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pkey) != 1) { 562189251Ssam warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); 563189251Ssam goto error; 564189251Ssam } 565189251Ssam if (EVP_DigestVerifyUpdate(mdctx, sha256, strlen(sha256)) != 1) { 566189251Ssam warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); 567189251Ssam goto error; 568189251Ssam } 569189251Ssam 570189251Ssam if (EVP_DigestVerifyFinal(mdctx, sig, siglen) != 1) { 571189251Ssam warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); 572189251Ssam goto error; 573189251Ssam } 574189251Ssam 575189251Ssam ret = true; 576189251Ssam printf("done\n"); 577189251Ssam goto cleanup; 578189251Ssam 579189251Ssamerror: 580189251Ssam printf("failed\n"); 581189251Ssam 582189251Ssamcleanup: 583189251Ssam if (pkey) 584189251Ssam EVP_PKEY_free(pkey); 585189251Ssam if (mdctx) 586189251Ssam EVP_MD_CTX_destroy(mdctx); 587189251Ssam ERR_free_strings(); 588189251Ssam 589189251Ssam return (ret); 590189251Ssam} 591189251Ssam 592189251Ssamstatic struct pubkey * 593189251Ssamread_pubkey(int fd) 594189251Ssam{ 595189251Ssam struct pubkey *pk; 596189251Ssam struct sbuf *sig; 597189251Ssam char buf[4096]; 598189251Ssam int r; 599189251Ssam 600189251Ssam if (lseek(fd, 0, 0) == -1) { 601189251Ssam warn("lseek"); 602189251Ssam return (NULL); 603189251Ssam } 604189251Ssam 605189251Ssam sig = sbuf_new_auto(); 606189251Ssam 607189251Ssam while ((r = read(fd, buf, sizeof(buf))) >0) { 608189251Ssam sbuf_bcat(sig, buf, r); 609189251Ssam } 610189251Ssam 611189251Ssam sbuf_finish(sig); 612189251Ssam pk = calloc(1, sizeof(struct pubkey)); 613189251Ssam pk->siglen = sbuf_len(sig); 614189251Ssam pk->sig = calloc(1, pk->siglen); 615189251Ssam memcpy(pk->sig, sbuf_data(sig), pk->siglen); 616189251Ssam sbuf_delete(sig); 617189251Ssam 618189251Ssam return (pk); 619189251Ssam} 620189251Ssam 621189251Ssamstatic struct sig_cert * 622189251Ssamparse_cert(int fd) { 623189251Ssam int my_fd; 624189251Ssam struct sig_cert *sc; 625189251Ssam FILE *fp; 626189251Ssam struct sbuf *buf, *sig, *cert; 627189251Ssam char *line; 628189251Ssam size_t linecap; 629189251Ssam ssize_t linelen; 630189251Ssam 631189251Ssam buf = NULL; 632189251Ssam my_fd = -1; 633189251Ssam sc = NULL; 634189251Ssam line = NULL; 635189251Ssam linecap = 0; 636189251Ssam 637189251Ssam if (lseek(fd, 0, 0) == -1) { 638189251Ssam warn("lseek"); 639189251Ssam return (NULL); 640189251Ssam } 641189251Ssam 642189251Ssam /* Duplicate the fd so that fclose(3) does not close it. */ 643189251Ssam if ((my_fd = dup(fd)) == -1) { 644189251Ssam warnx("dup"); 645189251Ssam return (NULL); 646189251Ssam } 647189251Ssam 648189251Ssam if ((fp = fdopen(my_fd, "rb")) == NULL) { 649189251Ssam warn("fdopen"); 650189251Ssam close(my_fd); 651189251Ssam return (NULL); 652189251Ssam } 653189251Ssam 654189251Ssam sig = sbuf_new_auto(); 655189251Ssam cert = sbuf_new_auto(); 656189251Ssam 657189251Ssam while ((linelen = getline(&line, &linecap, fp)) > 0) { 658189251Ssam if (strcmp(line, "SIGNATURE\n") == 0) { 659189251Ssam buf = sig; 660189251Ssam continue; 661189251Ssam } else if (strcmp(line, "CERT\n") == 0) { 662189251Ssam buf = cert; 663189251Ssam continue; 664189251Ssam } else if (strcmp(line, "END\n") == 0) { 665189251Ssam break; 666189251Ssam } 667189251Ssam if (buf != NULL) 668189251Ssam sbuf_bcat(buf, line, linelen); 669189251Ssam } 670189251Ssam 671189251Ssam fclose(fp); 672189251Ssam 673189251Ssam /* Trim out unrelated trailing newline */ 674189251Ssam sbuf_setpos(sig, sbuf_len(sig) - 1); 675189251Ssam 676189251Ssam sbuf_finish(sig); 677189251Ssam sbuf_finish(cert); 678189251Ssam 679189251Ssam sc = calloc(1, sizeof(struct sig_cert)); 680189251Ssam sc->siglen = sbuf_len(sig); 681189251Ssam sc->sig = calloc(1, sc->siglen); 682189251Ssam memcpy(sc->sig, sbuf_data(sig), sc->siglen); 683189251Ssam 684189251Ssam sc->certlen = sbuf_len(cert); 685189251Ssam sc->cert = strdup(sbuf_data(cert)); 686189251Ssam 687189251Ssam sbuf_delete(sig); 688189251Ssam sbuf_delete(cert); 689189251Ssam 690189251Ssam return (sc); 691189251Ssam} 692189251Ssam 693189251Ssamstatic bool 694189251Ssamverify_pubsignature(int fd_pkg, int fd_sig) 695189251Ssam{ 696337817Scy struct pubkey *pk; 697337817Scy const char *pubkey; 698281806Srpaulo bool ret; 699189251Ssam 700189251Ssam pk = NULL; 701189251Ssam pubkey = NULL; 702189251Ssam ret = false; 703189251Ssam if (config_string(PUBKEY, &pubkey) != 0) { 704189251Ssam warnx("No CONFIG_PUBKEY defined"); 705189251Ssam goto cleanup; 706189251Ssam } 707189251Ssam 708189251Ssam if ((pk = read_pubkey(fd_sig)) == NULL) { 709189251Ssam warnx("Error reading signature"); 710189251Ssam goto cleanup; 711189251Ssam } 712189251Ssam 713189251Ssam /* Verify the signature. */ 714189251Ssam printf("Verifying signature with public key %s... ", pubkey); 715189251Ssam if (rsa_verify_cert(fd_pkg, pubkey, NULL, 0, pk->sig, 716189251Ssam pk->siglen) == false) { 717189251Ssam fprintf(stderr, "Signature is not valid\n"); 718189251Ssam goto cleanup; 719189251Ssam } 720189251Ssam 721189251Ssam ret = true; 722189251Ssam 723189251Ssamcleanup: 724189251Ssam if (pk) { 725189251Ssam free(pk->sig); 726189251Ssam free(pk); 727189251Ssam } 728189251Ssam 729189251Ssam return (ret); 730189251Ssam} 731189251Ssam 732189251Ssamstatic bool 733189251Ssamverify_signature(int fd_pkg, int fd_sig) 734189251Ssam{ 735189251Ssam struct fingerprint_list *trusted, *revoked; 736189251Ssam struct fingerprint *fingerprint; 737189251Ssam struct sig_cert *sc; 738189251Ssam bool ret; 739189251Ssam int trusted_count, revoked_count; 740189251Ssam const char *fingerprints; 741189251Ssam char path[MAXPATHLEN]; 742189251Ssam char hash[SHA256_DIGEST_LENGTH * 2 + 1]; 743189251Ssam 744189251Ssam sc = NULL; 745189251Ssam trusted = revoked = NULL; 746189251Ssam ret = false; 747189251Ssam 748189251Ssam /* Read and parse fingerprints. */ 749189251Ssam if (config_string(FINGERPRINTS, &fingerprints) != 0) { 750189251Ssam warnx("No CONFIG_FINGERPRINTS defined"); 751189251Ssam goto cleanup; 752189251Ssam } 753189251Ssam 754189251Ssam snprintf(path, MAXPATHLEN, "%s/trusted", fingerprints); 755189251Ssam if ((trusted = load_fingerprints(path, &trusted_count)) == NULL) { 756189251Ssam warnx("Error loading trusted certificates"); 757189251Ssam goto cleanup; 758189251Ssam } 759189251Ssam 760189251Ssam if (trusted_count == 0 || trusted == NULL) { 761189251Ssam fprintf(stderr, "No trusted certificates found.\n"); 762189251Ssam goto cleanup; 763189251Ssam } 764189251Ssam 765189251Ssam snprintf(path, MAXPATHLEN, "%s/revoked", fingerprints); 766189251Ssam if ((revoked = load_fingerprints(path, &revoked_count)) == NULL) { 767189251Ssam warnx("Error loading revoked certificates"); 768189251Ssam goto cleanup; 769189251Ssam } 770189251Ssam 771189251Ssam /* Read certificate and signature in. */ 772189251Ssam if ((sc = parse_cert(fd_sig)) == NULL) { 773189251Ssam warnx("Error parsing certificate"); 774189251Ssam goto cleanup; 775189251Ssam } 776189251Ssam /* Explicitly mark as non-trusted until proven otherwise. */ 777189251Ssam sc->trusted = false; 778189251Ssam 779189251Ssam /* Parse signature and pubkey out of the certificate */ 780189251Ssam sha256_buf(sc->cert, sc->certlen, hash); 781189251Ssam 782189251Ssam /* Check if this hash is revoked */ 783189251Ssam if (revoked != NULL) { 784189251Ssam STAILQ_FOREACH(fingerprint, revoked, next) { 785189251Ssam if (strcasecmp(fingerprint->hash, hash) == 0) { 786189251Ssam fprintf(stderr, "The package was signed with " 787189251Ssam "revoked certificate %s\n", 788189251Ssam fingerprint->name); 789189251Ssam goto cleanup; 790189251Ssam } 791189251Ssam } 792189251Ssam } 793189251Ssam 794189251Ssam STAILQ_FOREACH(fingerprint, trusted, next) { 795189251Ssam if (strcasecmp(fingerprint->hash, hash) == 0) { 796189251Ssam sc->trusted = true; 797189251Ssam sc->name = strdup(fingerprint->name); 798189251Ssam break; 799189251Ssam } 800189251Ssam } 801189251Ssam 802189251Ssam if (sc->trusted == false) { 803189251Ssam fprintf(stderr, "No trusted fingerprint found matching " 804189251Ssam "package's certificate\n"); 805189251Ssam goto cleanup; 806189251Ssam } 807189251Ssam 808189251Ssam /* Verify the signature. */ 809189251Ssam printf("Verifying signature with trusted certificate %s... ", sc->name); 810189251Ssam if (rsa_verify_cert(fd_pkg, NULL, sc->cert, sc->certlen, sc->sig, 811189251Ssam sc->siglen) == false) { 812189251Ssam fprintf(stderr, "Signature is not valid\n"); 813189251Ssam goto cleanup; 814189251Ssam } 815189251Ssam 816189251Ssam ret = true; 817189251Ssam 818189251Ssamcleanup: 819189251Ssam if (trusted) 820189251Ssam free_fingerprint_list(trusted); 821189251Ssam if (revoked) 822189251Ssam free_fingerprint_list(revoked); 823189251Ssam if (sc) { 824189251Ssam if (sc->cert) 825189251Ssam free(sc->cert); 826189251Ssam if (sc->sig) 827189251Ssam free(sc->sig); 828189251Ssam if (sc->name) 829189251Ssam free(sc->name); 830189251Ssam free(sc); 831189251Ssam } 832189251Ssam 833189251Ssam return (ret); 834189251Ssam} 835189251Ssam 836189251Ssamstatic int 837189251Ssambootstrap_pkg(bool force) 838189251Ssam{ 839189251Ssam int fd_pkg, fd_sig; 840189251Ssam int ret; 841189251Ssam char url[MAXPATHLEN]; 842189251Ssam char tmppkg[MAXPATHLEN]; 843189251Ssam char tmpsig[MAXPATHLEN]; 844189251Ssam const char *packagesite; 845189251Ssam const char *signature_type; 846189251Ssam char pkgstatic[MAXPATHLEN]; 847189251Ssam 848189251Ssam fd_sig = -1; 849189251Ssam ret = -1; 850189251Ssam 851189251Ssam if (config_string(PACKAGESITE, &packagesite) != 0) { 852189251Ssam warnx("No PACKAGESITE defined"); 853189251Ssam return (-1); 854189251Ssam } 855189251Ssam 856189251Ssam if (config_string(SIGNATURE_TYPE, &signature_type) != 0) { 857189251Ssam warnx("Error looking up SIGNATURE_TYPE"); 858189251Ssam return (-1); 859189251Ssam } 860189251Ssam 861189251Ssam printf("Bootstrapping pkg from %s, please wait...\n", packagesite); 862189251Ssam 863189251Ssam /* Support pkg+http:// for PACKAGESITE which is the new format 864189251Ssam in 1.2 to avoid confusion on why http://pkg.FreeBSD.org has 865189251Ssam no A record. */ 866189251Ssam if (strncmp(URL_SCHEME_PREFIX, packagesite, 867189251Ssam strlen(URL_SCHEME_PREFIX)) == 0) 868189251Ssam packagesite += strlen(URL_SCHEME_PREFIX); 869189251Ssam snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", packagesite); 870189251Ssam 871189251Ssam snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX", 872189251Ssam getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); 873189251Ssam 874189251Ssam if ((fd_pkg = fetch_to_fd(url, tmppkg)) == -1) 875189251Ssam goto fetchfail; 876189251Ssam 877189251Ssam if (signature_type != NULL && 878189251Ssam strcasecmp(signature_type, "NONE") != 0) { 879189251Ssam if (strcasecmp(signature_type, "FINGERPRINTS") == 0) { 880189251Ssam 881189251Ssam snprintf(tmpsig, MAXPATHLEN, "%s/pkg.txz.sig.XXXXXX", 882189251Ssam getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); 883189251Ssam snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz.sig", 884189251Ssam packagesite); 885189251Ssam 886189251Ssam if ((fd_sig = fetch_to_fd(url, tmpsig)) == -1) { 887189251Ssam fprintf(stderr, "Signature for pkg not " 888189251Ssam "available.\n"); 889189251Ssam goto fetchfail; 890189251Ssam } 891189251Ssam 892189251Ssam if (verify_signature(fd_pkg, fd_sig) == false) 893189251Ssam goto cleanup; 894189251Ssam } else if (strcasecmp(signature_type, "PUBKEY") == 0) { 895189251Ssam 896189251Ssam snprintf(tmpsig, MAXPATHLEN, 897189251Ssam "%s/pkg.txz.pubkeysig.XXXXXX", 898189251Ssam getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); 899189251Ssam snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz.pubkeysig", 900189251Ssam packagesite); 901189251Ssam 902189251Ssam if ((fd_sig = fetch_to_fd(url, tmpsig)) == -1) { 903189251Ssam fprintf(stderr, "Signature for pkg not " 904189251Ssam "available.\n"); 905189251Ssam goto fetchfail; 906189251Ssam } 907189251Ssam 908189251Ssam if (verify_pubsignature(fd_pkg, fd_sig) == false) 909189251Ssam goto cleanup; 910189251Ssam } else { 911189251Ssam warnx("Signature type %s is not supported for " 912189251Ssam "bootstrapping.", signature_type); 913189251Ssam goto cleanup; 914189251Ssam } 915189251Ssam } 916189251Ssam 917189251Ssam if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0) 918189251Ssam ret = install_pkg_static(pkgstatic, tmppkg, force); 919189251Ssam 920189251Ssam goto cleanup; 921189251Ssam 922189251Ssamfetchfail: 923189251Ssam warnx("Error fetching %s: %s", url, fetchLastErrString); 924189251Ssam fprintf(stderr, "A pre-built version of pkg could not be found for " 925189251Ssam "your system.\n"); 926189251Ssam fprintf(stderr, "Consider changing PACKAGESITE or installing it from " 927189251Ssam "ports: 'ports-mgmt/pkg'.\n"); 928189251Ssam 929189251Ssamcleanup: 930189251Ssam if (fd_sig != -1) { 931189251Ssam close(fd_sig); 932189251Ssam unlink(tmpsig); 933189251Ssam } 934189251Ssam close(fd_pkg); 935189251Ssam unlink(tmppkg); 936189251Ssam 937189251Ssam return (ret); 938189251Ssam} 939189251Ssam 940189251Ssamstatic const char confirmation_message[] = 941189251Ssam"The package management tool is not yet installed on your system.\n" 942189251Ssam"Do you want to fetch and install it now? [y/N]: "; 943189251Ssam 944189251Ssamstatic int 945189251Ssampkg_query_yes_no(void) 946189251Ssam{ 947189251Ssam int ret, c; 948189251Ssam 949189251Ssam c = getchar(); 950189251Ssam 951189251Ssam if (c == 'y' || c == 'Y') 952189251Ssam ret = 1; 953189251Ssam else 954189251Ssam ret = 0; 955189251Ssam 956189251Ssam while (c != '\n' && c != EOF) 957189251Ssam c = getchar(); 958189251Ssam 959189251Ssam return (ret); 960189251Ssam} 961189251Ssam 962189251Ssamstatic int 963189251Ssambootstrap_pkg_local(const char *pkgpath, bool force) 964189251Ssam{ 965189251Ssam char path[MAXPATHLEN]; 966189251Ssam char pkgstatic[MAXPATHLEN]; 967189251Ssam const char *signature_type; 968189251Ssam int fd_pkg, fd_sig, ret; 969189251Ssam 970189251Ssam fd_sig = -1; 971189251Ssam ret = -1; 972189251Ssam 973189251Ssam fd_pkg = open(pkgpath, O_RDONLY); 974189251Ssam if (fd_pkg == -1) 975189251Ssam err(EXIT_FAILURE, "Unable to open %s", pkgpath); 976189251Ssam 977189251Ssam if (config_string(SIGNATURE_TYPE, &signature_type) != 0) { 978189251Ssam warnx("Error looking up SIGNATURE_TYPE"); 979189251Ssam return (-1); 980189251Ssam } 981189251Ssam if (signature_type != NULL && 982189251Ssam strcasecmp(signature_type, "NONE") != 0) { 983189251Ssam if (strcasecmp(signature_type, "FINGERPRINTS") == 0) { 984189251Ssam 985189251Ssam snprintf(path, sizeof(path), "%s.sig", pkgpath); 986189251Ssam 987189251Ssam if ((fd_sig = open(path, O_RDONLY)) == -1) { 988189251Ssam fprintf(stderr, "Signature for pkg not " 989189251Ssam "available.\n"); 990189251Ssam goto cleanup; 991189251Ssam } 992189251Ssam 993189251Ssam if (verify_signature(fd_pkg, fd_sig) == false) 994189251Ssam goto cleanup; 995189251Ssam 996189251Ssam } else if (strcasecmp(signature_type, "PUBKEY") == 0) { 997189251Ssam 998189251Ssam snprintf(path, sizeof(path), "%s.pubkeysig", pkgpath); 999189251Ssam 1000189251Ssam if ((fd_sig = open(path, O_RDONLY)) == -1) { 1001189251Ssam fprintf(stderr, "Signature for pkg not " 1002189251Ssam "available.\n"); 1003189251Ssam goto cleanup; 1004189251Ssam } 1005189251Ssam 1006189251Ssam if (verify_pubsignature(fd_pkg, fd_sig) == false) 1007189251Ssam goto cleanup; 1008189251Ssam 1009189251Ssam } else { 1010189251Ssam warnx("Signature type %s is not supported for " 1011189251Ssam "bootstrapping.", signature_type); 1012189251Ssam goto cleanup; 1013189251Ssam } 1014189251Ssam } 1015189251Ssam 1016189251Ssam if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0) 1017189251Ssam ret = install_pkg_static(pkgstatic, pkgpath, force); 1018189251Ssam 1019189251Ssamcleanup: 1020189251Ssam close(fd_pkg); 1021189251Ssam if (fd_sig != -1) 1022189251Ssam close(fd_sig); 1023189251Ssam 1024189251Ssam return (ret); 1025189251Ssam} 1026189251Ssam 1027189251Ssamint 1028189251Ssammain(__unused int argc, char *argv[]) 1029189251Ssam{ 1030189251Ssam char pkgpath[MAXPATHLEN]; 1031189251Ssam const char *pkgarg; 1032189251Ssam bool bootstrap_only, force, yes; 1033189251Ssam 1034189251Ssam bootstrap_only = false; 1035189251Ssam force = false; 1036189251Ssam pkgarg = NULL; 1037189251Ssam yes = false; 1038189251Ssam 1039189251Ssam snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", 1040189251Ssam getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); 1041189251Ssam 1042189251Ssam if (argc > 1 && strcmp(argv[1], "bootstrap") == 0) { 1043189251Ssam bootstrap_only = true; 1044189251Ssam if (argc == 3 && strcmp(argv[2], "-f") == 0) 1045189251Ssam force = true; 1046189251Ssam } 1047189251Ssam 1048189251Ssam if ((bootstrap_only && force) || access(pkgpath, X_OK) == -1) { 1049189251Ssam /* 1050189251Ssam * To allow 'pkg -N' to be used as a reliable test for whether 1051189251Ssam * a system is configured to use pkg, don't bootstrap pkg 1052189251Ssam * when that argument is given as argv[1]. 1053189251Ssam */ 1054189251Ssam if (argv[1] != NULL && strcmp(argv[1], "-N") == 0) 1055209158Srpaulo errx(EXIT_FAILURE, "pkg is not installed"); 1056189251Ssam 1057189251Ssam config_init(); 1058189251Ssam 1059189251Ssam if (argc > 1 && strcmp(argv[1], "add") == 0) { 1060189251Ssam if (argc > 2 && strcmp(argv[2], "-f") == 0) { 1061189251Ssam force = true; 1062189251Ssam pkgarg = argv[3]; 1063189251Ssam } else 1064189251Ssam pkgarg = argv[2]; 1065189251Ssam if (pkgarg == NULL) { 1066189251Ssam fprintf(stderr, "Path to pkg.txz required\n"); 1067189251Ssam exit(EXIT_FAILURE); 1068189251Ssam } 1069189251Ssam if (access(pkgarg, R_OK) == -1) { 1070189251Ssam fprintf(stderr, "No such file: %s\n", pkgarg); 1071189251Ssam exit(EXIT_FAILURE); 1072189251Ssam } 1073189251Ssam if (bootstrap_pkg_local(pkgarg, force) != 0) 1074189251Ssam exit(EXIT_FAILURE); 1075189251Ssam exit(EXIT_SUCCESS); 1076189251Ssam } 1077189251Ssam /* 1078189251Ssam * Do not ask for confirmation if either of stdin or stdout is 1079189251Ssam * not tty. Check the environment to see if user has answer 1080189251Ssam * tucked in there already. 1081189251Ssam */ 1082189251Ssam config_bool(ASSUME_ALWAYS_YES, &yes); 1083189251Ssam if (!yes) { 1084189251Ssam printf("%s", confirmation_message); 1085189251Ssam if (!isatty(fileno(stdin))) 1086189251Ssam exit(EXIT_FAILURE); 1087189251Ssam 1088189251Ssam if (pkg_query_yes_no() == 0) 1089189251Ssam exit(EXIT_FAILURE); 1090189251Ssam } 1091189251Ssam if (bootstrap_pkg(force) != 0) 1092189251Ssam exit(EXIT_FAILURE); 1093189251Ssam config_finish(); 1094189251Ssam 1095189251Ssam if (bootstrap_only) 1096281806Srpaulo exit(EXIT_SUCCESS); 1097281806Srpaulo } else if (bootstrap_only) { 1098189251Ssam printf("pkg already bootstrapped at %s\n", pkgpath); 1099281806Srpaulo exit(EXIT_SUCCESS); 1100189251Ssam } 1101189251Ssam 1102189251Ssam execv(pkgpath, argv); 1103189251Ssam 1104189251Ssam /* NOT REACHED */ 1105189251Ssam return (EXIT_FAILURE); 1106189251Ssam} 1107189251Ssam