package.c 23.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 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
#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
30
#include "apk_print.h"
31

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

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

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

	return pkg;
64
}
65

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

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

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

100 101
	list_del(&ipkg->installed_pkgs_list);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		blob = bname;
245 246
	}

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

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

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

	return 0;
}

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

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

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

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

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

	if (deps == NULL)
		return 0;

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

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

	return n;
}

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

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

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

359
	return APK_SCRIPT_INVALID;
360 361
}

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

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

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;

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

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

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

		if (fd < 0)
			return 0;

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

471
int apk_sign_ctx_parse_pkginfo_line(void *ctx, apk_blob_t line)
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 503
{
	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);
504 505 506
		apk_blob_for_each_segment(
			blob, "\n",
			apk_sign_ctx_parse_pkginfo_line, ctx);
507 508 509 510 511 512
		free(blob.ptr);
	}

	return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

915 916 917
	if (WIFEXITED(status))
		return WEXITSTATUS(status);
	return -1;
918 919
}

Timo Teräs's avatar
Timo Teräs committed
920
static int parse_index_line(void *ctx, apk_blob_t line)
921
{
Timo Teräs's avatar
Timo Teräs committed
922
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
923

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

927
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
928 929 930 931 932
	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
933
	struct read_info_ctx ctx;
934

935
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
936
	if (ctx.pkg == NULL)
937 938
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
939
	ctx.db = db;
940
	ctx.version = 0;
941

Timo Teräs's avatar
Timo Teräs committed
942 943 944 945
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
946 947
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
948
		ctx.pkg = NULL;
949 950
	}

Timo Teräs's avatar
Timo Teräs committed
951
	return ctx.pkg;
952 953
}

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

982 983
	bbuf = apk_blob_pushed(APK_BLOB_BUF(buf), bbuf);
	if (os->write(os, bbuf.ptr, bbuf.len) != bbuf.len)
984
		return -1;
985

Timo Teräs's avatar
Timo Teräs committed
986
	if (info->depends->num > 0) {
987 988 989 990 991 992 993
		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;
994 995
	}

996
	return 0;
997
}
998

999 1000 1001 1002
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}