2 * \file lib/signature.c
7 #include "rpmio_internal.h"
9 #include <rpmmacro.h> /* XXX for rpmGetPath() */
11 #include "misc.h" /* XXX for dosetenv() and makeTempFile() */
12 #include "legacy.h" /* XXX for mdbinfile() */
14 #include "signature.h"
17 /*@access Header@*/ /* XXX compared with NULL */
18 /*@access FD_t@*/ /* XXX compared with NULL */
21 /*@-mustmod@*/ /* FIX: internalState not modified? */
22 int rpmLookupSignatureType(int action)
24 static int disabled = 0;
28 case RPMLOOKUPSIG_DISABLE:
31 case RPMLOOKUPSIG_ENABLE:
34 case RPMLOOKUPSIG_QUERY:
37 { const char *name = rpmExpand("%{_signature}", NULL);
38 if (!(name && *name != '%'))
40 else if (!xstrcasecmp(name, "none"))
42 else if (!xstrcasecmp(name, "pgp"))
44 else if (!xstrcasecmp(name, "pgp5")) /* XXX legacy */
46 else if (!xstrcasecmp(name, "gpg"))
49 rc = -1; /* Invalid %_signature spec in macro file */
57 /* rpmDetectPGPVersion() returns the absolute path to the "pgp" */
58 /* executable of the requested version, or NULL when none found. */
60 const char * rpmDetectPGPVersion(pgpVersion * pgpVer)
62 /* Actually this should support having more then one pgp version. */
63 /* At the moment only one version is possible since we only */
64 /* have one %_pgpbin and one %_pgp_path. */
66 static pgpVersion saved_pgp_version = PGP_UNKNOWN;
67 const char *pgpbin = rpmGetPath("%{_pgpbin}", NULL);
69 if (saved_pgp_version == PGP_UNKNOWN) {
73 if (!(pgpbin && pgpbin[0] != '%')) {
74 pgpbin = _free(pgpbin);
75 saved_pgp_version = -1;
78 pgpvbin = (char *)alloca(strlen(pgpbin) + sizeof("v"));
79 (void)stpcpy(stpcpy(pgpvbin, pgpbin), "v");
81 if (stat(pgpvbin, &st) == 0)
82 saved_pgp_version = PGP_5;
83 else if (stat(pgpbin, &st) == 0)
84 saved_pgp_version = PGP_2;
86 saved_pgp_version = PGP_NOTDETECTED;
90 *pgpVer = saved_pgp_version;
96 * @todo rpmio: use fdSize rather than fstat(2) to get file size.
97 * @param fd package file handle
98 * @param siglen signature header size
99 * @param pad signature padding
100 * @param datalen length of header+payload
101 * @return rpmRC return code
103 static inline rpmRC checkSize(FD_t fd, int siglen, int pad, int datalen)
104 /*@globals fileSystem @*/
105 /*@modifies fileSystem @*/
110 if (fstat(Fileno(fd), &st))
113 if (!S_ISREG(st.st_mode)) {
114 rpmMessage(RPMMESS_DEBUG,
115 _("file is not regular -- skipping size check\n"));
120 rc = (((sizeof(struct rpmlead) + siglen + pad + datalen) - st.st_size)
121 ? RPMRC_BADSIZE : RPMRC_OK);
123 rpmMessage((rc == RPMRC_OK ? RPMMESS_DEBUG : RPMMESS_WARNING),
124 _("Expected size: %12d = lead(%d)+sigs(%d)+pad(%d)+data(%d)\n"),
125 (int)sizeof(struct rpmlead)+siglen+pad+datalen,
126 (int)sizeof(struct rpmlead), siglen, pad, datalen);
128 rpmMessage((rc == RPMRC_OK ? RPMMESS_DEBUG : RPMMESS_WARNING),
129 _(" Actual size: %12d\n"), (int)st.st_size);
134 rpmRC rpmReadSignature(FD_t fd, Header * headerp, sigType sig_type)
141 rpmRC rc = RPMRC_FAIL; /* assume failure */
148 case RPMSIGTYPE_NONE:
149 rpmMessage(RPMMESS_DEBUG, _("No signature\n"));
152 case RPMSIGTYPE_PGP262_1024:
153 rpmMessage(RPMMESS_DEBUG, _("Old PGP signature\n"));
154 /* These are always 256 bytes */
155 /*@-type@*/ /* FIX: eliminate timedRead @*/
156 if (timedRead(fd, buf, 256) != 256)
160 (void) headerAddEntry(h, RPMSIGTAG_PGP, RPM_BIN_TYPE, buf, 152);
164 case RPMSIGTYPE_MD5_PGP:
165 rpmError(RPMERR_BADSIGTYPE,
166 _("Old (internal-only) signature! How did you get that!?\n"));
168 case RPMSIGTYPE_HEADERSIG:
169 case RPMSIGTYPE_DISABLE:
170 /* This is a new style signature */
171 h = headerRead(fd, HEADER_MAGIC_YES);
176 sigSize = headerSizeof(h, HEADER_MAGIC_YES);
178 /* XXX Legacy headers have a HEADER_IMAGE tag added. */
179 if (headerIsEntry(h, RPMTAG_HEADERIMAGE))
180 sigSize -= (16 + 16);
182 pad = (8 - (sigSize % 8)) % 8; /* 8-byte pad */
183 if (sig_type == RPMSIGTYPE_HEADERSIG) {
184 if (! headerGetEntry(h, RPMSIGTAG_SIZE, &type,
185 (void **)&archSize, &count))
187 rc = checkSize(fd, sigSize, pad, *archSize);
189 /*@-type@*/ /* FIX: eliminate timedRead @*/
190 if (pad && timedRead(fd, buf, pad) != pad)
191 rc = RPMRC_SHORTREAD;
198 if (headerp && rc == 0)
206 int rpmWriteSignature(FD_t fd, Header h)
208 static byte buf[8] = "\000\000\000\000\000\000\000\000";
212 rc = headerWrite(fd, h, HEADER_MAGIC_YES);
216 sigSize = headerSizeof(h, HEADER_MAGIC_YES);
217 pad = (8 - (sigSize % 8)) % 8;
219 if (Fwrite(buf, sizeof(buf[0]), pad, fd) != pad)
222 rpmMessage(RPMMESS_DEBUG, _("Signature: size(%d)+pad(%d)\n"), sigSize, pad);
226 Header rpmNewSignature(void)
228 Header h = headerNew();
232 Header rpmFreeSignature(Header h)
234 return headerFree(h);
237 static int makePGPSignature(const char * file, /*@out@*/ void ** sig,
238 /*@out@*/ int_32 * size, /*@null@*/ const char * passPhrase)
239 /*@globals rpmGlobalMacroContext,
241 /*@modifies *sig, *size, rpmGlobalMacroContext,
244 char * sigfile = alloca(1024);
249 (void) stpcpy( stpcpy(sigfile, file), ".sig");
251 inpipe[0] = inpipe[1] = 0;
254 if (!(pid = fork())) {
255 const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
256 const char *name = rpmExpand("+myname=\"%{_pgp_name}\"", NULL);
260 (void) close(STDIN_FILENO);
261 (void) dup2(inpipe[0], 3);
262 (void) close(inpipe[1]);
264 (void) dosetenv("PGPPASSFD", "3", 1);
265 if (pgp_path && *pgp_path != '%')
266 (void) dosetenv("PGPPATH", pgp_path, 1);
268 /* dosetenv("PGPPASS", passPhrase, 1); */
270 if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
273 (void) execlp(path, "pgp", "+batchmode=on", "+verbose=0", "+armor=off",
274 name, "-sb", file, sigfile, NULL);
277 (void) execlp(path,"pgps", "+batchmode=on", "+verbose=0", "+armor=off",
278 name, "-b", file, "-o", sigfile, NULL);
281 case PGP_NOTDETECTED:
285 rpmError(RPMERR_EXEC, _("Couldn't exec pgp (%s)\n"),
286 (path ? path : NULL));
290 (void) close(inpipe[0]);
292 (void) write(inpipe[1], passPhrase, strlen(passPhrase));
293 (void) write(inpipe[1], "\n", 1);
294 (void) close(inpipe[1]);
296 (void)waitpid(pid, &status, 0);
297 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
298 rpmError(RPMERR_SIGGEN, _("pgp failed\n"));
302 if (stat(sigfile, &st)) {
303 /* PGP failed to write signature */
304 if (sigfile) (void) unlink(sigfile); /* Just in case */
305 rpmError(RPMERR_SIGGEN, _("pgp failed to write signature\n"));
310 rpmMessage(RPMMESS_DEBUG, _("PGP sig size: %d\n"), *size);
311 *sig = xmalloc(*size);
315 fd = Fopen(sigfile, "r.fdio");
316 if (fd != NULL && !Ferror(fd)) {
317 /*@-type@*/ /* FIX: eliminate timedRead @*/
318 rc = timedRead(fd, *sig, *size);
320 if (sigfile) (void) unlink(sigfile);
325 rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
330 rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of PGP sig\n"), *size);
335 /* This is an adaptation of the makePGPSignature function to use GPG instead
336 * of PGP to create signatures. I think I've made all the changes necessary,
337 * but this could be a good place to start looking if errors in GPG signature
340 static int makeGPGSignature(const char * file, /*@out@*/ void ** sig,
341 /*@out@*/ int_32 * size, /*@null@*/ const char * passPhrase)
342 /*@globals rpmGlobalMacroContext,
344 /*@modifies *sig, *size, rpmGlobalMacroContext,
347 char * sigfile = alloca(1024);
353 (void) stpcpy( stpcpy(sigfile, file), ".sig");
355 inpipe[0] = inpipe[1] = 0;
358 if (!(pid = fork())) {
359 const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
360 const char *name = rpmExpand("%{_gpg_name}", NULL);
362 (void) close(STDIN_FILENO);
363 (void) dup2(inpipe[0], 3);
364 (void) close(inpipe[1]);
366 if (gpg_path && *gpg_path != '%')
367 (void) dosetenv("GNUPGHOME", gpg_path, 1);
368 (void) execlp("gpg", "gpg",
369 "--batch", "--no-verbose", "--no-armor", "--passphrase-fd", "3",
370 "-u", name, "-sbo", sigfile, file,
372 rpmError(RPMERR_EXEC, _("Couldn't exec gpg\n"));
376 fpipe = fdopen(inpipe[1], "w");
377 (void) close(inpipe[0]);
379 fprintf(fpipe, "%s\n", (passPhrase ? passPhrase : ""));
380 (void) fclose(fpipe);
383 (void)waitpid(pid, &status, 0);
384 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
385 rpmError(RPMERR_SIGGEN, _("gpg failed\n"));
389 if (stat(sigfile, &st)) {
390 /* GPG failed to write signature */
391 if (sigfile) (void) unlink(sigfile); /* Just in case */
392 rpmError(RPMERR_SIGGEN, _("gpg failed to write signature\n"));
397 rpmMessage(RPMMESS_DEBUG, _("GPG sig size: %d\n"), *size);
398 *sig = xmalloc(*size);
402 fd = Fopen(sigfile, "r.fdio");
403 if (fd != NULL && !Ferror(fd)) {
404 /*@-type@*/ /* FIX: eliminate timedRead @*/
405 rc = timedRead(fd, *sig, *size);
407 if (sigfile) (void) unlink(sigfile);
412 rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
417 rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of GPG sig\n"), *size);
422 int rpmAddSignature(Header h, const char * file, int_32 sigTag,
423 const char *passPhrase)
433 (void) stat(file, &st);
436 (void) headerAddEntry(h, RPMSIGTAG_SIZE, RPM_INT32_TYPE, &size, 1);
439 ret = mdbinfile(file, buf);
441 (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, buf, 16);
443 case RPMSIGTAG_PGP5: /* XXX legacy */
445 rpmMessage(RPMMESS_VERBOSE, _("Generating signature using PGP.\n"));
446 ret = makePGPSignature(file, &sig, &size, passPhrase);
448 (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, sig, size);
451 rpmMessage(RPMMESS_VERBOSE, _("Generating signature using GPG.\n"));
452 ret = makeGPGSignature(file, &sig, &size, passPhrase);
454 (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, sig, size);
461 static int checkPassPhrase(const char * passPhrase, const int sigTag)
462 /*@globals rpmGlobalMacroContext,
464 /*@modifies rpmGlobalMacroContext,
467 int passPhrasePipe[2];
471 passPhrasePipe[0] = passPhrasePipe[1] = 0;
472 (void) pipe(passPhrasePipe);
473 if (!(pid = fork())) {
474 (void) close(STDIN_FILENO);
475 (void) close(STDOUT_FILENO);
476 (void) close(passPhrasePipe[1]);
477 /*@-internalglobs@*/ /* FIX: shrug */
478 if (! rpmIsVerbose()) {
479 (void) close(STDERR_FILENO);
482 if ((fd = open("/dev/null", O_RDONLY)) != STDIN_FILENO) {
483 (void) dup2(fd, STDIN_FILENO);
486 if ((fd = open("/dev/null", O_WRONLY)) != STDOUT_FILENO) {
487 (void) dup2(fd, STDOUT_FILENO);
490 (void) dup2(passPhrasePipe[0], 3);
494 { const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
495 const char *name = rpmExpand("%{_gpg_name}", NULL);
496 if (gpg_path && *gpg_path != '%')
497 (void) dosetenv("GNUPGHOME", gpg_path, 1);
498 (void) execlp("gpg", "gpg",
499 "--batch", "--no-verbose", "--passphrase-fd", "3",
500 "-u", name, "-so", "-",
502 rpmError(RPMERR_EXEC, _("Couldn't exec gpg\n"));
504 } /*@notreached@*/ break;
505 case RPMSIGTAG_PGP5: /* XXX legacy */
507 { const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
508 const char *name = rpmExpand("+myname=\"%{_pgp_name}\"", NULL);
512 (void) dosetenv("PGPPASSFD", "3", 1);
513 if (pgp_path && *pgp_path != '%')
514 (void) dosetenv("PGPPATH", pgp_path, 1);
516 if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
519 (void) execlp(path, "pgp", "+batchmode=on", "+verbose=0",
521 /*@innerbreak@*/ break;
522 case PGP_5: /* XXX legacy */
523 (void) execlp(path,"pgps", "+batchmode=on", "+verbose=0",
525 /*@innerbreak@*/ break;
527 case PGP_NOTDETECTED:
528 /*@innerbreak@*/ break;
531 rpmError(RPMERR_EXEC, _("Couldn't exec pgp\n"));
533 } /*@notreached@*/ break;
534 default: /* This case should have been screened out long ago. */
535 rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
536 _exit(RPMERR_SIGGEN);
537 /*@notreached@*/ break;
541 (void) close(passPhrasePipe[0]);
542 (void) write(passPhrasePipe[1], passPhrase, strlen(passPhrase));
543 (void) write(passPhrasePipe[1], "\n", 1);
544 (void) close(passPhrasePipe[1]);
546 (void)waitpid(pid, &status, 0);
547 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
551 /* passPhrase is good */
555 char * rpmGetPassPhrase(const char * prompt, const int sigTag)
562 { const char *name = rpmExpand("%{_gpg_name}", NULL);
563 aok = (name && *name != '%');
567 rpmError(RPMERR_SIGGEN,
568 _("You must set \"%%_gpg_name\" in your macro file\n"));
572 case RPMSIGTAG_PGP5: /* XXX legacy */
574 { const char *name = rpmExpand("%{_pgp_name}", NULL);
575 aok = (name && *name != '%');
579 rpmError(RPMERR_SIGGEN,
580 _("You must set \"%%_pgp_name\" in your macro file\n"));
585 /* Currently the calling function (rpm.c:main) is checking this and
586 * doing a better job. This section should never be accessed.
588 rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
590 /*@notreached@*/ break;
593 pass = /*@-unrecog@*/ getpass( (prompt ? prompt : "") ) /*@=unrecog@*/ ;
595 if (checkPassPhrase(pass, sigTag))
601 static rpmVerifySignatureReturn
602 verifySizeSignature(/*@unused@*/ const char * fn,
604 /*@unused@*/ int siglen,
605 const pgpDig dig, /*@out@*/ char * result)
606 /*@modifies *result @*/
609 int_32 size = *(int_32 *)sig;
613 t = stpcpy(t, "Header+Payload size: ");
616 if (size != dig->nbytes) {
618 sprintf(t, "BAD Expected(%d) != (%d)\n", size, dig->nbytes);
621 sprintf(t, "OK (%d)\n", dig->nbytes);
628 static rpmVerifySignatureReturn
629 verifyMD5Signature(/*@unused@*/ const char * fn, const byte * sig, int siglen,
630 const pgpDig dig, /*@out@*/ char * result)
631 /*@modifies *result @*/
634 byte * md5sum = NULL;
639 (void) rpmDigestFinal(rpmDigestDup(dig->md5ctx),
640 (void **)&md5sum, &md5len, 0);
644 t = stpcpy(t, "MD5 digest: ");
646 if (md5len != siglen || memcmp(md5sum, sig, md5len)) {
648 t = stpcpy(t, "BAD Expected(");
649 (void) pgpHexCvt(t, sig, siglen);
651 t = stpcpy(t, ") != (");
654 t = stpcpy(t, "OK (");
656 (void) pgpHexCvt(t, md5sum, md5len);
658 t = stpcpy(t, ")\n");
660 md5sum = _free(md5sum);
665 static rpmVerifySignatureReturn
666 verifyPGPSignature(/*@unused@*/ const char * fn,
667 /*@unused@*/ const byte * sig,
668 /*@unused@*/ int siglen,
669 const pgpDig dig, /*@out@*/ char * result)
670 /*@modifies *result */
672 /*@unchecked@*/ static const byte * pgppk = NULL;
673 /*@unchecked@*/ static size_t pgppklen = 0;
678 t = stpcpy(t, "V3 RSA/MD5 signature: ");
680 /* XXX retrieve by keyid from signature. */
682 /*@-globs -internalglobs -mods -modfilesys@*/
684 const char * pkfn = rpmExpand("%{_pgp_pubkey}", NULL);
685 if (pgpReadPkts(pkfn, &pgppk, &pgppklen)) {
687 t = stpcpy( stpcpy( stpcpy(t, "NOKEY ("), pkfn), ")\n");
691 rpmMessage(RPMMESS_DEBUG,
692 "========== PGP RSA/MD5 pubkey %s\n", pkfn);
693 xx = pgpPrtPkts(pgppk, pgppklen, NULL, rpmIsDebug());
697 /* XXX sanity check on pubkey and signature agreement. */
700 { pgpPktSigV3 dsig = dig->signature.v3;
701 pgpPktKeyV3 dpk = dig->pubkey.v3;
703 if (dsig->pubkey_algo != dpk->pubkey_algo) {
705 t = stpcpy(t, "NOKEY\n");
712 xx = pgpPrtPkts(pgppk, pgppklen, dig, 0);
714 /*@=globs =internalglobs =mods =modfilesys@*/
717 if (!rsavrfy(&dig->rsa_pk, &dig->rsahm, &dig->c)) {
719 t = stpcpy(t, "BAD\n");
722 t = stpcpy(t, "OK\n");
729 static rpmVerifySignatureReturn
730 verifyGPGSignature(/*@unused@*/ const char * fn,
731 /*@unused@*/ const byte * sig,
732 /*@unused@*/ int siglen,
733 const pgpDig dig, /*@out@*/ char * result)
734 /*@modifies *result @*/
736 /*@unchecked@*/ static const byte * gpgpk = NULL;
737 /*@unchecked@*/ static size_t gpgpklen = 0;
742 t = stpcpy(t, "V3 DSA signature: ");
744 /* XXX retrieve by keyid from signature. */
746 /*@-globs -internalglobs -mods -modfilesys@*/
748 const char * pkfn = rpmExpand("%{_gpg_pubkey}", NULL);
749 if (pgpReadPkts(pkfn, &gpgpk, &gpgpklen)) {
751 t = stpcpy( stpcpy( stpcpy(t, "NOKEY ("), pkfn), ")\n");
755 rpmMessage(RPMMESS_DEBUG,
756 "========== GPG DSA pubkey %s\n", pkfn);
757 xx = pgpPrtPkts(gpgpk, gpgpklen, NULL, rpmIsDebug());
761 /* XXX sanity check on pubkey and signature agreement. */
764 { pgpPktSigV3 dsig = dig->signature.v3;
765 pgpPktKeyV4 dpk = dig->pubkey.v4;
767 if (dsig->pubkey_algo != dpk->pubkey_algo) {
769 t = stpcpy(t, "NOKEY\n");
776 xx = pgpPrtPkts(gpgpk, gpgpklen, dig, 0);
778 /*@=globs =internalglobs =mods =modfilesys@*/
781 if (!dsavrfy(&dig->p, &dig->q, &dig->g, &dig->hm, &dig->y, &dig->r, &dig->s)) {
783 t = stpcpy(t, "BAD\n");
786 t = stpcpy(t, "OK\n");
793 rpmVerifySignatureReturn
794 rpmVerifySignature(const char * fn, int_32 sigTag, const void * sig,
795 int siglen, const pgpDig dig, char * result)
800 res = verifySizeSignature(fn, sig, siglen, dig, result);
803 res = verifyMD5Signature(fn, sig, siglen, dig, result);
805 case RPMSIGTAG_PGP5: /* XXX legacy */
807 res = verifyPGPSignature(fn, sig, siglen, dig, result);
810 res = verifyGPGSignature(fn, sig, siglen, dig, result);
812 case RPMSIGTAG_LEMD5_1:
813 case RPMSIGTAG_LEMD5_2:
814 sprintf(result, "Broken MD5 digest: UNSUPPORTED\n");
815 res = RPMSIG_UNKNOWN;
818 sprintf(result, "Signature: UNKNOWN(%d)\n", sigTag);
819 res = RPMSIG_UNKNOWN;