2 * \file lib/rpmchecksig.c
3 * Verify the signature of a package.
12 #include <rpm/rpmlib.h> /* RPMSIGTAG & related */
13 #include <rpm/rpmmacro.h>
14 #include <rpm/rpmpgp.h>
15 #include <rpm/rpmsign.h>
16 #include <rpm/rpmfileutil.h> /* rpmMkTemp() */
17 #include <rpm/rpmlog.h>
18 #include <rpm/rpmstring.h>
20 #include "lib/rpmlead.h"
21 #include "lib/signature.h"
25 #if !defined(__GLIBC__) && !defined(__APPLE__)
26 char ** environ = NULL;
29 static int closeFile(FD_t *fdp)
31 if (fdp == NULL || *fdp == NULL)
34 /* close and reset *fdp to NULL */
42 static int manageFile(FD_t *fdp, const char *fn, int flags)
46 if (fdp == NULL || fn == NULL) /* programmer error */
49 /* open a file and set *fdp */
50 if (*fdp == NULL && fn != NULL) {
51 fd = Fopen(fn, (flags & O_ACCMODE) == O_WRONLY ? "w.ufdio" : "r.ufdio");
52 if (fd == NULL || Ferror(fd)) {
53 rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), fn,
62 if (*fdp != NULL && fn != NULL)
65 /* XXX never reached */
70 * Copy header+payload, calculating digest(s) on the fly.
72 static int copyFile(FD_t *sfdp, const char *sfnp,
73 FD_t *tfdp, const char *tfnp)
75 unsigned char buf[BUFSIZ];
79 if (manageFile(sfdp, sfnp, O_RDONLY))
81 if (manageFile(tfdp, tfnp, O_WRONLY|O_CREAT|O_TRUNC))
84 while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), *sfdp)) > 0)
86 if (Fwrite(buf, sizeof(buf[0]), count, *tfdp) != count) {
87 rpmlog(RPMLOG_ERR, _("%s: Fwrite failed: %s\n"), tfnp,
93 rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), sfnp, Fstrerror(*sfdp));
96 if (Fflush(*tfdp) != 0) {
97 rpmlog(RPMLOG_ERR, _("%s: Fflush failed: %s\n"), tfnp,
104 if (*sfdp) (void) closeFile(sfdp);
105 if (*tfdp) (void) closeFile(tfdp);
110 * Validate generated signature and insert to header if it looks sane.
111 * NSS doesn't support everything GPG does. Basic tests to see if the
112 * generated signature is something we can use.
113 * Return 0 on success, 1 on failure.
115 static int putSignature(Header sigh, int ishdr, uint8_t *pkt, size_t pktlen)
117 pgpDigParams sigp = NULL;
119 struct rpmtd_s sigtd;
120 int rc = 1; /* assume failure */
121 unsigned int hash_algo;
122 unsigned int pubkey_algo;
124 if (pgpPrtParams(pkt, pktlen, PGPTAG_SIGNATURE, &sigp)) {
125 rpmlog(RPMLOG_ERR, _("Unsupported PGP signature\n"));
129 hash_algo = pgpDigParamsAlgo(sigp, PGPVAL_HASHALGO);
130 if (rpmDigestLength(hash_algo) == 0) {
131 rpmlog(RPMLOG_ERR, _("Unsupported PGP hash algorithm %u\n"), hash_algo);
135 pubkey_algo = pgpDigParamsAlgo(sigp, PGPVAL_PUBKEYALGO);
136 switch (pubkey_algo) {
137 case PGPPUBKEYALGO_DSA:
138 sigtag = ishdr ? RPMSIGTAG_DSA : RPMSIGTAG_GPG;
140 case PGPPUBKEYALGO_RSA:
141 sigtag = ishdr ? RPMSIGTAG_RSA : RPMSIGTAG_PGP;
144 rpmlog(RPMLOG_ERR, _("Unsupported PGP pubkey algorithm %u\n"),
150 /* Looks sane, insert into header */
152 sigtd.count = pktlen;
154 sigtd.type = RPM_BIN_TYPE;
157 /* Argh, reversed return codes */
158 rc = (headerPut(sigh, &sigtd, HEADERPUT_DEFAULT) == 0);
161 pgpDigParamsFree(sigp);
165 static int runGPG(const char *file, const char *sigfile, const char * passPhrase)
170 int rc = 1; /* assume failure */
172 inpipe[0] = inpipe[1] = 0;
173 if (pipe(inpipe) < 0) {
174 rpmlog(RPMLOG_ERR, _("Couldn't create pipe for signing: %m"));
178 addMacro(NULL, "__plaintext_filename", NULL, file, -1);
179 addMacro(NULL, "__signature_filename", NULL, sigfile, -1);
181 if (!(pid = fork())) {
184 const char *gpg_path = rpmExpand("%{?_gpg_path}", NULL);
186 (void) dup2(inpipe[0], 3);
187 (void) close(inpipe[1]);
189 if (gpg_path && *gpg_path != '\0')
190 (void) setenv("GNUPGHOME", gpg_path, 1);
191 (void) setenv("LC_ALL", "C", 1);
193 unsetenv("MALLOC_CHECK_");
194 cmd = rpmExpand("%{?__gpg_sign_cmd}", NULL);
195 rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
197 rc = execve(av[0], av+1, environ);
199 rpmlog(RPMLOG_ERR, _("Could not exec %s: %s\n"), "gpg",
204 delMacro(NULL, "__plaintext_filename");
205 delMacro(NULL, "__signature_filename");
207 fpipe = fdopen(inpipe[1], "w");
208 (void) close(inpipe[0]);
210 fprintf(fpipe, "%s\n", (passPhrase ? passPhrase : ""));
211 (void) fclose(fpipe);
214 (void) waitpid(pid, &status, 0);
215 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
216 rpmlog(RPMLOG_ERR, _("gpg exec failed (%d)\n"), WEXITSTATUS(status));
225 * Generate GPG signature(s) for a header+payload file.
226 * @param sigh signature header
227 * @param ishdr header-only signature?
228 * @param file header+payload file name
229 * @param passPhrase private key pass phrase
230 * @return 0 on success, 1 on failure
232 static int makeGPGSignature(Header sigh, int ishdr,
233 const char * file, const char * passPhrase)
235 char * sigfile = rstrscat(NULL, file, ".sig", NULL);
237 uint8_t * pkt = NULL;
239 int rc = 1; /* assume failure */
241 if (runGPG(file, sigfile, passPhrase))
244 if (stat(sigfile, &st)) {
245 /* GPG failed to write signature */
246 rpmlog(RPMLOG_ERR, _("gpg failed to write signature\n"));
251 rpmlog(RPMLOG_DEBUG, "GPG sig size: %zd\n", pktlen);
252 pkt = xmalloc(pktlen);
257 fd = Fopen(sigfile, "r.ufdio");
258 if (fd != NULL && !Ferror(fd)) {
259 rc = Fread(pkt, sizeof(*pkt), pktlen, fd);
263 rpmlog(RPMLOG_ERR, _("unable to read the signature\n"));
268 rpmlog(RPMLOG_DEBUG, "Got %zd bytes of GPG sig\n", pktlen);
270 /* Parse the signature, change signature tag as appropriate. */
271 rc = putSignature(sigh, ishdr, pkt, pktlen);
273 (void) unlink(sigfile);
281 * Generate header only signature(s) from a header+payload file.
282 * @param sigh signature header
283 * @param file header+payload file name
284 * @param passPhrase private key pass phrase
285 * @return 0 on success, -1 on failure
287 static int makeHDRSignature(Header sigh, const char * file,
288 const char * passPhrase)
293 int ret = -1; /* assume failure. */
295 fd = Fopen(file, "r.fdio");
296 if (fd == NULL || Ferror(fd))
298 h = headerRead(fd, HEADER_MAGIC_YES);
303 fd = rpmMkTempFile(NULL, &fn);
304 if (fd == NULL || Ferror(fd))
306 if (headerWrite(fd, h, HEADER_MAGIC_YES))
309 ret = makeGPGSignature(sigh, 1, fn, passPhrase);
317 if (fd != NULL) (void) Fclose(fd);
321 static int rpmGenSignature(Header sigh, const char * file,
322 const char * passPhrase)
324 int ret = -1; /* assume failure. */
326 if (makeGPGSignature(sigh, 0, file, passPhrase) == 0) {
327 /* XXX Piggyback a header-only DSA/RSA signature as well. */
328 ret = makeHDRSignature(sigh, file, passPhrase);
335 * Retrieve signature from header tag
336 * @param sigh signature header
337 * @param sigtag signature tag
338 * @return parsed pgp dig or NULL
340 static pgpDigParams getSig(Header sigh, rpmTagVal sigtag)
343 pgpDigParams sig = NULL;
345 if (headerGet(sigh, sigtag, &pkt, HEADERGET_DEFAULT) && pkt.data != NULL) {
346 pgpPrtParams(pkt.data, pkt.count, PGPTAG_SIGNATURE, &sig);
352 static void deleteSigs(Header sigh)
354 headerDel(sigh, RPMSIGTAG_GPG);
355 headerDel(sigh, RPMSIGTAG_PGP);
356 headerDel(sigh, RPMSIGTAG_DSA);
357 headerDel(sigh, RPMSIGTAG_RSA);
358 headerDel(sigh, RPMSIGTAG_PGP5);
361 static int sameSignature(rpmTagVal sigtag, Header h1, Header h2)
363 pgpDigParams sig1 = getSig(h1, sigtag);
364 pgpDigParams sig2 = getSig(h2, sigtag);;
366 int rc = pgpDigParamsCmp(sig1, sig2);
368 pgpDigParamsFree(sig1);
369 pgpDigParamsFree(sig2);
373 static int replaceSignature(Header sigh, const char *sigtarget,
374 const char *passPhrase)
376 /* Grab a copy of the header so we can compare the result */
377 Header oldsigh = headerCopy(sigh);
380 /* Nuke all signature tags */
384 * rpmGenSignature() internals parse the actual signing result and
385 * adds appropriate tags for DSA/RSA.
387 if (rpmGenSignature(sigh, sigtarget, passPhrase) == 0) {
388 /* Lets see what we got and whether its the same signature as before */
389 rpmTagVal sigtag = headerIsEntry(sigh, RPMSIGTAG_DSA) ?
390 RPMSIGTAG_DSA : RPMSIGTAG_RSA;
392 rc = sameSignature(sigtag, sigh, oldsigh);
401 * Create/modify elements in signature header.
402 * @param rpm path to package
403 * @param deleting adding or deleting signature?
404 * @param passPhrase passPhrase (ignored when deleting)
405 * @return 0 on success, -1 on error
407 static int rpmSign(const char *rpm, int deleting, const char *passPhrase)
412 char *sigtarget = NULL, *trpm = NULL;
415 int res = -1; /* assume failure */
419 fprintf(stdout, "%s:\n", rpm);
421 if (manageFile(&fd, rpm, O_RDONLY))
424 if ((rc = rpmLeadRead(fd, &lead, NULL, &msg)) != RPMRC_OK) {
425 rpmlog(RPMLOG_ERR, "%s: %s\n", rpm, msg);
430 rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
433 rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), rpm,
434 (msg && *msg ? msg : "\n"));
440 rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), rpm);
447 ofd = rpmMkTempFile(NULL, &sigtarget);
448 if (ofd == NULL || Ferror(ofd)) {
449 rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
452 /* Write the header and archive to a temp file */
453 if (copyFile(&fd, rpm, &ofd, sigtarget))
455 /* Both fd and ofd are now closed. sigtarget contains tempfile name. */
457 /* Dump the immutable region (if present). */
458 if (headerGet(sigh, RPMTAG_HEADERSIGNATURES, &utd, HEADERGET_DEFAULT)) {
459 struct rpmtd_s copytd;
460 Header nh = headerNew();
461 Header oh = headerCopyLoad(utd.data);
462 HeaderIterator hi = headerInitIterator(oh);
463 while (headerNext(hi, ©td)) {
465 headerPut(nh, ©td, HEADERPUT_DEFAULT);
466 rpmtdFreeData(©td);
468 headerFreeIterator(hi);
472 sigh = headerLink(nh);
476 /* Eliminate broken digest values. */
477 headerDel(sigh, RPMSIGTAG_BADSHA1_1);
478 headerDel(sigh, RPMSIGTAG_BADSHA1_2);
480 /* Toss and recalculate header+payload size and digests. */
482 rpmTagVal const sigs[] = { RPMSIGTAG_SIZE,
486 int nsigs = sizeof(sigs) / sizeof(rpmTagVal);
487 for (int i = 0; i < nsigs; i++) {
488 (void) headerDel(sigh, sigs[i]);
489 if (rpmGenDigest(sigh, sigtarget, sigs[i]))
494 if (deleting) { /* Nuke all the signature tags. */
497 res = replaceSignature(sigh, sigtarget, passPhrase);
500 rpmlog(RPMLOG_WARNING,
501 _("%s already contains identical signature, skipping\n"),
503 /* Identical signature is not an error */
510 /* Reallocate the signature into one contiguous region. */
511 sigh = headerReload(sigh, RPMTAG_HEADERSIGNATURES);
512 if (sigh == NULL) /* XXX can't happen */
515 rasprintf(&trpm, "%s.XXXXXX", rpm);
516 ofd = rpmMkTemp(trpm);
517 if (ofd == NULL || Ferror(ofd)) {
518 rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
522 /* Write the lead/signature of the output rpm */
523 rc = rpmLeadWrite(ofd, lead);
524 if (rc != RPMRC_OK) {
525 rpmlog(RPMLOG_ERR, _("%s: writeLead failed: %s\n"), trpm,
530 if (rpmWriteSignature(ofd, sigh)) {
531 rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), trpm,
536 /* Append the header and archive from the temp file */
537 if (copyFile(&fd, sigtarget, &ofd, trpm) == 0) {
540 /* Move final target into place, restore file permissions. */
541 if (stat(rpm, &st) == 0 && unlink(rpm) == 0 &&
542 rename(trpm, rpm) == 0 && chmod(rpm, st.st_mode) == 0) {
545 rpmlog(RPMLOG_ERR, _("replacing %s failed: %s\n"),
546 rpm, strerror(errno));
551 if (fd) (void) closeFile(&fd);
552 if (ofd) (void) closeFile(&ofd);
554 rpmFreeSignature(sigh);
557 /* Clean up intermediate target */
570 int rpmPkgSign(const char *path,
571 const struct rpmSignArgs * args, const char *passPhrase)
576 if (args->hashalgo) {
578 rasprintf(&algo, "%d", args->hashalgo);
579 addMacro(NULL, "_gpg_digest_algo", NULL, algo, RMIL_GLOBAL);
583 addMacro(NULL, "_gpg_name", NULL, args->keyid, RMIL_GLOBAL);
587 rc = rpmSign(path, 0, passPhrase);
590 if (args->hashalgo) {
591 delMacro(NULL, "_gpg_digest_algo");
594 delMacro(NULL, "_gpg_name");
601 int rpmPkgDelSign(const char *path)
603 return rpmSign(path, 1, NULL);