package.c 23 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 22
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

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

25 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"

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
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));
}

54
struct apk_package *apk_pkg_new(void)
55
{
56 57
	return calloc(1, sizeof(struct apk_package));
}
58

59 60 61 62 63 64 65 66 67 68
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
69 70 71 72 73 74 75

	/* 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);
	}
76 77 78 79 80 81 82 83 84 85 86 87 88 89

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

91 92 93 94
	list_del(&ipkg->installed_pkgs_list);

	if (ipkg->triggers) {
		list_del(&ipkg->trigger_pkgs_list);
95
		list_init(&ipkg->trigger_pkgs_list);
96 97 98 99 100 101 102 103 104 105
		for (i = 0; i < ipkg->triggers->num; i++)
			free(ipkg->triggers->item[i]);
		free(ipkg->triggers);
	}

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

108 109 110 111 112 113
int apk_pkg_parse_name(apk_blob_t apkname,
		       apk_blob_t *name,
		       apk_blob_t *version)
{
	int i, dash = 0;

114 115 116
	if (APK_BLOB_IS_NULL(apkname))
		return -1;

117 118 119 120 121 122 123 124
	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;
	}
125 126 127
	if (i < 0)
		return -1;

128 129 130 131 132 133 134 135 136
	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
137
static apk_blob_t trim(apk_blob_t str)
138 139
{
	if (str.ptr == NULL || str.len < 1)
Timo Teräs's avatar
Timo Teräs committed
140
		return str;
141

142 143
	if (str.ptr[str.len-1] == '\n') {
		str.ptr[str.len-1] = 0;
Timo Teräs's avatar
Timo Teräs committed
144
		return APK_BLOB_PTR_LEN(str.ptr, str.len-1);
145
	}
146

Timo Teräs's avatar
Timo Teräs committed
147
	return str;
148 149 150 151 152 153 154 155 156 157
}

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
158 159
			if (deps->item[i].name == dep->name) {
				deps->item[i] = *dep;
160
				return 0;
Timo Teräs's avatar
Timo Teräs committed
161
			}
162 163 164 165 166 167
		}
	}

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

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
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];
		*pdeps = apk_dependency_array_resize(deps, deps->num-1);
		break;
	}
}

188 189 190 191 192
struct parse_depend_ctx {
	struct apk_database *db;
	struct apk_dependency_array **depends;
};

Timo Teräs's avatar
Timo Teräs committed
193 194
int apk_dep_from_blob(struct apk_dependency *dep, struct apk_database *db,
		      apk_blob_t blob)
195 196
{
	struct apk_name *name;
197 198
	apk_blob_t bname, bop, bver = APK_BLOB_NULL;
	int mask = APK_VERSION_LESS | APK_VERSION_EQUAL | APK_VERSION_GREATER;
199

200 201 202 203 204 205 206 207 208 209
	/* [!]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
210
			return -EINVAL;
211
		if (!apk_blob_spn(bop, "<>=", &bop, &bver))
Timo Teräs's avatar
Timo Teräs committed
212 213 214 215
			return -EINVAL;
		mask = 0;
		for (i = 0; i < bop.len; i++) {
			switch (bop.ptr[i]) {
216 217 218 219 220 221 222 223 224 225 226 227 228
			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
229
			return -EINVAL;
230 231

		if (!apk_version_validate(bver))
Timo Teräs's avatar
Timo Teräs committed
232 233 234
			return -EINVAL;

		blob = bname;
235 236
	}

Timo Teräs's avatar
Timo Teräs committed
237
	name = apk_db_get_name(db, blob);
238
	if (name == NULL)
Timo Teräs's avatar
Timo Teräs committed
239
		return -ENOENT;
240 241 242

	*dep = (struct apk_dependency){
		.name = name,
243 244
		.version = APK_BLOB_IS_NULL(bver) ? NULL : apk_blob_cstr(bver),
		.result_mask = mask,
245
	};
Timo Teräs's avatar
Timo Teräs committed
246 247 248 249 250 251 252
	return 0;
}

void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db,
		      struct apk_package *pkg)
{
	*dep = (struct apk_dependency) {
253
		.name = pkg->name,
Timo Teräs's avatar
Timo Teräs committed
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
		.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;
274 275 276 277

	return 0;
}

278 279 280 281
void apk_deps_parse(struct apk_database *db,
		    struct apk_dependency_array **depends,
		    apk_blob_t blob)
{
282
	struct parse_depend_ctx ctx = { db, depends };
283

284
	if (blob.len > 0 && blob.ptr[blob.len-1] == '\n')
285
		blob.len--;
286

287
	apk_blob_for_each_segment(blob, " ", parse_depend, &ctx);
288 289
}

290 291 292 293 294 295 296 297 298 299 300 301 302 303
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));
	}
}

304 305
int apk_deps_write(struct apk_dependency_array *deps, struct apk_ostream *os)
{
306 307 308
	apk_blob_t blob;
	char tmp[256];
	int i, n = 0;
309 310 311 312 313

	if (deps == NULL)
		return 0;

	for (i = 0; i < deps->num; i++) {
314 315 316 317 318 319 320 321 322
		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;
323

324
		n += blob.len;
325 326 327 328 329
	}

	return n;
}

Timo Teräs's avatar
Timo Teräs committed
330
const char *apk_script_types[] = {
331 332 333 334 335 336
	[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",
337
	[APK_SCRIPT_TRIGGER]		= "trigger",
338 339 340 341 342 343
};

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

Timo Teräs's avatar
Timo Teräs committed
344 345 346
	for (i = 0; i < ARRAY_SIZE(apk_script_types); i++)
		if (apk_script_types[i] &&
		    strcmp(apk_script_types[i], name) == 0)
347 348
			return i;

349
	return APK_SCRIPT_INVALID;
350 351
}

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

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);
399
	EVP_MD_CTX_cleanup(&ctx->mdctx);
Timo Teräs's avatar
Timo Teräs committed
400 401 402 403 404 405 406 407 408
}

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;

409
	if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
Timo Teräs's avatar
Timo Teräs committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
		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 */
427 428
	if ((ctx->action != APK_SIGN_VERIFY &&
	     ctx->action != APK_SIGN_VERIFY_AND_GENERATE) ||
429
	    ctx->signature.pkey != NULL)
Timo Teräs's avatar
Timo Teräs committed
430 431
		return 0;

432 433 434
	if (ctx->keys_fd < 0)
		return 0;

Timo Teräs's avatar
Timo Teräs committed
435 436
	if (strncmp(&fi->name[6], "RSA.", 4) == 0 ||
	    strncmp(&fi->name[6], "DSA.", 4) == 0) {
437 438 439 440 441 442
		int fd = openat(ctx->keys_fd, &fi->name[10], O_RDONLY);
	        BIO *bio;

		if (fd < 0)
			return 0;

443
		bio = BIO_new_fp(fdopen(fd, "r"), BIO_CLOSE);
444
		ctx->signature.pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
Timo Teräs's avatar
Timo Teräs committed
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
		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;
}

461
int apk_sign_ctx_parse_pkginfo_line(void *ctx, apk_blob_t line)
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
{
	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);
494 495 496
		apk_blob_for_each_segment(
			blob, "\n",
			apk_sign_ctx_parse_pkginfo_line, ctx);
497 498 499 500 501 502
		free(blob.ptr);
	}

	return 0;
}

503
int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
Timo Teräs's avatar
Timo Teräs committed
504 505 506
{
	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
507 508 509 510 511 512 513
	int r, end_of_control;

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

	/* Still in signature blocks? */
514 515 516
	if (!sctx->control_started) {
		if (part == APK_MPART_END)
			return -EKEYREJECTED;
Timo Teräs's avatar
Timo Teräs committed
517
		goto reset_digest;
518
	}
Timo Teräs's avatar
Timo Teräs committed
519 520 521 522 523 524

	/* 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? */
525 526
	if (sctx->has_data_checksum == 0 && end_of_control &&
	    part != APK_MPART_END)
Timo Teräs's avatar
Timo Teräs committed
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
		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
548

Timo Teräs's avatar
Timo Teräs committed
549 550
	switch (sctx->action) {
	case APK_SIGN_VERIFY:
551
	case APK_SIGN_VERIFY_AND_GENERATE:
Timo Teräs's avatar
Timo Teräs committed
552 553 554 555
		if (sctx->signature.pkey == NULL) {
			if (apk_flags & APK_ALLOW_UNTRUSTED)
				break;
			return -ENOKEY;
Timo Teräs's avatar
Timo Teräs committed
556 557
		}

Timo Teräs's avatar
Timo Teräs committed
558 559 560 561 562 563 564 565
		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)
566
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
567 568 569 570 571 572 573 574 575
		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)
576
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
577 578 579 580 581 582 583 584 585
		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;
586
		break;
Timo Teräs's avatar
Timo Teräs committed
587
	}
588 589 590 591
	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
592 593 594 595 596 597 598 599
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
600 601 602
	return 0;
}

603 604 605
struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
606 607
	struct apk_sign_ctx *sctx;
	int version;
608 609
};

610 611
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
612 613 614
{
	switch (field) {
	case 'P':
615
		pkg->name = apk_db_get_name(db, value);
Timo Teräs's avatar
Timo Teräs committed
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
		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;
	case 'D':
		apk_deps_parse(db, &pkg->depends, value);
		break;
	case 'C':
Timo Teräs's avatar
Timo Teräs committed
633
		apk_blob_pull_csum(&value, &pkg->csum);
Timo Teräs's avatar
Timo Teräs committed
634 635
		break;
	case 'S':
636
		pkg->size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
637 638
		break;
	case 'I':
639
		pkg->installed_size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
640
		break;
641 642
	default:
		return -1;
Timo Teräs's avatar
Timo Teräs committed
643
	}
644 645
	if (APK_BLOB_IS_NULL(value))
		return -1;
Timo Teräs's avatar
Timo Teräs committed
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
	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' },
		{ "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;

670
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
671 672 673
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
674
		if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
675
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
676
			return 0;
Timo Teräs's avatar
Timo Teräs committed
677 678
		}
	}
679
	apk_sign_ctx_parse_pkginfo_line(ri->sctx, line);
680

Timo Teräs's avatar
Timo Teräs committed
681 682 683
	return 0;
}

684
static int read_info_entry(void *ctx, const struct apk_file_info *ae,
685
			   struct apk_istream *is)
686
{
Timo Teräs's avatar
Timo Teräs committed
687 688 689 690 691
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "DESC",	'T' },
692
		{ "WWW",	'U' },
Timo Teräs's avatar
Timo Teräs committed
693 694 695
		{ "LICENSE",	'L' },
		{ "DEPEND", 	'D' },
	};
696 697 698 699
	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;
700
	char *slash;
Timo Teräs's avatar
Timo Teräs committed
701
	int i;
702

Timo Teräs's avatar
Timo Teräs committed
703
	/* Meta info and scripts */
Timo Teräs's avatar
Timo Teräs committed
704 705
	if (apk_sign_ctx_process_file(ri->sctx, ae, is) == 0)
		return 0;
706

Timo Teräs's avatar
Timo Teräs committed
707
	if (ae->name[0] == '.') {
Timo Teräs's avatar
Timo Teräs committed
708 709
		/* APK 2.0 format */
		if (strcmp(ae->name, ".PKGINFO") == 0) {
710
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
Timo Teräs's avatar
Timo Teräs committed
711 712
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
713 714
			ri->version = 2;
		} else if (strcmp(ae->name, ".INSTALL") == 0) {
715 716
			apk_warning("Package '%s-%s' contains deprecated .INSTALL",
				    pkg->name->name, pkg->version);
Timo Teräs's avatar
Timo Teräs committed
717
		}
718 719 720 721
		return 0;
	}

	if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
Timo Teräs's avatar
Timo Teräs committed
722
		/* APK 1.0 format */
723
		ri->version = 1;
Timo Teräs's avatar
Timo Teräs committed
724 725
		if (!S_ISREG(ae->mode))
			return 0;
726

Timo Teräs's avatar
Timo Teräs committed
727 728 729
		slash = strchr(&ae->name[11], '/');
		if (slash == NULL)
			return 0;
730

Timo Teräs's avatar
Timo Teräs committed
731 732 733
		if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
				       &name, &version) < 0)
			return -1;
734

735 736
		if (pkg->name == NULL)
			pkg->name = apk_db_get_name(db, name);
Timo Teräs's avatar
Timo Teräs committed
737 738 739 740 741
		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) {
742
				apk_blob_t blob = apk_blob_from_istream(is, ae->size);
743 744
				apk_pkg_add_info(ri->db, ri->pkg, fields[i].field,
						 trim(blob));
Timo Teräs's avatar
Timo Teräs committed
745 746 747
				free(blob.ptr);
				break;
			}
748
		}
749
	} else if (ri->version < 2) {
750 751
		/* Version 1.x packages do not contain installed size
		 * in metadata, so we calculate it here */
752
		pkg->installed_size += apk_calc_installed_size(ae->size);
Timo Teräs's avatar
Timo Teräs committed
753
	}
754 755 756 757

	return 0;
}

758 759
int apk_pkg_read(struct apk_database *db, const char *file,
	         struct apk_sign_ctx *sctx, struct apk_package **pkg)
760 761
{
	struct read_info_ctx ctx;
762
	struct apk_file_info fi;
763
	struct apk_bstream *bs;
764
	struct apk_istream *tar;
765
	int r;
766

767
	r = apk_file_get_info(AT_FDCWD, file, APK_CHECKSUM_NONE, &fi);
768 769
	if (r != 0)
		return r;
770

771
	memset(&ctx, 0, sizeof(ctx));
Timo Teräs's avatar
Timo Teräs committed
772
	ctx.sctx = sctx;
773
	ctx.pkg = apk_pkg_new();
774
	r = -ENOMEM;
775
	if (ctx.pkg == NULL)
776
		goto err;
777
	bs = apk_bstream_from_file(AT_FDCWD, file);
Timo Teräs's avatar
Timo Teräs committed
778
	if (bs == NULL)
779 780 781
		goto err;

	ctx.db = db;
782
	ctx.pkg->size = fi.size;
783

Timo Teräs's avatar
Timo Teräs committed
784
	tar = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, sctx);
785
	r = apk_tar_parse(tar, read_info_entry, &ctx, FALSE);
786
	tar->close(tar);
Timo Teräs's avatar
Timo Teräs committed
787
	if (r < 0 && r != -ECANCELED)
788
		goto err;
789
	if (ctx.pkg->name == NULL) {
790
		r = -ENOMSG;
791
		goto err;
792
	}
Timo Teräs's avatar
Timo Teräs committed
793 794
	if (sctx->action != APK_SIGN_VERIFY)
		ctx.pkg->csum = sctx->identity;
795
	ctx.pkg->filename = strdup(file);
796

797 798 799
	ctx.pkg = apk_db_pkg_add(db, ctx.pkg);
	if (pkg != NULL)
		*pkg = ctx.pkg;
800
	return 0;
801 802
err:
	apk_pkg_free(ctx.pkg);
803
	return r;
804 805 806 807 808 809 810
}

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

811
	apk_pkg_uninstall(NULL, pkg);
812 813
	if (pkg->depends)
		free(pkg->depends);
814 815 816 817 818 819 820 821 822 823 824
	if (pkg->version)
		free(pkg->version);
	if (pkg->url)
		free(pkg->url);
	if (pkg->description)
		free(pkg->description);
	if (pkg->license)
		free(pkg->license);
	free(pkg);
}

825 826 827
int apk_ipkg_add_script(struct apk_installed_package *ipkg,
			struct apk_istream *is,
			unsigned int type, unsigned int size)
828
{
829
	void *ptr;
830 831
	int r;

832 833 834 835 836
	if (type >= APK_SCRIPT_MAX)
		return -1;

	ptr = malloc(size);
	r = is->read(is, ptr, size);
837
	if (r < 0) {
838
		free(ptr);
839 840 841
		return r;
	}

842 843 844 845 846
	if (ipkg->script[type].ptr)
		free(ipkg->script[type].ptr);
	ipkg->script[type].ptr = ptr;
	ipkg->script[type].len = size;
	return 0;
847 848
}

849 850
int apk_ipkg_run_script(struct apk_installed_package *ipkg, int root_fd,
			unsigned int type, char **argv)
851
{
852
	static char * const environment[] = {
853 854 855
		"PATH=/usr/sbin:/usr/bin:/sbin:/bin",
		NULL
	};
856 857
	struct apk_package *pkg = ipkg->pkg;
	char fn[PATH_MAX];
858 859 860
	int fd, status;
	pid_t pid;

861 862
	if (type >= APK_SCRIPT_MAX)
		return -1;
863

864 865 866 867
	if (ipkg->script[type].ptr == NULL)
		return 0;

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

869 870 871 872 873
	/* 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]);

874 875 876 877
	apk_message("Executing %s", &fn[15]);
	if (apk_flags & APK_SIMULATE)
		return 0;

878 879 880
	fd = openat(root_fd, fn, O_CREAT|O_RDWR|O_TRUNC, 0755);
	if (fd < 0) {
		mkdirat(root_fd, "var/cache/misc", 0755);
881
		fd = openat(root_fd, fn, O_CREAT|O_RDWR|O_TRUNC, 0755);
882 883 884 885 886
		if (fd < 0)
			return -errno;
	}
	write(fd, ipkg->script[type].ptr, ipkg->script[type].len);
	close(fd);
887

888 889
	pid = fork();
	if (pid == -1)
890
		return -1;
891 892 893 894 895 896 897 898
	if (pid == 0) {
		fchdir(root_fd);
		if (chroot(".") < 0) {
			apk_error("chroot: %s", strerror(errno));
		} else {
			execve(fn, argv, environment);
		}
		exit(1);
899
	}
900 901 902 903 904
	waitpid(pid, &status, 0);
	unlinkat(root_fd, fn, 0);
	if (WIFEXITED(status))
		return WEXITSTATUS(status);
	return -1;
905 906
}

Timo Teräs's avatar
Timo Teräs committed
907
static int parse_index_line(void *ctx, apk_blob_t line)
908
{
Timo Teräs's avatar
Timo Teräs committed
909
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
910

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

914
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
915 916 917 918 919
	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
920
	struct read_info_ctx ctx;
921

922
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
923
	if (ctx.pkg == NULL)
924 925
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
926
	ctx.db = db;
927
	ctx.version = 0;
928

Timo Teräs's avatar
Timo Teräs committed
929 930 931 932
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
933 934
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
935
		ctx.pkg = NULL;
936 937
	}

Timo Teräs's avatar
Timo Teräs committed
938
	return ctx.pkg;
939 940
}

941 942
int apk_pkg_write_index_entry(struct apk_package *info,
			      struct apk_ostream *os)
943
{
944
	char buf[512];
945 946 947 948
	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
949
	apk_blob_push_csum(&bbuf, &info->csum);
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
	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));
	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
966 967 968
	if (APK_BLOB_IS_NULL(bbuf))
		return -1;

969 970
	bbuf = apk_blob_pushed(APK_BLOB_BUF(buf), bbuf);
	if (os->write(os, bbuf.ptr, bbuf.len) != bbuf.len)
971
		return -1;
972 973

	if (info->depends != NULL) {
974 975 976 977 978 979 980
		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;
981 982
	}

983
	return 0;
984
}
985

986 987 988 989
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}