Skip to content

Commit

Permalink
Add support for multiple OpenPGP signatures per package
Browse files Browse the repository at this point in the history
Add support for multiple OpenPGP header signatures per package, base64
encoded in a string array, also known as rpm v6 signatures.

--addsign no longer deletes any signatures, it only creates and adds a
new signature if possible. --delete and --resign behave as before: they
delete ALL signatures on the package, and the latter then creates and
adds a new one.

For v6 packages this is the default signature type, but if requested,
one v4 compat signature can be created for compatible algorithms. v6
signatures on v4 packages are also supported, but have to be explicitly
requested. In that case, v3/v4 signatures are only added if none already
exist and a v4 compatible algorithm is used. v3 signatures on v6
packages are not supported (out of principle, not a technical
limitation)

On verification, if RPMTAG_OPENPGP exists then other signature tags are
ignored because they're expected to only contain compat copies of the
same content. As of now, all existing signatures must validate for
signature checking of a package to pass, further policies are to be
added later.

Besides the concrete RPMTAG_OPENPGP signature tag, add an extension by
the same name to handle compatibility with v3/v4 signatures: a user will
only need to query the RPMTAG_OPENPGP extension to get all the
signatures at once. Extend :pgpsig tag format to handle the new variant.

Update --info/-i query to output all existing signatures, one per line.
The no-signature case of "Signature  : (none)" is preserved as-is to
help backwards compatibility with scripts parsing the output.

Fixes: #3385
  • Loading branch information
pmatilai committed Nov 11, 2024
1 parent 5ae233e commit edec282
Show file tree
Hide file tree
Showing 16 changed files with 495 additions and 54 deletions.
48 changes: 39 additions & 9 deletions docs/man/rpmsign.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,25 @@ SIGNING PACKAGES:
rpmsign-options
---------------

\[**\--rpmv3**\] \[**\--fskpath ***KEY*\] \[**\--signfiles**\]
\[**\--rpmv3**\] \[**\--rpmv4**\] \[**\--fskpath ***KEY*\] \[**\--signfiles**\]

DESCRIPTION
===========

Both of the **\--addsign** and **\--resign** options generate and insert
new signatures for each package *PACKAGE\_FILE* given, replacing any
existing signatures. There are two options for historical reasons, there
is no difference in behavior currently.
**rpmsign** **\--addsign** generates and inserts a new OpenPGP signature
for each *PACKAGE\_FILE* given unless a signature with identical
parameters already exists, in which case no action is taken.
Arbitrary number of V6 signatures can be added.

**rpmsign** **\--resign** generates and inserts a new OpenPGP signature
for each *PACKAGE\_FILE*, replacing any and all previous signatures.

To create a signature rpmsign needs to verify the package\'s checksum. As a
result packages with a MD5/SHA1 checksums cannot be signed in FIPS mode.
result V4 packages with MD5/SHA1 checksums cannot be signed in FIPS mode.

**rpmsign** **\--delsign** *PACKAGE\_FILE \...*

Delete all signatures from each package *PACKAGE\_FILE* given.
Delete all OpenPGP signatures from each package *PACKAGE\_FILE* given.

**rpmsign** **\--delfilesign** *PACKAGE\_FILE \...*

Expand All @@ -52,13 +55,40 @@ SIGN OPTIONS

**\--rpmv3**

: Force RPM V3 header+payload signature addition. These are expensive
: Request RPM V3 header+payload signature addition on V4 packages.
These signatures are expensive
and redundant baggage on packages where a separate payload digest
exists (packages built with rpm \>= 4.14). Rpmsign will automatically
detect the need for V3 signatures, but this option can be used to
force their creation if the packages must be fully signature
request their creation if the packages must be fully signature
verifiable with rpm \< 4.14 or other interoperability reasons.

Has no effect when signing V6 packages.

**\--rpmv4**

: Request RPM V4 header signature addition on V6 packages.
Useful for making V6 packages signature verifiable
with rpm 4.x versions.

V4 compatibility signatures are only ever added if the signing algorithm
is one of those known to V4: RSA, EcDSA, EdDSA (and original DSA).
Only one V4 signature can be present in a package, so this is
added only on the first **\--addsign** with a V4 compatible
algorithm, and ignored otherwise.

Has no effect when signing V4 packages.

**\--rpmv6**

: Request RPM V6 header signature addition on V4 packages.

This generally always succeeds as there can be arbitrary number of
V6 signatures on a package. A V3/V4 compatibility signatures are
added usign the same logic as **\--rpmv4** on a V6 package.

Has no effect when signing V6 packages.

**\--fskpath ***KEY*

: Used with **\--signfiles**, use file signing key *Key*.
Expand Down
2 changes: 2 additions & 0 deletions docs/manual/tags.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ Tag Name | Value| Type | Description
------------------|------|--------------|------------
Dsaheader | 267 | bin | OpenPGP DSA signature of the header (if thus signed)
Longsigsize | 270 | int64 | Header+payload size if > 4GB.
Openpgp | 278 | string array | OpenPGP signature(s) of the header, base64 encoded
Payloaddigest | 5092 | string array | Cryptographic digest of the compressed payload.
Payloaddigestalgo | 5093 | int32 | ID of the payload digest algorithm.
Payloaddigestalt | 5097 | string array | Cryptographic digest of the uncompressed payload.
Expand Down Expand Up @@ -446,6 +447,7 @@ Longsigsize | Header+payload size in 64bit format

Tag Name | Value| Type | Description
----------------------|------|--------------|------------
Openpgp | 278 | string array | All OpenPGP signature(s) in the header, including legacy ones (base64 encoded)
Origfilenames | 5007 | string array | Original Filenames in relocated packages.
Providenevrs | 5042 | string array | Formatted `name [op version]` provide dependency strings.
Conflictnevrs | 5044 | string array | Formatted `name [op version]` conflict dependency strings.
Expand Down
3 changes: 3 additions & 0 deletions include/rpm/rpmsign.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ enum rpmSignFlags_e {
RPMSIGN_FLAG_IMA = (1 << 0),
RPMSIGN_FLAG_RPMV3 = (1 << 1),
RPMSIGN_FLAG_FSVERITY = (1 << 2),
RPMSIGN_FLAG_RESIGN = (1 << 3),
RPMSIGN_FLAG_RPMV4 = (1 << 4),
RPMSIGN_FLAG_RPMV6 = (1 << 5),
};
typedef rpmFlags rpmSignFlags;

Expand Down
2 changes: 2 additions & 0 deletions include/rpm/rpmtag.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ typedef enum rpmTag_e {
/* RPMTAG_SIG_BASE+19 reserved for RPMSIGTAG_FILESIGNATURELENGTH */
RPMTAG_VERITYSIGNATURES = RPMTAG_SIG_BASE+20, /* s[] */
RPMTAG_VERITYSIGNATUREALGO = RPMTAG_SIG_BASE+21, /* i */
RPMTAG_OPENPGP = RPMTAG_SIG_BASE+22, /* s[] */
RPMTAG_SIG_TOP = HEADER_SIGTOP,

RPMTAG_NAME = 1000, /* s */
Expand Down Expand Up @@ -455,6 +456,7 @@ typedef enum rpmSigTag_e {
RPMSIGTAG_FILESIGNATURELENGTH = RPMTAG_SIG_BASE + 19,
RPMSIGTAG_VERITYSIGNATURES = RPMTAG_VERITYSIGNATURES,
RPMSIGTAG_VERITYSIGNATUREALGO = RPMTAG_VERITYSIGNATUREALGO,
RPMSIGTAG_OPENPGP = RPMTAG_OPENPGP,
RPMSIGTAG_RESERVED = RPMTAG_SIG_TOP,
} rpmSigTag;

Expand Down
3 changes: 3 additions & 0 deletions include/rpm/rpmts.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ enum rpmVSFlags_e {
RPMVSF_NOSHA256HEADER = (1 << 9),
RPMVSF_NODSAHEADER = (1 << 10),
RPMVSF_NORSAHEADER = (1 << 11),
RPMVSF_NOOPENPGP = (1 << 12),
/* bit(s) 12-15 unused */
RPMVSF_NOPAYLOAD = (1 << 16),
RPMVSF_NOMD5 = (1 << 17),
Expand All @@ -126,13 +127,15 @@ typedef rpmFlags rpmVSFlags;
#define RPMVSF_MASK_NOSIGNATURES \
( RPMVSF_NODSAHEADER | \
RPMVSF_NORSAHEADER | \
RPMVSF_NOOPENPGP | \
RPMVSF_NODSA | \
RPMVSF_NORSA )
#define _RPMVSF_NOSIGNATURES RPMVSF_MASK_NOSIGNATURES

#define RPMVSF_MASK_NOHEADER \
( RPMVSF_NOSHA1HEADER | \
RPMVSF_NOSHA256HEADER | \
RPMVSF_NOOPENPGP | \
RPMVSF_NODSAHEADER | \
RPMVSF_NORSAHEADER )
#define _RPMVSF_NOHEADER RPMVSF_MASK_NOHEADER
Expand Down
24 changes: 21 additions & 3 deletions lib/formats.cc
Original file line number Diff line number Diff line change
Expand Up @@ -419,12 +419,12 @@ static char *jsonFormat(rpmtd td, char **emsg)
}

/* signature fingerprint and time formatting */
static char * pgpsigFormat(rpmtd td, char **emsg)
static char * pgpsigFormatOne(uint8_t *pkt, size_t pktlen, char **emsg)
{
char * val = NULL;
pgpDigParams sigp = NULL;

if (pgpPrtParams((uint8_t*)td->data, td->count, PGPTAG_SIGNATURE, &sigp)) {
if (pgpPrtParams(pkt, pktlen, PGPTAG_SIGNATURE, &sigp)) {
*emsg = xstrdup(_("(not an OpenPGP signature)"));
} else {
char dbuf[BUFSIZ];
Expand All @@ -451,6 +451,24 @@ static char * pgpsigFormat(rpmtd td, char **emsg)
return val;
}

static char * pgpsigFormat(rpmtd td, char **emsg)
{
char *val = NULL;
if (rpmtdType(td) == RPM_BIN_TYPE) {
val = pgpsigFormatOne((uint8_t *)td->data, td->count, emsg);
} else if (rpmtdType(td) == RPM_STRING_ARRAY_TYPE) {
uint8_t *pkt = NULL;
size_t pktlen = 0;
if (rpmBase64Decode(rpmtdGetString(td), (void **)&pkt, &pktlen) == 0) {
val = pgpsigFormatOne(pkt, pktlen, emsg);
free(pkt);
}
} else {
*emsg = xstrdup(_("(invalid type)"));
}
return val;
}

/* dependency flags formatting */
static char * depflagsFormat(rpmtd td, char **emsg)
{
Expand Down Expand Up @@ -581,7 +599,7 @@ static const struct headerFmt_s rpmHeaderFormats[] = {
{ RPMTD_FORMAT_BASE64, "base64",
RPM_BINARY_CLASS, base64Format },
{ RPMTD_FORMAT_PGPSIG, "pgpsig",
RPM_BINARY_CLASS, pgpsigFormat },
RPM_NULL_CLASS, pgpsigFormat },
{ RPMTD_FORMAT_DEPFLAGS, "depflags",
RPM_NUMERIC_CLASS, depflagsFormat },
{ RPMTD_FORMAT_DEPTYPE, "deptype",
Expand Down
1 change: 1 addition & 0 deletions lib/package.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ static struct taglate_s {
{ RPMSIGTAG_RSA, RPMTAG_RSAHEADER, 0, 0 },
{ RPMSIGTAG_LONGSIZE, RPMTAG_LONGSIGSIZE, 1, 0 },
{ RPMSIGTAG_LONGARCHIVESIZE, RPMTAG_LONGARCHIVESIZE, 1, 0 },
{ RPMSIGTAG_OPENPGP, RPMTAG_OPENPGP, 0, 0 },
{ 0 }
};

Expand Down
46 changes: 38 additions & 8 deletions lib/rpmvs.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "system.h"

#include <rpm/rpmbase64.h>
#include <rpm/rpmkeyring.h>
#include <rpm/rpmmacro.h>
#include <rpm/rpmlog.h>
Expand All @@ -26,6 +27,7 @@ struct vfytag_s {
};

static const struct vfytag_s rpmvfytags[] = {
{ RPMTAG_OPENPGP, RPM_STRING_ARRAY_TYPE, 0, 0, },
{ RPMSIGTAG_SIZE, RPM_BIN_TYPE, 0, 0, },
{ RPMSIGTAG_PGP, RPM_BIN_TYPE, 0, 0, },
{ RPMSIGTAG_MD5, RPM_BIN_TYPE, 0, 16, },
Expand All @@ -50,6 +52,9 @@ struct vfyinfo_s {
};

static const struct vfyinfo_s rpmvfyitems[] = {
{ RPMTAG_OPENPGP, 1,
{ RPMSIG_SIGNATURE_TYPE, RPMVSF_NOOPENPGP,
(RPMSIG_HEADER), 0, 0, }, },
{ RPMSIGTAG_SIZE, 1,
{ RPMSIG_OTHER_TYPE, 0,
(RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, 0, }, },
Expand Down Expand Up @@ -131,14 +136,16 @@ int rpmIsValidHex(const char *str, size_t slen)
return valid;
}

static void rpmsinfoInit(const struct vfyinfo_s *vinfo,
static rpmRC rpmsinfoInit(const struct vfyinfo_s *vinfo,
const struct vfytag_s *tinfo,
rpmtd td, const char *origin,
struct rpmsinfo_s *sinfo)
{
rpmRC rc = RPMRC_FAIL;
const void *data = NULL;
rpm_count_t dlen = 0;
uint8_t *pkt = NULL;
size_t pktlen = 0;

*sinfo = vinfo->vi; /* struct assignment */
sinfo->wrapped = (vinfo->sigh == 0);
Expand Down Expand Up @@ -193,6 +200,16 @@ static void rpmsinfoInit(const struct vfyinfo_s *vinfo,
}

if (sinfo->type == RPMSIG_SIGNATURE_TYPE) {
if (td->type == RPM_STRING_ARRAY_TYPE) {
if (rpmBase64Decode((const char *)data, (void **)&pkt, &pktlen)) {
rasprintf(&sinfo->msg, _("%s tag %u: invalid base64"),
origin, td->tag);
goto exit;
}
data = pkt;
dlen = pktlen;
}

char *lints = NULL;
int ec = pgpPrtParams2((const uint8_t *)data, dlen, PGPTAG_SIGNATURE,
&sinfo->sig, &lints);
Expand Down Expand Up @@ -234,8 +251,10 @@ static void rpmsinfoInit(const struct vfyinfo_s *vinfo,
rc = RPMRC_OK;

exit:
if (pkt && pkt != td->data)
free(pkt);
sinfo->rc = rc;
return;
return rc;
}

static void rpmsinfoFini(struct rpmsinfo_s *sinfo)
Expand Down Expand Up @@ -291,11 +310,16 @@ const char *rpmsinfoDescr(struct rpmsinfo_s *sinfo)
rangeName(sinfo->range), t);
free(t);
} else {
rasprintf(&sinfo->descr, _("%s OpenPGP %s%s %s"),
rangeName(sinfo->range),
pgpValString(PGPVAL_PUBKEYALGO, sinfo->sigalgo),
sinfo->alt ? " ALT" : "",
_("signature"));
if (sinfo->sigalgo) {
rasprintf(&sinfo->descr, _("%s OpenPGP %s %s"),
rangeName(sinfo->range),
pgpValString(PGPVAL_PUBKEYALGO, sinfo->sigalgo),
_("signature"));
} else {
rasprintf(&sinfo->descr, _("%s OpenPGP %s"),
rangeName(sinfo->range),
_("signature"));
}
}
break;
}
Expand Down Expand Up @@ -348,7 +372,13 @@ static void rpmvsAppend(struct rpmvs_s *sis, hdrblob blob,

if (!rpmsinfoDisabled(&vi->vi, sis->vsflags) && rc == RPMRC_OK) {
while (rpmtdNext(&td) >= 0) {
rpmsinfoInit(vi, ti, &td, o, &sis->sigs[sis->nsigs]);
if (!rpmsinfoInit(vi, ti, &td, o, &sis->sigs[sis->nsigs])) {
/* Don't bother with v3/v4 sigs when v6 sigs exist */
if (td.tag == RPMSIGTAG_OPENPGP) {
sis->vsflags |= (RPMVSF_NODSAHEADER|RPMVSF_NORSAHEADER);
sis->vsflags |= (RPMVSF_NODSA|RPMVSF_NORSA);
}
}
sis->nsigs++;
}
} else {
Expand Down
36 changes: 36 additions & 0 deletions lib/tagexts.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include "system.h"

#include <vector>

#include <rpm/rpmtypes.h>
#include <rpm/rpmlib.h>
#include <rpm/rpmmacro.h> /* XXX for %_i18ndomains */
Expand Down Expand Up @@ -1052,6 +1054,39 @@ static int sysusersTag(Header h, rpmtd td, headerGetFlags hgflags)
return (td->count > 0);
}

static void trySigTag(Header h, rpmTagVal tag, ARGV_t *sigs)
{
struct rpmtd_s td;
if (headerGet(h, tag, &td, HEADERGET_ALLOC)) {
char *b64 = rpmBase64Encode((uint8_t *)td.data, td.count, 0);
if (b64) {
argvAdd(sigs, b64);
free(b64);
}
rpmtdFreeData(&td);
}
}

static int openpgpTag(Header h, rpmtd td, headerGetFlags hgflags)
{
if (headerGet(h, RPMTAG_OPENPGP, td, HEADERGET_ALLOC))
return 1;

ARGV_t sigs = NULL;
trySigTag(h, RPMTAG_RSAHEADER, &sigs);
trySigTag(h, RPMTAG_DSAHEADER, &sigs);
trySigTag(h, RPMTAG_SIGPGP, &sigs);
trySigTag(h, RPMTAG_SIGGPG, &sigs);

if (sigs) {
td->data = sigs;
td->count = argvCount(sigs);
td->type = RPM_STRING_ARRAY_TYPE;
td->flags = RPMTD_ALLOCED|RPMTD_PTR_ALLOCED;
}
return td->count != 0;
}

static const struct headerTagFunc_s rpmHeaderTagExtensions[] = {
{ RPMTAG_GROUP, groupTag },
{ RPMTAG_DESCRIPTION, descriptionTag },
Expand Down Expand Up @@ -1093,6 +1128,7 @@ static const struct headerTagFunc_s rpmHeaderTagExtensions[] = {
{ RPMTAG_FILENLINKS, filenlinksTag },
{ RPMTAG_SYSUSERS, sysusersTag },
{ RPMTAG_FILEMIMES, filemimesTag },
{ RPMTAG_OPENPGP, openpgpTag },
{ 0, NULL }
};

Expand Down
2 changes: 1 addition & 1 deletion rpmpopt.in
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Install Date: %|INSTALLTIME?{%{INSTALLTIME:date}}:{(not installed)}|\n\
Group : %{GROUP}\n\
Size : %{LONGSIZE}\n\
%|LICENSE?{License : %{LICENSE}}|\n\
Signature : %|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|\n\
Signature :%|OPENPGP?{[\n %{OPENPGP:pgpsig}]}:{ (none)}|\n\
Source RPM : %{SOURCERPM}\n\
Build Date : %{BUILDTIME:date}\n\
Build Host : %{BUILDHOST}\n\
Expand Down
Loading

0 comments on commit edec282

Please sign in to comment.