diff options
Diffstat (limited to 'src/package.c')
-rw-r--r-- | src/package.c | 330 |
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); |