package.c 22.8 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
95
96
97
98
99
100
101
102
103
104
	list_del(&ipkg->installed_pkgs_list);

	if (ipkg->triggers) {
		list_del(&ipkg->trigger_pkgs_list);
		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;
105
106
}

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

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

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

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

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

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

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

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

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

	if (deps == NULL)
		return;

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

		deps->item[i] = deps->item[deps->num-1];
		*pdeps = apk_dependency_array_resize(deps, deps->num-1);
		break;
	}
}

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

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

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

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

		blob = bname;
234
235
	}

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

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

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

	return 0;
}

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

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

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

289
290
int apk_deps_write(struct apk_dependency_array *deps, struct apk_ostream *os)
{
291
	int i, r, n = 0;
292
293
294
295
296
297
298
299
300
301
302

	if (deps == NULL)
		return 0;

	for (i = 0; i < deps->num; i++) {
		if (i) {
			if (os->write(os, " ", 1) != 1)
				return -1;
			n += 1;
		}

303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
		if (deps->item[i].result_mask == APK_DEPMASK_CONFLICT) {
			if (os->write(os, "!", 1) != 1)
				return -1;
			n += 1;
		}

		r = apk_ostream_write_string(os, deps->item[i].name->name);
		if (r < 0)
			return r;
		n += r;

		if (deps->item[i].result_mask != APK_DEPMASK_CONFLICT &&
		    deps->item[i].result_mask != APK_DEPMASK_REQUIRE) {
			r = apk_ostream_write_string(os, apk_version_op_string(deps->item[i].result_mask));
			if (r < 0)
				return r;
			n += r;

			r = apk_ostream_write_string(os, deps->item[i].version);
			if (r < 0)
				return r;
			n += r;
		}
326
327
328
329
330
	}

	return n;
}

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

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

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

350
	return APK_SCRIPT_INVALID;
351
352
}

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

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

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;

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

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

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

		if (fd < 0)
			return 0;

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

462
int apk_sign_ctx_parse_pkginfo_line(void *ctx, apk_blob_t line)
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
494
{
	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);
495
496
497
		apk_blob_for_each_segment(
			blob, "\n",
			apk_sign_ctx_parse_pkginfo_line, ctx);
498
499
500
501
502
503
		free(blob.ptr);
	}

	return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return 0;
}

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

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

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

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

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

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

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

812
	apk_pkg_uninstall(NULL, pkg);
813
814
	if (pkg->depends)
		free(pkg->depends);
815
816
817
818
819
820
821
822
823
824
825
	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);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

970
	if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
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);
}