2 * \file lib/signature.c
5 /* signature.c - RPM signature functions */
9 * Things have been cleaned up wrt PGP. We can now handle
10 * signatures of any length (which means you can use any
11 * size key you like). We also honor PGPPATH finally.
16 #if HAVE_ASM_BYTEORDER_H
17 #include <asm/byteorder.h>
21 #include <rpmmacro.h> /* XXX for rpmGetPath */
26 #include "signature.h"
29 /*@access Header@*/ /* XXX compared with NULL */
31 typedef unsigned char byte;
33 typedef int (*md5func)(const char * fn, /*@out@*/ byte * digest);
35 int rpmLookupSignatureType(int action)
37 static int disabled = 0;
41 case RPMLOOKUPSIG_DISABLE:
44 case RPMLOOKUPSIG_ENABLE:
47 case RPMLOOKUPSIG_QUERY:
50 { const char *name = rpmExpand("%{_signature}", NULL);
51 if (!(name && *name != '%'))
53 else if (!xstrcasecmp(name, "none"))
55 else if (!xstrcasecmp(name, "pgp"))
57 else if (!xstrcasecmp(name, "pgp5")) /* XXX legacy */
59 else if (!xstrcasecmp(name, "gpg"))
62 rc = -1; /* Invalid %_signature spec in macro file */
69 /* rpmDetectPGPVersion() returns the absolute path to the "pgp" */
70 /* executable of the requested version, or NULL when none found. */
72 const char * rpmDetectPGPVersion(pgpVersion * pgpVer)
74 /* Actually this should support having more then one pgp version. */
75 /* At the moment only one version is possible since we only */
76 /* have one %_pgpbin and one %_pgp_path. */
78 static pgpVersion saved_pgp_version = PGP_UNKNOWN;
79 const char *pgpbin = rpmGetPath("%{_pgpbin}", NULL);
81 if (saved_pgp_version == PGP_UNKNOWN) {
85 if (!(pgpbin && pgpbin[0] != '%')) {
86 if (pgpbin) free((void *)pgpbin);
87 saved_pgp_version = -1;
90 pgpvbin = (char *)alloca(strlen(pgpbin) + sizeof("v"));
91 (void)stpcpy(stpcpy(pgpvbin, pgpbin), "v");
93 if (stat(pgpvbin, &st) == 0)
94 saved_pgp_version = PGP_5;
95 else if (stat(pgpbin, &st) == 0)
96 saved_pgp_version = PGP_2;
98 saved_pgp_version = PGP_NOTDETECTED;
101 if (pgpbin && pgpVer)
102 *pgpVer = saved_pgp_version;
106 static inline int checkSize(FD_t fd, int siglen, int pad, int datalen)
110 fstat(Fileno(fd), &st);
112 if (!S_ISREG(st.st_mode)) {
113 rpmMessage(RPMMESS_DEBUG,
114 _("file is not regular -- skipping size check\n"));
117 rpmMessage(RPMMESS_DEBUG,
118 _("Expected size: %12d = lead(%d)+sigs(%d)+pad(%d)+data(%d)\n"),
119 sizeof(struct rpmlead)+siglen+pad+datalen,
120 sizeof(struct rpmlead), siglen, pad, datalen);
121 rpmMessage(RPMMESS_DEBUG,
122 _(" Actual size: %12d\n"), st.st_size);
124 return ((sizeof(struct rpmlead) + siglen + pad + datalen) - st.st_size);
127 int rpmReadSignature(FD_t fd, Header * headerp, sigType sig_type)
134 int rc = 1; /* assume failure */
139 /* XXX Yuck, see ALPHA_LOSSAGE in lib/rpmchecksig.c */
141 if (sig_type == RPMSIGTYPE_NONE) sig_type = RPMSIGTYPE_HEADERSIG;
145 case RPMSIGTYPE_NONE:
146 rpmMessage(RPMMESS_DEBUG, _("No signature\n"));
149 case RPMSIGTYPE_PGP262_1024:
150 rpmMessage(RPMMESS_DEBUG, _("Old PGP signature\n"));
151 /* These are always 256 bytes */
152 if (timedRead(fd, buf, 256) != 256)
155 headerAddEntry(h, RPMSIGTAG_PGP, RPM_BIN_TYPE, buf, 152);
159 case RPMSIGTYPE_MD5_PGP:
160 rpmError(RPMERR_BADSIGTYPE,
161 _("Old (internal-only) signature! How did you get that!?\n"));
163 case RPMSIGTYPE_HEADERSIG:
164 case RPMSIGTYPE_DISABLE:
165 /* This is a new style signature */
166 h = headerRead(fd, HEADER_MAGIC_YES);
169 sigSize = headerSizeof(h, HEADER_MAGIC_YES);
171 /* XXX Legacy headers have a HEADER_IMAGE tag added. */
172 if (headerIsEntry(h, RPMTAG_HEADERIMAGE))
173 sigSize -= (16 + 16);
175 pad = (8 - (sigSize % 8)) % 8; /* 8-byte pad */
176 if (sig_type == RPMSIGTYPE_HEADERSIG) {
177 if (! headerGetEntry(h, RPMSIGTAG_SIZE, &type,
178 (void **)&archSize, &count))
180 if (checkSize(fd, sigSize, pad, *archSize))
184 if (timedRead(fd, buf, pad) != pad)
193 if (rc == 0 && headerp)
201 int rpmWriteSignature(FD_t fd, Header h)
203 static byte buf[8] = "\000\000\000\000\000\000\000\000";
207 rc = headerWrite(fd, h, HEADER_MAGIC_YES);
211 sigSize = headerSizeof(h, HEADER_MAGIC_YES);
212 pad = (8 - (sigSize % 8)) % 8;
214 if (Fwrite(buf, sizeof(buf[0]), pad, fd) != pad)
217 rpmMessage(RPMMESS_DEBUG, _("Signature: size(%d)+pad(%d)\n"), sigSize, pad);
221 Header rpmNewSignature(void)
223 Header h = headerNew();
227 void rpmFreeSignature(Header h)
232 static int makePGPSignature(const char * file, /*@out@*/ void ** sig,
233 /*@out@*/ int_32 * size, const char * passPhrase)
235 char * sigfile = alloca(1024);
240 (void) stpcpy( stpcpy(sigfile, file), ".sig");
242 inpipe[0] = inpipe[1] = 0;
245 if (!(pid = fork())) {
246 const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
247 const char *name = rpmExpand("+myname=\"%{_pgp_name}\"", NULL);
255 dosetenv("PGPPASSFD", "3", 1);
256 if (pgp_path && *pgp_path != '%')
257 dosetenv("PGPPATH", pgp_path, 1);
259 /* dosetenv("PGPPASS", passPhrase, 1); */
261 if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
264 execlp(path, "pgp", "+batchmode=on", "+verbose=0", "+armor=off",
265 name, "-sb", file, sigfile, NULL);
268 execlp(path,"pgps", "+batchmode=on", "+verbose=0", "+armor=off",
269 name, "-b", file, "-o", sigfile, NULL);
272 case PGP_NOTDETECTED:
276 rpmError(RPMERR_EXEC, _("Couldn't exec pgp (%s)\n"), path);
281 (void)write(inpipe[1], passPhrase, strlen(passPhrase));
282 (void)write(inpipe[1], "\n", 1);
285 (void)waitpid(pid, &status, 0);
286 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
287 rpmError(RPMERR_SIGGEN, _("pgp failed\n"));
291 if (stat(sigfile, &st)) {
292 /* PGP failed to write signature */
293 unlink(sigfile); /* Just in case */
294 rpmError(RPMERR_SIGGEN, _("pgp failed to write signature\n"));
299 rpmMessage(RPMMESS_DEBUG, _("PGP sig size: %d\n"), *size);
300 *sig = xmalloc(*size);
304 fd = Fopen(sigfile, "r.fdio");
305 rc = timedRead(fd, *sig, *size);
310 rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
315 rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of PGP sig\n"), *size);
320 /* This is an adaptation of the makePGPSignature function to use GPG instead
321 * of PGP to create signatures. I think I've made all the changes necessary,
322 * but this could be a good place to start looking if errors in GPG signature
325 static int makeGPGSignature(const char * file, /*@out@*/ void ** sig,
326 /*@out@*/ int_32 * size, const char * passPhrase)
328 char * sigfile = alloca(1024);
334 (void) stpcpy( stpcpy(sigfile, file), ".sig");
336 inpipe[0] = inpipe[1] = 0;
339 if (!(pid = fork())) {
340 const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
341 const char *name = rpmExpand("%{_gpg_name}", NULL);
347 if (gpg_path && *gpg_path != '%')
348 dosetenv("GNUPGHOME", gpg_path, 1);
350 "--batch", "--no-verbose", "--no-armor", "--passphrase-fd", "3",
351 "-u", name, "-sbo", sigfile, file,
353 rpmError(RPMERR_EXEC, _("Couldn't exec gpg\n"));
357 fpipe = fdopen(inpipe[1], "w");
359 fprintf(fpipe, "%s\n", passPhrase);
362 (void)waitpid(pid, &status, 0);
363 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
364 rpmError(RPMERR_SIGGEN, _("gpg failed\n"));
368 if (stat(sigfile, &st)) {
369 /* GPG failed to write signature */
370 unlink(sigfile); /* Just in case */
371 rpmError(RPMERR_SIGGEN, _("gpg failed to write signature\n"));
376 rpmMessage(RPMMESS_DEBUG, _("GPG sig size: %d\n"), *size);
377 *sig = xmalloc(*size);
381 fd = Fopen(sigfile, "r.fdio");
382 rc = timedRead(fd, *sig, *size);
387 rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
392 rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of GPG sig\n"), *size);
397 int rpmAddSignature(Header header, const char * file, int_32 sigTag,
398 const char *passPhrase)
411 headerAddEntry(header, RPMSIGTAG_SIZE, RPM_INT32_TYPE, &size, 1);
414 ret = mdbinfile(file, buf);
416 headerAddEntry(header, sigTag, RPM_BIN_TYPE, buf, 16);
418 case RPMSIGTAG_PGP5: /* XXX legacy */
420 rpmMessage(RPMMESS_VERBOSE, _("Generating signature using PGP.\n"));
421 ret = makePGPSignature(file, &sig, &size, passPhrase);
423 headerAddEntry(header, sigTag, RPM_BIN_TYPE, sig, size);
426 rpmMessage(RPMMESS_VERBOSE, _("Generating signature using GPG.\n"));
427 ret = makeGPGSignature(file, &sig, &size, passPhrase);
429 headerAddEntry(header, sigTag, RPM_BIN_TYPE, sig, size);
436 static rpmVerifySignatureReturn
437 verifySizeSignature(const char * datafile, int_32 size, char * result)
442 if (size != st.st_size) {
443 sprintf(result, "Header+Archive size mismatch.\n"
444 "Expected %d, saw %d.\n",
445 size, (int)st.st_size);
449 sprintf(result, "Header+Archive size OK: %d bytes\n", size);
453 #define X(_x) (unsigned)((_x) & 0xff)
455 static rpmVerifySignatureReturn
456 verifyMD5Signature(const char * datafile, const byte * sig,
457 char * result, md5func fn)
461 fn(datafile, md5sum);
462 if (memcmp(md5sum, sig, 16)) {
463 sprintf(result, "MD5 sum mismatch\n"
464 "Expected: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
465 "%02x%02x%02x%02x%02x\n"
466 "Saw : %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
467 "%02x%02x%02x%02x%02x\n",
468 X(sig[0]), X(sig[1]), X(sig[2]), X(sig[3]),
469 X(sig[4]), X(sig[5]), X(sig[6]), X(sig[7]),
470 X(sig[8]), X(sig[9]), X(sig[10]), X(sig[11]),
471 X(sig[12]), X(sig[13]), X(sig[14]), X(sig[15]),
472 X(md5sum[0]), X(md5sum[1]), X(md5sum[2]), X(md5sum[3]),
473 X(md5sum[4]), X(md5sum[5]), X(md5sum[6]), X(md5sum[7]),
474 X(md5sum[8]), X(md5sum[9]), X(md5sum[10]), X(md5sum[11]),
475 X(md5sum[12]), X(md5sum[13]), X(md5sum[14]), X(md5sum[15]) );
479 sprintf(result, "MD5 sum OK: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
480 "%02x%02x%02x%02x%02x\n",
481 X(md5sum[0]), X(md5sum[1]), X(md5sum[2]), X(md5sum[3]),
482 X(md5sum[4]), X(md5sum[5]), X(md5sum[6]), X(md5sum[7]),
483 X(md5sum[8]), X(md5sum[9]), X(md5sum[10]), X(md5sum[11]),
484 X(md5sum[12]), X(md5sum[13]), X(md5sum[14]), X(md5sum[15]) );
489 static rpmVerifySignatureReturn
490 verifyPGPSignature(const char * datafile, const void * sig, int count,
493 int pid, status, outpipe[2];
502 /* What version do we have? */
503 if ((path = rpmDetectPGPVersion(&pgpVer)) == NULL) {
505 rpmError(RPMERR_EXEC,
506 _("Could not run pgp. Use --nopgp to skip PGP checks.\n"));
511 * Sad but true: pgp-5.0 returns exit value of 0 on bad signature.
512 * Instead we have to use the text output to detect a bad signature.
517 /* Write out the signature */
518 { const char *tmppath = rpmGetPath("%{_tmppath}", NULL);
519 sigfile = tempnam(tmppath, "rpmsig");
520 free((void *)tmppath);
522 sfd = Fopen(sigfile, "w.fdio");
523 (void)Fwrite(sig, sizeof(char), count, sfd);
527 outpipe[0] = outpipe[1] = 0;
530 if (!(pid = fork())) {
531 const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
534 close(STDOUT_FILENO); /* XXX unnecessary */
535 dup2(outpipe[1], STDOUT_FILENO);
537 if (pgp_path && *pgp_path != '%')
538 dosetenv("PGPPATH", pgp_path, 1);
542 /* Some output (in particular "This signature applies to */
543 /* another message") is _always_ written to stderr; we */
544 /* want to catch that output, so dup stdout to stderr: */
545 { int save_stderr = dup(2);
547 execlp(path, "pgpv", "+batchmode=on", "+verbose=0",
548 /* Write "Good signature..." to stdout: */
549 "+OutputInformationFD=1",
550 /* Write "WARNING: ... is not trusted to... to stdout: */
551 "+OutputWarningFD=1",
552 sigfile, "-o", datafile, NULL);
553 /* Restore stderr so we can print the error message below. */
554 dup2(save_stderr, 2);
558 execlp(path, "pgp", "+batchmode=on", "+verbose=0",
559 sigfile, datafile, NULL);
562 case PGP_NOTDETECTED:
566 rpmError(RPMERR_EXEC,
567 _("Could not run pgp. Use --nopgp to skip PGP checks.\n"));
572 file = fdopen(outpipe[0], "r");
574 while (fgets(buf, 1024, file)) {
575 if (strncmp("File '", buf, 6) &&
576 strncmp("Text is assu", buf, 12) &&
577 strncmp("This signature applies to another message", buf, 41) &&
581 if (!strncmp("WARNING: Can't find the right public key", buf, 40))
583 else if (!strncmp("Signature by unknown keyid:", buf, 27))
585 else if (!strncmp("WARNING: The signing key is not trusted", buf, 39))
586 res = RPMSIG_NOTTRUSTED;
587 else if (!strncmp("Good signature", buf, 14))
592 (void)waitpid(pid, &status, 0);
594 if (!res && (!WIFEXITED(status) || WEXITSTATUS(status))) {
601 static rpmVerifySignatureReturn
602 verifyGPGSignature(const char * datafile, const void * sig, int count,
605 int pid, status, outpipe[2];
612 /* Write out the signature */
613 { const char *tmppath = rpmGetPath("%{_tmppath}", NULL);
614 sigfile = tempnam(tmppath, "rpmsig");
615 free((void *)tmppath);
617 sfd = Fopen(sigfile, "w.fdio");
618 (void)Fwrite(sig, sizeof(char), count, sfd);
622 outpipe[0] = outpipe[1] = 0;
625 if (!(pid = fork())) {
626 const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
629 /* gpg version 0.9 sends its output to stderr. */
630 dup2(outpipe[1], STDERR_FILENO);
632 if (gpg_path && *gpg_path != '%')
633 dosetenv("GNUPGHOME", gpg_path, 1);
636 "--batch", "--no-verbose",
637 "--verify", sigfile, datafile,
639 rpmError(RPMERR_EXEC,
640 _("Could not run gpg. Use --nogpg to skip GPG checks.\n"));
645 file = fdopen(outpipe[0], "r");
647 while (fgets(buf, 1024, file)) {
649 if (!xstrncasecmp("gpg: Can't check signature: Public key not found", buf, 48)) {
655 (void)waitpid(pid, &status, 0);
657 if (!res && (!WIFEXITED(status) || WEXITSTATUS(status))) {
664 static int checkPassPhrase(const char * passPhrase, const int sigTag)
666 int passPhrasePipe[2];
670 passPhrasePipe[0] = passPhrasePipe[1] = 0;
671 pipe(passPhrasePipe);
672 if (!(pid = fork())) {
674 close(STDOUT_FILENO);
675 close(passPhrasePipe[1]);
676 if (! rpmIsVerbose()) {
677 close(STDERR_FILENO);
679 if ((fd = open("/dev/null", O_RDONLY)) != STDIN_FILENO) {
680 dup2(fd, STDIN_FILENO);
683 if ((fd = open("/dev/null", O_WRONLY)) != STDOUT_FILENO) {
684 dup2(fd, STDOUT_FILENO);
687 dup2(passPhrasePipe[0], 3);
691 { const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
692 const char *name = rpmExpand("%{_gpg_name}", NULL);
693 if (gpg_path && *gpg_path != '%')
694 dosetenv("GNUPGHOME", gpg_path, 1);
696 "--batch", "--no-verbose", "--passphrase-fd", "3",
697 "-u", name, "-so", "-",
699 rpmError(RPMERR_EXEC, _("Couldn't exec gpg\n"));
701 } /*@notreached@*/ break;
702 case RPMSIGTAG_PGP5: /* XXX legacy */
704 { const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
705 const char *name = rpmExpand("+myname=\"%{_pgp_name}\"", NULL);
709 dosetenv("PGPPASSFD", "3", 1);
710 if (pgp_path && *pgp_path != '%')
711 dosetenv("PGPPATH", pgp_path, 1);
713 if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
716 execlp(path, "pgp", "+batchmode=on", "+verbose=0",
719 case PGP_5: /* XXX legacy */
720 execlp(path,"pgps", "+batchmode=on", "+verbose=0",
724 case PGP_NOTDETECTED:
728 rpmError(RPMERR_EXEC, _("Couldn't exec pgp\n"));
730 } /*@notreached@*/ break;
731 default: /* This case should have been screened out long ago. */
732 rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
733 _exit(RPMERR_SIGGEN);
734 /*@notreached@*/ break;
738 close(passPhrasePipe[0]);
739 (void)write(passPhrasePipe[1], passPhrase, strlen(passPhrase));
740 (void)write(passPhrasePipe[1], "\n", 1);
741 close(passPhrasePipe[1]);
743 (void)waitpid(pid, &status, 0);
744 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
748 /* passPhrase is good */
752 char *rpmGetPassPhrase(const char * prompt, const int sigTag)
759 { const char *name = rpmExpand("%{_gpg_name}", NULL);
760 aok = (name && *name != '%');
764 rpmError(RPMERR_SIGGEN,
765 _("You must set \"%%_gpg_name\" in your macro file\n"));
769 case RPMSIGTAG_PGP5: /* XXX legacy */
771 { const char *name = rpmExpand("%{_pgp_name}", NULL);
772 aok = (name && *name != '%');
776 rpmError(RPMERR_SIGGEN,
777 _("You must set \"%%_pgp_name\" in your macro file\n"));
782 /* Currently the calling function (rpm.c:main) is checking this and
783 * doing a better job. This section should never be accessed.
785 rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
787 /*@notreached@*/ break;
790 pass = /*@-unrecog@*/ getpass( (prompt ? prompt : "") ) /*@=unrecog@*/ ;
792 if (checkPassPhrase(pass, sigTag))
798 rpmVerifySignatureReturn
799 rpmVerifySignature(const char * file, int_32 sigTag, const void * sig,
800 int count, char * result)
804 return verifySizeSignature(file, *(int_32 *)sig, result);
805 /*@notreached@*/ break;
807 return verifyMD5Signature(file, sig, result, mdbinfile);
808 /*@notreached@*/ break;
809 case RPMSIGTAG_LEMD5_1:
810 case RPMSIGTAG_LEMD5_2:
811 return verifyMD5Signature(file, sig, result, mdbinfileBroken);
812 /*@notreached@*/ break;
813 case RPMSIGTAG_PGP5: /* XXX legacy */
815 return verifyPGPSignature(file, sig, count, result);
816 /*@notreached@*/ break;
818 return verifyGPGSignature(file, sig, count, result);
819 /*@notreached@*/ break;
821 sprintf(result, "Do not know how to verify sig type %d\n", sigTag);
822 return RPMSIG_UNKNOWN;