/** \ingroup rpmcli * \file lib/rpmchecksig.c * Verify the signature of a package. */ #include "system.h" #include "rpmcli.h" #include "rpmlead.h" #include "signature.h" #include "misc.h" /* XXX for makeTempFile() */ #include "debug.h" /*@access Header@*/ /* XXX compared with NULL */ /*@access FD_t@*/ /* XXX compared with NULL */ static int manageFile(FD_t *fdp, const char **fnp, int flags, /*@unused@*/ int rc) /*@globals rpmGlobalMacroContext, fileSystem @*/ /*@modifies *fdp, *fnp, rpmGlobalMacroContext, fileSystem @*/ { const char *fn; FD_t fd; if (fdp == NULL) { /* programmer error */ return 1; } /* close and reset *fdp to NULL */ if (*fdp && (fnp == NULL || *fnp == NULL)) { (void) Fclose(*fdp); *fdp = NULL; return 0; } /* open a file and set *fdp */ if (*fdp == NULL && fnp && *fnp) { fd = Fopen(*fnp, ((flags & O_WRONLY) ? "w.ufdio" : "r.ufdio")); if (fd == NULL || Ferror(fd)) { rpmError(RPMERR_OPEN, _("%s: open failed: %s\n"), *fnp, Fstrerror(fd)); return 1; } *fdp = fd; return 0; } /* open a temp file */ if (*fdp == NULL && (fnp == NULL || *fnp == NULL)) { fn = NULL; if (makeTempFile(NULL, (fnp ? &fn : NULL), &fd)) { rpmError(RPMERR_MAKETEMP, _("makeTempFile failed\n")); return 1; } if (fnp) *fnp = fn; *fdp = fdLink(fd, "manageFile return"); (void) fdFree(fd, "manageFile return"); return 0; } /* no operation */ if (*fdp && fnp && *fnp) { return 0; } /* XXX never reached */ return 1; } static int copyFile(FD_t *sfdp, const char **sfnp, FD_t *tfdp, const char **tfnp) /*@modifies *sfdp, *sfnp, *tfdp, *tfnp, fileSystem @*/ { unsigned char buffer[BUFSIZ]; ssize_t count; int rc = 1; if (manageFile(sfdp, sfnp, O_RDONLY, 0)) goto exit; if (manageFile(tfdp, tfnp, O_WRONLY|O_CREAT|O_TRUNC, 0)) goto exit; while ((count = Fread(buffer, sizeof(buffer[0]), sizeof(buffer), *sfdp)) > 0) { if (Fwrite(buffer, sizeof(buffer[0]), count, *tfdp) != count) { rpmError(RPMERR_FWRITE, _("%s: Fwrite failed: %s\n"), *tfnp, Fstrerror(*tfdp)); goto exit; } } if (count < 0) { rpmError(RPMERR_FREAD, _("%s: Fread failed: %s\n"), *sfnp, Fstrerror(*sfdp)); goto exit; } rc = 0; exit: if (*sfdp) (void) manageFile(sfdp, NULL, 0, rc); if (*tfdp) (void) manageFile(tfdp, NULL, 0, rc); return rc; } int rpmReSign(rpmResignFlags flags, char * passPhrase, const char ** argv) { FD_t fd = NULL; FD_t ofd = NULL; struct rpmlead lead, *l = &lead; int_32 sigtag; const char *rpm, *trpm; const char *sigtarget = NULL; char tmprpm[1024+1]; Header sig = NULL; void * uh = NULL; int_32 uht, uhc; int res = EXIT_FAILURE; rpmRC rc; int xx; tmprpm[0] = '\0'; if (argv) while ((rpm = *argv++) != NULL) { fprintf(stdout, "%s:\n", rpm); if (manageFile(&fd, &rpm, O_RDONLY, 0)) goto exit; memset(l, 0, sizeof(*l)); if (readLead(fd, l)) { rpmError(RPMERR_READLEAD, _("%s: readLead failed\n"), rpm); goto exit; } switch (l->major) { case 1: rpmError(RPMERR_BADSIGTYPE, _("%s: Can't sign v1.0 RPM\n"), rpm); goto exit; /*@notreached@*/ break; case 2: rpmError(RPMERR_BADSIGTYPE, _("%s: Can't re-sign v2.0 RPM\n"), rpm); goto exit; /*@notreached@*/ break; default: break; } rc = rpmReadSignature(fd, &sig, l->signature_type); if (!(rc == RPMRC_OK || rc == RPMRC_BADSIZE)) { rpmError(RPMERR_SIGGEN, _("%s: rpmReadSignature failed\n"), rpm); goto exit; } if (sig == NULL) { rpmError(RPMERR_SIGGEN, _("%s: No signature available\n"), rpm); goto exit; } /* Write the header and archive to a temp file */ /* ASSERT: ofd == NULL && sigtarget == NULL */ 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 (headerGetEntry(sig, RPMTAG_HEADERSIGNATURES, &uht, &uh, &uhc)) { HeaderIterator hi; int_32 tag, type, count; hPTR_t ptr; Header oh; Header nh; nh = headerNew(); if (nh == NULL) { uh = headerFreeData(uh, uht); goto exit; } oh = headerCopyLoad(uh); for (hi = headerInitIterator(oh); headerNextIterator(hi, &tag, &type, &ptr, &count); ptr = headerFreeData(ptr, type)) { if (ptr) xx = headerAddEntry(nh, tag, type, ptr, count); } hi = headerFreeIterator(hi); oh = headerFree(oh); sig = headerFree(sig); sig = headerLink(nh); nh = headerFree(nh); } /* Eliminate broken digest values. */ xx = headerRemoveEntry(sig, RPMSIGTAG_LEMD5_1); xx = headerRemoveEntry(sig, RPMSIGTAG_LEMD5_2); xx = headerRemoveEntry(sig, RPMSIGTAG_BADSHA1_1); xx = headerRemoveEntry(sig, RPMSIGTAG_BADSHA1_2); /* Toss and recalculate header+payload size and digests. */ xx = headerRemoveEntry(sig, RPMSIGTAG_SIZE); xx = rpmAddSignature(sig, sigtarget, RPMSIGTAG_SIZE, passPhrase); xx = headerRemoveEntry(sig, RPMSIGTAG_MD5); xx = rpmAddSignature(sig, sigtarget, RPMSIGTAG_MD5, passPhrase); #ifdef NOTYET /* XXX leave new-fangled header-only digest in place. */ xx = headerRemoveEntry(sig, RPMSIGTAG_SHA1); xx = rpmAddSignature(sig, sigtarget, RPMSIGTAG_SHA1, passPhrase); #endif if ((sigtag = rpmLookupSignatureType(RPMLOOKUPSIG_QUERY)) > 0) { #ifdef NOTYET /* XXX leave new-fangled header-only signatures in place. */ case RPMSIGTAG_GPG: xx = headerRemoveEntry(sig, RPMSIGTAG_DSA); /*@fallthrough@*/ case RPMSIGTAG_PGP5: case RPMSIGTAG_PGP: xx = headerRemoveEntry(sig, RPMSIGTAG_RSA); /*@switchbreak@*/ break; } #endif xx = headerRemoveEntry(sig, sigtag); xx = rpmAddSignature(sig, sigtarget, sigtag, passPhrase); } /* Reallocate the signature into one contiguous region. */ sig = headerReload(sig, RPMTAG_HEADERSIGNATURES); if (sig == NULL) /* XXX can't happen */ goto exit; /* Write the lead/signature of the output rpm */ strcpy(tmprpm, rpm); strcat(tmprpm, ".XXXXXX"); (void) /*@-unrecog@*/ mktemp(tmprpm) /*@=unrecog@*/; trpm = tmprpm; if (manageFile(&ofd, &trpm, O_WRONLY|O_CREAT|O_TRUNC, 0)) goto exit; l->signature_type = RPMSIGTYPE_HEADERSIG; if (writeLead(ofd, l)) { rpmError(RPMERR_WRITELEAD, _("%s: writeLead failed: %s\n"), trpm, Fstrerror(ofd)); goto exit; } if (rpmWriteSignature(ofd, sig)) { rpmError(RPMERR_SIGGEN, _("%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 */ /* Clean up intermediate target */ (void) unlink(sigtarget); sigtarget = _free(sigtarget); /* Move final target into place. */ (void) unlink(rpm); (void) rename(trpm, rpm); tmprpm[0] = '\0'; } res = 0; exit: if (fd) (void) manageFile(&fd, NULL, 0, res); if (ofd) (void) manageFile(&ofd, NULL, 0, res); sig = rpmFreeSignature(sig); if (sigtarget) { (void) unlink(sigtarget); sigtarget = _free(sigtarget); } if (tmprpm[0] != '\0') { (void) unlink(tmprpm); tmprpm[0] = '\0'; } return res; } int rpmCheckSig(rpmCheckSigFlags flags, const char ** argv) { FD_t fd = NULL; FD_t ofd = NULL; int res2, res3; struct rpmlead lead, *l = &lead; const char *rpm = NULL; char result[8*BUFSIZ]; const char * sigtarget = NULL; unsigned char buffer[8192]; unsigned char missingKeys[7164]; unsigned char untrustedKeys[7164]; Header sig; HeaderIterator hi; int_32 tag, type, count; const void * ptr; int res = 0; rpmRC rc; if (argv) while ((rpm = *argv++) != NULL) { if (manageFile(&fd, &rpm, O_RDONLY, 0)) { res++; goto bottom; } memset(l, 0, sizeof(*l)); if (readLead(fd, l)) { rpmError(RPMERR_READLEAD, _("%s: readLead failed\n"), rpm); res++; goto bottom; } switch (l->major) { case 1: rpmError(RPMERR_BADSIGTYPE, _("%s: No signature available (v1.0 RPM)\n"), rpm); res++; goto bottom; /*@notreached@*/ break; default: break; } rc = rpmReadSignature(fd, &sig, l->signature_type); if (!(rc == RPMRC_OK || rc == RPMRC_BADSIZE)) { rpmError(RPMERR_SIGGEN, _("%s: rpmReadSignature failed\n"), rpm); res++; goto bottom; } if (sig == NULL) { rpmError(RPMERR_SIGGEN, _("%s: No signature available\n"), rpm); res++; goto bottom; } /* Write the header and archive to a temp file */ /* ASSERT: ofd == NULL && sigtarget == NULL */ if (copyFile(&fd, &rpm, &ofd, &sigtarget)) { res++; goto bottom; } /* Both fd and ofd are now closed. sigtarget contains tempfile name. */ /* ASSERT: fd == NULL && ofd == NULL */ res2 = 0; missingKeys[0] = '\0'; untrustedKeys[0] = '\0'; sprintf(buffer, "%s:%c", rpm, (rpmIsVerbose() ? '\n' : ' ') ); for (hi = headerInitIterator(sig); headerNextIterator(hi, &tag, &type, &ptr, &count); ptr = headerFreeData(ptr, type)) { switch (tag) { case RPMSIGTAG_PGP5: /* XXX legacy */ case RPMSIGTAG_PGP: if (!(flags & CHECKSIG_PGP)) continue; break; case RPMSIGTAG_GPG: if (!(flags & CHECKSIG_GPG)) continue; break; case RPMSIGTAG_LEMD5_2: case RPMSIGTAG_LEMD5_1: case RPMSIGTAG_MD5: if (!(flags & CHECKSIG_MD5)) continue; break; default: continue; /*@notreached@*/ break; } if (ptr == NULL) continue; /* XXX can't happen */ if ((res3 = rpmVerifySignature(sigtarget, tag, ptr, count, result))) { if (rpmIsVerbose()) { strcat(buffer, result); res2 = 1; } else { char *tempKey; switch (tag) { case RPMSIGTAG_SIZE: strcat(buffer, "SIZE "); res2 = 1; break; case RPMSIGTAG_LEMD5_2: case RPMSIGTAG_LEMD5_1: case RPMSIGTAG_MD5: strcat(buffer, "MD5 "); res2 = 1; break; case RPMSIGTAG_PGP5: /* XXX legacy */ case RPMSIGTAG_PGP: switch (res3) { case RPMSIG_NOKEY: res2 = 1; /*@fallthrough@*/ case RPMSIG_NOTTRUSTED: { int offset = 7; strcat(buffer, "(PGP) "); tempKey = strstr(result, "Key ID"); if (tempKey == NULL) { tempKey = strstr(result, "keyid:"); offset = 9; } if (tempKey) { if (res3 == RPMSIG_NOKEY) { strcat(missingKeys, " PGP#"); /*@-compdef@*/ strncat(missingKeys, tempKey + offset, 8); /*@=compdef@*/ } else { strcat(untrustedKeys, " PGP#"); /*@-compdef@*/ strncat(untrustedKeys, tempKey + offset, 8); /*@=compdef@*/ } } } break; default: strcat(buffer, "PGP "); res2 = 1; break; } break; case RPMSIGTAG_GPG: /* Do not consider this a failure */ switch (res3) { case RPMSIG_NOKEY: strcat(buffer, "(GPG) "); strcat(missingKeys, " GPG#"); tempKey = strstr(result, "key ID"); if (tempKey) /*@-compdef@*/ strncat(missingKeys, tempKey+7, 8); /*@=compdef@*/ res2 = 1; break; default: strcat(buffer, "GPG "); res2 = 1; break; } break; default: strcat(buffer, "?UnknownSignatureType? "); res2 = 1; break; } } } else { if (rpmIsVerbose()) { strcat(buffer, result); } else { switch (tag) { case RPMSIGTAG_SIZE: strcat(buffer, "size "); break; case RPMSIGTAG_LEMD5_2: case RPMSIGTAG_LEMD5_1: case RPMSIGTAG_MD5: strcat(buffer, "md5 "); break; case RPMSIGTAG_PGP5: /* XXX legacy */ case RPMSIGTAG_PGP: strcat(buffer, "pgp "); break; case RPMSIGTAG_GPG: strcat(buffer, "gpg "); break; default: strcat(buffer, "??? "); break; } } } } hi = headerFreeIterator(hi); res += res2; (void) unlink(sigtarget); sigtarget = _free(sigtarget); if (res2) { if (rpmIsVerbose()) { rpmError(RPMERR_SIGVFY, "%s", (char *)buffer); } else { rpmError(RPMERR_SIGVFY, "%s%s%s%s%s%s%s%s\n", (char *)buffer, _("NOT OK"), (missingKeys[0] != '\0') ? _(" (MISSING KEYS:") : "", (char *)missingKeys, (missingKeys[0] != '\0') ? _(") ") : "", (untrustedKeys[0] != '\0') ? _(" (UNTRUSTED KEYS:") : "", (char *)untrustedKeys, (untrustedKeys[0] != '\0') ? _(")") : ""); } } else { if (rpmIsVerbose()) { rpmError(RPMERR_SIGVFY, "%s", (char *)buffer); } else { rpmError(RPMERR_SIGVFY, "%s%s%s%s%s%s%s%s\n", (char *)buffer, _("OK"), (missingKeys[0] != '\0') ? _(" (MISSING KEYS:") : "", (char *)missingKeys, (missingKeys[0] != '\0') ? _(") ") : "", (untrustedKeys[0] != '\0') ? _(" (UNTRUSTED KEYS:") : "", (char *)untrustedKeys, (untrustedKeys[0] != '\0') ? _(")") : ""); } } bottom: if (fd) (void) manageFile(&fd, NULL, 0, 0); if (ofd) (void) manageFile(&ofd, NULL, 0, 0); if (sigtarget) { (void) unlink(sigtarget); sigtarget = _free(sigtarget); } } return res; }