Commit eca9c222 authored by Timo Teräs's avatar Timo Teräs

db: signed index loading (ref #46)

prefer index in the new format as signed .tar.gz.
parent 99be653f
......@@ -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) {
......
......@@ -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);
......
......@@ -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,
......
......@@ -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;
......
......@@ -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,
......
......@@ -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)
......
......@@ -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);
......
......@@ -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);
......
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment