Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

signature fixups #306

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/xbps.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -1452,6 +1452,12 @@ struct xbps_repo {
* True if this repository has been signed, false otherwise.
*/
bool is_signed;
/**
* var pubkey
*
* The repositories public key.
*/
xbps_data_t pubkey;
};

void xbps_rpool_release(struct xbps_handle *xhp);
Expand Down
1 change: 1 addition & 0 deletions include/xbps_api_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ int HIDDEN xbps_transaction_pkg_deps(struct xbps_handle *, xbps_array_t, xbps_di

char HIDDEN *xbps_get_remote_repo_string(const char *);
int HIDDEN xbps_repo_sync(struct xbps_handle *, const char *);
xbps_data_t HIDDEN xbps_repo_pubkey(struct xbps_repo *repo);
int HIDDEN xbps_file_hash_check_dictionary(struct xbps_handle *,
xbps_dictionary_t, const char *, const char *);
int HIDDEN xbps_file_exec(struct xbps_handle *, const char *, ...);
Expand Down
62 changes: 62 additions & 0 deletions lib/repo.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,61 @@ xbps_repo_unlock(int lockfd, char *lockfname)
}
}

xbps_data_t
xbps_repo_pubkey(struct xbps_repo *repo)
{
char rkeyfile[PATH_MAX];
char *hexfp;
xbps_dictionary_t repokeyd;

if (repo->pubkey != NULL)
return repo->pubkey;

if (!repo->is_signed) {
errno = ENOENT;
return NULL;
}

if (repo->idxmeta == NULL || !xbps_dictionary_count(repo->idxmeta)) {
xbps_dbg_printf(repo->xhp,
"%s: missing or incomplete index-meta.plist\n", repo->uri);
errno = ENOENT;
return NULL;
}

hexfp = xbps_pubkey2fp(repo->xhp,
xbps_dictionary_get(repo->idxmeta, "public-key"));
if (hexfp == NULL) {
xbps_dbg_printf(repo->xhp, "%s: incomplete signed repo, missing hexfp obj\n", repo->uri);
errno = ENOENT;
return NULL;
}

snprintf(rkeyfile, sizeof rkeyfile, "%s/keys/%s.plist",
repo->xhp->metadir, hexfp);
free(hexfp);

repokeyd = xbps_plist_dictionary_from_file(repo->xhp, rkeyfile);
if (xbps_object_type(repokeyd) != XBPS_TYPE_DICTIONARY) {
xbps_dbg_printf(repo->xhp, "%s: cannot read pubkey data at %s: %s\n",
repo->uri, rkeyfile, strerror(errno));
return NULL;
}

repo->pubkey = xbps_dictionary_get(repokeyd, "public-key");
if (xbps_object_type(repo->pubkey) != XBPS_TYPE_DATA) {
xbps_dbg_printf(repo->xhp, "%s: bad pubkey file at %s: %s\n",
repo->uri, rkeyfile, strerror(errno));
repo->pubkey = NULL;
xbps_object_release(repokeyd);
errno = ENOTSUP;
return NULL;
}
xbps_object_retain(repo->pubkey);
xbps_object_release(repokeyd);
return repo->pubkey;
}

static bool
repo_open_local(struct xbps_repo *repo, const char *repofile)
{
Expand Down Expand Up @@ -163,6 +218,7 @@ repo_open_local(struct xbps_repo *repo, const char *repofile)
" index on archive, removing file.\n", repofile);
/* broken archive, remove it */
(void)unlink(repofile);
xbps_repo_close(repo);
return false;
}
xbps_dictionary_make_immutable(repo->idx);
Expand All @@ -171,6 +227,7 @@ repo_open_local(struct xbps_repo *repo, const char *repofile)
repo->is_signed = true;
xbps_dictionary_make_immutable(repo->idxmeta);
}

/*
* We don't need the archive anymore, we are only
* interested in the proplib dictionaries.
Expand Down Expand Up @@ -217,6 +274,7 @@ repo_open_with_type(struct xbps_handle *xhp, const char *url, const char *name)
repo->fd = -1;
repo->xhp = xhp;
repo->uri = url;
repo->pubkey = NULL;

if (xbps_repository_is_remote(url)) {
/* remote repository */
Expand Down Expand Up @@ -400,6 +458,10 @@ xbps_repo_release(struct xbps_repo *repo)
xbps_object_release(repo->idxmeta);
repo->idxmeta = NULL;
}
if (repo->pubkey != NULL) {
xbps_object_release(repo->pubkey);
repo->pubkey = NULL;
}
free(repo);
}

Expand Down
38 changes: 31 additions & 7 deletions lib/transaction_fetch.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,17 +209,41 @@ xbps_transaction_fetch(struct xbps_handle *xhp, xbps_object_iterator_t iter)
xbps_dictionary_get_cstring_nocopy(obj, "repository", &repoloc);

/*
* Download binary package and signature if either one
* of them don't exist.
* If this is a remote repository:
* - Make sure the public key exist.
* - Check if the package is not yet in the cache
* and add it to the fetch array to download later.
*/
if (xbps_repository_is_remote(repoloc) &&
!xbps_remote_binpkg_exists(xhp, obj)) {
if (!fetch && !(fetch = xbps_array_create())) {
if (xbps_repository_is_remote(repoloc)) {
struct xbps_repo *repo;

if ((repo = xbps_rpool_get_repo(repoloc)) == NULL) {
rv = errno;
xbps_error_printf("%s: repository not found in rpool\n",
repoloc);
goto out;
}
xbps_array_add(fetch, obj);
continue;
if (xbps_repo_pubkey(repo) == NULL) {
rv = errno;
xbps_error_printf("%s: failed to get public key: %s\n",
repoloc, strerror(errno));
goto out;
}

/*
* Download binary package and signature if either one
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enqueue downloading?

* of them don't exist.
*/
if (!xbps_remote_binpkg_exists(xhp, obj)) {
if (!fetch && !(fetch = xbps_array_create())) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Average transaction do fetch package, just creating array on top will be not only more readable, but also faster.

rv = errno;
goto out;
}
xbps_array_add(fetch, obj);

/* don't add it to the verify list, fetch implies that. */
continue;
}
}

/*
Expand Down
76 changes: 28 additions & 48 deletions lib/verifysig.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,20 @@
#include "xbps_api_impl.h"

static bool
rsa_verify_hash(struct xbps_repo *repo, xbps_data_t pubkey,
rsa_verify_hash(struct xbps_repo *repo,
unsigned char *sig, unsigned int siglen,
unsigned char *sha256)
{
BIO *bio;
RSA *rsa;
int rv;
xbps_data_t pubkey;

if ((pubkey = xbps_repo_pubkey(repo)) == NULL) {
xbps_error_printf("%s: failed to get public key: %s\n",
repo->uri, strerror(errno));
return false;
}

ERR_load_crypto_strings();
SSL_load_error_strings();
Expand Down Expand Up @@ -75,62 +82,35 @@ bool
xbps_verify_signature(struct xbps_repo *repo, const char *sigfile,
unsigned char *digest)
{
xbps_dictionary_t repokeyd = NULL;
xbps_data_t pubkey;
char *hexfp = NULL;
unsigned char *sig_buf = NULL;
size_t sigbuflen, sigfilelen;
char *rkeyfile = NULL;
bool val = false;
unsigned char buf[512];
struct stat st;
ssize_t rd = 0;
int fd = -1;

if (!xbps_dictionary_count(repo->idxmeta)) {
xbps_dbg_printf(repo->xhp, "%s: unsigned repository\n", repo->uri);
return false;
}
hexfp = xbps_pubkey2fp(repo->xhp,
xbps_dictionary_get(repo->idxmeta, "public-key"));
if (hexfp == NULL) {
xbps_dbg_printf(repo->xhp, "%s: incomplete signed repo, missing hexfp obj\n", repo->uri);
if ((fd = open(sigfile, O_RDONLY|O_CLOEXEC)) == -1 || fstat(fd, &st) == -1) {
xbps_error_printf("can't open signature file %s: %s\n",
sigfile, strerror(errno));
close(fd);
return false;
}

/*
* Prepare repository RSA public key to verify fname signature.
*/
rkeyfile = xbps_xasprintf("%s/keys/%s.plist", repo->xhp->metadir, hexfp);
repokeyd = xbps_plist_dictionary_from_file(repo->xhp, rkeyfile);
if (xbps_object_type(repokeyd) != XBPS_TYPE_DICTIONARY) {
xbps_dbg_printf(repo->xhp, "cannot read rkey data at %s: %s\n",
rkeyfile, strerror(errno));
goto out;
if (st.st_size != sizeof buf) {
xbps_error_printf("invalid signature file %s: size mismatch\n",
sigfile);
(void)close(fd);
return false;
}

pubkey = xbps_dictionary_get(repokeyd, "public-key");
if (xbps_object_type(pubkey) != XBPS_TYPE_DATA)
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",
rd = read(fd, buf, sizeof buf);
if (rd == -1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rd != sizeof buf, and errno ? errno : EIO ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess sigfile is required to have a size of 512 bytes, right?

It would be good if this could be a #define somewhere, and documented in comments. And I would just use rd != SIGFILE_SIZE, then.

xbps_error_printf("can't read signature file %s: %s\n",
sigfile, strerror(errno));
goto out;
close(fd);
return false;
}
/*
* Verify fname RSA signature.
*/
if (rsa_verify_hash(repo, pubkey, sig_buf, sigfilelen, digest))
val = true;

out:
if (hexfp)
free(hexfp);
if (rkeyfile)
free(rkeyfile);
if (sig_buf)
(void)munmap(sig_buf, sigbuflen);
if (repokeyd)
xbps_object_release(repokeyd);
close(fd);

return val;
return rsa_verify_hash(repo, buf, sizeof buf, digest);
}

bool
Expand Down