diff --git a/src/add.c b/src/add.c index c05d257..fac1edc 100644 --- a/src/add.c +++ b/src/add.c @@ -119,7 +119,7 @@ static int add_main(void *ctx, int argc, char **argv) struct apk_package *pkg; struct apk_sign_ctx sctx; - apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY); + apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL); pkg = apk_pkg_read(&db, argv[i], &sctx); apk_sign_ctx_free(&sctx); if (pkg == NULL) { diff --git a/src/apk_database.h b/src/apk_database.h index 979d0e8..4cf8b14 100644 --- a/src/apk_database.h +++ b/src/apk_database.h @@ -21,6 +21,7 @@ #define APK_CACHE_CSUM_BYTES 4 extern const char * const apk_index_gz; +extern const char * const apkindex_tar_gz; struct apk_name; APK_ARRAY(apk_name_array, struct apk_name *); @@ -144,7 +145,7 @@ int apk_db_index_write(struct apk_database *db, struct apk_ostream *os); int apk_db_add_repository(apk_database_t db, apk_blob_t repository); int apk_repository_update(struct apk_database *db, struct apk_repository *repo); int apk_cache_download(struct apk_database *db, struct apk_checksum *csum, - const char *url, const char *item); + const char *url, const char *item, int verify); int apk_cache_exists(struct apk_database *db, struct apk_checksum *csum, const char *item); diff --git a/src/apk_package.h b/src/apk_package.h index f77637a..c816633 100644 --- a/src/apk_package.h +++ b/src/apk_package.h @@ -30,9 +30,11 @@ struct apk_name; #define APK_PKG_NOT_INSTALLED 0 #define APK_PKG_INSTALLED 1 -#define APK_SIGN_VERIFY 0 -#define APK_SIGN_GENERATE_V1 1 -#define APK_SIGN_GENERATE 2 +#define APK_SIGN_NONE 0 +#define APK_SIGN_VERIFY 1 +#define APK_SIGN_VERIFY_IDENTITY 2 +#define APK_SIGN_GENERATE_V1 3 +#define APK_SIGN_GENERATE 4 struct apk_sign_ctx { int action; @@ -93,11 +95,14 @@ 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); +void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action, + struct apk_checksum *identity); 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_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); int apk_deps_add(struct apk_dependency_array **depends, diff --git a/src/archive.c b/src/archive.c index 717c67c..b069f2d 100644 --- a/src/archive.c +++ b/src/archive.c @@ -104,12 +104,17 @@ static size_t tar_entry_read(void *stream, void *ptr, size_t size) return size; } +static void tar_entry_close(void *stream) +{ +} + int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser, void *ctx) { struct apk_file_info entry; struct apk_tar_entry_istream teis = { .is.read = tar_entry_read, + .is.close = tar_entry_close, .tar_is = is, }; struct tar_header buf; diff --git a/src/cache.c b/src/cache.c index 7bcd636..7b5b7f1 100644 --- a/src/cache.c +++ b/src/cache.c @@ -54,7 +54,7 @@ static int cache_download(struct apk_database *db) continue; r = apk_cache_download(db, &pkg->csum, db->repos[i].url, - pkgfile); + pkgfile, APK_SIGN_VERIFY_IDENTITY); if (r != 0) return r; } @@ -91,7 +91,8 @@ static int cache_clean(struct apk_database *db) apk_blob_pull_hexdump(&b, APK_BLOB_BUF(csum)); apk_blob_pull_char(&b, '.'); - if (apk_blob_compare(b, APK_BLOB_STR(apk_index_gz)) == 0) { + if (apk_blob_compare(b, APK_BLOB_STR(apk_index_gz)) == 0 || + apk_blob_compare(b, APK_BLOB_STR(apkindex_tar_gz)) == 0) { /* Index - check for matching repository */ for (i = 0; i < db->num_repos; i++) { if (memcmp(db->repos[i].csum.data, diff --git a/src/database.c b/src/database.c index db8f521..331357a 100644 --- a/src/database.c +++ b/src/database.c @@ -25,7 +25,9 @@ #include "apk_database.h" #include "apk_state.h" #include "apk_applet.h" +#include "apk_archive.h" +const char * const apkindex_tar_gz = "APKINDEX.tar.gz"; const char * const apk_index_gz = "APK_INDEX.gz"; static const char * const apk_static_cache_dir = "var/lib/apk"; static const char * const apk_linked_cache_dir = "etc/apk/cache"; @@ -1034,12 +1036,13 @@ static struct apk_bstream *apk_repository_file_open(struct apk_repository *repo, } int apk_cache_download(struct apk_database *db, struct apk_checksum *csum, - const char *url, const char *item) + const char *url, const char *item, int verify) { char tmp[256], tmp2[256]; int r; - snprintf(tmp, sizeof(tmp), "%s/%s", url, item); + snprintf(tmp, sizeof(tmp), "%s%s%s", + url, url[strlen(url)-1] == '/' ? "" : "/", item); apk_message("fetch %s", tmp); if (apk_flags & APK_SIMULATE) @@ -1050,6 +1053,24 @@ int apk_cache_download(struct apk_database *db, struct apk_checksum *csum, if (r < 0) return r; + if (verify != APK_SIGN_NONE) { + struct apk_istream *is; + struct apk_sign_ctx sctx; + int ok; + + apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL); + is = apk_bstream_gunzip_mpart(apk_bstream_from_file(tmp2), + apk_sign_ctx_mpart_cb, &sctx); + is->close(is); + r = apk_tar_parse(is, apk_sign_ctx_verify_tar, &sctx); + ok = (r != 0) && sctx.control_verified && sctx.data_verified; + apk_sign_ctx_free(&sctx); + if (!ok) { + unlink(tmp2); + return -10; + } + } + apk_db_cache_get_name(tmp, sizeof(tmp), db, csum, item, FALSE); if (rename(tmp2, tmp) < 0) return -errno; @@ -1069,12 +1090,59 @@ int apk_cache_exists(struct apk_database *db, struct apk_checksum *csum, return access(tmp, R_OK | W_OK) == 0; } +static int apk_cache_delete(struct apk_database *db, struct apk_checksum *csum, + const char *item) +{ + char tmp[256]; + + if (db->root == NULL) + return 0; + + apk_db_cache_get_name(tmp, sizeof(tmp), db, csum, item, FALSE); + return unlink(tmp); +} + int apk_repository_update(struct apk_database *db, struct apk_repository *repo) { + int r; + if (repo->csum.type == APK_CHECKSUM_NONE) return 0; - return apk_cache_download(db, &repo->csum, repo->url, apk_index_gz); + r = apk_cache_download(db, &repo->csum, repo->url, apkindex_tar_gz, + APK_SIGN_VERIFY); + if (r == 0 || r == -10) { + apk_cache_delete(db, &repo->csum, apk_index_gz); + return r; + } + + return apk_cache_download(db, &repo->csum, repo->url, apk_index_gz, + APK_SIGN_NONE); +} + +struct apkindex_ctx { + struct apk_database *db; + struct apk_sign_ctx sctx; + int repo; +}; + +static int load_apkindex(void *sctx, const struct apk_file_info *fi, + struct apk_istream *is) +{ + struct apkindex_ctx *ctx = (struct apkindex_ctx *) sctx; + struct apk_bstream *bs; + + if (apk_sign_ctx_process_file(&ctx->sctx, fi, is) == 0) + return 0; + + if (strcmp(fi->name, "APKINDEX") != 0) + return 0; + + bs = apk_bstream_from_istream(is); + apk_db_index_read(ctx->db, bs, ctx->repo); + bs->close(bs, NULL); + + return 0; } int apk_db_add_repository(apk_database_t _db, apk_blob_t repository) @@ -1082,7 +1150,7 @@ int apk_db_add_repository(apk_database_t _db, apk_blob_t repository) struct apk_database *db = _db.db; struct apk_bstream *bs = NULL; struct apk_repository *repo; - int r, n = 1; + int r, targz = 1; if (repository.ptr == NULL || *repository.ptr == '\0' || *repository.ptr == '#') @@ -1102,26 +1170,44 @@ int apk_db_add_repository(apk_database_t _db, apk_blob_t repository) apk_blob_checksum(repository, apk_default_checksum(), &repo->csum); if (apk_flags & APK_UPDATE_CACHE) - n = apk_repository_update(db, repo); + apk_repository_update(db, repo); - bs = apk_db_cache_open(db, &repo->csum, apk_index_gz); + bs = apk_db_cache_open(db, &repo->csum, apkindex_tar_gz); if (bs == NULL) { - if (n == 1) - n = apk_repository_update(db, repo); - if (n < 0) - return n; bs = apk_db_cache_open(db, &repo->csum, apk_index_gz); + targz = 0; } } else { - bs = apk_repository_file_open(repo, apk_index_gz); + bs = apk_repository_file_open(repo, apkindex_tar_gz); + if (bs == NULL) { + bs = apk_repository_file_open(repo, apk_index_gz); + targz = 0; + } } - bs = apk_bstream_from_istream(apk_bstream_gunzip(bs)); if (bs == NULL) { apk_warning("Failed to open index for %s", repo->url); return -1; } - apk_db_index_read(db, bs, r); - bs->close(bs, NULL); + if (targz) { + struct apk_istream *is; + struct apkindex_ctx ctx; + + ctx.db = db; + ctx.repo = r; + apk_sign_ctx_init(&ctx.sctx, APK_SIGN_VERIFY, NULL); + is = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, &ctx.sctx); + r = apk_tar_parse(is, load_apkindex, &ctx); + is->close(is); + apk_sign_ctx_free(&ctx.sctx); + if (!ctx.sctx.data_verified) { + apk_error("Bad repository signature: %s", repo->url); + return -1; + } + } else { + bs = apk_bstream_from_istream(apk_bstream_gunzip(bs)); + apk_db_index_read(db, bs, r); + bs->close(bs, NULL); + } return 0; } @@ -1396,7 +1482,7 @@ static int apk_db_unpack_pkg(struct apk_database *db, .cb = cb, .cb_ctx = cb_ctx, }; - apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY); + apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY_IDENTITY, &newpkg->csum); tar = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, &sctx); apk_sign_ctx_free(&sctx); if (apk_tar_parse(tar, apk_db_install_archive_entry, &ctx) != 0) diff --git a/src/index.c b/src/index.c index c3124d8..2a7365e 100644 --- a/src/index.c +++ b/src/index.c @@ -159,7 +159,7 @@ static int index_main(void *ctx, int argc, char **argv) if (!found) { struct apk_sign_ctx sctx; - apk_sign_ctx_init(&sctx, ictx->method); + apk_sign_ctx_init(&sctx, ictx->method, NULL); if (apk_pkg_read(&db, argv[i], &sctx) != NULL) newpkgs++; apk_sign_ctx_free(&sctx); diff --git a/src/package.c b/src/package.c index df4922b..28f7f6e 100644 --- a/src/package.c +++ b/src/package.c @@ -256,14 +256,31 @@ int apk_script_type(const char *name) return APK_SCRIPT_INVALID; } -void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action) +void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action, + struct apk_checksum *identity) { memset(ctx, 0, sizeof(struct apk_sign_ctx)); ctx->action = action; switch (action) { + case APK_SIGN_NONE: + ctx->md = EVP_md_null(); + ctx->control_started = 1; + ctx->data_started = 1; + break; case APK_SIGN_VERIFY: ctx->md = EVP_md_null(); break; + case APK_SIGN_VERIFY_IDENTITY: + if (identity->type == APK_CHECKSUM_MD5) { + ctx->md = EVP_md5(); + ctx->control_started = 1; + ctx->data_started = 1; + ctx->has_data_checksum = 1; + } else { + ctx->md = EVP_sha1(); + } + memcpy(&ctx->identity, identity, sizeof(ctx->identity)); + break; case APK_SIGN_GENERATE_V1: ctx->md = EVP_md5(); ctx->control_started = 1; @@ -341,6 +358,46 @@ int apk_sign_ctx_process_file(struct apk_sign_ctx *ctx, return 0; } +static int read_datahash(void *ctx, apk_blob_t line) +{ + struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx; + apk_blob_t l, r; + + 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 (sctx->data_started == 0 && + 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; + + if (apk_sign_ctx_process_file(ctx, fi, is) == 0) + return 0; + + if (strcmp(fi->name, ".PKGINFO") == 0) { + apk_blob_t blob = apk_blob_from_istream(is, fi->size); + apk_blob_for_each_segment(blob, "\n", read_datahash, ctx); + free(blob.ptr); + } + + return 0; +} + int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data) { struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx; @@ -370,6 +427,8 @@ int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data) /* End of control block, make sure rest is handled as data */ sctx->data_started = 1; + if (!sctx->has_data_checksum) + return 0; /* Verify the signature if we have public key */ if (sctx->action == APK_SIGN_VERIFY && @@ -385,8 +444,7 @@ int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data) EVP_DigestInit_ex(&sctx->mdctx, sctx->md, NULL); EVP_MD_CTX_set_flags(&sctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT); return 0; - } else if (sctx->action == APK_SIGN_GENERATE && - sctx->has_data_checksum) { + } else if (sctx->action == APK_SIGN_GENERATE) { /* Package identity is checksum of control block */ sctx->identity.type = EVP_MD_CTX_size(&sctx->mdctx); EVP_DigestFinal_ex(&sctx->mdctx, sctx->identity.data, NULL); @@ -396,29 +454,37 @@ int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data) EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL); EVP_DigestInit_ex(&sctx->mdctx, sctx->md, NULL); EVP_MD_CTX_set_flags(&sctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT); + + if (sctx->action == APK_SIGN_VERIFY_IDENTITY) { + if (memcmp(calculated, sctx->identity.data, + sctx->identity.type) == 0) + sctx->control_verified = 1; + return 1; + } } break; case APK_MPART_END: - if (sctx->action == APK_SIGN_VERIFY) { - if (sctx->has_data_checksum) { - /* Check that data checksum matches */ - 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) - sctx->data_verified = 1; - } else if (sctx->signature.pkey != NULL) { - /* Assume that the data is fully signed */ - r = EVP_VerifyFinal(&sctx->mdctx, - (unsigned char *) sctx->signature.data.ptr, - sctx->signature.data.len, - sctx->signature.pkey); - if (r == 1) { - sctx->control_verified = 1; - sctx->data_verified = 1; - } + if (sctx->has_data_checksum) { + /* Check that data checksum matches */ + 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) + sctx->data_verified = 1; + } else if (sctx->action == APK_SIGN_VERIFY) { + if (sctx->signature.pkey == NULL) + return 1; + + /* Assume that the data is fully signed */ + r = EVP_VerifyFinal(&sctx->mdctx, + (unsigned char *) sctx->signature.data.ptr, + sctx->signature.data.len, + sctx->signature.pkey); + if (r == 1) { + sctx->control_verified = 1; + sctx->data_verified = 1; } - } else if (!sctx->has_data_checksum) { + } else { /* Package identity is checksum of all data */ sctx->identity.type = EVP_MD_CTX_size(&sctx->mdctx); EVP_DigestFinal_ex(&sctx->mdctx, sctx->identity.data, NULL); diff --git a/src/verify.c b/src/verify.c index 2fed4bd..1edf4e7 100644 --- a/src/verify.c +++ b/src/verify.c @@ -16,14 +16,16 @@ static int verify_main(void *ctx, int argc, char **argv) { - struct apk_database db; struct apk_sign_ctx sctx; - int i, ok, rc = 0; + struct apk_istream *is; + int i, r, ok, rc = 0; - apk_db_open(&db, NULL, APK_OPENF_NO_STATE); for (i = 0; i < argc; i++) { - apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY); - apk_pkg_read(&db, argv[i], &sctx); + apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL); + is = apk_bstream_gunzip_mpart(apk_bstream_from_file(argv[i]), + apk_sign_ctx_mpart_cb, &sctx); + r = apk_tar_parse(is, apk_sign_ctx_verify_tar, &sctx); + is->close(is); ok = sctx.control_verified && sctx.data_verified; if (apk_verbosity >= 1) apk_message("%s: %s", argv[i], @@ -33,7 +35,6 @@ static int verify_main(void *ctx, int argc, char **argv) rc++; apk_sign_ctx_free(&sctx); } - apk_db_close(&db); return rc; }