xref: /freebsd/usr.sbin/pkg/pkg.c (revision f12db248e7103a02ef63683a5d3054a5d88e9a5d)
13aa4b42aSBaptiste Daroussin /*-
29950eceeSBaptiste Daroussin  * Copyright (c) 2012-2013 Baptiste Daroussin <bapt@FreeBSD.org>
3*f12db248SBryan Drewery  * Copyright (c) 2013 Bryan Drewery <bdrewery@FreeBSD.org>
43aa4b42aSBaptiste Daroussin  * All rights reserved.
53aa4b42aSBaptiste Daroussin  *
63aa4b42aSBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
73aa4b42aSBaptiste Daroussin  * modification, are permitted provided that the following conditions
83aa4b42aSBaptiste Daroussin  * are met:
93aa4b42aSBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
103aa4b42aSBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
113aa4b42aSBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
123aa4b42aSBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in the
133aa4b42aSBaptiste Daroussin  *    documentation and/or other materials provided with the distribution.
143aa4b42aSBaptiste Daroussin  *
153aa4b42aSBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
163aa4b42aSBaptiste Daroussin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
173aa4b42aSBaptiste Daroussin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
183aa4b42aSBaptiste Daroussin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
193aa4b42aSBaptiste Daroussin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
203aa4b42aSBaptiste Daroussin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
213aa4b42aSBaptiste Daroussin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
223aa4b42aSBaptiste Daroussin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
233aa4b42aSBaptiste Daroussin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
243aa4b42aSBaptiste Daroussin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
253aa4b42aSBaptiste Daroussin  * SUCH DAMAGE.
263aa4b42aSBaptiste Daroussin  */
273aa4b42aSBaptiste Daroussin 
283aa4b42aSBaptiste Daroussin #include <sys/cdefs.h>
293aa4b42aSBaptiste Daroussin __FBSDID("$FreeBSD$");
303aa4b42aSBaptiste Daroussin 
313aa4b42aSBaptiste Daroussin #include <sys/param.h>
32*f12db248SBryan Drewery #include <sys/queue.h>
33*f12db248SBryan Drewery #include <sys/types.h>
34*f12db248SBryan Drewery #include <sys/sbuf.h>
353b05c2a8SBaptiste Daroussin #include <sys/wait.h>
363aa4b42aSBaptiste Daroussin 
37*f12db248SBryan Drewery #define _WITH_GETLINE
383aa4b42aSBaptiste Daroussin #include <archive.h>
393aa4b42aSBaptiste Daroussin #include <archive_entry.h>
40*f12db248SBryan Drewery #include <dirent.h>
413aa4b42aSBaptiste Daroussin #include <err.h>
423aa4b42aSBaptiste Daroussin #include <errno.h>
43b70213b5SBaptiste Daroussin #include <fcntl.h>
443b05c2a8SBaptiste Daroussin #include <fetch.h>
45a6454741SBaptiste Daroussin #include <paths.h>
469950eceeSBaptiste Daroussin #include <stdbool.h>
473aa4b42aSBaptiste Daroussin #include <stdlib.h>
483aa4b42aSBaptiste Daroussin #include <stdio.h>
493aa4b42aSBaptiste Daroussin #include <string.h>
503aa4b42aSBaptiste Daroussin #include <time.h>
513aa4b42aSBaptiste Daroussin #include <unistd.h>
52*f12db248SBryan Drewery #include <yaml.h>
53*f12db248SBryan Drewery 
54*f12db248SBryan Drewery #include <openssl/err.h>
55*f12db248SBryan Drewery #include <openssl/ssl.h>
563aa4b42aSBaptiste Daroussin 
5729aaa961SBaptiste Daroussin #include "dns_utils.h"
589950eceeSBaptiste Daroussin #include "config.h"
593aa4b42aSBaptiste Daroussin 
60*f12db248SBryan Drewery struct sig_cert {
61*f12db248SBryan Drewery 	unsigned char *sig;
62*f12db248SBryan Drewery 	int siglen;
63*f12db248SBryan Drewery 	unsigned char *cert;
64*f12db248SBryan Drewery 	int certlen;
65*f12db248SBryan Drewery 	bool trusted;
66*f12db248SBryan Drewery };
67*f12db248SBryan Drewery 
68*f12db248SBryan Drewery typedef enum {
69*f12db248SBryan Drewery        HASH_UNKNOWN,
70*f12db248SBryan Drewery        HASH_SHA256,
71*f12db248SBryan Drewery } hash_t;
72*f12db248SBryan Drewery 
73*f12db248SBryan Drewery struct fingerprint {
74*f12db248SBryan Drewery        hash_t type;
75*f12db248SBryan Drewery        char hash[BUFSIZ];
76*f12db248SBryan Drewery        STAILQ_ENTRY(fingerprint) next;
77*f12db248SBryan Drewery };
78*f12db248SBryan Drewery 
79*f12db248SBryan Drewery STAILQ_HEAD(fingerprint_list, fingerprint);
80*f12db248SBryan Drewery 
813aa4b42aSBaptiste Daroussin static int
823aa4b42aSBaptiste Daroussin extract_pkg_static(int fd, char *p, int sz)
833aa4b42aSBaptiste Daroussin {
843aa4b42aSBaptiste Daroussin 	struct archive *a;
853aa4b42aSBaptiste Daroussin 	struct archive_entry *ae;
863aa4b42aSBaptiste Daroussin 	char *end;
873aa4b42aSBaptiste Daroussin 	int ret, r;
883aa4b42aSBaptiste Daroussin 
89a6454741SBaptiste Daroussin 	ret = -1;
903aa4b42aSBaptiste Daroussin 	a = archive_read_new();
91a6454741SBaptiste Daroussin 	if (a == NULL) {
92a6454741SBaptiste Daroussin 		warn("archive_read_new");
93a6454741SBaptiste Daroussin 		return (ret);
94a6454741SBaptiste Daroussin 	}
95ff75c36aSBaptiste Daroussin 	archive_read_support_filter_all(a);
963aa4b42aSBaptiste Daroussin 	archive_read_support_format_tar(a);
973aa4b42aSBaptiste Daroussin 
98a6454741SBaptiste Daroussin 	if (lseek(fd, 0, 0) == -1) {
99a6454741SBaptiste Daroussin 		warn("lseek");
100a6454741SBaptiste Daroussin 		goto cleanup;
101a6454741SBaptiste Daroussin 	}
1023aa4b42aSBaptiste Daroussin 
1033aa4b42aSBaptiste Daroussin 	if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
104a6454741SBaptiste Daroussin 		warnx("archive_read_open_fd: %s", archive_error_string(a));
1053aa4b42aSBaptiste Daroussin 		goto cleanup;
1063aa4b42aSBaptiste Daroussin 	}
1073aa4b42aSBaptiste Daroussin 
1083aa4b42aSBaptiste Daroussin 	ae = NULL;
1093aa4b42aSBaptiste Daroussin 	while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
1103aa4b42aSBaptiste Daroussin 		end = strrchr(archive_entry_pathname(ae), '/');
1113aa4b42aSBaptiste Daroussin 		if (end == NULL)
1123aa4b42aSBaptiste Daroussin 			continue;
1133aa4b42aSBaptiste Daroussin 
1143aa4b42aSBaptiste Daroussin 		if (strcmp(end, "/pkg-static") == 0) {
1153aa4b42aSBaptiste Daroussin 			r = archive_read_extract(a, ae,
1163aa4b42aSBaptiste Daroussin 			    ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM |
1173aa4b42aSBaptiste Daroussin 			    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL |
1183aa4b42aSBaptiste Daroussin 			    ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR);
119a6454741SBaptiste Daroussin 			strlcpy(p, archive_entry_pathname(ae), sz);
1203aa4b42aSBaptiste Daroussin 			break;
1213aa4b42aSBaptiste Daroussin 		}
1223aa4b42aSBaptiste Daroussin 	}
1233aa4b42aSBaptiste Daroussin 
124a6454741SBaptiste Daroussin 	if (r == ARCHIVE_OK)
125a6454741SBaptiste Daroussin 		ret = 0;
126a6454741SBaptiste Daroussin 	else
1273aa4b42aSBaptiste Daroussin 		warnx("fail to extract pkg-static");
1283aa4b42aSBaptiste Daroussin 
1293aa4b42aSBaptiste Daroussin cleanup:
130ff75c36aSBaptiste Daroussin 	archive_read_free(a);
1313b05c2a8SBaptiste Daroussin 	return (ret);
1323aa4b42aSBaptiste Daroussin 
1333aa4b42aSBaptiste Daroussin }
1343aa4b42aSBaptiste Daroussin 
1353aa4b42aSBaptiste Daroussin static int
1363aa4b42aSBaptiste Daroussin install_pkg_static(char *path, char *pkgpath)
1373aa4b42aSBaptiste Daroussin {
1383aa4b42aSBaptiste Daroussin 	int pstat;
1393aa4b42aSBaptiste Daroussin 	pid_t pid;
1403aa4b42aSBaptiste Daroussin 
1413aa4b42aSBaptiste Daroussin 	switch ((pid = fork())) {
1423aa4b42aSBaptiste Daroussin 	case -1:
1433aa4b42aSBaptiste Daroussin 		return (-1);
1443aa4b42aSBaptiste Daroussin 	case 0:
145a6454741SBaptiste Daroussin 		execl(path, "pkg-static", "add", pkgpath, (char *)NULL);
1463b05c2a8SBaptiste Daroussin 		_exit(1);
1473aa4b42aSBaptiste Daroussin 	default:
1483aa4b42aSBaptiste Daroussin 		break;
1493aa4b42aSBaptiste Daroussin 	}
1503aa4b42aSBaptiste Daroussin 
151a6454741SBaptiste Daroussin 	while (waitpid(pid, &pstat, 0) == -1)
1523aa4b42aSBaptiste Daroussin 		if (errno != EINTR)
1533aa4b42aSBaptiste Daroussin 			return (-1);
1543aa4b42aSBaptiste Daroussin 
155a6454741SBaptiste Daroussin 	if (WEXITSTATUS(pstat))
1563aa4b42aSBaptiste Daroussin 		return (WEXITSTATUS(pstat));
157a6454741SBaptiste Daroussin 	else if (WIFSIGNALED(pstat))
158a6454741SBaptiste Daroussin 		return (128 & (WTERMSIG(pstat)));
159a6454741SBaptiste Daroussin 	return (pstat);
1603aa4b42aSBaptiste Daroussin }
1613aa4b42aSBaptiste Daroussin 
1623aa4b42aSBaptiste Daroussin static int
163*f12db248SBryan Drewery fetch_to_fd(const char *url, char *path)
1643aa4b42aSBaptiste Daroussin {
16529aaa961SBaptiste Daroussin 	struct url *u;
16629aaa961SBaptiste Daroussin 	struct dns_srvinfo *mirrors, *current;
167a6454741SBaptiste Daroussin 	struct url_stat st;
168*f12db248SBryan Drewery 	FILE *remote;
169*f12db248SBryan Drewery 	/* To store _https._tcp. + hostname + \0 */
170*f12db248SBryan Drewery 	int fd;
171*f12db248SBryan Drewery 	int retry, max_retry;
1723aa4b42aSBaptiste Daroussin 	off_t done, r;
173*f12db248SBryan Drewery 	time_t now, last;
174*f12db248SBryan Drewery 	char buf[10240];
175*f12db248SBryan Drewery 	char zone[MAXHOSTNAMELEN + 13];
176*f12db248SBryan Drewery 	static const char *mirror_type = NULL;
1773aa4b42aSBaptiste Daroussin 
1783aa4b42aSBaptiste Daroussin 	done = 0;
179a6454741SBaptiste Daroussin 	last = 0;
18029aaa961SBaptiste Daroussin 	max_retry = 3;
18129aaa961SBaptiste Daroussin 	current = mirrors = NULL;
182*f12db248SBryan Drewery 	remote = NULL;
1833aa4b42aSBaptiste Daroussin 
184*f12db248SBryan Drewery 	if (mirror_type == NULL && config_string(MIRROR_TYPE, &mirror_type)
185*f12db248SBryan Drewery 	    != 0) {
1869950eceeSBaptiste Daroussin 		warnx("No MIRROR_TYPE defined");
1879950eceeSBaptiste Daroussin 		return (-1);
1889950eceeSBaptiste Daroussin 	}
18962940ea9SBryan Drewery 
190*f12db248SBryan Drewery 	if ((fd = mkstemp(path)) == -1) {
1913aa4b42aSBaptiste Daroussin 		warn("mkstemp()");
1923b05c2a8SBaptiste Daroussin 		return (-1);
1933aa4b42aSBaptiste Daroussin 	}
1943aa4b42aSBaptiste Daroussin 
19529aaa961SBaptiste Daroussin 	retry = max_retry;
19629aaa961SBaptiste Daroussin 
19729aaa961SBaptiste Daroussin 	u = fetchParseURL(url);
19829aaa961SBaptiste Daroussin 	while (remote == NULL) {
19929aaa961SBaptiste Daroussin 		if (retry == max_retry) {
2009950eceeSBaptiste Daroussin 			if (strcmp(u->scheme, "file") != 0 &&
2019950eceeSBaptiste Daroussin 			    strcasecmp(mirror_type, "srv") == 0) {
20229aaa961SBaptiste Daroussin 				snprintf(zone, sizeof(zone),
20329aaa961SBaptiste Daroussin 				    "_%s._tcp.%s", u->scheme, u->host);
20429aaa961SBaptiste Daroussin 				mirrors = dns_getsrvinfo(zone);
20529aaa961SBaptiste Daroussin 				current = mirrors;
20629aaa961SBaptiste Daroussin 			}
20729aaa961SBaptiste Daroussin 		}
20829aaa961SBaptiste Daroussin 
20935e07a7aSBaptiste Daroussin 		if (mirrors != NULL) {
21029aaa961SBaptiste Daroussin 			strlcpy(u->host, current->host, sizeof(u->host));
21135e07a7aSBaptiste Daroussin 			u->port = current->port;
21235e07a7aSBaptiste Daroussin 		}
21329aaa961SBaptiste Daroussin 
21429aaa961SBaptiste Daroussin 		remote = fetchXGet(u, &st, "");
21529aaa961SBaptiste Daroussin 		if (remote == NULL) {
21629aaa961SBaptiste Daroussin 			--retry;
21729aaa961SBaptiste Daroussin 			if (retry <= 0)
21829aaa961SBaptiste Daroussin 				goto fetchfail;
21929aaa961SBaptiste Daroussin 			if (mirrors == NULL) {
2203aa4b42aSBaptiste Daroussin 				sleep(1);
22129aaa961SBaptiste Daroussin 			} else {
22229aaa961SBaptiste Daroussin 				current = current->next;
22329aaa961SBaptiste Daroussin 				if (current == NULL)
22429aaa961SBaptiste Daroussin 					current = mirrors;
22529aaa961SBaptiste Daroussin 			}
22629aaa961SBaptiste Daroussin 		}
22729aaa961SBaptiste Daroussin 	}
228a6454741SBaptiste Daroussin 
229a6454741SBaptiste Daroussin 	if (remote == NULL)
230a6454741SBaptiste Daroussin 		goto fetchfail;
2313aa4b42aSBaptiste Daroussin 
2323aa4b42aSBaptiste Daroussin 	while (done < st.size) {
2333aa4b42aSBaptiste Daroussin 		if ((r = fread(buf, 1, sizeof(buf), remote)) < 1)
2343aa4b42aSBaptiste Daroussin 			break;
2353aa4b42aSBaptiste Daroussin 
2363aa4b42aSBaptiste Daroussin 		if (write(fd, buf, r) != r) {
2373aa4b42aSBaptiste Daroussin 			warn("write()");
238*f12db248SBryan Drewery 			goto fetchfail;
2393aa4b42aSBaptiste Daroussin 		}
2403aa4b42aSBaptiste Daroussin 
2413aa4b42aSBaptiste Daroussin 		done += r;
2423aa4b42aSBaptiste Daroussin 		now = time(NULL);
243a6454741SBaptiste Daroussin 		if (now > last || done == st.size)
2443aa4b42aSBaptiste Daroussin 			last = now;
2453aa4b42aSBaptiste Daroussin 	}
2463aa4b42aSBaptiste Daroussin 
247a6454741SBaptiste Daroussin 	if (ferror(remote))
248a6454741SBaptiste Daroussin 		goto fetchfail;
2493aa4b42aSBaptiste Daroussin 
250*f12db248SBryan Drewery 	goto cleanup;
251*f12db248SBryan Drewery 
252*f12db248SBryan Drewery fetchfail:
253*f12db248SBryan Drewery 	if (fd != -1) {
254*f12db248SBryan Drewery 		close(fd);
255*f12db248SBryan Drewery 		fd = -1;
256*f12db248SBryan Drewery 		unlink(path);
257*f12db248SBryan Drewery 	}
258*f12db248SBryan Drewery 
259*f12db248SBryan Drewery cleanup:
260*f12db248SBryan Drewery 	if (remote != NULL)
261*f12db248SBryan Drewery 		fclose(remote);
262*f12db248SBryan Drewery 
263*f12db248SBryan Drewery 	return fd;
264*f12db248SBryan Drewery }
265*f12db248SBryan Drewery 
266*f12db248SBryan Drewery static struct fingerprint *
267*f12db248SBryan Drewery parse_fingerprint(yaml_document_t *doc, yaml_node_t *node)
268*f12db248SBryan Drewery {
269*f12db248SBryan Drewery 	yaml_node_pair_t *pair;
270*f12db248SBryan Drewery 	yaml_char_t *function, *fp;
271*f12db248SBryan Drewery 	struct fingerprint *f;
272*f12db248SBryan Drewery 	hash_t fct = HASH_UNKNOWN;
273*f12db248SBryan Drewery 
274*f12db248SBryan Drewery 	function = fp = NULL;
275*f12db248SBryan Drewery 
276*f12db248SBryan Drewery 	pair = node->data.mapping.pairs.start;
277*f12db248SBryan Drewery 	while (pair < node->data.mapping.pairs.top) {
278*f12db248SBryan Drewery 		yaml_node_t *key = yaml_document_get_node(doc, pair->key);
279*f12db248SBryan Drewery 		yaml_node_t *val = yaml_document_get_node(doc, pair->value);
280*f12db248SBryan Drewery 
281*f12db248SBryan Drewery 		if (key->data.scalar.length <= 0) {
282*f12db248SBryan Drewery 			++pair;
283*f12db248SBryan Drewery 			continue;
284*f12db248SBryan Drewery 		}
285*f12db248SBryan Drewery 
286*f12db248SBryan Drewery 		if (val->type != YAML_SCALAR_NODE) {
287*f12db248SBryan Drewery 			++pair;
288*f12db248SBryan Drewery 			continue;
289*f12db248SBryan Drewery 		}
290*f12db248SBryan Drewery 
291*f12db248SBryan Drewery 		if (strcasecmp(key->data.scalar.value, "function") == 0)
292*f12db248SBryan Drewery 			function = val->data.scalar.value;
293*f12db248SBryan Drewery 		else if (strcasecmp(key->data.scalar.value, "fingerprint")
294*f12db248SBryan Drewery 		    == 0)
295*f12db248SBryan Drewery 			fp = val->data.scalar.value;
296*f12db248SBryan Drewery 
297*f12db248SBryan Drewery 		++pair;
298*f12db248SBryan Drewery 		continue;
299*f12db248SBryan Drewery 	}
300*f12db248SBryan Drewery 
301*f12db248SBryan Drewery 	if (fp == NULL || function == NULL)
302*f12db248SBryan Drewery 		return (NULL);
303*f12db248SBryan Drewery 
304*f12db248SBryan Drewery 	if (strcasecmp(function, "sha256") == 0)
305*f12db248SBryan Drewery 		fct = HASH_SHA256;
306*f12db248SBryan Drewery 
307*f12db248SBryan Drewery 	if (fct == HASH_UNKNOWN) {
308*f12db248SBryan Drewery 		fprintf(stderr, "Unsupported hashing function: %s\n", function);
309*f12db248SBryan Drewery 		return (NULL);
310*f12db248SBryan Drewery 	}
311*f12db248SBryan Drewery 
312*f12db248SBryan Drewery 	f = calloc(1, sizeof(struct fingerprint));
313*f12db248SBryan Drewery 	f->type = fct;
314*f12db248SBryan Drewery 	strlcpy(f->hash, fp, sizeof(f->hash));
315*f12db248SBryan Drewery 
316*f12db248SBryan Drewery 	return (f);
317*f12db248SBryan Drewery }
318*f12db248SBryan Drewery 
319*f12db248SBryan Drewery static struct fingerprint *
320*f12db248SBryan Drewery load_fingerprint(const char *dir, const char *filename)
321*f12db248SBryan Drewery {
322*f12db248SBryan Drewery 	yaml_parser_t parser;
323*f12db248SBryan Drewery 	yaml_document_t doc;
324*f12db248SBryan Drewery 	yaml_node_t *node;
325*f12db248SBryan Drewery 	FILE *fp;
326*f12db248SBryan Drewery 	struct fingerprint *f;
327*f12db248SBryan Drewery 	char path[MAXPATHLEN];
328*f12db248SBryan Drewery 
329*f12db248SBryan Drewery 	f = NULL;
330*f12db248SBryan Drewery 
331*f12db248SBryan Drewery 	snprintf(path, MAXPATHLEN, "%s/%s", dir, filename);
332*f12db248SBryan Drewery 
333*f12db248SBryan Drewery 	if ((fp = fopen(path, "r")) == NULL)
334*f12db248SBryan Drewery 		return (NULL);
335*f12db248SBryan Drewery 
336*f12db248SBryan Drewery 	yaml_parser_initialize(&parser);
337*f12db248SBryan Drewery 	yaml_parser_set_input_file(&parser, fp);
338*f12db248SBryan Drewery 	yaml_parser_load(&parser, &doc);
339*f12db248SBryan Drewery 
340*f12db248SBryan Drewery 	node = yaml_document_get_root_node(&doc);
341*f12db248SBryan Drewery 	if (node == NULL || node->type != YAML_MAPPING_NODE)
342*f12db248SBryan Drewery 		goto out;
343*f12db248SBryan Drewery 
344*f12db248SBryan Drewery 	f = parse_fingerprint(&doc, node);
345*f12db248SBryan Drewery 
346*f12db248SBryan Drewery out:
347*f12db248SBryan Drewery 	yaml_document_delete(&doc);
348*f12db248SBryan Drewery 	yaml_parser_delete(&parser);
349*f12db248SBryan Drewery 	fclose(fp);
350*f12db248SBryan Drewery 
351*f12db248SBryan Drewery 	return (f);
352*f12db248SBryan Drewery }
353*f12db248SBryan Drewery 
354*f12db248SBryan Drewery static struct fingerprint_list *
355*f12db248SBryan Drewery load_fingerprints(const char *path, int *count)
356*f12db248SBryan Drewery {
357*f12db248SBryan Drewery 	DIR *d;
358*f12db248SBryan Drewery 	struct dirent *ent;
359*f12db248SBryan Drewery 	struct fingerprint *finger;
360*f12db248SBryan Drewery 	struct fingerprint_list *fingerprints;
361*f12db248SBryan Drewery 
362*f12db248SBryan Drewery 	*count = 0;
363*f12db248SBryan Drewery 
364*f12db248SBryan Drewery 	fingerprints = calloc(1, sizeof(struct fingerprint_list));
365*f12db248SBryan Drewery 	if (fingerprints == NULL)
366*f12db248SBryan Drewery 		return (NULL);
367*f12db248SBryan Drewery 	STAILQ_INIT(fingerprints);
368*f12db248SBryan Drewery 
369*f12db248SBryan Drewery 	if ((d = opendir(path)) == NULL)
370*f12db248SBryan Drewery 		return (NULL);
371*f12db248SBryan Drewery 
372*f12db248SBryan Drewery 	while ((ent = readdir(d))) {
373*f12db248SBryan Drewery 		if (strcmp(ent->d_name, ".") == 0 ||
374*f12db248SBryan Drewery 		    strcmp(ent->d_name, "..") == 0)
375*f12db248SBryan Drewery 			continue;
376*f12db248SBryan Drewery 		finger = load_fingerprint(path, ent->d_name);
377*f12db248SBryan Drewery 		if (finger != NULL) {
378*f12db248SBryan Drewery 			STAILQ_INSERT_TAIL(fingerprints, finger, next);
379*f12db248SBryan Drewery 			++(*count);
380*f12db248SBryan Drewery 		}
381*f12db248SBryan Drewery 	}
382*f12db248SBryan Drewery 
383*f12db248SBryan Drewery 	closedir(d);
384*f12db248SBryan Drewery 
385*f12db248SBryan Drewery 	return (fingerprints);
386*f12db248SBryan Drewery }
387*f12db248SBryan Drewery 
388*f12db248SBryan Drewery static void
389*f12db248SBryan Drewery sha256_hash(unsigned char hash[SHA256_DIGEST_LENGTH],
390*f12db248SBryan Drewery     char out[SHA256_DIGEST_LENGTH * 2 + 1])
391*f12db248SBryan Drewery {
392*f12db248SBryan Drewery 	int i;
393*f12db248SBryan Drewery 
394*f12db248SBryan Drewery 	for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
395*f12db248SBryan Drewery 		sprintf(out + (i * 2), "%02x", hash[i]);
396*f12db248SBryan Drewery 
397*f12db248SBryan Drewery 	out[SHA256_DIGEST_LENGTH * 2] = '\0';
398*f12db248SBryan Drewery }
399*f12db248SBryan Drewery 
400*f12db248SBryan Drewery static void
401*f12db248SBryan Drewery sha256_buf(char *buf, size_t len, char out[SHA256_DIGEST_LENGTH * 2 + 1])
402*f12db248SBryan Drewery {
403*f12db248SBryan Drewery 	unsigned char hash[SHA256_DIGEST_LENGTH];
404*f12db248SBryan Drewery 	SHA256_CTX sha256;
405*f12db248SBryan Drewery 
406*f12db248SBryan Drewery 	out[0] = '\0';
407*f12db248SBryan Drewery 
408*f12db248SBryan Drewery 	SHA256_Init(&sha256);
409*f12db248SBryan Drewery 	SHA256_Update(&sha256, buf, len);
410*f12db248SBryan Drewery 	SHA256_Final(hash, &sha256);
411*f12db248SBryan Drewery 	sha256_hash(hash, out);
412*f12db248SBryan Drewery }
413*f12db248SBryan Drewery 
414*f12db248SBryan Drewery static int
415*f12db248SBryan Drewery sha256_fd(int fd, char out[SHA256_DIGEST_LENGTH * 2 + 1])
416*f12db248SBryan Drewery {
417*f12db248SBryan Drewery 	int my_fd;
418*f12db248SBryan Drewery 	FILE *fp;
419*f12db248SBryan Drewery 	char buffer[BUFSIZ];
420*f12db248SBryan Drewery 	unsigned char hash[SHA256_DIGEST_LENGTH];
421*f12db248SBryan Drewery 	size_t r;
422*f12db248SBryan Drewery 	int ret;
423*f12db248SBryan Drewery 	SHA256_CTX sha256;
424*f12db248SBryan Drewery 
425*f12db248SBryan Drewery 	my_fd = -1;
426*f12db248SBryan Drewery 	fp = NULL;
427*f12db248SBryan Drewery 	r = 0;
428*f12db248SBryan Drewery 	ret = 1;
429*f12db248SBryan Drewery 
430*f12db248SBryan Drewery 	out[0] = '\0';
431*f12db248SBryan Drewery 
432*f12db248SBryan Drewery 	/* Duplicate the fd so that fclose(3) does not close it. */
433*f12db248SBryan Drewery 	if ((my_fd = dup(fd)) == -1) {
434*f12db248SBryan Drewery 		warnx("dup");
435*f12db248SBryan Drewery 		goto cleanup;
436*f12db248SBryan Drewery 	}
437*f12db248SBryan Drewery 
438*f12db248SBryan Drewery 	if ((fp = fdopen(my_fd, "rb")) == NULL) {
439*f12db248SBryan Drewery 		warnx("fdopen");
440*f12db248SBryan Drewery 		goto cleanup;
441*f12db248SBryan Drewery 	}
442*f12db248SBryan Drewery 
443*f12db248SBryan Drewery 	SHA256_Init(&sha256);
444*f12db248SBryan Drewery 
445*f12db248SBryan Drewery 	while ((r = fread(buffer, 1, BUFSIZ, fp)) > 0)
446*f12db248SBryan Drewery 		SHA256_Update(&sha256, buffer, r);
447*f12db248SBryan Drewery 
448*f12db248SBryan Drewery 	if (ferror(fp) != 0) {
449*f12db248SBryan Drewery 		warnx("fread");
450*f12db248SBryan Drewery 		goto cleanup;
451*f12db248SBryan Drewery 	}
452*f12db248SBryan Drewery 
453*f12db248SBryan Drewery 	SHA256_Final(hash, &sha256);
454*f12db248SBryan Drewery 	sha256_hash(hash, out);
455*f12db248SBryan Drewery 	ret = 0;
456*f12db248SBryan Drewery 
457*f12db248SBryan Drewery cleanup:
458*f12db248SBryan Drewery 	if (fp != NULL)
459*f12db248SBryan Drewery 		fclose(fp);
460*f12db248SBryan Drewery 	else if (my_fd != -1)
461*f12db248SBryan Drewery 		close(my_fd);
462*f12db248SBryan Drewery 	(void)lseek(fd, 0, SEEK_SET);
463*f12db248SBryan Drewery 
464*f12db248SBryan Drewery 	return (ret);
465*f12db248SBryan Drewery }
466*f12db248SBryan Drewery 
467*f12db248SBryan Drewery static EVP_PKEY *
468*f12db248SBryan Drewery load_public_key_buf(const unsigned char *cert, int certlen)
469*f12db248SBryan Drewery {
470*f12db248SBryan Drewery 	EVP_PKEY *pkey;
471*f12db248SBryan Drewery 	BIO *bp;
472*f12db248SBryan Drewery 	char errbuf[1024];
473*f12db248SBryan Drewery 
474*f12db248SBryan Drewery 	bp = BIO_new_mem_buf((void *)cert, certlen);
475*f12db248SBryan Drewery 
476*f12db248SBryan Drewery 	if ((pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL)) == NULL)
477*f12db248SBryan Drewery 		warnx("%s", ERR_error_string(ERR_get_error(), errbuf));
478*f12db248SBryan Drewery 
479*f12db248SBryan Drewery 	BIO_free(bp);
480*f12db248SBryan Drewery 
481*f12db248SBryan Drewery 	return (pkey);
482*f12db248SBryan Drewery }
483*f12db248SBryan Drewery 
484*f12db248SBryan Drewery static bool
485*f12db248SBryan Drewery rsa_verify_cert(int fd, const unsigned char *key, int keylen,
486*f12db248SBryan Drewery     unsigned char *sig, int siglen)
487*f12db248SBryan Drewery {
488*f12db248SBryan Drewery 	EVP_MD_CTX *mdctx;
489*f12db248SBryan Drewery 	EVP_PKEY *pkey;
490*f12db248SBryan Drewery 	char sha256[(SHA256_DIGEST_LENGTH * 2) + 2];
491*f12db248SBryan Drewery 	char errbuf[1024];
492*f12db248SBryan Drewery 	bool ret;
493*f12db248SBryan Drewery 
494*f12db248SBryan Drewery 	pkey = NULL;
495*f12db248SBryan Drewery 	mdctx = NULL;
496*f12db248SBryan Drewery 	ret = false;
497*f12db248SBryan Drewery 
498*f12db248SBryan Drewery 	/* Compute SHA256 of the package. */
499*f12db248SBryan Drewery 	if (lseek(fd, 0, 0) == -1) {
500*f12db248SBryan Drewery 		warn("lseek");
501*f12db248SBryan Drewery 		goto cleanup;
502*f12db248SBryan Drewery 	}
503*f12db248SBryan Drewery 	if ((sha256_fd(fd, sha256)) == -1) {
504*f12db248SBryan Drewery 		warnx("Error creating SHA256 hash for package");
505*f12db248SBryan Drewery 		goto cleanup;
506*f12db248SBryan Drewery 	}
507*f12db248SBryan Drewery 
508*f12db248SBryan Drewery 	if ((pkey = load_public_key_buf(key, keylen)) == NULL) {
509*f12db248SBryan Drewery 		warnx("Error reading public key");
510*f12db248SBryan Drewery 		goto cleanup;
511*f12db248SBryan Drewery 	}
512*f12db248SBryan Drewery 
513*f12db248SBryan Drewery 	/* Verify signature of the SHA256(pkg) is valid. */
514*f12db248SBryan Drewery 	printf("Verifying signature... ");
515*f12db248SBryan Drewery 	if ((mdctx = EVP_MD_CTX_create()) == NULL) {
516*f12db248SBryan Drewery 		warnx("%s", ERR_error_string(ERR_get_error(), errbuf));
517*f12db248SBryan Drewery 		goto error;
518*f12db248SBryan Drewery 	}
519*f12db248SBryan Drewery 
520*f12db248SBryan Drewery 	if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pkey) != 1) {
521*f12db248SBryan Drewery 		warnx("%s", ERR_error_string(ERR_get_error(), errbuf));
522*f12db248SBryan Drewery 		goto error;
523*f12db248SBryan Drewery 	}
524*f12db248SBryan Drewery 	if (EVP_DigestVerifyUpdate(mdctx, sha256, strlen(sha256)) != 1) {
525*f12db248SBryan Drewery 		warnx("%s", ERR_error_string(ERR_get_error(), errbuf));
526*f12db248SBryan Drewery 		goto error;
527*f12db248SBryan Drewery 	}
528*f12db248SBryan Drewery 
529*f12db248SBryan Drewery 	if (EVP_DigestVerifyFinal(mdctx, sig, siglen) != 1) {
530*f12db248SBryan Drewery 		warnx("%s", ERR_error_string(ERR_get_error(), errbuf));
531*f12db248SBryan Drewery 		goto error;
532*f12db248SBryan Drewery 	}
533*f12db248SBryan Drewery 
534*f12db248SBryan Drewery 	ret = true;
535*f12db248SBryan Drewery 	printf("done\n");
536*f12db248SBryan Drewery 	goto cleanup;
537*f12db248SBryan Drewery 
538*f12db248SBryan Drewery error:
539*f12db248SBryan Drewery 	printf("failed\n");
540*f12db248SBryan Drewery 
541*f12db248SBryan Drewery cleanup:
542*f12db248SBryan Drewery 	if (pkey)
543*f12db248SBryan Drewery 		EVP_PKEY_free(pkey);
544*f12db248SBryan Drewery 	if (mdctx)
545*f12db248SBryan Drewery 		EVP_MD_CTX_destroy(mdctx);
546*f12db248SBryan Drewery 	ERR_free_strings();
547*f12db248SBryan Drewery 
548*f12db248SBryan Drewery 	return (ret);
549*f12db248SBryan Drewery }
550*f12db248SBryan Drewery 
551*f12db248SBryan Drewery static struct sig_cert *
552*f12db248SBryan Drewery parse_cert(int fd) {
553*f12db248SBryan Drewery 	int my_fd;
554*f12db248SBryan Drewery 	struct sig_cert *sc;
555*f12db248SBryan Drewery 	FILE *fp;
556*f12db248SBryan Drewery 	struct sbuf *buf, *sig, *cert;
557*f12db248SBryan Drewery 	char *line;
558*f12db248SBryan Drewery 	size_t linecap;
559*f12db248SBryan Drewery 	ssize_t linelen;
560*f12db248SBryan Drewery 
561*f12db248SBryan Drewery 	my_fd = -1;
562*f12db248SBryan Drewery 	sc = NULL;
563*f12db248SBryan Drewery 	line = NULL;
564*f12db248SBryan Drewery 	linecap = 0;
565*f12db248SBryan Drewery 
566*f12db248SBryan Drewery 	if (lseek(fd, 0, 0) == -1) {
567*f12db248SBryan Drewery 		warn("lseek");
568*f12db248SBryan Drewery 		return (NULL);
569*f12db248SBryan Drewery 	}
570*f12db248SBryan Drewery 
571*f12db248SBryan Drewery 	/* Duplicate the fd so that fclose(3) does not close it. */
572*f12db248SBryan Drewery 	if ((my_fd = dup(fd)) == -1) {
573*f12db248SBryan Drewery 		warnx("dup");
574*f12db248SBryan Drewery 		return (NULL);
575*f12db248SBryan Drewery 	}
576*f12db248SBryan Drewery 
577*f12db248SBryan Drewery 	if ((fp = fdopen(my_fd, "rb")) == NULL) {
578*f12db248SBryan Drewery 		warn("fdopen");
579*f12db248SBryan Drewery 		close(my_fd);
580*f12db248SBryan Drewery 		return (NULL);
581*f12db248SBryan Drewery 	}
582*f12db248SBryan Drewery 
583*f12db248SBryan Drewery 	sig = sbuf_new_auto();
584*f12db248SBryan Drewery 	cert = sbuf_new_auto();
585*f12db248SBryan Drewery 
586*f12db248SBryan Drewery 	while ((linelen = getline(&line, &linecap, fp)) > 0) {
587*f12db248SBryan Drewery 		if (strcmp(line, "SIGNATURE\n") == 0) {
588*f12db248SBryan Drewery 			buf = sig;
589*f12db248SBryan Drewery 			continue;
590*f12db248SBryan Drewery 		} else if (strcmp(line, "CERT\n") == 0) {
591*f12db248SBryan Drewery 			buf = cert;
592*f12db248SBryan Drewery 			continue;
593*f12db248SBryan Drewery 		} else if (strcmp(line, "END\n") == 0) {
594*f12db248SBryan Drewery 			break;
595*f12db248SBryan Drewery 		}
596*f12db248SBryan Drewery 		if (buf != NULL)
597*f12db248SBryan Drewery 			sbuf_bcat(buf, line, linelen);
598*f12db248SBryan Drewery 	}
599*f12db248SBryan Drewery 
600*f12db248SBryan Drewery 	fclose(fp);
601*f12db248SBryan Drewery 
602*f12db248SBryan Drewery 	/* Trim out unrelated trailing newline */
603*f12db248SBryan Drewery 	sbuf_setpos(sig, sbuf_len(sig) - 1);
604*f12db248SBryan Drewery 
605*f12db248SBryan Drewery 	sbuf_finish(sig);
606*f12db248SBryan Drewery 	sbuf_finish(cert);
607*f12db248SBryan Drewery 
608*f12db248SBryan Drewery 	sc = calloc(1, sizeof(struct sig_cert));
609*f12db248SBryan Drewery 	sc->siglen = sbuf_len(sig);
610*f12db248SBryan Drewery 	sc->sig = calloc(1, sc->siglen);
611*f12db248SBryan Drewery 	memcpy(sc->sig, sbuf_data(sig), sc->siglen);
612*f12db248SBryan Drewery 
613*f12db248SBryan Drewery 	sc->certlen = sbuf_len(cert);
614*f12db248SBryan Drewery 	sc->cert = strdup(sbuf_data(cert));
615*f12db248SBryan Drewery 
616*f12db248SBryan Drewery 	sbuf_delete(sig);
617*f12db248SBryan Drewery 	sbuf_delete(cert);
618*f12db248SBryan Drewery 
619*f12db248SBryan Drewery 	return (sc);
620*f12db248SBryan Drewery }
621*f12db248SBryan Drewery 
622*f12db248SBryan Drewery static bool
623*f12db248SBryan Drewery verify_signature(int fd_pkg, int fd_sig)
624*f12db248SBryan Drewery {
625*f12db248SBryan Drewery 	struct fingerprint_list *trusted, *revoked;
626*f12db248SBryan Drewery 	struct fingerprint *fingerprint;
627*f12db248SBryan Drewery 	struct sig_cert *sc;
628*f12db248SBryan Drewery 	bool ret;
629*f12db248SBryan Drewery 	int trusted_count, revoked_count;
630*f12db248SBryan Drewery 	const char *fingerprints;
631*f12db248SBryan Drewery 	char path[MAXPATHLEN];
632*f12db248SBryan Drewery 	char hash[SHA256_DIGEST_LENGTH * 2 + 1];
633*f12db248SBryan Drewery 
634*f12db248SBryan Drewery 	trusted = revoked = NULL;
635*f12db248SBryan Drewery 	ret = false;
636*f12db248SBryan Drewery 
637*f12db248SBryan Drewery 	/* Read and parse fingerprints. */
638*f12db248SBryan Drewery 	if (config_string(FINGERPRINTS, &fingerprints) != 0) {
639*f12db248SBryan Drewery 		warnx("No CONFIG_FINGERPRINTS defined");
640*f12db248SBryan Drewery 		goto cleanup;
641*f12db248SBryan Drewery 	}
642*f12db248SBryan Drewery 
643*f12db248SBryan Drewery 	snprintf(path, MAXPATHLEN, "%s/trusted", fingerprints);
644*f12db248SBryan Drewery 	if ((trusted = load_fingerprints(path, &trusted_count)) == NULL) {
645*f12db248SBryan Drewery 		warnx("Error loading trusted certificates");
646*f12db248SBryan Drewery 		goto cleanup;
647*f12db248SBryan Drewery 	}
648*f12db248SBryan Drewery 
649*f12db248SBryan Drewery 	if (trusted_count == 0 || trusted == NULL) {
650*f12db248SBryan Drewery 		fprintf(stderr, "No trusted certificates found.\n");
651*f12db248SBryan Drewery 		goto cleanup;
652*f12db248SBryan Drewery 	}
653*f12db248SBryan Drewery 
654*f12db248SBryan Drewery 	snprintf(path, MAXPATHLEN, "%s/revoked", fingerprints);
655*f12db248SBryan Drewery 	if ((revoked = load_fingerprints(path, &revoked_count)) == NULL) {
656*f12db248SBryan Drewery 		warnx("Error loading revoked certificates");
657*f12db248SBryan Drewery 		goto cleanup;
658*f12db248SBryan Drewery 	}
659*f12db248SBryan Drewery 
660*f12db248SBryan Drewery 	/* Read certificate and signature in. */
661*f12db248SBryan Drewery 	if ((sc = parse_cert(fd_sig)) == NULL) {
662*f12db248SBryan Drewery 		warnx("Error parsing certificate");
663*f12db248SBryan Drewery 		goto cleanup;
664*f12db248SBryan Drewery 	}
665*f12db248SBryan Drewery 	/* Explicitly mark as non-trusted until proven otherwise. */
666*f12db248SBryan Drewery 	sc->trusted = false;
667*f12db248SBryan Drewery 
668*f12db248SBryan Drewery 	/* Parse signature and pubkey out of the certificate */
669*f12db248SBryan Drewery 	sha256_buf(sc->cert, sc->certlen, hash);
670*f12db248SBryan Drewery 
671*f12db248SBryan Drewery 	/* Check if this hash is revoked */
672*f12db248SBryan Drewery 	if (revoked != NULL) {
673*f12db248SBryan Drewery 		STAILQ_FOREACH(fingerprint, revoked, next) {
674*f12db248SBryan Drewery 			if (strcasecmp(fingerprint->hash, hash) == 0) {
675*f12db248SBryan Drewery 				fprintf(stderr, "The certificate has been "
676*f12db248SBryan Drewery 				    "revoked\n");
677*f12db248SBryan Drewery 				goto cleanup;
678*f12db248SBryan Drewery 			}
679*f12db248SBryan Drewery 		}
680*f12db248SBryan Drewery 	}
681*f12db248SBryan Drewery 
682*f12db248SBryan Drewery 	STAILQ_FOREACH(fingerprint, trusted, next) {
683*f12db248SBryan Drewery 		if (strcasecmp(fingerprint->hash, hash) == 0) {
684*f12db248SBryan Drewery 			sc->trusted = true;
685*f12db248SBryan Drewery 			break;
686*f12db248SBryan Drewery 		}
687*f12db248SBryan Drewery 	}
688*f12db248SBryan Drewery 
689*f12db248SBryan Drewery 	if (sc->trusted == false) {
690*f12db248SBryan Drewery 		fprintf(stderr, "No trusted certificate found matching "
691*f12db248SBryan Drewery 		    "package's certificate\n");
692*f12db248SBryan Drewery 		goto cleanup;
693*f12db248SBryan Drewery 	}
694*f12db248SBryan Drewery 
695*f12db248SBryan Drewery 	/* Verify the signature. */
696*f12db248SBryan Drewery 	if (rsa_verify_cert(fd_pkg, sc->cert, sc->certlen, sc->sig,
697*f12db248SBryan Drewery 	    sc->siglen) == false) {
698*f12db248SBryan Drewery 		fprintf(stderr, "Signature is not valid\n");
699*f12db248SBryan Drewery 		goto cleanup;
700*f12db248SBryan Drewery 	}
701*f12db248SBryan Drewery 
702*f12db248SBryan Drewery 	ret = true;
703*f12db248SBryan Drewery 
704*f12db248SBryan Drewery cleanup:
705*f12db248SBryan Drewery 	if (trusted) {
706*f12db248SBryan Drewery 		STAILQ_FOREACH(fingerprint, trusted, next)
707*f12db248SBryan Drewery 		    free(fingerprint);
708*f12db248SBryan Drewery 		free(trusted);
709*f12db248SBryan Drewery 	}
710*f12db248SBryan Drewery 	if (revoked) {
711*f12db248SBryan Drewery 		STAILQ_FOREACH(fingerprint, revoked, next)
712*f12db248SBryan Drewery 		    free(fingerprint);
713*f12db248SBryan Drewery 		free(revoked);
714*f12db248SBryan Drewery 	}
715*f12db248SBryan Drewery 	if (sc) {
716*f12db248SBryan Drewery 		if (sc->cert)
717*f12db248SBryan Drewery 			free(sc->cert);
718*f12db248SBryan Drewery 		if (sc->sig)
719*f12db248SBryan Drewery 			free(sc->sig);
720*f12db248SBryan Drewery 		free(sc);
721*f12db248SBryan Drewery 	}
722*f12db248SBryan Drewery 
723*f12db248SBryan Drewery 	return (ret);
724*f12db248SBryan Drewery }
725*f12db248SBryan Drewery 
726*f12db248SBryan Drewery static int
727*f12db248SBryan Drewery bootstrap_pkg(void)
728*f12db248SBryan Drewery {
729*f12db248SBryan Drewery 	FILE *config;
730*f12db248SBryan Drewery 	int fd_pkg, fd_sig;
731*f12db248SBryan Drewery 	int ret;
732*f12db248SBryan Drewery 	char *site;
733*f12db248SBryan Drewery 	char url[MAXPATHLEN];
734*f12db248SBryan Drewery 	char conf[MAXPATHLEN];
735*f12db248SBryan Drewery 	char tmppkg[MAXPATHLEN];
736*f12db248SBryan Drewery 	char tmpsig[MAXPATHLEN];
737*f12db248SBryan Drewery 	const char *packagesite;
738*f12db248SBryan Drewery 	const char *signature_type;
739*f12db248SBryan Drewery 	char pkgstatic[MAXPATHLEN];
740*f12db248SBryan Drewery 
741*f12db248SBryan Drewery 	fd_sig = -1;
742*f12db248SBryan Drewery 	ret = -1;
743*f12db248SBryan Drewery 	config = NULL;
744*f12db248SBryan Drewery 
745*f12db248SBryan Drewery 	if (config_string(PACKAGESITE, &packagesite) != 0) {
746*f12db248SBryan Drewery 		warnx("No PACKAGESITE defined");
747*f12db248SBryan Drewery 		return (-1);
748*f12db248SBryan Drewery 	}
749*f12db248SBryan Drewery 
750*f12db248SBryan Drewery 	if (config_string(SIGNATURE_TYPE, &signature_type) != 0) {
751*f12db248SBryan Drewery 		warnx("Error looking up SIGNATURE_TYPE");
752*f12db248SBryan Drewery 		return (-1);
753*f12db248SBryan Drewery 	}
754*f12db248SBryan Drewery 
755*f12db248SBryan Drewery 	printf("Bootstrapping pkg from %s, please wait...\n", packagesite);
756*f12db248SBryan Drewery 
757*f12db248SBryan Drewery 	/* Support pkg+http:// for PACKAGESITE which is the new format
758*f12db248SBryan Drewery 	   in 1.2 to avoid confusion on why http://pkg.FreeBSD.org has
759*f12db248SBryan Drewery 	   no A record. */
760*f12db248SBryan Drewery 	if (strncmp(URL_SCHEME_PREFIX, packagesite,
761*f12db248SBryan Drewery 	    strlen(URL_SCHEME_PREFIX)) == 0)
762*f12db248SBryan Drewery 		packagesite += strlen(URL_SCHEME_PREFIX);
763*f12db248SBryan Drewery 	snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", packagesite);
764*f12db248SBryan Drewery 
765*f12db248SBryan Drewery 	snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX",
766*f12db248SBryan Drewery 	    getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP);
767*f12db248SBryan Drewery 
768*f12db248SBryan Drewery 	if ((fd_pkg = fetch_to_fd(url, tmppkg)) == -1)
769*f12db248SBryan Drewery 		goto fetchfail;
770*f12db248SBryan Drewery 
771*f12db248SBryan Drewery 	if (signature_type != NULL &&
772*f12db248SBryan Drewery 	    strcasecmp(signature_type, "FINGERPRINTS") == 0) {
773*f12db248SBryan Drewery 		snprintf(tmpsig, MAXPATHLEN, "%s/pkg.txz.sig.XXXXXX",
774*f12db248SBryan Drewery 		    getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP);
775*f12db248SBryan Drewery 		snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz.sig",
776*f12db248SBryan Drewery 		    packagesite);
777*f12db248SBryan Drewery 
778*f12db248SBryan Drewery 		if ((fd_sig = fetch_to_fd(url, tmpsig)) == -1) {
779*f12db248SBryan Drewery 			fprintf(stderr, "Signature for pkg not available.\n");
780*f12db248SBryan Drewery 			goto fetchfail;
781*f12db248SBryan Drewery 		}
782*f12db248SBryan Drewery 
783*f12db248SBryan Drewery 		if (verify_signature(fd_pkg, fd_sig) == false)
784*f12db248SBryan Drewery 			goto cleanup;
785*f12db248SBryan Drewery 	}
786*f12db248SBryan Drewery 
787*f12db248SBryan Drewery 	if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0)
7883aa4b42aSBaptiste Daroussin 		ret = install_pkg_static(pkgstatic, tmppkg);
7893aa4b42aSBaptiste Daroussin 
7902fe3761eSBaptiste Daroussin 	snprintf(conf, MAXPATHLEN, "%s/etc/pkg.conf",
7912fe3761eSBaptiste Daroussin 	    getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
7922fe3761eSBaptiste Daroussin 
7932fe3761eSBaptiste Daroussin 	if (access(conf, R_OK) == -1) {
7942fe3761eSBaptiste Daroussin 		site = strrchr(url, '/');
7952fe3761eSBaptiste Daroussin 		if (site == NULL)
7962fe3761eSBaptiste Daroussin 			goto cleanup;
7972fe3761eSBaptiste Daroussin 		site[0] = '\0';
7982fe3761eSBaptiste Daroussin 		site = strrchr(url, '/');
7992fe3761eSBaptiste Daroussin 		if (site == NULL)
8002fe3761eSBaptiste Daroussin 			goto cleanup;
8012fe3761eSBaptiste Daroussin 		site[0] = '\0';
8022fe3761eSBaptiste Daroussin 
8032fe3761eSBaptiste Daroussin 		config = fopen(conf, "w+");
8042fe3761eSBaptiste Daroussin 		if (config == NULL)
8052fe3761eSBaptiste Daroussin 			goto cleanup;
806514ead92SBaptiste Daroussin 		fprintf(config, "packagesite: %s\n", url);
8072fe3761eSBaptiste Daroussin 		fclose(config);
8082fe3761eSBaptiste Daroussin 	}
8092fe3761eSBaptiste Daroussin 
810a6454741SBaptiste Daroussin 	goto cleanup;
811a6454741SBaptiste Daroussin 
812a6454741SBaptiste Daroussin fetchfail:
813a6454741SBaptiste Daroussin 	warnx("Error fetching %s: %s", url, fetchLastErrString);
8144ff9a7efSBryan Drewery 	fprintf(stderr, "A pre-built version of pkg could not be found for "
8154ff9a7efSBryan Drewery 	    "your system.\n");
8164ff9a7efSBryan Drewery 	fprintf(stderr, "Consider changing PACKAGESITE or installing it from "
8174ff9a7efSBryan Drewery 	    "ports: 'ports-mgmt/pkg'.\n");
818a6454741SBaptiste Daroussin 
8193aa4b42aSBaptiste Daroussin cleanup:
820*f12db248SBryan Drewery 	if (fd_sig != -1) {
821*f12db248SBryan Drewery 		close(fd_sig);
822*f12db248SBryan Drewery 		unlink(tmpsig);
823*f12db248SBryan Drewery 	}
824*f12db248SBryan Drewery 	close(fd_pkg);
8253aa4b42aSBaptiste Daroussin 	unlink(tmppkg);
8263aa4b42aSBaptiste Daroussin 
827a6454741SBaptiste Daroussin 	return (ret);
8283aa4b42aSBaptiste Daroussin }
8293aa4b42aSBaptiste Daroussin 
830e18ad51cSAlexander Kabaev static const char confirmation_message[] =
831e18ad51cSAlexander Kabaev "The package management tool is not yet installed on your system.\n"
832e18ad51cSAlexander Kabaev "Do you want to fetch and install it now? [y/N]: ";
833e18ad51cSAlexander Kabaev 
834e18ad51cSAlexander Kabaev static int
835e18ad51cSAlexander Kabaev pkg_query_yes_no(void)
836e18ad51cSAlexander Kabaev {
837e18ad51cSAlexander Kabaev 	int ret, c;
838e18ad51cSAlexander Kabaev 
839e18ad51cSAlexander Kabaev 	c = getchar();
840e18ad51cSAlexander Kabaev 
841e18ad51cSAlexander Kabaev 	if (c == 'y' || c == 'Y')
842e18ad51cSAlexander Kabaev 		ret = 1;
843e18ad51cSAlexander Kabaev 	else
844e18ad51cSAlexander Kabaev 		ret = 0;
845e18ad51cSAlexander Kabaev 
846e18ad51cSAlexander Kabaev 	while (c != '\n' && c != EOF)
847e18ad51cSAlexander Kabaev 		c = getchar();
848e18ad51cSAlexander Kabaev 
849e18ad51cSAlexander Kabaev 	return (ret);
850e18ad51cSAlexander Kabaev }
851e18ad51cSAlexander Kabaev 
8523aa4b42aSBaptiste Daroussin int
8533aa4b42aSBaptiste Daroussin main(__unused int argc, char *argv[])
8543aa4b42aSBaptiste Daroussin {
8553aa4b42aSBaptiste Daroussin 	char pkgpath[MAXPATHLEN];
856b70213b5SBaptiste Daroussin 	char pkgstatic[MAXPATHLEN];
8579950eceeSBaptiste Daroussin 	bool yes = false;
858b70213b5SBaptiste Daroussin 	int fd, ret;
8593aa4b42aSBaptiste Daroussin 
8603aa4b42aSBaptiste Daroussin 	snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg",
8613aa4b42aSBaptiste Daroussin 	    getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
8623aa4b42aSBaptiste Daroussin 
863e18ad51cSAlexander Kabaev 	if (access(pkgpath, X_OK) == -1) {
864e18ad51cSAlexander Kabaev 		/*
865d482e1f4SMatthew Seaman 		 * To allow 'pkg -N' to be used as a reliable test for whether
866ecfed9f2SMatthew Seaman 		 * a system is configured to use pkg, don't bootstrap pkg
867ecfed9f2SMatthew Seaman 		 * when that argument is given as argv[1].
868ecfed9f2SMatthew Seaman 		 */
869d8f9490cSMatthew Seaman 		if (argv[1] != NULL && strcmp(argv[1], "-N") == 0)
870e41b8acbSMatthew Seaman 			errx(EXIT_FAILURE, "pkg is not installed");
871ecfed9f2SMatthew Seaman 
872b70213b5SBaptiste Daroussin 		if (argc > 2 && strcmp(argv[1], "add") == 0 &&
873b70213b5SBaptiste Daroussin 		    access(argv[2], R_OK) == 0) {
874b70213b5SBaptiste Daroussin 			fd = open(argv[2], O_RDONLY);
875b70213b5SBaptiste Daroussin 			if (fd == -1)
876b70213b5SBaptiste Daroussin 				err(EXIT_FAILURE, "Unable to open %s", argv[2]);
877b70213b5SBaptiste Daroussin 
878b70213b5SBaptiste Daroussin 			if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0)
879b70213b5SBaptiste Daroussin 				ret = install_pkg_static(pkgstatic, argv[2]);
880b70213b5SBaptiste Daroussin 			close(fd);
881b70213b5SBaptiste Daroussin 			if (ret != 0)
882b70213b5SBaptiste Daroussin 				exit(EXIT_FAILURE);
883b70213b5SBaptiste Daroussin 			exit(EXIT_SUCCESS);
884b70213b5SBaptiste Daroussin 		}
885ecfed9f2SMatthew Seaman 		/*
886e18ad51cSAlexander Kabaev 		 * Do not ask for confirmation if either of stdin or stdout is
887e18ad51cSAlexander Kabaev 		 * not tty. Check the environment to see if user has answer
888e18ad51cSAlexander Kabaev 		 * tucked in there already.
889e18ad51cSAlexander Kabaev 		 */
8909950eceeSBaptiste Daroussin 		config_init();
8919950eceeSBaptiste Daroussin 		config_bool(ASSUME_ALWAYS_YES, &yes);
8929950eceeSBaptiste Daroussin 		if (!yes) {
893e18ad51cSAlexander Kabaev 			printf("%s", confirmation_message);
894204ea792SBaptiste Daroussin 			if (!isatty(fileno(stdin)))
8953a480126SBaptiste Daroussin 				exit(EXIT_FAILURE);
896204ea792SBaptiste Daroussin 
897204ea792SBaptiste Daroussin 			if (pkg_query_yes_no() == 0)
898e18ad51cSAlexander Kabaev 				exit(EXIT_FAILURE);
899e18ad51cSAlexander Kabaev 		}
900a6454741SBaptiste Daroussin 		if (bootstrap_pkg() != 0)
901a6454741SBaptiste Daroussin 			exit(EXIT_FAILURE);
9029950eceeSBaptiste Daroussin 		config_finish();
903e18ad51cSAlexander Kabaev 	}
9043aa4b42aSBaptiste Daroussin 
9053aa4b42aSBaptiste Daroussin 	execv(pkgpath, argv);
9063aa4b42aSBaptiste Daroussin 
907a6454741SBaptiste Daroussin 	/* NOT REACHED */
9083b05c2a8SBaptiste Daroussin 	return (EXIT_FAILURE);
9093aa4b42aSBaptiste Daroussin }
910