package.c 22.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
56
57
58
59
60
61
62
63
64
{
	struct apk_package *pkg;

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

	return pkg;
}

65
66
67
68
69
70
int apk_pkg_parse_name(apk_blob_t apkname,
		       apk_blob_t *name,
		       apk_blob_t *version)
{
	int i, dash = 0;

71
72
73
	if (APK_BLOB_IS_NULL(apkname))
		return -1;

74
75
76
77
78
79
80
81
	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;
	}
82
83
84
	if (i < 0)
		return -1;

85
86
87
88
89
90
91
92
93
	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
94
static apk_blob_t trim(apk_blob_t str)
95
96
{
	if (str.ptr == NULL || str.len < 1)
Timo Teräs's avatar
Timo Teräs committed
97
		return str;
98

99
100
	if (str.ptr[str.len-1] == '\n') {
		str.ptr[str.len-1] = 0;
Timo Teräs's avatar
Timo Teräs committed
101
		return APK_BLOB_PTR_LEN(str.ptr, str.len-1);
102
	}
103

Timo Teräs's avatar
Timo Teräs committed
104
	return str;
105
106
107
108
109
110
111
112
113
114
}

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
115
116
			if (deps->item[i].name == dep->name) {
				deps->item[i] = *dep;
117
				return 0;
Timo Teräs's avatar
Timo Teräs committed
118
			}
119
120
121
122
123
124
		}
	}

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

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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;
	}
}

145
146
147
148
149
struct parse_depend_ctx {
	struct apk_database *db;
	struct apk_dependency_array **depends;
};

Timo Teräs's avatar
Timo Teräs committed
150
151
int apk_dep_from_blob(struct apk_dependency *dep, struct apk_database *db,
		      apk_blob_t blob)
152
153
{
	struct apk_name *name;
154
155
	apk_blob_t bname, bop, bver = APK_BLOB_NULL;
	int mask = APK_VERSION_LESS | APK_VERSION_EQUAL | APK_VERSION_GREATER;
156

157
158
159
160
161
162
163
164
165
166
	/* [!]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
167
			return -EINVAL;
168
		if (!apk_blob_spn(bop, "<>=", &bop, &bver))
Timo Teräs's avatar
Timo Teräs committed
169
170
171
172
			return -EINVAL;
		mask = 0;
		for (i = 0; i < bop.len; i++) {
			switch (bop.ptr[i]) {
173
174
175
176
177
178
179
180
181
182
183
184
185
			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
186
			return -EINVAL;
187
188

		if (!apk_version_validate(bver))
Timo Teräs's avatar
Timo Teräs committed
189
190
191
			return -EINVAL;

		blob = bname;
192
193
	}

Timo Teräs's avatar
Timo Teräs committed
194
	name = apk_db_get_name(db, blob);
195
	if (name == NULL)
Timo Teräs's avatar
Timo Teräs committed
196
		return -ENOENT;
197
198
199

	*dep = (struct apk_dependency){
		.name = name,
200
201
		.version = APK_BLOB_IS_NULL(bver) ? NULL : apk_blob_cstr(bver),
		.result_mask = mask,
202
	};
Timo Teräs's avatar
Timo Teräs committed
203
204
205
206
207
208
209
	return 0;
}

void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db,
		      struct apk_package *pkg)
{
	*dep = (struct apk_dependency) {
210
		.name = pkg->name,
Timo Teräs's avatar
Timo Teräs committed
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
		.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;
231
232
233
234

	return 0;
}

235
236
237
238
void apk_deps_parse(struct apk_database *db,
		    struct apk_dependency_array **depends,
		    apk_blob_t blob)
{
239
	struct parse_depend_ctx ctx = { db, depends };
240

241
242
	if (blob.len > 1 && blob.ptr[blob.len-1] == '\n')
		blob.len--;
243

244
	apk_blob_for_each_segment(blob, " ", parse_depend, &ctx);
245
246
}

247
248
int apk_deps_write(struct apk_dependency_array *deps, struct apk_ostream *os)
{
249
	int i, r, n = 0;
250
251
252
253
254
255
256
257
258
259
260

	if (deps == NULL)
		return 0;

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

261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
		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;
		}
284
285
286
287
288
	}

	return n;
}

Timo Teräs's avatar
Timo Teräs committed
289
const char *apk_script_types[] = {
290
291
292
293
294
295
296
297
298
299
300
301
	[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",
};

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

Timo Teräs's avatar
Timo Teräs committed
302
303
304
	for (i = 0; i < ARRAY_SIZE(apk_script_types); i++)
		if (apk_script_types[i] &&
		    strcmp(apk_script_types[i], name) == 0)
305
306
			return i;

307
	return APK_SCRIPT_INVALID;
308
309
}

310
void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action,
311
		       struct apk_checksum *identity, int keys_fd)
Timo Teräs's avatar
Timo Teräs committed
312
313
{
	memset(ctx, 0, sizeof(struct apk_sign_ctx));
314
	ctx->keys_fd = keys_fd;
Timo Teräs's avatar
Timo Teräs committed
315
316
	ctx->action = action;
	switch (action) {
317
318
319
320
321
	case 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
322
323
324
	case APK_SIGN_VERIFY:
		ctx->md = EVP_md_null();
		break;
325
326
327
328
329
330
331
332
333
334
	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
335
336
	case APK_SIGN_GENERATE_V1:
		ctx->md = EVP_md5();
Timo Teräs's avatar
Timo Teräs committed
337
338
		ctx->control_started = 1;
		ctx->data_started = 1;
Timo Teräs's avatar
Timo Teräs committed
339
340
		break;
	case APK_SIGN_GENERATE:
341
	case APK_SIGN_VERIFY_AND_GENERATE:
Timo Teräs's avatar
Timo Teräs committed
342
343
344
345
346
	default:
		action = APK_SIGN_GENERATE;
		ctx->md = EVP_sha1();
		break;
	}
347
348
	EVP_MD_CTX_init(&ctx->mdctx);
	EVP_DigestInit_ex(&ctx->mdctx, ctx->md, NULL);
Timo Teräs's avatar
Timo Teräs committed
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
}


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);
}

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;

367
	if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
Timo Teräs's avatar
Timo Teräs committed
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
		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 */
385
386
	if (ctx->action != APK_SIGN_VERIFY ||
	    ctx->signature.pkey != NULL)
Timo Teräs's avatar
Timo Teräs committed
387
388
		return 0;

389
390
391
	if (ctx->keys_fd < 0)
		return 0;

Timo Teräs's avatar
Timo Teräs committed
392
393
	if (strncmp(&fi->name[6], "RSA.", 4) == 0 ||
	    strncmp(&fi->name[6], "DSA.", 4) == 0) {
394
395
396
397
398
399
400
401
		int fd = openat(ctx->keys_fd, &fi->name[10], O_RDONLY);
	        BIO *bio;

		if (fd < 0)
			return 0;

		bio  = BIO_new_fp(fdopen(fd, "r"), 0);
		ctx->signature.pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
Timo Teräs's avatar
Timo Teräs committed
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
		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;
}

418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
static int read_datahash(void *ctx, apk_blob_t line)
{
	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);
		apk_blob_for_each_segment(blob, "\n", read_datahash, ctx);
		free(blob.ptr);
	}

	return 0;
}

458
int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
Timo Teräs's avatar
Timo Teräs committed
459
460
461
{
	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
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
	int r, end_of_control;

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

	/* Still in signature blocks? */
	if (!sctx->control_started)
		goto reset_digest;

	/* 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? */
	if (sctx->has_data_checksum == 0 && end_of_control)
		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
499

Timo Teräs's avatar
Timo Teräs committed
500
501
	switch (sctx->action) {
	case APK_SIGN_VERIFY:
502
	case APK_SIGN_VERIFY_AND_GENERATE:
Timo Teräs's avatar
Timo Teräs committed
503
504
505
506
		if (sctx->signature.pkey == NULL) {
			if (apk_flags & APK_ALLOW_UNTRUSTED)
				break;
			return -ENOKEY;
Timo Teräs's avatar
Timo Teräs committed
507
508
		}

Timo Teräs's avatar
Timo Teräs committed
509
510
511
512
513
514
515
516
		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)
517
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
518
519
520
521
522
523
524
525
526
		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)
527
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
528
529
530
531
532
533
534
535
536
		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;
537
		break;
Timo Teräs's avatar
Timo Teräs committed
538
	}
539
540
541
542
	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
543
544
545
546
547
548
549
550
551

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
552
553
554
	return 0;
}

555
556
557
struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
558
559
	struct apk_sign_ctx *sctx;
	int version;
560
561
};

562
563
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
564
565
566
{
	switch (field) {
	case 'P':
567
		pkg->name = apk_db_get_name(db, value);
Timo Teräs's avatar
Timo Teräs committed
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
		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
585
		apk_blob_pull_csum(&value, &pkg->csum);
Timo Teräs's avatar
Timo Teräs committed
586
587
		break;
	case 'S':
588
		pkg->size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
589
590
		break;
	case 'I':
591
		pkg->installed_size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
592
		break;
593
594
	default:
		return -1;
Timo Teräs's avatar
Timo Teräs committed
595
	}
596
597
	if (APK_BLOB_IS_NULL(value))
		return -1;
Timo Teräs's avatar
Timo Teräs committed
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
	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;

622
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
623
624
625
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
626
		if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
627
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
628
			return 0;
Timo Teräs's avatar
Timo Teräs committed
629
630
631
		}
	}

Timo Teräs's avatar
Timo Teräs committed
632
633
634
635
636
637
638
639
	if (ri->sctx->data_started == 0 &&
	    apk_blob_compare(APK_BLOB_STR("datahash"), l) == 0) {
		ri->sctx->has_data_checksum = 1;
		ri->sctx->md = EVP_sha256();
		apk_blob_pull_hexdump(
			&r, APK_BLOB_PTR_LEN(ri->sctx->data_checksum,
					     EVP_MD_size(ri->sctx->md)));
	}
640

Timo Teräs's avatar
Timo Teräs committed
641
642
643
	return 0;
}

644
static int read_info_entry(void *ctx, const struct apk_file_info *ae,
645
			   struct apk_istream *is)
646
{
Timo Teräs's avatar
Timo Teräs committed
647
648
649
650
651
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "DESC",	'T' },
652
		{ "WWW",	'U' },
Timo Teräs's avatar
Timo Teräs committed
653
654
655
		{ "LICENSE",	'L' },
		{ "DEPEND", 	'D' },
	};
656
657
658
659
	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;
660
	char *slash;
Timo Teräs's avatar
Timo Teräs committed
661
	int i;
662

Timo Teräs's avatar
Timo Teräs committed
663
	/* Meta info and scripts */
Timo Teräs's avatar
Timo Teräs committed
664
665
	if (apk_sign_ctx_process_file(ri->sctx, ae, is) == 0)
		return 0;
666

Timo Teräs's avatar
Timo Teräs committed
667
	if (ae->name[0] == '.') {
Timo Teräs's avatar
Timo Teräs committed
668
669
		/* APK 2.0 format */
		if (strcmp(ae->name, ".PKGINFO") == 0) {
670
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
Timo Teräs's avatar
Timo Teräs committed
671
672
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
673
674
			ri->version = 2;
		} else if (strcmp(ae->name, ".INSTALL") == 0) {
675
676
			apk_warning("Package '%s-%s' contains deprecated .INSTALL",
				    pkg->name->name, pkg->version);
Timo Teräs's avatar
Timo Teräs committed
677
		}
678
679
680
681
		return 0;
	}

	if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
Timo Teräs's avatar
Timo Teräs committed
682
		/* APK 1.0 format */
683
		ri->version = 1;
Timo Teräs's avatar
Timo Teräs committed
684
685
		if (!S_ISREG(ae->mode))
			return 0;
686

Timo Teräs's avatar
Timo Teräs committed
687
688
689
		slash = strchr(&ae->name[11], '/');
		if (slash == NULL)
			return 0;
690

Timo Teräs's avatar
Timo Teräs committed
691
692
693
		if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
				       &name, &version) < 0)
			return -1;
694

695
696
		if (pkg->name == NULL)
			pkg->name = apk_db_get_name(db, name);
Timo Teräs's avatar
Timo Teräs committed
697
698
699
700
701
		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) {
702
				apk_blob_t blob = apk_blob_from_istream(is, ae->size);
703
704
				apk_pkg_add_info(ri->db, ri->pkg, fields[i].field,
						 trim(blob));
Timo Teräs's avatar
Timo Teräs committed
705
706
707
				free(blob.ptr);
				break;
			}
708
		}
709
	} else if (ri->version < 2) {
710
711
		/* Version 1.x packages do not contain installed size
		 * in metadata, so we calculate it here */
712
		pkg->installed_size += apk_calc_installed_size(ae->size);
Timo Teräs's avatar
Timo Teräs committed
713
	}
714
715
716
717

	return 0;
}

718
719
int apk_pkg_read(struct apk_database *db, const char *file,
	         struct apk_sign_ctx *sctx, struct apk_package **pkg)
720
721
{
	struct read_info_ctx ctx;
722
	struct apk_file_info fi;
723
	struct apk_bstream *bs;
724
	struct apk_istream *tar;
725
	int r;
726

727
	r = apk_file_get_info(AT_FDCWD, file, APK_CHECKSUM_NONE, &fi);
728
729
	if (r != 0)
		return r;
730

731
	memset(&ctx, 0, sizeof(ctx));
Timo Teräs's avatar
Timo Teräs committed
732
	ctx.sctx = sctx;
733
	ctx.pkg = apk_pkg_new();
734
	r = -ENOMEM;
735
	if (ctx.pkg == NULL)
736
		goto err;
737
	bs = apk_bstream_from_file(AT_FDCWD, file);
Timo Teräs's avatar
Timo Teräs committed
738
	if (bs == NULL)
739
740
741
		goto err;

	ctx.db = db;
742
	ctx.pkg->size = fi.size;
743

Timo Teräs's avatar
Timo Teräs committed
744
	tar = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, sctx);
745
	r = apk_tar_parse(tar, read_info_entry, &ctx, FALSE);
746
	tar->close(tar);
Timo Teräs's avatar
Timo Teräs committed
747
	if (r < 0 && r != -ECANCELED)
748
		goto err;
749
	if (ctx.pkg->name == NULL) {
750
		r = -ENOMSG;
751
		goto err;
752
	}
Timo Teräs's avatar
Timo Teräs committed
753
754
	if (sctx->action != APK_SIGN_VERIFY)
		ctx.pkg->csum = sctx->identity;
755
	ctx.pkg->filename = strdup(file);
756

757
758
759
	ctx.pkg = apk_db_pkg_add(db, ctx.pkg);
	if (pkg != NULL)
		*pkg = ctx.pkg;
760
	return 0;
761
762
err:
	apk_pkg_free(ctx.pkg);
763
	return r;
764
765
766
767
768
769
770
771
772
773
774
775
776
}

void apk_pkg_free(struct apk_package *pkg)
{
	struct apk_script *script;
	struct hlist_node *c, *n;

	if (pkg == NULL)
		return;

	hlist_for_each_entry_safe(script, c, n, &pkg->scripts, script_list)
		free(script);

777
778
	if (pkg->depends)
		free(pkg->depends);
779
780
781
782
783
784
785
786
787
788
789
790
791
	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);
}

int apk_pkg_get_state(struct apk_package *pkg)
{
792
	if (list_hashed(&pkg->installed_pkgs_list))
793
794
		return APK_PKG_INSTALLED;
	return APK_PKG_NOT_INSTALLED;
795
796
}

797
798
799
void apk_pkg_set_state(struct apk_database *db, struct apk_package *pkg, int state)
{
	switch (state) {
800
	case APK_PKG_INSTALLED:
801
802
803
804
805
806
		if (!list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages++;
			list_add_tail(&pkg->installed_pkgs_list,
				      &db->installed.packages);
		}
		break;
807
	case APK_PKG_NOT_INSTALLED:
808
809
810
811
812
813
814
815
		if (list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages--;
			list_del(&pkg->installed_pkgs_list);
		}
		break;
	}
}

816
int apk_pkg_add_script(struct apk_package *pkg, struct apk_istream *is,
817
818
819
820
821
822
823
824
		       unsigned int type, unsigned int size)
{
	struct apk_script *script;
	int r;

	script = malloc(sizeof(struct apk_script) + size);
	script->type = type;
	script->size = size;
825
	r = is->read(is, script->script, size);
826
827
828
829
830
831
832
833
834
	if (r < 0) {
		free(script);
		return r;
	}

	hlist_add_head(&script->script_list, &pkg->scripts);
	return r;
}

835
int apk_pkg_run_script(struct apk_package *pkg, int root_fd,
836
837
		       unsigned int type)
{
838
839
840
841
	static const char * const environment[] = {
		"PATH=/usr/sbin:/usr/bin:/sbin:/bin",
		NULL
	};
842
843
844
845
	struct apk_script *script;
	struct hlist_node *c;
	int fd, status;
	pid_t pid;
846
	char fn[PATH_MAX];
847
848

	hlist_for_each_entry(script, c, &pkg->scripts, script_list) {
849
		if (script->type != type)
850
851
			continue;

852
		snprintf(fn, sizeof(fn), "tmp/%s-%s.%s",
853
			pkg->name->name, pkg->version,
Timo Teräs's avatar
Timo Teräs committed
854
			apk_script_types[type]);
855
856

		fd = openat(root_fd, fn, O_CREAT|O_RDWR|O_TRUNC, 0777);
857
858
859
860
861
862
863
864
865
866
867
		if (fd < 0)
			return fd;
		write(fd, script->script, script->size);
		close(fd);

		apk_message("Executing %s", &fn[4]);

		pid = fork();
		if (pid == -1)
			return -1;
		if (pid == 0) {
868
			fchdir(root_fd);
869
870
			if (chroot(".") < 0) {
				apk_error("chroot: %s", strerror(errno));
Timo Teräs's avatar
Timo Teräs committed
871
			} else {
Timo Teräs's avatar
Timo Teräs committed
872
				execle(fn, apk_script_types[type],
873
				       pkg->version, "", NULL, environment);
Timo Teräs's avatar
Timo Teräs committed
874
			}
875
876
877
			exit(1);
		}
		waitpid(pid, &status, 0);
878
		unlinkat(root_fd, fn, 0);
879
880
881
882
883
884
885
886
		if (WIFEXITED(status))
			return WEXITSTATUS(status);
		return -1;
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
887
static int parse_index_line(void *ctx, apk_blob_t line)
888
{
Timo Teräs's avatar
Timo Teräs committed
889
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
890

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

894
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
895
896
897
898
899
	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
900
	struct read_info_ctx ctx;
901

902
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
903
	if (ctx.pkg == NULL)
904
905
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
906
	ctx.db = db;
907
	ctx.version = 0;
908

Timo Teräs's avatar
Timo Teräs committed
909
910
911
912
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
913
914
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
915
		ctx.pkg = NULL;
916
917
	}

Timo Teräs's avatar
Timo Teräs committed
918
	return ctx.pkg;
919
920
}

921
922
int apk_pkg_write_index_entry(struct apk_package *info,
			      struct apk_ostream *os)
923
{
924
	char buf[512];
925
926
927
928
	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
929
	apk_blob_push_csum(&bbuf, &info->csum);
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
	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
946
947
948
	if (APK_BLOB_IS_NULL(bbuf))
		return -1;

949
	if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
950
		return -1;
951
952

	if (info->depends != NULL) {
953
954
955
956
957
958
959
		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;
960
961
	}

962
	return 0;
963
}
964

965
966
967
968
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}