Skip to content

Commit 87509bb

Browse files
committed
Allow building rpm without OpenPGP support
For bootstrapping purposes, having rpm depend on Rust is painful, but directing people to unmaintained crypto code as an alternative is hair-raising. As a middle ground, let rpm be built without OpenPGP support at all, which at least gives you a functional rpm and rpm-build even if you can't sign or verify signatures. Achieving this is a moderately complex dance which can't meaningfully be split into multiple commits because everything is interconnected: Add a new WITH_SEQUOIA option to control use of Sequoia, on by default. When Sequoia is disabled, default to a newly added dummy PGP implementation instead which just returns error on everything. And finally, if the older WITH_INTERNAL_OPENPGP is enabled, use the old PGP implementation. As the intent is to cut out rpmpgp_legacy to a separate repository, sanity requires that we also split the openssl/libgcrypt code at the digest/signature fault line. It's not ideal, but the alternative of having unused crypto code on which an external component depends on is just not sustainable. This way, the signature side of things is quite neatly cut off with the PGP stuff.
1 parent 9d6a296 commit 87509bb

File tree

9 files changed

+455
-264
lines changed

9 files changed

+455
-264
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ option(WITH_AUDIT "Build with audit support" ON)
3030
option(WITH_FSVERITY "Build with fsverity support" OFF)
3131
option(WITH_IMAEVM "Build with IMA support" OFF)
3232
option(WITH_FAPOLICYD "Build with fapolicyd support" ON)
33+
option(WITH_SEQUOIA "Build with Sequoia OpenPGP support" ON)
34+
option(WITH_OPENSSL "Use openssl instead of libgcrypt for internal crypto" OFF)
3335
option(WITH_INTERNAL_OPENPGP "Use internal OpenPGP parser (DEPRECATED)" OFF)
3436
option(WITH_READLINE "Build with readline support" ON)
3537
option(WITH_BZIP2 "Build with bzip2 support" ON)

INSTALL

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ The source for the file utility + library is available from
2727
ftp://ftp.astron.com/pub/file/
2828

2929
You will need a cryptographic library to support digests and
30-
signatures. This depends on the OpenPGP parser used: the default is
31-
rpm-sequoia library (>= 1.3.0 required), which is available from
30+
signatures. rpm-sequoia (>= 1.3.0 required) is the most complete
31+
solution and also the default:
3232
https://github.com/rpm-software-management/rpm-sequoia
3333

3434
Use of rpm-sequoia is strongly recommended. Most importantly, the internal
@@ -38,9 +38,17 @@ in Sequoia. Some other Sequoia advantages include being implemented in a
3838
memory-safe language, configurable policy and user-relevant error messages.
3939
For more information, see https://sequoia-pgp.org/
4040

41-
If using the deprecated internal parser (-DWITH_INTERNAL_OPENPGP=ON),
42-
the default is libgcrypt, but alternatively OpenSSL can be used by
43-
additionally specifying -DWITH_OPENSSL=ON.
41+
However, for bootstrapping purposes it may be desireable to avoid the
42+
Rust dependency from rpm-sequoia. When building with -DWITH_SEQUOIA=OFF,
43+
rpm is built with OpenPGP support disabled. That means, you cannot
44+
sign packages, verify signatures or import keys, but you otherwise
45+
you can build (and install) packages normally. In this mode, libgcrypt
46+
is used for crypthographic hash calculations by default, but alternatively
47+
OpenSSL can be selected by specifying -DWITH_OPENSSL=ON.
48+
49+
Finally, the deprecated internal OpenPGP parser can be enabled with
50+
-DWITH_INTERNAL_OPENPGP=ON, which libgcrypt/OpenSSL depending on
51+
WITH_OPENSSL.
4452

4553
libgcrypt library is available from https://www.gnupg.org/software/libgcrypt/
4654

rpmio/CMakeLists.txt

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,26 @@ target_include_directories(librpmio PRIVATE
1313
${Intl_INCLUDE_DIRS}
1414
)
1515

16-
if (WITH_INTERNAL_OPENPGP)
17-
add_subdirectory(rpmpgp_legacy)
18-
target_link_libraries(librpmio PRIVATE rpmpgp_legacy)
19-
else()
16+
if (WITH_SEQUOIA)
2017
pkg_check_modules(RPMSEQUOIA REQUIRED IMPORTED_TARGET rpm-sequoia>=1.4.0)
2118
target_sources(librpmio PRIVATE rpmpgp_sequoia.c)
2219
target_link_libraries(librpmio PRIVATE PkgConfig::RPMSEQUOIA)
20+
else()
21+
if (WITH_INTERNAL_OPENPGP)
22+
add_subdirectory(rpmpgp_legacy)
23+
target_link_libraries(librpmio PRIVATE rpmpgp_legacy)
24+
else()
25+
target_sources(librpmio PRIVATE rpmpgp_dummy.c)
26+
endif()
27+
if (WITH_OPENSSL)
28+
find_package(OpenSSL 3.0.0 REQUIRED)
29+
target_sources(librpmio PRIVATE digest_openssl.c)
30+
target_link_libraries(librpmio PRIVATE OpenSSL::Crypto)
31+
else()
32+
pkg_check_modules(LIBGCRYPT REQUIRED IMPORTED_TARGET libgcrypt)
33+
target_sources(librpmio PRIVATE digest_libgcrypt.c)
34+
target_link_libraries(librpmio PRIVATE PkgConfig::LIBGCRYPT)
35+
endif()
2336
endif()
2437

2538
set_target_properties(librpmio PROPERTIES

rpmio/digest_libgcrypt.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#include "system.h"
2+
3+
#include <gcrypt.h>
4+
5+
#include <rpm/rpmcrypto.h>
6+
#include <rpm/rpmstring.h>
7+
8+
#include "debug.h"
9+
10+
/**
11+
* MD5/SHA1 digest private data.
12+
*/
13+
struct DIGEST_CTX_s {
14+
rpmDigestFlags flags; /*!< Bit(s) to control digest operation. */
15+
int algo; /*!< Used hash algorithm */
16+
gcry_md_hd_t h;
17+
};
18+
19+
20+
/**************************** init ************************************/
21+
22+
int rpmInitCrypto(void) {
23+
gcry_check_version (NULL);
24+
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
25+
return 0;
26+
}
27+
28+
int rpmFreeCrypto(void) {
29+
return 0;
30+
}
31+
32+
/**************************** digest ************************************/
33+
34+
size_t rpmDigestLength(int hashalgo)
35+
{
36+
switch (hashalgo) {
37+
case RPM_HASH_MD5:
38+
return 16;
39+
case RPM_HASH_SHA1:
40+
return 20;
41+
case RPM_HASH_SHA224:
42+
return 28;
43+
case RPM_HASH_SHA256:
44+
return 32;
45+
case RPM_HASH_SHA384:
46+
return 48;
47+
case RPM_HASH_SHA512:
48+
return 64;
49+
default:
50+
return 0;
51+
}
52+
}
53+
54+
static int hashalgo2gcryalgo(int hashalgo)
55+
{
56+
switch (hashalgo) {
57+
case RPM_HASH_MD5:
58+
return GCRY_MD_MD5;
59+
case RPM_HASH_SHA1:
60+
return GCRY_MD_SHA1;
61+
case RPM_HASH_SHA224:
62+
return GCRY_MD_SHA224;
63+
case RPM_HASH_SHA256:
64+
return GCRY_MD_SHA256;
65+
case RPM_HASH_SHA384:
66+
return GCRY_MD_SHA384;
67+
case RPM_HASH_SHA512:
68+
return GCRY_MD_SHA512;
69+
default:
70+
return 0;
71+
}
72+
}
73+
74+
DIGEST_CTX rpmDigestInit(int hashalgo, rpmDigestFlags flags)
75+
{
76+
gcry_md_hd_t h;
77+
DIGEST_CTX ctx;
78+
int gcryalgo = hashalgo2gcryalgo(hashalgo);
79+
80+
if (!gcryalgo || gcry_md_open(&h, gcryalgo, 0) != 0)
81+
return NULL;
82+
83+
ctx = xcalloc(1, sizeof(*ctx));
84+
ctx->flags = flags;
85+
ctx->algo = hashalgo;
86+
ctx->h = h;
87+
return ctx;
88+
}
89+
90+
int rpmDigestUpdate(DIGEST_CTX ctx, const void * data, size_t len)
91+
{
92+
if (ctx == NULL)
93+
return -1;
94+
gcry_md_write(ctx->h, data, len);
95+
return 0;
96+
}
97+
98+
int rpmDigestFinal(DIGEST_CTX ctx, void ** datap, size_t *lenp, int asAscii)
99+
{
100+
unsigned char *digest;
101+
int digestlen;
102+
if (ctx == NULL)
103+
return -1;
104+
digest = gcry_md_read(ctx->h, 0);
105+
digestlen = rpmDigestLength(ctx->algo);
106+
if (!asAscii) {
107+
if (lenp)
108+
*lenp = digestlen;
109+
if (datap) {
110+
*datap = xmalloc(digestlen);
111+
memcpy(*datap, digest, digestlen);
112+
}
113+
} else {
114+
if (lenp)
115+
*lenp = 2 * digestlen + 1;
116+
if (datap) {
117+
*datap = rpmhex((const uint8_t *)digest, digestlen);
118+
}
119+
}
120+
gcry_md_close(ctx->h);
121+
free(ctx);
122+
return 0;
123+
}
124+
125+
DIGEST_CTX rpmDigestDup(DIGEST_CTX octx)
126+
{
127+
DIGEST_CTX nctx = NULL;
128+
if (octx) {
129+
gcry_md_hd_t h;
130+
if (gcry_md_copy(&h, octx->h))
131+
return NULL;
132+
nctx = memcpy(xcalloc(1, sizeof(*nctx)), octx, sizeof(*nctx));
133+
nctx->h = h;
134+
}
135+
return nctx;
136+
}

rpmio/digest_openssl.c

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#include "system.h"
2+
3+
#include <openssl/evp.h>
4+
#include <openssl/core_names.h>
5+
#include <openssl/param_build.h>
6+
#include <rpm/rpmcrypto.h>
7+
#include <rpm/rpmstring.h>
8+
9+
struct DIGEST_CTX_s {
10+
rpmDigestFlags flags; /*!< Bit(s) to control digest operation. */
11+
int algo; /*!< Used hash algorithm */
12+
13+
EVP_MD_CTX *md_ctx; /* Digest context (opaque) */
14+
15+
};
16+
17+
/**************************** init ************************************/
18+
19+
int rpmInitCrypto(void) {
20+
return 0;
21+
}
22+
23+
int rpmFreeCrypto(void) {
24+
return 0;
25+
}
26+
27+
/**************************** digest ************************************/
28+
29+
DIGEST_CTX rpmDigestDup(DIGEST_CTX octx)
30+
{
31+
if (!octx) return NULL;
32+
33+
DIGEST_CTX nctx = NULL;
34+
nctx = xcalloc(1, sizeof(*nctx));
35+
36+
nctx->flags = octx->flags;
37+
nctx->algo = octx->algo;
38+
nctx->md_ctx = EVP_MD_CTX_new();
39+
if (!nctx->md_ctx) {
40+
free(nctx);
41+
return NULL;
42+
}
43+
44+
if (!EVP_MD_CTX_copy(nctx->md_ctx, octx->md_ctx)) {
45+
free(nctx);
46+
return NULL;
47+
}
48+
49+
return nctx;
50+
}
51+
52+
static const EVP_MD *getEVPMD(int hashalgo)
53+
{
54+
switch (hashalgo) {
55+
56+
case RPM_HASH_MD5:
57+
return EVP_md5();
58+
59+
case RPM_HASH_SHA1:
60+
return EVP_sha1();
61+
62+
case RPM_HASH_SHA256:
63+
return EVP_sha256();
64+
65+
case RPM_HASH_SHA384:
66+
return EVP_sha384();
67+
68+
case RPM_HASH_SHA512:
69+
return EVP_sha512();
70+
71+
case RPM_HASH_SHA224:
72+
return EVP_sha224();
73+
74+
default:
75+
return EVP_md_null();
76+
}
77+
}
78+
79+
size_t rpmDigestLength(int hashalgo)
80+
{
81+
return EVP_MD_size(getEVPMD(hashalgo));
82+
}
83+
84+
DIGEST_CTX rpmDigestInit(int hashalgo, rpmDigestFlags flags)
85+
{
86+
DIGEST_CTX ctx = xcalloc(1, sizeof(*ctx));
87+
88+
ctx->md_ctx = EVP_MD_CTX_new();
89+
if (!ctx->md_ctx) {
90+
free(ctx);
91+
return NULL;
92+
}
93+
94+
const EVP_MD *md = getEVPMD(hashalgo);
95+
if (md == EVP_md_null()) {
96+
free(ctx->md_ctx);
97+
free(ctx);
98+
return NULL;
99+
}
100+
101+
ctx->algo = hashalgo;
102+
ctx->flags = flags;
103+
if (!EVP_DigestInit_ex(ctx->md_ctx, md, NULL)) {
104+
free(ctx->md_ctx);
105+
free(ctx);
106+
return NULL;
107+
}
108+
109+
return ctx;
110+
}
111+
112+
int rpmDigestUpdate(DIGEST_CTX ctx, const void *data, size_t len)
113+
{
114+
if (ctx == NULL) return -1;
115+
116+
EVP_DigestUpdate(ctx->md_ctx, data, len);
117+
118+
return 0;
119+
}
120+
121+
int rpmDigestFinal(DIGEST_CTX ctx, void ** datap, size_t *lenp, int asAscii)
122+
{
123+
int ret;
124+
unsigned char *digest = NULL;
125+
unsigned int digestlen;
126+
127+
if (ctx == NULL) return -1;
128+
129+
digestlen = EVP_MD_CTX_size(ctx->md_ctx);
130+
digest = xcalloc(digestlen, sizeof(*digest));
131+
132+
ret = EVP_DigestFinal_ex(ctx->md_ctx, digest, &digestlen);
133+
if (ret != 1) goto done;
134+
135+
if (!asAscii) {
136+
/* Raw data requested */
137+
if (lenp) *lenp = digestlen;
138+
if (datap) {
139+
*datap = digest;
140+
digest = NULL;
141+
}
142+
}
143+
144+
else {
145+
/* ASCII requested */
146+
if (lenp) *lenp = (2*digestlen) + 1;
147+
if (datap) {
148+
const uint8_t * s = (const uint8_t *) digest;
149+
*datap = rpmhex(s, digestlen);
150+
}
151+
}
152+
153+
ret = 1;
154+
155+
done:
156+
if (digest) {
157+
/* Zero the digest, just in case it's sensitive */
158+
memset(digest, 0, digestlen);
159+
free(digest);
160+
}
161+
162+
EVP_MD_CTX_free(ctx->md_ctx);
163+
free(ctx);
164+
165+
if (ret != 1) {
166+
return -1;
167+
}
168+
169+
return 0;
170+
}
171+

0 commit comments

Comments
 (0)