Verify repodata signature

This commit is contained in:
Piotr Wójcik 2019-10-01 21:18:25 +02:00 committed by Juan RP
parent 04194f44c8
commit a7830cf780
7 changed files with 102 additions and 27 deletions

View File

@ -110,7 +110,7 @@
*/ */
#define XBPS_PKGFILES "files.plist" #define XBPS_PKGFILES "files.plist"
/** /**
* @def XBPS_REPOIDX * @def XBPS_REPOIDX
* Filename for the repository index property list. * Filename for the repository index property list.
*/ */
@ -1897,6 +1897,22 @@ int xbps_file_hash_check(const char *file, const char *sha256);
bool xbps_verify_signature(struct xbps_repo *repo, const char *sigfile, bool xbps_verify_signature(struct xbps_repo *repo, const char *sigfile,
unsigned char *digest); unsigned char *digest);
/**
* Verifies the RSA signature \sig_buf of bytes with hash \a digest
* with the RSA public-key associated in \a repo.
*
* @param[in] repo Repository to use with the RSA public key associated.
* @param[in] idxmeta Meta dictionary related to \a repo, containing
* public-key field.
* @param[in] sig_buf The signature of file content that has hash \digest.
* @param[in] sigfilelen The length of signature.
* @param[in] digest The digest of file content to verify.
*
* @return True if the signature is valid, false otherwise.
*/
bool xbps_verify_digest_signature(struct xbps_repo *repo, xbps_dictionary_t idxmeta,
unsigned char *sig_buf, size_t sigfilelen, unsigned char *digest);
/** /**
* Verifies the RSA signature of \a fname with the RSA public-key associated * Verifies the RSA signature of \a fname with the RSA public-key associated
* in \a repo. * in \a repo.

View File

@ -149,7 +149,7 @@ int HIDDEN xbps_register_pkg(struct xbps_handle *, xbps_dictionary_t);
void HIDDEN xbps_transaction_conflicts(struct xbps_handle *, xbps_array_t); void HIDDEN xbps_transaction_conflicts(struct xbps_handle *, xbps_array_t);
char HIDDEN *xbps_archive_get_file(struct archive *, struct archive_entry *); char HIDDEN *xbps_archive_get_file(struct archive *, struct archive_entry *);
xbps_dictionary_t HIDDEN xbps_archive_get_dictionary(struct archive *, xbps_dictionary_t HIDDEN xbps_archive_get_dictionary(struct archive *,
struct archive_entry *); struct archive_entry *, char **bytes);
const char HIDDEN *vpkg_user_conf(struct xbps_handle *, const char *, bool); const char HIDDEN *vpkg_user_conf(struct xbps_handle *, const char *, bool);
xbps_array_t HIDDEN xbps_get_pkg_fulldeptree(struct xbps_handle *, xbps_array_t HIDDEN xbps_get_pkg_fulldeptree(struct xbps_handle *,
const char *, bool); const char *, bool);

View File

@ -56,7 +56,7 @@ xbps_archive_get_file(struct archive *ar, struct archive_entry *entry)
} }
xbps_dictionary_t HIDDEN xbps_dictionary_t HIDDEN
xbps_archive_get_dictionary(struct archive *ar, struct archive_entry *entry) xbps_archive_get_dictionary(struct archive *ar, struct archive_entry *entry, char **bytes)
{ {
xbps_dictionary_t d = NULL; xbps_dictionary_t d = NULL;
char *buf; char *buf;
@ -66,7 +66,11 @@ xbps_archive_get_dictionary(struct archive *ar, struct archive_entry *entry)
/* If blob is already a dictionary we are done */ /* If blob is already a dictionary we are done */
d = xbps_dictionary_internalize(buf); d = xbps_dictionary_internalize(buf);
free(buf); if (bytes == NULL) {
free(buf);
} else {
*bytes = buf;
}
return d; return d;
} }

View File

@ -180,13 +180,13 @@ unpack_archive(struct xbps_handle *xhp,
goto out; goto out;
} }
} else if (strcmp("./props.plist", entry_pname) == 0) { } else if (strcmp("./props.plist", entry_pname) == 0) {
binpkg_propsd = xbps_archive_get_dictionary(ar, entry); binpkg_propsd = xbps_archive_get_dictionary(ar, entry, NULL);
if (binpkg_propsd == NULL) { if (binpkg_propsd == NULL) {
rv = EINVAL; rv = EINVAL;
goto out; goto out;
} }
} else if (strcmp("./files.plist", entry_pname) == 0) { } else if (strcmp("./files.plist", entry_pname) == 0) {
binpkg_filesd = xbps_archive_get_dictionary(ar, entry); binpkg_filesd = xbps_archive_get_dictionary(ar, entry, NULL);
if (binpkg_filesd == NULL) { if (binpkg_filesd == NULL) {
rv = EINVAL; rv = EINVAL;
goto out; goto out;

View File

@ -61,11 +61,48 @@ xbps_repo_path_with_name(struct xbps_handle *xhp, const char *url, const char *n
url, xhp->target_arch ? xhp->target_arch : xhp->native_arch, name); url, xhp->target_arch ? xhp->target_arch : xhp->native_arch, name);
} }
static bool
repo_verify_index(struct xbps_repo *repo, unsigned char *digest) {
bool verified = false;
unsigned char *sig_buf = NULL;
size_t sigfilelen = 0;
struct archive_entry *entry;
if (archive_read_next_header(repo->ar, &entry) != ARCHIVE_OK) {
xbps_dbg_printf(repo->xhp,
"%s: read_next_header %s\n", repo->uri,
archive_error_string(repo->ar));
return false;
}
if (strcmp(archive_entry_pathname(entry), XBPS_REPOIDX_SIG) != 0) {
xbps_dbg_printf(repo->xhp,
"%s: no signature of %s\n", repo->uri, XBPS_REPOIDX);
return false;
}
sigfilelen = (size_t)archive_entry_size(entry);
sig_buf = (unsigned char *) xbps_archive_get_file(repo->ar, entry);
if (sig_buf == NULL) {
return false;
}
verified = xbps_verify_digest_signature(repo, sig_buf, sigfilelen, digest);
free(sig_buf);
return verified;
}
static xbps_dictionary_t static xbps_dictionary_t
repo_get_dict(struct xbps_repo *repo) repo_get_dict(struct xbps_repo *repo, bool *verified)
{ {
struct archive_entry *entry; struct archive_entry *entry;
int rv; int rv;
xbps_dictionary_t dict;
char *bytes = NULL;
unsigned char *digest = NULL;
if (verified != NULL)
*verified = false;
if (repo->ar == NULL) if (repo->ar == NULL)
return NULL; return NULL;
@ -77,7 +114,15 @@ repo_get_dict(struct xbps_repo *repo)
archive_error_string(repo->ar)); archive_error_string(repo->ar));
return NULL; return NULL;
} }
return xbps_archive_get_dictionary(repo->ar, entry); dict = xbps_archive_get_dictionary(repo->ar, entry, &bytes);
if (verified != NULL &&
bytes != NULL &&
(digest = xbps_buffer_hash_raw(bytes, strlen(bytes))) != NULL &&
repo_verify_index(repo, digest))
*verified = true;
free(digest);
free(bytes);
return dict;
} }
bool bool
@ -135,6 +180,7 @@ repo_open_local(struct xbps_repo *repo, const char *repofile)
{ {
struct stat st; struct stat st;
int rv = 0; int rv = 0;
bool verified = false;
if (fstat(repo->fd, &st) == -1) { if (fstat(repo->fd, &st) == -1) {
rv = errno; rv = errno;
@ -158,7 +204,7 @@ repo_open_local(struct xbps_repo *repo, const char *repofile)
repofile, strerror(rv)); repofile, strerror(rv));
return false; return false;
} }
if ((repo->idx = repo_get_dict(repo)) == NULL) { if ((repo->idx = repo_get_dict(repo, &verified)) == NULL) {
xbps_dbg_printf(repo->xhp, "[repo] `%s' failed to internalize " xbps_dbg_printf(repo->xhp, "[repo] `%s' failed to internalize "
" index on archive, removing file.\n", repofile); " index on archive, removing file.\n", repofile);
/* broken archive, remove it */ /* broken archive, remove it */
@ -166,13 +212,13 @@ repo_open_local(struct xbps_repo *repo, const char *repofile)
return false; return false;
} }
xbps_dictionary_make_immutable(repo->idx); xbps_dictionary_make_immutable(repo->idx);
repo->idxmeta = repo_get_dict(repo); repo->idxmeta = repo_get_dict(repo, NULL);
if (repo->idxmeta != NULL) { if (repo->idxmeta != NULL) {
repo->is_signed = true; repo->is_signed = true;
xbps_dictionary_make_immutable(repo->idxmeta); xbps_dictionary_make_immutable(repo->idxmeta);
} }
return true; return verified || (!repo->is_remote && !repo->is_signed);
} }
static bool static bool

View File

@ -732,7 +732,7 @@ collect_binpkg_files(struct xbps_handle *xhp, xbps_dictionary_t pkg_repod,
entry_pname = archive_entry_pathname(entry); entry_pname = archive_entry_pathname(entry);
if ((strcmp("./files.plist", entry_pname)) == 0) { if ((strcmp("./files.plist", entry_pname)) == 0) {
filesd = xbps_archive_get_dictionary(ar, entry); filesd = xbps_archive_get_dictionary(ar, entry, NULL);
if (filesd == NULL) { if (filesd == NULL) {
rv = EINVAL; rv = EINVAL;
goto out; goto out;

View File

@ -72,28 +72,25 @@ rsa_verify_hash(struct xbps_repo *repo, xbps_data_t pubkey,
} }
bool bool
xbps_verify_signature(struct xbps_repo *repo, const char *sigfile, xbps_verify_digest_signature(struct xbps_repo *repo, xbps_dictionary_t idxmeta,
unsigned char *digest) unsigned char *sig_buf, size_t sigfilelen, unsigned char *digest)
{ {
xbps_dictionary_t repokeyd = NULL; xbps_dictionary_t repokeyd = NULL;
xbps_data_t pubkey; xbps_data_t pubkey;
char *hexfp = NULL; char *hexfp = NULL;
unsigned char *sig_buf = NULL;
size_t sigbuflen, sigfilelen;
char *rkeyfile = NULL; char *rkeyfile = NULL;
bool val = false; bool val = false;
if (!xbps_dictionary_count(repo->idxmeta)) { if (!xbps_dictionary_count(idxmeta)) {
xbps_dbg_printf(repo->xhp, "%s: unsigned repository\n", repo->uri); xbps_dbg_printf(repo->xhp, "%s: unsigned repository\n", repo->uri);
return false; return false;
} }
hexfp = xbps_pubkey2fp(repo->xhp, hexfp = xbps_pubkey2fp(repo->xhp,
xbps_dictionary_get(repo->idxmeta, "public-key")); xbps_dictionary_get(idxmeta, "public-key"));
if (hexfp == NULL) { if (hexfp == NULL) {
xbps_dbg_printf(repo->xhp, "%s: incomplete signed repo, missing hexfp obj\n", repo->uri); xbps_dbg_printf(repo->xhp, "%s: incomplete signed repo, missing hexfp obj\n", repo->uri);
return false; return false;
} }
/* /*
* Prepare repository RSA public key to verify fname signature. * Prepare repository RSA public key to verify fname signature.
*/ */
@ -108,12 +105,6 @@ xbps_verify_signature(struct xbps_repo *repo, const char *sigfile,
pubkey = xbps_dictionary_get(repokeyd, "public-key"); pubkey = xbps_dictionary_get(repokeyd, "public-key");
if (xbps_object_type(pubkey) != XBPS_TYPE_DATA) if (xbps_object_type(pubkey) != XBPS_TYPE_DATA)
goto out; goto out;
if (!xbps_mmap_file(sigfile, (void *)&sig_buf, &sigbuflen, &sigfilelen)) {
xbps_dbg_printf(repo->xhp, "can't open signature file %s: %s\n",
sigfile, strerror(errno));
goto out;
}
/* /*
* Verify fname RSA signature. * Verify fname RSA signature.
*/ */
@ -125,14 +116,32 @@ out:
free(hexfp); free(hexfp);
if (rkeyfile) if (rkeyfile)
free(rkeyfile); free(rkeyfile);
if (sig_buf)
(void)munmap(sig_buf, sigbuflen);
if (repokeyd) if (repokeyd)
xbps_object_release(repokeyd); xbps_object_release(repokeyd);
return val; return val;
} }
bool
xbps_verify_signature(struct xbps_repo *repo, const char *sigfile,
unsigned char *digest)
{
unsigned char *sig_buf = NULL;
size_t sigbuflen, sigfilelen;
bool result = false;
if (xbps_mmap_file(sigfile, (void *)&sig_buf, &sigbuflen, &sigfilelen)) {
result = xbps_verify_digest_signature(repo, repo->idxmeta, sig_buf, sigfilelen, digest);
} else {
xbps_dbg_printf(repo->xhp, "can't open signature file %s: %s\n",
sigfile, strerror(errno));
}
if (sig_buf)
(void)munmap(sig_buf, sigbuflen);
return result;
}
bool bool
xbps_verify_file_signature(struct xbps_repo *repo, const char *fname) xbps_verify_file_signature(struct xbps_repo *repo, const char *fname)
{ {