static rpmRC writeRPM(Header *hdrp, unsigned char ** pkgidp, const char *fileName, CSA_t csa, char **cookie) { FD_t fd = NULL; FD_t ifd = NULL; ssize_t count; char * sigtarget = NULL;; char * rpmio_flags = NULL; char * SHA1 = NULL; const char *s; char *buf = NULL; Header h; Header sig = NULL; int xx; rpmRC rc = RPMRC_OK; struct rpmtd_s td; rpmTagVal sizetag; rpmTagVal payloadtag; /* Transfer header reference form *hdrp to h. */ h = headerLink(*hdrp); *hdrp = headerFree(*hdrp); if (pkgidp) *pkgidp = NULL; /* Save payload information */ if (headerIsSource(h)) rpmio_flags = rpmExpand("%{?_source_payload}", NULL); else rpmio_flags = rpmExpand("%{?_binary_payload}", NULL); if (!(rpmio_flags && *rpmio_flags)) { rpmio_flags = _free(rpmio_flags); rpmio_flags = xstrdup("w9.gzdio"); } s = strchr(rpmio_flags, '.'); if (s) { const char *compr = NULL; headerPutString(h, RPMTAG_PAYLOADFORMAT, "cpio"); if (rstreq(s+1, "ufdio")) { compr = NULL; } else if (rstreq(s+1, "gzdio")) { compr = "gzip"; #if HAVE_BZLIB_H } else if (rstreq(s+1, "bzdio")) { compr = "bzip2"; /* Add prereq on rpm version that understands bzip2 payloads */ (void) rpmlibNeedsFeature(h, "PayloadIsBzip2", "3.0.5-1"); #endif #if HAVE_LZMA_H } else if (rstreq(s+1, "xzdio")) { compr = "xz"; (void) rpmlibNeedsFeature(h, "PayloadIsXz", "5.2-1"); } else if (rstreq(s+1, "lzdio")) { compr = "lzma"; (void) rpmlibNeedsFeature(h, "PayloadIsLzma", "4.4.6-1"); #endif } else { rpmlog(RPMLOG_ERR, _("Unknown payload compression: %s\n"), rpmio_flags); rc = RPMRC_FAIL; goto exit; } if (compr) headerPutString(h, RPMTAG_PAYLOADCOMPRESSOR, compr); buf = xstrdup(rpmio_flags); buf[s - rpmio_flags] = '\0'; headerPutString(h, RPMTAG_PAYLOADFLAGS, buf+1); free(buf); } /* Create and add the cookie */ if (cookie) { rasprintf(cookie, "%s %d", buildHost(), (int) (*getBuildTime())); headerPutString(h, RPMTAG_COOKIE, *cookie); } /* Reallocate the header into one contiguous region. */ h = headerReload(h, RPMTAG_HEADERIMMUTABLE); if (h == NULL) { /* XXX can't happen */ rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to create immutable header region.\n")); goto exit; } /* Re-reference reallocated header. */ *hdrp = headerLink(h); /* * Write the header+archive into a temp file so that the size of * archive (after compression) can be added to the header. */ fd = rpmMkTempFile(NULL, &sigtarget); if (fd == NULL || Ferror(fd)) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to open temp file.\n")); goto exit; } fdInitDigest(fd, PGPHASHALGO_SHA1, 0); if (headerWrite(fd, h, HEADER_MAGIC_YES)) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to write temp header\n")); } else { /* Write the archive and get the size */ (void) Fflush(fd); fdFiniDigest(fd, PGPHASHALGO_SHA1, (void **)&SHA1, NULL, 1); if (csa->cpioList != NULL) { rc = cpio_doio(fd, h, csa, rpmio_flags); } else if (Fileno(csa->cpioFdIn) >= 0) { rc = cpio_copy(fd, csa); } else { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Bad CSA data\n")); } } if (rc != RPMRC_OK) goto exit; (void) Fclose(fd); fd = NULL; (void) unlink(fileName); /* Generate the signature */ (void) fflush(stdout); sig = rpmNewSignature(); /* * There should be rpmlib() dependency on this, but that doesn't * really do much good as these are signature tags that get read * way before dependency checking has a chance to figure out anything. * On the positive side, not inserting the 32bit tag at all means * older rpm will just bail out with error message on attempt to read * such a package. */ if (csa->cpioArchiveSize < UINT32_MAX) { sizetag = RPMSIGTAG_SIZE; payloadtag = RPMSIGTAG_PAYLOADSIZE; } else { sizetag = RPMSIGTAG_LONGSIZE; payloadtag = RPMSIGTAG_LONGARCHIVESIZE; } (void) rpmGenDigest(sig, sigtarget, sizetag); (void) rpmGenDigest(sig, sigtarget, RPMSIGTAG_MD5); if (SHA1) { /* XXX can't use rpmtdFromFoo() on RPMSIGTAG_* items */ rpmtdReset(&td); td.tag = RPMSIGTAG_SHA1; td.type = RPM_STRING_TYPE; td.data = SHA1; td.count = 1; headerPut(sig, &td, HEADERPUT_DEFAULT); SHA1 = _free(SHA1); } { /* XXX can't use headerPutType() on legacy RPMSIGTAG_* items */ rpmtdReset(&td); td.tag = payloadtag; td.count = 1; if (payloadtag == RPMSIGTAG_PAYLOADSIZE) { rpm_off_t asize = csa->cpioArchiveSize; td.type = RPM_INT32_TYPE; td.data = &asize; headerPut(sig, &td, HEADERPUT_DEFAULT); } else { rpm_loff_t asize = csa->cpioArchiveSize; td.type = RPM_INT64_TYPE; td.data = &asize; headerPut(sig, &td, HEADERPUT_DEFAULT); } } /* Reallocate the signature into one contiguous region. */ sig = headerReload(sig, RPMTAG_HEADERSIGNATURES); if (sig == NULL) { /* XXX can't happen */ rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to reload signature header.\n")); goto exit; } /* Open the output file */ fd = Fopen(fileName, "w.ufdio"); if (fd == NULL || Ferror(fd)) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Could not open %s: %s\n"), fileName, Fstrerror(fd)); goto exit; } /* Write the lead section into the package. */ { rpmlead lead = rpmLeadFromHeader(h); rc = rpmLeadWrite(fd, lead); lead = rpmLeadFree(lead); if (rc != RPMRC_OK) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to write package: %s\n"), Fstrerror(fd)); goto exit; } } /* Write the signature section into the package. */ if (rpmWriteSignature(fd, sig)) { rc = RPMRC_FAIL; goto exit; } /* Append the header and archive */ ifd = Fopen(sigtarget, "r.ufdio"); if (ifd == NULL || Ferror(ifd)) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to open sigtarget %s: %s\n"), sigtarget, Fstrerror(ifd)); goto exit; } /* Add signatures to header, and write header into the package. */ /* XXX header+payload digests/signatures might be checked again here. */ { Header nh = headerRead(ifd, HEADER_MAGIC_YES); if (nh == NULL) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to read header from %s: %s\n"), sigtarget, Fstrerror(ifd)); goto exit; } #ifdef NOTYET (void) headerMergeLegacySigs(nh, sig); #endif xx = headerWrite(fd, nh, HEADER_MAGIC_YES); nh = headerFree(nh); if (xx) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to write header to %s: %s\n"), fileName, Fstrerror(fd)); goto exit; } } /* Write the payload into the package. */ buf = xmalloc(BUFSIZ); while ((count = Fread(buf, 1, BUFSIZ, ifd)) > 0) { if (count == -1) { free(buf); rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to read payload from %s: %s\n"), sigtarget, Fstrerror(ifd)); goto exit; } if (Fwrite(buf, sizeof(buf[0]), count, fd) != count) { free(buf); rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to write payload to %s: %s\n"), fileName, Fstrerror(fd)); goto exit; } } free(buf); rc = RPMRC_OK; exit: rpmio_flags = _free(rpmio_flags); SHA1 = _free(SHA1); h = headerFree(h); /* XXX Fish the pkgid out of the signature header. */ if (sig != NULL && pkgidp != NULL) { struct rpmtd_s md5tag; headerGet(sig, RPMSIGTAG_MD5, &md5tag, HEADERGET_DEFAULT); if (rpmtdType(&md5tag) == RPM_BIN_TYPE && md5tag.count == 16 && md5tag.data != NULL) { *pkgidp = md5tag.data; } } sig = rpmFreeSignature(sig); if (ifd) { (void) Fclose(ifd); ifd = NULL; } if (fd) { (void) Fclose(fd); fd = NULL; } if (sigtarget) { (void) unlink(sigtarget); sigtarget = _free(sigtarget); } if (rc == RPMRC_OK) rpmlog(RPMLOG_NOTICE, _("Wrote: %s\n"), fileName); else (void) unlink(fileName); return rc; }
int rpmDoDigest(int algo, const char * fn,int asAscii, unsigned char * digest, rpm_loff_t * fsizep) { const char * path; urltype ut = urlPath(fn, &path); unsigned char * dig = NULL; size_t diglen; unsigned char buf[32*BUFSIZ]; FD_t fd; rpm_loff_t fsize = 0; pid_t pid = 0; int rc = 0; int fdno; fdno = open_dso(path, &pid, &fsize); if (fdno < 0) { rc = 1; goto exit; } switch(ut) { case URL_IS_PATH: case URL_IS_UNKNOWN: case URL_IS_HTTPS: case URL_IS_HTTP: case URL_IS_FTP: case URL_IS_HKP: case URL_IS_DASH: default: /* Either use the pipe to prelink -y or open the URL. */ fd = (pid != 0) ? fdDup(fdno) : Fopen(fn, "r.ufdio"); (void) close(fdno); if (fd == NULL || Ferror(fd)) { rc = 1; if (fd != NULL) (void) Fclose(fd); break; } fdInitDigest(fd, algo, 0); fsize = 0; while ((rc = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) fsize += rc; fdFiniDigest(fd, algo, (void **)&dig, &diglen, asAscii); if (dig == NULL || Ferror(fd)) rc = 1; (void) Fclose(fd); break; } /* Reap the prelink -y helper. */ if (pid) { int status; (void) waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status)) rc = 1; } exit: if (fsizep) *fsizep = fsize; if (!rc) memcpy(digest, dig, diglen); dig = _free(dig); return rc; }
/* * This is more than just a little insane: * In order to write the signature, we need to know the size and * the size and digests of the header and payload, which are located * after the signature on disk. We also need a digest of the compressed * payload for the main header, and of course the payload is after the * header on disk. So we need to create placeholders for both the * signature and main header that exactly match the final sizes, calculate * the payload digest, then generate and write the real main header to * be able to FINALLY calculate the digests we need for the signature * header. In other words, we need to write things in the exact opposite * order to how the RPM format is laid on disk. */ static rpmRC writeRPM(Package pkg, unsigned char ** pkgidp, const char *fileName, char **cookie) { FD_t fd = NULL; char * rpmio_flags = NULL; char * SHA1 = NULL; char * SHA256 = NULL; uint8_t * MD5 = NULL; char * pld = NULL; uint32_t pld_algo = PGPHASHALGO_SHA256; /* TODO: macro configuration */ rpmRC rc = RPMRC_FAIL; /* assume failure */ rpm_loff_t archiveSize = 0; off_t sigStart, hdrStart, payloadStart, payloadEnd; if (pkgidp) *pkgidp = NULL; rpmio_flags = getIOFlags(pkg); if (!rpmio_flags) goto exit; finalizeDeps(pkg); /* Create and add the cookie */ if (cookie) { rasprintf(cookie, "%s %d", buildHost(), (int) (*getBuildTime())); headerPutString(pkg->header, RPMTAG_COOKIE, *cookie); } /* Create a dummy payload digest to get the header size right */ pld = nullDigest(pld_algo, 1); headerPutUint32(pkg->header, RPMTAG_PAYLOADDIGESTALGO, &pld_algo, 1); headerPutString(pkg->header, RPMTAG_PAYLOADDIGEST, pld); pld = _free(pld); /* Check for UTF-8 encoding of string tags, add encoding tag if all good */ if (checkForEncoding(pkg->header, 1)) goto exit; /* Open the output file */ fd = Fopen(fileName, "w+.ufdio"); if (fd == NULL || Ferror(fd)) { rpmlog(RPMLOG_ERR, _("Could not open %s: %s\n"), fileName, Fstrerror(fd)); goto exit; } /* Write the lead section into the package. */ if (rpmLeadWrite(fd, pkg->header)) { rpmlog(RPMLOG_ERR, _("Unable to write package: %s\n"), Fstrerror(fd)); goto exit; } /* Save the position of signature section */ sigStart = Ftell(fd); /* Generate and write a placeholder signature header */ SHA1 = nullDigest(PGPHASHALGO_SHA1, 1); SHA256 = nullDigest(PGPHASHALGO_SHA256, 1); MD5 = nullDigest(PGPHASHALGO_MD5, 0); if (rpmGenerateSignature(SHA256, SHA1, MD5, 0, 0, fd)) goto exit; SHA1 = _free(SHA1); SHA256 = _free(SHA256); MD5 = _free(MD5); /* Write a placeholder header. */ hdrStart = Ftell(fd); if (writeHdr(fd, pkg->header)) goto exit; /* Write payload section (cpio archive) */ payloadStart = Ftell(fd); if (cpio_doio(fd, pkg, rpmio_flags, &archiveSize)) goto exit; payloadEnd = Ftell(fd); /* Re-read payload to calculate compressed digest */ fdInitDigestID(fd, pld_algo, RPMTAG_PAYLOADDIGEST, 0); if (fdConsume(fd, payloadStart, payloadEnd - payloadStart)) goto exit; fdFiniDigest(fd, RPMTAG_PAYLOADDIGEST, (void **)&pld, NULL, 1); /* Insert the payload digest in main header */ headerDel(pkg->header, RPMTAG_PAYLOADDIGEST); headerPutString(pkg->header, RPMTAG_PAYLOADDIGEST, pld); pld = _free(pld); /* Write the final header */ if (fdJump(fd, hdrStart)) goto exit; if (writeHdr(fd, pkg->header)) goto exit; /* Calculate digests: SHA on header, legacy MD5 on header + payload */ fdInitDigestID(fd, PGPHASHALGO_MD5, RPMTAG_SIGMD5, 0); fdInitDigestID(fd, PGPHASHALGO_SHA1, RPMTAG_SHA1HEADER, 0); fdInitDigestID(fd, PGPHASHALGO_SHA256, RPMTAG_SHA256HEADER, 0); if (fdConsume(fd, hdrStart, payloadStart - hdrStart)) goto exit; fdFiniDigest(fd, RPMTAG_SHA1HEADER, (void **)&SHA1, NULL, 1); fdFiniDigest(fd, RPMTAG_SHA256HEADER, (void **)&SHA256, NULL, 1); if (fdConsume(fd, 0, payloadEnd - payloadStart)) goto exit; fdFiniDigest(fd, RPMTAG_SIGMD5, (void **)&MD5, NULL, 0); if (fdJump(fd, sigStart)) goto exit; /* Generate the signature. Now with right values */ if (rpmGenerateSignature(SHA256, SHA1, MD5, payloadEnd - hdrStart, archiveSize, fd)) goto exit; rc = RPMRC_OK; exit: free(rpmio_flags); free(SHA1); free(SHA256); /* XXX Fish the pkgid out of the signature header. */ if (pkgidp != NULL) { if (MD5 != NULL) { *pkgidp = MD5; } } else { free(MD5); } Fclose(fd); if (rc == RPMRC_OK) rpmlog(RPMLOG_NOTICE, _("Wrote: %s\n"), fileName); else (void) unlink(fileName); return rc; }
static rpmRC includeFileSignatures(FD_t fd, const char *rpm, Header *sigp, Header *hdrp, off_t sigStart, off_t headerStart) { FD_t ofd = NULL; char *trpm = NULL; char *key; char *keypass; char *SHA1 = NULL; uint8_t *MD5 = NULL; size_t sha1len; size_t md5len; off_t sigTargetSize; rpmRC rc = RPMRC_OK; struct rpmtd_s osigtd; char *o_sha1 = NULL; uint8_t o_md5[16]; #ifndef WITH_IMAEVM rpmlog(RPMLOG_ERR, _("missing libimaevm\n")); return RPMRC_FAIL; #endif unloadImmutableRegion(hdrp, RPMTAG_HEADERIMMUTABLE); key = rpmExpand("%{?_file_signing_key}", NULL); keypass = rpmExpand("%{_file_signing_key_password}", NULL); if (rstreq(keypass, "")) { free(keypass); keypass = NULL; } rc = rpmSignFiles(*hdrp, key, keypass); if (rc != RPMRC_OK) { goto exit; } *hdrp = headerReload(*hdrp, RPMTAG_HEADERIMMUTABLE); if (*hdrp == NULL) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("headerReload failed\n")); goto exit; } ofd = rpmMkTempFile(NULL, &trpm); if (ofd == NULL || Ferror(ofd)) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n")); goto exit; } /* Copy archive to temp file */ if (copyFile(&fd, rpm, &ofd, trpm)) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("copyFile failed\n")); goto exit; } if (Fseek(fd, headerStart, SEEK_SET) < 0) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), rpm, Fstrerror(fd)); goto exit; } /* Start MD5 calculation */ fdInitDigest(fd, PGPHASHALGO_MD5, 0); /* Write header to rpm and recalculate SHA1 */ fdInitDigest(fd, PGPHASHALGO_SHA1, 0); rc = headerWrite(fd, *hdrp, HEADER_MAGIC_YES); if (rc != RPMRC_OK) { rpmlog(RPMLOG_ERR, _("headerWrite failed\n")); goto exit; } fdFiniDigest(fd, PGPHASHALGO_SHA1, (void **)&SHA1, &sha1len, 1); /* Copy archive from temp file */ if (Fseek(ofd, 0, SEEK_SET) < 0) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), rpm, Fstrerror(fd)); goto exit; } if (copyFile(&ofd, trpm, &fd, rpm)) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("copyFile failed\n")); goto exit; } unlink(trpm); sigTargetSize = Ftell(fd) - headerStart; fdFiniDigest(fd, PGPHASHALGO_MD5, (void **)&MD5, &md5len, 0); if (headerGet(*sigp, RPMSIGTAG_MD5, &osigtd, HEADERGET_DEFAULT)) { memcpy(o_md5, osigtd.data, 16); rpmtdFreeData(&osigtd); } if (headerGet(*sigp, RPMSIGTAG_SHA1, &osigtd, HEADERGET_DEFAULT)) { o_sha1 = xstrdup(osigtd.data); rpmtdFreeData(&osigtd); } if (memcmp(MD5, o_md5, md5len) == 0 && strcmp(SHA1, o_sha1) == 0) rpmlog(RPMLOG_WARNING, _("%s already contains identical file signatures\n"), rpm); else replaceSigDigests(fd, rpm, sigp, sigStart, sigTargetSize, SHA1, MD5); exit: free(trpm); free(MD5); free(SHA1); free(o_sha1); free(keypass); free(key); if (ofd) (void) closeFile(&ofd); return rc; }