diff options
author | Panu Matilainen <pmatilai@redhat.com> | 2010-10-22 18:28:08 +0300 |
---|---|---|
committer | Panu Matilainen <pmatilai@redhat.com> | 2010-10-22 18:31:01 +0300 |
commit | bf1b4dede614fba082ac8abdf6ff3bb9560060ac (patch) | |
tree | 2e6e938024c4a24ff721448173bc641ad078c664 /sign | |
parent | 1cb6b12fd2d0367861cb3d2d13fd74a1451831b4 (diff) | |
download | rpm-bf1b4dede614fba082ac8abdf6ff3bb9560060ac.tar.gz rpm-bf1b4dede614fba082ac8abdf6ff3bb9560060ac.tar.bz2 rpm-bf1b4dede614fba082ac8abdf6ff3bb9560060ac.zip |
Eek.. librpmsign sources went missing in the move
- Re-adding the files that should've gotten moved (not removed) in
commit 9f2c1bd9096cbf382c864737a6565e5487767ce2
- Mild wtf ... probably user (not git) error though. Too bad the
broken state got pushed already :-/
Diffstat (limited to 'sign')
-rw-r--r-- | sign/rpmgensig.c | 645 | ||||
-rw-r--r-- | sign/rpmsign.h | 38 |
2 files changed, 683 insertions, 0 deletions
diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c new file mode 100644 index 000000000..44167b091 --- /dev/null +++ b/sign/rpmgensig.c @@ -0,0 +1,645 @@ +/** \ingroup rpmcli + * \file lib/rpmchecksig.c + * Verify the signature of a package. + */ + +#include "system.h" + +#include <errno.h> +#include <sys/wait.h> +#include <popt.h> + +#include <rpm/rpmlib.h> /* RPMSIGTAG & related */ +#include <rpm/rpmmacro.h> +#include <rpm/rpmpgp.h> +#include <rpm/rpmsign.h> +#include <rpm/rpmfileutil.h> /* rpmMkTemp() */ +#include <rpm/rpmlog.h> +#include <rpm/rpmstring.h> + +#include "rpmio/digest.h" +#include "lib/rpmlead.h" +#include "lib/signature.h" + +#include "debug.h" + +#if !defined(__GLIBC__) && !defined(__APPLE__) +char ** environ = NULL; +#endif + +static int closeFile(FD_t *fdp) +{ + if (fdp == NULL || *fdp == NULL) + return 1; + + /* close and reset *fdp to NULL */ + (void) Fclose(*fdp); + *fdp = NULL; + return 0; +} + +/** + */ +static int manageFile(FD_t *fdp, const char *fn, int flags) +{ + FD_t fd; + + 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"); + if (fd == NULL || Ferror(fd)) { + rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), fn, + Fstrerror(fd)); + return 1; + } + *fdp = fd; + return 0; + } + + /* no operation */ + if (*fdp != NULL && fn != NULL) + return 0; + + /* XXX never reached */ + return 1; +} + +/** + * Copy header+payload, calculating digest(s) on the fly. + */ +static int copyFile(FD_t *sfdp, const char *sfnp, + FD_t *tfdp, const char *tfnp) +{ + unsigned char buf[BUFSIZ]; + 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) { + rpmlog(RPMLOG_ERR, _("%s: Fwrite failed: %s\n"), tfnp, + Fstrerror(*tfdp)); + goto exit; + } + } + if (count < 0) { + rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), sfnp, Fstrerror(*sfdp)); + goto exit; + } + if (Fflush(*tfdp) != 0) { + rpmlog(RPMLOG_ERR, _("%s: Fflush failed: %s\n"), tfnp, + Fstrerror(*tfdp)); + } + + rc = 0; + +exit: + if (*sfdp) (void) closeFile(sfdp); + if (*tfdp) (void) closeFile(tfdp); + return rc; +} + +/* + * 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. + */ +static int putSignature(Header sigh, int ishdr, uint8_t *pkt, size_t pktlen) +{ + pgpDig dig = pgpNewDig(); + pgpDigParams sigp = &dig->signature; + rpmTagVal sigtag; + struct rpmtd_s sigtd; + int rc = 1; /* assume failure */ + + if (pgpPrtPkts(pkt, pktlen, dig, 0) != 0) + goto exit; + + if (rpmDigestLength(sigp->hash_algo) == 0) { + rpmlog(RPMLOG_ERR, _("Unsupported PGP hash algorithm %d\n"), + sigp->hash_algo); + goto exit; + } + + switch (sigp->pubkey_algo) { + case PGPPUBKEYALGO_DSA: + sigtag = ishdr ? RPMSIGTAG_DSA : RPMSIGTAG_GPG; + break; + case PGPPUBKEYALGO_RSA: + sigtag = ishdr ? RPMSIGTAG_RSA : RPMSIGTAG_PGP; + break; + default: + rpmlog(RPMLOG_ERR, _("Unsupported PGP pubkey algorithm %d\n"), + sigp->pubkey_algo); + goto exit; + 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); + +exit: + dig = pgpFreeDig(dig); + return rc; +} + +static int runGPG(const char *file, const char *sigfile, const char * passPhrase) +{ + int pid, status; + int inpipe[2]; + FILE * fpipe; + 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; + } + + addMacro(NULL, "__plaintext_filename", NULL, file, -1); + addMacro(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); + rc = poptParseArgvString(cmd, NULL, (const char ***)&av); + if (!rc) + rc = execve(av[0], av+1, environ); + + rpmlog(RPMLOG_ERR, _("Could not exec %s: %s\n"), "gpg", + strerror(errno)); + _exit(EXIT_FAILURE); + } + + delMacro(NULL, "__plaintext_filename"); + delMacro(NULL, "__signature_filename"); + + fpipe = fdopen(inpipe[1], "w"); + (void) close(inpipe[0]); + if (fpipe) { + fprintf(fpipe, "%s\n", (passPhrase ? passPhrase : "")); + (void) fclose(fpipe); + } + + (void) waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + rpmlog(RPMLOG_ERR, _("gpg exec failed (%d)\n"), WEXITSTATUS(status)); + } else { + rc = 0; + } +exit: + return rc; +} + +/** + * 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 passPhrase private key pass phrase + * @return 0 on success, 1 on failure + */ +static int makeGPGSignature(Header sigh, int ishdr, + const char * file, const char * passPhrase) +{ + char * sigfile = rstrscat(NULL, file, ".sig", NULL); + struct stat st; + uint8_t * pkt = NULL; + size_t pktlen = 0; + int rc = 1; /* assume failure */ + + if (runGPG(file, sigfile, passPhrase)) + goto exit; + + if (stat(sigfile, &st)) { + /* GPG failed to write signature */ + rpmlog(RPMLOG_ERR, _("gpg failed to write signature\n")); + goto exit; + } + + pktlen = st.st_size; + rpmlog(RPMLOG_DEBUG, "GPG sig size: %zd\n", pktlen); + pkt = xmalloc(pktlen); + + { FD_t fd; + + rc = 0; + fd = Fopen(sigfile, "r.ufdio"); + if (fd != NULL && !Ferror(fd)) { + rc = Fread(pkt, sizeof(*pkt), pktlen, fd); + (void) Fclose(fd); + } + if (rc != pktlen) { + rpmlog(RPMLOG_ERR, _("unable to read the signature\n")); + goto exit; + } + } + + 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); +exit: + (void) unlink(sigfile); + free(sigfile); + free(pkt); + + return rc; +} + +/** + * 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) +{ + Header h = NULL; + FD_t fd = NULL; + char * fn = NULL; + int ret = -1; /* assume failure. */ + + fd = Fopen(file, "r.fdio"); + if (fd == NULL || Ferror(fd)) + goto exit; + h = headerRead(fd, HEADER_MAGIC_YES); + if (h == NULL) + goto exit; + (void) Fclose(fd); + + fd = rpmMkTempFile(NULL, &fn); + if (fd == NULL || Ferror(fd)) + goto exit; + if (headerWrite(fd, h, HEADER_MAGIC_YES)) + goto exit; + + ret = makeGPGSignature(sigh, 1, fn, passPhrase); + +exit: + if (fn) { + (void) unlink(fn); + free(fn); + } + h = headerFree(h); + if (fd != NULL) (void) Fclose(fd); + return ret; +} + +static int rpmGenSignature(Header sigh, const char * file, + const char * passPhrase) +{ + int ret = -1; /* assume failure. */ + + if (makeGPGSignature(sigh, 0, file, passPhrase) == 0) { + /* XXX Piggyback a header-only DSA/RSA signature as well. */ + ret = makeHDRSignature(sigh, file, passPhrase); + } + + return ret; +} + +/** + * Retrieve signature from header tag + * @param sigh signature header + * @param sigtag signature tag + * @return parsed pgp dig or NULL + */ +static pgpDig getSig(Header sigh, rpmTagVal sigtag) +{ + struct rpmtd_s pkt; + pgpDig dig = NULL; + + if (headerGet(sigh, sigtag, &pkt, HEADERGET_DEFAULT) && pkt.data != NULL) { + dig = pgpNewDig(); + + if (pgpPrtPkts(pkt.data, pkt.count, dig, 0) != 0) { + dig = pgpFreeDig(dig); + } + rpmtdFreeData(&pkt); + } + return dig; +} + +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); +} + +static int sameSignature(rpmTagVal sigtag, Header h1, Header h2) +{ + pgpDig dig1 = getSig(h1, sigtag); + pgpDig dig2 = getSig(h2, sigtag); + int rc = 0; /* assume different, eg if either signature doesn't exist */ + + /* XXX This part really belongs to rpmpgp.[ch] */ + if (dig1 && dig2) { + pgpDigParams sig1 = &dig1->signature; + pgpDigParams sig2 = &dig2->signature; + + /* XXX Should we compare something else too? */ + if (sig1->hash_algo != sig2->hash_algo) + goto exit; + if (sig1->pubkey_algo != sig2->pubkey_algo) + goto exit; + if (sig1->version != sig2->version) + goto exit; + if (sig1->sigtype != sig2->sigtype) + goto exit; + if (memcmp(sig1->signid, sig2->signid, sizeof(sig1->signid)) != 0) + goto exit; + + /* Parameters match, assume same signature */ + rc = 1; + } + +exit: + pgpFreeDig(dig1); + pgpFreeDig(dig2); + return rc; +} + +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); + + /* + * 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; + + rc = sameSignature(sigtag, sigh, oldsigh); + + } + + headerFree(oldsigh); + return rc; +} + +/** \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) + * @return 0 on success, -1 on error + */ +static int rpmSign(const char *rpm, int deleting, const char *passPhrase) +{ + FD_t fd = NULL; + FD_t ofd = NULL; + rpmlead lead; + char *sigtarget = NULL, *trpm = NULL; + Header sigh = NULL; + char * msg; + int res = -1; /* assume failure */ + int xx; + + { + rpmRC rc; + struct rpmtd_s utd; + + fprintf(stdout, "%s:\n", rpm); + + if (manageFile(&fd, rpm, O_RDONLY)) + goto exit; + + lead = rpmLeadNew(); + + if ((rc = rpmLeadRead(fd, lead)) == RPMRC_OK) { + const char *lmsg = NULL; + rc = rpmLeadCheck(lead, &lmsg); + if (rc != RPMRC_OK) + rpmlog(RPMLOG_ERR, "%s: %s\n", rpm, lmsg); + } + + if (rc != RPMRC_OK) { + lead = rpmLeadFree(lead); + goto exit; + } + + msg = NULL; + rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg); + switch (rc) { + default: + 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); + + /* ASSERT: ofd == NULL && sigtarget == NULL */ + ofd = rpmMkTempFile(NULL, &sigtarget); + if (ofd == NULL || Ferror(ofd)) { + rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n")); + 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. */ + /* ASSERT: fd == NULL && ofd == NULL */ + + /* 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) + xx = headerPut(nh, ©td, HEADERPUT_DEFAULT); + rpmtdFreeData(©td); + } + hi = headerFreeIterator(hi); + oh = headerFree(oh); + + sigh = headerFree(sigh); + sigh = headerLink(nh); + nh = headerFree(nh); + } + + /* Eliminate broken digest values. */ + xx = headerDel(sigh, RPMSIGTAG_BADSHA1_1); + xx = 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 (deleting) { /* Nuke all the signature tags. */ + deleteSigs(sigh); + } else { + res = replaceSignature(sigh, sigtarget, passPhrase); + if (res != 0) { + if (res == 1) { + rpmlog(RPMLOG_WARNING, + "%s already contains identical signature, skipping\n", + rpm); + /* Identical signature is not an error */ + res = 0; + } + goto exit; + } + } + + /* Reallocate the signature into one contiguous region. */ + sigh = headerReload(sigh, RPMTAG_HEADERSIGNATURES); + 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; + } + + /* Write the lead/signature of the output rpm */ + rc = rpmLeadWrite(ofd, lead); + lead = rpmLeadFree(lead); + if (rc != RPMRC_OK) { + rpmlog(RPMLOG_ERR, _("%s: writeLead failed: %s\n"), trpm, + Fstrerror(ofd)); + goto exit; + } + + if (rpmWriteSignature(ofd, sigh)) { + rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), trpm, + Fstrerror(ofd)); + goto exit; + } + + /* Append the header and archive from the temp file */ + /* ASSERT: fd == NULL && ofd != NULL */ + if (copyFile(&fd, sigtarget, &ofd, trpm)) + goto exit; + /* Both fd and ofd are now closed. */ + /* ASSERT: fd == NULL && ofd == NULL */ + + /* Move final target into place, restore file permissions. */ + { + struct stat st; + xx = stat(rpm, &st); + xx = unlink(rpm); + xx = rename(trpm, rpm); + xx = chmod(rpm, st.st_mode); + } + } + + res = 0; + +exit: + if (fd) (void) closeFile(&fd); + if (ofd) (void) closeFile(&ofd); + + sigh = rpmFreeSignature(sigh); + + /* Clean up intermediate target */ + if (sigtarget) { + xx = unlink(sigtarget); + sigtarget = _free(sigtarget); + } + if (trpm) { + (void) unlink(trpm); + free(trpm); + } + + return res; +} + +int rpmPkgSign(const char *path, + const struct rpmSignArgs * args, const char *passPhrase) +{ + int rc; + + if (args) { + if (args->hashalgo) { + char *algo = NULL; + rasprintf(&algo, "%d", args->hashalgo); + addMacro(NULL, "_gpg_digest_algo", NULL, algo, RMIL_GLOBAL); + free(algo); + } + if (args->keyid) { + addMacro(NULL, "_gpg_name", NULL, args->keyid, RMIL_GLOBAL); + } + } + + rc = rpmSign(path, 0, passPhrase); + + if (args) { + if (args->hashalgo) { + delMacro(NULL, "_gpg_digest_algo"); + } + if (args->keyid) { + delMacro(NULL, "_gpg_name"); + } + } + + return rc; +} + +int rpmPkgDelSign(const char *path) +{ + return rpmSign(path, 1, NULL); +} diff --git a/sign/rpmsign.h b/sign/rpmsign.h new file mode 100644 index 000000000..15b3e0fe8 --- /dev/null +++ b/sign/rpmsign.h @@ -0,0 +1,38 @@ +#ifndef _RPMSIGN_H +#define _RPMSIGN_H + +#include <rpm/argv.h> +#include <rpm/rpmpgp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct rpmSignArgs { + char *keyid; + pgpHashAlgo hashalgo; + /* ... what else? */ +}; + +/** \ingroup rpmsign + * 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); + +/** \ingroup rpmsign + * Delete signature(s) from a package + * @param path path to package + * @return 0 on success + */ +int rpmPkgDelSign(const char *path); + +#ifdef __cplusplus +} +#endif + +#endif /* _RPMSIGN_H */ |