From 9c843e4ecdfee916ec835b5d35c10b3818aba9e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Mon, 26 Jul 2021 16:25:03 +0300 Subject: [PATCH] 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. --- src/Makefile | 2 +- src/apk_archive.h | 35 ---- src/apk_database.h | 3 +- src/apk_defines.h | 1 + src/apk_extract.h | 64 +++++++ src/apk_package.h | 42 +---- src/apk_tar.h | 22 +++ src/app_add.c | 7 +- src/app_cache.c | 3 +- src/app_convdb.c | 1 + src/app_convndx.c | 24 +-- src/app_extract.c | 17 +- src/app_index.c | 58 +++--- src/app_manifest.c | 35 ++-- src/app_mkndx.c | 34 ++-- src/app_mkpkg.c | 3 +- src/app_verify.c | 30 +--- src/database.c | 121 +++++-------- src/extract.c | 147 +++++++++++++++ src/extract_v2.c | 347 ++++++++++++++++++++++++++++++++++++ src/extract_v3.c | 16 ++ src/meson.build | 8 +- src/package.c | 330 ++-------------------------------- src/print.c | 1 + src/{io_archive.c => tar.c} | 140 +-------------- 25 files changed, 743 insertions(+), 748 deletions(-) delete mode 100644 src/apk_archive.h create mode 100644 src/apk_extract.h create mode 100644 src/apk_tar.h create mode 100644 src/extract.c create mode 100644 src/extract_v2.c create mode 100644 src/extract_v3.c rename src/{io_archive.c => tar.c} (71%) diff --git a/src/Makefile b/src/Makefile index 970354b..f7ebb66 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 diff --git a/src/apk_archive.h b/src/apk_archive.h deleted file mode 100644 index 3dbd284..0000000 --- a/src/apk_archive.h +++ /dev/null @@ -1,35 +0,0 @@ -/* apk_archive.c - Alpine Package Keeper (APK) - * - * Copyright (C) 2005-2008 Natanael Copa - * Copyright (C) 2008-2011 Timo Teräs - * All rights reserved. - * - * SPDX-License-Identifier: GPL-2.0-only - */ - -#ifndef APK_ARCHIVE -#define APK_ARCHIVE - -#include -#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 diff --git a/src/apk_database.h b/src/apk_database.h index 94507bc..e65a1de 100644 --- a/src/apk_database.h +++ b/src/apk_database.h @@ -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, diff --git a/src/apk_defines.h b/src/apk_defines.h index 3716802..52fef0d 100644 --- a/src/apk_defines.h +++ b/src/apk_defines.h @@ -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, diff --git a/src/apk_extract.h b/src/apk_extract.h new file mode 100644 index 0000000..cc8bae7 --- /dev/null +++ b/src/apk_extract.h @@ -0,0 +1,64 @@ +/* apk_extract.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2005-2008 Natanael Copa + * Copyright (C) 2008-2021 Timo Teräs + * 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 diff --git a/src/apk_package.h b/src/apk_package.h index 0f7542c..c6545c2 100644 --- a/src/apk_package.h +++ b/src/apk_package.h @@ -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); diff --git a/src/apk_tar.h b/src/apk_tar.h new file mode 100644 index 0000000..c3d951c --- /dev/null +++ b/src/apk_tar.h @@ -0,0 +1,22 @@ +/* apk_tar.h - Alpine Package Keeper (APK) + * + * Copyright (C) 2005-2008 Natanael Copa + * Copyright (C) 2008-2011 Timo Teräs + * 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 diff --git a/src/app_add.c b/src/app_add.c index b4fb356..345fd7a 100644 --- a/src/app_add.c +++ b/src/app_add.c @@ -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; diff --git a/src/app_cache.c b/src/app_cache.c index db562cb..b744f25 100644 --- a/src/app_cache.c +++ b/src/app_cache.c @@ -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++; diff --git a/src/app_convdb.c b/src/app_convdb.c index f30d0f7..5e60115 100644 --- a/src/app_convdb.c +++ b/src/app_convdb.c @@ -5,6 +5,7 @@ #include "apk_adb.h" #include "apk_applet.h" +#include "apk_tar.h" struct conv_script { struct list_head script_node; diff --git a/src/app_convndx.c b/src/app_convndx.c index 21015cb..d405b03 100644 --- a/src/app_convndx.c +++ b/src/app_convndx.c @@ -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; } diff --git a/src/app_extract.c b/src/app_extract.c index 89f3f5c..dd410e5 100644 --- a/src/app_extract.c +++ b/src/app_extract.c @@ -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); diff --git a/src/app_index.c b/src/app_index.c index cc37fa4..b614283 100644 --- a/src/app_index.c +++ b/src/app_index.c @@ -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); diff --git a/src/app_manifest.c b/src/app_manifest.c index 86164f6..ff63d7c 100644 --- a/src/app_manifest.c +++ b/src/app_manifest.c @@ -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)); } diff --git a/src/app_mkndx.c b/src/app_mkndx.c index 3316de5..c95c494 100644 --- a/src/app_mkndx.c +++ b/src/app_mkndx.c @@ -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++; } diff --git a/src/app_mkpkg.c b/src/app_mkpkg.c index e68a426..b16ec2f 100644 --- a/src/app_mkpkg.c +++ b/src/app_mkpkg.c @@ -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; diff --git a/src/app_verify.c b/src/app_verify.c index adfe0ec..fd1a148 100644 --- a/src/app_verify.c +++ b/src/app_verify.c @@ -12,38 +12,26 @@ #include #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; diff --git a/src/database.c b/src/database.c index 2a18a47..9ed0085 100644 --- a/src/database.c +++ b/src/database.c @@ -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; diff --git a/src/extract.c b/src/extract.c new file mode 100644 index 0000000..c5169be --- /dev/null +++ b/src/extract.c @@ -0,0 +1,147 @@ +/* extract.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2005-2008 Natanael Copa + * Copyright (C) 2008-2011 Timo Teräs + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include +#include +#include + +#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); +} diff --git a/src/extract_v2.c b/src/extract_v2.c new file mode 100644 index 0000000..217a07d --- /dev/null +++ b/src/extract_v2.c @@ -0,0 +1,347 @@ +/* extract_v2.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2005-2008 Natanael Copa + * Copyright (C) 2008-2011 Timo Teräs + * 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))); + } +} diff --git a/src/extract_v3.c b/src/extract_v3.c new file mode 100644 index 0000000..c6e80df --- /dev/null +++ b/src/extract_v3.c @@ -0,0 +1,16 @@ +/* extract_v3.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2005-2008 Natanael Copa + * Copyright (C) 2008-2011 Timo Teräs + * 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); +} diff --git a/src/meson.build b/src/meson.build index 98b2461..3bdb477 100644 --- a/src/meson.build +++ b/src/meson.build @@ -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', ] diff --git a/src/package.c b/src/package.c index 9686286..9e272b6 100644 --- a/src/package.c +++ b/src/package.c @@ -23,10 +23,10 @@ #include #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); diff --git a/src/print.c b/src/print.c index 1248995..ea66b90 100644 --- a/src/print.c +++ b/src/print.c @@ -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"; diff --git a/src/io_archive.c b/src/tar.c similarity index 71% rename from src/io_archive.c rename to src/tar.c index 85f095b..c84ab29 100644 --- a/src/io_archive.c +++ b/src/tar.c @@ -1,4 +1,4 @@ -/* io_archive.c - Alpine Package Keeper (APK) +/* tar.c - Alpine Package Keeper (APK) * * Copyright (C) 2005-2008 Natanael Copa * Copyright (C) 2008-2011 Timo Teräs @@ -7,27 +7,11 @@ * SPDX-License-Identifier: GPL-2.0-only */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include #include -#include #include -#include -#include -#include #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; -}