summaryrefslogtreecommitdiff
path: root/src/package.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/package.c')
-rw-r--r--src/package.c330
1 files changed, 15 insertions, 315 deletions
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 <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);