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