package.c 23.3 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
{
Timo Teräs's avatar
Timo Teräs committed
56 57 58 59 60 61 62
	struct apk_package *pkg;

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

	return pkg;
63
}
64

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

	/* 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);
	}
84 85 86 87 88 89 90 91 92 93 94 95 96 97

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

99 100
	list_del(&ipkg->installed_pkgs_list);

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

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

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

123 124 125
	if (APK_BLOB_IS_NULL(apkname))
		return -1;

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

137 138 139 140 141 142 143 144 145
	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
146
static apk_blob_t trim(apk_blob_t str)
147 148
{
	if (str.ptr == NULL || str.len < 1)
Timo Teräs's avatar
Timo Teräs committed
149
		return str;
150

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

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

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

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

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

197 198 199 200 201
struct parse_depend_ctx {
	struct apk_database *db;
	struct apk_dependency_array **depends;
};

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

209 210 211 212 213 214 215 216 217 218
	/* [!]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
219
			return -EINVAL;
220
		if (!apk_blob_spn(bop, "<>=", &bop, &bver))
Timo Teräs's avatar
Timo Teräs committed
221 222 223 224
			return -EINVAL;
		mask = 0;
		for (i = 0; i < bop.len; i++) {
			switch (bop.ptr[i]) {
225 226 227 228 229 230 231 232 233 234 235 236 237
			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
238
			return -EINVAL;
239 240

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

		blob = bname;
244 245
	}

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

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

void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db,
		      struct apk_package *pkg)
{
	*dep = (struct apk_dependency) {
262
		.name = pkg->name,
Timo Teräs's avatar
Timo Teräs committed
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
		.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;
283 284 285 286

	return 0;
}

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

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

296
	apk_blob_for_each_segment(blob, " ", parse_depend, &ctx);
297 298
}

299 300 301 302 303 304 305 306 307 308 309 310 311 312
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));
	}
}

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

	if (deps == NULL)
		return 0;

	for (i = 0; i < deps->num; i++) {
323 324 325 326 327 328 329 330 331
		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;
332

333
		n += blob.len;
334 335 336 337 338
	}

	return n;
}

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

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

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

358
	return APK_SCRIPT_INVALID;
359 360
}

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

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);
408
	EVP_MD_CTX_cleanup(&ctx->mdctx);
Timo Teräs's avatar
Timo Teräs committed
409 410 411 412 413 414 415 416 417
}

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;

418
	if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
Timo Teräs's avatar
Timo Teräs committed
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
		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 */
436 437
	if ((ctx->action != APK_SIGN_VERIFY &&
	     ctx->action != APK_SIGN_VERIFY_AND_GENERATE) ||
438
	    ctx->signature.pkey != NULL)
Timo Teräs's avatar
Timo Teräs committed
439 440
		return 0;

441 442 443
	if (ctx->keys_fd < 0)
		return 0;

Timo Teräs's avatar
Timo Teräs committed
444 445
	if (strncmp(&fi->name[6], "RSA.", 4) == 0 ||
	    strncmp(&fi->name[6], "DSA.", 4) == 0) {
446 447 448 449 450 451
		int fd = openat(ctx->keys_fd, &fi->name[10], O_RDONLY);
	        BIO *bio;

		if (fd < 0)
			return 0;

452
		bio = BIO_new_fp(fdopen(fd, "r"), BIO_CLOSE);
453
		ctx->signature.pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
Timo Teräs's avatar
Timo Teräs committed
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
		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;
}

470
int apk_sign_ctx_parse_pkginfo_line(void *ctx, apk_blob_t line)
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
{
	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);
503 504 505
		apk_blob_for_each_segment(
			blob, "\n",
			apk_sign_ctx_parse_pkginfo_line, ctx);
506 507 508 509 510 511
		free(blob.ptr);
	}

	return 0;
}

512
int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
Timo Teräs's avatar
Timo Teräs committed
513 514 515
{
	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
516 517 518 519 520 521 522
	int r, end_of_control;

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

	/* Still in signature blocks? */
523 524 525
	if (!sctx->control_started) {
		if (part == APK_MPART_END)
			return -EKEYREJECTED;
Timo Teräs's avatar
Timo Teräs committed
526
		goto reset_digest;
527
	}
Timo Teräs's avatar
Timo Teräs committed
528 529 530 531 532 533

	/* 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? */
534 535
	if (sctx->has_data_checksum == 0 && end_of_control &&
	    part != APK_MPART_END)
Timo Teräs's avatar
Timo Teräs committed
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
		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
557

Timo Teräs's avatar
Timo Teräs committed
558 559
	switch (sctx->action) {
	case APK_SIGN_VERIFY:
560
	case APK_SIGN_VERIFY_AND_GENERATE:
Timo Teräs's avatar
Timo Teräs committed
561 562 563 564
		if (sctx->signature.pkey == NULL) {
			if (apk_flags & APK_ALLOW_UNTRUSTED)
				break;
			return -ENOKEY;
Timo Teräs's avatar
Timo Teräs committed
565 566
		}

Timo Teräs's avatar
Timo Teräs committed
567 568 569 570 571 572 573 574
		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)
575
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
576 577 578 579 580 581 582 583 584
		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)
585
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
586 587 588 589 590 591 592 593 594
		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;
595
		break;
Timo Teräs's avatar
Timo Teräs committed
596
	}
597 598 599 600
	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
601 602 603 604 605 606 607 608
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
609 610 611
	return 0;
}

612 613 614
struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
615 616
	struct apk_sign_ctx *sctx;
	int version;
617 618
};

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

679
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
680 681 682
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
683
		if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
684
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
685
			return 0;
Timo Teräs's avatar
Timo Teräs committed
686 687
		}
	}
688
	apk_sign_ctx_parse_pkginfo_line(ri->sctx, line);
689

Timo Teräs's avatar
Timo Teräs committed
690 691 692
	return 0;
}

693
static int read_info_entry(void *ctx, const struct apk_file_info *ae,
694
			   struct apk_istream *is)
695
{
Timo Teräs's avatar
Timo Teräs committed
696 697 698 699 700
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "DESC",	'T' },
701
		{ "WWW",	'U' },
Timo Teräs's avatar
Timo Teräs committed
702 703 704
		{ "LICENSE",	'L' },
		{ "DEPEND", 	'D' },
	};
705 706 707 708
	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;
709
	char *slash;
Timo Teräs's avatar
Timo Teräs committed
710
	int i;
711

Timo Teräs's avatar
Timo Teräs committed
712
	/* Meta info and scripts */
Timo Teräs's avatar
Timo Teräs committed
713 714
	if (apk_sign_ctx_process_file(ri->sctx, ae, is) == 0)
		return 0;
715

Timo Teräs's avatar
Timo Teräs committed
716
	if (ae->name[0] == '.') {
Timo Teräs's avatar
Timo Teräs committed
717 718
		/* APK 2.0 format */
		if (strcmp(ae->name, ".PKGINFO") == 0) {
719
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
Timo Teräs's avatar
Timo Teräs committed
720 721
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
722 723
			ri->version = 2;
		} else if (strcmp(ae->name, ".INSTALL") == 0) {
724 725
			apk_warning("Package '%s-%s' contains deprecated .INSTALL",
				    pkg->name->name, pkg->version);
Timo Teräs's avatar
Timo Teräs committed
726
		}
727 728 729 730
		return 0;
	}

	if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
Timo Teräs's avatar
Timo Teräs committed
731
		/* APK 1.0 format */
732
		ri->version = 1;
Timo Teräs's avatar
Timo Teräs committed
733 734
		if (!S_ISREG(ae->mode))
			return 0;
735

Timo Teräs's avatar
Timo Teräs committed
736 737 738
		slash = strchr(&ae->name[11], '/');
		if (slash == NULL)
			return 0;
739

Timo Teräs's avatar
Timo Teräs committed
740 741 742
		if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
				       &name, &version) < 0)
			return -1;
743

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

	return 0;
}

767 768
int apk_pkg_read(struct apk_database *db, const char *file,
	         struct apk_sign_ctx *sctx, struct apk_package **pkg)
769 770
{
	struct read_info_ctx ctx;
771
	struct apk_file_info fi;
772
	struct apk_bstream *bs;
773
	struct apk_istream *tar;
774
	int r;
775

776
	r = apk_file_get_info(AT_FDCWD, file, APK_CHECKSUM_NONE, &fi);
777 778
	if (r != 0)
		return r;
779

780
	memset(&ctx, 0, sizeof(ctx));
Timo Teräs's avatar
Timo Teräs committed
781
	ctx.sctx = sctx;
782
	ctx.pkg = apk_pkg_new();
783
	r = -ENOMEM;
784
	if (ctx.pkg == NULL)
785
		goto err;
786
	bs = apk_bstream_from_file(AT_FDCWD, file);
Timo Teräs's avatar
Timo Teräs committed
787
	if (bs == NULL)
788 789 790
		goto err;

	ctx.db = db;
791
	ctx.pkg->size = fi.size;
792

Timo Teräs's avatar
Timo Teräs committed
793
	tar = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, sctx);
794
	r = apk_tar_parse(tar, read_info_entry, &ctx, FALSE);
795
	tar->close(tar);
Timo Teräs's avatar
Timo Teräs committed
796
	if (r < 0 && r != -ECANCELED)
797
		goto err;
798
	if (ctx.pkg->name == NULL) {
799
		r = -ENOMSG;
800
		goto err;
801
	}
Timo Teräs's avatar
Timo Teräs committed
802 803
	if (sctx->action != APK_SIGN_VERIFY)
		ctx.pkg->csum = sctx->identity;
804
	ctx.pkg->filename = strdup(file);
805

806 807 808
	ctx.pkg = apk_db_pkg_add(db, ctx.pkg);
	if (pkg != NULL)
		*pkg = ctx.pkg;
809
	return 0;
810 811
err:
	apk_pkg_free(ctx.pkg);
812
	return r;
813 814 815 816 817 818 819
}

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

820
	apk_pkg_uninstall(NULL, pkg);
Timo Teräs's avatar
Timo Teräs committed
821
	apk_dependency_array_free(&pkg->depends);
822 823 824 825 826 827 828 829 830 831 832
	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);
}

833 834 835
int apk_ipkg_add_script(struct apk_installed_package *ipkg,
			struct apk_istream *is,
			unsigned int type, unsigned int size)
836
{
837
	void *ptr;
838 839
	int r;

840 841 842 843 844
	if (type >= APK_SCRIPT_MAX)
		return -1;

	ptr = malloc(size);
	r = is->read(is, ptr, size);
845
	if (r < 0) {
846
		free(ptr);
847 848 849
		return r;
	}

850 851 852 853 854
	if (ipkg->script[type].ptr)
		free(ipkg->script[type].ptr);
	ipkg->script[type].ptr = ptr;
	ipkg->script[type].len = size;
	return 0;
855 856
}

857 858
int apk_ipkg_run_script(struct apk_installed_package *ipkg, int root_fd,
			unsigned int type, char **argv)
859
{
860
	static char * const environment[] = {
861 862 863
		"PATH=/usr/sbin:/usr/bin:/sbin:/bin",
		NULL
	};
864 865
	struct apk_package *pkg = ipkg->pkg;
	char fn[PATH_MAX];
866 867 868
	int fd, status;
	pid_t pid;

869 870
	if (type >= APK_SCRIPT_MAX)
		return -1;
871

872 873 874 875
	if (ipkg->script[type].ptr == NULL)
		return 0;

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

877 878 879 880 881
	/* 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]);

882 883 884 885
	apk_message("Executing %s", &fn[15]);
	if (apk_flags & APK_SIMULATE)
		return 0;

886 887 888
	fd = openat(root_fd, fn, O_CREAT|O_RDWR|O_TRUNC, 0755);
	if (fd < 0) {
		mkdirat(root_fd, "var/cache/misc", 0755);
889
		fd = openat(root_fd, fn, O_CREAT|O_RDWR|O_TRUNC, 0755);
890 891 892 893 894
		if (fd < 0)
			return -errno;
	}
	write(fd, ipkg->script[type].ptr, ipkg->script[type].len);
	close(fd);
895

896 897
	pid = fork();
	if (pid == -1)
898
		return -1;
899 900 901 902 903 904 905 906
	if (pid == 0) {
		fchdir(root_fd);
		if (chroot(".") < 0) {
			apk_error("chroot: %s", strerror(errno));
		} else {
			execve(fn, argv, environment);
		}
		exit(1);
907
	}
908 909 910 911 912
	waitpid(pid, &status, 0);
	unlinkat(root_fd, fn, 0);
	if (WIFEXITED(status))
		return WEXITSTATUS(status);
	return -1;
913 914
}

Timo Teräs's avatar
Timo Teräs committed
915
static int parse_index_line(void *ctx, apk_blob_t line)
916
{
Timo Teräs's avatar
Timo Teräs committed
917
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
918

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

922
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
923 924 925 926 927
	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
928
	struct read_info_ctx ctx;
929

930
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
931
	if (ctx.pkg == NULL)
932 933
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
934
	ctx.db = db;
935
	ctx.version = 0;
936

Timo Teräs's avatar
Timo Teräs committed
937 938 939 940
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
941 942
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
943
		ctx.pkg = NULL;
944 945
	}

Timo Teräs's avatar
Timo Teräs committed
946
	return ctx.pkg;
947 948
}

949 950
int apk_pkg_write_index_entry(struct apk_package *info,
			      struct apk_ostream *os)
951
{
952
	char buf[512];
953 954 955 956
	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
957
	apk_blob_push_csum(&bbuf, &info->csum);
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973
	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
974 975 976
	if (APK_BLOB_IS_NULL(bbuf))
		return -1;

977 978
	bbuf = apk_blob_pushed(APK_BLOB_BUF(buf), bbuf);
	if (os->write(os, bbuf.ptr, bbuf.len) != bbuf.len)
979
		return -1;
980

Timo Teräs's avatar
Timo Teräs committed
981
	if (info->depends->num > 0) {
982 983 984 985 986 987 988
		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;
989 990
	}

991
	return 0;
992
}
993

994 995 996 997
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}