2 * \file lib/rpmchecksig.c
3 * Verify the signature of a package.
13 #include <rpm/rpmlib.h> /* RPMSIGTAG & related */
14 #include <rpm/rpmmacro.h>
15 #include <rpm/rpmpgp.h>
16 #include <rpm/rpmsign.h>
17 #include <rpm/rpmfileutil.h> /* rpmMkTemp() */
18 #include <rpm/rpmlog.h>
19 #include <rpm/rpmstring.h>
20 #include <rpmio/rpmio_internal.h>
22 #include "lib/rpmlead.h"
23 #include "lib/signature.h"
24 #include "sign/rpmsignfiles.h"
28 typedef struct sigTarget_s {
36 * There is no function for creating unique temporary fifos so create
37 * unique temporary directory and then create fifo in it.
39 static char *mkTempFifo(void)
41 char *tmppath = NULL, *tmpdir = NULL, *fifofn = NULL;
44 tmppath = rpmExpand("%{_tmppath}", NULL);
45 if (rpmioMkpath(tmppath, 0755, (uid_t) -1, (gid_t) -1))
49 tmpdir = rpmGetPath(tmppath, "/rpm-tmp.XXXXXX", NULL);
51 tmpdir = mkdtemp(tmpdir);
54 rpmlog(RPMLOG_ERR, _("error creating temp directory %s: %m\n"),
56 tmpdir = _free(tmpdir);
60 fifofn = rpmGetPath(tmpdir, "/fifo", NULL);
61 if (mkfifo(fifofn, 0600) == -1) {
62 rpmlog(RPMLOG_ERR, _("error creating fifo %s: %m\n"), fifofn);
63 fifofn = _free(fifofn);
67 if (fifofn == NULL && tmpdir != NULL)
76 /* Delete fifo and then temporary directory in which it was located */
77 static int rpmRmTempFifo(const char *fn)
80 char *dfn = NULL, *dir = NULL;
82 if ((rc = unlink(fn)) != 0) {
83 rpmlog(RPMLOG_ERR, _("error delete fifo %s: %m\n"), fn);
90 if ((rc = rmdir(dir)) != 0)
91 rpmlog(RPMLOG_ERR, _("error delete directory %s: %m\n"), dir);
97 static int closeFile(FD_t *fdp)
99 if (fdp == NULL || *fdp == NULL)
102 /* close and reset *fdp to NULL */
110 static int manageFile(FD_t *fdp, const char *fn, int flags)
115 if (fdp == NULL || fn == NULL) /* programmer error */
118 /* open a file and set *fdp */
119 if (*fdp == NULL && fn != NULL) {
120 switch (flags & O_ACCMODE) {
132 fd = Fopen(fn, fmode);
133 if (fd == NULL || Ferror(fd)) {
134 rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), fn,
143 if (*fdp != NULL && fn != NULL)
146 /* XXX never reached */
151 * Copy header+payload, calculating digest(s) on the fly.
152 * @param sfdp source file
153 * @param sfnp source path
154 * @param tfdp destination file
155 * @param tfnp destination path
157 static int copyFile(FD_t *sfdp, const char *sfnp,
158 FD_t *tfdp, const char *tfnp)
160 unsigned char buf[BUFSIZ];
164 while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), *sfdp)) > 0)
166 if (Fwrite(buf, sizeof(buf[0]), count, *tfdp) != count) {
167 rpmlog(RPMLOG_ERR, _("%s: Fwrite failed: %s\n"), tfnp,
173 rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), sfnp, Fstrerror(*sfdp));
176 if (Fflush(*tfdp) != 0) {
177 rpmlog(RPMLOG_ERR, _("%s: Fflush failed: %s\n"), tfnp,
188 * Validate generated signature and insert to header if it looks sane.
189 * NSS doesn't support everything GPG does. Basic tests to see if the
190 * generated signature is something we can use.
191 * Return generated signature tag data on success, NULL on failure.
193 static rpmtd makeSigTag(Header sigh, int ishdr, uint8_t *pkt, size_t pktlen)
195 pgpDigParams sigp = NULL;
198 unsigned int hash_algo;
199 unsigned int pubkey_algo;
201 if (pgpPrtParams(pkt, pktlen, PGPTAG_SIGNATURE, &sigp)) {
202 rpmlog(RPMLOG_ERR, _("Unsupported PGP signature\n"));
206 hash_algo = pgpDigParamsAlgo(sigp, PGPVAL_HASHALGO);
207 if (rpmDigestLength(hash_algo) == 0) {
208 rpmlog(RPMLOG_ERR, _("Unsupported PGP hash algorithm %u\n"), hash_algo);
212 pubkey_algo = pgpDigParamsAlgo(sigp, PGPVAL_PUBKEYALGO);
213 switch (pubkey_algo) {
214 case PGPPUBKEYALGO_DSA:
215 sigtag = ishdr ? RPMSIGTAG_DSA : RPMSIGTAG_GPG;
217 case PGPPUBKEYALGO_RSA:
218 sigtag = ishdr ? RPMSIGTAG_RSA : RPMSIGTAG_PGP;
221 rpmlog(RPMLOG_ERR, _("Unsupported PGP pubkey algorithm %u\n"),
227 /* Looks sane, create the tag data */
229 sigtd->count = pktlen;
230 sigtd->data = memcpy(xmalloc(pktlen), pkt, pktlen);;
231 sigtd->type = RPM_BIN_TYPE;
233 sigtd->flags |= RPMTD_ALLOCED;
236 pgpDigParamsFree(sigp);
240 static int runGPG(sigTarget sigt, const char *sigfile)
243 FD_t fnamedPipe = NULL;
244 char *namedPipeName = NULL;
245 unsigned char buf[BUFSIZ];
249 int rc = 1; /* assume failure */
251 namedPipeName = mkTempFifo();
253 rpmPushMacro(NULL, "__plaintext_filename", NULL, namedPipeName, -1);
254 rpmPushMacro(NULL, "__signature_filename", NULL, sigfile, -1);
256 if (!(pid = fork())) {
259 const char *gpg_path = rpmExpand("%{?_gpg_path}", NULL);
261 if (gpg_path && *gpg_path != '\0')
262 (void) setenv("GNUPGHOME", gpg_path, 1);
264 unsetenv("MALLOC_CHECK_");
265 cmd = rpmExpand("%{?__gpg_sign_cmd}", NULL);
266 rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
268 rc = execve(av[0], av+1, environ);
270 rpmlog(RPMLOG_ERR, _("Could not exec %s: %s\n"), "gpg",
275 rpmPopMacro(NULL, "__plaintext_filename");
276 rpmPopMacro(NULL, "__signature_filename");
278 fnamedPipe = Fopen(namedPipeName, "w");
280 rpmlog(RPMLOG_ERR, _("Fopen failed\n"));
284 if (Fseek(sigt->fd, sigt->start, SEEK_SET) < 0) {
285 rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
286 sigt->fileName, Fstrerror(sigt->fd));
291 wantCount = size < sizeof(buf) ? size : sizeof(buf);
292 while ((count = Fread(buf, sizeof(buf[0]), wantCount, sigt->fd)) > 0) {
293 Fwrite(buf, sizeof(buf[0]), count, fnamedPipe);
294 if (Ferror(fnamedPipe)) {
295 rpmlog(RPMLOG_ERR, _("Could not write to pipe\n"));
299 wantCount = size < sizeof(buf) ? size : sizeof(buf);
302 rpmlog(RPMLOG_ERR, _("Could not read from file %s: %s\n"),
303 sigt->fileName, Fstrerror(sigt->fd));
309 (void) waitpid(pid, &status, 0);
311 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
312 rpmlog(RPMLOG_ERR, _("gpg exec failed (%d)\n"), WEXITSTATUS(status));
323 waitpid(pid, &status, 0);
326 rpmRmTempFifo(namedPipeName);
334 * Generate GPG signature(s) for a header+payload file.
335 * @param sigh signature header
336 * @param ishdr header-only signature?
337 * @param sigt signature target
338 * @param passPhrase private key pass phrase
339 * @return generated sigtag on success, 0 on failure
341 static rpmtd makeGPGSignature(Header sigh, int ishdr, sigTarget sigt)
343 char * sigfile = rstrscat(NULL, sigt->fileName, ".sig", NULL);
345 uint8_t * pkt = NULL;
349 if (runGPG(sigt, sigfile))
352 if (stat(sigfile, &st)) {
353 /* GPG failed to write signature */
354 rpmlog(RPMLOG_ERR, _("gpg failed to write signature\n"));
359 rpmlog(RPMLOG_DEBUG, "GPG sig size: %zd\n", pktlen);
360 pkt = xmalloc(pktlen);
365 fd = Fopen(sigfile, "r.ufdio");
366 if (fd != NULL && !Ferror(fd)) {
367 rc = Fread(pkt, sizeof(*pkt), pktlen, fd);
371 rpmlog(RPMLOG_ERR, _("unable to read the signature\n"));
376 rpmlog(RPMLOG_DEBUG, "Got %zd bytes of GPG sig\n", pktlen);
378 /* Parse the signature, change signature tag as appropriate. */
379 sigtd = makeSigTag(sigh, ishdr, pkt, pktlen);
381 (void) unlink(sigfile);
388 static void deleteSigs(Header sigh)
390 headerDel(sigh, RPMSIGTAG_GPG);
391 headerDel(sigh, RPMSIGTAG_PGP);
392 headerDel(sigh, RPMSIGTAG_DSA);
393 headerDel(sigh, RPMSIGTAG_RSA);
394 headerDel(sigh, RPMSIGTAG_PGP5);
397 static int haveSignature(rpmtd sigtd, Header h)
399 pgpDigParams sig1 = NULL;
400 pgpDigParams sig2 = NULL;
401 struct rpmtd_s oldtd;
402 int rc = 0; /* assume no */
404 if (!headerGet(h, rpmtdTag(sigtd), &oldtd, HEADERGET_DEFAULT))
407 pgpPrtParams(sigtd->data, sigtd->count, PGPTAG_SIGNATURE, &sig1);
408 while (rpmtdNext(&oldtd) >= 0 && rc == 0) {
409 pgpPrtParams(oldtd.data, oldtd.count, PGPTAG_SIGNATURE, &sig2);
410 if (pgpDigParamsCmp(sig1, sig2) == 0)
412 pgpDigParamsFree(sig2);
414 pgpDigParamsFree(sig1);
415 rpmtdFreeData(&oldtd);
420 static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4)
425 /* Make the cheaper v4 signature first */
426 if ((sigtd = makeGPGSignature(sigh, 1, sigt_v4)) == NULL)
429 /* See if we already have a signature by the same key and parameters */
430 if (haveSignature(sigtd, sigh)) {
434 /* Nuke all signature tags */
437 if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0)
441 /* Assume the same signature test holds for v3 signature too */
442 if ((sigtd = makeGPGSignature(sigh, 0, sigt_v3)) == NULL)
445 if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0)
454 static void unloadImmutableRegion(Header *hdrp, rpmTagVal tag)
461 if (headerGet(*hdrp, tag, utd, HEADERGET_DEFAULT)) {
462 oh = headerCopyLoad(utd->data);
467 *hdrp = headerLink(nh);
473 static rpmRC replaceSigDigests(FD_t fd, const char *rpm, Header *sigp,
474 off_t sigStart, off_t sigTargetSize,
475 char *SHA256, char *SHA1, uint8_t *MD5)
480 if (Fseek(fd, sigStart, SEEK_SET) < 0) {
482 rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
487 /* Get payload size from signature tag */
488 archiveSize = headerGetNumber(*sigp, RPMSIGTAG_PAYLOADSIZE);
490 archiveSize = headerGetNumber(*sigp, RPMSIGTAG_LONGARCHIVESIZE);
493 /* Set reserved space to 0 */
494 rpmPushMacro(NULL, "__gpg_reserved_space", NULL, 0, RMIL_GLOBAL);
496 /* Replace old digests in sigh */
497 rc = rpmGenerateSignature(SHA256, SHA1, MD5, sigTargetSize, archiveSize, fd);
498 if (rc != RPMRC_OK) {
499 rpmlog(RPMLOG_ERR, _("generateSignature failed\n"));
503 if (Fseek(fd, sigStart, SEEK_SET) < 0) {
505 rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
511 rc = rpmReadSignature(fd, sigp, NULL);
512 if (rc != RPMRC_OK) {
513 rpmlog(RPMLOG_ERR, _("rpmReadSignature failed\n"));
522 static rpmRC includeFileSignatures(FD_t fd, const char *rpm,
523 Header *sigp, Header *hdrp,
524 off_t sigStart, off_t headerStart)
536 struct rpmtd_s osigtd;
539 unloadImmutableRegion(hdrp, RPMTAG_HEADERIMMUTABLE);
541 key = rpmExpand("%{?_file_signing_key}", NULL);
543 keypass = rpmExpand("%{?_file_signing_key_password}", NULL);
544 if (rstreq(keypass, "")) {
549 rc = rpmSignFiles(*hdrp, key, keypass);
550 if (rc != RPMRC_OK) {
554 *hdrp = headerReload(*hdrp, RPMTAG_HEADERIMMUTABLE);
557 rpmlog(RPMLOG_ERR, _("headerReload failed\n"));
561 ofd = rpmMkTempFile(NULL, &trpm);
562 if (ofd == NULL || Ferror(ofd)) {
564 rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
568 /* Copy archive to temp file */
569 if (copyFile(&fd, rpm, &ofd, trpm)) {
571 rpmlog(RPMLOG_ERR, _("copyFile failed\n"));
575 if (Fseek(fd, headerStart, SEEK_SET) < 0) {
577 rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
582 /* Start MD5 calculation */
583 fdInitDigestID(fd, PGPHASHALGO_MD5, RPMSIGTAG_MD5, 0);
585 /* Write header to rpm and recalculate digests */
586 fdInitDigestID(fd, PGPHASHALGO_SHA1, RPMSIGTAG_SHA1, 0);
587 fdInitDigestID(fd, PGPHASHALGO_SHA256, RPMSIGTAG_SHA256, 0);
588 rc = headerWrite(fd, *hdrp, HEADER_MAGIC_YES);
589 if (rc != RPMRC_OK) {
590 rpmlog(RPMLOG_ERR, _("headerWrite failed\n"));
593 fdFiniDigest(fd, RPMSIGTAG_SHA1, (void **)&SHA1, NULL, 1);
594 /* Only add SHA256 if it was there to begin with */
595 if (headerIsEntry(*sigp, RPMSIGTAG_SHA256))
596 fdFiniDigest(fd, RPMSIGTAG_SHA256, (void **)&SHA256, NULL, 1);
598 /* Copy archive from temp file */
599 if (Fseek(ofd, 0, SEEK_SET) < 0) {
601 rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
605 if (copyFile(&ofd, trpm, &fd, rpm)) {
607 rpmlog(RPMLOG_ERR, _("copyFile failed\n"));
612 sigTargetSize = Ftell(fd) - headerStart;
613 fdFiniDigest(fd, RPMSIGTAG_MD5, (void **)&MD5, NULL, 0);
615 if (headerGet(*sigp, RPMSIGTAG_SHA1, &osigtd, HEADERGET_DEFAULT)) {
616 o_sha1 = xstrdup(osigtd.data);
617 rpmtdFreeData(&osigtd);
620 if (strcmp(SHA1, o_sha1) == 0)
621 rpmlog(RPMLOG_WARNING,
622 _("%s already contains identical file signatures\n"),
625 replaceSigDigests(fd, rpm, sigp, sigStart, sigTargetSize, SHA256, SHA1, MD5);
636 (void) closeFile(&ofd);
639 rpmlog(RPMLOG_ERR, _("file signing support not built in\n"));
645 * Create/modify elements in signature header.
646 * @param rpm path to package
647 * @param deleting adding or deleting signature?
648 * @param signfiles sign files if non-zero
649 * @return 0 on success, -1 on error
651 static int rpmSign(const char *rpm, int deleting, int signfiles)
659 int res = -1; /* assume failure */
664 struct sigTarget_s sigt_v3;
665 struct sigTarget_s sigt_v4;
666 unsigned int origSigSize;
669 fprintf(stdout, "%s:\n", rpm);
671 if (manageFile(&fd, rpm, O_RDWR))
674 if ((rc = rpmLeadRead(fd, NULL, &msg)) != RPMRC_OK) {
675 rpmlog(RPMLOG_ERR, "%s: %s\n", rpm, msg);
679 sigStart = Ftell(fd);
680 rc = rpmReadSignature(fd, &sigh, &msg);
681 if (rc != RPMRC_OK) {
682 rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), rpm,
683 (msg && *msg ? msg : "\n"));
687 headerStart = Ftell(fd);
688 if (rpmReadHeader(NULL, fd, &h, &msg) != RPMRC_OK) {
689 rpmlog(RPMLOG_ERR, _("%s: headerRead failed: %s\n"), rpm, msg);
693 if (!headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
694 rpmlog(RPMLOG_ERR, _("Cannot sign RPM v3 packages\n"));
699 includeFileSignatures(fd, rpm, &sigh, &h, sigStart, headerStart);
702 unloadImmutableRegion(&sigh, RPMTAG_HEADERSIGNATURES);
703 origSigSize = headerSizeof(sigh, HEADER_MAGIC_YES);
705 if (deleting) { /* Nuke all the signature tags. */
708 /* Signature target containing header + payload */
710 sigt_v3.start = headerStart;
711 sigt_v3.fileName = rpm;
712 sigt_v3.size = fdSize(fd) - headerStart;
714 /* Signature target containing only header */
716 sigt_v4.size = headerSizeof(h, HEADER_MAGIC_YES);
718 res = replaceSignature(sigh, &sigt_v3, &sigt_v4);
721 rpmlog(RPMLOG_WARNING,
722 _("%s already contains identical signature, skipping\n"),
724 /* Identical signature is not an error */
732 /* Try to make new signature smaller to have size of original signature */
734 if (headerGet(sigh, RPMSIGTAG_RESERVEDSPACE, &utd, HEADERGET_MINMEM)) {
737 char *reservedSpace = NULL;
740 diff = headerSizeof(sigh, HEADER_MAGIC_YES) - origSigSize;
743 reservedSpace = xcalloc(count - diff, sizeof(char));
744 headerDel(sigh, RPMSIGTAG_RESERVEDSPACE);
746 utd.tag = RPMSIGTAG_RESERVEDSPACE;
747 utd.count = count - diff;
748 utd.type = RPM_BIN_TYPE;
749 utd.data = reservedSpace;
750 headerPut(sigh, &utd, HEADERPUT_DEFAULT);
756 /* Reallocate the signature into one contiguous region. */
757 sigh = headerReload(sigh, RPMTAG_HEADERSIGNATURES);
758 if (sigh == NULL) /* XXX can't happen */
762 /* Insert new signature into original rpm */
763 if (Fseek(fd, sigStart, SEEK_SET) < 0) {
764 rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
769 if (rpmWriteSignature(fd, sigh)) {
770 rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), rpm,
776 /* Replace orignal rpm with new rpm containing new signature */
777 rasprintf(&trpm, "%s.XXXXXX", rpm);
778 ofd = rpmMkTemp(trpm);
779 if (ofd == NULL || Ferror(ofd)) {
780 rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
784 /* Write the lead/signature of the output rpm */
785 rc = rpmLeadWrite(ofd, h);
786 if (rc != RPMRC_OK) {
787 rpmlog(RPMLOG_ERR, _("%s: writeLead failed: %s\n"), trpm,
792 if (rpmWriteSignature(ofd, sigh)) {
793 rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), trpm,
798 if (Fseek(fd, headerStart, SEEK_SET) < 0) {
799 rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
803 /* Append the header and archive from the temp file */
804 if (copyFile(&fd, rpm, &ofd, trpm) == 0) {
807 /* Move final target into place, restore file permissions. */
808 if (stat(rpm, &st) == 0 && unlink(rpm) == 0 &&
809 rename(trpm, rpm) == 0 && chmod(rpm, st.st_mode) == 0) {
812 rpmlog(RPMLOG_ERR, _("replacing %s failed: %s\n"),
813 rpm, strerror(errno));
819 if (fd) (void) closeFile(&fd);
820 if (ofd) (void) closeFile(&ofd);
826 /* Clean up intermediate target */
835 int rpmPkgSign(const char *path, const struct rpmSignArgs * args)
840 if (args->hashalgo) {
842 rasprintf(&algo, "%d", args->hashalgo);
843 rpmPushMacro(NULL, "_gpg_digest_algo", NULL, algo, RMIL_GLOBAL);
847 rpmPushMacro(NULL, "_gpg_name", NULL, args->keyid, RMIL_GLOBAL);
851 rc = rpmSign(path, 0, args ? args->signfiles : 0);
854 if (args->hashalgo) {
855 rpmPopMacro(NULL, "_gpg_digest_algo");
858 rpmPopMacro(NULL, "_gpg_name");
865 int rpmPkgDelSign(const char *path, const struct rpmSignArgs * args)
867 return rpmSign(path, 1, 0);