package.c 24.4 KB
Newer Older
1 2 3 4 5 6
/* package.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
 * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
 * 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 30
#include "apk_defines.h"
#include "apk_archive.h"
#include "apk_package.h"
#include "apk_database.h"
#include "apk_state.h"
Natanael Copa's avatar
Natanael Copa committed
31
#include "apk_print.h"
32

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
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("-"));
	apk_blob_push_blob(&to, APK_BLOB_STR(pkg->version));
	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("-"));
	apk_blob_push_blob(&to, APK_BLOB_STR(pkg->version));
	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));
}

56
struct apk_package *apk_pkg_new(void)
57
{
Timo Teräs's avatar
Timo Teräs committed
58 59 60 61 62 63 64
	struct apk_package *pkg;

	pkg = calloc(1, sizeof(struct apk_package));
	if (pkg != NULL)
		apk_dependency_array_init(&pkg->depends);

	return pkg;
65
}
66

67 68 69 70 71 72 73 74 75 76
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
77 78
	apk_string_array_init(&ipkg->triggers);
	apk_string_array_init(&ipkg->pending_triggers);
Timo Teräs's avatar
Timo Teräs committed
79 80 81 82 83 84 85

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

	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;

	if (db != NULL)
		db->installed.stats.packages--;
100

101 102
	list_del(&ipkg->installed_pkgs_list);

Timo Teräs's avatar
Timo Teräs committed
103
	if (ipkg->triggers->num) {
104
		list_del(&ipkg->trigger_pkgs_list);
105
		list_init(&ipkg->trigger_pkgs_list);
106 107 108
		for (i = 0; i < ipkg->triggers->num; i++)
			free(ipkg->triggers->item[i]);
	}
Timo Teräs's avatar
Timo Teräs committed
109 110
	apk_string_array_free(&ipkg->triggers);
	apk_string_array_free(&ipkg->pending_triggers);
111 112 113 114 115 116

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

119 120 121 122 123 124
int apk_pkg_parse_name(apk_blob_t apkname,
		       apk_blob_t *name,
		       apk_blob_t *version)
{
	int i, dash = 0;

125 126 127
	if (APK_BLOB_IS_NULL(apkname))
		return -1;

128 129 130 131 132 133 134 135
	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;
	}
136 137 138
	if (i < 0)
		return -1;

139 140 141 142 143 144 145 146 147
	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;
}

Timo Teräs's avatar
Timo Teräs committed
148
static apk_blob_t trim(apk_blob_t str)
149 150
{
	if (str.ptr == NULL || str.len < 1)
Timo Teräs's avatar
Timo Teräs committed
151
		return str;
152

153 154
	if (str.ptr[str.len-1] == '\n') {
		str.ptr[str.len-1] = 0;
Timo Teräs's avatar
Timo Teräs committed
155
		return APK_BLOB_PTR_LEN(str.ptr, str.len-1);
156
	}
157

Timo Teräs's avatar
Timo Teräs committed
158
	return str;
159 160 161 162 163 164 165 166 167 168
}

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
169 170
			if (deps->item[i].name == dep->name) {
				deps->item[i] = *dep;
171
				return 0;
Timo Teräs's avatar
Timo Teräs committed
172
			}
173 174 175 176 177 178
		}
	}

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

180 181 182 183 184 185 186 187 188 189 190 191 192 193
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
194
		apk_dependency_array_resize(pdeps, deps->num-1);
195 196 197 198
		break;
	}
}

199 200 201 202 203
struct parse_depend_ctx {
	struct apk_database *db;
	struct apk_dependency_array **depends;
};

Timo Teräs's avatar
Timo Teräs committed
204 205
int apk_dep_from_blob(struct apk_dependency *dep, struct apk_database *db,
		      apk_blob_t blob)
206 207
{
	struct apk_name *name;
208 209
	apk_blob_t bname, bop, bver = APK_BLOB_NULL;
	int mask = APK_VERSION_LESS | APK_VERSION_EQUAL | APK_VERSION_GREATER;
210

211 212 213 214 215 216 217 218 219 220
	/* [!]name[<,<=,=,>=,>]ver */
	if (blob.ptr[0] == '!') {
		mask = 0;
		blob.ptr++;
		blob.len--;
	}
	if (apk_blob_cspn(blob, "<>=", &bname, &bop)) {
		int i;

		if (mask == 0)
Timo Teräs's avatar
Timo Teräs committed
221
			return -EINVAL;
222
		if (!apk_blob_spn(bop, "<>=", &bop, &bver))
Timo Teräs's avatar
Timo Teräs committed
223 224 225 226
			return -EINVAL;
		mask = 0;
		for (i = 0; i < bop.len; i++) {
			switch (bop.ptr[i]) {
227 228 229 230 231 232 233 234 235 236 237 238 239
			case '<':
				mask |= APK_VERSION_LESS;
				break;
			case '>':
				mask |= APK_VERSION_GREATER;
				break;
			case '=':
				mask |= APK_VERSION_EQUAL;
				break;
			}
		}
		if ((mask & (APK_VERSION_LESS|APK_VERSION_GREATER))
		    == (APK_VERSION_LESS|APK_VERSION_GREATER))
Timo Teräs's avatar
Timo Teräs committed
240
			return -EINVAL;
241 242

		if (!apk_version_validate(bver))
Timo Teräs's avatar
Timo Teräs committed
243 244 245
			return -EINVAL;

		blob = bname;
246 247
	}

Timo Teräs's avatar
Timo Teräs committed
248
	name = apk_db_get_name(db, blob);
249
	if (name == NULL)
Timo Teräs's avatar
Timo Teräs committed
250
		return -ENOENT;
251 252 253

	*dep = (struct apk_dependency){
		.name = name,
254 255
		.version = APK_BLOB_IS_NULL(bver) ? NULL : apk_blob_cstr(bver),
		.result_mask = mask,
256
	};
Timo Teräs's avatar
Timo Teräs committed
257 258 259 260 261 262 263
	return 0;
}

void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db,
		      struct apk_package *pkg)
{
	*dep = (struct apk_dependency) {
264
		.name = pkg->name,
Timo Teräs's avatar
Timo Teräs committed
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
		.version = pkg->version,
		.result_mask = APK_VERSION_EQUAL,
	};
}

static int parse_depend(void *ctx, apk_blob_t blob)
{
	struct parse_depend_ctx *pctx = (struct parse_depend_ctx *) ctx;
	struct apk_dependency *dep, p;

	if (blob.len == 0)
		return 0;

	if (apk_dep_from_blob(&p, pctx->db, blob) < 0)
		return -1;

	dep = apk_dependency_array_add(pctx->depends);
	if (dep == NULL)
		return -1;
	*dep = p;
285 286 287 288

	return 0;
}

289 290 291 292
void apk_deps_parse(struct apk_database *db,
		    struct apk_dependency_array **depends,
		    apk_blob_t blob)
{
293
	struct parse_depend_ctx ctx = { db, depends };
294

295
	if (blob.len > 0 && blob.ptr[blob.len-1] == '\n')
296
		blob.len--;
297

298
	apk_blob_for_each_segment(blob, " ", parse_depend, &ctx);
299 300
}

301 302 303 304 305 306 307 308 309 310 311 312 313 314
void apk_blob_push_dep(apk_blob_t *to, struct apk_dependency *dep)
{
	if (dep->result_mask == APK_DEPMASK_CONFLICT)
		apk_blob_push_blob(to, APK_BLOB_PTR_LEN("!", 1));

	apk_blob_push_blob(to, APK_BLOB_STR(dep->name->name));

	if (dep->result_mask != APK_DEPMASK_CONFLICT &&
	    dep->result_mask != APK_DEPMASK_REQUIRE) {
		apk_blob_push_blob(to, APK_BLOB_STR(apk_version_op_string(dep->result_mask)));
		apk_blob_push_blob(to, APK_BLOB_STR(dep->version));
	}
}

315 316
int apk_deps_write(struct apk_dependency_array *deps, struct apk_ostream *os)
{
317 318 319
	apk_blob_t blob;
	char tmp[256];
	int i, n = 0;
320 321 322 323 324

	if (deps == NULL)
		return 0;

	for (i = 0; i < deps->num; i++) {
325 326 327 328 329 330 331 332 333
		blob = APK_BLOB_BUF(tmp);
		if (i)
			apk_blob_push_blob(&blob, APK_BLOB_PTR_LEN(" ", 1));
		apk_blob_push_dep(&blob, &deps->item[i]);

		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;
334

335
		n += blob.len;
336 337 338 339 340
	}

	return n;
}

Timo Teräs's avatar
Timo Teräs committed
341
const char *apk_script_types[] = {
342 343 344 345 346 347
	[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",
348
	[APK_SCRIPT_TRIGGER]		= "trigger",
349 350 351 352 353 354
};

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

Timo Teräs's avatar
Timo Teräs committed
355 356 357
	for (i = 0; i < ARRAY_SIZE(apk_script_types); i++)
		if (apk_script_types[i] &&
		    strcmp(apk_script_types[i], name) == 0)
358 359
			return i;

360
	return APK_SCRIPT_INVALID;
361 362
}

363
void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action,
364
		       struct apk_checksum *identity, int keys_fd)
Timo Teräs's avatar
Timo Teräs committed
365 366
{
	memset(ctx, 0, sizeof(struct apk_sign_ctx));
367
	ctx->keys_fd = keys_fd;
Timo Teräs's avatar
Timo Teräs committed
368 369
	ctx->action = action;
	switch (action) {
Timo Teräs's avatar
Timo Teräs committed
370 371 372
	case APK_SIGN_VERIFY:
		ctx->md = EVP_md_null();
		break;
373 374 375 376 377 378 379 380 381 382
	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
383 384
	case APK_SIGN_GENERATE_V1:
		ctx->md = EVP_md5();
Timo Teräs's avatar
Timo Teräs committed
385 386
		ctx->control_started = 1;
		ctx->data_started = 1;
Timo Teräs's avatar
Timo Teräs committed
387 388
		break;
	case APK_SIGN_GENERATE:
389
	case APK_SIGN_VERIFY_AND_GENERATE:
Timo Teräs's avatar
Timo Teräs committed
390 391
		ctx->md = EVP_sha1();
		break;
392 393 394 395 396 397
	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
398
	}
399 400
	EVP_MD_CTX_init(&ctx->mdctx);
	EVP_DigestInit_ex(&ctx->mdctx, ctx->md, NULL);
401
	EVP_MD_CTX_set_flags(&ctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
Timo Teräs's avatar
Timo Teräs committed
402 403 404 405 406 407 408 409
}

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);
410
	EVP_MD_CTX_cleanup(&ctx->mdctx);
Timo Teräs's avatar
Timo Teräs committed
411 412
}

413 414 415 416 417 418 419 420 421 422 423 424 425 426
static int check_signing_key_trust(struct apk_sign_ctx *sctx)
{
	switch (sctx->action) {
	case APK_SIGN_VERIFY:
	case APK_SIGN_VERIFY_AND_GENERATE:
		if (sctx->signature.pkey == NULL) {
			if (apk_flags & APK_ALLOW_UNTRUSTED)
				break;
			return -ENOKEY;
		}
	}
	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
427 428 429 430
int apk_sign_ctx_process_file(struct apk_sign_ctx *ctx,
			      const struct apk_file_info *fi,
			      struct apk_istream *is)
{
431 432
	int r;

Timo Teräs's avatar
Timo Teräs committed
433 434 435
	if (ctx->data_started)
		return 1;

436
	if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
Timo Teräs's avatar
Timo Teräs committed
437 438
		ctx->data_started = 1;
		ctx->control_started = 1;
439 440 441
		r = check_signing_key_trust(ctx);
		if (r < 0)
			return r;
Timo Teräs's avatar
Timo Teräs committed
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
		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 */
457 458
	if ((ctx->action != APK_SIGN_VERIFY &&
	     ctx->action != APK_SIGN_VERIFY_AND_GENERATE) ||
459
	    ctx->signature.pkey != NULL)
Timo Teräs's avatar
Timo Teräs committed
460 461
		return 0;

462 463 464
	if (ctx->keys_fd < 0)
		return 0;

Timo Teräs's avatar
Timo Teräs committed
465 466
	if (strncmp(&fi->name[6], "RSA.", 4) == 0 ||
	    strncmp(&fi->name[6], "DSA.", 4) == 0) {
467
		int fd = openat(ctx->keys_fd, &fi->name[10], O_RDONLY|O_CLOEXEC);
468
		BIO *bio;
469 470 471 472

		if (fd < 0)
			return 0;

473
		bio = BIO_new_fp(fdopen(fd, "r"), BIO_CLOSE);
474
		ctx->signature.pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
Timo Teräs's avatar
Timo Teräs committed
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
		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;
}

491
int apk_sign_ctx_parse_pkginfo_line(void *ctx, apk_blob_t line)
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
{
	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;

	if (apk_sign_ctx_process_file(ctx, fi, is) == 0)
		return 0;

	if (strcmp(fi->name, ".PKGINFO") == 0) {
		apk_blob_t blob = apk_blob_from_istream(is, fi->size);
524 525 526
		apk_blob_for_each_segment(
			blob, "\n",
			apk_sign_ctx_parse_pkginfo_line, ctx);
527 528 529 530 531 532
		free(blob.ptr);
	}

	return 0;
}

533
int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
Timo Teräs's avatar
Timo Teräs committed
534 535 536
{
	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
537 538 539 540 541 542 543
	int r, end_of_control;

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

	/* Still in signature blocks? */
544 545 546
	if (!sctx->control_started) {
		if (part == APK_MPART_END)
			return -EKEYREJECTED;
Timo Teräs's avatar
Timo Teräs committed
547
		goto reset_digest;
548
	}
Timo Teräs's avatar
Timo Teräs committed
549 550 551 552 553 554

	/* 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? */
555 556
	if (sctx->has_data_checksum == 0 && end_of_control &&
	    part != APK_MPART_END)
Timo Teräs's avatar
Timo Teräs committed
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
		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
578

579 580 581 582
	r = check_signing_key_trust(sctx);
	if (r < 0)
		return r;

Timo Teräs's avatar
Timo Teräs committed
583 584
	switch (sctx->action) {
	case APK_SIGN_VERIFY:
585
	case APK_SIGN_VERIFY_AND_GENERATE:
Timo Teräs's avatar
Timo Teräs committed
586 587 588 589 590 591 592 593
		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)
594
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
595 596 597 598 599 600 601 602 603
		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)
604
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
605 606 607 608 609 610 611 612 613
		break;
	case APK_SIGN_GENERATE:
	case APK_SIGN_GENERATE_V1:
		/* 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;
614
		break;
Timo Teräs's avatar
Timo Teräs committed
615
	}
616 617 618 619
	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
620 621 622 623 624 625 626 627
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
628 629 630
	return 0;
}

631 632 633
struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
634 635
	struct apk_sign_ctx *sctx;
	int version;
636 637
};

638 639
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
640 641 642
{
	switch (field) {
	case 'P':
643
		pkg->name = apk_db_get_name(db, value);
Timo Teräs's avatar
Timo Teräs committed
644 645 646 647 648 649 650 651 652 653 654 655 656
		break;
	case 'V':
		pkg->version = apk_blob_cstr(value);
		break;
	case 'T':
		pkg->description = apk_blob_cstr(value);
		break;
	case 'U':
		pkg->url = apk_blob_cstr(value);
		break;
	case 'L':
		pkg->license = apk_blob_cstr(value);
		break;
657 658 659
	case 'A':
		pkg->arch = apk_blob_cstr(value);
		break;
Timo Teräs's avatar
Timo Teräs committed
660 661 662 663
	case 'D':
		apk_deps_parse(db, &pkg->depends, value);
		break;
	case 'C':
Timo Teräs's avatar
Timo Teräs committed
664
		apk_blob_pull_csum(&value, &pkg->csum);
Timo Teräs's avatar
Timo Teräs committed
665 666
		break;
	case 'S':
667
		pkg->size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
668 669
		break;
	case 'I':
670
		pkg->installed_size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
671
		break;
672 673 674
	case 'F': case 'M': case 'R': case 'Z':
		/* installed db entries which are handled in database.c */
		return 1;
675
	default:
676 677 678 679 680 681 682
		/* 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;
		return 1;
Timo Teräs's avatar
Timo Teräs committed
683
	}
684 685
	if (APK_BLOB_IS_NULL(value))
		return -1;
Timo Teräs's avatar
Timo Teräs committed
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
	return 0;
}

static int read_info_line(void *ctx, apk_blob_t line)
{
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "pkgname", 'P' },
		{ "pkgver",  'V' },
		{ "pkgdesc", 'T' },
		{ "url",     'U' },
		{ "size",    'I' },
		{ "license", 'L' },
701
		{ "arch",    'A' },
Timo Teräs's avatar
Timo Teräs committed
702 703 704 705 706 707 708 709 710
		{ "depend",  'D' },
	};
	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;

711
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
712 713 714
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
715
		if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
716
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
717
			return 0;
Timo Teräs's avatar
Timo Teräs committed
718 719
		}
	}
720
	apk_sign_ctx_parse_pkginfo_line(ri->sctx, line);
721

Timo Teräs's avatar
Timo Teräs committed
722 723 724
	return 0;
}

725
static int read_info_entry(void *ctx, const struct apk_file_info *ae,
726
			   struct apk_istream *is)
727
{
Timo Teräs's avatar
Timo Teräs committed
728 729 730 731 732
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "DESC",	'T' },
733
		{ "WWW",	'U' },
Timo Teräs's avatar
Timo Teräs committed
734 735 736
		{ "LICENSE",	'L' },
		{ "DEPEND", 	'D' },
	};
737 738 739 740
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
	struct apk_database *db = ri->db;
	struct apk_package *pkg = ri->pkg;
	apk_blob_t name, version;
741
	char *slash;
Timo Teräs's avatar
Timo Teräs committed
742
	int i;
743

Timo Teräs's avatar
Timo Teräs committed
744
	/* Meta info and scripts */
Timo Teräs's avatar
Timo Teräs committed
745 746
	if (apk_sign_ctx_process_file(ri->sctx, ae, is) == 0)
		return 0;
747

Timo Teräs's avatar
Timo Teräs committed
748
	if (ae->name[0] == '.') {
Timo Teräs's avatar
Timo Teräs committed
749 750
		/* APK 2.0 format */
		if (strcmp(ae->name, ".PKGINFO") == 0) {
751
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
Timo Teräs's avatar
Timo Teräs committed
752 753
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
754 755
			ri->version = 2;
		} else if (strcmp(ae->name, ".INSTALL") == 0) {
756 757
			apk_warning("Package '%s-%s' contains deprecated .INSTALL",
				    pkg->name->name, pkg->version);
Timo Teräs's avatar
Timo Teräs committed
758
		}
759 760 761 762
		return 0;
	}

	if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
Timo Teräs's avatar
Timo Teräs committed
763
		/* APK 1.0 format */
764
		ri->version = 1;
Timo Teräs's avatar
Timo Teräs committed
765 766
		if (!S_ISREG(ae->mode))
			return 0;
767

Timo Teräs's avatar
Timo Teräs committed
768 769 770
		slash = strchr(&ae->name[11], '/');
		if (slash == NULL)
			return 0;
771

Timo Teräs's avatar
Timo Teräs committed
772 773 774
		if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
				       &name, &version) < 0)
			return -1;
775

776 777
		if (pkg->name == NULL)
			pkg->name = apk_db_get_name(db, name);
Timo Teräs's avatar
Timo Teräs committed
778 779 780 781 782
		if (pkg->version == NULL)
			pkg->version = apk_blob_cstr(version);

		for (i = 0; i < ARRAY_SIZE(fields); i++) {
			if (strcmp(fields[i].str, slash+1) == 0) {
783
				apk_blob_t blob = apk_blob_from_istream(is, ae->size);
784 785
				apk_pkg_add_info(ri->db, ri->pkg, fields[i].field,
						 trim(blob));
Timo Teräs's avatar
Timo Teräs committed
786 787 788
				free(blob.ptr);
				break;
			}
789
		}
790
	} else if (ri->version < 2) {
791 792
		/* Version 1.x packages do not contain installed size
		 * in metadata, so we calculate it here */
793
		pkg->installed_size += apk_calc_installed_size(ae->size);
Timo Teräs's avatar
Timo Teräs committed
794
	}
795 796 797 798

	return 0;
}

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

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

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

	ctx.db = db;
823
	ctx.pkg->size = fi.size;
824

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

950 951 952
	if (WIFEXITED(status))
		return WEXITSTATUS(status);
	return -1;
953 954
}

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

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

962
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
963 964 965 966 967
	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
968
	struct read_info_ctx ctx;
969

970
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
971
	if (ctx.pkg == NULL)
972 973
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
974
	ctx.db = db;
975
	ctx.version = 0;
976

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

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
981 982
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
983
		ctx.pkg = NULL;
984 985
	}

Timo Teräs's avatar
Timo Teräs committed
986
	return ctx.pkg;
987 988
}

989
int apk_pkg_write_index_entry(struct apk_package *info,
990
			      struct apk_ostream *os, int write_arch)
991
{
992
	char buf[512];
993 994 995 996
	apk_blob_t bbuf = APK_BLOB_BUF(buf);
	int r;

	apk_blob_push_blob(&bbuf, APK_BLOB_STR("C:"));
Timo Teräs's avatar
Timo Teräs committed
997
	apk_blob_push_csum(&bbuf, &info->csum);
998 999 1000 1001
	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:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->version));
1002 1003 1004
	if (write_arch && info->arch != NULL) {
		apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nA:"));
		apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->arch));
1005
        }
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
	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:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->license));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));

Timo Teräs's avatar
Timo Teräs committed
1018 1019 1020
	if (APK_BLOB_IS_NULL(bbuf))
		return -1;

1021 1022
	bbuf = apk_blob_pushed(APK_BLOB_BUF(buf), bbuf);
	if (os->write(os, bbuf.ptr, bbuf.len) != bbuf.len)
1023
		return -1;
1024

Timo Teräs's avatar
Timo Teräs committed
1025
	if (info->depends->num > 0) {
1026 1027 1028 1029 1030 1031 1032
		if (os->write(os, "D:", 2) != 2)
			return -1;
		r = apk_deps_write(info->depends, os);
		if (r < 0)
			return r;
		if (os->write(os, "\n", 1) != 1)
			return -1;
1033 1034
	}

1035
	return 0;
1036
}
1037

1038 1039 1040 1041
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}