diff options
author | wangbiao <biao716.wang@samsung.com> | 2023-11-16 18:17:42 +0900 |
---|---|---|
committer | wangbiao <biao716.wang@samsung.com> | 2023-11-16 18:17:42 +0900 |
commit | c30d127e8780dc678168ee121b9f2eeb1a8aaafa (patch) | |
tree | cdc9ddc3123edd5741e5151f3076c9bfc9535916 /sign | |
parent | 258ff2bdb80c458b743417c427f66fa5f27bf7c5 (diff) | |
download | librpm-tizen-c30d127e8780dc678168ee121b9f2eeb1a8aaafa.tar.gz librpm-tizen-c30d127e8780dc678168ee121b9f2eeb1a8aaafa.tar.bz2 librpm-tizen-c30d127e8780dc678168ee121b9f2eeb1a8aaafa.zip |
Upgrade version to 4.14tizen/4.14.1.1.tizen20230628
Change-Id: I21bf1a3a7c25cbec43022202cf2e5865b603a309
Signed-off-by: wangbiao <biao716.wang@samsung.com>
Diffstat (limited to 'sign')
-rw-r--r-- | sign/Makefile.am | 9 | ||||
-rw-r--r-- | sign/rpmgensig.c | 756 | ||||
-rw-r--r-- | sign/rpmsign.h | 13 | ||||
-rw-r--r-- | sign/rpmsignfiles.c | 135 | ||||
-rw-r--r-- | sign/rpmsignfiles.h | 25 |
5 files changed, 687 insertions, 251 deletions
diff --git a/sign/Makefile.am b/sign/Makefile.am index db82702bc..db774de0e 100644 --- a/sign/Makefile.am +++ b/sign/Makefile.am @@ -1,8 +1,10 @@ # Makefile for rpm library. include $(top_srcdir)/rpm.am +AM_CFLAGS = @RPMCFLAGS@ AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) -I$(top_builddir)/include/ +AM_CPPFLAGS += @WITH_BEECRYPT_INCLUDE@ AM_CPPFLAGS += @WITH_NSS_INCLUDE@ AM_CPPFLAGS += @WITH_POPT_INCLUDE@ AM_CPPFLAGS += -I$(top_srcdir)/misc @@ -11,9 +13,14 @@ usrlibdir = $(libdir) usrlib_LTLIBRARIES = librpmsign.la librpmsign_la_SOURCES = rpmgensig.c -librpmsign_la_LDFLAGS = -version-info 2:0:1 +librpmsign_la_LDFLAGS = -version-info $(rpm_version_info) librpmsign_la_LIBADD = \ $(top_builddir)/lib/librpm.la \ $(top_builddir)/rpmio/librpmio.la \ @WITH_POPT_LIB@ \ @LIBINTL@ + +if WITH_IMAEVM +librpmsign_la_SOURCES += rpmsignfiles.c rpmsignfiles.h +librpmsign_la_LIBADD += @WITH_IMAEVM_LIB@ +endif diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c index 7695ffad7..d29c17837 100644 --- a/sign/rpmgensig.c +++ b/sign/rpmgensig.c @@ -8,6 +8,7 @@ #include <errno.h> #include <sys/wait.h> #include <popt.h> +#include <libgen.h> #include <rpm/rpmlib.h> /* RPMSIGTAG & related */ #include <rpm/rpmmacro.h> @@ -16,15 +17,82 @@ #include <rpm/rpmfileutil.h> /* rpmMkTemp() */ #include <rpm/rpmlog.h> #include <rpm/rpmstring.h> +#include <rpmio/rpmio_internal.h> #include "lib/rpmlead.h" #include "lib/signature.h" +#include "sign/rpmsignfiles.h" #include "debug.h" -#if !defined(__GLIBC__) && !defined(__APPLE__) -char ** environ = NULL; -#endif +typedef struct sigTarget_s { + FD_t fd; + const char *fileName; + off_t start; + rpm_loff_t size; +} *sigTarget; + +/* + * There is no function for creating unique temporary fifos so create + * unique temporary directory and then create fifo in it. + */ +static char *mkTempFifo(void) +{ + char *tmppath = NULL, *tmpdir = NULL, *fifofn = NULL; + mode_t mode; + + tmppath = rpmExpand("%{_tmppath}", NULL); + if (rpmioMkpath(tmppath, 0755, (uid_t) -1, (gid_t) -1)) + goto exit; + + + tmpdir = rpmGetPath(tmppath, "/rpm-tmp.XXXXXX", NULL); + mode = umask(0077); + tmpdir = mkdtemp(tmpdir); + umask(mode); + if (tmpdir == NULL) { + rpmlog(RPMLOG_ERR, _("error creating temp directory %s: %m\n"), + tmpdir); + tmpdir = _free(tmpdir); + goto exit; + } + + fifofn = rpmGetPath(tmpdir, "/fifo", NULL); + if (mkfifo(fifofn, 0600) == -1) { + rpmlog(RPMLOG_ERR, _("error creating fifo %s: %m\n"), fifofn); + fifofn = _free(fifofn); + } + +exit: + if (fifofn == NULL && tmpdir != NULL) + unlink(tmpdir); + + free(tmppath); + free(tmpdir); + + return fifofn; +} + +/* Delete fifo and then temporary directory in which it was located */ +static int rpmRmTempFifo(const char *fn) +{ + int rc = 0; + char *dfn = NULL, *dir = NULL; + + if ((rc = unlink(fn)) != 0) { + rpmlog(RPMLOG_ERR, _("error delete fifo %s: %m\n"), fn); + return rc; + } + + dfn = xstrdup(fn); + dir = dirname(dfn); + + if ((rc = rmdir(dir)) != 0) + rpmlog(RPMLOG_ERR, _("error delete directory %s: %m\n"), dir); + free(dfn); + + return rc; +} static int closeFile(FD_t *fdp) { @@ -42,13 +110,26 @@ static int closeFile(FD_t *fdp) static int manageFile(FD_t *fdp, const char *fn, int flags) { FD_t fd; + const char *fmode; if (fdp == NULL || fn == NULL) /* programmer error */ return 1; /* open a file and set *fdp */ if (*fdp == NULL && fn != NULL) { - fd = Fopen(fn, (flags & O_ACCMODE) == O_WRONLY ? "w.ufdio" : "r.ufdio"); + switch (flags & O_ACCMODE) { + case O_WRONLY: + fmode = "w.ufdio"; + break; + case O_RDONLY: + fmode = "r.ufdio"; + break; + default: + case O_RDWR: + fmode = "r+.ufdio"; + break; + } + fd = Fopen(fn, fmode); if (fd == NULL || Ferror(fd)) { rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), fn, Fstrerror(fd)); @@ -68,6 +149,10 @@ static int manageFile(FD_t *fdp, const char *fn, int flags) /** * Copy header+payload, calculating digest(s) on the fly. + * @param sfdp source file + * @param sfnp source path + * @param tfdp destination file + * @param tfnp destination path */ static int copyFile(FD_t *sfdp, const char *sfnp, FD_t *tfdp, const char *tfnp) @@ -76,11 +161,6 @@ static int copyFile(FD_t *sfdp, const char *sfnp, ssize_t count; int rc = 1; - if (manageFile(sfdp, sfnp, O_RDONLY)) - goto exit; - if (manageFile(tfdp, tfnp, O_WRONLY|O_CREAT|O_TRUNC)) - goto exit; - while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), *sfdp)) > 0) { if (Fwrite(buf, sizeof(buf[0]), count, *tfdp) != count) { @@ -101,8 +181,6 @@ static int copyFile(FD_t *sfdp, const char *sfnp, rc = 0; exit: - if (*sfdp) (void) closeFile(sfdp); - if (*tfdp) (void) closeFile(tfdp); return rc; } @@ -110,14 +188,13 @@ exit: * Validate generated signature and insert to header if it looks sane. * NSS doesn't support everything GPG does. Basic tests to see if the * generated signature is something we can use. - * Return 0 on success, 1 on failure. + * Return generated signature tag data on success, NULL on failure. */ -static int putSignature(Header sigh, int ishdr, uint8_t *pkt, size_t pktlen) +static rpmtd makeSigTag(Header sigh, int ishdr, uint8_t *pkt, size_t pktlen) { pgpDigParams sigp = NULL; rpmTagVal sigtag; - struct rpmtd_s sigtd; - int rc = 1; /* assume failure */ + rpmtd sigtd = NULL; unsigned int hash_algo; unsigned int pubkey_algo; @@ -147,48 +224,42 @@ static int putSignature(Header sigh, int ishdr, uint8_t *pkt, size_t pktlen) break; } - /* Looks sane, insert into header */ - rpmtdReset(&sigtd); - sigtd.count = pktlen; - sigtd.data = pkt; - sigtd.type = RPM_BIN_TYPE; - sigtd.tag = sigtag; - - /* Argh, reversed return codes */ - rc = (headerPut(sigh, &sigtd, HEADERPUT_DEFAULT) == 0); + /* Looks sane, create the tag data */ + sigtd = rpmtdNew(); + sigtd->count = pktlen; + sigtd->data = memcpy(xmalloc(pktlen), pkt, pktlen);; + sigtd->type = RPM_BIN_TYPE; + sigtd->tag = sigtag; + sigtd->flags |= RPMTD_ALLOCED; exit: pgpDigParamsFree(sigp); - return rc; + return sigtd; } -static int runGPG(const char *file, const char *sigfile, const char * passPhrase) +static int runGPG(sigTarget sigt, const char *sigfile) { - int pid, status; - int inpipe[2]; - FILE * fpipe; + int pid = 0, status; + FD_t fnamedPipe = NULL; + char *namedPipeName = NULL; + unsigned char buf[BUFSIZ]; + ssize_t count; + ssize_t wantCount; + rpm_loff_t size; int rc = 1; /* assume failure */ - inpipe[0] = inpipe[1] = 0; - if (pipe(inpipe) < 0) { - rpmlog(RPMLOG_ERR, _("Couldn't create pipe for signing: %m")); - goto exit; - } + namedPipeName = mkTempFifo(); - addMacro(NULL, "__plaintext_filename", NULL, file, -1); - addMacro(NULL, "__signature_filename", NULL, sigfile, -1); + rpmPushMacro(NULL, "__plaintext_filename", NULL, namedPipeName, -1); + rpmPushMacro(NULL, "__signature_filename", NULL, sigfile, -1); if (!(pid = fork())) { char *const *av; char *cmd = NULL; const char *gpg_path = rpmExpand("%{?_gpg_path}", NULL); - (void) dup2(inpipe[0], 3); - (void) close(inpipe[1]); - if (gpg_path && *gpg_path != '\0') (void) setenv("GNUPGHOME", gpg_path, 1); - (void) setenv("LC_ALL", "C", 1); unsetenv("MALLOC_CHECK_"); cmd = rpmExpand("%{?__gpg_sign_cmd}", NULL); @@ -201,23 +272,61 @@ static int runGPG(const char *file, const char *sigfile, const char * passPhrase _exit(EXIT_FAILURE); } - delMacro(NULL, "__plaintext_filename"); - delMacro(NULL, "__signature_filename"); + rpmPopMacro(NULL, "__plaintext_filename"); + rpmPopMacro(NULL, "__signature_filename"); - fpipe = fdopen(inpipe[1], "w"); - (void) close(inpipe[0]); - if (fpipe) { - fprintf(fpipe, "%s\n", (passPhrase ? passPhrase : "")); - (void) fclose(fpipe); + fnamedPipe = Fopen(namedPipeName, "w"); + if (!fnamedPipe) { + rpmlog(RPMLOG_ERR, _("Fopen failed\n")); + goto exit; + } + + if (Fseek(sigt->fd, sigt->start, SEEK_SET) < 0) { + rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), + sigt->fileName, Fstrerror(sigt->fd)); + goto exit; } + size = sigt->size; + wantCount = size < sizeof(buf) ? size : sizeof(buf); + while ((count = Fread(buf, sizeof(buf[0]), wantCount, sigt->fd)) > 0) { + Fwrite(buf, sizeof(buf[0]), count, fnamedPipe); + if (Ferror(fnamedPipe)) { + rpmlog(RPMLOG_ERR, _("Could not write to pipe\n")); + goto exit; + } + size -= count; + wantCount = size < sizeof(buf) ? size : sizeof(buf); + } + if (count < 0) { + rpmlog(RPMLOG_ERR, _("Could not read from file %s: %s\n"), + sigt->fileName, Fstrerror(sigt->fd)); + goto exit; + } + Fclose(fnamedPipe); + fnamedPipe = NULL; + (void) waitpid(pid, &status, 0); + pid = 0; if (!WIFEXITED(status) || WEXITSTATUS(status)) { rpmlog(RPMLOG_ERR, _("gpg exec failed (%d)\n"), WEXITSTATUS(status)); } else { rc = 0; } + exit: + + if (fnamedPipe) + Fclose(fnamedPipe); + + if (pid) + waitpid(pid, &status, 0); + + if (namedPipeName) { + rpmRmTempFifo(namedPipeName); + free(namedPipeName); + } + return rc; } @@ -225,20 +334,19 @@ exit: * Generate GPG signature(s) for a header+payload file. * @param sigh signature header * @param ishdr header-only signature? - * @param file header+payload file name + * @param sigt signature target * @param passPhrase private key pass phrase - * @return 0 on success, 1 on failure + * @return generated sigtag on success, 0 on failure */ -static int makeGPGSignature(Header sigh, int ishdr, - const char * file, const char * passPhrase) +static rpmtd makeGPGSignature(Header sigh, int ishdr, sigTarget sigt) { - char * sigfile = rstrscat(NULL, file, ".sig", NULL); + char * sigfile = rstrscat(NULL, sigt->fileName, ".sig", NULL); struct stat st; uint8_t * pkt = NULL; size_t pktlen = 0; - int rc = 1; /* assume failure */ + rpmtd sigtd = NULL; - if (runGPG(file, sigfile, passPhrase)) + if (runGPG(sigt, sigfile)) goto exit; if (stat(sigfile, &st)) { @@ -253,7 +361,7 @@ static int makeGPGSignature(Header sigh, int ishdr, { FD_t fd; - rc = 0; + int rc = 0; fd = Fopen(sigfile, "r.ufdio"); if (fd != NULL && !Ferror(fd)) { rc = Fread(pkt, sizeof(*pkt), pktlen, fd); @@ -268,233 +376,346 @@ static int makeGPGSignature(Header sigh, int ishdr, rpmlog(RPMLOG_DEBUG, "Got %zd bytes of GPG sig\n", pktlen); /* Parse the signature, change signature tag as appropriate. */ - rc = putSignature(sigh, ishdr, pkt, pktlen); + sigtd = makeSigTag(sigh, ishdr, pkt, pktlen); exit: (void) unlink(sigfile); free(sigfile); free(pkt); - return rc; + return sigtd; } -/** - * Generate header only signature(s) from a header+payload file. - * @param sigh signature header - * @param file header+payload file name - * @param passPhrase private key pass phrase - * @return 0 on success, -1 on failure - */ -static int makeHDRSignature(Header sigh, const char * file, - const char * passPhrase) +static void deleteSigs(Header sigh) { - Header h = NULL; - FD_t fd = NULL; - char * fn = NULL; - int ret = -1; /* assume failure. */ + headerDel(sigh, RPMSIGTAG_GPG); + headerDel(sigh, RPMSIGTAG_PGP); + headerDel(sigh, RPMSIGTAG_DSA); + headerDel(sigh, RPMSIGTAG_RSA); + headerDel(sigh, RPMSIGTAG_PGP5); +} + +static int haveSignature(rpmtd sigtd, Header h) +{ + pgpDigParams sig1 = NULL; + pgpDigParams sig2 = NULL; + struct rpmtd_s oldtd; + int rc = 0; /* assume no */ + + if (!headerGet(h, rpmtdTag(sigtd), &oldtd, HEADERGET_DEFAULT)) + return rc; + + pgpPrtParams(sigtd->data, sigtd->count, PGPTAG_SIGNATURE, &sig1); + while (rpmtdNext(&oldtd) >= 0 && rc == 0) { + pgpPrtParams(oldtd.data, oldtd.count, PGPTAG_SIGNATURE, &sig2); + if (pgpDigParamsCmp(sig1, sig2) == 0) + rc = 1; + pgpDigParamsFree(sig2); + } + pgpDigParamsFree(sig1); + rpmtdFreeData(&oldtd); + + return rc; +} - fd = Fopen(file, "r.fdio"); - if (fd == NULL || Ferror(fd)) +static int replaceSignature(Header sigh, 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) goto exit; - h = headerRead(fd, HEADER_MAGIC_YES); - if (h == NULL) + + /* See if we already have a signature by the same key and parameters */ + if (haveSignature(sigtd, sigh)) { + rc = 1; goto exit; - (void) Fclose(fd); + } + /* Nuke all signature tags */ + deleteSigs(sigh); - fd = rpmMkTempFile(NULL, &fn); - if (fd == NULL || Ferror(fd)) + if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) goto exit; - if (headerWrite(fd, h, HEADER_MAGIC_YES)) + rpmtdFree(sigtd); + + /* Assume the same signature test holds for v3 signature too */ + if ((sigtd = makeGPGSignature(sigh, 0, sigt_v3)) == NULL) goto exit; - ret = makeGPGSignature(sigh, 1, fn, passPhrase); + if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) + goto exit; + rc = 0; exit: - if (fn) { - (void) unlink(fn); - free(fn); + rpmtdFree(sigtd); + return rc; +} + +static void unloadImmutableRegion(Header *hdrp, rpmTagVal tag) +{ + struct rpmtd_s td; + rpmtd utd = &td; + Header nh; + Header oh; + + if (headerGet(*hdrp, tag, utd, HEADERGET_DEFAULT)) { + oh = headerCopyLoad(utd->data); + nh = headerCopy(oh); + headerFree(oh); + rpmtdFreeData(utd); + headerFree(*hdrp); + *hdrp = headerLink(nh); + headerFree(nh); } - headerFree(h); - if (fd != NULL) (void) Fclose(fd); - return ret; } -static int rpmGenSignature(Header sigh, const char * file, - const char * passPhrase) +#ifdef WITH_IMAEVM +static rpmRC replaceSigDigests(FD_t fd, const char *rpm, Header *sigp, + off_t sigStart, off_t sigTargetSize, + char *SHA256, char *SHA1, uint8_t *MD5) { - int ret = -1; /* assume failure. */ + off_t archiveSize; + rpmRC rc = RPMRC_OK; - if (makeGPGSignature(sigh, 0, file, passPhrase) == 0) { - /* XXX Piggyback a header-only DSA/RSA signature as well. */ - ret = makeHDRSignature(sigh, file, passPhrase); + if (Fseek(fd, sigStart, SEEK_SET) < 0) { + rc = RPMRC_FAIL; + rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), + rpm, Fstrerror(fd)); + goto exit; } - return ret; -} + /* Get payload size from signature tag */ + archiveSize = headerGetNumber(*sigp, RPMSIGTAG_PAYLOADSIZE); + if (!archiveSize) { + archiveSize = headerGetNumber(*sigp, RPMSIGTAG_LONGARCHIVESIZE); + } -/** - * Retrieve signature from header tag - * @param sigh signature header - * @param sigtag signature tag - * @return parsed pgp dig or NULL - */ -static pgpDigParams getSig(Header sigh, rpmTagVal sigtag) -{ - struct rpmtd_s pkt; - pgpDigParams sig = NULL; + /* Set reserved space to 0 */ + rpmPushMacro(NULL, "__gpg_reserved_space", NULL, 0, RMIL_GLOBAL); - if (headerGet(sigh, sigtag, &pkt, HEADERGET_DEFAULT) && pkt.data != NULL) { - pgpPrtParams(pkt.data, pkt.count, PGPTAG_SIGNATURE, &sig); - rpmtdFreeData(&pkt); + /* Replace old digests in sigh */ + rc = rpmGenerateSignature(SHA256, SHA1, MD5, sigTargetSize, archiveSize, fd); + if (rc != RPMRC_OK) { + rpmlog(RPMLOG_ERR, _("generateSignature failed\n")); + goto exit; } - return sig; -} -static void deleteSigs(Header sigh) -{ - headerDel(sigh, RPMSIGTAG_GPG); - headerDel(sigh, RPMSIGTAG_PGP); - headerDel(sigh, RPMSIGTAG_DSA); - headerDel(sigh, RPMSIGTAG_RSA); - headerDel(sigh, RPMSIGTAG_PGP5); + if (Fseek(fd, sigStart, SEEK_SET) < 0) { + rc = RPMRC_FAIL; + rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), + rpm, Fstrerror(fd)); + goto exit; + } + + headerFree(*sigp); + rc = rpmReadSignature(fd, sigp, NULL); + if (rc != RPMRC_OK) { + rpmlog(RPMLOG_ERR, _("rpmReadSignature failed\n")); + goto exit; + } + +exit: + return rc; } +#endif -static int sameSignature(rpmTagVal sigtag, Header h1, Header h2) +static rpmRC includeFileSignatures(FD_t fd, const char *rpm, + Header *sigp, Header *hdrp, + off_t sigStart, off_t headerStart) { - pgpDigParams sig1 = getSig(h1, sigtag); - pgpDigParams sig2 = getSig(h2, sigtag);; +#ifdef WITH_IMAEVM + FD_t ofd = NULL; + char *trpm = NULL; + char *key; + char *keypass; + char *SHA1 = NULL; + char *SHA256 = NULL; + uint8_t *MD5 = NULL; + off_t sigTargetSize; + rpmRC rc = RPMRC_OK; + struct rpmtd_s osigtd; + char *o_sha1 = NULL; + + unloadImmutableRegion(hdrp, RPMTAG_HEADERIMMUTABLE); + + key = rpmExpand("%{?_file_signing_key}", NULL); + + keypass = rpmExpand("%{?_file_signing_key_password}", NULL); + if (rstreq(keypass, "")) { + free(keypass); + keypass = NULL; + } - int rc = pgpDigParamsCmp(sig1, sig2); + rc = rpmSignFiles(*hdrp, key, keypass); + if (rc != RPMRC_OK) { + goto exit; + } - pgpDigParamsFree(sig1); - pgpDigParamsFree(sig2); - return (rc == 0); -} + *hdrp = headerReload(*hdrp, RPMTAG_HEADERIMMUTABLE); + if (*hdrp == NULL) { + rc = RPMRC_FAIL; + rpmlog(RPMLOG_ERR, _("headerReload failed\n")); + goto exit; + } -static int replaceSignature(Header sigh, const char *sigtarget, - const char *passPhrase) -{ - /* Grab a copy of the header so we can compare the result */ - Header oldsigh = headerCopy(sigh); - int rc = -1; - - /* Nuke all signature tags */ - deleteSigs(sigh); + ofd = rpmMkTempFile(NULL, &trpm); + if (ofd == NULL || Ferror(ofd)) { + rc = RPMRC_FAIL; + rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n")); + goto exit; + } + + /* Copy archive to temp file */ + if (copyFile(&fd, rpm, &ofd, trpm)) { + rc = RPMRC_FAIL; + rpmlog(RPMLOG_ERR, _("copyFile failed\n")); + goto exit; + } + + if (Fseek(fd, headerStart, SEEK_SET) < 0) { + rc = RPMRC_FAIL; + rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), + rpm, Fstrerror(fd)); + goto exit; + } - /* - * rpmGenSignature() internals parse the actual signing result and - * adds appropriate tags for DSA/RSA. - */ - if (rpmGenSignature(sigh, sigtarget, passPhrase) == 0) { - /* Lets see what we got and whether its the same signature as before */ - rpmTagVal sigtag = headerIsEntry(sigh, RPMSIGTAG_DSA) ? - RPMSIGTAG_DSA : RPMSIGTAG_RSA; + /* Start MD5 calculation */ + fdInitDigestID(fd, PGPHASHALGO_MD5, RPMSIGTAG_MD5, 0); - rc = sameSignature(sigtag, sigh, oldsigh); + /* Write header to rpm and recalculate digests */ + fdInitDigestID(fd, PGPHASHALGO_SHA1, RPMSIGTAG_SHA1, 0); + fdInitDigestID(fd, PGPHASHALGO_SHA256, RPMSIGTAG_SHA256, 0); + rc = headerWrite(fd, *hdrp, HEADER_MAGIC_YES); + if (rc != RPMRC_OK) { + rpmlog(RPMLOG_ERR, _("headerWrite failed\n")); + goto exit; + } + fdFiniDigest(fd, RPMSIGTAG_SHA1, (void **)&SHA1, NULL, 1); + /* Only add SHA256 if it was there to begin with */ + if (headerIsEntry(*sigp, RPMSIGTAG_SHA256)) + fdFiniDigest(fd, RPMSIGTAG_SHA256, (void **)&SHA256, NULL, 1); + + /* Copy archive from temp file */ + if (Fseek(ofd, 0, SEEK_SET) < 0) { + rc = RPMRC_FAIL; + rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), + rpm, Fstrerror(fd)); + goto exit; + } + if (copyFile(&ofd, trpm, &fd, rpm)) { + rc = RPMRC_FAIL; + rpmlog(RPMLOG_ERR, _("copyFile failed\n")); + goto exit; + } + unlink(trpm); + + sigTargetSize = Ftell(fd) - headerStart; + fdFiniDigest(fd, RPMSIGTAG_MD5, (void **)&MD5, NULL, 0); + if (headerGet(*sigp, RPMSIGTAG_SHA1, &osigtd, HEADERGET_DEFAULT)) { + o_sha1 = xstrdup(osigtd.data); + rpmtdFreeData(&osigtd); } - headerFree(oldsigh); + if (strcmp(SHA1, o_sha1) == 0) + rpmlog(RPMLOG_WARNING, + _("%s already contains identical file signatures\n"), + rpm); + else + replaceSigDigests(fd, rpm, sigp, sigStart, sigTargetSize, SHA256, SHA1, MD5); + +exit: + free(trpm); + free(MD5); + free(SHA1); + free(SHA256); + free(o_sha1); + free(keypass); + free(key); + if (ofd) + (void) closeFile(&ofd); return rc; +#else + rpmlog(RPMLOG_ERR, _("file signing support not built in\n")); + return RPMRC_FAIL; +#endif } /** \ingroup rpmcli * Create/modify elements in signature header. * @param rpm path to package * @param deleting adding or deleting signature? - * @param passPhrase passPhrase (ignored when deleting) + * @param signfiles sign files if non-zero * @return 0 on success, -1 on error */ -static int rpmSign(const char *rpm, int deleting, const char *passPhrase) +static int rpmSign(const char *rpm, int deleting, int signfiles) { FD_t fd = NULL; FD_t ofd = NULL; - rpmlead lead = NULL; - char *sigtarget = NULL, *trpm = NULL; + char *trpm = NULL; Header sigh = NULL; - char * msg = NULL; + Header h = NULL; + char *msg = NULL; int res = -1; /* assume failure */ rpmRC rc; struct rpmtd_s utd; + off_t headerStart; + off_t sigStart; + struct sigTarget_s sigt_v3; + struct sigTarget_s sigt_v4; + unsigned int origSigSize; + int insSig = 0; fprintf(stdout, "%s:\n", rpm); - if (manageFile(&fd, rpm, O_RDONLY)) + if (manageFile(&fd, rpm, O_RDWR)) goto exit; - if ((rc = rpmLeadRead(fd, &lead, NULL, &msg)) != RPMRC_OK) { + if ((rc = rpmLeadRead(fd, NULL, &msg)) != RPMRC_OK) { rpmlog(RPMLOG_ERR, "%s: %s\n", rpm, msg); - free(msg); goto exit; } - rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg); - switch (rc) { - default: + sigStart = Ftell(fd); + rc = rpmReadSignature(fd, &sigh, &msg); + if (rc != RPMRC_OK) { rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), rpm, (msg && *msg ? msg : "\n")); - msg = _free(msg); goto exit; - break; - case RPMRC_OK: - if (sigh == NULL) { - rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), rpm); - goto exit; - } - break; } - msg = _free(msg); - ofd = rpmMkTempFile(NULL, &sigtarget); - if (ofd == NULL || Ferror(ofd)) { - rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n")); + headerStart = Ftell(fd); + if (rpmReadHeader(NULL, fd, &h, &msg) != RPMRC_OK) { + rpmlog(RPMLOG_ERR, _("%s: headerRead failed: %s\n"), rpm, msg); goto exit; } - /* Write the header and archive to a temp file */ - if (copyFile(&fd, rpm, &ofd, sigtarget)) - goto exit; - /* Both fd and ofd are now closed. sigtarget contains tempfile name. */ - - /* Dump the immutable region (if present). */ - if (headerGet(sigh, RPMTAG_HEADERSIGNATURES, &utd, HEADERGET_DEFAULT)) { - struct rpmtd_s copytd; - Header nh = headerNew(); - Header oh = headerCopyLoad(utd.data); - HeaderIterator hi = headerInitIterator(oh); - while (headerNext(hi, ©td)) { - if (copytd.data) - headerPut(nh, ©td, HEADERPUT_DEFAULT); - rpmtdFreeData(©td); - } - headerFreeIterator(hi); - headerFree(oh); - headerFree(sigh); - sigh = headerLink(nh); - headerFree(nh); + if (!headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) { + rpmlog(RPMLOG_ERR, _("Cannot sign RPM v3 packages\n")); + goto exit; } - /* Eliminate broken digest values. */ - headerDel(sigh, RPMSIGTAG_BADSHA1_1); - headerDel(sigh, RPMSIGTAG_BADSHA1_2); - - /* Toss and recalculate header+payload size and digests. */ - { - rpmTagVal const sigs[] = { RPMSIGTAG_SIZE, - RPMSIGTAG_MD5, - RPMSIGTAG_SHA1, - }; - int nsigs = sizeof(sigs) / sizeof(rpmTagVal); - for (int i = 0; i < nsigs; i++) { - (void) headerDel(sigh, sigs[i]); - if (rpmGenDigest(sigh, sigtarget, sigs[i])) - goto exit; - } + if (signfiles) { + includeFileSignatures(fd, rpm, &sigh, &h, sigStart, headerStart); } + unloadImmutableRegion(&sigh, RPMTAG_HEADERSIGNATURES); + origSigSize = headerSizeof(sigh, HEADER_MAGIC_YES); + if (deleting) { /* Nuke all the signature tags. */ deleteSigs(sigh); } else { - res = replaceSignature(sigh, sigtarget, passPhrase); + /* Signature target containing header + payload */ + sigt_v3.fd = fd; + sigt_v3.start = headerStart; + sigt_v3.fileName = rpm; + sigt_v3.size = fdSize(fd) - headerStart; + + /* Signature target containing only header */ + sigt_v4 = sigt_v3; + sigt_v4.size = headerSizeof(h, HEADER_MAGIC_YES); + + res = replaceSignature(sigh, &sigt_v3, &sigt_v4); if (res != 0) { if (res == 1) { rpmlog(RPMLOG_WARNING, @@ -505,6 +726,31 @@ static int rpmSign(const char *rpm, int deleting, const char *passPhrase) } goto exit; } + res = -1; + } + + /* Try to make new signature smaller to have size of original signature */ + rpmtdReset(&utd); + if (headerGet(sigh, RPMSIGTAG_RESERVEDSPACE, &utd, HEADERGET_MINMEM)) { + int diff; + int count; + char *reservedSpace = NULL; + + count = utd.count; + diff = headerSizeof(sigh, HEADER_MAGIC_YES) - origSigSize; + + if (diff < count) { + reservedSpace = xcalloc(count - diff, sizeof(char)); + headerDel(sigh, RPMSIGTAG_RESERVEDSPACE); + rpmtdReset(&utd); + utd.tag = RPMSIGTAG_RESERVEDSPACE; + utd.count = count - diff; + utd.type = RPM_BIN_TYPE; + utd.data = reservedSpace; + headerPut(sigh, &utd, HEADERPUT_DEFAULT); + free(reservedSpace); + insSig = 1; + } } /* Reallocate the signature into one contiguous region. */ @@ -512,38 +758,60 @@ static int rpmSign(const char *rpm, int deleting, const char *passPhrase) if (sigh == NULL) /* XXX can't happen */ goto exit; - rasprintf(&trpm, "%s.XXXXXX", rpm); - ofd = rpmMkTemp(trpm); - if (ofd == NULL || Ferror(ofd)) { - rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n")); - goto exit; - } + if (insSig) { + /* Insert new signature into original rpm */ + if (Fseek(fd, sigStart, SEEK_SET) < 0) { + rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), + rpm, Fstrerror(fd)); + goto exit; + } - /* Write the lead/signature of the output rpm */ - rc = rpmLeadWrite(ofd, lead); - if (rc != RPMRC_OK) { - rpmlog(RPMLOG_ERR, _("%s: writeLead failed: %s\n"), trpm, - Fstrerror(ofd)); - goto exit; - } + if (rpmWriteSignature(fd, sigh)) { + rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), rpm, + Fstrerror(fd)); + goto exit; + } + res = 0; + } else { + /* Replace orignal rpm with new rpm containing new signature */ + rasprintf(&trpm, "%s.XXXXXX", rpm); + ofd = rpmMkTemp(trpm); + if (ofd == NULL || Ferror(ofd)) { + rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n")); + goto exit; + } - if (rpmWriteSignature(ofd, sigh)) { - rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), trpm, - Fstrerror(ofd)); - goto exit; - } + /* Write the lead/signature of the output rpm */ + rc = rpmLeadWrite(ofd, h); + if (rc != RPMRC_OK) { + rpmlog(RPMLOG_ERR, _("%s: writeLead failed: %s\n"), trpm, + Fstrerror(ofd)); + goto exit; + } - /* Append the header and archive from the temp file */ - if (copyFile(&fd, sigtarget, &ofd, trpm) == 0) { - struct stat st; + if (rpmWriteSignature(ofd, sigh)) { + rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), trpm, + Fstrerror(ofd)); + goto exit; + } - /* Move final target into place, restore file permissions. */ - if (stat(rpm, &st) == 0 && unlink(rpm) == 0 && - rename(trpm, rpm) == 0 && chmod(rpm, st.st_mode) == 0) { - res = 0; - } else { - rpmlog(RPMLOG_ERR, _("replacing %s failed: %s\n"), - rpm, strerror(errno)); + if (Fseek(fd, headerStart, SEEK_SET) < 0) { + rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), + rpm, Fstrerror(fd)); + goto exit; + } + /* Append the header and archive from the temp file */ + if (copyFile(&fd, rpm, &ofd, trpm) == 0) { + struct stat st; + + /* Move final target into place, restore file permissions. */ + if (stat(rpm, &st) == 0 && unlink(rpm) == 0 && + rename(trpm, rpm) == 0 && chmod(rpm, st.st_mode) == 0) { + res = 0; + } else { + rpmlog(RPMLOG_ERR, _("replacing %s failed: %s\n"), + rpm, strerror(errno)); + } } } @@ -551,14 +819,11 @@ exit: if (fd) (void) closeFile(&fd); if (ofd) (void) closeFile(&ofd); - rpmFreeSignature(sigh); - rpmLeadFree(lead); + headerFree(sigh); + headerFree(h); + free(msg); /* Clean up intermediate target */ - if (sigtarget) { - unlink(sigtarget); - free(sigtarget); - } if (trpm) { (void) unlink(trpm); free(trpm); @@ -567,8 +832,7 @@ exit: return res; } -int rpmPkgSign(const char *path, - const struct rpmSignArgs * args, const char *passPhrase) +int rpmPkgSign(const char *path, const struct rpmSignArgs * args) { int rc; @@ -576,29 +840,29 @@ int rpmPkgSign(const char *path, if (args->hashalgo) { char *algo = NULL; rasprintf(&algo, "%d", args->hashalgo); - addMacro(NULL, "_gpg_digest_algo", NULL, algo, RMIL_GLOBAL); + rpmPushMacro(NULL, "_gpg_digest_algo", NULL, algo, RMIL_GLOBAL); free(algo); } if (args->keyid) { - addMacro(NULL, "_gpg_name", NULL, args->keyid, RMIL_GLOBAL); + rpmPushMacro(NULL, "_gpg_name", NULL, args->keyid, RMIL_GLOBAL); } } - rc = rpmSign(path, 0, passPhrase); + rc = rpmSign(path, 0, args ? args->signfiles : 0); if (args) { if (args->hashalgo) { - delMacro(NULL, "_gpg_digest_algo"); + rpmPopMacro(NULL, "_gpg_digest_algo"); } if (args->keyid) { - delMacro(NULL, "_gpg_name"); + rpmPopMacro(NULL, "_gpg_name"); } } return rc; } -int rpmPkgDelSign(const char *path) +int rpmPkgDelSign(const char *path, const struct rpmSignArgs * args) { - return rpmSign(path, 1, NULL); + return rpmSign(path, 1, 0); } diff --git a/sign/rpmsign.h b/sign/rpmsign.h index 15b3e0fe8..bed8d6245 100644 --- a/sign/rpmsign.h +++ b/sign/rpmsign.h @@ -1,6 +1,11 @@ #ifndef _RPMSIGN_H #define _RPMSIGN_H +/** \file sign/rpmsign.h + * + * Signature API + */ + #include <rpm/argv.h> #include <rpm/rpmpgp.h> @@ -11,6 +16,7 @@ extern "C" { struct rpmSignArgs { char *keyid; pgpHashAlgo hashalgo; + int signfiles; /* ... what else? */ }; @@ -18,18 +24,17 @@ struct rpmSignArgs { * Sign a package * @param path path to package * @param args signing parameters (or NULL for defaults) - * @param passPhrase passphrase for the signing key * @return 0 on success */ -int rpmPkgSign(const char *path, - const struct rpmSignArgs * args, const char *passPhrase); +int rpmPkgSign(const char *path, const struct rpmSignArgs * args); /** \ingroup rpmsign * Delete signature(s) from a package * @param path path to package + * @param args signing parameters (or NULL for defaults) * @return 0 on success */ -int rpmPkgDelSign(const char *path); +int rpmPkgDelSign(const char *path, const struct rpmSignArgs * args); #ifdef __cplusplus } diff --git a/sign/rpmsignfiles.c b/sign/rpmsignfiles.c new file mode 100644 index 000000000..61b73bd40 --- /dev/null +++ b/sign/rpmsignfiles.c @@ -0,0 +1,135 @@ +/** + * Copyright (C) 2014 IBM Corporation + * + * Author: Fionnuala Gunter <fin@linux.vnet.ibm.com> + */ + +#include "system.h" +#include "imaevm.h" + +#include <rpm/rpmlog.h> /* rpmlog */ +#include <rpm/rpmstring.h> /* rnibble */ +#include <rpm/rpmpgp.h> /* rpmDigestLength */ +#include "lib/header.h" /* HEADERGET_MINMEM */ +#include "lib/rpmtypes.h" /* rpmRC */ + +#include "sign/rpmsignfiles.h" + +#define MAX_SIGNATURE_LENGTH 1024 + +static const char *hash_algo_name[] = { + [PGPHASHALGO_MD5] = "md5", + [PGPHASHALGO_SHA1] = "sha1", + [PGPHASHALGO_RIPEMD160] = "rmd160", + [PGPHASHALGO_MD2] = "md2", + [PGPHASHALGO_TIGER192] = "tgr192", + [PGPHASHALGO_HAVAL_5_160] = "haval5160", + [PGPHASHALGO_SHA256] = "sha256", + [PGPHASHALGO_SHA384] = "sha384", + [PGPHASHALGO_SHA512] = "sha512", + [PGPHASHALGO_SHA224] = "sha224", +}; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +static char *signFile(const char *algo, const char *fdigest, int diglen, +const char *key, char *keypass) +{ + char *fsignature; + unsigned char digest[diglen]; + unsigned char signature[MAX_SIGNATURE_LENGTH]; + int siglen; + + /* convert file digest hex to binary */ + memset(digest, 0, diglen); + /* some entries don't have a digest - we return an empty signature */ + if (strlen(fdigest) != diglen * 2) + return strdup(""); + + for (int i = 0; i < diglen; ++i, fdigest += 2) + digest[i] = (rnibble(fdigest[0]) << 4) | rnibble(fdigest[1]); + + /* prepare file signature */ + memset(signature, 0, MAX_SIGNATURE_LENGTH); + signature[0] = '\x03'; + + /* calculate file signature */ + siglen = sign_hash(algo, digest, diglen, key, keypass, signature+1); + if (siglen < 0) { + rpmlog(RPMLOG_ERR, _("sign_hash failed\n")); + return NULL; + } + + /* convert file signature binary to hex */ + fsignature = pgpHexStr(signature, siglen+1); + return fsignature; +} + +static uint32_t signatureLength(const char *algo, int diglen, const char *key, +char *keypass) +{ + unsigned char digest[diglen]; + unsigned char signature[MAX_SIGNATURE_LENGTH]; + + memset(digest, 0, diglen); + memset(signature, 0, MAX_SIGNATURE_LENGTH); + signature[0] = '\x03'; + + uint32_t siglen = sign_hash(algo, digest, diglen, key, keypass, + signature+1); + return siglen + 1; +} + +rpmRC rpmSignFiles(Header h, const char *key, char *keypass) +{ + struct rpmtd_s digests; + int algo; + int diglen; + uint32_t siglen; + const char *algoname; + const char *digest; + char *signature; + rpmRC rc = RPMRC_OK; + + algo = headerGetNumber(h, RPMTAG_FILEDIGESTALGO); + if (!algo) { + /* use default algorithm */ + algo = PGPHASHALGO_MD5; + } else if (algo < 0 || algo >= ARRAY_SIZE(hash_algo_name)) { + rpmlog(RPMLOG_ERR, _("File digest algorithm id is invalid")); + return RPMRC_FAIL; + } + + diglen = rpmDigestLength(algo); + algoname = hash_algo_name[algo]; + if (!algoname) { + rpmlog(RPMLOG_ERR, _("hash_algo_name failed\n")); + return RPMRC_FAIL; + } + + headerDel(h, RPMTAG_FILESIGNATURELENGTH); + headerDel(h, RPMTAG_FILESIGNATURES); + siglen = signatureLength(algoname, diglen, key, keypass); + headerPutUint32(h, RPMTAG_FILESIGNATURELENGTH, &siglen, 1); + + headerGet(h, RPMTAG_FILEDIGESTS, &digests, HEADERGET_MINMEM); + while ((digest = rpmtdNextString(&digests))) { + signature = signFile(algoname, digest, diglen, key, keypass); + if (!signature) { + rpmlog(RPMLOG_ERR, _("signFile failed\n")); + rc = RPMRC_FAIL; + goto exit; + } + if (!headerPutString(h, RPMTAG_FILESIGNATURES, signature)) { + free(signature); + rpmlog(RPMLOG_ERR, _("headerPutString failed\n")); + rc = RPMRC_FAIL; + goto exit; + } + free(signature); + } + +exit: + rpmtdFreeData(&digests); + return rc; +} diff --git a/sign/rpmsignfiles.h b/sign/rpmsignfiles.h new file mode 100644 index 000000000..4163fafde --- /dev/null +++ b/sign/rpmsignfiles.h @@ -0,0 +1,25 @@ +#ifndef H_RPMSIGNFILES +#define H_RPMSIGNFILES + +#include <rpm/rpmtypes.h> +#include <rpm/rpmutil.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Sign file digests in header and store the signatures in header + * @param h package header + * @param key signing key + * @param keypass signing key password + * @return RPMRC_OK on success + */ +RPM_GNUC_INTERNAL +rpmRC rpmSignFiles(Header h, const char *key, char *keypass); + +#ifdef _cplusplus +} +#endif + +#endif /* H_RPMSIGNFILES */ |