Modify eu-strip option to perform strip in post script of rpm package & add option...
[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
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>
19
20 #include "lib/rpmlead.h"
21 #include "lib/signature.h"
22
23 #include "debug.h"
24
25 #if !defined(__GLIBC__) && !defined(__APPLE__)
26 char ** environ = NULL;
27 #endif
28
29 static int closeFile(FD_t *fdp)
30 {
31     if (fdp == NULL || *fdp == NULL)
32         return 1;
33
34     /* close and reset *fdp to NULL */
35     (void) Fclose(*fdp);
36     *fdp = NULL;
37     return 0;
38 }
39
40 /**
41  */
42 static int manageFile(FD_t *fdp, const char *fn, int flags)
43 {
44     FD_t fd;
45
46     if (fdp == NULL || fn == NULL)      /* programmer error */
47         return 1;
48
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,
54                 Fstrerror(fd));
55             return 1;
56         }
57         *fdp = fd;
58         return 0;
59     }
60
61     /* no operation */
62     if (*fdp != NULL && fn != NULL)
63         return 0;
64
65     /* XXX never reached */
66     return 1;
67 }
68
69 /**
70  * Copy header+payload, calculating digest(s) on the fly.
71  */
72 static int copyFile(FD_t *sfdp, const char *sfnp,
73                 FD_t *tfdp, const char *tfnp)
74 {
75     unsigned char buf[BUFSIZ];
76     ssize_t count;
77     int rc = 1;
78
79     if (manageFile(sfdp, sfnp, O_RDONLY))
80         goto exit;
81     if (manageFile(tfdp, tfnp, O_WRONLY|O_CREAT|O_TRUNC))
82         goto exit;
83
84     while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), *sfdp)) > 0)
85     {
86         if (Fwrite(buf, sizeof(buf[0]), count, *tfdp) != count) {
87             rpmlog(RPMLOG_ERR, _("%s: Fwrite failed: %s\n"), tfnp,
88                 Fstrerror(*tfdp));
89             goto exit;
90         }
91     }
92     if (count < 0) {
93         rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), sfnp, Fstrerror(*sfdp));
94         goto exit;
95     }
96     if (Fflush(*tfdp) != 0) {
97         rpmlog(RPMLOG_ERR, _("%s: Fflush failed: %s\n"), tfnp,
98             Fstrerror(*tfdp));
99     }
100
101     rc = 0;
102
103 exit:
104     if (*sfdp)  (void) closeFile(sfdp);
105     if (*tfdp)  (void) closeFile(tfdp);
106     return rc;
107 }
108
109 /*
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.
114  */
115 static int putSignature(Header sigh, int ishdr, uint8_t *pkt, size_t pktlen)
116 {
117     pgpDigParams sigp = NULL;
118     rpmTagVal sigtag;
119     struct rpmtd_s sigtd;
120     int rc = 1; /* assume failure */
121     unsigned int hash_algo;
122     unsigned int pubkey_algo;
123
124     if (pgpPrtParams(pkt, pktlen, PGPTAG_SIGNATURE, &sigp)) {
125         rpmlog(RPMLOG_ERR, _("Unsupported PGP signature\n"));
126         goto exit;
127     }
128
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);
132         goto exit;
133     }
134
135     pubkey_algo = pgpDigParamsAlgo(sigp, PGPVAL_PUBKEYALGO);
136     switch (pubkey_algo) {
137     case PGPPUBKEYALGO_DSA:
138         sigtag = ishdr ? RPMSIGTAG_DSA : RPMSIGTAG_GPG;
139         break;
140     case PGPPUBKEYALGO_RSA:
141         sigtag = ishdr ? RPMSIGTAG_RSA : RPMSIGTAG_PGP;
142         break;
143     default:
144         rpmlog(RPMLOG_ERR, _("Unsupported PGP pubkey algorithm %u\n"),
145                 pubkey_algo);
146         goto exit;
147         break;
148     }
149
150     /* Looks sane, insert into header */
151     rpmtdReset(&sigtd);
152     sigtd.count = pktlen;
153     sigtd.data = pkt;
154     sigtd.type = RPM_BIN_TYPE;
155     sigtd.tag = sigtag;
156
157     /* Argh, reversed return codes */
158     rc = (headerPut(sigh, &sigtd, HEADERPUT_DEFAULT) == 0);
159
160 exit:
161     pgpDigParamsFree(sigp);
162     return rc;
163 }
164
165 static int runGPG(const char *file, const char *sigfile, const char * passPhrase)
166 {
167     int pid, status;
168     int inpipe[2];
169     FILE * fpipe;
170     int rc = 1; /* assume failure */
171
172     inpipe[0] = inpipe[1] = 0;
173     if (pipe(inpipe) < 0) {
174         rpmlog(RPMLOG_ERR, _("Couldn't create pipe for signing: %m"));
175         goto exit;
176     }
177
178     addMacro(NULL, "__plaintext_filename", NULL, file, -1);
179     addMacro(NULL, "__signature_filename", NULL, sigfile, -1);
180
181     if (!(pid = fork())) {
182         char *const *av;
183         char *cmd = NULL;
184         const char *gpg_path = rpmExpand("%{?_gpg_path}", NULL);
185
186         (void) dup2(inpipe[0], 3);
187         (void) close(inpipe[1]);
188
189         if (gpg_path && *gpg_path != '\0')
190             (void) setenv("GNUPGHOME", gpg_path, 1);
191         (void) setenv("LC_ALL", "C", 1);
192
193         unsetenv("MALLOC_CHECK_");
194         cmd = rpmExpand("%{?__gpg_sign_cmd}", NULL);
195         rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
196         if (!rc)
197             rc = execve(av[0], av+1, environ);
198
199         rpmlog(RPMLOG_ERR, _("Could not exec %s: %s\n"), "gpg",
200                         strerror(errno));
201         _exit(EXIT_FAILURE);
202     }
203
204     delMacro(NULL, "__plaintext_filename");
205     delMacro(NULL, "__signature_filename");
206
207     fpipe = fdopen(inpipe[1], "w");
208     (void) close(inpipe[0]);
209     if (fpipe) {
210         fprintf(fpipe, "%s\n", (passPhrase ? passPhrase : ""));
211         (void) fclose(fpipe);
212     }
213
214     (void) waitpid(pid, &status, 0);
215     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
216         rpmlog(RPMLOG_ERR, _("gpg exec failed (%d)\n"), WEXITSTATUS(status));
217     } else {
218         rc = 0;
219     }
220 exit:
221     return rc;
222 }
223
224 /**
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
231  */
232 static int makeGPGSignature(Header sigh, int ishdr,
233                             const char * file, const char * passPhrase)
234 {
235     char * sigfile = rstrscat(NULL, file, ".sig", NULL);
236     struct stat st;
237     uint8_t * pkt = NULL;
238     size_t pktlen = 0;
239     int rc = 1; /* assume failure */
240
241     if (runGPG(file, sigfile, passPhrase))
242         goto exit;
243
244     if (stat(sigfile, &st)) {
245         /* GPG failed to write signature */
246         rpmlog(RPMLOG_ERR, _("gpg failed to write signature\n"));
247         goto exit;
248     }
249
250     pktlen = st.st_size;
251     rpmlog(RPMLOG_DEBUG, "GPG sig size: %zd\n", pktlen);
252     pkt = xmalloc(pktlen);
253
254     {   FD_t fd;
255
256         rc = 0;
257         fd = Fopen(sigfile, "r.ufdio");
258         if (fd != NULL && !Ferror(fd)) {
259             rc = Fread(pkt, sizeof(*pkt), pktlen, fd);
260             (void) Fclose(fd);
261         }
262         if (rc != pktlen) {
263             rpmlog(RPMLOG_ERR, _("unable to read the signature\n"));
264             goto exit;
265         }
266     }
267
268     rpmlog(RPMLOG_DEBUG, "Got %zd bytes of GPG sig\n", pktlen);
269
270     /* Parse the signature, change signature tag as appropriate. */
271     rc = putSignature(sigh, ishdr, pkt, pktlen);
272 exit:
273     (void) unlink(sigfile);
274     free(sigfile);
275     free(pkt);
276
277     return rc;
278 }
279
280 /**
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
286  */
287 static int makeHDRSignature(Header sigh, const char * file,
288                 const char * passPhrase)
289 {
290     Header h = NULL;
291     FD_t fd = NULL;
292     char * fn = NULL;
293     int ret = -1;       /* assume failure. */
294
295     fd = Fopen(file, "r.fdio");
296     if (fd == NULL || Ferror(fd))
297         goto exit;
298     h = headerRead(fd, HEADER_MAGIC_YES);
299     if (h == NULL)
300         goto exit;
301     (void) Fclose(fd);
302
303     fd = rpmMkTempFile(NULL, &fn);
304     if (fd == NULL || Ferror(fd))
305         goto exit;
306     if (headerWrite(fd, h, HEADER_MAGIC_YES))
307         goto exit;
308
309     ret = makeGPGSignature(sigh, 1, fn, passPhrase);
310
311 exit:
312     if (fn) {
313         (void) unlink(fn);
314         free(fn);
315     }
316     headerFree(h);
317     if (fd != NULL) (void) Fclose(fd);
318     return ret;
319 }
320
321 static int rpmGenSignature(Header sigh, const char * file,
322                 const char * passPhrase)
323 {
324     int ret = -1;       /* assume failure. */
325
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);
329     }
330
331     return ret;
332 }
333
334 /**
335  * Retrieve signature from header tag
336  * @param sigh          signature header
337  * @param sigtag        signature tag
338  * @return              parsed pgp dig or NULL
339  */
340 static pgpDigParams getSig(Header sigh, rpmTagVal sigtag)
341 {
342     struct rpmtd_s pkt;
343     pgpDigParams sig = NULL;
344
345     if (headerGet(sigh, sigtag, &pkt, HEADERGET_DEFAULT) && pkt.data != NULL) {
346         pgpPrtParams(pkt.data, pkt.count, PGPTAG_SIGNATURE, &sig);
347         rpmtdFreeData(&pkt);
348     }
349     return sig;
350 }
351
352 static void deleteSigs(Header sigh)
353 {
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);
359 }
360
361 static int sameSignature(rpmTagVal sigtag, Header h1, Header h2)
362 {
363     pgpDigParams sig1 = getSig(h1, sigtag);
364     pgpDigParams sig2 = getSig(h2, sigtag);;
365
366     int rc = pgpDigParamsCmp(sig1, sig2);
367
368     pgpDigParamsFree(sig1);
369     pgpDigParamsFree(sig2);
370     return (rc == 0);
371 }
372
373 static int replaceSignature(Header sigh, const char *sigtarget,
374                             const char *passPhrase)
375 {
376     /* Grab a copy of the header so we can compare the result */
377     Header oldsigh = headerCopy(sigh);
378     int rc = -1;
379     
380     /* Nuke all signature tags */
381     deleteSigs(sigh);
382
383     /*
384      * rpmGenSignature() internals parse the actual signing result and 
385      * adds appropriate tags for DSA/RSA.
386      */
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;
391
392         rc = sameSignature(sigtag, sigh, oldsigh);
393
394     }
395
396     headerFree(oldsigh);
397     return rc;
398 }
399
400 /** \ingroup rpmcli
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
406  */
407 static int rpmSign(const char *rpm, int deleting, const char *passPhrase)
408 {
409     FD_t fd = NULL;
410     FD_t ofd = NULL;
411     rpmlead lead = NULL;
412     char *sigtarget = NULL, *trpm = NULL;
413     Header sigh = NULL;
414     char * msg = NULL;
415     int res = -1; /* assume failure */
416     rpmRC rc;
417     struct rpmtd_s utd;
418
419     fprintf(stdout, "%s:\n", rpm);
420
421     if (manageFile(&fd, rpm, O_RDONLY))
422         goto exit;
423
424     if ((rc = rpmLeadRead(fd, &lead, NULL, &msg)) != RPMRC_OK) {
425         rpmlog(RPMLOG_ERR, "%s: %s\n", rpm, msg);
426         free(msg);
427         goto exit;
428     }
429
430     rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
431     switch (rc) {
432     default:
433         rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), rpm,
434                     (msg && *msg ? msg : "\n"));
435         msg = _free(msg);
436         goto exit;
437         break;
438     case RPMRC_OK:
439         if (sigh == NULL) {
440             rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), rpm);
441             goto exit;
442         }
443         break;
444     }
445     msg = _free(msg);
446
447     ofd = rpmMkTempFile(NULL, &sigtarget);
448     if (ofd == NULL || Ferror(ofd)) {
449         rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
450         goto exit;
451     }
452     /* Write the header and archive to a temp file */
453     if (copyFile(&fd, rpm, &ofd, sigtarget))
454         goto exit;
455     /* Both fd and ofd are now closed. sigtarget contains tempfile name. */
456
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, &copytd)) {
464             if (copytd.data)
465                 headerPut(nh, &copytd, HEADERPUT_DEFAULT);
466             rpmtdFreeData(&copytd);
467         }
468         headerFreeIterator(hi);
469         headerFree(oh);
470
471         headerFree(sigh);
472         sigh = headerLink(nh);
473         headerFree(nh);
474     }
475
476     /* Eliminate broken digest values. */
477     headerDel(sigh, RPMSIGTAG_BADSHA1_1);
478     headerDel(sigh, RPMSIGTAG_BADSHA1_2);
479
480     /* Toss and recalculate header+payload size and digests. */
481     {
482         rpmTagVal const sigs[] = {      RPMSIGTAG_SIZE, 
483                                     RPMSIGTAG_MD5,
484                                     RPMSIGTAG_SHA1,
485                                  };
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]))
490                 goto exit;
491         }
492     }
493
494     if (deleting) {     /* Nuke all the signature tags. */
495         deleteSigs(sigh);
496     } else {
497         res = replaceSignature(sigh, sigtarget, passPhrase);
498         if (res != 0) {
499             if (res == 1) {
500                 rpmlog(RPMLOG_WARNING,
501                    _("%s already contains identical signature, skipping\n"),
502                    rpm);
503                 /* Identical signature is not an error */
504                 res = 0;
505             }
506             goto exit;
507         }
508     }
509
510     /* Reallocate the signature into one contiguous region. */
511     sigh = headerReload(sigh, RPMTAG_HEADERSIGNATURES);
512     if (sigh == NULL)   /* XXX can't happen */
513         goto exit;
514
515     rasprintf(&trpm, "%s.XXXXXX", rpm);
516     ofd = rpmMkTemp(trpm);
517     if (ofd == NULL || Ferror(ofd)) {
518         rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
519         goto exit;
520     }
521
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,
526             Fstrerror(ofd));
527         goto exit;
528     }
529
530     if (rpmWriteSignature(ofd, sigh)) {
531         rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), trpm,
532             Fstrerror(ofd));
533         goto exit;
534     }
535
536     /* Append the header and archive from the temp file */
537     if (copyFile(&fd, sigtarget, &ofd, trpm) == 0) {
538         struct stat st;
539
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) {
543             res = 0;
544         } else {
545             rpmlog(RPMLOG_ERR, _("replacing %s failed: %s\n"),
546                    rpm, strerror(errno));
547         }
548     }
549
550 exit:
551     if (fd)     (void) closeFile(&fd);
552     if (ofd)    (void) closeFile(&ofd);
553
554     rpmFreeSignature(sigh);
555     rpmLeadFree(lead);
556
557     /* Clean up intermediate target */
558     if (sigtarget) {
559         unlink(sigtarget);
560         free(sigtarget);
561     }
562     if (trpm) {
563         (void) unlink(trpm);
564         free(trpm);
565     }
566
567     return res;
568 }
569
570 int rpmPkgSign(const char *path,
571                 const struct rpmSignArgs * args, const char *passPhrase)
572 {
573     int rc;
574
575     if (args) {
576         if (args->hashalgo) {
577             char *algo = NULL;
578             rasprintf(&algo, "%d", args->hashalgo);
579             addMacro(NULL, "_gpg_digest_algo", NULL, algo, RMIL_GLOBAL);
580             free(algo);
581         }
582         if (args->keyid) {
583             addMacro(NULL, "_gpg_name", NULL, args->keyid, RMIL_GLOBAL);
584         }
585     }
586
587     rc = rpmSign(path, 0, passPhrase);
588
589     if (args) {
590         if (args->hashalgo) {
591             delMacro(NULL, "_gpg_digest_algo");
592         }
593         if (args->keyid) {
594             delMacro(NULL, "_gpg_name");
595         }
596     }
597
598     return rc;
599 }
600
601 int rpmPkgDelSign(const char *path)
602 {
603     return rpmSign(path, 1, NULL);
604 }