Refactor .apk extraction code

This moves and isolates the tar code to tar.c. And the actual
file extraction to disk is moved to extract.c.

A new API is introduced and used for v2 file extraction. This
essentially moves and isolates the apk_sign_ctx_* beast into
extract_v2.c and offers a saner interface to handling packages.

A place holder is added for v3 extraction.
cute-signatures
Timo Teräs 2021-07-26 16:25:03 +03:00
parent 2d4e88aeb1
commit 9c843e4ecd
25 changed files with 743 additions and 748 deletions

View File

@ -21,7 +21,7 @@ libapk_so := $(obj)/libapk.so.$(libapk_soname)
libapk.so.$(libapk_soname)-objs := \
adb.o adb_comp.o adb_walk_adb.o adb_walk_genadb.o adb_walk_gentext.o adb_walk_text.o apk_adb.o \
atom.o blob.o commit.o common.o context.o crypto_openssl.o database.o hash.o \
io.o io_archive.o io_gunzip.o io_url.o \
extract.o extract_v2.o extract_v3.o io.o io_gunzip.o io_url.o tar.o \
package.o pathbuilder.o print.o solver.o trust.o version.o
libapk.so.$(libapk_soname)-libs := libfetch/libfetch.a

View File

@ -1,35 +0,0 @@
/* apk_archive.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#ifndef APK_ARCHIVE
#define APK_ARCHIVE
#include <sys/types.h>
#include "apk_blob.h"
#include "apk_print.h"
#include "apk_io.h"
int apk_tar_parse(struct apk_istream *,
apk_archive_entry_parser parser, void *ctx,
struct apk_id_cache *);
int apk_tar_write_entry(struct apk_ostream *, const struct apk_file_info *ae,
const char *data);
int apk_tar_write_padding(struct apk_ostream *, const struct apk_file_info *ae);
#define APK_EXTRACTF_NO_CHOWN 0x0001
#define APK_EXTRACTF_NO_OVERWRITE 0x0002
int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
const char *extract_name, const char *hardlink_name,
struct apk_istream *is,
apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx,
unsigned int extract_flags,
struct apk_out *out);
#endif

View File

@ -13,7 +13,6 @@
#include "apk_version.h"
#include "apk_hash.h"
#include "apk_atom.h"
#include "apk_archive.h"
#include "apk_package.h"
#include "apk_io.h"
#include "apk_context.h"
@ -221,7 +220,7 @@ unsigned int apk_db_get_pinning_mask_repos(struct apk_database *db, unsigned sho
int apk_db_cache_active(struct apk_database *db);
int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
struct apk_package *pkg, int verify, int autoupdate,
struct apk_package *pkg, int autoupdate,
apk_progress_cb cb, void *cb_ctx);
typedef void (*apk_cache_item_cb)(struct apk_database *db,

View File

@ -43,6 +43,7 @@ enum {
APKE_SIGNATURE_FAIL,
APKE_SIGNATURE_UNTRUSTED,
APKE_SIGNATURE_INVALID,
APKE_FORMAT_NOT_SUPPORTED,
APKE_ADB_COMPRESSION,
APKE_ADB_HEADER,
APKE_ADB_VERSION,

64
src/apk_extract.h Normal file
View File

@ -0,0 +1,64 @@
/* apk_extract.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2021 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#ifndef APK_EXTRACT
#define APK_EXTRACT
#include "apk_crypto.h"
#include "apk_print.h"
#include "apk_io.h"
struct apk_ctx;
struct apk_extract_ctx;
#define APK_EXTRACTF_NO_CHOWN 0x0001
#define APK_EXTRACTF_NO_OVERWRITE 0x0002
int apk_extract_file(int atfd, const struct apk_file_info *ae,
const char *extract_name, const char *hardlink_name,
struct apk_istream *is,
apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx,
unsigned int extract_flags, struct apk_out *out);
typedef int (*apk_extract_cb)(struct apk_extract_ctx *,
const struct apk_file_info *ae,
struct apk_istream *istream);
struct apk_extract_ctx {
struct apk_ctx *ac;
apk_extract_cb cb;
struct apk_checksum *identity;
unsigned generate_identity : 1;
unsigned metadata : 1;
unsigned metadata_verified : 1;
void *pctx;
};
static inline void apk_extract_init(struct apk_extract_ctx *ectx, struct apk_ctx *ac, apk_extract_cb cb) {
*ectx = (struct apk_extract_ctx){.ac = ac, .cb = cb};
}
static inline void apk_extract_reset(struct apk_extract_ctx *ectx) {
apk_extract_init(ectx, ectx->ac, ectx->cb);
}
static inline void apk_extract_generate_identity(struct apk_extract_ctx *ctx, struct apk_checksum *id) {
ctx->identity = id;
ctx->generate_identity = 1;
}
static inline void apk_extract_verify_identity(struct apk_extract_ctx *ctx, struct apk_checksum *id) {
ctx->identity = id;
}
int apk_extract(struct apk_extract_ctx *, struct apk_istream *is);
int apk_extract_v2(struct apk_extract_ctx *, struct apk_istream *is);
void apk_extract_v2_control(struct apk_extract_ctx *, apk_blob_t, apk_blob_t);
int apk_extract_v3(struct apk_extract_ctx *, struct apk_istream *is);
#endif

View File

@ -30,12 +30,6 @@ struct apk_trust;
#define APK_SCRIPT_TRIGGER 6
#define APK_SCRIPT_MAX 7
#define APK_SIGN_NONE 0
#define APK_SIGN_VERIFY 1
#define APK_SIGN_VERIFY_IDENTITY 2
#define APK_SIGN_GENERATE 4
#define APK_SIGN_VERIFY_AND_GENERATE 5
#define APK_DEP_IRRELEVANT 0x01
#define APK_DEP_SATISFIES 0x02
#define APK_DEP_CONFLICTS 0x04
@ -45,28 +39,6 @@ struct apk_trust;
#define APK_FOREACH_DEP 0x80
#define APK_FOREACH_GENID_MASK 0xffffff00
struct apk_sign_ctx {
struct apk_trust *trust;
int action;
const EVP_MD *md;
int num_signatures;
int control_started : 1;
int data_started : 1;
int has_data_checksum : 1;
int control_verified : 1;
int data_verified : 1;
int allow_untrusted : 1;
char data_checksum[EVP_MAX_MD_SIZE];
struct apk_checksum identity;
EVP_MD_CTX *mdctx;
struct {
apk_blob_t data;
EVP_PKEY *pkey;
char *identity;
} signature;
};
struct apk_dependency {
struct apk_name *name;
apk_blob_t *version;
@ -132,17 +104,6 @@ APK_ARRAY(apk_package_array, struct apk_package *);
extern const char *apk_script_types[];
void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action,
struct apk_checksum *identity, struct apk_trust *trust);
void apk_sign_ctx_free(struct apk_sign_ctx *ctx);
int apk_sign_ctx_process_file(struct apk_sign_ctx *ctx,
const struct apk_file_info *fi,
struct apk_istream *is);
int apk_sign_ctx_parse_pkginfo_line(void *ctx, apk_blob_t line);
int apk_sign_ctx_verify_tar(void *ctx, const struct apk_file_info *fi,
struct apk_istream *is);
int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t blob);
void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db,
struct apk_package *pkg);
int apk_dep_is_materialized(struct apk_dependency *dep, struct apk_package *pkg);
@ -165,8 +126,7 @@ int apk_script_type(const char *name);
struct apk_package *apk_pkg_get_installed(struct apk_name *name);
struct apk_package *apk_pkg_new(void);
int apk_pkg_read(struct apk_database *db, const char *name,
struct apk_sign_ctx *ctx, struct apk_package **pkg);
int apk_pkg_read(struct apk_database *db, const char *name, struct apk_package **pkg);
void apk_pkg_free(struct apk_package *pkg);
int apk_pkg_parse_name(apk_blob_t apkname, apk_blob_t *name, apk_blob_t *version);

22
src/apk_tar.h Normal file
View File

@ -0,0 +1,22 @@
/* apk_tar.h - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#ifndef APK_TAR
#define APK_TAR
#include "apk_io.h"
int apk_tar_parse(struct apk_istream *,
apk_archive_entry_parser parser, void *ctx,
struct apk_id_cache *);
int apk_tar_write_entry(struct apk_ostream *, const struct apk_file_info *ae,
const char *data);
int apk_tar_write_padding(struct apk_ostream *, const struct apk_file_info *ae);
#endif

View File

@ -14,6 +14,7 @@
#include "apk_database.h"
#include "apk_print.h"
#include "apk_solver.h"
#include "apk_extract.h"
struct add_ctx {
const char *virtpkg;
@ -153,15 +154,11 @@ static int add_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args
if (strstr(*parg, ".apk") != NULL) {
struct apk_package *pkg = NULL;
struct apk_sign_ctx sctx;
if (non_repository_check(db))
return -1;
apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY_AND_GENERATE,
NULL, apk_ctx_get_trust(ac));
r = apk_pkg_read(db, *parg, &sctx, &pkg);
apk_sign_ctx_free(&sctx);
r = apk_pkg_read(db, *parg, &pkg);
if (r != 0) {
apk_err(out, "%s: %s", *parg, apk_error_str(r));
return -1;

View File

@ -98,8 +98,7 @@ static int cache_download(struct cache_ctx *cctx, struct apk_database *db)
if (repo == NULL)
continue;
r = apk_cache_download(db, repo, pkg, APK_SIGN_VERIFY_IDENTITY, 0,
progress_cb, &prog);
r = apk_cache_download(db, repo, pkg, 0, progress_cb, &prog);
if (r && r != -EALREADY) {
apk_err(out, PKG_VER_FMT ": %s", PKG_VER_PRINTF(pkg), apk_error_str(r));
ret++;

View File

@ -5,6 +5,7 @@
#include "apk_adb.h"
#include "apk_applet.h"
#include "apk_tar.h"
struct conv_script {
struct list_head script_node;

View File

@ -4,12 +4,13 @@
#include "apk_adb.h"
#include "apk_applet.h"
#include "apk_extract.h"
struct conv_ctx {
struct apk_ctx *ac;
struct adb_obj pkgs;
struct adb dbi;
struct apk_sign_ctx sctx;
struct apk_extract_ctx ectx;
int found;
};
@ -31,38 +32,27 @@ static void convert_index(struct conv_ctx *ctx, struct apk_istream *is)
}
}
static int load_apkindex(void *sctx, const struct apk_file_info *fi,
static int load_apkindex(struct apk_extract_ctx *ectx, const struct apk_file_info *fi,
struct apk_istream *is)
{
struct conv_ctx *ctx = sctx;
int r;
r = apk_sign_ctx_process_file(&ctx->sctx, fi, is);
if (r <= 0) return r;
struct conv_ctx *ctx = container_of(ectx, struct conv_ctx, ectx);
if (strcmp(fi->name, "APKINDEX") == 0) {
ctx->found = 1;
convert_index(ctx, is);
return apk_istream_close(is);
}
return 0;
}
static int load_index(struct conv_ctx *ctx, struct apk_istream *is)
{
int r = 0;
if (IS_ERR_OR_NULL(is)) return is ? PTR_ERR(is) : -EINVAL;
if (IS_ERR(is)) return PTR_ERR(is);
ctx->found = 0;
apk_sign_ctx_init(&ctx->sctx, APK_SIGN_VERIFY, NULL, apk_ctx_get_trust(ctx->ac));
r = apk_tar_parse(
apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &ctx->sctx),
load_apkindex, ctx, apk_ctx_get_id_cache(ctx->ac));
apk_sign_ctx_free(&ctx->sctx);
apk_extract_init(&ctx->ectx, ctx->ac, load_apkindex);
r = apk_extract(&ctx->ectx, is);
if (r >= 0 && ctx->found == 0) r = -APKE_V2NDX_FORMAT;
return r;
}

View File

@ -18,6 +18,7 @@
#include "apk_print.h"
#include "apk_adb.h"
#include "apk_pathbuilder.h"
#include "apk_extract.h"
struct extract_ctx {
const char *destination;
@ -151,7 +152,7 @@ static int apk_extract_volume(struct apk_ctx *ac, struct apk_file_info *fi, stru
return uvol_extract(ac, fi->uvol_name, size, fi->size, is, dctx);
}
static int apk_extract_file(struct extract_ctx *ctx, off_t sz, struct apk_istream *is)
static int apk_extract_adb_file(struct extract_ctx *ctx, off_t sz, struct apk_istream *is)
{
struct apk_ctx *ac = ctx->ac;
struct apk_out *out = &ac->out;
@ -199,7 +200,7 @@ static int apk_extract_file(struct extract_ctx *ctx, off_t sz, struct apk_istrea
return -APKE_ADB_SCHEMA;
}
fi.mode |= mode;
return apk_archive_entry_extract(
return apk_extract_file(
ctx->root_fd, &fi, 0, 0, is, 0, 0, 0,
ctx->extract_flags, out);
}
@ -212,7 +213,7 @@ static int apk_extract_file(struct extract_ctx *ctx, off_t sz, struct apk_istrea
if (fi.uvol_name) {
r = apk_extract_volume(ac, &fi, is, &dctx);
} else {
r = apk_archive_entry_extract(
r = apk_extract_file(
ctx->root_fd, &fi, 0, 0, is, 0, 0, &dctx,
ctx->extract_flags, out);
if (r < 0) return r;
@ -245,7 +246,7 @@ static int apk_extract_directory(struct extract_ctx *ctx)
apk_extract_acl(&fi, adb_ro_obj(&ctx->path, ADBI_DI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac));
fi.mode |= S_IFDIR;
return apk_archive_entry_extract(
return apk_extract_file(
ctx->root_fd, &fi, 0, 0, 0, 0, 0, 0,
ctx->extract_flags, out);
}
@ -285,7 +286,7 @@ static int apk_extract_next_file(struct extract_ctx *ctx)
APK_BLOB_IS_NULL(target)) {
return 0;
}
r = apk_extract_file(ctx, 0, 0);
r = apk_extract_adb_file(ctx, 0, 0);
if (r != 0) return r;
} while (1);
}
@ -316,7 +317,7 @@ static int apk_extract_data_block(struct adb *db, struct adb_block *b, struct ap
return -APKE_ADB_BLOCK;
}
return apk_extract_file(ctx, sz, is);
return apk_extract_adb_file(ctx, sz, is);
}
static int apk_extract_pkg(struct extract_ctx *ctx, const char *fn)
@ -367,12 +368,12 @@ static int extract_main(void *pctx, struct apk_ctx *ac, struct apk_string_array
return r;
}
static struct apk_applet apk_extract = {
static struct apk_applet app_extract = {
.name = "extract",
.context_size = sizeof(struct extract_ctx),
.optgroups = { &optgroup_global, &optgroup_applet },
.main = extract_main,
};
APK_DEFINE_APPLET(apk_extract);
APK_DEFINE_APPLET(app_extract);

View File

@ -16,6 +16,7 @@
#include "apk_applet.h"
#include "apk_database.h"
#include "apk_print.h"
#include "apk_tar.h"
#define APK_INDEXF_NO_WARNINGS 0x0001
@ -30,7 +31,6 @@ struct index_ctx {
const char *description;
const char *rewrite_arch;
time_t index_mtime;
int method;
unsigned short index_flags;
};
@ -110,7 +110,7 @@ static int index_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
struct apk_out *out = &ac->out;
struct apk_database *db = ac->db;
struct counts counts = { .out = out };
struct apk_ostream *os;
struct apk_ostream *os, *counter;
struct apk_file_info fi;
int total, r, found, newpkgs = 0, errors = 0;
struct index_ctx *ictx = (struct index_ctx *) ctx;
@ -126,9 +126,6 @@ static int index_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
return -1;
}
if (ictx->method == 0)
ictx->method = APK_SIGN_GENERATE;
if ((r = index_read_file(db, ictx)) < 0) {
apk_err(out, "%s: %s", ictx->index, apk_error_str(r));
return r;
@ -185,9 +182,7 @@ static int index_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
} while (0);
if (!found) {
struct apk_sign_ctx sctx;
apk_sign_ctx_init(&sctx, ictx->method, NULL, apk_ctx_get_trust(ac));
r = apk_pkg_read(db, *parg, &sctx, &pkg);
r = apk_pkg_read(db, *parg, &pkg);
if (r < 0) {
apk_err(out, "%s: %s", *parg, apk_error_str(r));
errors++;
@ -195,7 +190,6 @@ static int index_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
newpkgs++;
if (rewrite_arch) pkg->arch = rewrite_arch;
}
apk_sign_ctx_free(&sctx);
}
}
if (errors)
@ -207,35 +201,29 @@ static int index_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
os = apk_ostream_to_fd(STDOUT_FILENO);
if (IS_ERR_OR_NULL(os)) return -1;
if (ictx->method == APK_SIGN_GENERATE) {
struct apk_ostream *counter;
memset(&fi, 0, sizeof(fi));
fi.mode = 0644 | S_IFREG;
fi.name = "APKINDEX";
counter = apk_ostream_counter(&fi.size);
r = apk_db_index_write(db, counter);
apk_ostream_close(counter);
memset(&fi, 0, sizeof(fi));
fi.mode = 0644 | S_IFREG;
fi.name = "APKINDEX";
counter = apk_ostream_counter(&fi.size);
r = apk_db_index_write(db, counter);
apk_ostream_close(counter);
if (r >= 0) {
os = apk_ostream_gzip(os);
if (ictx->description != NULL) {
struct apk_file_info fi_desc;
memset(&fi_desc, 0, sizeof(fi));
fi_desc.mode = 0644 | S_IFREG;
fi_desc.name = "DESCRIPTION";
fi_desc.size = strlen(ictx->description);
apk_tar_write_entry(os, &fi_desc, ictx->description);
}
apk_tar_write_entry(os, &fi, NULL);
r = apk_db_index_write(db, os);
apk_tar_write_padding(os, &fi);
apk_tar_write_entry(os, NULL, NULL);
if (r >= 0) {
os = apk_ostream_gzip(os);
if (ictx->description != NULL) {
struct apk_file_info fi_desc;
memset(&fi_desc, 0, sizeof(fi));
fi_desc.mode = 0644 | S_IFREG;
fi_desc.name = "DESCRIPTION";
fi_desc.size = strlen(ictx->description);
apk_tar_write_entry(os, &fi_desc, ictx->description);
}
} else {
apk_tar_write_entry(os, &fi, NULL);
r = apk_db_index_write(db, os);
apk_tar_write_padding(os, &fi);
apk_tar_write_entry(os, NULL, NULL);
}
apk_ostream_close(os);

View File

@ -14,6 +14,7 @@
#include "apk_defines.h"
#include "apk_applet.h"
#include "apk_database.h"
#include "apk_extract.h"
#include "apk_version.h"
#include "apk_print.h"
@ -60,62 +61,48 @@ static void process_package(struct apk_database *db, struct apk_package *pkg)
struct manifest_file_ctx {
struct apk_out *out;
struct apk_sign_ctx *sctx;
struct apk_extract_ctx ectx;
const char *prefix1, *prefix2;
};
static int read_file_entry(void *ctx, const struct apk_file_info *ae, struct apk_istream *is)
static int read_file_entry(struct apk_extract_ctx *ectx, const struct apk_file_info *fi, struct apk_istream *is)
{
struct manifest_file_ctx *mctx = ctx;
struct manifest_file_ctx *mctx = container_of(ectx, struct manifest_file_ctx, ectx);
struct apk_out *out = mctx->out;
char csum_buf[APK_BLOB_CHECKSUM_BUF];
apk_blob_t csum_blob = APK_BLOB_BUF(csum_buf);
int r;
r = apk_sign_ctx_verify_tar(mctx->sctx, ae, is);
if (r != 0)
return r;
if (!mctx->sctx->data_started)
return 0;
if ((ae->mode & S_IFMT) != S_IFREG)
return 0;
if (ectx->metadata) return 0;
if ((fi->mode & S_IFMT) != S_IFREG) return 0;
memset(csum_buf, '\0', sizeof(csum_buf));
apk_blob_push_hexdump(&csum_blob, APK_DIGEST_BLOB(ae->digest));
apk_blob_push_hexdump(&csum_blob, APK_DIGEST_BLOB(fi->digest));
apk_out(out, "%s%s%s:%s %s",
mctx->prefix1, mctx->prefix2,
apk_digest_alg_str(ae->digest.alg), csum_buf,
ae->name);
apk_digest_alg_str(fi->digest.alg), csum_buf,
fi->name);
return 0;
}
static void process_file(struct apk_database *db, const char *match)
{
struct apk_id_cache *idc = apk_ctx_get_id_cache(db->ctx);
struct apk_out *out = &db->ctx->out;
struct apk_sign_ctx sctx;
struct manifest_file_ctx ctx = {
.out = out,
.sctx = &sctx,
.prefix1 = "",
.prefix2 = "",
};
int r;
apk_extract_init(&ctx.ectx, db->ctx, read_file_entry);
if (apk_out_verbosity(out) > 1) {
ctx.prefix1 = match;
ctx.prefix2 = ": ";
}
apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL, apk_ctx_get_trust(db->ctx));
r = apk_tar_parse(
apk_istream_gunzip_mpart(apk_istream_from_file(AT_FDCWD, match), apk_sign_ctx_mpart_cb, &sctx),
read_file_entry, &ctx, idc);
apk_sign_ctx_free(&sctx);
r = apk_extract(&ctx.ectx, apk_istream_from_file(AT_FDCWD, match));
if (r < 0) apk_err(out, "%s: %s", match, apk_error_str(r));
}

View File

@ -18,6 +18,7 @@
#include "apk_adb.h"
#include "apk_applet.h"
#include "apk_database.h"
#include "apk_extract.h"
#include "apk_print.h"
struct mkndx_ctx {
@ -31,7 +32,7 @@ struct mkndx_ctx {
struct adb_obj pkgs;
time_t index_mtime;
struct apk_sign_ctx sctx;
struct apk_extract_ctx ectx;
size_t file_size;
};
@ -83,7 +84,7 @@ static int cmpfield(const void *pa, const void *pb)
return apk_blob_sort(a->str, b->str);
}
static adb_val_t mkndx_read_v2_pkginfo(struct adb *db, struct apk_istream *is, size_t file_size, struct apk_sign_ctx *sctx, apk_blob_t rewrite_arch)
static adb_val_t mkndx_read_v2_pkginfo(struct adb *db, struct apk_istream *is, size_t file_size, struct apk_extract_ctx *ectx, apk_blob_t rewrite_arch)
{
static struct field fields[] = {
FIELD("arch", ADBI_PI_ARCH),
@ -118,9 +119,9 @@ static adb_val_t mkndx_read_v2_pkginfo(struct adb *db, struct apk_istream *is, s
adb_wo_alloca(&deps[2], &schema_dependency_array, db);
while ((r = apk_istream_get_delim(is, token, &line)) == 0) {
if (sctx) apk_sign_ctx_parse_pkginfo_line(sctx, line);
if (line.len < 1 || line.ptr[0] == '#') continue;
if (!apk_blob_split(line, APK_BLOB_STR(" = "), &k, &v)) continue;
apk_extract_v2_control(ectx, k, v);
key.str = k;
f = bsearch(&key, fields, ARRAY_SIZE(fields), sizeof(fields[0]), cmpfield);
@ -163,22 +164,16 @@ static adb_val_t mkndx_read_v2_pkginfo(struct adb *db, struct apk_istream *is, s
return adb_w_obj(&pkginfo);
}
static int mkndx_parse_v2_tar(void *pctx, const struct apk_file_info *ae, struct apk_istream *is)
static int mkndx_parse_v2_tar(struct apk_extract_ctx *ectx, const struct apk_file_info *ae, struct apk_istream *is)
{
struct mkndx_ctx *ctx = pctx;
adb_val_t o;
int r;
struct mkndx_ctx *ctx = container_of(ectx, struct mkndx_ctx, ectx);
r = apk_sign_ctx_process_file(&ctx->sctx, ae, is);
if (r <= 0) return r;
if (ctx->sctx.control_verified) return -ECANCELED;
if (!ctx->sctx.control_started || ctx->sctx.data_started) return 0;
if (strcmp(ae->name, ".PKGINFO") == 0) {
o = adb_wa_append(
if (ectx->metadata_verified) return -ECANCELED;
if (ectx->metadata && strcmp(ae->name, ".PKGINFO") == 0) {
adb_val_t o = adb_wa_append(
&ctx->pkgs,
mkndx_read_v2_pkginfo(
&ctx->db, is, ctx->file_size, &ctx->sctx,
&ctx->db, is, ctx->file_size, &ctx->ectx,
ctx->rewrite_arch));
if (ADB_IS_ERROR(o)) return -ADB_VAL_VALUE(o);
}
@ -189,7 +184,6 @@ static int mkndx_parse_v2_tar(void *pctx, const struct apk_file_info *ae, struct
static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
{
struct apk_out *out = &ac->out;
struct apk_id_cache *idc = apk_ctx_get_id_cache(ac);
struct apk_trust *trust = apk_ctx_get_trust(ac);
struct adb odb, tmpdb;
struct adb_obj oroot, opkgs, ndx, tmpl;
@ -205,6 +199,8 @@ static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
return -1;
}
apk_extract_init(&ctx->ectx, ac, mkndx_parse_v2_tar);
adb_init(&odb);
adb_w_init_tmp(&tmpdb, 200);
adb_wo_alloca(&tmpl, &schema_pkginfo, &tmpdb);
@ -278,11 +274,7 @@ static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
}
if (!found) {
do_file:
apk_sign_ctx_init(&ctx->sctx, APK_SIGN_VERIFY, NULL, trust);
r = apk_tar_parse(
apk_istream_gunzip_mpart(apk_istream_from_file(AT_FDCWD, *parg), apk_sign_ctx_mpart_cb, &ctx->sctx),
mkndx_parse_v2_tar, ctx, idc);
apk_sign_ctx_free(&ctx->sctx);
r = apk_extract(&ctx->ectx, apk_istream_from_file(AT_FDCWD, *parg));
if (r < 0 && r != -ECANCELED) goto err_pkg;
newpkgs++;
}

View File

@ -20,6 +20,7 @@
#include "apk_applet.h"
#include "apk_database.h"
#include "apk_pathbuilder.h"
#include "apk_extract.h"
#include "apk_print.h"
#define BLOCK_SIZE 4096
@ -29,7 +30,7 @@ struct mkpkg_ctx {
const char *files_dir, *output;
struct adb db;
struct adb_obj paths, *files;
struct apk_sign_ctx sctx;
struct apk_extract_ctx ectx;
apk_blob_t info[ADBI_PI_MAX];
uint64_t installed_size;
struct apk_pathbuilder pb;

View File

@ -12,38 +12,26 @@
#include <unistd.h>
#include "apk_applet.h"
#include "apk_database.h"
#include "apk_print.h"
#include "apk_extract.h"
static int verify_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args)
{
struct apk_out *out = &ac->out;
struct apk_sign_ctx sctx;
struct apk_id_cache *idc = apk_ctx_get_id_cache(ac);
struct apk_trust *trust = apk_ctx_get_trust(ac);
struct apk_extract_ctx ectx;
char **parg;
int r, ok, rc = 0;
int r, rc = 0;
trust->allow_untrusted = 1;
apk_extract_init(&ectx, ac, 0);
foreach_array_item(parg, args) {
apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL, trust);
r = apk_tar_parse(
apk_istream_gunzip_mpart(apk_istream_from_file(AT_FDCWD, *parg),
apk_sign_ctx_mpart_cb, &sctx),
apk_sign_ctx_verify_tar, &sctx, idc);
ok = sctx.control_verified && sctx.data_verified;
r = apk_extract(&ectx, apk_istream_from_file(AT_FDCWD, *parg));
if (apk_out_verbosity(out) >= 1)
apk_msg(out, "%s: %d - %s", *parg, r,
r < 0 ? apk_error_str(r) :
ok ? "OK" :
!sctx.control_verified ? "UNTRUSTED" : "FAILED");
else if (!ok)
apk_msg(out, "%s: %s", *parg,
r < 0 ? apk_error_str(r) : "OK");
else if (r < 0)
apk_out(out, "%s", *parg);
if (!ok)
rc++;
apk_sign_ctx_free(&sctx);
if (r < 0) rc++;
}
return rc;

View File

@ -32,9 +32,10 @@
#include "apk_package.h"
#include "apk_database.h"
#include "apk_applet.h"
#include "apk_archive.h"
#include "apk_extract.h"
#include "apk_print.h"
#include "apk_openssl.h"
#include "apk_tar.h"
static const apk_spn_match_def apk_spn_repo_separators = {
[1] = (1<<1) /* tab */,
@ -70,7 +71,7 @@ struct install_ctx {
struct apk_db_dir_instance *diri;
struct apk_checksum data_csum;
struct apk_sign_ctx sctx;
struct apk_extract_ctx ectx;
apk_progress_cb cb;
void *cb_ctx;
@ -621,7 +622,7 @@ int apk_repo_format_item(struct apk_database *db, struct apk_repository *repo, s
}
int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
struct apk_package *pkg, int verify, int autoupdate,
struct apk_package *pkg, int autoupdate,
apk_progress_cb cb, void *cb_ctx)
{
struct apk_out *out = &db->ctx->out;
@ -629,7 +630,7 @@ int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
struct apk_url_print urlp;
struct apk_istream *is;
struct apk_ostream *os;
struct apk_sign_ctx sctx;
struct apk_extract_ctx ectx;
char url[PATH_MAX];
char cacheitem[128];
int r;
@ -658,29 +659,16 @@ int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
if (cb) cb(cb_ctx, 0);
if (verify != APK_SIGN_NONE) {
apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL, apk_ctx_get_trust(db->ctx));
is = apk_istream_from_url(url, apk_db_url_since(db, st.st_mtime));
is = apk_istream_tee(is, os, autoupdate ? 0 : APK_ISTREAM_TEE_COPY_META, cb, cb_ctx);
is = apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &sctx);
r = apk_tar_parse(is, apk_sign_ctx_verify_tar, &sctx, db->id_cache);
apk_sign_ctx_free(&sctx);
} else {
is = apk_istream_from_url(url, apk_db_url_since(db, st.st_mtime));
if (!IS_ERR(is)) {
apk_stream_copy(is, os, APK_IO_ALL, cb, cb_ctx, 0);
if (!autoupdate) apk_ostream_copy_meta(os, is);
apk_istream_close(is);
} else {
apk_ostream_cancel(os, PTR_ERR(is));
}
r = apk_ostream_close(os);
}
is = apk_istream_from_url(url, apk_db_url_since(db, st.st_mtime));
is = apk_istream_tee(is, os, autoupdate ? 0 : APK_ISTREAM_TEE_COPY_META, cb, cb_ctx);
apk_extract_init(&ectx, db->ctx, 0);
if (pkg) apk_extract_verify_identity(&ectx, &pkg->csum);
r = apk_extract(&ectx, is);
if (r == -EALREADY) {
if (autoupdate) utimensat(db->cache_fd, cacheitem, NULL, 0);
return r;
}
return 0;
return r;
}
static struct apk_db_dir_instance *find_diri(struct apk_installed_package *ipkg,
@ -2154,9 +2142,9 @@ static int apk_repository_update(struct apk_database *db, struct apk_repository
{
struct apk_out *out = &db->ctx->out;
struct apk_url_print urlp;
int r, verify = (db->ctx->flags & APK_ALLOW_UNTRUSTED) ? APK_SIGN_NONE : APK_SIGN_VERIFY;
int r;
r = apk_cache_download(db, repo, NULL, verify, 1, NULL, NULL);
r = apk_cache_download(db, repo, NULL, 1, NULL, NULL);
if (r == -EALREADY) return 0;
if (r != 0) {
apk_url_parse(&urlp, repo->url);
@ -2171,67 +2159,46 @@ static int apk_repository_update(struct apk_database *db, struct apk_repository
struct apkindex_ctx {
struct apk_database *db;
struct apk_sign_ctx sctx;
struct apk_extract_ctx ectx;
int repo, found;
};
static int load_apkindex(void *sctx, const struct apk_file_info *fi,
static int load_apkindex(struct apk_extract_ctx *ectx, const struct apk_file_info *fi,
struct apk_istream *is)
{
struct apkindex_ctx *ctx = (struct apkindex_ctx *) sctx;
struct apkindex_ctx *ctx = container_of(ectx, struct apkindex_ctx, ectx);
struct apk_repository *repo;
int r;
r = apk_sign_ctx_process_file(&ctx->sctx, fi, is);
if (r <= 0)
return r;
r = 0;
repo = &ctx->db->repos[ctx->repo];
if (strcmp(fi->name, "DESCRIPTION") == 0) {
repo->description = apk_blob_from_istream(is, fi->size);
} else if (strcmp(fi->name, "APKINDEX") == 0) {
ctx->found = 1;
r = apk_db_index_read(ctx->db, is, ctx->repo);
return apk_db_index_read(ctx->db, is, ctx->repo);
}
return r;
return 0;
}
static int load_index(struct apk_database *db, struct apk_istream *is,
int targz, int repo)
static int load_index(struct apk_database *db, struct apk_istream *is, int repo)
{
struct apkindex_ctx ctx;
int r = 0;
if (IS_ERR_OR_NULL(is)) return is ? PTR_ERR(is) : -EINVAL;
if (IS_ERR(is)) return PTR_ERR(is);
if (targz) {
struct apkindex_ctx ctx;
ctx.db = db;
ctx.repo = repo;
ctx.found = 0;
apk_sign_ctx_init(&ctx.sctx, APK_SIGN_VERIFY, NULL, apk_ctx_get_trust(db->ctx));
r = apk_tar_parse(apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &ctx.sctx), load_apkindex, &ctx, db->id_cache);
apk_sign_ctx_free(&ctx.sctx);
if (r >= 0 && ctx.found == 0)
r = -APKE_V2NDX_FORMAT;
} else {
apk_db_index_read(db, apk_istream_gunzip(is), repo);
}
ctx.db = db;
ctx.repo = repo;
ctx.found = 0;
apk_extract_init(&ctx.ectx, db->ctx, load_apkindex);
r = apk_extract(&ctx.ectx, is);
if (r >= 0 && ctx.found == 0)
r = -APKE_V2NDX_FORMAT;
return r;
}
int apk_db_index_read_file(struct apk_database *db, const char *file, int repo)
{
int targz = 1;
if (strstr(file, ".tar.gz") == NULL && strstr(file, ".gz") != NULL)
targz = 0;
return load_index(db, apk_istream_from_file(AT_FDCWD, file), targz, repo);
return load_index(db, apk_istream_from_file(AT_FDCWD, file), repo);
}
int apk_db_add_repository(apk_database_t _db, apk_blob_t _repository)
@ -2241,7 +2208,7 @@ int apk_db_add_repository(apk_database_t _db, apk_blob_t _repository)
struct apk_repository *repo;
struct apk_url_print urlp;
apk_blob_t brepo, btag;
int repo_num, r, targz = 1, tag_id = 0;
int repo_num, r, tag_id = 0;
char buf[PATH_MAX], *url;
brepo = _repository;
@ -2294,7 +2261,7 @@ int apk_db_add_repository(apk_database_t _db, apk_blob_t _repository)
r = apk_repo_format_real_url(db->arch, repo, NULL, buf, sizeof(buf), &urlp);
}
if (r == 0) {
r = load_index(db, apk_istream_from_fd_url(db->cache_fd, buf, apk_db_url_since(db, 0)), targz, repo_num);
r = load_index(db, apk_istream_from_fd_url(db->cache_fd, buf, apk_db_url_since(db, 0)), repo_num);
}
if (r != 0) {
@ -2318,7 +2285,7 @@ static void extract_cb(void *_ctx, size_t bytes_done)
static void apk_db_run_pending_script(struct install_ctx *ctx)
{
if (ctx->script_pending && ctx->sctx.control_verified) {
if (ctx->script_pending && ctx->ectx.metadata_verified) {
ctx->script_pending = FALSE;
apk_ipkg_run_script(ctx->ipkg, ctx->db, ctx->script, ctx->script_args);
}
@ -2350,7 +2317,7 @@ static int read_info_line(void *_ctx, apk_blob_t line)
list_add_tail(&ipkg->trigger_pkgs_list,
&db->installed.triggers);
} else {
apk_sign_ctx_parse_pkginfo_line(&ctx->sctx, line);
apk_extract_v2_control(&ctx->ectx, l, r);
}
return 0;
}
@ -2409,12 +2376,12 @@ static int contains_control_character(const char *str)
return 0;
}
static int apk_db_install_archive_entry(void *_ctx,
static int apk_db_install_archive_entry(struct apk_extract_ctx *ectx,
const struct apk_file_info *ae,
struct apk_istream *is)
{
struct install_ctx *ctx = container_of(ectx, struct install_ctx, ectx);
static const char dot1[] = "/./", dot2[] = "/../";
struct install_ctx *ctx = (struct install_ctx *) _ctx;
struct apk_database *db = ctx->db;
struct apk_out *out = &db->ctx->out;
struct apk_package *pkg = ctx->pkg, *opkg;
@ -2426,12 +2393,8 @@ static int apk_db_install_archive_entry(void *_ctx,
int ret = 0, r;
char tmpname_file[TMPNAME_MAX], tmpname_link_target[TMPNAME_MAX];
r = apk_sign_ctx_process_file(&ctx->sctx, ae, is);
if (r <= 0)
return r;
/* Package metainfo and script processing */
if (ctx->sctx.control_started && !ctx->sctx.data_started) {
if (ectx->metadata) {
if (ae->name[0] != '.') return 0;
if (strcmp(ae->name, ".PKGINFO") == 0) {
apk_blob_t l, token = APK_BLOB_STR("\n");
@ -2452,8 +2415,7 @@ static int apk_db_install_archive_entry(void *_ctx,
apk_db_run_pending_script(ctx);
/* Rest of files need to be inside data portion */
if (!ctx->sctx.data_started || ae->name[0] == '.')
return 0;
if (ae->name[0] == '.') return 0;
/* Sanity check the file name */
if (ae->name[0] == '/' || contains_control_character(ae->name) ||
@ -2582,7 +2544,7 @@ static int apk_db_install_archive_entry(void *_ctx,
/* Extract the file with temporary name */
file->acl = apk_db_acl_atomize_digest(db, ae->mode, ae->uid, ae->gid, &ae->xattr_digest);
r = apk_archive_entry_extract(
r = apk_extract_file(
db->root_fd, ae,
format_tmpname(pkg, file, tmpname_file),
format_tmpname(pkg, link_target_file, tmpname_link_target),
@ -2843,10 +2805,9 @@ static int apk_db_unpack_pkg(struct apk_database *db,
.cb = cb,
.cb_ctx = cb_ctx,
};
apk_sign_ctx_init(&ctx.sctx, APK_SIGN_VERIFY_IDENTITY, &pkg->csum, apk_ctx_get_trust(db->ctx));
r = apk_tar_parse(apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &ctx.sctx), apk_db_install_archive_entry, &ctx, db->id_cache);
apk_sign_ctx_free(&ctx.sctx);
apk_extract_init(&ctx.ectx, db->ctx, apk_db_install_archive_entry);
apk_extract_verify_identity(&ctx.ectx, &pkg->csum);
r = apk_extract(&ctx.ectx, is);
if (need_copy && r == 0) pkg->repos |= BIT(APK_REPOSITORY_CACHED);
if (r != 0) goto err_msg;

147
src/extract.c Normal file
View File

@ -0,0 +1,147 @@
/* extract.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#include <sys/stat.h>
#include <sys/xattr.h>
#include <unistd.h>
#include "apk_extract.h"
int apk_extract_file(int atfd, const struct apk_file_info *ae,
const char *extract_name, const char *link_target,
struct apk_istream *is,
apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx,
unsigned int extract_flags, struct apk_out *out)
{
struct apk_xattr *xattr;
const char *fn = extract_name ?: ae->name;
int fd, r = -1, atflags = 0, ret = 0;
if (!(extract_flags & APK_EXTRACTF_NO_OVERWRITE)) {
if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno;
}
switch (ae->mode & S_IFMT) {
case S_IFDIR:
r = mkdirat(atfd, fn, ae->mode & 07777);
if (r < 0 && errno != EEXIST)
ret = -errno;
break;
case S_IFREG:
if (ae->link_target == NULL) {
int flags = O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC | O_EXCL;
int fd = openat(atfd, fn, flags, ae->mode & 07777);
if (fd < 0) {
ret = -errno;
break;
}
struct apk_ostream *os = apk_ostream_to_fd(fd);
if (IS_ERR(os)) {
ret = PTR_ERR(os);
break;
}
apk_stream_copy(is, os, ae->size, cb, cb_ctx, dctx);
r = apk_ostream_close(os);
if (r < 0) {
unlinkat(atfd, fn, 0);
ret = r;
}
} else {
r = linkat(atfd, link_target ?: ae->link_target, atfd, fn, 0);
if (r < 0) ret = -errno;
}
break;
case S_IFLNK:
r = symlinkat(link_target ?: ae->link_target, atfd, fn);
if (r < 0) ret = -errno;
atflags |= AT_SYMLINK_NOFOLLOW;
break;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
r = mknodat(atfd, fn, ae->mode, ae->device);
if (r < 0) ret = -errno;
break;
}
if (ret) {
apk_err(out, "Failed to create %s: %s", ae->name, strerror(-ret));
return ret;
}
if (!(extract_flags & APK_EXTRACTF_NO_CHOWN)) {
r = fchownat(atfd, fn, ae->uid, ae->gid, atflags);
if (r < 0) {
apk_err(out, "Failed to set ownership on %s: %s",
fn, strerror(errno));
if (!ret) ret = -errno;
}
/* chown resets suid bit so we need set it again */
if (ae->mode & 07000) {
r = fchmodat(atfd, fn, ae->mode & 07777, atflags);
if (r < 0) {
apk_err(out, "Failed to set file permissions on %s: %s",
fn, strerror(errno));
if (!ret) ret = -errno;
}
}
}
/* extract xattrs */
if (!S_ISLNK(ae->mode) && ae->xattrs && ae->xattrs->num) {
r = 0;
fd = openat(atfd, fn, O_RDWR);
if (fd >= 0) {
foreach_array_item(xattr, ae->xattrs) {
if (fsetxattr(fd, xattr->name, xattr->value.ptr, xattr->value.len, 0) < 0) {
r = -errno;
if (r != -ENOTSUP) break;
}
}
close(fd);
} else {
r = -errno;
}
if (r) {
if (r != -ENOTSUP)
apk_err(out, "Failed to set xattrs on %s: %s",
fn, strerror(-r));
if (!ret) ret = r;
}
}
if (!S_ISLNK(ae->mode)) {
/* preserve modification time */
struct timespec times[2];
times[0].tv_sec = times[1].tv_sec = ae->mtime;
times[0].tv_nsec = times[1].tv_nsec = 0;
r = utimensat(atfd, fn, times, atflags);
if (r < 0) {
apk_err(out, "Failed to preserve modification time on %s: %s",
fn, strerror(errno));
if (!ret || ret == -ENOTSUP) ret = -errno;
}
}
return ret;
}
int apk_extract(struct apk_extract_ctx *ectx, struct apk_istream *is)
{
void *sig;
if (IS_ERR(is)) return PTR_ERR(is);
sig = apk_istream_peek(is, 4);
if (IS_ERR(sig)) return apk_istream_close_error(is, PTR_ERR(sig));
if (memcmp(sig, "ADB", 3) == 0) return apk_extract_v3(ectx, is);
return apk_extract_v2(ectx, is);
}

347
src/extract_v2.c Normal file
View File

@ -0,0 +1,347 @@
/* extract_v2.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "apk_context.h"
#include "apk_extract.h"
#include "apk_package.h"
#include "apk_tar.h"
#define APK_SIGN_NONE 0
#define APK_SIGN_VERIFY 1
#define APK_SIGN_VERIFY_IDENTITY 2
#define APK_SIGN_GENERATE 4
#define APK_SIGN_VERIFY_AND_GENERATE 5
struct apk_sign_ctx {
struct apk_trust *trust;
int action;
const EVP_MD *md;
int num_signatures;
int control_started : 1;
int data_started : 1;
int has_data_checksum : 1;
int control_verified : 1;
int data_verified : 1;
int allow_untrusted : 1;
char data_checksum[EVP_MAX_MD_SIZE];
struct apk_checksum identity;
EVP_MD_CTX *mdctx;
struct {
apk_blob_t data;
EVP_PKEY *pkey;
char *identity;
} signature;
};
static void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action, struct apk_checksum *identity, struct apk_trust *trust)
{
memset(ctx, 0, sizeof(struct apk_sign_ctx));
ctx->trust = trust;
ctx->action = action;
ctx->allow_untrusted = trust->allow_untrusted;
switch (action) {
case APK_SIGN_VERIFY:
/* If we're only verifing, we're going to start with a
* signature section, which we don't need a hash of */
ctx->md = EVP_md_null();
break;
case APK_SIGN_VERIFY_IDENTITY:
/* If we're checking the package against a particular hash,
* we need to start with that hash, because there may not
* be a signature section to deduce it from */
ctx->md = EVP_sha1();
memcpy(&ctx->identity, identity, sizeof(ctx->identity));
break;
case APK_SIGN_GENERATE:
case APK_SIGN_VERIFY_AND_GENERATE:
ctx->md = EVP_sha1();
break;
default:
ctx->action = APK_SIGN_NONE;
ctx->md = EVP_md_null();
ctx->control_started = 1;
ctx->data_started = 1;
break;
}
ctx->mdctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx->mdctx, ctx->md, NULL);
EVP_MD_CTX_set_flags(ctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
}
static void apk_sign_ctx_free(struct apk_sign_ctx *ctx)
{
if (ctx->signature.data.ptr != NULL)
free(ctx->signature.data.ptr);
EVP_MD_CTX_free(ctx->mdctx);
}
static int check_signing_key_trust(struct apk_sign_ctx *sctx)
{
switch (sctx->action) {
case APK_SIGN_VERIFY:
case APK_SIGN_VERIFY_AND_GENERATE:
if (sctx->signature.pkey == NULL) {
if (sctx->allow_untrusted)
break;
return -APKE_SIGNATURE_UNTRUSTED;
}
}
return 0;
}
static int apk_sign_ctx_process_file(struct apk_sign_ctx *ctx, const struct apk_file_info *fi,
struct apk_istream *is)
{
static struct {
char type[8];
unsigned int nid;
} signature_type[] = {
{ "RSA512", NID_sha512 },
{ "RSA256", NID_sha256 },
{ "RSA", NID_sha1 },
{ "DSA", NID_dsa },
};
const EVP_MD *md = NULL;
const char *name = NULL;
struct apk_pkey *pkey;
int r, i;
if (ctx->data_started)
return 1;
if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
/* APKv1.0 compatibility - first non-hidden file is
* considered to start the data section of the file.
* This does not make any sense if the file has v2.0
* style .PKGINFO */
if (ctx->has_data_checksum)
return -APKE_V2PKG_FORMAT;
/* Error out early if identity part is missing */
if (ctx->action == APK_SIGN_VERIFY_IDENTITY)
return -APKE_V2PKG_FORMAT;
ctx->data_started = 1;
ctx->control_started = 1;
r = check_signing_key_trust(ctx);
if (r < 0)
return r;
return 1;
}
if (ctx->control_started)
return 1;
if (strncmp(fi->name, ".SIGN.", 6) != 0) {
ctx->control_started = 1;
return 1;
}
/* By this point, we must be handling a signature file */
ctx->num_signatures++;
/* Already found a signature by a trusted key; no need to keep searching */
if ((ctx->action != APK_SIGN_VERIFY &&
ctx->action != APK_SIGN_VERIFY_AND_GENERATE) ||
ctx->signature.pkey != NULL)
return 0;
for (i = 0; i < ARRAY_SIZE(signature_type); i++) {
size_t slen = strlen(signature_type[i].type);
if (strncmp(&fi->name[6], signature_type[i].type, slen) == 0 &&
fi->name[6+slen] == '.') {
md = EVP_get_digestbynid(signature_type[i].nid);
name = &fi->name[6+slen+1];
break;
}
}
if (!md) return 0;
pkey = apk_trust_key_by_name(ctx->trust, name);
if (pkey) {
ctx->md = md;
ctx->signature.pkey = pkey->key;
ctx->signature.data = apk_blob_from_istream(is, fi->size);
}
return 0;
}
/* apk_sign_ctx_mpart_cb() handles hashing archives and checking signatures, but
it can't do it alone. apk_sign_ctx_process_file() must be in the loop to
actually select which signature is to be verified and load the corresponding
public key into the context object, and apk_sign_ctx_parse_pkginfo_line()
needs to be called when handling the .PKGINFO file to find any applicable
datahash and load it into the context for this function to check against. */
static int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
{
struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
unsigned char calculated[EVP_MAX_MD_SIZE];
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) {
if (part == APK_MPART_END)
return -APKE_V2PKG_FORMAT;
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 &&
part != APK_MPART_END)
goto update_digest;
/* Drool in the remainder of the digest block now, we will finish
* hashing it in all cases */
EVP_DigestUpdate(sctx->mdctx, data.ptr, data.len);
if (sctx->has_data_checksum && !end_of_control) {
/* End of data-block with a checksum read from the control block */
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 -APKE_V2PKG_INTEGRITY;
sctx->data_verified = 1;
if (!sctx->allow_untrusted && !sctx->control_verified)
return -APKE_SIGNATURE_UNTRUSTED;
return 0;
}
/* Either end of control block with a data checksum or end
* of the data block following a control block without a data
* checksum. In either case, we're checking a signature. */
r = check_signing_key_trust(sctx);
if (r < 0)
return r;
switch (sctx->action) {
case APK_SIGN_VERIFY:
case APK_SIGN_VERIFY_AND_GENERATE:
if (sctx->signature.pkey != NULL) {
r = EVP_VerifyFinal(sctx->mdctx,
(unsigned char *) sctx->signature.data.ptr,
sctx->signature.data.len,
sctx->signature.pkey);
if (r != 1 && !sctx->allow_untrusted)
return -APKE_SIGNATURE_INVALID;
} else {
r = 0;
if (!sctx->allow_untrusted)
return -APKE_SIGNATURE_UNTRUSTED;
}
if (r == 1) {
sctx->control_verified = 1;
if (!sctx->has_data_checksum && part == APK_MPART_END)
sctx->data_verified = 1;
}
if (sctx->action == APK_SIGN_VERIFY_AND_GENERATE) goto generate_identity;
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 -APKE_V2PKG_INTEGRITY;
sctx->control_verified = 1;
if (!sctx->has_data_checksum && part == APK_MPART_END)
sctx->data_verified = 1;
break;
case APK_SIGN_GENERATE:
generate_identity:
/* 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->has_data_checksum) return -APKE_V2PKG_FORMAT;
break;
}
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);
return 0;
}
static int apk_extract_v2_entry(void *pctx, const struct apk_file_info *fi, struct apk_istream *is)
{
struct apk_extract_ctx *ectx = pctx;
struct apk_sign_ctx *sctx = ectx->pctx;
int r;
r = apk_sign_ctx_process_file(sctx, fi, is);
if (r <= 0) return r;
ectx->metadata = sctx->control_started && !sctx->data_started;
ectx->metadata_verified = sctx->control_verified;
r = ectx->cb ? ectx->cb(ectx, fi, is) : 0;
if (ectx->metadata && strcmp(fi->name, ".PKGINFO") == 0) {
// Parse the .PKGINFO for the data, in case the callback did not do it.
apk_blob_t l, r, token = APK_BLOB_STR("\n");
while (apk_istream_get_delim(is, token, &l) == 0) {
if (l.len < 1 || l.ptr[0] == '#') continue;
if (apk_blob_split(l, APK_BLOB_STR(" = "), &l, &r))
apk_extract_v2_control(ectx, l, r);
}
}
return r;
}
int apk_extract_v2(struct apk_extract_ctx *ectx, struct apk_istream *is)
{
struct apk_ctx *ac = ectx->ac;
struct apk_trust *trust = apk_ctx_get_trust(ac);
struct apk_sign_ctx sctx;
int r, action;
if (ectx->generate_identity)
action = trust->allow_untrusted ? APK_SIGN_GENERATE : APK_SIGN_VERIFY_AND_GENERATE;
else if (ectx->identity)
action = trust->allow_untrusted ? APK_SIGN_NONE : APK_SIGN_VERIFY_IDENTITY;
else
action = trust->allow_untrusted ? APK_SIGN_NONE : APK_SIGN_VERIFY;
ectx->pctx = &sctx;
apk_sign_ctx_init(&sctx, action, ectx->identity, trust);
r = apk_tar_parse(
apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &sctx),
apk_extract_v2_entry, ectx, apk_ctx_get_id_cache(ac));
if (ectx->generate_identity) *ectx->identity = sctx.identity;
apk_sign_ctx_free(&sctx);
apk_extract_reset(ectx);
return r;
}
void apk_extract_v2_control(struct apk_extract_ctx *ectx, apk_blob_t l, apk_blob_t r)
{
struct apk_sign_ctx *sctx = ectx->pctx;
if (!sctx || !sctx->control_started || sctx->data_started) return;
if (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)));
}
}

16
src/extract_v3.c Normal file
View File

@ -0,0 +1,16 @@
/* extract_v3.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "apk_context.h"
#include "apk_extract.h"
int apk_extract_v3(struct apk_extract_ctx *ectx, struct apk_istream *is)
{
return apk_istream_close_error(is, -APKE_FORMAT_NOT_SUPPORTED);
}

View File

@ -14,15 +14,18 @@ libapk_src = [
'context.c',
'crypto_openssl.c',
'database.c',
'extract.c',
'extract_v2.c',
'extract_v3.c',
'hash.c',
'io.c',
'io_archive.c',
'io_url.c',
'io_gunzip.c',
'package.c',
'pathbuilder.c',
'print.c',
'solver.c',
'tar.c',
'trust.c',
'version.c',
]
@ -30,11 +33,11 @@ libapk_src = [
libapk_headers = [
'apk_applet.h',
'apk_atom.h',
'apk_archive.h',
'apk_blob.h',
'apk_crypto.h',
'apk_database.h',
'apk_defines.h',
'apk_extract.h',
'apk_hash.h',
'apk_io.h',
'apk_openssl.h',
@ -44,6 +47,7 @@ libapk_headers = [
'apk_provider_data.h',
'apk_solver_data.h',
'apk_solver.h',
'apk_tar.h',
'apk_version.h',
]

View File

@ -23,10 +23,10 @@
#include <openssl/pem.h>
#include "apk_defines.h"
#include "apk_archive.h"
#include "apk_package.h"
#include "apk_database.h"
#include "apk_print.h"
#include "apk_extract.h"
const apk_spn_match_def apk_spn_dependency_comparer = {
[7] = (1<<4) /*<*/ | (1<<5) /*=*/ | (1<<6) /*<*/,
@ -470,301 +470,10 @@ int apk_script_type(const char *name)
return APK_SCRIPT_INVALID;
}
void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action,
struct apk_checksum *identity, struct apk_trust *trust)
{
memset(ctx, 0, sizeof(struct apk_sign_ctx));
ctx->trust = trust;
ctx->action = action;
ctx->allow_untrusted = trust->allow_untrusted;
switch (action) {
case APK_SIGN_VERIFY:
/* If we're only verifing, we're going to start with a
* signature section, which we don't need a hash of */
ctx->md = EVP_md_null();
break;
case APK_SIGN_VERIFY_IDENTITY:
/* If we're checking the package against a particular hash,
* we need to start with that hash, because there may not
* be a signature section to deduce it from */
ctx->md = EVP_sha1();
memcpy(&ctx->identity, identity, sizeof(ctx->identity));
break;
case APK_SIGN_GENERATE:
case APK_SIGN_VERIFY_AND_GENERATE:
ctx->md = EVP_sha1();
break;
default:
ctx->action = APK_SIGN_NONE;
ctx->md = EVP_md_null();
ctx->control_started = 1;
ctx->data_started = 1;
break;
}
ctx->mdctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx->mdctx, ctx->md, NULL);
EVP_MD_CTX_set_flags(ctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
}
void apk_sign_ctx_free(struct apk_sign_ctx *ctx)
{
if (ctx->signature.data.ptr != NULL)
free(ctx->signature.data.ptr);
EVP_MD_CTX_free(ctx->mdctx);
}
static int check_signing_key_trust(struct apk_sign_ctx *sctx)
{
switch (sctx->action) {
case APK_SIGN_VERIFY:
case APK_SIGN_VERIFY_AND_GENERATE:
if (sctx->signature.pkey == NULL) {
if (sctx->allow_untrusted)
break;
return -APKE_SIGNATURE_UNTRUSTED;
}
}
return 0;
}
int apk_sign_ctx_process_file(struct apk_sign_ctx *ctx,
const struct apk_file_info *fi,
struct apk_istream *is)
{
static struct {
char type[8];
unsigned int nid;
} signature_type[] = {
{ "RSA512", NID_sha512 },
{ "RSA256", NID_sha256 },
{ "RSA", NID_sha1 },
{ "DSA", NID_dsa },
};
const EVP_MD *md = NULL;
const char *name = NULL;
struct apk_pkey *pkey;
int r, i;
if (ctx->data_started)
return 1;
if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
/* APKv1.0 compatibility - first non-hidden file is
* considered to start the data section of the file.
* This does not make any sense if the file has v2.0
* style .PKGINFO */
if (ctx->has_data_checksum)
return -APKE_V2PKG_FORMAT;
/* Error out early if identity part is missing */
if (ctx->action == APK_SIGN_VERIFY_IDENTITY)
return -APKE_V2PKG_FORMAT;
ctx->data_started = 1;
ctx->control_started = 1;
r = check_signing_key_trust(ctx);
if (r < 0)
return r;
return 1;
}
if (ctx->control_started)
return 1;
if (strncmp(fi->name, ".SIGN.", 6) != 0) {
ctx->control_started = 1;
return 1;
}
/* By this point, we must be handling a signature file */
ctx->num_signatures++;
/* Already found a signature by a trusted key; no need to keep searching */
if ((ctx->action != APK_SIGN_VERIFY &&
ctx->action != APK_SIGN_VERIFY_AND_GENERATE) ||
ctx->signature.pkey != NULL)
return 0;
for (i = 0; i < ARRAY_SIZE(signature_type); i++) {
size_t slen = strlen(signature_type[i].type);
if (strncmp(&fi->name[6], signature_type[i].type, slen) == 0 &&
fi->name[6+slen] == '.') {
md = EVP_get_digestbynid(signature_type[i].nid);
name = &fi->name[6+slen+1];
break;
}
}
if (!md) return 0;
pkey = apk_trust_key_by_name(ctx->trust, name);
if (pkey) {
ctx->md = md;
ctx->signature.pkey = pkey->key;
ctx->signature.data = apk_blob_from_istream(is, fi->size);
}
return 0;
}
int apk_sign_ctx_parse_pkginfo_line(void *ctx, apk_blob_t line)
{
struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
apk_blob_t l, r;
if (!sctx->control_started || sctx->data_started)
return 0;
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 (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;
int r;
r = apk_sign_ctx_process_file(ctx, fi, is);
if (r <= 0)
return r;
if (!ctx->control_started || ctx->data_started)
return 0;
if (strcmp(fi->name, ".PKGINFO") == 0) {
apk_blob_t l, token = APK_BLOB_STR("\n");
while (apk_istream_get_delim(is, token, &l) == 0)
apk_sign_ctx_parse_pkginfo_line(ctx, l);
}
return 0;
}
/* apk_sign_ctx_mpart_cb() handles hashing archives and checking signatures, but
it can't do it alone. apk_sign_ctx_process_file() must be in the loop to
actually select which signature is to be verified and load the corresponding
public key into the context object, and apk_sign_ctx_parse_pkginfo_line()
needs to be called when handling the .PKGINFO file to find any applicable
datahash and load it into the context for this function to check against. */
int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
{
struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
unsigned char calculated[EVP_MAX_MD_SIZE];
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) {
if (part == APK_MPART_END)
return -APKE_V2PKG_FORMAT;
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 &&
part != APK_MPART_END)
goto update_digest;
/* Drool in the remainder of the digest block now, we will finish
* hashing it in all cases */
EVP_DigestUpdate(sctx->mdctx, data.ptr, data.len);
if (sctx->has_data_checksum && !end_of_control) {
/* End of data-block with a checksum read from the control block */
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 -APKE_V2PKG_INTEGRITY;
sctx->data_verified = 1;
if (!sctx->allow_untrusted && !sctx->control_verified)
return -APKE_SIGNATURE_UNTRUSTED;
return 0;
}
/* Either end of control block with a data checksum or end
* of the data block following a control block without a data
* checksum. In either case, we're checking a signature. */
r = check_signing_key_trust(sctx);
if (r < 0)
return r;
switch (sctx->action) {
case APK_SIGN_VERIFY:
case APK_SIGN_VERIFY_AND_GENERATE:
if (sctx->signature.pkey != NULL) {
r = EVP_VerifyFinal(sctx->mdctx,
(unsigned char *) sctx->signature.data.ptr,
sctx->signature.data.len,
sctx->signature.pkey);
if (r != 1 && !sctx->allow_untrusted)
return -APKE_SIGNATURE_INVALID;
} else {
r = 0;
if (!sctx->allow_untrusted)
return -APKE_SIGNATURE_UNTRUSTED;
}
if (r == 1) {
sctx->control_verified = 1;
if (!sctx->has_data_checksum && part == APK_MPART_END)
sctx->data_verified = 1;
}
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);
}
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 -APKE_V2PKG_INTEGRITY;
sctx->control_verified = 1;
if (!sctx->has_data_checksum && part == APK_MPART_END)
sctx->data_verified = 1;
break;
case APK_SIGN_GENERATE:
/* 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;
break;
}
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);
return 0;
}
struct read_info_ctx {
struct apk_database *db;
struct apk_package *pkg;
struct apk_sign_ctx *sctx;
struct apk_extract_ctx ectx;
};
int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
@ -840,7 +549,7 @@ int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
return 0;
}
static int read_info_line(void *ctx, apk_blob_t line)
static int read_info_line(struct read_info_ctx *ri, apk_blob_t line)
{
static struct {
const char *str;
@ -862,7 +571,6 @@ static int read_info_line(void *ctx, apk_blob_t line)
{ "commit", 'c' },
{ "provider_priority", 'k' },
};
struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
apk_blob_t l, r;
int i;
@ -872,36 +580,32 @@ static int read_info_line(void *ctx, apk_blob_t line)
if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
return 0;
apk_extract_v2_control(&ri->ectx, l, r);
for (i = 0; i < ARRAY_SIZE(fields); i++) {
if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
return 0;
}
}
apk_sign_ctx_parse_pkginfo_line(ri->sctx, line);
return 0;
}
static int read_info_entry(void *ctx, const struct apk_file_info *ae,
struct apk_istream *is)
static int apk_pkg_parse_entry(struct apk_extract_ctx *ectx, const struct apk_file_info *ae,
struct apk_istream *is)
{
struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
struct read_info_ctx *ri = container_of(ectx, struct read_info_ctx, ectx);
struct apk_package *pkg = ri->pkg;
int r;
r = apk_sign_ctx_process_file(ri->sctx, ae, is);
if (r <= 0)
return r;
if (!ri->sctx->control_started || ri->sctx->data_started)
return 0;
if (ectx->metadata_verified) return -ECANCELED;
if (!ectx->metadata) return 0;
if (strcmp(ae->name, ".PKGINFO") == 0) {
/* APK 2.0 format */
apk_blob_t l, token = APK_BLOB_STR("\n");
while (apk_istream_get_delim(is, token, &l) == 0)
read_info_line(ctx, l);
read_info_line(ri, l);
} else if (strcmp(ae->name, ".INSTALL") == 0) {
apk_warn(&ri->db->ctx->out,
"Package '%s-" BLOB_FMT "' contains deprecated .INSTALL",
@ -911,8 +615,7 @@ static int read_info_entry(void *ctx, const struct apk_file_info *ae,
return 0;
}
int apk_pkg_read(struct apk_database *db, const char *file,
struct apk_sign_ctx *sctx, struct apk_package **pkg)
int apk_pkg_read(struct apk_database *db, const char *file, struct apk_package **pkg)
{
struct read_info_ctx ctx;
struct apk_file_info fi;
@ -924,25 +627,22 @@ int apk_pkg_read(struct apk_database *db, const char *file,
memset(&ctx, 0, sizeof(ctx));
ctx.db = db;
ctx.sctx = sctx;
ctx.pkg = apk_pkg_new();
r = -ENOMEM;
if (ctx.pkg == NULL)
goto err;
ctx.pkg->size = fi.size;
apk_extract_init(&ctx.ectx, db->ctx, apk_pkg_parse_entry);
apk_extract_generate_identity(&ctx.ectx, &ctx.pkg->csum);
r = apk_tar_parse(
apk_istream_gunzip_mpart(apk_istream_from_file(AT_FDCWD, file), apk_sign_ctx_mpart_cb, sctx),
read_info_entry, &ctx, db->id_cache);
r = apk_extract(&ctx.ectx, apk_istream_from_file(AT_FDCWD, file));
if (r < 0 && r != -ECANCELED)
goto err;
if (ctx.pkg->name == NULL || ctx.pkg->uninstallable) {
r = -ENOTSUP;
goto err;
}
if (sctx->action != APK_SIGN_VERIFY)
ctx.pkg->csum = sctx->identity;
ctx.pkg->filename = strdup(file);
ctx.pkg = apk_db_pkg_add(db, ctx.pkg);

View File

@ -39,6 +39,7 @@ const char *apk_error_str(int error)
case APKE_SIGNATURE_FAIL: return "signing failure";
case APKE_SIGNATURE_UNTRUSTED: return "UNTRUSTED signature";
case APKE_SIGNATURE_INVALID: return "BAD signature";
case APKE_FORMAT_NOT_SUPPORTED: return "file format not supported (in this applet)";
case APKE_ADB_COMPRESSION: return "ADB compression not supported";
case APKE_ADB_HEADER: return "ADB header error";
case APKE_ADB_VERSION: return "incompatible ADB version";

View File

@ -1,4 +1,4 @@
/* io_archive.c - Alpine Package Keeper (APK)
/* tar.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
@ -7,27 +7,11 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
#include <stdio.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <utime.h>
#include <string.h>
#include <unistd.h>
#include <sysexits.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <sys/sysmacros.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include "apk_defines.h"
#include "apk_print.h"
#include "apk_archive.h"
#include "apk_openssl.h"
#include "apk_tar.h"
struct tar_header {
/* ustar header, Posix 1003.1 */
@ -331,123 +315,3 @@ int apk_tar_write_padding(struct apk_ostream *os, const struct apk_file_info *ae
return 0;
}
int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
const char *extract_name, const char *link_target,
struct apk_istream *is,
apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx,
unsigned int extract_flags, struct apk_out *out)
{
struct apk_xattr *xattr;
const char *fn = extract_name ?: ae->name;
int fd, r = -1, atflags = 0, ret = 0;
if (!(extract_flags & APK_EXTRACTF_NO_OVERWRITE)) {
if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno;
}
switch (ae->mode & S_IFMT) {
case S_IFDIR:
r = mkdirat(atfd, fn, ae->mode & 07777);
if (r < 0 && errno != EEXIST)
ret = -errno;
break;
case S_IFREG:
if (ae->link_target == NULL) {
int flags = O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC | O_EXCL;
int fd = openat(atfd, fn, flags, ae->mode & 07777);
if (fd < 0) {
ret = -errno;
break;
}
struct apk_ostream *os = apk_ostream_to_fd(fd);
if (IS_ERR(os)) {
ret = PTR_ERR(os);
break;
}
apk_stream_copy(is, os, ae->size, cb, cb_ctx, dctx);
r = apk_ostream_close(os);
if (r < 0) {
unlinkat(atfd, fn, 0);
ret = r;
}
} else {
r = linkat(atfd, link_target ?: ae->link_target, atfd, fn, 0);
if (r < 0) ret = -errno;
}
break;
case S_IFLNK:
r = symlinkat(link_target ?: ae->link_target, atfd, fn);
if (r < 0) ret = -errno;
atflags |= AT_SYMLINK_NOFOLLOW;
break;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
r = mknodat(atfd, fn, ae->mode, ae->device);
if (r < 0) ret = -errno;
break;
}
if (ret) {
apk_err(out, "Failed to create %s: %s", ae->name, strerror(-ret));
return ret;
}
if (!(extract_flags & APK_EXTRACTF_NO_CHOWN)) {
r = fchownat(atfd, fn, ae->uid, ae->gid, atflags);
if (r < 0) {
apk_err(out, "Failed to set ownership on %s: %s",
fn, strerror(errno));
if (!ret) ret = -errno;
}
/* chown resets suid bit so we need set it again */
if (ae->mode & 07000) {
r = fchmodat(atfd, fn, ae->mode & 07777, atflags);
if (r < 0) {
apk_err(out, "Failed to set file permissions on %s: %s",
fn, strerror(errno));
if (!ret) ret = -errno;
}
}
}
/* extract xattrs */
if (!S_ISLNK(ae->mode) && ae->xattrs && ae->xattrs->num) {
r = 0;
fd = openat(atfd, fn, O_RDWR);
if (fd >= 0) {
foreach_array_item(xattr, ae->xattrs) {
if (fsetxattr(fd, xattr->name, xattr->value.ptr, xattr->value.len, 0) < 0) {
r = -errno;
if (r != -ENOTSUP) break;
}
}
close(fd);
} else {
r = -errno;
}
if (r) {
if (r != -ENOTSUP)
apk_err(out, "Failed to set xattrs on %s: %s",
fn, strerror(-r));
if (!ret) ret = r;
}
}
if (!S_ISLNK(ae->mode)) {
/* preserve modification time */
struct timespec times[2];
times[0].tv_sec = times[1].tv_sec = ae->mtime;
times[0].tv_nsec = times[1].tv_nsec = 0;
r = utimensat(atfd, fn, times, atflags);
if (r < 0) {
apk_err(out, "Failed to preserve modification time on %s: %s",
fn, strerror(errno));
if (!ret || ret == -ENOTSUP) ret = -errno;
}
}
return ret;
}