5fa669089ca5a46b516c7b1247365196701b208b
[platform/upstream/rpm.git] / lib / signature.c
1 /** \ingroup signature
2  * \file lib/signature.c
3  */
4
5 #include "system.h"
6
7 #include "rpmio_internal.h"
8 #include <rpmlib.h>
9 #include <rpmmacro.h>   /* XXX for rpmGetPath() */
10 #include "rpmdb.h"
11
12 #define _RPMTS_INTERNAL
13 #include "rpmts.h"
14
15 #include "misc.h"       /* XXX for dosetenv() and makeTempFile() */
16 #include "legacy.h"     /* XXX for mdbinfile() */
17 #include "rpmlead.h"
18 #include "signature.h"
19 #include "header_internal.h"
20 #include "debug.h"
21
22 /*@access FD_t@*/               /* XXX ufdio->read arg1 is void ptr */
23 /*@access Header@*/             /* XXX compared with NULL */
24 /*@access entryInfo @*/         /* XXX rpmReadSignature */
25 /*@access indexEntry @*/        /* XXX rpmReadSignature */
26 /*@access DIGEST_CTX@*/         /* XXX compared with NULL */
27 /*@access pgpDig@*/
28 /*@access pgpDigParams@*/
29
30 #if !defined(__GLIBC__)
31 char ** environ = NULL;
32 #endif
33
34 int rpmLookupSignatureType(int action)
35 {
36     /*@unchecked@*/
37     static int disabled = 0;
38     int rc = 0;
39
40     switch (action) {
41     case RPMLOOKUPSIG_DISABLE:
42         disabled = -2;
43         break;
44     case RPMLOOKUPSIG_ENABLE:
45         disabled = 0;
46         /*@fallthrough@*/
47     case RPMLOOKUPSIG_QUERY:
48         if (disabled)
49             break;      /* Disabled */
50 /*@-boundsread@*/
51       { const char *name = rpmExpand("%{?_signature}", NULL);
52         if (!(name && *name != '\0'))
53             rc = 0;
54         else if (!xstrcasecmp(name, "none"))
55             rc = 0;
56         else if (!xstrcasecmp(name, "pgp"))
57             rc = RPMSIGTAG_PGP;
58         else if (!xstrcasecmp(name, "pgp5"))    /* XXX legacy */
59             rc = RPMSIGTAG_PGP;
60         else if (!xstrcasecmp(name, "gpg"))
61             rc = RPMSIGTAG_GPG;
62         else
63             rc = -1;    /* Invalid %_signature spec in macro file */
64         name = _free(name);
65       } break;
66 /*@=boundsread@*/
67     }
68     return rc;
69 }
70
71 /* rpmDetectPGPVersion() returns the absolute path to the "pgp"  */
72 /* executable of the requested version, or NULL when none found. */
73
74 const char * rpmDetectPGPVersion(pgpVersion * pgpVer)
75 {
76     /* Actually this should support having more then one pgp version. */
77     /* At the moment only one version is possible since we only       */
78     /* have one %_pgpbin and one %_pgp_path.                          */
79
80     static pgpVersion saved_pgp_version = PGP_UNKNOWN;
81     const char *pgpbin = rpmGetPath("%{?_pgpbin}", NULL);
82
83     if (saved_pgp_version == PGP_UNKNOWN) {
84         char *pgpvbin;
85         struct stat st;
86
87 /*@-boundsread@*/
88         if (!(pgpbin && pgpbin[0] != '\0')) {
89             pgpbin = _free(pgpbin);
90             saved_pgp_version = -1;
91             return NULL;
92         }
93 /*@=boundsread@*/
94 /*@-boundswrite@*/
95         pgpvbin = (char *)alloca(strlen(pgpbin) + sizeof("v"));
96         (void)stpcpy(stpcpy(pgpvbin, pgpbin), "v");
97 /*@=boundswrite@*/
98
99         if (stat(pgpvbin, &st) == 0)
100             saved_pgp_version = PGP_5;
101         else if (stat(pgpbin, &st) == 0)
102             saved_pgp_version = PGP_2;
103         else
104             saved_pgp_version = PGP_NOTDETECTED;
105     }
106
107 /*@-boundswrite@*/
108     if (pgpVer && pgpbin)
109         *pgpVer = saved_pgp_version;
110 /*@=boundswrite@*/
111     return pgpbin;
112 }
113
114 /**
115  * Print package size.
116  * @todo rpmio: use fdSize rather than fstat(2) to get file size.
117  * @param fd                    package file handle
118  * @param siglen                signature header size
119  * @param pad                   signature padding
120  * @param datalen               length of header+payload
121  * @return                      rpmRC return code
122  */
123 static inline rpmRC printSize(FD_t fd, int siglen, int pad, int datalen)
124         /*@globals fileSystem @*/
125         /*@modifies fileSystem @*/
126 {
127     struct stat st;
128
129     if (fstat(Fileno(fd), &st) < 0)
130         return RPMRC_FAIL;
131
132     /*@-sizeoftype@*/
133     rpmMessage(RPMMESS_DEBUG,
134         _("Expected size: %12d = lead(%d)+sigs(%d)+pad(%d)+data(%d)\n"),
135                 (int)sizeof(struct rpmlead)+siglen+pad+datalen,
136                 (int)sizeof(struct rpmlead), siglen, pad, datalen);
137     /*@=sizeoftype@*/
138     rpmMessage(RPMMESS_DEBUG,
139         _("  Actual size: %12d\n"), (int)st.st_size);
140
141     return RPMRC_OK;
142 }
143
144 /*@unchecked@*/
145 static unsigned char header_magic[8] = {
146     0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
147 };
148
149 rpmRC rpmReadSignature(FD_t fd, Header * sighp, sigType sig_type,
150                 const char ** msg)
151 {
152     char buf[BUFSIZ];
153     int_32 block[4];
154     int_32 il;
155     int_32 dl;
156     int_32 * ei = NULL;
157     entryInfo pe;
158     size_t nb;
159     int_32 ril = 0;
160     indexEntry entry = memset(alloca(sizeof(*entry)), 0, sizeof(*entry));
161     entryInfo info = memset(alloca(sizeof(*info)), 0, sizeof(*info));
162     unsigned char * dataStart;
163     unsigned char * dataEnd = NULL;
164     Header sigh = NULL;
165     rpmRC rc = RPMRC_FAIL;              /* assume failure */
166     int xx;
167     int i;
168
169 /*@-boundswrite@*/
170     if (sighp)
171         *sighp = NULL;
172
173     buf[0] = '\0';
174 /*@=boundswrite@*/
175
176     if (sig_type != RPMSIGTYPE_HEADERSIG)
177         goto exit;
178
179     memset(block, 0, sizeof(block));
180     if ((xx = timedRead(fd, (char *)block, sizeof(block))) != sizeof(block)) {
181         (void) snprintf(buf, sizeof(buf),
182                 _("sigh size(%d): BAD, read returned %d\n"), (int)sizeof(block), xx);
183         goto exit;
184     }
185     if (memcmp(block, header_magic, sizeof(header_magic))) {
186         (void) snprintf(buf, sizeof(buf),
187                 _("sigh magic: BAD\n"));
188         goto exit;
189     }
190 /*@-boundsread@*/
191     il = ntohl(block[2]);
192 /*@=boundsread@*/
193     if (il < 0 || il > 32) {
194         (void) snprintf(buf, sizeof(buf),
195                 _("sigh tags: BAD, no. of tags(%d) out of range\n"), il);
196         goto exit;
197     }
198 /*@-boundsread@*/
199     dl = ntohl(block[3]);
200 /*@=boundsread@*/
201     if (dl < 0 || dl > 8192) {
202         (void) snprintf(buf, sizeof(buf),
203                 _("sigh data: BAD, no. of  bytes(%d) out of range\n"), dl);
204         goto exit;
205     }
206
207 /*@-sizeoftype@*/
208     nb = (il * sizeof(struct entryInfo_s)) + dl;
209 /*@=sizeoftype@*/
210     ei = xmalloc(sizeof(il) + sizeof(dl) + nb);
211 /*@-bounds@*/
212     ei[0] = block[2];
213     ei[1] = block[3];
214     pe = (entryInfo) &ei[2];
215 /*@=bounds@*/
216     dataStart = (unsigned char *) (pe + il);
217     if ((xx = timedRead(fd, (char *)pe, nb)) != nb) {
218         (void) snprintf(buf, sizeof(buf),
219                 _("sigh blob(%d): BAD, read returned %d\n"), (int)nb, xx);
220         goto exit;
221     }
222     
223     /* Check (and convert) the 1st tag element. */
224     xx = headerVerifyInfo(1, dl, pe, &entry->info, 0);
225     if (xx != -1) {
226         (void) snprintf(buf, sizeof(buf),
227                 _("tag[%d]: BAD, tag %d type %d offset %d count %d\n"),
228                 0, entry->info.tag, entry->info.type,
229                 entry->info.offset, entry->info.count);
230         goto exit;
231     }
232
233     /* Is there an immutable header region tag? */
234 /*@-sizeoftype@*/
235     if (entry->info.tag == RPMTAG_HEADERSIGNATURES
236        && entry->info.type == RPM_BIN_TYPE
237        && entry->info.count == REGION_TAG_COUNT)
238     {
239 /*@=sizeoftype@*/
240
241         if (entry->info.offset >= dl) {
242             (void) snprintf(buf, sizeof(buf),
243                 _("region offset: BAD, tag %d type %d offset %d count %d\n"),
244                 entry->info.tag, entry->info.type,
245                 entry->info.offset, entry->info.count);
246             goto exit;
247         }
248
249         /* Is there an immutable header region tag trailer? */
250         dataEnd = dataStart + entry->info.offset;
251 /*@-sizeoftype@*/
252 /*@-bounds@*/
253         (void) memcpy(info, dataEnd, REGION_TAG_COUNT);
254 /*@=bounds@*/
255         dataEnd += REGION_TAG_COUNT;
256
257         xx = headerVerifyInfo(1, dl, info, &entry->info, 1);
258         if (xx != -1 ||
259             !(entry->info.tag == RPMTAG_HEADERSIGNATURES
260            && entry->info.type == RPM_BIN_TYPE
261            && entry->info.count == REGION_TAG_COUNT))
262         {
263             (void) snprintf(buf, sizeof(buf),
264                 _("region trailer: BAD, tag %d type %d offset %d count %d\n"),
265                 entry->info.tag, entry->info.type,
266                 entry->info.offset, entry->info.count);
267             goto exit;
268         }
269 /*@=sizeoftype@*/
270 /*@-boundswrite@*/
271         memset(info, 0, sizeof(*info));
272 /*@=boundswrite@*/
273
274         /* Is the no. of tags in the region less than the total no. of tags? */
275         ril = entry->info.offset/sizeof(*pe);
276         if ((entry->info.offset % sizeof(*pe)) || ril > il) {
277             (void) snprintf(buf, sizeof(buf),
278                 _("region size: BAD, ril(%d) > il(%d)\n"), ril, il);
279             goto exit;
280         }
281     }
282
283     /* Sanity check signature tags */
284 /*@-boundswrite@*/
285     memset(info, 0, sizeof(*info));
286 /*@=boundswrite@*/
287     for (i = 1; i < il; i++) {
288         xx = headerVerifyInfo(1, dl, pe+i, &entry->info, 0);
289         if (xx != -1) {
290             (void) snprintf(buf, sizeof(buf),
291                 _("sigh tag[%d]: BAD, tag %d type %d offset %d count %d\n"),
292                 i, entry->info.tag, entry->info.type,
293                 entry->info.offset, entry->info.count);
294             goto exit;
295         }
296     }
297
298     /* OK, blob looks sane, load the header. */
299     sigh = headerLoad(ei);
300     if (sigh == NULL) {
301         (void) snprintf(buf, sizeof(buf), _("sigh load: BAD\n"));
302         goto exit;
303     }
304     sigh->flags |= HEADERFLAG_ALLOCATED;
305
306     {   int sigSize = headerSizeof(sigh, HEADER_MAGIC_YES);
307         int pad = (8 - (sigSize % 8)) % 8; /* 8-byte pad */
308         int_32 * archSize = NULL;
309
310         /* Position at beginning of header. */
311         if (pad && (xx = timedRead(fd, (char *)block, pad)) != pad) {
312             (void) snprintf(buf, sizeof(buf),
313                 _("sigh pad(%d): BAD, read %d bytes\n"), pad, xx);
314             goto exit;
315         }
316
317         /* Print package component sizes. */
318         if (headerGetEntry(sigh, RPMSIGTAG_SIZE, NULL,(void **)&archSize, NULL))
319             rc = printSize(fd, sigSize, pad, *archSize);
320     }
321
322 exit:
323 /*@-boundswrite@*/
324     if (sighp && sigh && rc == RPMRC_OK)
325         *sighp = headerLink(sigh);
326     sigh = headerFree(sigh);
327
328     if (msg != NULL) {
329         buf[sizeof(buf)-1] = '\0';
330         *msg = xstrdup(buf);
331     }
332 /*@=boundswrite@*/
333
334     return rc;
335 }
336
337 int rpmWriteSignature(FD_t fd, Header h)
338 {
339     static byte buf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
340     int sigSize, pad;
341     int rc;
342
343     rc = headerWrite(fd, h, HEADER_MAGIC_YES);
344     if (rc)
345         return rc;
346
347     sigSize = headerSizeof(h, HEADER_MAGIC_YES);
348     pad = (8 - (sigSize % 8)) % 8;
349     if (pad) {
350 /*@-boundswrite@*/
351         if (Fwrite(buf, sizeof(buf[0]), pad, fd) != pad)
352             rc = 1;
353 /*@=boundswrite@*/
354     }
355     rpmMessage(RPMMESS_DEBUG, _("Signature: size(%d)+pad(%d)\n"), sigSize, pad);
356     return rc;
357 }
358
359 Header rpmNewSignature(void)
360 {
361     Header h = headerNew();
362     return h;
363 }
364
365 Header rpmFreeSignature(Header h)
366 {
367     return headerFree(h);
368 }
369
370 /**
371  * Generate PGP (aka RSA/MD5) signature(s) for a header+payload file.
372  * @param file          header+payload file name
373  * @retval pkt          signature packet(s)
374  * @retval pktlen       signature packet(s) length
375  * @param passPhrase    private key pass phrase
376  * @return              0 on success, 1 on failure
377  */
378 static int makePGPSignature(const char * file, /*@out@*/ byte ** pkt,
379                 /*@out@*/ int_32 * pktlen, /*@null@*/ const char * passPhrase)
380         /*@globals errno, rpmGlobalMacroContext,
381                 fileSystem, internalState @*/
382         /*@modifies errno, *pkt, *pktlen, rpmGlobalMacroContext,
383                 fileSystem, internalState @*/
384 {
385     char * sigfile = alloca(1024);
386     int pid, status;
387     int inpipe[2];
388     struct stat st;
389     const char * cmd;
390     char *const *av;
391     int rc;
392
393 /*@-boundswrite@*/
394     (void) stpcpy( stpcpy(sigfile, file), ".sig");
395 /*@=boundswrite@*/
396
397     addMacro(NULL, "__plaintext_filename", NULL, file, -1);
398     addMacro(NULL, "__signature_filename", NULL, sigfile, -1);
399
400     inpipe[0] = inpipe[1] = 0;
401 /*@-boundsread@*/
402     (void) pipe(inpipe);
403 /*@=boundsread@*/
404
405     if (!(pid = fork())) {
406         const char *pgp_path = rpmExpand("%{?_pgp_path}", NULL);
407         const char *path;
408         pgpVersion pgpVer;
409
410         (void) close(STDIN_FILENO);
411         (void) dup2(inpipe[0], 3);
412         (void) close(inpipe[1]);
413
414         (void) dosetenv("PGPPASSFD", "3", 1);
415 /*@-boundsread@*/
416         if (pgp_path && *pgp_path != '\0')
417             (void) dosetenv("PGPPATH", pgp_path, 1);
418 /*@=boundsread@*/
419
420         /* dosetenv("PGPPASS", passPhrase, 1); */
421
422         unsetenv("MALLOC_CHECK_");
423         if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
424             switch(pgpVer) {
425             case PGP_2:
426                 cmd = rpmExpand("%{?__pgp_sign_cmd}", NULL);
427                 rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
428 /*@-boundsread@*/
429                 if (!rc)
430                     rc = execve(av[0], av+1, environ);
431 /*@=boundsread@*/
432                 break;
433             case PGP_5:
434                 cmd = rpmExpand("%{?__pgp5_sign_cmd}", NULL);
435                 rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
436 /*@-boundsread@*/
437                 if (!rc)
438                     rc = execve(av[0], av+1, environ);
439 /*@=boundsread@*/
440                 break;
441             case PGP_UNKNOWN:
442             case PGP_NOTDETECTED:
443                 errno = ENOENT;
444                 break;
445             }
446         }
447         rpmError(RPMERR_EXEC, _("Could not exec %s: %s\n"), "pgp",
448                         strerror(errno));
449         _exit(RPMERR_EXEC);
450     }
451
452     delMacro(NULL, "__plaintext_filename");
453     delMacro(NULL, "__signature_filename");
454
455     (void) close(inpipe[0]);
456     if (passPhrase)
457         (void) write(inpipe[1], passPhrase, strlen(passPhrase));
458     (void) write(inpipe[1], "\n", 1);
459     (void) close(inpipe[1]);
460
461     (void)waitpid(pid, &status, 0);
462     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
463         rpmError(RPMERR_SIGGEN, _("pgp failed\n"));
464         return 1;
465     }
466
467     if (stat(sigfile, &st)) {
468         /* PGP failed to write signature */
469         if (sigfile) (void) unlink(sigfile);  /* Just in case */
470         rpmError(RPMERR_SIGGEN, _("pgp failed to write signature\n"));
471         return 1;
472     }
473
474 /*@-boundswrite@*/
475     *pktlen = st.st_size;
476     rpmMessage(RPMMESS_DEBUG, _("PGP sig size: %d\n"), *pktlen);
477     *pkt = xmalloc(*pktlen);
478 /*@=boundswrite@*/
479
480 /*@-boundsread@*/
481     {   FD_t fd;
482
483         rc = 0;
484         fd = Fopen(sigfile, "r.fdio");
485         if (fd != NULL && !Ferror(fd)) {
486             rc = timedRead(fd, *pkt, *pktlen);
487             if (sigfile) (void) unlink(sigfile);
488             (void) Fclose(fd);
489         }
490         if (rc != *pktlen) {
491 /*@-boundswrite@*/
492             *pkt = _free(*pkt);
493 /*@=boundswrite@*/
494             rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
495             return 1;
496         }
497     }
498
499     rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of PGP sig\n"), *pktlen);
500 /*@=boundsread@*/
501
502     return 0;
503 }
504
505 /**
506  * Generate GPG (aka DSA) signature(s) for a header+payload file.
507  * @param file          header+payload file name
508  * @retval pkt          signature packet(s)
509  * @retval pktlen       signature packet(s) length
510  * @param passPhrase    private key pass phrase
511  * @return              0 on success, 1 on failure
512  */
513 static int makeGPGSignature(const char * file, /*@out@*/ byte ** pkt,
514                 /*@out@*/ int_32 * pktlen, /*@null@*/ const char * passPhrase)
515         /*@globals rpmGlobalMacroContext,
516                 fileSystem, internalState @*/
517         /*@modifies *pkt, *pktlen, rpmGlobalMacroContext,
518                 fileSystem, internalState @*/
519 {
520     char * sigfile = alloca(1024);
521     int pid, status;
522     int inpipe[2];
523     FILE * fpipe;
524     struct stat st;
525     const char * cmd;
526     char *const *av;
527     int rc;
528
529 /*@-boundswrite@*/
530     (void) stpcpy( stpcpy(sigfile, file), ".sig");
531 /*@=boundswrite@*/
532
533     addMacro(NULL, "__plaintext_filename", NULL, file, -1);
534     addMacro(NULL, "__signature_filename", NULL, sigfile, -1);
535
536     inpipe[0] = inpipe[1] = 0;
537 /*@-boundsread@*/
538     (void) pipe(inpipe);
539 /*@=boundsread@*/
540
541     if (!(pid = fork())) {
542         const char *gpg_path = rpmExpand("%{?_gpg_path}", NULL);
543
544         (void) close(STDIN_FILENO);
545         (void) dup2(inpipe[0], 3);
546         (void) close(inpipe[1]);
547
548 /*@-boundsread@*/
549         if (gpg_path && *gpg_path != '\0')
550             (void) dosetenv("GNUPGHOME", gpg_path, 1);
551 /*@=boundsread@*/
552
553         unsetenv("MALLOC_CHECK_");
554         cmd = rpmExpand("%{?__gpg_sign_cmd}", NULL);
555         rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
556 /*@-boundsread@*/
557         if (!rc)
558             rc = execve(av[0], av+1, environ);
559 /*@=boundsread@*/
560
561         rpmError(RPMERR_EXEC, _("Could not exec %s: %s\n"), "gpg",
562                         strerror(errno));
563         _exit(RPMERR_EXEC);
564     }
565
566     delMacro(NULL, "__plaintext_filename");
567     delMacro(NULL, "__signature_filename");
568
569     fpipe = fdopen(inpipe[1], "w");
570     (void) close(inpipe[0]);
571     if (fpipe) {
572         fprintf(fpipe, "%s\n", (passPhrase ? passPhrase : ""));
573         (void) fclose(fpipe);
574     }
575
576     (void) waitpid(pid, &status, 0);
577     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
578         rpmError(RPMERR_SIGGEN, _("gpg failed\n"));
579         return 1;
580     }
581
582     if (stat(sigfile, &st)) {
583         /* GPG failed to write signature */
584         if (sigfile) (void) unlink(sigfile);  /* Just in case */
585         rpmError(RPMERR_SIGGEN, _("gpg failed to write signature\n"));
586         return 1;
587     }
588
589 /*@-boundswrite@*/
590     *pktlen = st.st_size;
591     rpmMessage(RPMMESS_DEBUG, _("GPG sig size: %d\n"), *pktlen);
592     *pkt = xmalloc(*pktlen);
593 /*@=boundswrite@*/
594
595 /*@-boundsread@*/
596     {   FD_t fd;
597
598         rc = 0;
599         fd = Fopen(sigfile, "r.fdio");
600         if (fd != NULL && !Ferror(fd)) {
601             rc = timedRead(fd, *pkt, *pktlen);
602             if (sigfile) (void) unlink(sigfile);
603             (void) Fclose(fd);
604         }
605         if (rc != *pktlen) {
606 /*@-boundswrite@*/
607             *pkt = _free(*pkt);
608 /*@=boundswrite@*/
609             rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
610             return 1;
611         }
612     }
613
614     rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of GPG sig\n"), *pktlen);
615 /*@=boundsread@*/
616
617     return 0;
618 }
619
620 /**
621  * Generate header only signature(s) from a header+payload file.
622  * @param sig           signature header
623  * @param file          header+payload file name
624  * @param sigTag        type of signature(s) to add
625  * @param passPhrase    private key pass phrase
626  * @return              0 on success, -1 on failure
627  */
628 static int makeHDRSignature(Header sig, const char * file, int_32 sigTag,
629                 /*@null@*/ const char * passPhrase)
630         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
631         /*@modifies sig, rpmGlobalMacroContext, fileSystem, internalState @*/
632 {
633     Header h = NULL;
634     FD_t fd = NULL;
635     byte * pkt;
636     int_32 pktlen;
637     const char * fn = NULL;
638     const char * SHA1 = NULL;
639     int ret = -1;       /* assume failure. */
640
641     switch (sigTag) {
642     case RPMSIGTAG_SIZE:
643     case RPMSIGTAG_MD5:
644     case RPMSIGTAG_PGP5:        /* XXX legacy */
645     case RPMSIGTAG_PGP:
646     case RPMSIGTAG_GPG:
647         goto exit;
648         /*@notreached@*/ break;
649     case RPMSIGTAG_SHA1:
650         fd = Fopen(file, "r.fdio");
651         if (fd == NULL || Ferror(fd))
652             goto exit;
653         h = headerRead(fd, HEADER_MAGIC_YES);
654         if (h == NULL)
655             goto exit;
656         (void) Fclose(fd);      fd = NULL;
657
658         if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
659             DIGEST_CTX ctx;
660             void * uh;
661             int_32 uht, uhc;
662         
663             if (!headerGetEntry(h, RPMTAG_HEADERIMMUTABLE, &uht, &uh, &uhc)
664              ||  uh == NULL)
665             {
666                 h = headerFree(h);
667                 goto exit;
668             }
669             ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
670             (void) rpmDigestUpdate(ctx, header_magic, sizeof(header_magic));
671             (void) rpmDigestUpdate(ctx, uh, uhc);
672             (void) rpmDigestFinal(ctx, (void **)&SHA1, NULL, 1);
673             uh = headerFreeData(uh, uht);
674         }
675         h = headerFree(h);
676
677         if (SHA1 == NULL)
678             goto exit;
679         if (!headerAddEntry(sig, RPMSIGTAG_SHA1, RPM_STRING_TYPE, SHA1, 1))
680             goto exit;
681         ret = 0;
682         break;
683     case RPMSIGTAG_DSA:
684         fd = Fopen(file, "r.fdio");
685         if (fd == NULL || Ferror(fd))
686             goto exit;
687         h = headerRead(fd, HEADER_MAGIC_YES);
688         if (h == NULL)
689             goto exit;
690         (void) Fclose(fd);      fd = NULL;
691         if (makeTempFile(NULL, &fn, &fd))
692             goto exit;
693         if (headerWrite(fd, h, HEADER_MAGIC_YES))
694             goto exit;
695         (void) Fclose(fd);      fd = NULL;
696         if (makeGPGSignature(fn, &pkt, &pktlen, passPhrase)
697          || !headerAddEntry(sig, sigTag, RPM_BIN_TYPE, pkt, pktlen))
698             goto exit;
699         ret = 0;
700         break;
701     case RPMSIGTAG_RSA:
702         fd = Fopen(file, "r.fdio");
703         if (fd == NULL || Ferror(fd))
704             goto exit;
705         h = headerRead(fd, HEADER_MAGIC_YES);
706         if (h == NULL)
707             goto exit;
708         (void) Fclose(fd);      fd = NULL;
709         if (makeTempFile(NULL, &fn, &fd))
710             goto exit;
711         if (headerWrite(fd, h, HEADER_MAGIC_YES))
712             goto exit;
713         (void) Fclose(fd);      fd = NULL;
714         if (makePGPSignature(fn, &pkt, &pktlen, passPhrase)
715          || !headerAddEntry(sig, sigTag, RPM_BIN_TYPE, pkt, pktlen))
716             goto exit;
717         ret = 0;
718         break;
719     }
720
721 exit:
722     if (fn) {
723         (void) unlink(fn);
724         fn = _free(fn);
725     }
726     SHA1 = _free(SHA1);
727     h = headerFree(h);
728     if (fd != NULL) (void) Fclose(fd);
729     return ret;
730 }
731
732 int rpmAddSignature(Header sig, const char * file, int_32 sigTag,
733                 const char * passPhrase)
734 {
735     struct stat st;
736     byte * pkt;
737     int_32 pktlen;
738     int ret = -1;       /* assume failure. */
739
740     switch (sigTag) {
741     case RPMSIGTAG_SIZE:
742         if (stat(file, &st) != 0)
743             break;
744         pktlen = st.st_size;
745         if (!headerAddEntry(sig, sigTag, RPM_INT32_TYPE, &pktlen, 1))
746             break;
747         ret = 0;
748         break;
749     case RPMSIGTAG_MD5:
750         pktlen = 16;
751         pkt = xcalloc(1, pktlen);
752         if (domd5(file, pkt, 0, NULL)
753          || !headerAddEntry(sig, sigTag, RPM_BIN_TYPE, pkt, pktlen))
754             break;
755         ret = 0;
756         break;
757     case RPMSIGTAG_PGP5:        /* XXX legacy */
758     case RPMSIGTAG_PGP:
759         if (makePGPSignature(file, &pkt, &pktlen, passPhrase)
760          || !headerAddEntry(sig, sigTag, RPM_BIN_TYPE, pkt, pktlen))
761             break;
762 #ifdef  NOTYET  /* XXX needs hdrmd5ctx, like hdrsha1ctx. */
763         /* XXX Piggyback a header-only RSA signature as well. */
764         ret = makeHDRSignature(sig, file, RPMSIGTAG_RSA, passPhrase);
765 #endif
766         ret = 0;
767         break;
768     case RPMSIGTAG_GPG:
769         if (makeGPGSignature(file, &pkt, &pktlen, passPhrase)
770          || !headerAddEntry(sig, sigTag, RPM_BIN_TYPE, pkt, pktlen))
771             break;
772         /* XXX Piggyback a header-only DSA signature as well. */
773         ret = makeHDRSignature(sig, file, RPMSIGTAG_DSA, passPhrase);
774         break;
775     case RPMSIGTAG_RSA:
776     case RPMSIGTAG_DSA:
777     case RPMSIGTAG_SHA1:
778         ret = makeHDRSignature(sig, file, sigTag, passPhrase);
779         break;
780     }
781
782     return ret;
783 }
784
785 static int checkPassPhrase(const char * passPhrase, const int sigTag)
786         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
787         /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
788 {
789     int passPhrasePipe[2];
790     int pid, status;
791     int rc;
792     int xx;
793
794     passPhrasePipe[0] = passPhrasePipe[1] = 0;
795 /*@-boundsread@*/
796     xx = pipe(passPhrasePipe);
797 /*@=boundsread@*/
798     if (!(pid = fork())) {
799         const char * cmd;
800         char *const *av;
801         int fdno;
802
803         xx = close(STDIN_FILENO);
804         xx = close(STDOUT_FILENO);
805         xx = close(passPhrasePipe[1]);
806         if (! rpmIsVerbose())
807             xx = close(STDERR_FILENO);
808         if ((fdno = open("/dev/null", O_RDONLY)) != STDIN_FILENO) {
809             xx = dup2(fdno, STDIN_FILENO);
810             xx = close(fdno);
811         }
812         if ((fdno = open("/dev/null", O_WRONLY)) != STDOUT_FILENO) {
813             xx = dup2(fdno, STDOUT_FILENO);
814             xx = close(fdno);
815         }
816         xx = dup2(passPhrasePipe[0], 3);
817
818         unsetenv("MALLOC_CHECK_");
819         switch (sigTag) {
820         case RPMSIGTAG_DSA:
821         case RPMSIGTAG_GPG:
822         {   const char *gpg_path = rpmExpand("%{?_gpg_path}", NULL);
823
824 /*@-boundsread@*/
825             if (gpg_path && *gpg_path != '\0')
826                 (void) dosetenv("GNUPGHOME", gpg_path, 1);
827 /*@=boundsread@*/
828
829             cmd = rpmExpand("%{?__gpg_check_password_cmd}", NULL);
830             rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
831 /*@-boundsread@*/
832             if (!rc)
833                 rc = execve(av[0], av+1, environ);
834 /*@=boundsread@*/
835
836             rpmError(RPMERR_EXEC, _("Could not exec %s: %s\n"), "gpg",
837                         strerror(errno));
838         }   /*@notreached@*/ break;
839         case RPMSIGTAG_RSA:
840         case RPMSIGTAG_PGP5:    /* XXX legacy */
841         case RPMSIGTAG_PGP:
842         {   const char *pgp_path = rpmExpand("%{?_pgp_path}", NULL);
843             const char *path;
844             pgpVersion pgpVer;
845
846             (void) dosetenv("PGPPASSFD", "3", 1);
847 /*@-boundsread@*/
848             if (pgp_path && *pgp_path != '\0')
849                 xx = dosetenv("PGPPATH", pgp_path, 1);
850 /*@=boundsread@*/
851
852             if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
853                 switch(pgpVer) {
854                 case PGP_2:
855                     cmd = rpmExpand("%{?__pgp_check_password_cmd}", NULL);
856                     rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
857 /*@-boundsread@*/
858                     if (!rc)
859                         rc = execve(av[0], av+1, environ);
860 /*@=boundsread@*/
861                     /*@innerbreak@*/ break;
862                 case PGP_5:     /* XXX legacy */
863                     cmd = rpmExpand("%{?__pgp5_check_password_cmd}", NULL);
864                     rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
865 /*@-boundsread@*/
866                     if (!rc)
867                         rc = execve(av[0], av+1, environ);
868 /*@=boundsread@*/
869                     /*@innerbreak@*/ break;
870                 case PGP_UNKNOWN:
871                 case PGP_NOTDETECTED:
872                     /*@innerbreak@*/ break;
873                 }
874             }
875             rpmError(RPMERR_EXEC, _("Could not exec %s: %s\n"), "pgp",
876                         strerror(errno));
877             _exit(RPMERR_EXEC);
878         }   /*@notreached@*/ break;
879         default: /* This case should have been screened out long ago. */
880             rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
881             _exit(RPMERR_SIGGEN);
882             /*@notreached@*/ break;
883         }
884     }
885
886     xx = close(passPhrasePipe[0]);
887     xx = write(passPhrasePipe[1], passPhrase, strlen(passPhrase));
888     xx = write(passPhrasePipe[1], "\n", 1);
889     xx = close(passPhrasePipe[1]);
890
891     (void) waitpid(pid, &status, 0);
892
893     return ((!WIFEXITED(status) || WEXITSTATUS(status)) ? 1 : 0);
894 }
895
896 char * rpmGetPassPhrase(const char * prompt, const int sigTag)
897 {
898     char *pass;
899     int aok;
900
901     switch (sigTag) {
902     case RPMSIGTAG_DSA:
903     case RPMSIGTAG_GPG:
904 /*@-boundsread@*/
905       { const char *name = rpmExpand("%{?_gpg_name}", NULL);
906         aok = (name && *name != '\0');
907         name = _free(name);
908       }
909 /*@=boundsread@*/
910         if (!aok) {
911             rpmError(RPMERR_SIGGEN,
912                 _("You must set \"%%_gpg_name\" in your macro file\n"));
913             return NULL;
914         }
915         break;
916     case RPMSIGTAG_RSA:
917     case RPMSIGTAG_PGP5:        /* XXX legacy */
918     case RPMSIGTAG_PGP:
919 /*@-boundsread@*/
920       { const char *name = rpmExpand("%{?_pgp_name}", NULL);
921         aok = (name && *name != '\0');
922         name = _free(name);
923       }
924 /*@=boundsread@*/
925         if (!aok) {
926             rpmError(RPMERR_SIGGEN,
927                 _("You must set \"%%_pgp_name\" in your macro file\n"));
928             return NULL;
929         }
930         break;
931     default:
932         /* Currently the calling function (rpm.c:main) is checking this and
933          * doing a better job.  This section should never be accessed.
934          */
935         rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
936         return NULL;
937         /*@notreached@*/ break;
938     }
939
940 /*@-moduncon -nullpass @*/
941     pass = /*@-unrecog@*/ getpass( (prompt ? prompt : "") ) /*@=unrecog@*/ ;
942 /*@=moduncon -nullpass @*/
943
944     if (checkPassPhrase(pass, sigTag))
945         return NULL;
946
947     return pass;
948 }
949
950 static /*@observer@*/ const char * rpmSigString(rpmRC res)
951         /*@*/
952 {
953     const char * str;
954     switch (res) {
955     case RPMRC_OK:              str = "OK";             break;
956     case RPMRC_FAIL:            str = "BAD";            break;
957     case RPMRC_NOKEY:           str = "NOKEY";          break;
958     case RPMRC_NOTTRUSTED:      str = "NOTRUSTED";      break;
959     default:
960     case RPMRC_NOTFOUND:        str = "UNKNOWN";        break;
961     }
962     return str;
963 }
964
965 /*@-boundswrite@*/
966 static rpmRC
967 verifySizeSignature(const rpmts ts, /*@out@*/ char * t)
968         /*@modifies *t @*/
969 {
970     const void * sig = rpmtsSig(ts);
971     pgpDig dig = rpmtsDig(ts);
972     rpmRC res;
973     int_32 size = 0x7fffffff;
974
975     *t = '\0';
976     t = stpcpy(t, _("Header+Payload size: "));
977
978     if (sig == NULL || dig == NULL || dig->nbytes == 0) {
979         res = RPMRC_NOKEY;
980         t = stpcpy(t, rpmSigString(res));
981         goto exit;
982     }
983
984     memcpy(&size, sig, sizeof(size));
985
986     if (size != dig->nbytes) {
987         res = RPMRC_FAIL;
988         t = stpcpy(t, rpmSigString(res));
989         sprintf(t, " Expected(%d) != (%d)\n", (int)size, (int)dig->nbytes);
990     } else {
991         res = RPMRC_OK;
992         t = stpcpy(t, rpmSigString(res));
993         sprintf(t, " (%d)", (int)dig->nbytes);
994     }
995
996 exit:
997     t = stpcpy(t, "\n");
998     return res;
999 }
1000 /*@=boundswrite@*/
1001
1002 /*@-boundswrite@*/
1003 static rpmRC
1004 verifyMD5Signature(const rpmts ts, /*@out@*/ char * t,
1005                 /*@null@*/ DIGEST_CTX md5ctx)
1006         /*@modifies *t @*/
1007 {
1008     const void * sig = rpmtsSig(ts);
1009     int_32 siglen = rpmtsSiglen(ts);
1010     pgpDig dig = rpmtsDig(ts);
1011     rpmRC res;
1012     byte * md5sum = NULL;
1013     size_t md5len = 0;
1014
1015     *t = '\0';
1016     t = stpcpy(t, _("MD5 digest: "));
1017
1018     if (md5ctx == NULL || sig == NULL || dig == NULL) {
1019         res = RPMRC_NOKEY;
1020         t = stpcpy(t, rpmSigString(res));
1021         goto exit;
1022     }
1023
1024     (void) rpmswEnter(&ts->op_digest, 0);
1025     (void) rpmDigestFinal(rpmDigestDup(md5ctx),
1026                 (void **)&md5sum, &md5len, 0);
1027     (void) rpmswExit(&ts->op_digest, 0);
1028
1029     if (md5len != siglen || memcmp(md5sum, sig, md5len)) {
1030         res = RPMRC_FAIL;
1031         t = stpcpy(t, rpmSigString(res));
1032         t = stpcpy(t, " Expected(");
1033         (void) pgpHexCvt(t, sig, siglen);
1034         t += strlen(t);
1035         t = stpcpy(t, ") != (");
1036     } else {
1037         res = RPMRC_OK;
1038         t = stpcpy(t, rpmSigString(res));
1039         t = stpcpy(t, " (");
1040     }
1041     (void) pgpHexCvt(t, md5sum, md5len);
1042     t += strlen(t);
1043     t = stpcpy(t, ")");
1044
1045 exit:
1046     md5sum = _free(md5sum);
1047     t = stpcpy(t, "\n");
1048     return res;
1049 }
1050 /*@=boundswrite@*/
1051
1052 /*@-boundswrite@*/
1053 /**
1054  * Verify header immutable region SHA1 digest.
1055  * @param ts            transaction set
1056  * @retval t            verbose success/failure text
1057  * @param sha1ctx
1058  * @return              RPMRC_OK on success
1059  */
1060 static rpmRC
1061 verifySHA1Signature(const rpmts ts, /*@out@*/ char * t,
1062                 /*@null@*/ DIGEST_CTX sha1ctx)
1063         /*@modifies *t @*/
1064 {
1065     const void * sig = rpmtsSig(ts);
1066 #ifdef  NOTYET
1067     int_32 siglen = rpmtsSiglen(ts);
1068 #endif
1069     pgpDig dig = rpmtsDig(ts);
1070     rpmRC res;
1071     const char * SHA1 = NULL;
1072
1073     *t = '\0';
1074     t = stpcpy(t, _("Header SHA1 digest: "));
1075
1076     if (sha1ctx == NULL || sig == NULL || dig == NULL) {
1077         res = RPMRC_NOKEY;
1078         t = stpcpy(t, rpmSigString(res));
1079         goto exit;
1080     }
1081
1082     (void) rpmswEnter(&ts->op_digest, 0);
1083     (void) rpmDigestFinal(rpmDigestDup(sha1ctx),
1084                 (void **)&SHA1, NULL, 1);
1085     (void) rpmswExit(&ts->op_digest, 0);
1086
1087     if (SHA1 == NULL || strlen(SHA1) != strlen(sig) || strcmp(SHA1, sig)) {
1088         res = RPMRC_FAIL;
1089         t = stpcpy(t, rpmSigString(res));
1090         t = stpcpy(t, " Expected(");
1091         t = stpcpy(t, sig);
1092         t = stpcpy(t, ") != (");
1093     } else {
1094         res = RPMRC_OK;
1095         t = stpcpy(t, rpmSigString(res));
1096         t = stpcpy(t, " (");
1097     }
1098     if (SHA1)
1099         t = stpcpy(t, SHA1);
1100     t = stpcpy(t, ")");
1101
1102 exit:
1103     SHA1 = _free(SHA1);
1104     t = stpcpy(t, "\n");
1105     return res;
1106 }
1107 /*@=boundswrite@*/
1108
1109 /**
1110  * Convert hex to binary nibble.
1111  * @param c            hex character
1112  * @return             binary nibble
1113  */
1114 static inline unsigned char nibble(char c)
1115         /*@*/
1116 {
1117     if (c >= '0' && c <= '9')
1118         return (c - '0');
1119     if (c >= 'A' && c <= 'F')
1120         return (c - 'A') + 10;
1121     if (c >= 'a' && c <= 'f')
1122         return (c - 'a') + 10;
1123     return 0;
1124 }
1125
1126 /*@-boundswrite@*/
1127 /**
1128  * Verify PGP (aka RSA/MD5) signature.
1129  * @param ts            transaction set
1130  * @retval t            verbose success/failure text
1131  * @param md5ctx
1132  * @return              RPMRC_OK on success
1133  */
1134 static rpmRC
1135 verifyPGPSignature(rpmts ts, /*@out@*/ char * t,
1136                 /*@null@*/ DIGEST_CTX md5ctx)
1137         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
1138         /*@modifies ts, *t, rpmGlobalMacroContext, fileSystem, internalState */
1139 {
1140     const void * sig = rpmtsSig(ts);
1141 #ifdef  NOTYET
1142     int_32 siglen = rpmtsSiglen(ts);
1143 #endif
1144     int_32 sigtag = rpmtsSigtag(ts);
1145     pgpDig dig = rpmtsDig(ts);
1146     pgpDigParams sigp = rpmtsSignature(ts);
1147     rpmRC res;
1148     int xx;
1149
1150     *t = '\0';
1151     t = stpcpy(t, _("V3 RSA/MD5 signature: "));
1152
1153     if (md5ctx == NULL || sig == NULL || dig == NULL || sigp == NULL) {
1154         res = RPMRC_NOKEY;
1155         goto exit;
1156     }
1157
1158     /* XXX sanity check on sigtag and signature agreement. */
1159     if (!(sigtag == RPMSIGTAG_PGP
1160         && sigp->pubkey_algo == PGPPUBKEYALGO_RSA
1161         && sigp->hash_algo == PGPHASHALGO_MD5))
1162     {
1163         res = RPMRC_NOKEY;
1164         goto exit;
1165     }
1166
1167     (void) rpmswEnter(&ts->op_digest, 0);
1168     {   DIGEST_CTX ctx = rpmDigestDup(md5ctx);
1169         byte signhash16[2];
1170         const char * s;
1171
1172         if (sigp->hash != NULL)
1173             xx = rpmDigestUpdate(ctx, sigp->hash, sigp->hashlen);
1174
1175 #ifdef  NOTYET  /* XXX not for binary/text document signatures. */
1176         if (sigp->sigtype == 4) {
1177             int nb = dig->nbytes + sigp->hashlen;
1178             byte trailer[6];
1179             nb = htonl(nb);
1180             trailer[0] = 0x4;
1181             trailer[1] = 0xff;
1182             memcpy(trailer+2, &nb, sizeof(nb));
1183             xx = rpmDigestUpdate(ctx, trailer, sizeof(trailer));
1184         }
1185 #endif
1186
1187         xx = rpmDigestFinal(ctx, (void **)&dig->md5, &dig->md5len, 1);
1188         (void) rpmswExit(&ts->op_digest, sigp->hashlen);
1189
1190         /* Compare leading 16 bits of digest for quick check. */
1191         s = dig->md5;
1192         signhash16[0] = (nibble(s[0]) << 4) | nibble(s[1]);
1193         signhash16[1] = (nibble(s[2]) << 4) | nibble(s[3]);
1194         if (memcmp(signhash16, sigp->signhash16, sizeof(signhash16))) {
1195             res = RPMRC_FAIL;
1196             goto exit;
1197         }
1198
1199     }
1200
1201     {   const char * prefix = "3020300c06082a864886f70d020505000410";
1202         unsigned int nbits = 1024;
1203         unsigned int nb = (nbits + 7) >> 3;
1204         const char * hexstr;
1205         char * tt;
1206
1207         hexstr = tt = xmalloc(2 * nb + 1);
1208         memset(tt, 'f', (2 * nb));
1209         tt[0] = '0'; tt[1] = '0';
1210         tt[2] = '0'; tt[3] = '1';
1211         tt += (2 * nb) - strlen(prefix) - strlen(dig->md5) - 2;
1212         *tt++ = '0'; *tt++ = '0';
1213         tt = stpcpy(tt, prefix);
1214         tt = stpcpy(tt, dig->md5);
1215
1216         mp32nzero(&dig->rsahm); mp32nsethex(&dig->rsahm, hexstr);
1217
1218         hexstr = _free(hexstr);
1219
1220     }
1221
1222     /* Retrieve the matching public key. */
1223     res = rpmtsFindPubkey(ts);
1224     if (res != RPMRC_OK)
1225         goto exit;
1226
1227     (void) rpmswEnter(&ts->op_signature, 0);
1228     if (rsavrfy(&dig->rsa_pk, &dig->rsahm, &dig->c))
1229         res = RPMRC_OK;
1230     else
1231         res = RPMRC_FAIL;
1232     (void) rpmswExit(&ts->op_signature, 0);
1233
1234 exit:
1235     t = stpcpy(t, rpmSigString(res));
1236     if (sigp != NULL) {
1237         t = stpcpy(t, ", key ID ");
1238         (void) pgpHexCvt(t, sigp->signid+4, sizeof(sigp->signid)-4);
1239         t += strlen(t);
1240     }
1241     t = stpcpy(t, "\n");
1242     return res;
1243 }
1244 /*@=boundswrite@*/
1245
1246 /**
1247  * Verify GPG (aka DSA) signature.
1248  * @param ts            transaction set
1249  * @retval t            verbose success/failure text
1250  * @param sha1ctx
1251  * @return              RPMRC_OK on success
1252  */
1253 /*@-boundswrite@*/
1254 static rpmRC
1255 verifyGPGSignature(rpmts ts, /*@out@*/ char * t,
1256                 /*@null@*/ DIGEST_CTX sha1ctx)
1257         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
1258         /*@modifies ts, *t, rpmGlobalMacroContext, fileSystem, internalState */
1259 {
1260     const void * sig = rpmtsSig(ts);
1261 #ifdef  NOTYET
1262     int_32 siglen = rpmtsSiglen(ts);
1263 #endif
1264     int_32 sigtag = rpmtsSigtag(ts);
1265     pgpDig dig = rpmtsDig(ts);
1266     pgpDigParams sigp = rpmtsSignature(ts);
1267     rpmRC res;
1268     int xx;
1269
1270     *t = '\0';
1271     if (dig != NULL && dig->hdrsha1ctx == sha1ctx)
1272         t = stpcpy(t, _("Header "));
1273     t = stpcpy(t, _("V3 DSA signature: "));
1274
1275     if (sha1ctx == NULL || sig == NULL || dig == NULL || sigp == NULL) {
1276         res = RPMRC_NOKEY;
1277         goto exit;
1278     }
1279
1280     /* XXX sanity check on sigtag and signature agreement. */
1281     if (!((sigtag == RPMSIGTAG_GPG || sigtag == RPMSIGTAG_DSA)
1282         && sigp->pubkey_algo == PGPPUBKEYALGO_DSA
1283         && sigp->hash_algo == PGPHASHALGO_SHA1))
1284     {
1285         res = RPMRC_NOKEY;
1286         goto exit;
1287     }
1288
1289     (void) rpmswEnter(&ts->op_digest, 0);
1290     {   DIGEST_CTX ctx = rpmDigestDup(sha1ctx);
1291         byte signhash16[2];
1292
1293         (void) rpmswEnter(&ts->op_digest, 0);
1294         if (sigp->hash != NULL)
1295             xx = rpmDigestUpdate(ctx, sigp->hash, sigp->hashlen);
1296
1297 #ifdef  NOTYET  /* XXX not for binary/text document signatures. */
1298         if (sigp->sigtype == 4) {
1299             int nb = dig->nbytes + sigp->hashlen;
1300             byte trailer[6];
1301             nb = htonl(nb);
1302             trailer[0] = 0x4;
1303             trailer[1] = 0xff;
1304             memcpy(trailer+2, &nb, sizeof(nb));
1305             xx = rpmDigestUpdate(ctx, trailer, sizeof(trailer));
1306         }
1307 #endif
1308         xx = rpmDigestFinal(ctx, (void **)&dig->sha1, &dig->sha1len, 1);
1309         (void) rpmswExit(&ts->op_digest, sigp->hashlen);
1310
1311         mp32nzero(&dig->hm);    mp32nsethex(&dig->hm, dig->sha1);
1312
1313         /* Compare leading 16 bits of digest for quick check. */
1314         signhash16[0] = (*dig->hm.data >> 24) & 0xff;
1315         signhash16[1] = (*dig->hm.data >> 16) & 0xff;
1316         if (memcmp(signhash16, sigp->signhash16, sizeof(signhash16))) {
1317             res = RPMRC_FAIL;
1318             goto exit;
1319         }
1320     }
1321
1322     /* Retrieve the matching public key. */
1323     res = rpmtsFindPubkey(ts);
1324     if (res != RPMRC_OK)
1325         goto exit;
1326
1327     (void) rpmswEnter(&ts->op_signature, 0);
1328     if (dsavrfy(&dig->p, &dig->q, &dig->g,
1329                 &dig->hm, &dig->y, &dig->r, &dig->s))
1330         res = RPMRC_OK;
1331     else
1332         res = RPMRC_FAIL;
1333     (void) rpmswExit(&ts->op_signature, 0);
1334
1335 exit:
1336     t = stpcpy(t, rpmSigString(res));
1337     if (sigp != NULL) {
1338         t = stpcpy(t, ", key ID ");
1339         (void) pgpHexCvt(t, sigp->signid+4, sizeof(sigp->signid)-4);
1340         t += strlen(t);
1341     }
1342     t = stpcpy(t, "\n");
1343     return res;
1344 }
1345 /*@=boundswrite@*/
1346
1347 rpmRC
1348 rpmVerifySignature(const rpmts ts, char * result)
1349 {
1350     const void * sig = rpmtsSig(ts);
1351     int_32 siglen = rpmtsSiglen(ts);
1352     int_32 sigtag = rpmtsSigtag(ts);
1353     pgpDig dig = rpmtsDig(ts);
1354     rpmRC res;
1355
1356     if (sig == NULL || siglen <= 0 || dig == NULL) {
1357         sprintf(result, _("Verify signature: BAD PARAMETERS\n"));
1358         return RPMRC_NOTFOUND;
1359     }
1360
1361     switch (sigtag) {
1362     case RPMSIGTAG_SIZE:
1363         res = verifySizeSignature(ts, result);
1364         break;
1365     case RPMSIGTAG_MD5:
1366         res = verifyMD5Signature(ts, result, dig->md5ctx);
1367         break;
1368     case RPMSIGTAG_SHA1:
1369         res = verifySHA1Signature(ts, result, dig->hdrsha1ctx);
1370         break;
1371     case RPMSIGTAG_RSA:
1372     case RPMSIGTAG_PGP5:        /* XXX legacy */
1373     case RPMSIGTAG_PGP:
1374         res = verifyPGPSignature(ts, result, dig->md5ctx);
1375         break;
1376     case RPMSIGTAG_DSA:
1377         res = verifyGPGSignature(ts, result, dig->hdrsha1ctx);
1378         break;
1379     case RPMSIGTAG_GPG:
1380         res = verifyGPGSignature(ts, result, dig->sha1ctx);
1381         break;
1382     case RPMSIGTAG_LEMD5_1:
1383     case RPMSIGTAG_LEMD5_2:
1384         sprintf(result, _("Broken MD5 digest: UNSUPPORTED\n"));
1385         res = RPMRC_NOTFOUND;
1386         break;
1387     default:
1388         sprintf(result, _("Signature: UNKNOWN (%d)\n"), sigtag);
1389         res = RPMRC_NOTFOUND;
1390         break;
1391     }
1392     return res;
1393 }