package.c 25.5 KB
Newer Older
1 2 3
/* package.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
4
 * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
5 6
 * All rights reserved.
 *
7
 * This program is free software; you can redistribute it and/or modify it
8 9 10 11
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation. See http://www.gnu.org/ for details.
 */

12
#include <errno.h>
13 14 15
#include <fcntl.h>
#include <ctype.h>
#include <stdio.h>
16
#include <limits.h>
17 18 19 20 21
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
Natanael Copa's avatar
Natanael Copa committed
22
#include <sys/stat.h>
23

Timo Teräs's avatar
Timo Teräs committed
24 25
#include <openssl/pem.h>

26 27 28 29
#include "apk_defines.h"
#include "apk_archive.h"
#include "apk_package.h"
#include "apk_database.h"
Natanael Copa's avatar
Natanael Copa committed
30
#include "apk_print.h"
31

32 33 34 35 36
void apk_pkg_format_plain(struct apk_package *pkg, apk_blob_t to)
{
	/* pkgname-1.0.apk */
	apk_blob_push_blob(&to, APK_BLOB_STR(pkg->name->name));
	apk_blob_push_blob(&to, APK_BLOB_STR("-"));
37
	apk_blob_push_blob(&to, *pkg->version);
38 39 40 41 42 43 44 45 46
	apk_blob_push_blob(&to, APK_BLOB_STR(".apk"));
	apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1));
}

void apk_pkg_format_cache(struct apk_package *pkg, apk_blob_t to)
{
	/* pkgname-1.0_alpha1.12345678.apk */
	apk_blob_push_blob(&to, APK_BLOB_STR(pkg->name->name));
	apk_blob_push_blob(&to, APK_BLOB_STR("-"));
47
	apk_blob_push_blob(&to, *pkg->version);
48 49 50 51 52 53 54
	apk_blob_push_blob(&to, APK_BLOB_STR("."));
	apk_blob_push_hexdump(&to, APK_BLOB_PTR_LEN((char *) pkg->csum.data,
						    APK_CACHE_CSUM_BYTES));
	apk_blob_push_blob(&to, APK_BLOB_STR(".apk"));
	apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1));
}

55
struct apk_package *apk_pkg_new(void)
56
{
Timo Teräs's avatar
Timo Teräs committed
57 58 59
	struct apk_package *pkg;

	pkg = calloc(1, sizeof(struct apk_package));
Timo Teräs's avatar
Timo Teräs committed
60
	if (pkg != NULL) {
Timo Teräs's avatar
Timo Teräs committed
61
		apk_dependency_array_init(&pkg->depends);
Timo Teräs's avatar
Timo Teräs committed
62 63
		apk_dependency_array_init(&pkg->install_if);
	}
Timo Teräs's avatar
Timo Teräs committed
64 65

	return pkg;
66
}
67

68 69 70 71 72 73 74 75 76 77
struct apk_installed_package *apk_pkg_install(struct apk_database *db,
					      struct apk_package *pkg)
{
	struct apk_installed_package *ipkg;

	if (pkg->ipkg != NULL)
		return pkg->ipkg;

	pkg->ipkg = ipkg = calloc(1, sizeof(struct apk_installed_package));
	ipkg->pkg = pkg;
Timo Teräs's avatar
Timo Teräs committed
78 79
	apk_string_array_init(&ipkg->triggers);
	apk_string_array_init(&ipkg->pending_triggers);
80
	apk_dependency_array_init(&ipkg->replaces);
Timo Teräs's avatar
Timo Teräs committed
81 82 83 84

	/* Overlay override information resides in a nameless package */
	if (pkg->name != NULL) {
		db->installed.stats.packages++;
85
		db->installed.stats.bytes += pkg->installed_size;
Timo Teräs's avatar
Timo Teräs committed
86 87 88
		list_add_tail(&ipkg->installed_pkgs_list,
			      &db->installed.packages);
	}
89 90 91 92 93 94 95 96 97 98 99 100

	return ipkg;
}

void apk_pkg_uninstall(struct apk_database *db, struct apk_package *pkg)
{
	struct apk_installed_package *ipkg = pkg->ipkg;
	int i;

	if (ipkg == NULL)
		return;

101
	if (db != NULL) {
102
		db->installed.stats.packages--;
103 104
		db->installed.stats.bytes -= pkg->installed_size;
	}
105

106 107
	list_del(&ipkg->installed_pkgs_list);

Timo Teräs's avatar
Timo Teräs committed
108
	if (ipkg->triggers->num) {
109
		list_del(&ipkg->trigger_pkgs_list);
110
		list_init(&ipkg->trigger_pkgs_list);
111 112 113
		for (i = 0; i < ipkg->triggers->num; i++)
			free(ipkg->triggers->item[i]);
	}
Timo Teräs's avatar
Timo Teräs committed
114 115
	apk_string_array_free(&ipkg->triggers);
	apk_string_array_free(&ipkg->pending_triggers);
116
	apk_dependency_array_free(&ipkg->replaces);
117 118 119 120 121 122

	for (i = 0; i < APK_SCRIPT_MAX; i++)
		if (ipkg->script[i].ptr != NULL)
			free(ipkg->script[i].ptr);
	free(ipkg);
	pkg->ipkg = NULL;
123 124
}

125 126 127 128 129 130
int apk_pkg_parse_name(apk_blob_t apkname,
		       apk_blob_t *name,
		       apk_blob_t *version)
{
	int i, dash = 0;

131 132 133
	if (APK_BLOB_IS_NULL(apkname))
		return -1;

134 135 136 137 138 139 140 141
	for (i = apkname.len - 2; i >= 0; i--) {
		if (apkname.ptr[i] != '-')
			continue;
		if (isdigit(apkname.ptr[i+1]))
			break;
		if (++dash >= 2)
			return -1;
	}
142 143 144
	if (i < 0)
		return -1;

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
	if (name != NULL)
		*name = APK_BLOB_PTR_LEN(apkname.ptr, i);
	if (version != NULL)
		*version = APK_BLOB_PTR_PTR(&apkname.ptr[i+1],
					    &apkname.ptr[apkname.len-1]);

	return 0;
}

int apk_deps_add(struct apk_dependency_array **depends,
		 struct apk_dependency *dep)
{
	struct apk_dependency_array *deps = *depends;
	int i;

	if (deps != NULL) {
		for (i = 0; i < deps->num; i++) {
Timo Teräs's avatar
Timo Teräs committed
162 163
			if (deps->item[i].name == dep->name) {
				deps->item[i] = *dep;
164
				return 0;
Timo Teräs's avatar
Timo Teräs committed
165
			}
166 167 168 169 170 171
		}
	}

	*apk_dependency_array_add(depends) = *dep;
	return 0;
}
Timo Teräs's avatar
Timo Teräs committed
172

173 174 175 176 177 178 179 180 181 182 183 184 185 186
void apk_deps_del(struct apk_dependency_array **pdeps,
		  struct apk_name *name)
{
	struct apk_dependency_array *deps = *pdeps;
	int i;

	if (deps == NULL)
		return;

	for (i = 0; i < deps->num; i++) {
		if (deps->item[i].name != name)
			continue;

		deps->item[i] = deps->item[deps->num-1];
Timo Teräs's avatar
Timo Teräs committed
187
		apk_dependency_array_resize(pdeps, deps->num-1);
188 189 190 191
		break;
	}
}

192
void apk_blob_pull_dep(apk_blob_t *b, struct apk_database *db, struct apk_dependency *dep)
193 194
{
	struct apk_name *name;
195
	apk_blob_t bdep, bname, bop, bver = APK_BLOB_NULL, btag;
196
	int mask = APK_DEPMASK_REQUIRE, optional = 0, tag = 0;
197

198
	/* [!]name[<,<=,=,>=,>,><]ver */
199 200
	if (APK_BLOB_IS_NULL(*b))
		goto fail;
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217

	/* grap one token */
	if (!apk_blob_cspn(*b, " \n", &bdep, NULL))
		bdep = *b;
	b->ptr += bdep.len;
	b->len -= bdep.len;

	/* skip also all separator chars */
	if (!apk_blob_spn(*b, " \n", NULL, b)) {
		b->ptr += b->len;
		b->len = 0;
	}

	/* parse the version */
	if (bdep.ptr[0] == '!') {
		bdep.ptr++;
		bdep.len--;
218
		optional = 1;
219
	}
220 221

	if (apk_blob_cspn(bdep, "<>=", &bname, &bop)) {
222 223 224
		int i;

		if (mask == 0)
225
			goto fail;
226
		if (!apk_blob_spn(bop, "<>=", &bop, &bver))
227
			goto fail;
Timo Teräs's avatar
Timo Teräs committed
228 229 230
		mask = 0;
		for (i = 0; i < bop.len; i++) {
			switch (bop.ptr[i]) {
231 232 233 234 235 236 237 238 239 240 241
			case '<':
				mask |= APK_VERSION_LESS;
				break;
			case '>':
				mask |= APK_VERSION_GREATER;
				break;
			case '=':
				mask |= APK_VERSION_EQUAL;
				break;
			}
		}
242 243
		if ((mask & APK_DEPMASK_CHECKSUM) != APK_DEPMASK_CHECKSUM &&
		    !apk_version_validate(bver))
244 245
			goto fail;
	} else {
246
		bname = bdep;
247 248
		bop = APK_BLOB_NULL;
		bver = APK_BLOB_NULL;
249 250
	}

251 252 253
	if (apk_blob_split(bname, APK_BLOB_STR("@"), &bname, &btag))
		tag = apk_db_get_tag_id(db, btag);

254
	/* convert to apk_dependency */
255
	name = apk_db_get_name(db, bname);
256
	if (name == NULL)
257
		goto fail;
258

259 260 261
	if (optional)
		mask ^= APK_DEPMASK_REQUIRE;

262 263
	*dep = (struct apk_dependency){
		.name = name,
264
		.version = apk_blob_atomize_dup(bver),
265
		.repository_tag = tag,
266
		.result_mask = mask,
267
		.optional = optional,
268
	};
269 270 271 272
	return;
fail:
	*dep = (struct apk_dependency){ .name = NULL };
	*b = APK_BLOB_NULL;
Timo Teräs's avatar
Timo Teräs committed
273 274
}

275 276 277 278 279 280 281 282 283 284 285 286 287
void apk_blob_pull_deps(apk_blob_t *b, struct apk_database *db, struct apk_dependency_array **deps)
{
	struct apk_dependency dep;

	while (b->len > 0) {
		apk_blob_pull_dep(b, db, &dep);
		if (APK_BLOB_IS_NULL(*b) || dep.name == NULL)
			break;

		*apk_dependency_array_add(deps) = dep;
	}
}

Timo Teräs's avatar
Timo Teräs committed
288 289 290
void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db,
		      struct apk_package *pkg)
{
291 292 293 294 295 296
	char buf[64];
	apk_blob_t b = APK_BLOB_BUF(buf);

	apk_blob_push_csum(&b, &pkg->csum);
	b = apk_blob_pushed(APK_BLOB_BUF(buf), b);

Timo Teräs's avatar
Timo Teräs committed
297
	*dep = (struct apk_dependency) {
298
		.name = pkg->name,
299 300
		.version = apk_blob_atomize_dup(b),
		.result_mask = APK_DEPMASK_CHECKSUM,
Timo Teräs's avatar
Timo Teräs committed
301 302 303
	};
}

304 305
int apk_dep_is_satisfied(struct apk_dependency *dep, struct apk_package *pkg)
{
306 307
	if (pkg == NULL)
		return dep->optional;
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
	if (dep->name != pkg->name)
		return 0;
	if (dep->result_mask == APK_DEPMASK_CHECKSUM) {
		struct apk_checksum csum;
		apk_blob_t b = *dep->version;

		apk_blob_pull_csum(&b, &csum);
		if (apk_checksum_compare(&csum, &pkg->csum) == 0)
			return 1;
	} else {
		if (apk_version_compare_blob(*pkg->version, *dep->version)
		    & dep->result_mask)
			return 1;
	}
	return 0;
323 324
}

325
void apk_blob_push_dep(apk_blob_t *to, struct apk_database *db, struct apk_dependency *dep)
326
{
327 328 329
	int result_mask = dep->result_mask;

	if (dep->optional) {
330
		apk_blob_push_blob(to, APK_BLOB_PTR_LEN("!", 1));
331 332
		result_mask ^= APK_DEPMASK_REQUIRE;
	}
333 334

	apk_blob_push_blob(to, APK_BLOB_STR(dep->name->name));
335 336 337 338
	if (dep->repository_tag && db != NULL) {
		apk_blob_push_blob(to, APK_BLOB_PTR_LEN("@", 1));
		apk_blob_push_blob(to, *db->repo_tags[dep->repository_tag].name);
	}
339 340 341 342
	if (!APK_BLOB_IS_NULL(*dep->version)) {
		apk_blob_push_blob(to, APK_BLOB_STR(apk_version_op_string(dep->result_mask)));
		apk_blob_push_blob(to, *dep->version);
	}
343 344
}

345
void apk_blob_push_deps(apk_blob_t *to, struct apk_database *db, struct apk_dependency_array *deps)
346 347 348 349 350 351 352 353 354
{
	int i;

	if (deps == NULL)
		return;

	for (i = 0; i < deps->num; i++) {
		if (i)
			apk_blob_push_blob(to, APK_BLOB_PTR_LEN(" ", 1));
355
		apk_blob_push_dep(to, db, &deps->item[i]);
356 357 358
	}
}

359
int apk_deps_write(struct apk_database *db, struct apk_dependency_array *deps, struct apk_ostream *os, apk_blob_t separator)
360
{
361 362 363
	apk_blob_t blob;
	char tmp[256];
	int i, n = 0;
364 365 366 367 368

	if (deps == NULL)
		return 0;

	for (i = 0; i < deps->num; i++) {
369 370
		blob = APK_BLOB_BUF(tmp);
		if (i)
371
			apk_blob_push_blob(&blob, separator);
372
		apk_blob_push_dep(&blob, db, &deps->item[i]);
373 374 375 376 377

		blob = apk_blob_pushed(APK_BLOB_BUF(tmp), blob);
		if (APK_BLOB_IS_NULL(blob) || 
		    os->write(os, blob.ptr, blob.len) != blob.len)
			return -1;
378

379
		n += blob.len;
380 381 382 383 384
	}

	return n;
}

Timo Teräs's avatar
Timo Teräs committed
385
const char *apk_script_types[] = {
386 387 388 389 390 391
	[APK_SCRIPT_PRE_INSTALL]	= "pre-install",
	[APK_SCRIPT_POST_INSTALL]	= "post-install",
	[APK_SCRIPT_PRE_DEINSTALL]	= "pre-deinstall",
	[APK_SCRIPT_POST_DEINSTALL]	= "post-deinstall",
	[APK_SCRIPT_PRE_UPGRADE]	= "pre-upgrade",
	[APK_SCRIPT_POST_UPGRADE]	= "post-upgrade",
392
	[APK_SCRIPT_TRIGGER]		= "trigger",
393 394 395 396 397 398
};

int apk_script_type(const char *name)
{
	int i;

Timo Teräs's avatar
Timo Teräs committed
399 400 401
	for (i = 0; i < ARRAY_SIZE(apk_script_types); i++)
		if (apk_script_types[i] &&
		    strcmp(apk_script_types[i], name) == 0)
402 403
			return i;

404
	return APK_SCRIPT_INVALID;
405 406
}

407
void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action,
408
		       struct apk_checksum *identity, int keys_fd)
Timo Teräs's avatar
Timo Teräs committed
409 410
{
	memset(ctx, 0, sizeof(struct apk_sign_ctx));
411
	ctx->keys_fd = keys_fd;
Timo Teräs's avatar
Timo Teräs committed
412 413
	ctx->action = action;
	switch (action) {
Timo Teräs's avatar
Timo Teräs committed
414 415 416
	case APK_SIGN_VERIFY:
		ctx->md = EVP_md_null();
		break;
417 418 419 420 421 422 423 424 425 426
	case APK_SIGN_VERIFY_IDENTITY:
		if (identity->type == APK_CHECKSUM_MD5) {
			ctx->md = EVP_md5();
			ctx->control_started = 1;
			ctx->data_started = 1;
		} else {
			ctx->md = EVP_sha1();
		}
		memcpy(&ctx->identity, identity, sizeof(ctx->identity));
		break;
Timo Teräs's avatar
Timo Teräs committed
427
	case APK_SIGN_GENERATE:
428
	case APK_SIGN_VERIFY_AND_GENERATE:
Timo Teräs's avatar
Timo Teräs committed
429 430
		ctx->md = EVP_sha1();
		break;
431 432 433 434 435 436
	default:
		action = APK_SIGN_NONE;
		ctx->md = EVP_md_null();
		ctx->control_started = 1;
		ctx->data_started = 1;
		break;
Timo Teräs's avatar
Timo Teräs committed
437
	}
438 439
	EVP_MD_CTX_init(&ctx->mdctx);
	EVP_DigestInit_ex(&ctx->mdctx, ctx->md, NULL);
440
	EVP_MD_CTX_set_flags(&ctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
Timo Teräs's avatar
Timo Teräs committed
441 442 443 444 445 446 447 448
}

void apk_sign_ctx_free(struct apk_sign_ctx *ctx)
{
	if (ctx->signature.data.ptr != NULL)
		free(ctx->signature.data.ptr);
	if (ctx->signature.pkey != NULL)
		EVP_PKEY_free(ctx->signature.pkey);
449
	EVP_MD_CTX_cleanup(&ctx->mdctx);
Timo Teräs's avatar
Timo Teräs committed
450 451 452 453 454 455 456 457 458
}

int apk_sign_ctx_process_file(struct apk_sign_ctx *ctx,
			      const struct apk_file_info *fi,
			      struct apk_istream *is)
{
	if (ctx->data_started)
		return 1;

459
	if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
460 461 462 463 464 465
		/* APKv1.0 compatibility - first non-hidden file is
		 * considered to start the data section of the file.
		 * This does not make any sense if the file has v2.0
		 * style .PKGINFO */
		if (ctx->has_data_checksum)
			return -ENOMSG;
Timo Teräs's avatar
Timo Teräs committed
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
		ctx->data_started = 1;
		ctx->control_started = 1;
		return 1;
	}

	if (ctx->control_started)
		return 1;

	if (strncmp(fi->name, ".SIGN.", 6) != 0) {
		ctx->control_started = 1;
		return 1;
	}

	/* A signature file */
	ctx->num_signatures++;

	/* Found already a trusted key */
483 484
	if ((ctx->action != APK_SIGN_VERIFY &&
	     ctx->action != APK_SIGN_VERIFY_AND_GENERATE) ||
485
	    ctx->signature.pkey != NULL)
Timo Teräs's avatar
Timo Teräs committed
486 487
		return 0;

488 489 490
	if (ctx->keys_fd < 0)
		return 0;

Timo Teräs's avatar
Timo Teräs committed
491 492
	if (strncmp(&fi->name[6], "RSA.", 4) == 0 ||
	    strncmp(&fi->name[6], "DSA.", 4) == 0) {
493
		int fd = openat(ctx->keys_fd, &fi->name[10], O_RDONLY|O_CLOEXEC);
494 495 496 497 498
	        BIO *bio;

		if (fd < 0)
			return 0;

499
		bio = BIO_new_fp(fdopen(fd, "r"), BIO_CLOSE);
500
		ctx->signature.pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
Timo Teräs's avatar
Timo Teräs committed
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
		if (ctx->signature.pkey != NULL) {
			if (fi->name[6] == 'R')
				ctx->md = EVP_sha1();
			else
				ctx->md = EVP_dss1();
		}
		BIO_free(bio);
	} else
		return 0;

	if (ctx->signature.pkey != NULL)
		ctx->signature.data = apk_blob_from_istream(is, fi->size);

	return 0;
}

517
int apk_sign_ctx_parse_pkginfo_line(void *ctx, apk_blob_t line)
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
{
	struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
	apk_blob_t l, r;

	if (line.ptr == NULL || line.len < 1 || line.ptr[0] == '#')
		return 0;

	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
		return 0;

	if (sctx->data_started == 0 &&
	    apk_blob_compare(APK_BLOB_STR("datahash"), l) == 0) {
		sctx->has_data_checksum = 1;
		sctx->md = EVP_sha256();
		apk_blob_pull_hexdump(
			&r, APK_BLOB_PTR_LEN(sctx->data_checksum,
					     EVP_MD_size(sctx->md)));
	}

	return 0;
}

int apk_sign_ctx_verify_tar(void *sctx, const struct apk_file_info *fi,
			    struct apk_istream *is)
{
	struct apk_sign_ctx *ctx = (struct apk_sign_ctx *) sctx;
544
	int r;
545

546 547 548
	r = apk_sign_ctx_process_file(ctx, fi, is);
	if (r <= 0)
		return r;
549 550 551

	if (strcmp(fi->name, ".PKGINFO") == 0) {
		apk_blob_t blob = apk_blob_from_istream(is, fi->size);
552 553 554
		apk_blob_for_each_segment(
			blob, "\n",
			apk_sign_ctx_parse_pkginfo_line, ctx);
555 556 557 558 559 560
		free(blob.ptr);
	}

	return 0;
}

561
int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
Timo Teräs's avatar
Timo Teräs committed
562 563 564
{
	struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
	unsigned char calculated[EVP_MAX_MD_SIZE];
Timo Teräs's avatar
Timo Teräs committed
565 566 567 568 569 570 571
	int r, end_of_control;

	if ((part == APK_MPART_DATA) ||
	    (part == APK_MPART_BOUNDARY && sctx->data_started))
		goto update_digest;

	/* Still in signature blocks? */
572 573 574
	if (!sctx->control_started) {
		if (part == APK_MPART_END)
			return -EKEYREJECTED;
Timo Teräs's avatar
Timo Teräs committed
575
		goto reset_digest;
576
	}
Timo Teräs's avatar
Timo Teräs committed
577 578 579 580 581 582

	/* Grab state and mark all remaining block as data */
	end_of_control = (sctx->data_started == 0);
	sctx->data_started = 1;

	/* End of control-block and control does not have data checksum? */
583 584
	if (sctx->has_data_checksum == 0 && end_of_control &&
	    part != APK_MPART_END)
Timo Teräs's avatar
Timo Teräs committed
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
		goto update_digest;

	/* Drool in the remaining of the digest block now, we will finish
	 * it on all cases */
	EVP_DigestUpdate(&sctx->mdctx, data.ptr, data.len);

	/* End of control-block and checking control hash/signature or
	 * end of data-block and checking its hash/signature */
	if (sctx->has_data_checksum && !end_of_control) {
		/* End of control-block and check it's hash */
		EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL);
		if (EVP_MD_CTX_size(&sctx->mdctx) == 0 ||
		    memcmp(calculated, sctx->data_checksum,
		           EVP_MD_CTX_size(&sctx->mdctx)) != 0)
			return -EKEYREJECTED;
		sctx->data_verified = 1;
		if (!(apk_flags & APK_ALLOW_UNTRUSTED) &&
		    !sctx->control_verified)
			return -ENOKEY;
		return 0;
	}
Timo Teräs's avatar
Timo Teräs committed
606

Timo Teräs's avatar
Timo Teräs committed
607 608
	switch (sctx->action) {
	case APK_SIGN_VERIFY:
609
	case APK_SIGN_VERIFY_AND_GENERATE:
Timo Teräs's avatar
Timo Teräs committed
610 611 612 613
		if (sctx->signature.pkey == NULL) {
			if (apk_flags & APK_ALLOW_UNTRUSTED)
				break;
			return -ENOKEY;
Timo Teräs's avatar
Timo Teräs committed
614 615
		}

Timo Teräs's avatar
Timo Teräs committed
616 617 618 619 620 621 622 623
		r = EVP_VerifyFinal(&sctx->mdctx,
			(unsigned char *) sctx->signature.data.ptr,
			sctx->signature.data.len,
			sctx->signature.pkey);
		if (r != 1)
			return -EKEYREJECTED;
		sctx->control_verified = 1;
		if (!sctx->has_data_checksum && part == APK_MPART_END)
624
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
625 626 627 628 629 630 631 632 633
		break;
	case APK_SIGN_VERIFY_IDENTITY:
		/* Reset digest for hashing data */
		EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL);
		if (memcmp(calculated, sctx->identity.data,
			   sctx->identity.type) != 0)
			return -EKEYREJECTED;
		sctx->control_verified = 1;
		if (!sctx->has_data_checksum && part == APK_MPART_END)
634
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
635 636 637 638 639 640 641 642
		break;
	case APK_SIGN_GENERATE:
		/* Package identity is the checksum */
		sctx->identity.type = EVP_MD_CTX_size(&sctx->mdctx);
		EVP_DigestFinal_ex(&sctx->mdctx, sctx->identity.data, NULL);
		if (sctx->action == APK_SIGN_GENERATE &&
		    sctx->has_data_checksum)
			return -ECANCELED;
643
		break;
Timo Teräs's avatar
Timo Teräs committed
644
	}
645 646 647 648
	if (sctx->action == APK_SIGN_VERIFY_AND_GENERATE) {
		sctx->identity.type = EVP_MD_CTX_size(&sctx->mdctx);
		EVP_DigestFinal_ex(&sctx->mdctx, sctx->identity.data, NULL);
	}
Timo Teräs's avatar
Timo Teräs committed
649 650 651 652 653 654 655 656
reset_digest:
	EVP_DigestInit_ex(&sctx->mdctx, sctx->md, NULL);
	EVP_MD_CTX_set_flags(&sctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
	return 0;

update_digest:
	EVP_MD_CTX_clear_flags(&sctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
	EVP_DigestUpdate(&sctx->mdctx, data.ptr, data.len);
Timo Teräs's avatar
Timo Teräs committed
657 658 659
	return 0;
}

660 661 662
struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
663
	struct apk_sign_ctx *sctx;
664 665
};

666 667
int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
		     char field, apk_blob_t value)
Timo Teräs's avatar
Timo Teräs committed
668 669 670
{
	switch (field) {
	case 'P':
671
		pkg->name = apk_db_get_name(db, value);
Timo Teräs's avatar
Timo Teräs committed
672 673
		break;
	case 'V':
674
		pkg->version = apk_blob_atomize_dup(value);
Timo Teräs's avatar
Timo Teräs committed
675 676 677 678 679 680 681 682
		break;
	case 'T':
		pkg->description = apk_blob_cstr(value);
		break;
	case 'U':
		pkg->url = apk_blob_cstr(value);
		break;
	case 'L':
683
		pkg->license = apk_blob_atomize_dup(value);
Timo Teräs's avatar
Timo Teräs committed
684
		break;
685
	case 'A':
686
		pkg->arch = apk_blob_atomize_dup(value);
687
		break;
Timo Teräs's avatar
Timo Teräs committed
688
	case 'D':
689
		apk_blob_pull_deps(&value, db, &pkg->depends);
Timo Teräs's avatar
Timo Teräs committed
690 691
		break;
	case 'C':
Timo Teräs's avatar
Timo Teräs committed
692
		apk_blob_pull_csum(&value, &pkg->csum);
Timo Teräs's avatar
Timo Teräs committed
693 694
		break;
	case 'S':
695
		pkg->size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
696 697
		break;
	case 'I':
698
		pkg->installed_size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
699
		break;
Timo Teräs's avatar
Timo Teräs committed
700
	case 'i':
701
		apk_blob_pull_deps(&value, db, &pkg->install_if);
Timo Teräs's avatar
Timo Teräs committed
702
		break;
703 704 705 706 707 708 709 710 711 712 713 714
	case 'o':
		pkg->origin = apk_blob_atomize_dup(value);
		break;
	case 'm':
		pkg->maintainer = apk_blob_atomize_dup(value);
		break;
	case 't':
		pkg->build_time = apk_blob_pull_uint(&value, 10);
		break;
	case 'c':
		pkg->commit = apk_blob_cstr(value);
		break;
715
	case 'F': case 'M': case 'R': case 'Z': case 'r': case 'q':
716 717
		/* installed db entries which are handled in database.c */
		return 1;
718
	default:
719 720 721 722 723 724
		/* lower case index entries are safe to be ignored */
		if (!islower(field)) {
			pkg->filename = APK_PKG_UNINSTALLABLE;
			db->compat_notinstallable = 1;
		}
		db->compat_newfeatures = 1;
725
		return 2;
Timo Teräs's avatar
Timo Teräs committed
726
	}
727 728
	if (APK_BLOB_IS_NULL(value))
		return -1;
Timo Teräs's avatar
Timo Teräs committed
729 730 731 732 733 734 735 736 737
	return 0;
}

static int read_info_line(void *ctx, apk_blob_t line)
{
	static struct {
		const char *str;
		char field;
	} fields[] = {
Timo Teräs's avatar
Timo Teräs committed
738 739 740 741 742 743 744 745 746
		{ "pkgname",	'P' },
		{ "pkgver", 	'V' },
		{ "pkgdesc",	'T' },
		{ "url",	'U' },
		{ "size",	'I' },
		{ "license",	'L' },
		{ "arch",	'A' },
		{ "depend",	'D' },
		{ "install_if",	'i' },
747 748 749 750
		{ "origin",	'o' },
		{ "maintainer",	'm' },
		{ "builddate",	't' },
		{ "commit",	'c' },
Timo Teräs's avatar
Timo Teräs committed
751 752 753 754 755 756 757 758
	};
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
	apk_blob_t l, r;
	int i;

	if (line.ptr == NULL || line.len < 1 || line.ptr[0] == '#')
		return 0;

759
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
760 761 762
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
763
		if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
764
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
765
			return 0;
Timo Teräs's avatar
Timo Teräs committed
766 767
		}
	}
768
	apk_sign_ctx_parse_pkginfo_line(ri->sctx, line);
769

Timo Teräs's avatar
Timo Teräs committed
770 771 772
	return 0;
}

773
static int read_info_entry(void *ctx, const struct apk_file_info *ae,
774
			   struct apk_istream *is)
775 776 777
{
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
	struct apk_package *pkg = ri->pkg;
778
	int r;
779

Timo Teräs's avatar
Timo Teräs committed
780
	/* Meta info and scripts */
781 782 783
	r = apk_sign_ctx_process_file(ri->sctx, ae, is);
	if (r <= 0)
		return r;
784

Timo Teräs's avatar
Timo Teräs committed
785
	if (ae->name[0] == '.') {
Timo Teräs's avatar
Timo Teräs committed
786 787
		/* APK 2.0 format */
		if (strcmp(ae->name, ".PKGINFO") == 0) {
788
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
Timo Teräs's avatar
Timo Teräs committed
789 790
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
791
		} else if (strcmp(ae->name, ".INSTALL") == 0) {
792 793
			apk_warning("Package '%s-%s' contains deprecated .INSTALL",
				    pkg->name->name, pkg->version);
Timo Teräs's avatar
Timo Teräs committed
794
		}
795 796 797
		return 0;
	}

798 799 800
	return 0;
}

801 802
int apk_pkg_read(struct apk_database *db, const char *file,
	         struct apk_sign_ctx *sctx, struct apk_package **pkg)
803 804
{
	struct read_info_ctx ctx;
805
	struct apk_file_info fi;
806
	struct apk_bstream *bs;
807
	struct apk_istream *tar;
808
	int r;
809

810
	r = apk_file_get_info(AT_FDCWD, file, APK_CHECKSUM_NONE, &fi);
811 812
	if (r != 0)
		return r;
813

814
	memset(&ctx, 0, sizeof(ctx));
Timo Teräs's avatar
Timo Teräs committed
815
	ctx.sctx = sctx;
816
	ctx.pkg = apk_pkg_new();
817
	r = -ENOMEM;
818
	if (ctx.pkg == NULL)
819
		goto err;
820
	bs = apk_bstream_from_file(AT_FDCWD, file);
Timo Teräs's avatar
Timo Teräs committed
821
	if (bs == NULL)
822 823 824
		goto err;

	ctx.db = db;
825
	ctx.pkg->size = fi.size;
826

Timo Teräs's avatar
Timo Teräs committed
827
	tar = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, sctx);
828
	r = apk_tar_parse(tar, read_info_entry, &ctx, FALSE, &db->id_cache);
829
	tar->close(tar);
Timo Teräs's avatar
Timo Teräs committed
830
	if (r < 0 && r != -ECANCELED)
831
		goto err;
832 833 834
	if (ctx.pkg->name == NULL ||
	    ctx.pkg->filename == APK_PKG_UNINSTALLABLE) {
		r = -ENOTSUP;
835
		goto err;
836
	}
Timo Teräs's avatar
Timo Teräs committed
837 838
	if (sctx->action != APK_SIGN_VERIFY)
		ctx.pkg->csum = sctx->identity;
839
	ctx.pkg->filename = strdup(file);
840

841 842 843
	ctx.pkg = apk_db_pkg_add(db, ctx.pkg);
	if (pkg != NULL)
		*pkg = ctx.pkg;
844
	return 0;
845 846
err:
	apk_pkg_free(ctx.pkg);
847
	return r;
848 849 850 851 852 853 854
}

void apk_pkg_free(struct apk_package *pkg)
{
	if (pkg == NULL)
		return;

855
	apk_pkg_uninstall(NULL, pkg);
Timo Teräs's avatar
Timo Teräs committed
856
	apk_dependency_array_free(&pkg->depends);
Timo Teräs's avatar
Timo Teräs committed
857
	apk_dependency_array_free(&pkg->install_if);
858 859 860 861
	if (pkg->url)
		free(pkg->url);
	if (pkg->description)
		free(pkg->description);
862 863
	if (pkg->commit)
		free(pkg->commit);
864 865 866
	free(pkg);
}

867 868 869
int apk_ipkg_add_script(struct apk_installed_package *ipkg,
			struct apk_istream *is,
			unsigned int type, unsigned int size)
870
{
871
	void *ptr;
872 873
	int r;

874 875 876 877 878
	if (type >= APK_SCRIPT_MAX)
		return -1;

	ptr = malloc(size);
	r = is->read(is, ptr, size);
879
	if (r < 0) {
880
		free(ptr);
881 882 883
		return r;
	}

884 885 886 887 888
	if (ipkg->script[type].ptr)
		free(ipkg->script[type].ptr);
	ipkg->script[type].ptr = ptr;
	ipkg->script[type].len = size;
	return 0;
889 890
}

891 892
int apk_ipkg_run_script(struct apk_installed_package *ipkg,
			struct apk_database *db,
893
			unsigned int type, char **argv)
894
{
895
	static char * const environment[] = {
896 897 898
		"PATH=/usr/sbin:/usr/bin:/sbin:/bin",
		NULL
	};
899 900
	struct apk_package *pkg = ipkg->pkg;
	char fn[PATH_MAX];
901
	int fd, status, root_fd = db->root_fd;
902 903
	pid_t pid;

904 905
	if (type >= APK_SCRIPT_MAX)
		return -1;
906

907 908 909 910
	if (ipkg->script[type].ptr == NULL)
		return 0;

	argv[0] = (char *) apk_script_types[type];
911

912
	/* Avoid /tmp as it can be mounted noexec */
913 914
	snprintf(fn, sizeof(fn), "var/cache/misc/" PKG_VER_FMT ".%s",
		PKG_VER_PRINTF(pkg),
915 916
		apk_script_types[type]);

917 918 919 920
	apk_message("Executing %s", &fn[15]);
	if (apk_flags & APK_SIMULATE)
		return 0;

921
	fd = openat(root_fd, fn, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, 0755);
922 923
	if (fd < 0) {
		mkdirat(root_fd, "var/cache/misc", 0755);
924
		fd = openat(root_fd, fn, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, 0755);
925 926 927
		if (fd < 0)
			return -errno;
	}
928 929 930 931
	if (write(fd, ipkg->script[type].ptr, ipkg->script[type].len) < 0) {
		close(fd);
		return -errno;
	}
932
	close(fd);
933

934 935
	pid = fork();
	if (pid == -1)
936
		return -1;
937
	if (pid == 0) {
938
		if (fchdir(root_fd) < 0 || chroot(".") < 0) {
939 940 941 942 943
			apk_error("chroot: %s", strerror(errno));
		} else {
			execve(fn, argv, environment);
		}
		exit(1);
944
	}
945 946
	waitpid(pid, &status, 0);
	unlinkat(root_fd, fn, 0);
947
	apk_id_cache_reset(&db->id_cache);
948

949 950 951 952 953 954
	if (WIFEXITED(status)) {
		int rc = WEXITSTATUS(status);
		if (rc != 0)
			apk_warning("%s: returned error %d", &fn[15], rc);
		return 0;
	}
955
	return -1;
956 957
}

Timo Teräs's avatar
Timo Teräs committed
958
static int parse_index_line(void *ctx, apk_blob_t line)
959
{
Timo Teräs's avatar
Timo Teräs committed
960
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
961

Timo Teräs's avatar
Timo Teräs committed
962 963
	if (line.len < 3 || line.ptr[1] != ':')
		return 0;
964

965
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
966 967 968 969 970
	return 0;
}

struct apk_package *apk_pkg_parse_index_entry(struct apk_database *db, apk_blob_t blob)
{
Timo Teräs's avatar
Timo Teräs committed
971
	struct read_info_ctx ctx;
972

973
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
974
	if (ctx.pkg == NULL)
975 976
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
977
	ctx.db = db;
978

Timo Teräs's avatar
Timo Teräs committed
979 980 981 982
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
983 984
		apk_error("Failed to parse index entry: " BLOB_FMT,
			  BLOB_PRINTF(blob));
Timo Teräs's avatar
Timo Teräs committed
985
		ctx.pkg = NULL;
986 987
	}

Timo Teräs's avatar
Timo Teräs committed
988
	return ctx.pkg;
989 990
}

Timo Teräs's avatar
Timo Teräs committed
991 992 993 994 995 996 997 998 999 1000
static int write_depends(struct apk_ostream *os, const char *field,
			 struct apk_dependency_array *deps)
{
	int r;

	if (deps->num == 0)
		return 0;

	if (os->write(os, field, 2) != 2)
		return -1;
1001
	r = apk_deps_write(NULL, deps, os, APK_BLOB_PTR_LEN(" ", 1));
Timo Teräs's avatar
Timo Teräs committed
1002 1003 1004 1005 1006 1007 1008 1009
	if (r < 0)
		return r;
	if (os->write(os, "\n", 1) != 1)
		return -1;

	return 0;
}

1010 1011
int apk_pkg_write_index_entry(struct apk_package *info,
			      struct apk_ostream *os)
1012
{
1013
	char buf[512];
1014 1015 1016
	apk_blob_t bbuf = APK_BLOB_BUF(buf);

	apk_blob_push_blob(&bbuf, APK_BLOB_STR("C:"));
Timo Teräs's avatar
Timo Teräs committed
1017
	apk_blob_push_csum(&bbuf, &info->csum);
1018 1019 1020
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nP:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->name->name));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nV:"));
1021
	apk_blob_push_blob(&bbuf, *info->version);
1022 1023
	if (info->arch != NULL) {
		apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nA:"));
1024
		apk_blob_push_blob(&bbuf, *info->arch);
1025
	}
1026 1027 1028 1029 1030 1031 1032 1033 1034
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nS:"));
	apk_blob_push_uint(&bbuf, info->size, 10);
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nI:"));
	apk_blob_push_uint(&bbuf, info->installed_size, 10);
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nT:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->description));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nU:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->url));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nL:"));
1035
	apk_blob_push_blob(&bbuf, *info->license);
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
	if (info->origin) {
		apk_blob_push_blob(&bbuf, APK_BLOB_STR("\no:"));
		apk_blob_push_blob(&bbuf, *info->origin);
	}
	if (info->maintainer) {
		apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nm:"));
		apk_blob_push_blob(&bbuf, *info->maintainer);
	}
	if (info->build_time) {
		apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nt:"));
		apk_blob_push_uint(&bbuf, info->build_time, 10);
	}
	if (info->commit) {
		apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nc:"));
		apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->commit));
	}
1052 1053
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));

Timo Teräs's avatar
Timo Teräs committed
1054 1055 1056
	if (APK_BLOB_IS_NULL(bbuf))
		return -1;

1057
	bbuf = apk_blob_pushed(APK_BLOB_BUF(buf), bbuf);
Timo Teräs's avatar
Timo Teräs committed
1058 1059 1060
	if (os->write(os, bbuf.ptr, bbuf.len) != bbuf.len ||
	    write_depends(os, "D:", info->depends) ||
	    write_depends(os, "i:", info->install_if))
1061
		return -1;
1062

1063
	return 0;
1064
}
1065

1066 1067
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
1068 1069 1070 1071
	if (a->version == b->version)
		return APK_VERSION_EQUAL;

	return apk_version_compare_blob(*a->version, *b->version);
1072
}