upgrade rpm version to 4.14.1
[platform/upstream/rpm.git] / sign / rpmgensig.c
1 /** \ingroup rpmcli
2  * \file lib/rpmchecksig.c
3  * Verify the signature of a package.
4  */
5
6 #include "system.h"
7
8 #include <errno.h>
9 #include <sys/wait.h>
10 #include <popt.h>
11 #include <libgen.h>
12
13 #include <rpm/rpmlib.h>                 /* RPMSIGTAG & related */
14 #include <rpm/rpmmacro.h>
15 #include <rpm/rpmpgp.h>
16 #include <rpm/rpmsign.h>
17 #include <rpm/rpmfileutil.h>    /* rpmMkTemp() */
18 #include <rpm/rpmlog.h>
19 #include <rpm/rpmstring.h>
20 #include <rpmio/rpmio_internal.h>
21
22 #include "lib/rpmlead.h"
23 #include "lib/signature.h"
24 #include "sign/rpmsignfiles.h"
25
26 #include "debug.h"
27
28 typedef struct sigTarget_s {
29     FD_t fd;
30     const char *fileName;
31     off_t start;
32     rpm_loff_t size;
33 } *sigTarget;
34
35 /*
36  * There is no function for creating unique temporary fifos so create
37  * unique temporary directory and then create fifo in it.
38  */
39 static char *mkTempFifo(void)
40 {
41     char *tmppath = NULL, *tmpdir = NULL, *fifofn = NULL;
42     mode_t mode;
43
44     tmppath = rpmExpand("%{_tmppath}", NULL);
45     if (rpmioMkpath(tmppath, 0755, (uid_t) -1, (gid_t) -1))
46         goto exit;
47
48
49     tmpdir = rpmGetPath(tmppath, "/rpm-tmp.XXXXXX", NULL);
50     mode = umask(0077);
51     tmpdir = mkdtemp(tmpdir);
52     umask(mode);
53     if (tmpdir == NULL) {
54         rpmlog(RPMLOG_ERR, _("error creating temp directory %s: %m\n"),
55             tmpdir);
56         tmpdir = _free(tmpdir);
57         goto exit;
58     }
59
60     fifofn = rpmGetPath(tmpdir, "/fifo", NULL);
61     if (mkfifo(fifofn, 0600) == -1) {
62         rpmlog(RPMLOG_ERR, _("error creating fifo %s: %m\n"), fifofn);
63         fifofn = _free(fifofn);
64     }
65
66 exit:
67     if (fifofn == NULL && tmpdir != NULL)
68         unlink(tmpdir);
69
70     free(tmppath);
71     free(tmpdir);
72
73     return fifofn;
74 }
75
76 /* Delete fifo and then temporary directory in which it was located */
77 static int rpmRmTempFifo(const char *fn)
78 {
79     int rc = 0;
80     char *dfn = NULL, *dir = NULL;
81
82     if ((rc = unlink(fn)) != 0) {
83         rpmlog(RPMLOG_ERR, _("error delete fifo %s: %m\n"), fn);
84         return rc;
85     }
86
87     dfn = xstrdup(fn);
88     dir = dirname(dfn);
89
90     if ((rc = rmdir(dir)) != 0)
91         rpmlog(RPMLOG_ERR, _("error delete directory %s: %m\n"), dir);
92     free(dfn);
93
94     return rc;
95 }
96
97 static int closeFile(FD_t *fdp)
98 {
99     if (fdp == NULL || *fdp == NULL)
100         return 1;
101
102     /* close and reset *fdp to NULL */
103     (void) Fclose(*fdp);
104     *fdp = NULL;
105     return 0;
106 }
107
108 /**
109  */
110 static int manageFile(FD_t *fdp, const char *fn, int flags)
111 {
112     FD_t fd;
113     const char *fmode;
114
115     if (fdp == NULL || fn == NULL)      /* programmer error */
116         return 1;
117
118     /* open a file and set *fdp */
119     if (*fdp == NULL && fn != NULL) {
120         switch (flags & O_ACCMODE) {
121             case O_WRONLY:
122                 fmode = "w.ufdio";
123                 break;
124             case O_RDONLY:
125                 fmode = "r.ufdio";
126                 break;
127             default:
128             case O_RDWR:
129                 fmode = "r+.ufdio";
130                 break;
131         }
132         fd = Fopen(fn, fmode);
133         if (fd == NULL || Ferror(fd)) {
134             rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), fn,
135                 Fstrerror(fd));
136             return 1;
137         }
138         *fdp = fd;
139         return 0;
140     }
141
142     /* no operation */
143     if (*fdp != NULL && fn != NULL)
144         return 0;
145
146     /* XXX never reached */
147     return 1;
148 }
149
150 /**
151  * Copy header+payload, calculating digest(s) on the fly.
152  * @param sfdp source file
153  * @param sfnp source path
154  * @param tfdp destination file
155  * @param tfnp destination path
156  */
157 static int copyFile(FD_t *sfdp, const char *sfnp,
158                 FD_t *tfdp, const char *tfnp)
159 {
160     unsigned char buf[BUFSIZ];
161     ssize_t count;
162     int rc = 1;
163
164     while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), *sfdp)) > 0)
165     {
166         if (Fwrite(buf, sizeof(buf[0]), count, *tfdp) != count) {
167             rpmlog(RPMLOG_ERR, _("%s: Fwrite failed: %s\n"), tfnp,
168                 Fstrerror(*tfdp));
169             goto exit;
170         }
171     }
172     if (count < 0) {
173         rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), sfnp, Fstrerror(*sfdp));
174         goto exit;
175     }
176     if (Fflush(*tfdp) != 0) {
177         rpmlog(RPMLOG_ERR, _("%s: Fflush failed: %s\n"), tfnp,
178             Fstrerror(*tfdp));
179     }
180
181     rc = 0;
182
183 exit:
184     return rc;
185 }
186
187 /*
188  * Validate generated signature and insert to header if it looks sane.
189  * NSS doesn't support everything GPG does. Basic tests to see if the 
190  * generated signature is something we can use.
191  * Return generated signature tag data on success, NULL on failure.
192  */
193 static rpmtd makeSigTag(Header sigh, int ishdr, uint8_t *pkt, size_t pktlen)
194 {
195     pgpDigParams sigp = NULL;
196     rpmTagVal sigtag;
197     rpmtd sigtd = NULL;
198     unsigned int hash_algo;
199     unsigned int pubkey_algo;
200
201     if (pgpPrtParams(pkt, pktlen, PGPTAG_SIGNATURE, &sigp)) {
202         rpmlog(RPMLOG_ERR, _("Unsupported PGP signature\n"));
203         goto exit;
204     }
205
206     hash_algo = pgpDigParamsAlgo(sigp, PGPVAL_HASHALGO);
207     if (rpmDigestLength(hash_algo) == 0) {
208         rpmlog(RPMLOG_ERR, _("Unsupported PGP hash algorithm %u\n"), hash_algo);
209         goto exit;
210     }
211
212     pubkey_algo = pgpDigParamsAlgo(sigp, PGPVAL_PUBKEYALGO);
213     switch (pubkey_algo) {
214     case PGPPUBKEYALGO_DSA:
215         sigtag = ishdr ? RPMSIGTAG_DSA : RPMSIGTAG_GPG;
216         break;
217     case PGPPUBKEYALGO_RSA:
218         sigtag = ishdr ? RPMSIGTAG_RSA : RPMSIGTAG_PGP;
219         break;
220     default:
221         rpmlog(RPMLOG_ERR, _("Unsupported PGP pubkey algorithm %u\n"),
222                 pubkey_algo);
223         goto exit;
224         break;
225     }
226
227     /* Looks sane, create the tag data */
228     sigtd = rpmtdNew();
229     sigtd->count = pktlen;
230     sigtd->data = memcpy(xmalloc(pktlen), pkt, pktlen);;
231     sigtd->type = RPM_BIN_TYPE;
232     sigtd->tag = sigtag;
233     sigtd->flags |= RPMTD_ALLOCED;
234
235 exit:
236     pgpDigParamsFree(sigp);
237     return sigtd;
238 }
239
240 static int runGPG(sigTarget sigt, const char *sigfile)
241 {
242     int pid = 0, status;
243     FD_t fnamedPipe = NULL;
244     char *namedPipeName = NULL;
245     unsigned char buf[BUFSIZ];
246     ssize_t count;
247     ssize_t wantCount;
248     rpm_loff_t size;
249     int rc = 1; /* assume failure */
250
251     namedPipeName = mkTempFifo();
252
253     rpmPushMacro(NULL, "__plaintext_filename", NULL, namedPipeName, -1);
254     rpmPushMacro(NULL, "__signature_filename", NULL, sigfile, -1);
255
256     if (!(pid = fork())) {
257         char *const *av;
258         char *cmd = NULL;
259         const char *gpg_path = rpmExpand("%{?_gpg_path}", NULL);
260
261         if (gpg_path && *gpg_path != '\0')
262             (void) setenv("GNUPGHOME", gpg_path, 1);
263
264         unsetenv("MALLOC_CHECK_");
265         cmd = rpmExpand("%{?__gpg_sign_cmd}", NULL);
266         rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
267         if (!rc)
268             rc = execve(av[0], av+1, environ);
269
270         rpmlog(RPMLOG_ERR, _("Could not exec %s: %s\n"), "gpg",
271                         strerror(errno));
272         _exit(EXIT_FAILURE);
273     }
274
275     rpmPopMacro(NULL, "__plaintext_filename");
276     rpmPopMacro(NULL, "__signature_filename");
277
278     fnamedPipe = Fopen(namedPipeName, "w");
279     if (!fnamedPipe) {
280         rpmlog(RPMLOG_ERR, _("Fopen failed\n"));
281         goto exit;
282     }
283
284     if (Fseek(sigt->fd, sigt->start, SEEK_SET) < 0) {
285         rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
286                 sigt->fileName, Fstrerror(sigt->fd));
287         goto exit;
288     }
289
290     size = sigt->size;
291     wantCount = size < sizeof(buf) ? size : sizeof(buf);
292     while ((count = Fread(buf, sizeof(buf[0]), wantCount, sigt->fd)) > 0) {
293         Fwrite(buf, sizeof(buf[0]), count, fnamedPipe);
294         if (Ferror(fnamedPipe)) {
295             rpmlog(RPMLOG_ERR, _("Could not write to pipe\n"));
296             goto exit;
297         }
298         size -= count;
299         wantCount = size < sizeof(buf) ? size : sizeof(buf);
300     }
301     if (count < 0) {
302         rpmlog(RPMLOG_ERR, _("Could not read from file %s: %s\n"),
303                 sigt->fileName, Fstrerror(sigt->fd));
304         goto exit;
305     }
306     Fclose(fnamedPipe);
307     fnamedPipe = NULL;
308
309     (void) waitpid(pid, &status, 0);
310     pid = 0;
311     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
312         rpmlog(RPMLOG_ERR, _("gpg exec failed (%d)\n"), WEXITSTATUS(status));
313     } else {
314         rc = 0;
315     }
316
317 exit:
318
319     if (fnamedPipe)
320         Fclose(fnamedPipe);
321
322     if (pid)
323         waitpid(pid, &status, 0);
324
325     if (namedPipeName) {
326         rpmRmTempFifo(namedPipeName);
327         free(namedPipeName);
328     }
329
330     return rc;
331 }
332
333 /**
334  * Generate GPG signature(s) for a header+payload file.
335  * @param sigh          signature header
336  * @param ishdr         header-only signature?
337  * @param sigt          signature target
338  * @param passPhrase    private key pass phrase
339  * @return              generated sigtag on success, 0 on failure
340  */
341 static rpmtd makeGPGSignature(Header sigh, int ishdr, sigTarget sigt)
342 {
343     char * sigfile = rstrscat(NULL, sigt->fileName, ".sig", NULL);
344     struct stat st;
345     uint8_t * pkt = NULL;
346     size_t pktlen = 0;
347     rpmtd sigtd = NULL;
348
349     if (runGPG(sigt, sigfile))
350         goto exit;
351
352     if (stat(sigfile, &st)) {
353         /* GPG failed to write signature */
354         rpmlog(RPMLOG_ERR, _("gpg failed to write signature\n"));
355         goto exit;
356     }
357
358     pktlen = st.st_size;
359     rpmlog(RPMLOG_DEBUG, "GPG sig size: %zd\n", pktlen);
360     pkt = xmalloc(pktlen);
361
362     {   FD_t fd;
363
364         int rc = 0;
365         fd = Fopen(sigfile, "r.ufdio");
366         if (fd != NULL && !Ferror(fd)) {
367             rc = Fread(pkt, sizeof(*pkt), pktlen, fd);
368             (void) Fclose(fd);
369         }
370         if (rc != pktlen) {
371             rpmlog(RPMLOG_ERR, _("unable to read the signature\n"));
372             goto exit;
373         }
374     }
375
376     rpmlog(RPMLOG_DEBUG, "Got %zd bytes of GPG sig\n", pktlen);
377
378     /* Parse the signature, change signature tag as appropriate. */
379     sigtd = makeSigTag(sigh, ishdr, pkt, pktlen);
380 exit:
381     (void) unlink(sigfile);
382     free(sigfile);
383     free(pkt);
384
385     return sigtd;
386 }
387
388 static void deleteSigs(Header sigh)
389 {
390     headerDel(sigh, RPMSIGTAG_GPG);
391     headerDel(sigh, RPMSIGTAG_PGP);
392     headerDel(sigh, RPMSIGTAG_DSA);
393     headerDel(sigh, RPMSIGTAG_RSA);
394     headerDel(sigh, RPMSIGTAG_PGP5);
395 }
396
397 static int haveSignature(rpmtd sigtd, Header h)
398 {
399     pgpDigParams sig1 = NULL;
400     pgpDigParams sig2 = NULL;
401     struct rpmtd_s oldtd;
402     int rc = 0; /* assume no */
403
404     if (!headerGet(h, rpmtdTag(sigtd), &oldtd, HEADERGET_DEFAULT))
405         return rc;
406
407     pgpPrtParams(sigtd->data, sigtd->count, PGPTAG_SIGNATURE, &sig1);
408     while (rpmtdNext(&oldtd) >= 0 && rc == 0) {
409         pgpPrtParams(oldtd.data, oldtd.count, PGPTAG_SIGNATURE, &sig2);
410         if (pgpDigParamsCmp(sig1, sig2) == 0)
411             rc = 1;
412         pgpDigParamsFree(sig2);
413     }
414     pgpDigParamsFree(sig1);
415     rpmtdFreeData(&oldtd);
416
417     return rc;
418 }
419
420 static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4)
421 {
422     int rc = -1;
423     rpmtd sigtd = NULL;
424     
425     /* Make the cheaper v4 signature first */
426     if ((sigtd = makeGPGSignature(sigh, 1, sigt_v4)) == NULL)
427         goto exit;
428
429     /* See if we already have a signature by the same key and parameters */
430     if (haveSignature(sigtd, sigh)) {
431         rc = 1;
432         goto exit;
433     }
434     /* Nuke all signature tags */
435     deleteSigs(sigh);
436
437     if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0)
438         goto exit;
439     rpmtdFree(sigtd);
440
441     /* Assume the same signature test holds for v3 signature too */
442     if ((sigtd = makeGPGSignature(sigh, 0, sigt_v3)) == NULL)
443         goto exit;
444
445     if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0)
446         goto exit;
447
448     rc = 0;
449 exit:
450     rpmtdFree(sigtd);
451     return rc;
452 }
453
454 static void unloadImmutableRegion(Header *hdrp, rpmTagVal tag)
455 {
456     struct rpmtd_s td;
457     rpmtd utd = &td;
458     Header nh;
459     Header oh;
460
461     if (headerGet(*hdrp, tag, utd, HEADERGET_DEFAULT)) {
462         oh = headerCopyLoad(utd->data);
463         nh = headerCopy(oh);
464         headerFree(oh);
465         rpmtdFreeData(utd);
466         headerFree(*hdrp);
467         *hdrp = headerLink(nh);
468         headerFree(nh);
469     }
470 }
471
472 #ifdef WITH_IMAEVM
473 static rpmRC replaceSigDigests(FD_t fd, const char *rpm, Header *sigp,
474                                off_t sigStart, off_t sigTargetSize,
475                                char *SHA256, char *SHA1, uint8_t *MD5)
476 {
477     off_t archiveSize;
478     rpmRC rc = RPMRC_OK;
479
480     if (Fseek(fd, sigStart, SEEK_SET) < 0) {
481         rc = RPMRC_FAIL;
482         rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
483                 rpm, Fstrerror(fd));
484         goto exit;
485     }
486
487     /* Get payload size from signature tag */
488     archiveSize = headerGetNumber(*sigp, RPMSIGTAG_PAYLOADSIZE);
489     if (!archiveSize) {
490         archiveSize = headerGetNumber(*sigp, RPMSIGTAG_LONGARCHIVESIZE);
491     }
492
493     /* Set reserved space to 0 */
494     rpmPushMacro(NULL, "__gpg_reserved_space", NULL, 0, RMIL_GLOBAL);
495
496     /* Replace old digests in sigh */
497     rc = rpmGenerateSignature(SHA256, SHA1, MD5, sigTargetSize, archiveSize, fd);
498     if (rc != RPMRC_OK) {
499         rpmlog(RPMLOG_ERR, _("generateSignature failed\n"));
500         goto exit;
501     }
502
503     if (Fseek(fd, sigStart, SEEK_SET) < 0) {
504         rc = RPMRC_FAIL;
505         rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
506                 rpm, Fstrerror(fd));
507         goto exit;
508     }
509
510     headerFree(*sigp);
511     rc = rpmReadSignature(fd, sigp, NULL);
512     if (rc != RPMRC_OK) {
513         rpmlog(RPMLOG_ERR, _("rpmReadSignature failed\n"));
514         goto exit;
515     }
516
517 exit:
518     return rc;
519 }
520 #endif
521
522 static rpmRC includeFileSignatures(FD_t fd, const char *rpm,
523                                    Header *sigp, Header *hdrp,
524                                    off_t sigStart, off_t headerStart)
525 {
526 #ifdef WITH_IMAEVM
527     FD_t ofd = NULL;
528     char *trpm = NULL;
529     char *key;
530     char *keypass;
531     char *SHA1 = NULL;
532     char *SHA256 = NULL;
533     uint8_t *MD5 = NULL;
534     off_t sigTargetSize;
535     rpmRC rc = RPMRC_OK;
536     struct rpmtd_s osigtd;
537     char *o_sha1 = NULL;
538
539     unloadImmutableRegion(hdrp, RPMTAG_HEADERIMMUTABLE);
540
541     key = rpmExpand("%{?_file_signing_key}", NULL);
542
543     keypass = rpmExpand("%{?_file_signing_key_password}", NULL);
544     if (rstreq(keypass, "")) {
545         free(keypass);
546         keypass = NULL;
547     }
548
549     rc = rpmSignFiles(*hdrp, key, keypass);
550     if (rc != RPMRC_OK) {
551         goto exit;
552     }
553
554     *hdrp = headerReload(*hdrp, RPMTAG_HEADERIMMUTABLE);
555     if (*hdrp == NULL) {
556         rc = RPMRC_FAIL;
557         rpmlog(RPMLOG_ERR, _("headerReload failed\n"));
558         goto exit;
559     }
560
561     ofd = rpmMkTempFile(NULL, &trpm);
562     if (ofd == NULL || Ferror(ofd)) {
563         rc = RPMRC_FAIL;
564         rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
565         goto exit;
566     }
567
568     /* Copy archive to temp file */
569     if (copyFile(&fd, rpm, &ofd, trpm)) {
570         rc = RPMRC_FAIL;
571         rpmlog(RPMLOG_ERR, _("copyFile failed\n"));
572         goto exit;
573     }
574
575     if (Fseek(fd, headerStart, SEEK_SET) < 0) {
576         rc = RPMRC_FAIL;
577         rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
578                 rpm, Fstrerror(fd));
579         goto exit;
580     }
581
582     /* Start MD5 calculation */
583     fdInitDigestID(fd, PGPHASHALGO_MD5, RPMSIGTAG_MD5, 0);
584
585     /* Write header to rpm and recalculate digests */
586     fdInitDigestID(fd, PGPHASHALGO_SHA1, RPMSIGTAG_SHA1, 0);
587     fdInitDigestID(fd, PGPHASHALGO_SHA256, RPMSIGTAG_SHA256, 0);
588     rc = headerWrite(fd, *hdrp, HEADER_MAGIC_YES);
589     if (rc != RPMRC_OK) {
590         rpmlog(RPMLOG_ERR, _("headerWrite failed\n"));
591         goto exit;
592     }
593     fdFiniDigest(fd, RPMSIGTAG_SHA1, (void **)&SHA1, NULL, 1);
594     /* Only add SHA256 if it was there to begin with */
595     if (headerIsEntry(*sigp, RPMSIGTAG_SHA256))
596         fdFiniDigest(fd, RPMSIGTAG_SHA256, (void **)&SHA256, NULL, 1);
597
598     /* Copy archive from temp file */
599     if (Fseek(ofd, 0, SEEK_SET) < 0) {
600         rc = RPMRC_FAIL;
601         rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
602                 rpm, Fstrerror(fd));
603         goto exit;
604     }
605     if (copyFile(&ofd, trpm, &fd, rpm)) {
606         rc = RPMRC_FAIL;
607         rpmlog(RPMLOG_ERR, _("copyFile failed\n"));
608         goto exit;
609     }
610     unlink(trpm);
611
612     sigTargetSize = Ftell(fd) - headerStart;
613     fdFiniDigest(fd, RPMSIGTAG_MD5, (void **)&MD5, NULL, 0);
614
615     if (headerGet(*sigp, RPMSIGTAG_SHA1, &osigtd, HEADERGET_DEFAULT)) {
616         o_sha1 = xstrdup(osigtd.data);
617         rpmtdFreeData(&osigtd);
618     }
619
620     if (strcmp(SHA1, o_sha1) == 0)
621         rpmlog(RPMLOG_WARNING,
622                _("%s already contains identical file signatures\n"),
623                rpm);
624     else
625         replaceSigDigests(fd, rpm, sigp, sigStart, sigTargetSize, SHA256, SHA1, MD5);
626
627 exit:
628     free(trpm);
629     free(MD5);
630     free(SHA1);
631     free(SHA256);
632     free(o_sha1);
633     free(keypass);
634     free(key);
635     if (ofd)
636         (void) closeFile(&ofd);
637     return rc;
638 #else
639     rpmlog(RPMLOG_ERR, _("file signing support not built in\n"));
640     return RPMRC_FAIL;
641 #endif
642 }
643
644 /** \ingroup rpmcli
645  * Create/modify elements in signature header.
646  * @param rpm           path to package
647  * @param deleting      adding or deleting signature?
648  * @param signfiles     sign files if non-zero
649  * @return              0 on success, -1 on error
650  */
651 static int rpmSign(const char *rpm, int deleting, int signfiles)
652 {
653     FD_t fd = NULL;
654     FD_t ofd = NULL;
655     char *trpm = NULL;
656     Header sigh = NULL;
657     Header h = NULL;
658     char *msg = NULL;
659     int res = -1; /* assume failure */
660     rpmRC rc;
661     struct rpmtd_s utd;
662     off_t headerStart;
663     off_t sigStart;
664     struct sigTarget_s sigt_v3;
665     struct sigTarget_s sigt_v4;
666     unsigned int origSigSize;
667     int insSig = 0;
668
669     fprintf(stdout, "%s:\n", rpm);
670
671     if (manageFile(&fd, rpm, O_RDWR))
672         goto exit;
673
674     if ((rc = rpmLeadRead(fd, NULL, &msg)) != RPMRC_OK) {
675         rpmlog(RPMLOG_ERR, "%s: %s\n", rpm, msg);
676         goto exit;
677     }
678
679     sigStart = Ftell(fd);
680     rc = rpmReadSignature(fd, &sigh, &msg);
681     if (rc != RPMRC_OK) {
682         rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), rpm,
683                     (msg && *msg ? msg : "\n"));
684         goto exit;
685     }
686
687     headerStart = Ftell(fd);
688     if (rpmReadHeader(NULL, fd, &h, &msg) != RPMRC_OK) {
689         rpmlog(RPMLOG_ERR, _("%s: headerRead failed: %s\n"), rpm, msg);
690         goto exit;
691     }
692
693     if (!headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
694         rpmlog(RPMLOG_ERR, _("Cannot sign RPM v3 packages\n"));
695         goto exit;
696     }
697
698     if (signfiles) {
699         includeFileSignatures(fd, rpm, &sigh, &h, sigStart, headerStart);
700     }
701
702     unloadImmutableRegion(&sigh, RPMTAG_HEADERSIGNATURES);
703     origSigSize = headerSizeof(sigh, HEADER_MAGIC_YES);
704
705     if (deleting) {     /* Nuke all the signature tags. */
706         deleteSigs(sigh);
707     } else {
708         /* Signature target containing header + payload */
709         sigt_v3.fd = fd;
710         sigt_v3.start = headerStart;
711         sigt_v3.fileName = rpm;
712         sigt_v3.size = fdSize(fd) - headerStart;
713
714         /* Signature target containing only header */
715         sigt_v4 = sigt_v3;
716         sigt_v4.size = headerSizeof(h, HEADER_MAGIC_YES);
717
718         res = replaceSignature(sigh, &sigt_v3, &sigt_v4);
719         if (res != 0) {
720             if (res == 1) {
721                 rpmlog(RPMLOG_WARNING,
722                    _("%s already contains identical signature, skipping\n"),
723                    rpm);
724                 /* Identical signature is not an error */
725                 res = 0;
726             }
727             goto exit;
728         }
729         res = -1;
730     }
731
732     /* Try to make new signature smaller to have size of original signature */
733     rpmtdReset(&utd);
734     if (headerGet(sigh, RPMSIGTAG_RESERVEDSPACE, &utd, HEADERGET_MINMEM)) {
735         int diff;
736         int count;
737         char *reservedSpace = NULL;
738
739         count = utd.count;
740         diff = headerSizeof(sigh, HEADER_MAGIC_YES) - origSigSize;
741
742         if (diff < count) {
743             reservedSpace = xcalloc(count - diff, sizeof(char));
744             headerDel(sigh, RPMSIGTAG_RESERVEDSPACE);
745             rpmtdReset(&utd);
746             utd.tag = RPMSIGTAG_RESERVEDSPACE;
747             utd.count = count - diff;
748             utd.type = RPM_BIN_TYPE;
749             utd.data = reservedSpace;
750             headerPut(sigh, &utd, HEADERPUT_DEFAULT);
751             free(reservedSpace);
752             insSig = 1;
753         }
754     }
755
756     /* Reallocate the signature into one contiguous region. */
757     sigh = headerReload(sigh, RPMTAG_HEADERSIGNATURES);
758     if (sigh == NULL)   /* XXX can't happen */
759         goto exit;
760
761     if (insSig) {
762         /* Insert new signature into original rpm */
763         if (Fseek(fd, sigStart, SEEK_SET) < 0) {
764             rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
765                     rpm, Fstrerror(fd));
766             goto exit;
767         }
768
769         if (rpmWriteSignature(fd, sigh)) {
770             rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), rpm,
771                 Fstrerror(fd));
772             goto exit;
773         }
774         res = 0;
775     } else {
776         /* Replace orignal rpm with new rpm containing new signature */
777         rasprintf(&trpm, "%s.XXXXXX", rpm);
778         ofd = rpmMkTemp(trpm);
779         if (ofd == NULL || Ferror(ofd)) {
780             rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
781             goto exit;
782         }
783
784         /* Write the lead/signature of the output rpm */
785         rc = rpmLeadWrite(ofd, h);
786         if (rc != RPMRC_OK) {
787             rpmlog(RPMLOG_ERR, _("%s: writeLead failed: %s\n"), trpm,
788                 Fstrerror(ofd));
789             goto exit;
790         }
791
792         if (rpmWriteSignature(ofd, sigh)) {
793             rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), trpm,
794                 Fstrerror(ofd));
795             goto exit;
796         }
797
798         if (Fseek(fd, headerStart, SEEK_SET) < 0) {
799             rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
800                     rpm, Fstrerror(fd));
801             goto exit;
802         }
803         /* Append the header and archive from the temp file */
804         if (copyFile(&fd, rpm, &ofd, trpm) == 0) {
805             struct stat st;
806
807             /* Move final target into place, restore file permissions. */
808             if (stat(rpm, &st) == 0 && unlink(rpm) == 0 &&
809                         rename(trpm, rpm) == 0 && chmod(rpm, st.st_mode) == 0) {
810                 res = 0;
811             } else {
812                 rpmlog(RPMLOG_ERR, _("replacing %s failed: %s\n"),
813                        rpm, strerror(errno));
814             }
815         }
816     }
817
818 exit:
819     if (fd)     (void) closeFile(&fd);
820     if (ofd)    (void) closeFile(&ofd);
821
822     headerFree(sigh);
823     headerFree(h);
824     free(msg);
825
826     /* Clean up intermediate target */
827     if (trpm) {
828         (void) unlink(trpm);
829         free(trpm);
830     }
831
832     return res;
833 }
834
835 int rpmPkgSign(const char *path, const struct rpmSignArgs * args)
836 {
837     int rc;
838
839     if (args) {
840         if (args->hashalgo) {
841             char *algo = NULL;
842             rasprintf(&algo, "%d", args->hashalgo);
843             rpmPushMacro(NULL, "_gpg_digest_algo", NULL, algo, RMIL_GLOBAL);
844             free(algo);
845         }
846         if (args->keyid) {
847             rpmPushMacro(NULL, "_gpg_name", NULL, args->keyid, RMIL_GLOBAL);
848         }
849     }
850
851     rc = rpmSign(path, 0, args ? args->signfiles : 0);
852
853     if (args) {
854         if (args->hashalgo) {
855             rpmPopMacro(NULL, "_gpg_digest_algo");
856         }
857         if (args->keyid) {
858             rpmPopMacro(NULL, "_gpg_name");
859         }
860     }
861
862     return rc;
863 }
864
865 int rpmPkgDelSign(const char *path, const struct rpmSignArgs * args)
866 {
867     return rpmSign(path, 1, 0);
868 }