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
parent
2d4e88aeb1
commit
9c843e4ecd
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "apk_adb.h"
|
||||
#include "apk_applet.h"
|
||||
#include "apk_tar.h"
|
||||
|
||||
struct conv_script {
|
||||
struct list_head script_node;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
121
src/database.c
121
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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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',
|
||||
]
|
||||
|
||||
|
|
330
src/package.c
330
src/package.c
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue