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