diff --git a/docs/man/rpmsign.8.md b/docs/man/rpmsign.8.md index adff642b3f..aa8c306007 100644 --- a/docs/man/rpmsign.8.md +++ b/docs/man/rpmsign.8.md @@ -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 \...* @@ -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*. diff --git a/docs/manual/tags.md b/docs/manual/tags.md index 334e90fbbb..4880cfda41 100644 --- a/docs/manual/tags.md +++ b/docs/manual/tags.md @@ -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. @@ -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. diff --git a/include/rpm/rpmsign.h b/include/rpm/rpmsign.h index c876ef1bda..c0dbdab533 100644 --- a/include/rpm/rpmsign.h +++ b/include/rpm/rpmsign.h @@ -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; diff --git a/include/rpm/rpmtag.h b/include/rpm/rpmtag.h index 3a60516a30..c716bd3f43 100644 --- a/include/rpm/rpmtag.h +++ b/include/rpm/rpmtag.h @@ -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 */ @@ -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; diff --git a/include/rpm/rpmts.h b/include/rpm/rpmts.h index dc5b31c55c..86ca62e0ef 100644 --- a/include/rpm/rpmts.h +++ b/include/rpm/rpmts.h @@ -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), @@ -126,6 +127,7 @@ typedef rpmFlags rpmVSFlags; #define RPMVSF_MASK_NOSIGNATURES \ ( RPMVSF_NODSAHEADER | \ RPMVSF_NORSAHEADER | \ + RPMVSF_NOOPENPGP | \ RPMVSF_NODSA | \ RPMVSF_NORSA ) #define _RPMVSF_NOSIGNATURES RPMVSF_MASK_NOSIGNATURES @@ -133,6 +135,7 @@ typedef rpmFlags rpmVSFlags; #define RPMVSF_MASK_NOHEADER \ ( RPMVSF_NOSHA1HEADER | \ RPMVSF_NOSHA256HEADER | \ + RPMVSF_NOOPENPGP | \ RPMVSF_NODSAHEADER | \ RPMVSF_NORSAHEADER ) #define _RPMVSF_NOHEADER RPMVSF_MASK_NOHEADER diff --git a/lib/formats.cc b/lib/formats.cc index 61d04fe65e..a5e891d1ab 100644 --- a/lib/formats.cc +++ b/lib/formats.cc @@ -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]; @@ -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) { @@ -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", diff --git a/lib/package.cc b/lib/package.cc index 83de81a93c..b16c52460f 100644 --- a/lib/package.cc +++ b/lib/package.cc @@ -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 } }; diff --git a/lib/rpmvs.cc b/lib/rpmvs.cc index 53837720c4..e4909f0213 100644 --- a/lib/rpmvs.cc +++ b/lib/rpmvs.cc @@ -1,5 +1,6 @@ #include "system.h" +#include #include #include #include @@ -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, }, @@ -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, }, }, @@ -131,7 +136,7 @@ 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) @@ -139,6 +144,8 @@ static void rpmsinfoInit(const struct vfyinfo_s *vinfo, 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); @@ -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); @@ -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) @@ -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; } @@ -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 { diff --git a/lib/tagexts.cc b/lib/tagexts.cc index ed2f678964..9f8386ffda 100644 --- a/lib/tagexts.cc +++ b/lib/tagexts.cc @@ -1052,6 +1052,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 }, @@ -1093,6 +1126,7 @@ static const struct headerTagFunc_s rpmHeaderTagExtensions[] = { { RPMTAG_FILENLINKS, filenlinksTag }, { RPMTAG_SYSUSERS, sysusersTag }, { RPMTAG_FILEMIMES, filemimesTag }, + { RPMTAG_OPENPGP, openpgpTag }, { 0, NULL } }; diff --git a/rpmpopt.in b/rpmpopt.in index 0df8adacdd..259b187e40 100644 --- a/rpmpopt.in +++ b/rpmpopt.in @@ -96,11 +96,11 @@ 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\ -%|PREFIXES?{Relocations : [%{PREFIXES} ]\n}|\ +%|PREFIXES?{Relocations :[ %{PREFIXES}]\n}|\ %|PACKAGER?{Packager : %{PACKAGER}\n}|\ %|VENDOR?{Vendor : %{VENDOR}\n}|\ %|VCS?{VCS : %{VCS}\n}|\ diff --git a/sign/rpmgensig.cc b/sign/rpmgensig.cc index 469fe71b05..85a515e3bd 100644 --- a/sign/rpmgensig.cc +++ b/sign/rpmgensig.cc @@ -17,6 +17,7 @@ #include "rpmsignfiles.hh" #endif +#include #include /* RPMSIGTAG & related */ #include #include @@ -129,13 +130,8 @@ static int copyFile(FD_t *sfdp, const char *sfnp, return rc; } -/* - * Validate generated signature and insert to header if it looks sane. - * RPM doesn't support everything GPG does. Basic tests to see if the - * generated signature is something we can use. - * Return generated signature tag data on success, NULL on failure. - */ -static rpmtd makeSigTag(Header sigh, int ishdr, uint8_t *pkt, size_t pktlen) +/* Wrap a raw signature in an rpmtd and sanity check, return NULL on fail */ +static rpmtd makeSigTag(int ishdr, uint8_t *pkt, size_t pktlen) { pgpDigParams sigp = NULL; rpmTagVal sigtag; @@ -180,11 +176,12 @@ static rpmtd makeSigTag(Header sigh, int ishdr, uint8_t *pkt, size_t pktlen) /* Looks sane, create the tag data */ sigtd = rpmtdNew(); + sigtd->tag = sigtag; + sigtd->flags |= RPMTD_ALLOCED; sigtd->count = pktlen; sigtd->data = memcpy(xmalloc(pktlen), pkt, pktlen);; - sigtd->type = RPM_BIN_TYPE; sigtd->tag = sigtag; - sigtd->flags |= RPMTD_ALLOCED; + sigtd->type = RPM_BIN_TYPE; exit: pgpDigParamsFree(sigp); @@ -319,15 +316,8 @@ static int runGPG(sigTarget sigt, const char *sigfile) return rc; } -/** - * Generate GPG signature(s) for a header+payload file. - * @param sigh signature header - * @param ishdr header-only signature? - * @param sigt signature target - * @param passPhrase private key pass phrase - * @return generated sigtag on success, 0 on failure - */ -static rpmtd makeGPGSignature(Header sigh, int ishdr, sigTarget sigt) +/* Generate an OpenPGP signature(s) for a target */ +static rpmtd makeGPGSignature(int ishdr, sigTarget sigt) { char * sigfile = rstrscat(NULL, sigt->fileName, ".sig", NULL); struct stat st; @@ -366,7 +356,7 @@ static rpmtd makeGPGSignature(Header sigh, int ishdr, sigTarget sigt) rpmlog(RPMLOG_DEBUG, "Got %zd bytes of OpenPGP sig\n", pktlen); /* Parse the signature, change signature tag as appropriate. */ - sigtd = makeSigTag(sigh, ishdr, pkt, pktlen); + sigtd = makeSigTag(ishdr, pkt, pktlen); exit: (void) unlink(sigfile); free(sigfile); @@ -382,6 +372,7 @@ static void deleteSigs(Header sigh) headerDel(sigh, RPMSIGTAG_DSA); headerDel(sigh, RPMSIGTAG_RSA); headerDel(sigh, RPMSIGTAG_PGP5); + headerDel(sigh, RPMSIGTAG_OPENPGP); } static void deleteFileSigs(Header sigh) @@ -392,36 +383,84 @@ static void deleteFileSigs(Header sigh) headerDel(sigh, RPMSIGTAG_VERITYSIGNATUREALGO); } -static int haveSignature(rpmtd sigtd, Header h) +static pgpDigParams tdParams(rpmtd td) +{ + pgpDigParams sig = NULL; + if (td->tag == RPMTAG_OPENPGP) { + uint8_t *pkt = NULL; + size_t pktlen = 0; + if (rpmBase64Decode(rpmtdGetString(td), (void **)&pkt, &pktlen) == 0) { + pgpPrtParams(pkt, pktlen, PGPTAG_SIGNATURE, &sig); + free(pkt); + } + } else { + pgpPrtParams((uint8_t *)td->data, td->count, PGPTAG_SIGNATURE, &sig); + } + return sig; +} + +static int haveSignature(rpmtd sigtd, Header sigh) { - pgpDigParams sig1 = NULL; - pgpDigParams sig2 = NULL; struct rpmtd_s oldtd; int rc = 0; /* assume no */ + rpmTagVal tag = headerIsEntry(sigh, RPMSIGTAG_RESERVED) ? + RPMTAG_OPENPGP : sigtd->tag; - if (!headerGet(h, rpmtdTag(sigtd), &oldtd, HEADERGET_DEFAULT)) + if (!headerGet(sigh, tag, &oldtd, HEADERGET_DEFAULT)) return rc; - pgpPrtParams((uint8_t *)sigtd->data, sigtd->count, PGPTAG_SIGNATURE, &sig1); + pgpDigParams newsig = tdParams(sigtd); while (rpmtdNext(&oldtd) >= 0 && rc == 0) { - pgpPrtParams((uint8_t *)oldtd.data, oldtd.count, PGPTAG_SIGNATURE, &sig2); - if (pgpDigParamsCmp(sig1, sig2) == 0) + pgpDigParams oldsig = tdParams(&oldtd); + if (pgpDigParamsCmp(newsig, oldsig) == 0) rc = 1; - sig2 = pgpDigParamsFree(sig2); + pgpDigParamsFree(oldsig); } - pgpDigParamsFree(sig1); + pgpDigParamsFree(newsig); rpmtdFreeData(&oldtd); return rc; } -static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4) +static int putSignature(Header sigh, rpmtd sigtd, int multisig) +{ + int rc = 1; + if (multisig) { + char *b64 = rpmBase64Encode(sigtd->data, sigtd->count, 0); + char **arr = (char **)xmalloc(1 * sizeof(*arr)); + arr[0] = b64; + + rpmtd_s mtd = { + .tag = RPMSIGTAG_OPENPGP, + .type = RPM_STRING_ARRAY_TYPE, + .count = 1, + .data = arr, + .flags = RPMTD_ALLOCED|RPMTD_PTR_ALLOCED, + .ix = -1, + .size = 0, + }; + rc = (headerPut(sigh, &mtd, HEADERPUT_APPEND) == 0); + rpmtdFreeData(&mtd); + } else { + rc = (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0); + } + return rc; +} + +static int haveLegacySig(Header sigh) +{ + return headerIsEntry(sigh, RPMSIGTAG_RSA) || + headerIsEntry(sigh, RPMSIGTAG_DSA); +} + +static int addSignature(Header sigh, rpmSignFlags flags, + sigTarget sigt_v3, sigTarget sigt_v4) { int rc = -1; rpmtd sigtd = NULL; - /* Make the cheaper v4 signature first */ - if ((sigtd = makeGPGSignature(sigh, 1, sigt_v4)) == NULL) + /* Make a header signature */ + if ((sigtd = makeGPGSignature(1, sigt_v4)) == NULL) goto exit; /* See if we already have a signature by the same key and parameters */ @@ -429,23 +468,31 @@ static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4) rc = 1; goto exit; } - /* Nuke all signature tags */ - deleteSigs(sigh); - - if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) - goto exit; - - if (sigt_v3) { - rpmtdFree(sigtd); - /* Assume the same signature test holds for v3 signature too */ - if ((sigtd = makeGPGSignature(sigh, 0, sigt_v3)) == NULL) + /* Add a v6 signature if requested */ + if (flags & RPMSIGN_FLAG_RPMV6) + if (putSignature(sigh, sigtd, 1)) goto exit; - if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) + /* Add a v4 signature if requested */ + if (flags & RPMSIGN_FLAG_RPMV4) { + if (putSignature(sigh, sigtd, 0)) goto exit; + + /* Only consider v3 signature if also adding v4 */ + if (flags & RPMSIGN_FLAG_RPMV3) { + rpmtdFree(sigtd); + + /* Assume the same signature test holds for v3 signature too */ + if ((sigtd = makeGPGSignature(0, sigt_v3)) == NULL) + goto exit; + + if (putSignature(sigh, sigtd, 0)) + goto exit; + } } + rc = 0; exit: rpmtdFree(sigtd); @@ -635,6 +682,23 @@ static int rpmSign(const char *rpm, int deleting, int flags) flags |= RPMSIGN_FLAG_RPMV3; } + /* Only v6 packages have this */ + if (headerIsEntry(sigh, RPMSIGTAG_RESERVED)) { + flags |= RPMSIGN_FLAG_RPMV6; + reserveTag = RPMSIGTAG_RESERVED; + /* v3 signatures are not welcome in v6 packages */ + if (flags & RPMSIGN_FLAG_RPMV3) { + rpmlog(RPMLOG_WARNING, + _("not generating v3 signature for v6 package: %s\n"), rpm); + flags &= ~RPMSIGN_FLAG_RPMV3; + } + } else { + flags |= RPMSIGN_FLAG_RPMV4; + /* Ensure only one legacy signature is added if adding v6 signatures */ + if ((flags & RPMSIGN_FLAG_RPMV6) && haveLegacySig(sigh)) + flags &= ~(RPMSIGN_FLAG_RPMV4|RPMSIGN_FLAG_RPMV3); + } + unloadImmutableRegion(&sigh, RPMTAG_HEADERSIGNATURES); origSigSize = headerSizeof(sigh, HEADER_MAGIC_YES); @@ -654,7 +718,6 @@ static int rpmSign(const char *rpm, int deleting, int flags) deleteSigs(sigh); } else { /* Signature target containing header + payload */ - int v3 = (flags & RPMSIGN_FLAG_RPMV3); sigt_v3.fd = fd; sigt_v3.start = headerStart; sigt_v3.fileName = rpm; @@ -664,7 +727,10 @@ static int rpmSign(const char *rpm, int deleting, int flags) sigt_v4 = sigt_v3; sigt_v4.size = headerSizeof(h, HEADER_MAGIC_YES); - res = replaceSignature(sigh, v3 ? &sigt_v3 : NULL, &sigt_v4); + if (flags & RPMSIGN_FLAG_RESIGN) + deleteSigs(sigh); + + res = addSignature(sigh, flags, &sigt_v3, &sigt_v4); if (res != 0) { if (res == 1) { rpmlog(RPMLOG_WARNING, @@ -678,10 +744,6 @@ static int rpmSign(const char *rpm, int deleting, int flags) res = -1; } - /* Only v6 packages have this */ - if (headerIsEntry(h, RPMSIGTAG_RESERVED)) - reserveTag = RPMSIGTAG_RESERVED; - /* Adjust reserved size for added/removed signatures */ if (headerGet(sigh, reserveTag, &utd, HEADERGET_MINMEM)) { int diff = headerSizeof(sigh, HEADER_MAGIC_YES) - origSigSize; diff --git a/tests/rpmgeneral.at b/tests/rpmgeneral.at index b31e322cd3..2c7f2c217b 100644 --- a/tests/rpmgeneral.at +++ b/tests/rpmgeneral.at @@ -191,6 +191,7 @@ OLDSUGGESTS OLDSUGGESTSFLAGS OLDSUGGESTSNAME OLDSUGGESTSVERSION +OPENPGP OPTFLAGS ORDERFLAGS ORDERNAME diff --git a/tests/rpmquery.at b/tests/rpmquery.at index 33c5708bc9..1f20bac9e5 100644 --- a/tests/rpmquery.at +++ b/tests/rpmquery.at @@ -420,6 +420,18 @@ rpm \ [RSA/SHA256, Thu Apr 6 13:02:33 2017, Key ID 4344591e1964c5fc], [warning: /data/RPMS/hello-2.0-1.x86_64-signed.rpm: Header OpenPGP V4 RSA/SHA256 signature, key ID 4344591e1964c5fc: NOKEY ]) + +RPMTEST_CHECK([[ +runroot rpm \ + --nosignature \ + --qf "[%{openpgp:pgpsig}\n]" \ + -qp /data/RPMS/hello-2.0-1.x86_64-signed.rpm +]], +[0], +[RSA/SHA256, Thu Apr 6 13:02:33 2017, Key ID 4344591e1964c5fc +RSA/SHA256, Thu Apr 6 13:02:32 2017, Key ID 4344591e1964c5fc +], +[]) RPMTEST_CLEANUP # ------------------------------ @@ -1396,3 +1408,32 @@ runroot rpm -qp --filemime /build/RPMS/noarch/filetypes-1.0-1.noarch.rpm | sed - ], []) RPMTEST_CLEANUP + +AT_SETUP([info query output]) +AT_KEYWORDS([query signature]) +RPMTEST_CHECK([ +runroot rpm -qi --nosignature /data/RPMS/hello-2.0-1.x86_64-signed.rpm +], +[0], +[[Name : hello +Version : 2.0 +Release : 1 +Architecture: x86_64 +Install Date: (not installed) +Group : Testing +Size : 7243 +License : GPL +Signature : + RSA/SHA256, Thu Apr 6 13:02:33 2017, Key ID 4344591e1964c5fc + RSA/SHA256, Thu Apr 6 13:02:32 2017, Key ID 4344591e1964c5fc +Source RPM : hello-2.0-1.src.rpm +Build Date : Sat Nov 22 12:00:00 2008 +Build Host : localhost +Relocations : /usr +Summary : hello -- hello, world rpm +Description : +Simple rpm demonstration. +]], +[]) +RPMTEST_CLEANUP + diff --git a/tests/rpmsigdig.at b/tests/rpmsigdig.at index 7ce324af5a..7e3df2795d 100644 --- a/tests/rpmsigdig.at +++ b/tests/rpmsigdig.at @@ -641,6 +641,7 @@ runroot rpmkeys --define '_pkgverify_level all' -Kv --nosignature /data/RPMS/hel [[Checking package before importing key: /data/RPMS/hello-2.0-1.x86_64-signed-with-subkey.rpm: Header OpenPGP V4 RSA/SHA512 signature, key ID 1f71177215217ee0: NOKEY + Header OpenPGP signature: NOTFOUND Header OpenPGP DSA signature: NOTFOUND Header SHA256 digest: OK Payload SHA256 digest: OK @@ -699,6 +700,7 @@ runroot rpmkeys --define '_pkgverify_level all' -Kv --nosignature /data/RPMS/hel [Checking package before importing key: /data/RPMS/hello-2.0-1.x86_64-signed-with-subkey.rpm: Header OpenPGP V4 RSA/SHA512 signature, key ID 1f71177215217ee0: NOKEY + Header OpenPGP signature: NOTFOUND Header OpenPGP DSA signature: NOTFOUND Header SHA256 digest: OK Payload SHA256 digest: OK @@ -717,6 +719,7 @@ RPMOUTPUT_SEQUOIA([ Key 1F71177215217EE0 invalid: key is not alive])dnl RPMOUTPUT_SEQUOIA([ because: The subkey is not live])dnl RPMOUTPUT_SEQUOIA([ because: Expired on 2022-04-12T00:00:15Z])dnl Header OpenPGP V4 RSA/SHA512 signature, key fingerprint: b6542f92f30650c36b6f41bcb3a771bfeb04e625: NOTTRUSTED + Header OpenPGP signature: NOTFOUND Header OpenPGP DSA signature: NOTFOUND Header SHA256 digest: OK Payload SHA256 digest: OK @@ -731,6 +734,7 @@ RPMOUTPUT_SEQUOIA([ Key 1F71177215217EE0 invalid: key is not alive])dnl RPMOUTPUT_SEQUOIA([ because: The subkey is not live])dnl RPMOUTPUT_SEQUOIA([ because: Expired on 2022-04-12T00:00:15Z])dnl Header OpenPGP V4 RSA/SHA512 signature, key fingerprint: b6542f92f30650c36b6f41bcb3a771bfeb04e625: NOTTRUSTED + Header OpenPGP signature: NOTFOUND Header OpenPGP DSA signature: NOTFOUND Legacy OpenPGP RSA signature: NOTFOUND Legacy OpenPGP DSA signature: NOTFOUND @@ -769,6 +773,7 @@ runroot rpmkeys --define '_pkgverify_level all' -Kv --nosignature /data/RPMS/hel [Checking package before importing key: /data/RPMS/hello-2.0-1.x86_64-signed-with-subkey.rpm: Header OpenPGP V4 RSA/SHA512 signature, key ID 1f71177215217ee0: NOKEY + Header OpenPGP signature: NOTFOUND Header OpenPGP DSA signature: NOTFOUND Header SHA256 digest: OK Payload SHA256 digest: OK @@ -785,6 +790,7 @@ RPMOUTPUT_LEGACY([error: Subkey 1f71177215217ee0 of key b3a771bfeb04e625 (Alice RPMOUTPUT_SEQUOIA([error: Verifying a signature using certificate B6542F92F30650C36B6F41BCB3A771BFEB04E625 (Alice ):])dnl RPMOUTPUT_SEQUOIA([ Key 1F71177215217EE0 is invalid: key is revoked])dnl Header OpenPGP V4 RSA/SHA512 signature, key fingerprint: b6542f92f30650c36b6f41bcb3a771bfeb04e625: NOTTRUSTED + Header OpenPGP signature: NOTFOUND Header OpenPGP DSA signature: NOTFOUND Header SHA256 digest: OK Payload SHA256 digest: OK @@ -797,6 +803,7 @@ RPMOUTPUT_LEGACY([error: Subkey 1f71177215217ee0 of key b3a771bfeb04e625 (Alice RPMOUTPUT_SEQUOIA([error: Verifying a signature using certificate B6542F92F30650C36B6F41BCB3A771BFEB04E625 (Alice ):])dnl RPMOUTPUT_SEQUOIA([ Key 1F71177215217EE0 is invalid: key is revoked])dnl Header OpenPGP V4 RSA/SHA512 signature, key fingerprint: b6542f92f30650c36b6f41bcb3a771bfeb04e625: NOTTRUSTED + Header OpenPGP signature: NOTFOUND Header OpenPGP DSA signature: NOTFOUND Legacy OpenPGP RSA signature: NOTFOUND Legacy OpenPGP DSA signature: NOTFOUND @@ -1387,7 +1394,7 @@ AT_SKIP_IF([test x$PGP = xdummy]) RPMDB_INIT RPMTEST_CHECK([ -cat << EOF > ${HOME}/.rpmmacros +cat << EOF >> ${HOME}/.rpmmacros %_openpgp_sign sq %_openpgp_sign_id 771B18D3D7BAA28734333C424344591E1964C5FC EOF @@ -1441,6 +1448,195 @@ runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm [ignore]) RPMTEST_CLEANUP +AT_SETUP([rpmsign --addsign multisig v4]) +AT_KEYWORDS([rpmsign signature multisig v4]) +AT_SKIP_IF([test x$PGP = xdummy]) +RPMDB_INIT + +RPMTEST_CHECK([ +cat << EOF >> ${HOME}/.rpmmacros +%_openpgp_sign sq +%_openpgp_sign_id 771B18D3D7BAA28734333C424344591E1964C5FC +EOF + +runroot_other sq key import /data/keys/*.secret +runroot rpmkeys --import /data/keys/*.pub +], +[0], +[ignore], +[ignore]) + +RPMTEST_CHECK([ +cp "${RPMTEST}"/data/RPMS/hello-2.0-1.x86_64.rpm "${RPMTEST}"/tmp/ +runroot rpmsign --addsign --rpmv6 /tmp/hello-2.0-1.x86_64.rpm &> /dev/null +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm +], +[0], +[/tmp/hello-2.0-1.x86_64.rpm: + Header OpenPGP V4 RSA/SHA512 signature, key fingerprint: 771b18d3d7baa28734333c424344591e1964c5fc: OK + Header SHA256 digest: OK + Payload SHA256 digest: OK +], +[]) + +RPMTEST_CHECK([ +# resigning with identical key shouldn't add anything +runroot rpmsign --addsign --rpmv6 /tmp/hello-2.0-1.x86_64.rpm &> /dev/null +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm +], +[0], +[/tmp/hello-2.0-1.x86_64.rpm: + Header OpenPGP V4 RSA/SHA512 signature, key fingerprint: 771b18d3d7baa28734333c424344591e1964c5fc: OK + Header SHA256 digest: OK + Payload SHA256 digest: OK +], +[]) + +RPMTEST_CHECK([ +runroot rpmsign --addsign --rpmv6 --key-id 152BB32FD9CA982797E835CFB0645AEC757BF69E /tmp/hello-2.0-1.x86_64.rpm &> /dev/null +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm +], +[0], +[/tmp/hello-2.0-1.x86_64.rpm: + Header OpenPGP V4 EdDSA/SHA512 signature, key fingerprint: 152bb32fd9ca982797e835cfb0645aec757bf69e: OK + Header OpenPGP V4 RSA/SHA512 signature, key fingerprint: 771b18d3d7baa28734333c424344591e1964c5fc: OK + Header SHA256 digest: OK + Payload SHA256 digest: OK +], +[]) + +RPMTEST_CHECK([ +runroot rpmsign --addsign --rpmv6 --key-id E8A62C0512B06B5D2183BA207F1C21F95F65BBE8 /tmp/hello-2.0-1.x86_64.rpm &> /dev/null +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm +], +[0], +[/tmp/hello-2.0-1.x86_64.rpm: + Header OpenPGP V4 ECDSA/SHA512 signature, key fingerprint: e8a62c0512b06b5d2183ba207f1c21f95f65bbe8: OK + Header OpenPGP V4 EdDSA/SHA512 signature, key fingerprint: 152bb32fd9ca982797e835cfb0645aec757bf69e: OK + Header OpenPGP V4 RSA/SHA512 signature, key fingerprint: 771b18d3d7baa28734333c424344591e1964c5fc: OK + Header SHA256 digest: OK + Payload SHA256 digest: OK +], +[]) + +RPMTEST_CHECK([ +runroot rpmkeys --delete 152bb32fd9ca982797e835cfb0645aec757bf69e +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm +], +[1], +[/tmp/hello-2.0-1.x86_64.rpm: + Header OpenPGP V4 ECDSA/SHA512 signature, key fingerprint: e8a62c0512b06b5d2183ba207f1c21f95f65bbe8: OK + Header OpenPGP V4 EdDSA/SHA512 signature, key ID b0645aec757bf69e: NOKEY + Header OpenPGP V4 RSA/SHA512 signature, key fingerprint: 771b18d3d7baa28734333c424344591e1964c5fc: OK + Header SHA256 digest: OK + Payload SHA256 digest: OK +], +[]) + +RPMTEST_CHECK([ +runroot rpmsign --resign --key-id E8A62C0512B06B5D2183BA207F1C21F95F65BBE8 /tmp/hello-2.0-1.x86_64.rpm &> /dev/null +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm +], +[0], +[/tmp/hello-2.0-1.x86_64.rpm: + Header OpenPGP V4 ECDSA/SHA512 signature, key fingerprint: e8a62c0512b06b5d2183ba207f1c21f95f65bbe8: OK + Header SHA256 digest: OK + Payload SHA256 digest: OK +], +[]) + +RPMTEST_CHECK([ +runroot rpmsign --delsign /tmp/hello-2.0-1.x86_64.rpm &> /dev/null +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm +], +[0], +[/tmp/hello-2.0-1.x86_64.rpm: + Header SHA256 digest: OK + Payload SHA256 digest: OK +], +[]) + +RPMTEST_CLEANUP + +AT_SETUP([rpmsign --addsign multisig v6]) +AT_KEYWORDS([rpmsign signature multisig v6]) +AT_SKIP_IF([test x$PGP = xdummy]) +RPMDB_INIT + +RPMTEST_CHECK([ +cat << EOF >> ${HOME}/.rpmmacros +%_openpgp_sign sq +%_openpgp_sign_id 771B18D3D7BAA28734333C424344591E1964C5FC +EOF + +runroot_other sq key import /data/keys/*.secret +runroot rpmkeys --import /data/keys/*.pub +], +[0], +[ignore], +[ignore]) + +RPMTEST_CHECK([ +runroot rpmbuild -bb --quiet \ + --define "_rpmfilever 6" \ + /data/SPECS/attrtest.spec +runroot rpmsign --addsign /build/RPMS/noarch/attrtest-1.0-1.noarch.rpm +], +[0], +[], +[ignore]) + +RPMTEST_CHECK([ +runroot rpmkeys -K /build/RPMS/noarch/attrtest-1.0-1.noarch.rpm +], +[0], +[/build/RPMS/noarch/attrtest-1.0-1.noarch.rpm: digests signatures OK +], +[]) + +RPMTEST_CHECK([ +for t in DSAHEADER RSAHEADER SIGGPG SIGPGP OPENPGP; do + runroot rpm -qp --qf "$t: %{$t}\n" /build/RPMS/noarch/attrtest-1.0-1.noarch.rpm | grep \(none\) +done +], +[ignore], +[DSAHEADER: (none) +RSAHEADER: (none) +SIGGPG: (none) +SIGPGP: (none) +], +[]) + +RPMTEST_CHECK([ +runroot rpmsign --resign --rpmv4 \ + --key-id E8A62C0512B06B5D2183BA207F1C21F95F65BBE8 \ + /build/RPMS/noarch/attrtest-1.0-1.noarch.rpm +], +[0], +[], +[ignore]) + +RPMTEST_CHECK([ +runroot rpmkeys -K /build/RPMS/noarch/attrtest-1.0-1.noarch.rpm +], +[0], +[/build/RPMS/noarch/attrtest-1.0-1.noarch.rpm: digests signatures OK +], +[]) + +RPMTEST_CHECK([ +for t in DSAHEADER RSAHEADER SIGGPG SIGPGP OPENPGP; do + runroot rpm -qp --qf "$t: %{$t}\n" /build/RPMS/noarch/attrtest-1.0-1.noarch.rpm | grep \(none\) +done +], +[ignore], +[RSAHEADER: (none) +SIGGPG: (none) +SIGPGP: (none) +], +[]) +RPMTEST_CLEANUP + # ------------------------------ # Test --delsign AT_SETUP([rpmsign --delsign]) diff --git a/tests/rpmvfylevel.at b/tests/rpmvfylevel.at index 3562d96dce..f34713153a 100644 --- a/tests/rpmvfylevel.at +++ b/tests/rpmvfylevel.at @@ -346,6 +346,7 @@ nohdrs 0 nosig /data/RPMS/hello-2.0-1.x86_64-signed.rpm: + Header OpenPGP signature: NOTFOUND Header OpenPGP RSA signature: NOTFOUND Header OpenPGP DSA signature: NOTFOUND Header SHA256 digest: OK diff --git a/tools/rpmsign.cc b/tools/rpmsign.cc index 16063991a3..006fb2d478 100644 --- a/tools/rpmsign.cc +++ b/tools/rpmsign.cc @@ -43,7 +43,13 @@ static struct poptOption signOptsTable[] = { #endif { "rpmv3", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &sargs.signflags, RPMSIGN_FLAG_RPMV3, - N_("create rpm v3 header+payload signatures") }, + N_("request legacy rpm v3 header+payload signatures on v4 packages") }, + { "rpmv4", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), + &sargs.signflags, RPMSIGN_FLAG_RPMV4, + N_("request legacy rpm v4 header signatures on v6 packages") }, + { "rpmv6", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), + &sargs.signflags, RPMSIGN_FLAG_RPMV6, + N_("request rpm v6 header signature on v4 packages") }, #ifdef WITH_IMAEVM { "signfiles", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &sargs.signflags, RPMSIGN_FLAG_IMA, @@ -207,8 +213,10 @@ int main(int argc, char *argv[]) #endif switch (mode) { - case MODE_ADDSIGN: case MODE_RESIGN: + sargs.signflags |= RPMSIGN_FLAG_RESIGN; + /* fallthrough */ + case MODE_ADDSIGN: ec = doSign(optCon, &sargs); break; case MODE_DELSIGN: