Sanitize python object -> tag number exception handling
[platform/upstream/rpm.git] / lib / signature.c
1 /** \ingroup signature
2  * \file lib/signature.c
3  */
4
5 #include "system.h"
6
7 #include <inttypes.h>
8 #include <popt.h>
9
10 #include <rpm/rpmtypes.h>
11 #include <rpm/rpmmacro.h>       /* XXX for rpmExpand() */
12 #include <rpm/rpmstring.h>
13 #include <rpm/rpmfileutil.h>
14 #include <rpm/rpmlog.h>
15 #include <rpm/rpmkeyring.h>
16
17 #include "rpmio/digest.h"
18 #include "lib/rpmlead.h"
19 #include "lib/signature.h"
20 #include "lib/header_internal.h"
21
22 #include "debug.h"
23
24 #if !defined(__GLIBC__) && !defined(__APPLE__)
25 char ** environ = NULL;
26 #endif
27
28 static int sighdrPut(Header h, rpmSigTag tag, rpmTagType type,
29                      rpm_data_t p, rpm_count_t c)
30 {
31     struct rpmtd_s sigtd;
32     rpmtdReset(&sigtd);
33     sigtd.tag = tag;
34     sigtd.type = type;
35     sigtd.data = p;
36     sigtd.count = c;
37     return headerPut(h, &sigtd, HEADERPUT_DEFAULT);
38 }
39
40 int rpmLookupSignatureType(int action)
41 {
42     static int disabled = 0;
43     int rc = 0;
44
45     switch (action) {
46     case RPMLOOKUPSIG_DISABLE:
47         disabled = -2;
48         break;
49     case RPMLOOKUPSIG_ENABLE:
50         disabled = 0;
51     case RPMLOOKUPSIG_QUERY:
52         if (disabled)
53             break;      /* Disabled */
54       { char *name = rpmExpand("%{?_signature}", NULL);
55         if (!(name && *name != '\0'))
56             rc = 0;
57         else if (!rstrcasecmp(name, "none"))
58             rc = 0;
59         else if (!rstrcasecmp(name, "pgp"))
60             rc = RPMSIGTAG_PGP;
61         else if (!rstrcasecmp(name, "pgp5"))    /* XXX legacy */
62             rc = RPMSIGTAG_PGP;
63         else if (!rstrcasecmp(name, "gpg"))
64             rc = RPMSIGTAG_GPG;
65         else
66             rc = -1;    /* Invalid %_signature spec in macro file */
67         name = _free(name);
68       } break;
69     }
70     return rc;
71 }
72
73 /**
74  * Print package size.
75  * @todo rpmio: use fdSize rather than fstat(2) to get file size.
76  * @param fd                    package file handle
77  * @param siglen                signature header size
78  * @param pad                   signature padding
79  * @param datalen               length of header+payload
80  * @return                      rpmRC return code
81  */
82 static inline rpmRC printSize(FD_t fd, size_t siglen, size_t pad, rpm_loff_t datalen)
83 {
84     struct stat st;
85     int fdno = Fileno(fd);
86
87     if (fstat(fdno, &st) < 0)
88         return RPMRC_FAIL;
89
90     rpmlog(RPMLOG_DEBUG,
91                 "Expected size: %12" PRIu64 \
92                 " = lead(%d)+sigs(%zd)+pad(%zd)+data(%" PRIu64 ")\n",
93                 RPMLEAD_SIZE+siglen+pad+datalen,
94                 RPMLEAD_SIZE, siglen, pad, datalen);
95     rpmlog(RPMLOG_DEBUG,
96                 "  Actual size: %12" PRIu64 "\n", (rpm_loff_t) st.st_size);
97
98     return RPMRC_OK;
99 }
100
101 rpmRC rpmReadSignature(FD_t fd, Header * sighp, sigType sig_type, char ** msg)
102 {
103     char *buf = NULL;
104     int32_t block[4];
105     int32_t il;
106     int32_t dl;
107     int32_t * ei = NULL;
108     entryInfo pe;
109     size_t nb;
110     int32_t ril = 0;
111     struct indexEntry_s entry;
112     struct entryInfo_s info;
113     unsigned char * dataStart;
114     unsigned char * dataEnd = NULL;
115     Header sigh = NULL;
116     rpmRC rc = RPMRC_FAIL;              /* assume failure */
117     int xx;
118     int i;
119
120     if (sighp)
121         *sighp = NULL;
122
123     if (sig_type != RPMSIGTYPE_HEADERSIG)
124         goto exit;
125
126     memset(block, 0, sizeof(block));
127     if ((xx = timedRead(fd, (void *)block, sizeof(block))) != sizeof(block)) {
128         rasprintf(&buf, _("sigh size(%d): BAD, read returned %d\n"), 
129                   (int)sizeof(block), xx);
130         goto exit;
131     }
132     if (memcmp(block, rpm_header_magic, sizeof(rpm_header_magic))) {
133         rasprintf(&buf, _("sigh magic: BAD\n"));
134         goto exit;
135     }
136     il = ntohl(block[2]);
137     if (il < 0 || il > 32) {
138         rasprintf(&buf, 
139                   _("sigh tags: BAD, no. of tags(%d) out of range\n"), il);
140         goto exit;
141     }
142     dl = ntohl(block[3]);
143     if (dl < 0 || dl > 8192) {
144         rasprintf(&buf, 
145                   _("sigh data: BAD, no. of  bytes(%d) out of range\n"), dl);
146         goto exit;
147     }
148
149     memset(&entry, 0, sizeof(entry));
150     memset(&info, 0, sizeof(info));
151
152     nb = (il * sizeof(struct entryInfo_s)) + dl;
153     ei = xmalloc(sizeof(il) + sizeof(dl) + nb);
154     ei[0] = block[2];
155     ei[1] = block[3];
156     pe = (entryInfo) &ei[2];
157     dataStart = (unsigned char *) (pe + il);
158     if ((xx = timedRead(fd, (void *)pe, nb)) != nb) {
159         rasprintf(&buf,
160                   _("sigh blob(%d): BAD, read returned %d\n"), (int)nb, xx);
161         goto exit;
162     }
163     
164     /* Check (and convert) the 1st tag element. */
165     xx = headerVerifyInfo(1, dl, pe, &entry.info, 0);
166     if (xx != -1) {
167         rasprintf(&buf, _("tag[%d]: BAD, tag %d type %d offset %d count %d\n"),
168                   0, entry.info.tag, entry.info.type,
169                   entry.info.offset, entry.info.count);
170         goto exit;
171     }
172
173     /* Is there an immutable header region tag? */
174     if (entry.info.tag == RPMTAG_HEADERSIGNATURES
175        && entry.info.type == RPM_BIN_TYPE
176        && entry.info.count == REGION_TAG_COUNT)
177     {
178
179         if (entry.info.offset >= dl) {
180             rasprintf(&buf, 
181                 _("region offset: BAD, tag %d type %d offset %d count %d\n"),
182                 entry.info.tag, entry.info.type,
183                 entry.info.offset, entry.info.count);
184             goto exit;
185         }
186
187         /* Is there an immutable header region tag trailer? */
188         dataEnd = dataStart + entry.info.offset;
189         (void) memcpy(&info, dataEnd, REGION_TAG_COUNT);
190         /* XXX Really old packages have HEADER_IMAGE, not HEADER_SIGNATURES. */
191         if (info.tag == htonl(RPMTAG_HEADERIMAGE)) {
192             rpmSigTag stag = htonl(RPMTAG_HEADERSIGNATURES);
193             info.tag = stag;
194             memcpy(dataEnd, &stag, sizeof(stag));
195         }
196         dataEnd += REGION_TAG_COUNT;
197
198         xx = headerVerifyInfo(1, dl, &info, &entry.info, 1);
199         if (xx != -1 ||
200             !((entry.info.tag == RPMTAG_HEADERSIGNATURES || entry.info.tag == RPMTAG_HEADERIMAGE)
201            && entry.info.type == RPM_BIN_TYPE
202            && entry.info.count == REGION_TAG_COUNT))
203         {
204             rasprintf(&buf,
205                 _("region trailer: BAD, tag %d type %d offset %d count %d\n"),
206                 entry.info.tag, entry.info.type,
207                 entry.info.offset, entry.info.count);
208             goto exit;
209         }
210         memset(&info, 0, sizeof(info));
211
212         /* Is the no. of tags in the region less than the total no. of tags? */
213         ril = entry.info.offset/sizeof(*pe);
214         if ((entry.info.offset % sizeof(*pe)) || ril > il) {
215             rasprintf(&buf, _("region size: BAD, ril(%d) > il(%d)\n"), ril, il);
216             goto exit;
217         }
218     }
219
220     /* Sanity check signature tags */
221     memset(&info, 0, sizeof(info));
222     for (i = 1; i < il; i++) {
223         xx = headerVerifyInfo(1, dl, pe+i, &entry.info, 0);
224         if (xx != -1) {
225             rasprintf(&buf, 
226                 _("sigh tag[%d]: BAD, tag %d type %d offset %d count %d\n"),
227                 i, entry.info.tag, entry.info.type,
228                 entry.info.offset, entry.info.count);
229             goto exit;
230         }
231     }
232
233     /* OK, blob looks sane, load the header. */
234     sigh = headerLoad(ei);
235     if (sigh == NULL) {
236         rasprintf(&buf, _("sigh load: BAD\n"));
237         goto exit;
238     }
239
240     {   size_t sigSize = headerSizeof(sigh, HEADER_MAGIC_YES);
241         size_t pad = (8 - (sigSize % 8)) % 8; /* 8-byte pad */
242         ssize_t trc;
243         struct rpmtd_s sizetag;
244         rpm_loff_t archSize = 0;
245
246         /* Position at beginning of header. */
247         if (pad && (trc = timedRead(fd, (void *)block, pad)) != pad) {
248             rasprintf(&buf,
249                       _("sigh pad(%zd): BAD, read %zd bytes\n"), pad, trc);
250             goto exit;
251         }
252
253         /* Print package component sizes. */
254         if (headerGet(sigh, RPMSIGTAG_LONGSIZE, &sizetag, HEADERGET_DEFAULT)) {
255             rpm_loff_t *tsize = rpmtdGetUint64(&sizetag);
256             archSize = (tsize) ? *tsize : 0;
257         } else if (headerGet(sigh, RPMSIGTAG_SIZE, &sizetag, HEADERGET_DEFAULT)) {
258             rpm_off_t *tsize = rpmtdGetUint32(&sizetag);
259             archSize = (tsize) ? *tsize : 0;
260         }
261         rpmtdFreeData(&sizetag);
262         rc = printSize(fd, sigSize, pad, archSize);
263         if (rc != RPMRC_OK) {
264             rasprintf(&buf,
265                    _("sigh sigSize(%zd): BAD, fstat(2) failed\n"), sigSize);
266             goto exit;
267         }
268     }
269     ei = NULL; /* XXX will be freed with header */
270
271 exit:
272     if (sighp && sigh && rc == RPMRC_OK)
273         *sighp = headerLink(sigh);
274     sigh = headerFree(sigh);
275     free(ei);
276
277     if (msg != NULL) {
278         *msg = buf;
279     } else {
280         free(buf);
281     }
282
283     return rc;
284 }
285
286 int rpmWriteSignature(FD_t fd, Header sigh)
287 {
288     static uint8_t buf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
289     int sigSize, pad;
290     int rc;
291
292     rc = headerWrite(fd, sigh, HEADER_MAGIC_YES);
293     if (rc)
294         return rc;
295
296     sigSize = headerSizeof(sigh, HEADER_MAGIC_YES);
297     pad = (8 - (sigSize % 8)) % 8;
298     if (pad) {
299         if (Fwrite(buf, sizeof(buf[0]), pad, fd) != pad)
300             rc = 1;
301     }
302     rpmlog(RPMLOG_DEBUG, "Signature: size(%d)+pad(%d)\n", sigSize, pad);
303     return rc;
304 }
305
306 Header rpmNewSignature(void)
307 {
308     Header sigh = headerNew();
309     return sigh;
310 }
311
312 Header rpmFreeSignature(Header sigh)
313 {
314     return headerFree(sigh);
315 }
316
317 /**
318  * Generate GPG signature(s) for a header+payload file.
319  * @param file          header+payload file name
320  * @retval *sigTagp     signature tag
321  * @retval *pktp        signature packet(s)
322  * @retval *pktlenp     signature packet(s) length
323  * @param passPhrase    private key pass phrase
324  * @return              0 on success, 1 on failure
325  */
326 static int makeGPGSignature(const char * file, rpmSigTag * sigTagp,
327                 uint8_t ** pktp, size_t * pktlenp,
328                 const char * passPhrase)
329 {
330     char * sigfile = NULL;
331     int pid, status;
332     int inpipe[2];
333     FILE * fpipe;
334     struct stat st;
335     const char * cmd;
336     char *const *av;
337     pgpDig dig = NULL;
338     pgpDigParams sigp = NULL;
339     int rc = 1; /* assume failure */
340
341     rasprintf(&sigfile, "%s.sig", file);
342
343     addMacro(NULL, "__plaintext_filename", NULL, file, -1);
344     addMacro(NULL, "__signature_filename", NULL, sigfile, -1);
345
346     inpipe[0] = inpipe[1] = 0;
347     if (pipe(inpipe) < 0) {
348         rpmlog(RPMLOG_ERR, _("Couldn't create pipe for signing: %m"));
349         goto exit;
350     }
351
352     if (!(pid = fork())) {
353         const char *gpg_path = rpmExpand("%{?_gpg_path}", NULL);
354
355         (void) dup2(inpipe[0], 3);
356         (void) close(inpipe[1]);
357
358         if (gpg_path && *gpg_path != '\0')
359             (void) setenv("GNUPGHOME", gpg_path, 1);
360         (void) setenv("LC_ALL", "C", 1);
361
362         unsetenv("MALLOC_CHECK_");
363         cmd = rpmExpand("%{?__gpg_sign_cmd}", NULL);
364         rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
365         if (!rc)
366             rc = execve(av[0], av+1, environ);
367
368         rpmlog(RPMLOG_ERR, _("Could not exec %s: %s\n"), "gpg",
369                         strerror(errno));
370         _exit(EXIT_FAILURE);
371     }
372
373     delMacro(NULL, "__plaintext_filename");
374     delMacro(NULL, "__signature_filename");
375
376     fpipe = fdopen(inpipe[1], "w");
377     (void) close(inpipe[0]);
378     if (fpipe) {
379         fprintf(fpipe, "%s\n", (passPhrase ? passPhrase : ""));
380         (void) fclose(fpipe);
381     }
382
383     (void) waitpid(pid, &status, 0);
384     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
385         rpmlog(RPMLOG_ERR, _("gpg exec failed (%d)\n"), WEXITSTATUS(status));
386         goto exit;
387     }
388
389     if (stat(sigfile, &st)) {
390         /* GPG failed to write signature */
391         rpmlog(RPMLOG_ERR, _("gpg failed to write signature\n"));
392         goto exit;
393     }
394
395     *pktlenp = st.st_size;
396     rpmlog(RPMLOG_DEBUG, "GPG sig size: %zd\n", *pktlenp);
397     *pktp = xmalloc(*pktlenp);
398
399     {   FD_t fd;
400
401         rc = 0;
402         fd = Fopen(sigfile, "r.ufdio");
403         if (fd != NULL && !Ferror(fd)) {
404             rc = Fread(*pktp, sizeof(**pktp), *pktlenp, fd);
405             (void) Fclose(fd);
406         }
407         if (rc != *pktlenp) {
408             *pktp = _free(*pktp);
409             rpmlog(RPMLOG_ERR, _("unable to read the signature\n"));
410             goto exit;
411         }
412     }
413
414     rpmlog(RPMLOG_DEBUG, "Got %zd bytes of GPG sig\n", *pktlenp);
415
416     /* Parse the signature, change signature tag as appropriate. */
417     dig = pgpNewDig();
418
419     (void) pgpPrtPkts(*pktp, *pktlenp, dig, 0);
420     sigp = &dig->signature;
421
422     switch (*sigTagp) {
423     case RPMSIGTAG_GPG:
424         /* XXX check MD5 hash too? */
425         if (sigp->pubkey_algo == PGPPUBKEYALGO_RSA)
426             *sigTagp = RPMSIGTAG_PGP;
427         break;
428     case RPMSIGTAG_PGP5:        /* XXX legacy */
429     case RPMSIGTAG_PGP:
430         if (sigp->pubkey_algo == PGPPUBKEYALGO_DSA)
431             *sigTagp = RPMSIGTAG_GPG;
432         break;
433     case RPMSIGTAG_DSA:
434         /* XXX check MD5 hash too? */
435         if (sigp->pubkey_algo == PGPPUBKEYALGO_RSA)
436             *sigTagp = RPMSIGTAG_RSA;
437         break;
438     case RPMSIGTAG_RSA:
439         if (sigp->pubkey_algo == PGPPUBKEYALGO_DSA)
440             *sigTagp = RPMSIGTAG_DSA;
441         break;
442     default:
443         break;
444     }
445
446     dig = pgpFreeDig(dig);
447     rc = 0;
448
449 exit:
450     (void) unlink(sigfile);
451     free(sigfile);
452
453     return rc;
454 }
455
456 /**
457  * Generate header only signature(s) from a header+payload file.
458  * @param sigh          signature header
459  * @param file          header+payload file name
460  * @param sigTag        type of signature(s) to add
461  * @param passPhrase    private key pass phrase
462  * @return              0 on success, -1 on failure
463  */
464 static int makeHDRSignature(Header sigh, const char * file, rpmSigTag sigTag,
465                 const char * passPhrase)
466 {
467     Header h = NULL;
468     FD_t fd = NULL;
469     uint8_t * pkt = NULL;
470     size_t pktlen;
471     char * fn = NULL;
472     char * SHA1 = NULL;
473     int ret = -1;       /* assume failure. */
474
475     switch (sigTag) {
476     case RPMSIGTAG_SHA1:
477         fd = Fopen(file, "r.fdio");
478         if (fd == NULL || Ferror(fd))
479             goto exit;
480         h = headerRead(fd, HEADER_MAGIC_YES);
481         if (h == NULL)
482             goto exit;
483         (void) Fclose(fd);      fd = NULL;
484
485         if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
486             DIGEST_CTX ctx;
487             struct rpmtd_s utd;
488         
489             if (!headerGet(h, RPMTAG_HEADERIMMUTABLE, &utd, HEADERGET_DEFAULT)
490                 ||  utd.data == NULL)
491             {
492                 rpmlog(RPMLOG_ERR, 
493                                 _("Immutable header region could not be read. "
494                                 "Corrupted package?\n"));
495                 h = headerFree(h);
496                 goto exit;
497             }
498             ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
499             (void) rpmDigestUpdate(ctx, rpm_header_magic, sizeof(rpm_header_magic));
500             (void) rpmDigestUpdate(ctx, utd.data, utd.count);
501             (void) rpmDigestFinal(ctx, (void **)&SHA1, NULL, 1);
502             rpmtdFreeData(&utd);
503         }
504         h = headerFree(h);
505
506         if (SHA1 == NULL)
507             goto exit;
508         if (!sighdrPut(sigh, RPMSIGTAG_SHA1, RPM_STRING_TYPE, SHA1, 1))
509             goto exit;
510         ret = 0;
511         break;
512     case RPMSIGTAG_DSA:
513     case RPMSIGTAG_RSA:
514         fd = Fopen(file, "r.fdio");
515         if (fd == NULL || Ferror(fd))
516             goto exit;
517         h = headerRead(fd, HEADER_MAGIC_YES);
518         if (h == NULL)
519             goto exit;
520         (void) Fclose(fd);
521         fd = rpmMkTempFile(NULL, &fn);
522         if (fd == NULL || Ferror(fd))
523             goto exit;
524         if (headerWrite(fd, h, HEADER_MAGIC_YES))
525             goto exit;
526         (void) Fclose(fd);      fd = NULL;
527         if (makeGPGSignature(fn, &sigTag, &pkt, &pktlen, passPhrase)
528          || !sighdrPut(sigh, sigTag, RPM_BIN_TYPE, pkt, pktlen))
529             goto exit;
530         ret = 0;
531         break;
532     default:
533         goto exit;
534         break;
535     }
536
537 exit:
538     if (fn) {
539         (void) unlink(fn);
540         free(fn);
541     }
542     free(pkt);
543     free(SHA1);
544     h = headerFree(h);
545     if (fd != NULL) (void) Fclose(fd);
546     return ret;
547 }
548
549 int rpmAddSignature(Header sigh, const char * file, rpmSigTag sigTag,
550                 const char * passPhrase)
551 {
552     struct stat st;
553     uint8_t * pkt = NULL;
554     size_t pktlen;
555     int ret = -1;       /* assume failure. */
556
557     switch (sigTag) {
558     case RPMSIGTAG_SIZE: {
559         rpm_off_t size;
560         if (stat(file, &st) != 0)
561             break;
562         size = st.st_size;
563         if (!sighdrPut(sigh, sigTag, RPM_INT32_TYPE, &size, 1))
564             break;
565         ret = 0;
566         } break;
567     case RPMSIGTAG_LONGSIZE: {
568         rpm_loff_t size;
569         if (stat(file, &st) != 0)
570             break;
571         size = st.st_size;
572         if (!sighdrPut(sigh, sigTag, RPM_INT64_TYPE, &size, 1))
573             break;
574         ret = 0;
575         } break;
576     case RPMSIGTAG_MD5:
577         pktlen = 16;
578         pkt = xcalloc(pktlen, sizeof(*pkt));
579         if (rpmDoDigest(PGPHASHALGO_MD5, file, 0, pkt, NULL)
580          || !sighdrPut(sigh, sigTag, RPM_BIN_TYPE, pkt, pktlen))
581             break;
582         ret = 0;
583         break;
584     case RPMSIGTAG_PGP5:        /* XXX legacy */
585     case RPMSIGTAG_PGP:
586     case RPMSIGTAG_GPG: {
587         rpmSigTag hdrtag = (sigTag == RPMSIGTAG_GPG) ?
588                             RPMSIGTAG_DSA : RPMSIGTAG_RSA;
589         if (makeGPGSignature(file, &sigTag, &pkt, &pktlen, passPhrase)
590          || !sighdrPut(sigh, sigTag, RPM_BIN_TYPE, pkt, pktlen))
591             break;
592         /* XXX Piggyback a header-only DSA/RSA signature as well. */
593         ret = makeHDRSignature(sigh, file, hdrtag, passPhrase);
594         } break;
595     case RPMSIGTAG_RSA:
596     case RPMSIGTAG_DSA:
597     case RPMSIGTAG_SHA1:
598         ret = makeHDRSignature(sigh, file, sigTag, passPhrase);
599         break;
600     default:
601         break;
602     }
603     free(pkt);
604
605     return ret;
606 }
607
608 static int checkPassPhrase(const char * passPhrase, const rpmSigTag sigTag)
609 {
610     int passPhrasePipe[2];
611     int pid, status;
612     int rc;
613     int xx;
614
615     passPhrasePipe[0] = passPhrasePipe[1] = 0;
616     xx = pipe(passPhrasePipe);
617     if (!(pid = fork())) {
618         const char * cmd;
619         char *const *av;
620         int fdno;
621
622         xx = close(STDIN_FILENO);
623         xx = close(STDOUT_FILENO);
624         xx = close(passPhrasePipe[1]);
625         if (! rpmIsVerbose())
626             xx = close(STDERR_FILENO);
627         if ((fdno = open("/dev/null", O_RDONLY)) != STDIN_FILENO) {
628             xx = dup2(fdno, STDIN_FILENO);
629             xx = close(fdno);
630         }
631         if ((fdno = open("/dev/null", O_WRONLY)) != STDOUT_FILENO) {
632             xx = dup2(fdno, STDOUT_FILENO);
633             xx = close(fdno);
634         }
635         xx = dup2(passPhrasePipe[0], 3);
636
637         unsetenv("MALLOC_CHECK_");
638         switch (sigTag) {
639         case RPMSIGTAG_RSA:
640         case RPMSIGTAG_PGP5:    /* XXX legacy */
641         case RPMSIGTAG_PGP:
642         case RPMSIGTAG_DSA:
643         case RPMSIGTAG_GPG:
644         {   const char *gpg_path = rpmExpand("%{?_gpg_path}", NULL);
645
646             if (gpg_path && *gpg_path != '\0')
647                 (void) setenv("GNUPGHOME", gpg_path, 1);
648
649             cmd = rpmExpand("%{?__gpg_check_password_cmd}", NULL);
650             rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
651             if (!rc)
652                 rc = execve(av[0], av+1, environ);
653
654             rpmlog(RPMLOG_ERR, _("Could not exec %s: %s\n"), "gpg",
655                         strerror(errno));
656         }   break;
657         default: /* This case should have been screened out long ago. */
658             rpmlog(RPMLOG_ERR, _("Invalid %%_signature spec in macro file\n"));
659             _exit(EXIT_FAILURE);
660             break;
661         }
662     }
663
664     xx = close(passPhrasePipe[0]);
665     xx = write(passPhrasePipe[1], passPhrase, strlen(passPhrase));
666     xx = write(passPhrasePipe[1], "\n", 1);
667     xx = close(passPhrasePipe[1]);
668
669     (void) waitpid(pid, &status, 0);
670
671     return ((!WIFEXITED(status) || WEXITSTATUS(status)) ? 1 : 0);
672 }
673
674 char * rpmGetPassPhrase(const char * prompt, const rpmSigTag sigTag)
675 {
676     char *pass = NULL;
677     int aok = 0;
678
679     switch (sigTag) {
680     case RPMSIGTAG_RSA:
681     case RPMSIGTAG_PGP5:        /* XXX legacy */
682     case RPMSIGTAG_PGP:
683     case RPMSIGTAG_DSA:
684     case RPMSIGTAG_GPG:
685       { char *name = rpmExpand("%{?_gpg_name}", NULL);
686         aok = (name && *name != '\0');
687         name = _free(name);
688       }
689         if (aok)
690             break;
691         rpmlog(RPMLOG_ERR,
692                 _("You must set \"%%_gpg_name\" in your macro file\n"));
693         break;
694     default:
695         /* Currently the calling function (rpm.c:main) is checking this and
696          * doing a better job.  This section should never be accessed.
697          */
698         rpmlog(RPMLOG_ERR, _("Invalid %%_signature spec in macro file\n"));
699         break;
700     }
701
702     if (aok) {
703         pass = getpass( (prompt ? prompt : "") );
704
705         if (checkPassPhrase(pass, sigTag))
706             pass = NULL;
707     }
708
709     return pass;
710 }
711
712 static const char * rpmSigString(rpmRC res)
713 {
714     const char * str;
715     switch (res) {
716     case RPMRC_OK:              str = "OK";             break;
717     case RPMRC_FAIL:            str = "BAD";            break;
718     case RPMRC_NOKEY:           str = "NOKEY";          break;
719     case RPMRC_NOTTRUSTED:      str = "NOTRUSTED";      break;
720     default:
721     case RPMRC_NOTFOUND:        str = "UNKNOWN";        break;
722     }
723     return str;
724 }
725
726 static rpmRC
727 verifyMD5Digest(rpmtd sigtd, DIGEST_CTX md5ctx, char **msg)
728 {
729     rpmRC res = RPMRC_FAIL; /* assume failure */
730     uint8_t * md5sum = NULL;
731     size_t md5len = 0;
732     char *md5;
733     const char *title = _("MD5 digest:");
734     *msg = NULL;
735     DIGEST_CTX ctx = rpmDigestDup(md5ctx);
736
737     if (ctx == NULL || sigtd->data == NULL) {
738         rasprintf(msg, "%s %s\n", title, rpmSigString(res));
739         goto exit;
740     }
741
742     (void) rpmDigestFinal(ctx, (void **)&md5sum, &md5len, 0);
743
744     md5 = pgpHexStr(md5sum, md5len);
745     if (md5len != sigtd->count || memcmp(md5sum, sigtd->data, md5len)) {
746         char *hex = rpmtdFormat(sigtd, RPMTD_FORMAT_STRING, NULL);
747         rasprintf(msg, "%s %s Expected(%s) != (%s)\n", title,
748                   rpmSigString(res), hex, md5);
749         free(hex);
750     } else {
751         res = RPMRC_OK;
752         rasprintf(msg, "%s %s (%s)\n", title, rpmSigString(res), md5);
753     }
754     free(md5);
755
756 exit:
757     md5sum = _free(md5sum);
758     return res;
759 }
760
761 /**
762  * Verify header immutable region SHA1 digest.
763  * @retval msg          verbose success/failure text
764  * @param sha1ctx
765  * @return              RPMRC_OK on success
766  */
767 static rpmRC
768 verifySHA1Digest(rpmtd sigtd, DIGEST_CTX sha1ctx, char **msg)
769 {
770     rpmRC res = RPMRC_FAIL; /* assume failure */
771     char * SHA1 = NULL;
772     const char *title = _("Header SHA1 digest:");
773     const char *sig = sigtd->data;
774     *msg = NULL;
775     DIGEST_CTX ctx = rpmDigestDup(sha1ctx);
776
777     if (ctx == NULL || sigtd->data == NULL) {
778         rasprintf(msg, "%s %s\n", title, rpmSigString(res));
779         goto exit;
780     }
781
782     (void) rpmDigestFinal(ctx, (void **)&SHA1, NULL, 1);
783
784     if (SHA1 == NULL || strlen(SHA1) != strlen(sig) || !rstreq(SHA1, sig)) {
785         rasprintf(msg, "%s %s Expected(%s) != (%s)\n", title,
786                   rpmSigString(res), sig, SHA1 ? SHA1 : "(nil)");
787     } else {
788         res = RPMRC_OK;
789         rasprintf(msg, "%s %s (%s)\n", title, rpmSigString(res), SHA1);
790     }
791
792 exit:
793     SHA1 = _free(SHA1);
794     return res;
795 }
796
797 /**
798  * Verify DSA/RSA signature.
799  * @param keyring       pubkey keyring
800  * @param dig           OpenPGP container
801  * @param hashctx       digest context
802  * @param isHdr         header-only signature?
803  * @retval msg          verbose success/failure text
804  * @return              RPMRC_OK on success
805  */
806 static rpmRC
807 verifySignature(rpmKeyring keyring, pgpDig dig, DIGEST_CTX hashctx, int isHdr, 
808                 char **msg)
809 {
810     pgpDigParams sigp = dig ? &dig->signature : NULL;
811     rpmRC res = RPMRC_FAIL; /* assume failure */
812     char *sigid = NULL;
813     *msg = NULL;
814
815     if (hashctx == NULL) {
816         goto exit;
817     }
818
819     /* Retrieve the matching public key and verify. */
820     res = rpmKeyringLookup(keyring, dig);
821     if (res == RPMRC_OK) {
822         res = pgpVerifySig(dig, hashctx);
823     }
824
825 exit:
826     sigid = pgpIdentItem(sigp);
827     rasprintf(msg, "%s%s: %s\n", isHdr ? _("Header ") : "", sigid, 
828                 rpmSigString(res));
829     free(sigid);
830     return res;
831 }
832
833 rpmRC
834 rpmVerifySignature(rpmKeyring keyring, rpmtd sigtd, pgpDig dig, DIGEST_CTX ctx, char ** result)
835 {
836     rpmRC res = RPMRC_NOTFOUND;
837     char *msg = NULL;
838
839     if (sigtd->data == NULL || sigtd->count <= 0 || dig == NULL) {
840         rasprintf(&msg, _("Verify signature: BAD PARAMETERS\n"));
841         goto exit;
842     }
843
844     switch (sigtd->tag) {
845     case RPMSIGTAG_MD5:
846         res = verifyMD5Digest(sigtd, ctx, &msg);
847         break;
848     case RPMSIGTAG_SHA1:
849         res = verifySHA1Digest(sigtd, ctx, &msg);
850         break;
851     case RPMSIGTAG_RSA:
852     case RPMSIGTAG_DSA:
853         res = verifySignature(keyring, dig, ctx, 1, &msg);
854         break;
855     case RPMSIGTAG_PGP5:        /* XXX legacy */
856     case RPMSIGTAG_PGP:
857     case RPMSIGTAG_GPG:
858         res = verifySignature(keyring, dig, ctx, 0, &msg);
859         break;
860     default:
861         rasprintf(&msg, _("Signature: UNKNOWN (%d)\n"), sigtd->tag);
862         break;
863     }
864
865 exit:
866     if (result) {
867         *result = msg;
868     } else {
869         free(msg);
870     }
871     return res;
872 }