Skip to content

Commit 5b0b81e

Browse files
committed
lib: rewrite signature verification
- modernize code with good error messages. - do not use mmap to read 512 byte signature files. - preparing for swappable signature algorithms - remove usage of deprecated openssl functions - open public key when opening repo to avoid reading it every time its needed
1 parent 5509734 commit 5b0b81e

File tree

5 files changed

+385
-149
lines changed

5 files changed

+385
-149
lines changed

include/xbps.h.in

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,17 +1465,17 @@ struct xbps_repo {
14651465
*/
14661466
const char *uri;
14671467
/**
1468-
* var is_remote
1469-
*
14701468
* True if repository is remote, false if it's a local repository.
14711469
*/
14721470
bool is_remote;
14731471
/**
1474-
* var is_signed
1475-
*
14761472
* True if this repository has been signed, false otherwise.
14771473
*/
14781474
bool is_signed;
1475+
/**
1476+
* Loaded trusted public-key.
1477+
*/
1478+
struct pubkey *pubkey;
14791479
};
14801480

14811481
void xbps_rpool_release(struct xbps_handle *xhp);
@@ -1910,31 +1910,6 @@ bool xbps_file_sha256_raw(unsigned char *dst, size_t len, const char *file);
19101910
*/
19111911
int xbps_file_sha256_check(const char *file, const char *sha256);
19121912

1913-
/**
1914-
* Verifies the RSA signature \a sigfile against \a digest with the
1915-
* RSA public-key associated in \a repo.
1916-
*
1917-
* @param[in] repo Repository to use with the RSA public key associated.
1918-
* @param[in] sigfile The signature file name used to verify \a digest.
1919-
* @param[in] digest The digest to verify.
1920-
*
1921-
* @return True if the signature is valid, false otherwise.
1922-
*/
1923-
bool xbps_verify_signature(struct xbps_repo *repo, const char *sigfile,
1924-
unsigned char *digest);
1925-
1926-
/**
1927-
* Verifies the RSA signature of \a fname with the RSA public-key associated
1928-
* in \a repo.
1929-
*
1930-
* @param[in] repo Repository to use with the RSA public key associated.
1931-
* @param[in] fname The filename to verify, the signature file must have a .sig2
1932-
* extension, i.e `<fname>.sig2`.
1933-
*
1934-
* @return True if the signature is valid, false otherwise.
1935-
*/
1936-
bool xbps_verify_file_signature(struct xbps_repo *repo, const char *fname);
1937-
19381913
/**
19391914
* Checks if a package is currently installed in pkgdb by matching \a pkg.
19401915
* To be installed, the pkg must be in "installed" or "unpacked" state.

include/xbps_api_impl.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,15 @@ struct xbps_repo HIDDEN *xbps_regget_repo(struct xbps_handle *,
127127
const char *);
128128
int HIDDEN xbps_conf_init(struct xbps_handle *);
129129

130+
struct pubkey;
131+
struct pubkey HIDDEN *pubkey_load_rsa(xbps_data_t data);
132+
void HIDDEN pubkey_free(struct pubkey *pubkey);
133+
int pubkey_verify(const struct pubkey *pubkey, const unsigned char *sig,
134+
unsigned int siglen, const unsigned char *md, size_t mdlen);
135+
136+
int HIDDEN repo_sig_verify_file(struct xbps_repo *repo, const char *file,
137+
const char *sigfile);
138+
int HIDDEN repo_sig_verify_digest(struct xbps_repo *repo, const char *sigfile,
139+
const unsigned char *md, size_t mdlen);
140+
130141
#endif /* !_XBPS_API_IMPL_H_ */

lib/repo.c

Lines changed: 193 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
#include <stdlib.h>
3535
#include <string.h>
3636

37+
#include <openssl/sha.h>
38+
3739
#include <archive.h>
3840
#include <archive_entry.h>
3941

@@ -164,6 +166,99 @@ repo_read_index(struct xbps_repo *repo, struct archive *ar)
164166
return 0;
165167
}
166168

169+
static int
170+
repo_load_key(struct xbps_repo *repo)
171+
{
172+
char keypath[PATH_MAX];
173+
xbps_dictionary_t trusted_keyd;
174+
xbps_data_t pubkey, trusted_pubkey;
175+
uint16_t pubkey_size, trusted_pubkey_size;
176+
char *hexfp;
177+
int r;
178+
179+
if (!repo->idxmeta)
180+
return 0;
181+
182+
pubkey = xbps_dictionary_get(repo->idxmeta, "public-key");
183+
if (xbps_object_type(pubkey) != XBPS_TYPE_DATA)
184+
return 0;
185+
186+
if (!xbps_dictionary_get_uint16(
187+
repo->idxmeta, "public-key-size", &pubkey_size)) {
188+
xbps_error_printf(
189+
"%s: failed to load public key: missing public key size\n",
190+
repo->uri);
191+
return -EINVAL;
192+
}
193+
194+
hexfp = xbps_pubkey2fp(pubkey);
195+
if (!hexfp) {
196+
r = errno ? -errno : -EINVAL;
197+
xbps_error_printf(
198+
"%s: failed to create public key fingerprint: %s\n",
199+
repo->uri, strerror(-r));
200+
return r;
201+
}
202+
203+
repo->is_signed = true;
204+
205+
r = snprintf(keypath, sizeof(keypath), "%s/keys/%s.plist",
206+
repo->xhp->metadir, hexfp);
207+
if (r < 0 || (size_t)r >= sizeof(keypath)) {
208+
r = -ENAMETOOLONG;
209+
xbps_error_printf("%s: failed get public key path: %s\n",
210+
repo->uri, strerror(-r));
211+
return r;
212+
}
213+
214+
trusted_keyd = xbps_plist_dictionary_from_file(keypath);
215+
if (!trusted_keyd) {
216+
if (errno == ENOENT)
217+
return 0;
218+
r = errno ? -errno : -EINVAL;
219+
xbps_error_printf("failed to read public key: %s: %s\n",
220+
keypath, strerror(-r));
221+
return r;
222+
}
223+
224+
trusted_pubkey = xbps_dictionary_get(trusted_keyd, "public-key");
225+
if (xbps_object_type(trusted_pubkey) != XBPS_TYPE_DATA) {
226+
r = -EINVAL;
227+
xbps_error_printf(
228+
"failed to read public key: %s: missing public-key\n",
229+
keypath);
230+
return r;
231+
}
232+
if (!xbps_dictionary_get_uint16(
233+
trusted_keyd, "public-key-size", &trusted_pubkey_size)) {
234+
r = -EINVAL;
235+
xbps_error_printf(
236+
"failed to read public key: %s: missing public-key-size\n",
237+
keypath);
238+
xbps_object_release(trusted_keyd);
239+
return r;
240+
}
241+
242+
if (pubkey_size != trusted_pubkey_size ||
243+
!xbps_data_equals(pubkey, trusted_pubkey)) {
244+
r = -ERANGE;
245+
xbps_error_printf("%s: repository public-key does not "
246+
"match trusted public key: %s\n",
247+
repo->uri, keypath);
248+
xbps_object_release(trusted_keyd);
249+
return r;
250+
}
251+
252+
repo->pubkey = pubkey_load_rsa(trusted_pubkey);
253+
if (!repo->pubkey) {
254+
xbps_object_release(trusted_keyd);
255+
return -errno;
256+
}
257+
258+
xbps_object_release(trusted_keyd);
259+
return 0;
260+
}
261+
167262
static int
168263
repo_read_meta(struct xbps_repo *repo, struct archive *ar)
169264
{
@@ -224,7 +319,6 @@ repo_read_meta(struct xbps_repo *repo, struct archive *ar)
224319
return r;
225320
}
226321

227-
repo->is_signed = true;
228322
xbps_dictionary_make_immutable(repo->idxmeta);
229323
return 0;
230324
}
@@ -511,6 +605,13 @@ xbps_repo_open(struct xbps_handle *xhp, const char *url)
511605
return NULL;
512606
}
513607

608+
r = repo_load_key(repo);
609+
if (r < 0) {
610+
xbps_repo_release(repo);
611+
errno = -r;
612+
return NULL;
613+
}
614+
514615
if (xbps_dictionary_count(repo->stage) == 0 ||
515616
(repo->is_remote && !(xhp->flags & XBPS_FLAG_USE_STAGE))) {
516617
repo->idx = repo->index;
@@ -550,6 +651,10 @@ xbps_repo_release(struct xbps_repo *repo)
550651
xbps_object_release(repo->stage);
551652
repo->idx = NULL;
552653
}
654+
if (repo->pubkey) {
655+
pubkey_free(repo->pubkey);
656+
repo->pubkey = NULL;
657+
}
553658
if (repo->idxmeta) {
554659
xbps_object_release(repo->idxmeta);
555660
repo->idxmeta = NULL;
@@ -861,3 +966,90 @@ xbps_repo_key_import(struct xbps_repo *repo)
861966
free(rkeyfile);
862967
return rv;
863968
}
969+
970+
static int
971+
read_signature(const char *path, unsigned char *buf, size_t bufsz)
972+
{
973+
size_t used = 0;
974+
int fd;
975+
int r;
976+
977+
fd = open(path, O_RDONLY);
978+
if (fd == -1) {
979+
r = -errno;
980+
xbps_error_printf("failed to read signature file: could not "
981+
"open: %s: %s\n",
982+
path, strerror(-r));
983+
return r;
984+
}
985+
986+
for (;;) {
987+
ssize_t rd;
988+
989+
rd = read(fd, buf + used, bufsz - used);
990+
if (rd == -1) {
991+
if (errno == EINTR || errno == EAGAIN)
992+
continue;
993+
r = -errno;
994+
xbps_error_printf("failed to read signature file: %s: "
995+
"could not read: %s\n",
996+
path, strerror(-r));
997+
close(fd);
998+
return r;
999+
}
1000+
used += rd;
1001+
if (rd == 0 || used == bufsz)
1002+
break;
1003+
}
1004+
1005+
close(fd);
1006+
1007+
if (used < bufsz) {
1008+
xbps_error_printf(
1009+
"failed to read signature: file too short\n");
1010+
return -EINVAL;
1011+
}
1012+
return 0;
1013+
}
1014+
1015+
int HIDDEN
1016+
repo_sig_verify_digest(struct xbps_repo *repo, const char *sigfile,
1017+
const unsigned char *md, size_t mdlen)
1018+
{
1019+
unsigned char sigbuf[512];
1020+
int r;
1021+
1022+
if (!repo->pubkey) {
1023+
xbps_error_printf("failed to verify file: repository has no "
1024+
"trusted public key\n");
1025+
return -EPERM;
1026+
}
1027+
1028+
r = read_signature(sigfile, sigbuf, sizeof(sigbuf));
1029+
if (r < 0)
1030+
return r;
1031+
1032+
return pubkey_verify(repo->pubkey, sigbuf, sizeof(sigbuf), md, mdlen);
1033+
}
1034+
1035+
int HIDDEN
1036+
repo_sig_verify_file(struct xbps_repo *repo, const char *file, const char *sigfile)
1037+
{
1038+
unsigned char md[SHA256_DIGEST_LENGTH];
1039+
1040+
if (!repo->pubkey) {
1041+
xbps_error_printf("failed to verify file: repository has no "
1042+
"trusted public key\n");
1043+
return -EPERM;
1044+
}
1045+
1046+
if (!xbps_file_sha256_raw(md, sizeof(md), file)) {
1047+
int r = -errno;
1048+
xbps_error_printf("failed to verify file: failed to "
1049+
"generate sha256 checksum: %s\n",
1050+
strerror(-r));
1051+
return r;
1052+
}
1053+
1054+
return repo_sig_verify_digest(repo, sigfile, md, sizeof(md));
1055+
}

0 commit comments

Comments
 (0)