1 /* signature.c - RPM signature functions */
5 * Things have been cleaned up wrt PGP. We can now handle
6 * signatures of any length (which means you can use any
7 * size key you like). We also honor PGPPATH finally.
12 #if HAVE_ASM_BYTEORDER_H
13 #include <asm/byteorder.h>
17 #include <rpmmacro.h> /* XXX for rpmGetPath */
22 #include "signature.h"
24 typedef int (*md5func)(const char * fn, /*@out@*/unsigned char * digest);
26 int rpmLookupSignatureType(int action)
28 static int disabled = 0;
32 case RPMLOOKUPSIG_DISABLE:
35 case RPMLOOKUPSIG_ENABLE:
39 case RPMLOOKUPSIG_QUERY:
42 { const char *name = rpmExpand("%{_signature}", NULL);
43 if (!(name && *name != '%'))
45 else if (!strcasecmp(name, "none"))
47 else if (!strcasecmp(name, "pgp"))
49 else if (!strcasecmp(name, "pgp5")) /* XXX legacy */
51 else if (!strcasecmp(name, "gpg"))
54 rc = -1; /* Invalid %_signature spec in macro file */
61 /* rpmDetectPGPVersion() returns the absolute path to the "pgp" */
62 /* executable of the requested version, or NULL when none found. */
64 const char * rpmDetectPGPVersion(pgpVersion *pgpVer)
66 /* Actually this should support having more then one pgp version. */
67 /* At the moment only one version is possible since we only */
68 /* have one %_pgpbin and one %_pgp_path. */
70 static pgpVersion saved_pgp_version = PGP_UNKNOWN;
71 const char *pgpbin = rpmGetPath("%{_pgpbin}", NULL);
73 if (saved_pgp_version == PGP_UNKNOWN) {
77 if (!(pgpbin && pgpbin[0] != '%') || ! (pgpvbin = (char *)alloca(strlen(pgpbin) + 2))) {
78 if (pgpbin) xfree(pgpbin);
79 saved_pgp_version = -1;
82 sprintf(pgpvbin, "%sv", pgpbin);
84 if (stat(pgpvbin, &statbuf) == 0)
85 saved_pgp_version = PGP_5;
86 else if (stat(pgpbin, &statbuf) == 0)
87 saved_pgp_version = PGP_2;
89 saved_pgp_version = PGP_NOTDETECTED;
93 *pgpVer = saved_pgp_version;
97 static int checkSize(FD_t fd, int size, int sigsize)
99 int headerArchiveSize;
102 fstat(Fileno(fd), &statbuf);
104 if (S_ISREG(statbuf.st_mode)) {
105 headerArchiveSize = statbuf.st_size - sizeof(struct rpmlead) - sigsize;
107 rpmMessage(RPMMESS_DEBUG, _("sigsize : %d\n"), sigsize);
108 rpmMessage(RPMMESS_DEBUG, _("Header + Archive: %d\n"), headerArchiveSize);
109 rpmMessage(RPMMESS_DEBUG, _("expected size : %d\n"), size);
111 return size - headerArchiveSize;
113 rpmMessage(RPMMESS_DEBUG, _("file is not regular -- skipping size check\n"));
118 /* rpmReadSignature() emulates the new style signatures if it finds an */
119 /* old-style one. It also immediately verifies the header+archive */
120 /* size and returns an error if it doesn't match. */
122 int rpmReadSignature(FD_t fd, Header *headerp, short sig_type)
124 unsigned char buf[2048];
135 rpmMessage(RPMMESS_DEBUG, _("No signature\n"));
137 case RPMSIG_PGP262_1024:
138 rpmMessage(RPMMESS_DEBUG, _("Old PGP signature\n"));
139 /* These are always 256 bytes */
140 if (timedRead(fd, buf, 256) != 256)
143 *headerp = headerNew();
144 headerAddEntry(*headerp, RPMSIGTAG_PGP, RPM_BIN_TYPE, buf, 152);
149 rpmError(RPMERR_BADSIGTYPE,
150 _("Old (internal-only) signature! How did you get that!?"));
152 /*@notreached@*/ break;
153 case RPMSIG_HEADERSIG:
154 rpmMessage(RPMMESS_DEBUG, _("New Header signature\n"));
155 /* This is a new style signature */
156 h = headerRead(fd, HEADER_MAGIC_YES);
159 sigSize = headerSizeof(h, HEADER_MAGIC_YES);
160 pad = (8 - (sigSize % 8)) % 8; /* 8-byte pad */
161 rpmMessage(RPMMESS_DEBUG, _("Signature size: %d\n"), sigSize);
162 rpmMessage(RPMMESS_DEBUG, _("Signature pad : %d\n"), pad);
163 if (! headerGetEntry(h, RPMSIGTAG_SIZE, &type, (void **)&archSize, &count)) {
167 if (checkSize(fd, *archSize, sigSize + pad)) {
172 if (timedRead(fd, buf, pad) != pad) {
190 int rpmWriteSignature(FD_t fd, Header header)
193 unsigned char buf[8];
196 rc = headerWrite(fd, header, HEADER_MAGIC_YES);
200 sigSize = headerSizeof(header, HEADER_MAGIC_YES);
201 pad = (8 - (sigSize % 8)) % 8;
203 rpmMessage(RPMMESS_DEBUG, _("Signature size: %d\n"), sigSize);
204 rpmMessage(RPMMESS_DEBUG, _("Signature pad : %d\n"), pad);
206 if (Fwrite(buf, pad, 1, fd) != pad)
212 Header rpmNewSignature(void)
214 Header h = headerNew();
218 void rpmFreeSignature(Header h)
223 static int makePGPSignature(const char *file, /*@out@*/void **sig, /*@out@*/int_32 *size,
224 const char *passPhrase)
231 sprintf(sigfile, "%s.sig", file);
233 inpipe[0] = inpipe[1] = 0;
236 if (!(pid = fork())) {
237 const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
238 const char *name = rpmExpand("+myname=\"%{_pgp_name}\"", NULL);
246 dosetenv("PGPPASSFD", "3", 1);
247 if (pgp_path && *pgp_path != '%')
248 dosetenv("PGPPATH", pgp_path, 1);
250 /* dosetenv("PGPPASS", passPhrase, 1); */
252 if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
255 execlp(path, "pgp", "+batchmode=on", "+verbose=0", "+armor=off",
256 name, "-sb", file, sigfile, NULL);
259 execlp(path,"pgps", "+batchmode=on", "+verbose=0", "+armor=off",
260 name, "-b", file, "-o", sigfile, NULL);
263 case PGP_NOTDETECTED:
267 rpmError(RPMERR_EXEC, _("Couldn't exec pgp (%s)"), path);
272 (void)write(inpipe[1], passPhrase, strlen(passPhrase));
273 (void)write(inpipe[1], "\n", 1);
276 (void)waitpid(pid, &status, 0);
277 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
278 rpmError(RPMERR_SIGGEN, _("pgp failed"));
282 if (stat(sigfile, &statbuf)) {
283 /* PGP failed to write signature */
284 unlink(sigfile); /* Just in case */
285 rpmError(RPMERR_SIGGEN, _("pgp failed to write signature"));
289 *size = statbuf.st_size;
290 rpmMessage(RPMMESS_DEBUG, _("PGP sig size: %d\n"), *size);
291 *sig = xmalloc(*size);
295 fd = fdOpen(sigfile, O_RDONLY, 0);
296 rc = timedRead(fd, *sig, *size);
301 rpmError(RPMERR_SIGGEN, _("unable to read the signature"));
306 rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of PGP sig\n"), *size);
311 /* This is an adaptation of the makePGPSignature function to use GPG instead
312 * of PGP to create signatures. I think I've made all the changes necessary,
313 * but this could be a good place to start looking if errors in GPG signature
316 static int makeGPGSignature(const char *file, /*@out@*/void **sig, /*@out@*/int_32 *size,
317 const char *passPhrase)
325 sprintf(sigfile, "%s.sig", file);
327 inpipe[0] = inpipe[1] = 0;
330 if (!(pid = fork())) {
331 const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
332 const char *name = rpmExpand("%{_gpg_name}", NULL);
338 if (gpg_path && *gpg_path != '%')
339 dosetenv("GNUPGHOME", gpg_path, 1);
341 "--batch", "--no-verbose", "--no-armor", "--passphrase-fd", "3",
342 "-u", name, "-sbo", sigfile, file,
344 rpmError(RPMERR_EXEC, _("Couldn't exec gpg"));
348 fpipe = fdopen(inpipe[1], "w");
350 fprintf(fpipe, "%s\n", passPhrase);
353 (void)waitpid(pid, &status, 0);
354 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
355 rpmError(RPMERR_SIGGEN, _("gpg failed"));
359 if (stat(sigfile, &statbuf)) {
360 /* GPG failed to write signature */
361 unlink(sigfile); /* Just in case */
362 rpmError(RPMERR_SIGGEN, _("gpg failed to write signature"));
366 *size = statbuf.st_size;
367 rpmMessage(RPMMESS_DEBUG, _("GPG sig size: %d\n"), *size);
368 *sig = xmalloc(*size);
372 fd = fdOpen(sigfile, O_RDONLY, 0);
373 rc = timedRead(fd, *sig, *size);
378 rpmError(RPMERR_SIGGEN, _("unable to read the signature"));
383 rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of GPG sig\n"), *size);
388 int rpmAddSignature(Header header, const char *file, int_32 sigTag, const char *passPhrase)
392 unsigned char buf[16];
398 stat(file, &statbuf);
399 size = statbuf.st_size;
401 headerAddEntry(header, RPMSIGTAG_SIZE, RPM_INT32_TYPE, &size, 1);
404 ret = mdbinfile(file, buf);
406 headerAddEntry(header, sigTag, RPM_BIN_TYPE, buf, 16);
408 case RPMSIGTAG_PGP5: /* XXX legacy */
410 rpmMessage(RPMMESS_VERBOSE, _("Generating signature using PGP.\n"));
411 ret = makePGPSignature(file, &sig, &size, passPhrase);
413 headerAddEntry(header, sigTag, RPM_BIN_TYPE, sig, size);
416 rpmMessage(RPMMESS_VERBOSE, _("Generating signature using GPG.\n"));
417 ret = makeGPGSignature(file, &sig, &size, passPhrase);
419 headerAddEntry(header, sigTag, RPM_BIN_TYPE, sig, size);
426 static int verifySizeSignature(const char *datafile, int_32 size, char *result)
430 stat(datafile, &statbuf);
431 if (size != statbuf.st_size) {
432 sprintf(result, "Header+Archive size mismatch.\n"
433 "Expected %d, saw %d.\n",
434 size, (int)statbuf.st_size);
438 sprintf(result, "Header+Archive size OK: %d bytes\n", size);
442 #define X(_x) (unsigned)((_x) & 0xff)
444 static int verifyMD5Signature(const char *datafile, unsigned char *sig,
445 char *result, md5func fn)
447 unsigned char md5sum[16];
449 fn(datafile, md5sum);
450 if (memcmp(md5sum, sig, 16)) {
451 sprintf(result, "MD5 sum mismatch\n"
452 "Expected: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
453 "%02x%02x%02x%02x%02x\n"
454 "Saw : %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
455 "%02x%02x%02x%02x%02x\n",
456 X(sig[0]), X(sig[1]), X(sig[2]), X(sig[3]),
457 X(sig[4]), X(sig[5]), X(sig[6]), X(sig[7]),
458 X(sig[8]), X(sig[9]), X(sig[10]), X(sig[11]),
459 X(sig[12]), X(sig[13]), X(sig[14]), X(sig[15]),
460 X(md5sum[0]), X(md5sum[1]), X(md5sum[2]), X(md5sum[3]),
461 X(md5sum[4]), X(md5sum[5]), X(md5sum[6]), X(md5sum[7]),
462 X(md5sum[8]), X(md5sum[9]), X(md5sum[10]), X(md5sum[11]),
463 X(md5sum[12]), X(md5sum[13]), X(md5sum[14]), X(md5sum[15]) );
467 sprintf(result, "MD5 sum OK: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
468 "%02x%02x%02x%02x%02x\n",
469 X(md5sum[0]), X(md5sum[1]), X(md5sum[2]), X(md5sum[3]),
470 X(md5sum[4]), X(md5sum[5]), X(md5sum[6]), X(md5sum[7]),
471 X(md5sum[8]), X(md5sum[9]), X(md5sum[10]), X(md5sum[11]),
472 X(md5sum[12]), X(md5sum[13]), X(md5sum[14]), X(md5sum[15]) );
477 static int verifyPGPSignature(const char *datafile, void *sig,
478 int count, char *result)
480 int pid, status, outpipe[2];
483 unsigned char buf[8192];
489 /* What version do we have? */
490 if ((path = rpmDetectPGPVersion(&pgpVer)) == NULL) {
492 rpmError(RPMERR_EXEC,
493 _("Could not run pgp. Use --nopgp to skip PGP checks."));
498 * Sad but true: pgp-5.0 returns exit value of 0 on bad signature.
499 * Instead we have to use the text output to detect a bad signature.
504 /* Write out the signature */
505 { const char *tmppath = rpmGetPath("%{_tmppath}", NULL);
506 sigfile = tempnam(tmppath, "rpmsig");
509 sfd = fdOpen(sigfile, O_WRONLY|O_CREAT|O_TRUNC, 0644);
510 (void)Fwrite(sig, count, 1, sfd);
514 outpipe[0] = outpipe[1] = 0;
517 if (!(pid = fork())) {
518 const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
521 close(STDOUT_FILENO); /* XXX unnecessary */
522 dup2(outpipe[1], STDOUT_FILENO);
524 if (pgp_path && *pgp_path != '%')
525 dosetenv("PGPPATH", pgp_path, 1);
529 /* Some output (in particular "This signature applies to */
530 /* another message") is _always_ written to stderr; we */
531 /* want to catch that output, so dup stdout to stderr: */
532 { int save_stderr = dup(2);
534 execlp(path, "pgpv", "+batchmode=on", "+verbose=0",
535 /* Write "Good signature..." to stdout: */
536 "+OutputInformationFD=1",
537 /* Write "WARNING: ... is not trusted to... to stdout: */
538 "+OutputWarningFD=1",
539 sigfile, "-o", datafile, NULL);
540 /* Restore stderr so we can print the error message below. */
541 dup2(save_stderr, 2);
545 execlp(path, "pgp", "+batchmode=on", "+verbose=0",
546 sigfile, datafile, NULL);
549 case PGP_NOTDETECTED:
553 fprintf(stderr, _("exec failed!\n"));
554 rpmError(RPMERR_EXEC,
555 _("Could not run pgp. Use --nopgp to skip PGP checks."));
560 file = fdopen(outpipe[0], "r");
562 while (fgets(buf, 1024, file)) {
563 if (strncmp("File '", buf, 6) &&
564 strncmp("Text is assu", buf, 12) &&
565 strncmp("This signature applies to another message", buf, 41) &&
569 if (!strncmp("WARNING: Can't find the right public key", buf, 40))
571 else if (!strncmp("Signature by unknown keyid:", buf, 27))
573 else if (!strncmp("WARNING: The signing key is not trusted", buf, 39))
574 res = RPMSIG_NOTTRUSTED;
575 else if (!strncmp("Good signature", buf, 14))
580 (void)waitpid(pid, &status, 0);
582 if (!res && (!WIFEXITED(status) || WEXITSTATUS(status))) {
589 static int verifyGPGSignature(const char *datafile, void *sig,
590 int count, char *result)
592 int pid, status, outpipe[2];
595 unsigned char buf[8192];
599 /* Write out the signature */
600 { const char *tmppath = rpmGetPath("%{_tmppath}", NULL);
601 sigfile = tempnam(tmppath, "rpmsig");
604 sfd = fdOpen(sigfile, O_WRONLY|O_CREAT|O_TRUNC, 0644);
605 (void)Fwrite(sig, count, 1, sfd);
609 outpipe[0] = outpipe[1] = 0;
612 if (!(pid = fork())) {
613 const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
616 /* gpg version 0.9 sends its output to stderr. */
617 dup2(outpipe[1], STDERR_FILENO);
619 if (gpg_path && *gpg_path != '%')
620 dosetenv("GNUPGHOME", gpg_path, 1);
623 "--batch", "--no-verbose",
624 "--verify", sigfile, datafile,
626 fprintf(stderr, _("exec failed!\n"));
627 rpmError(RPMERR_EXEC,
628 _("Could not run gpg. Use --nogpg to skip GPG checks."));
633 file = fdopen(outpipe[0], "r");
635 while (fgets(buf, 1024, file)) {
637 if (!strncmp("gpg: Can't check signature: Public key not found", buf, 48)) {
643 (void)waitpid(pid, &status, 0);
645 if (!res && (!WIFEXITED(status) || WEXITSTATUS(status))) {
652 static int checkPassPhrase(const char *passPhrase, const int sigTag)
654 int passPhrasePipe[2];
658 passPhrasePipe[0] = passPhrasePipe[1] = 0;
659 pipe(passPhrasePipe);
660 if (!(pid = fork())) {
662 close(STDOUT_FILENO);
663 close(passPhrasePipe[1]);
664 if (! rpmIsVerbose()) {
665 close(STDERR_FILENO);
667 if ((fd = open("/dev/null", O_RDONLY)) != STDIN_FILENO) {
668 dup2(fd, STDIN_FILENO);
671 if ((fd = open("/dev/null", O_WRONLY)) != STDOUT_FILENO) {
672 dup2(fd, STDOUT_FILENO);
675 dup2(passPhrasePipe[0], 3);
679 { const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
680 const char *name = rpmExpand("%{_gpg_name}", NULL);
681 if (gpg_path && *gpg_path != '%')
682 dosetenv("GNUPGHOME", gpg_path, 1);
684 "--batch", "--no-verbose", "--passphrase-fd", "3",
685 "-u", name, "-so", "-",
687 rpmError(RPMERR_EXEC, _("Couldn't exec gpg"));
689 } /*@notreached@*/ break;
690 case RPMSIGTAG_PGP5: /* XXX legacy */
692 { const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
693 const char *name = rpmExpand("+myname=\"%{_pgp_name}\"", NULL);
697 dosetenv("PGPPASSFD", "3", 1);
698 if (pgp_path && *pgp_path != '%')
699 dosetenv("PGPPATH", pgp_path, 1);
701 if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
704 execlp(path, "pgp", "+batchmode=on", "+verbose=0",
707 case PGP_5: /* XXX legacy */
708 execlp(path,"pgps", "+batchmode=on", "+verbose=0",
712 case PGP_NOTDETECTED:
716 rpmError(RPMERR_EXEC, _("Couldn't exec pgp"));
718 } /*@notreached@*/ break;
719 default: /* This case should have been screened out long ago. */
720 rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file"));
721 _exit(RPMERR_SIGGEN);
722 /*@notreached@*/ break;
726 close(passPhrasePipe[0]);
727 (void)write(passPhrasePipe[1], passPhrase, strlen(passPhrase));
728 (void)write(passPhrasePipe[1], "\n", 1);
729 close(passPhrasePipe[1]);
731 (void)waitpid(pid, &status, 0);
732 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
736 /* passPhrase is good */
740 char *rpmGetPassPhrase(const char *prompt, const int sigTag)
747 { const char *name = rpmExpand("%{_gpg_name}", NULL);
748 aok = (name && *name != '%');
752 rpmError(RPMERR_SIGGEN,
753 _("You must set \"%%_gpg_name\" in your macro file"));
757 case RPMSIGTAG_PGP5: /* XXX legacy */
759 { const char *name = rpmExpand("%{_pgp_name}", NULL);
760 aok = (name && *name != '%');
764 rpmError(RPMERR_SIGGEN,
765 _("You must set \"%%_pgp_name\" in your macro file"));
770 /* Currently the calling function (rpm.c:main) is checking this and
771 * doing a better job. This section should never be accessed.
773 rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file"));
775 /*@notreached@*/ break;
778 pass = /*@-unrecog@*/ getpass( (prompt ? prompt : "") ) /*@=unrecog@*/ ;
780 if (checkPassPhrase(pass, sigTag))
786 int rpmVerifySignature(const char *file, int_32 sigTag, void *sig, int count,
791 if (verifySizeSignature(file, *(int_32 *)sig, result)) {
796 if (verifyMD5Signature(file, sig, result, mdbinfile)) {
800 case RPMSIGTAG_LEMD5_1:
801 case RPMSIGTAG_LEMD5_2:
802 if (verifyMD5Signature(file, sig, result, mdbinfileBroken)) {
806 case RPMSIGTAG_PGP5: /* XXX legacy */
808 return verifyPGPSignature(file, sig, count, result);
809 /*@notreached@*/ break;
811 return verifyGPGSignature(file, sig, count, result);
812 /*@notreached@*/ break;
814 sprintf(result, "Do not know how to verify sig type %d\n", sigTag);
815 return RPMSIG_UNKNOWN;