- all symbols but hdrVec are now forward references in linkage.
[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
11 #include "misc.h"       /* XXX for dosetenv() and makeTempFile() */
12 #include "legacy.h"     /* XXX for mdbinfile() */
13 #include "rpmlead.h"
14 #include "signature.h"
15 #include "debug.h"
16
17 /*@access Header@*/             /* XXX compared with NULL */
18 /*@access FD_t@*/               /* XXX compared with NULL */
19 /*@access pgpDig@*/
20
21 /*@-mustmod@*/ /* FIX: internalState not modified? */
22 int rpmLookupSignatureType(int action)
23 {
24     static int disabled = 0;
25     int rc = 0;
26
27     switch (action) {
28     case RPMLOOKUPSIG_DISABLE:
29         disabled = -2;
30         break;
31     case RPMLOOKUPSIG_ENABLE:
32         disabled = 0;
33         /*@fallthrough@*/
34     case RPMLOOKUPSIG_QUERY:
35         if (disabled)
36             break;      /* Disabled */
37       { const char *name = rpmExpand("%{_signature}", NULL);
38         if (!(name && *name != '%'))
39             rc = 0;
40         else if (!xstrcasecmp(name, "none"))
41             rc = 0;
42         else if (!xstrcasecmp(name, "pgp"))
43             rc = RPMSIGTAG_PGP;
44         else if (!xstrcasecmp(name, "pgp5"))    /* XXX legacy */
45             rc = RPMSIGTAG_PGP;
46         else if (!xstrcasecmp(name, "gpg"))
47             rc = RPMSIGTAG_GPG;
48         else
49             rc = -1;    /* Invalid %_signature spec in macro file */
50         name = _free(name);
51       } break;
52     }
53     return rc;
54 }
55 /*@=mustmod@*/
56
57 /* rpmDetectPGPVersion() returns the absolute path to the "pgp"  */
58 /* executable of the requested version, or NULL when none found. */
59
60 const char * rpmDetectPGPVersion(pgpVersion * pgpVer)
61 {
62     /* Actually this should support having more then one pgp version. */
63     /* At the moment only one version is possible since we only       */
64     /* have one %_pgpbin and one %_pgp_path.                          */
65
66     static pgpVersion saved_pgp_version = PGP_UNKNOWN;
67     const char *pgpbin = rpmGetPath("%{_pgpbin}", NULL);
68
69     if (saved_pgp_version == PGP_UNKNOWN) {
70         char *pgpvbin;
71         struct stat st;
72
73         if (!(pgpbin && pgpbin[0] != '%')) {
74           pgpbin = _free(pgpbin);
75           saved_pgp_version = -1;
76           return NULL;
77         }
78         pgpvbin = (char *)alloca(strlen(pgpbin) + sizeof("v"));
79         (void)stpcpy(stpcpy(pgpvbin, pgpbin), "v");
80
81         if (stat(pgpvbin, &st) == 0)
82           saved_pgp_version = PGP_5;
83         else if (stat(pgpbin, &st) == 0)
84           saved_pgp_version = PGP_2;
85         else
86           saved_pgp_version = PGP_NOTDETECTED;
87     }
88
89     if (pgpVer && pgpbin)
90         *pgpVer = saved_pgp_version;
91     return pgpbin;
92 }
93
94 /**
95  * Check package size.
96  * @todo rpmio: use fdSize rather than fstat(2) to get file size.
97  * @param fd                    package file handle
98  * @param siglen                signature header size
99  * @param pad                   signature padding
100  * @param datalen               length of header+payload
101  * @return                      rpmRC return code
102  */
103 static inline rpmRC checkSize(FD_t fd, int siglen, int pad, int datalen)
104         /*@globals fileSystem @*/
105         /*@modifies fileSystem @*/
106 {
107     struct stat st;
108     rpmRC rc;
109
110     if (fstat(Fileno(fd), &st))
111         return RPMRC_FAIL;
112
113     if (!S_ISREG(st.st_mode)) {
114         rpmMessage(RPMMESS_DEBUG,
115             _("file is not regular -- skipping size check\n"));
116         return RPMRC_OK;
117     }
118
119     /*@-sizeoftype@*/
120     rc = (((sizeof(struct rpmlead) + siglen + pad + datalen) - st.st_size)
121         ? RPMRC_BADSIZE : RPMRC_OK);
122
123     rpmMessage((rc == RPMRC_OK ? RPMMESS_DEBUG : RPMMESS_WARNING),
124         _("Expected size: %12d = lead(%d)+sigs(%d)+pad(%d)+data(%d)\n"),
125                 (int)sizeof(struct rpmlead)+siglen+pad+datalen,
126                 (int)sizeof(struct rpmlead), siglen, pad, datalen);
127     /*@=sizeoftype@*/
128     rpmMessage((rc == RPMRC_OK ? RPMMESS_DEBUG : RPMMESS_WARNING),
129         _("  Actual size: %12d\n"), (int)st.st_size);
130
131     return rc;
132 }
133
134 rpmRC rpmReadSignature(FD_t fd, Header * headerp, sigType sig_type)
135 {
136     byte buf[2048];
137     int sigSize, pad;
138     int_32 type, count;
139     int_32 *archSize;
140     Header h = NULL;
141     rpmRC rc = RPMRC_FAIL;              /* assume failure */
142
143     if (headerp)
144         *headerp = NULL;
145
146     buf[0] = 0;
147     switch (sig_type) {
148     case RPMSIGTYPE_NONE:
149         rpmMessage(RPMMESS_DEBUG, _("No signature\n"));
150         rc = RPMRC_OK;
151         break;
152     case RPMSIGTYPE_PGP262_1024:
153         rpmMessage(RPMMESS_DEBUG, _("Old PGP signature\n"));
154         /* These are always 256 bytes */
155         /*@-type@*/ /* FIX: eliminate timedRead @*/
156         if (timedRead(fd, buf, 256) != 256)
157             break;
158         /*@=type@*/
159         h = headerNew();
160         (void) headerAddEntry(h, RPMSIGTAG_PGP, RPM_BIN_TYPE, buf, 152);
161         rc = RPMRC_OK;
162         break;
163     case RPMSIGTYPE_MD5:
164     case RPMSIGTYPE_MD5_PGP:
165         rpmError(RPMERR_BADSIGTYPE,
166               _("Old (internal-only) signature!  How did you get that!?\n"));
167         break;
168     case RPMSIGTYPE_HEADERSIG:
169     case RPMSIGTYPE_DISABLE:
170         /* This is a new style signature */
171         h = headerRead(fd, HEADER_MAGIC_YES);
172         if (h == NULL)
173             break;
174
175         rc = RPMRC_OK;
176         sigSize = headerSizeof(h, HEADER_MAGIC_YES);
177
178         /* XXX Legacy headers have a HEADER_IMAGE tag added. */
179         if (headerIsEntry(h, RPMTAG_HEADERIMAGE))
180             sigSize -= (16 + 16);
181
182         pad = (8 - (sigSize % 8)) % 8; /* 8-byte pad */
183         if (sig_type == RPMSIGTYPE_HEADERSIG) {
184             if (! headerGetEntry(h, RPMSIGTAG_SIZE, &type,
185                                 (void **)&archSize, &count))
186                 break;
187             rc = checkSize(fd, sigSize, pad, *archSize);
188         }
189         /*@-type@*/ /* FIX: eliminate timedRead @*/
190         if (pad && timedRead(fd, buf, pad) != pad)
191             rc = RPMRC_SHORTREAD;
192         /*@=type@*/
193         break;
194     default:
195         break;
196     }
197
198     if (headerp && rc == 0)
199         *headerp = h;
200     else
201         h = headerFree(h);
202
203     return rc;
204 }
205
206 int rpmWriteSignature(FD_t fd, Header h)
207 {
208     static byte buf[8] = "\000\000\000\000\000\000\000\000";
209     int sigSize, pad;
210     int rc;
211
212     rc = headerWrite(fd, h, HEADER_MAGIC_YES);
213     if (rc)
214         return rc;
215
216     sigSize = headerSizeof(h, HEADER_MAGIC_YES);
217     pad = (8 - (sigSize % 8)) % 8;
218     if (pad) {
219         if (Fwrite(buf, sizeof(buf[0]), pad, fd) != pad)
220             rc = 1;
221     }
222     rpmMessage(RPMMESS_DEBUG, _("Signature: size(%d)+pad(%d)\n"), sigSize, pad);
223     return rc;
224 }
225
226 Header rpmNewSignature(void)
227 {
228     Header h = headerNew();
229     return h;
230 }
231
232 Header rpmFreeSignature(Header h)
233 {
234     return headerFree(h);
235 }
236
237 static int makePGPSignature(const char * file, /*@out@*/ void ** sig,
238                 /*@out@*/ int_32 * size, /*@null@*/ const char * passPhrase)
239         /*@globals rpmGlobalMacroContext,
240                 fileSystem @*/
241         /*@modifies *sig, *size, rpmGlobalMacroContext,
242                 fileSystem @*/
243 {
244     char * sigfile = alloca(1024);
245     int pid, status;
246     int inpipe[2];
247     struct stat st;
248
249     (void) stpcpy( stpcpy(sigfile, file), ".sig");
250
251     inpipe[0] = inpipe[1] = 0;
252     (void) pipe(inpipe);
253
254     if (!(pid = fork())) {
255         const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
256         const char *name = rpmExpand("+myname=\"%{_pgp_name}\"", NULL);
257         const char *path;
258         pgpVersion pgpVer;
259
260         (void) close(STDIN_FILENO);
261         (void) dup2(inpipe[0], 3);
262         (void) close(inpipe[1]);
263
264         (void) dosetenv("PGPPASSFD", "3", 1);
265         if (pgp_path && *pgp_path != '%')
266             (void) dosetenv("PGPPATH", pgp_path, 1);
267
268         /* dosetenv("PGPPASS", passPhrase, 1); */
269
270         if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
271             switch(pgpVer) {
272             case PGP_2:
273                 (void) execlp(path, "pgp", "+batchmode=on", "+verbose=0", "+armor=off",
274                     name, "-sb", file, sigfile, NULL);
275                 break;
276             case PGP_5:
277                 (void) execlp(path,"pgps", "+batchmode=on", "+verbose=0", "+armor=off",
278                     name, "-b", file, "-o", sigfile, NULL);
279                 break;
280             case PGP_UNKNOWN:
281             case PGP_NOTDETECTED:
282                 break;
283             }
284         }
285         rpmError(RPMERR_EXEC, _("Couldn't exec pgp (%s)\n"),
286                         (path ? path : NULL));
287         _exit(RPMERR_EXEC);
288     }
289
290     (void) close(inpipe[0]);
291     if (passPhrase)
292         (void) write(inpipe[1], passPhrase, strlen(passPhrase));
293     (void) write(inpipe[1], "\n", 1);
294     (void) close(inpipe[1]);
295
296     (void)waitpid(pid, &status, 0);
297     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
298         rpmError(RPMERR_SIGGEN, _("pgp failed\n"));
299         return 1;
300     }
301
302     if (stat(sigfile, &st)) {
303         /* PGP failed to write signature */
304         if (sigfile) (void) unlink(sigfile);  /* Just in case */
305         rpmError(RPMERR_SIGGEN, _("pgp failed to write signature\n"));
306         return 1;
307     }
308
309     *size = st.st_size;
310     rpmMessage(RPMMESS_DEBUG, _("PGP sig size: %d\n"), *size);
311     *sig = xmalloc(*size);
312
313     {   FD_t fd;
314         int rc = 0;
315         fd = Fopen(sigfile, "r.fdio");
316         if (fd != NULL && !Ferror(fd)) {
317             /*@-type@*/ /* FIX: eliminate timedRead @*/
318             rc = timedRead(fd, *sig, *size);
319             /*@=type@*/
320             if (sigfile) (void) unlink(sigfile);
321             (void) Fclose(fd);
322         }
323         if (rc != *size) {
324             *sig = _free(*sig);
325             rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
326             return 1;
327         }
328     }
329
330     rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of PGP sig\n"), *size);
331
332     return 0;
333 }
334
335 /* This is an adaptation of the makePGPSignature function to use GPG instead
336  * of PGP to create signatures.  I think I've made all the changes necessary,
337  * but this could be a good place to start looking if errors in GPG signature
338  * creation crop up.
339  */
340 static int makeGPGSignature(const char * file, /*@out@*/ void ** sig,
341                 /*@out@*/ int_32 * size, /*@null@*/ const char * passPhrase)
342         /*@globals rpmGlobalMacroContext,
343                 fileSystem @*/
344         /*@modifies *sig, *size, rpmGlobalMacroContext,
345                 fileSystem @*/
346 {
347     char * sigfile = alloca(1024);
348     int pid, status;
349     int inpipe[2];
350     FILE * fpipe;
351     struct stat st;
352
353     (void) stpcpy( stpcpy(sigfile, file), ".sig");
354
355     inpipe[0] = inpipe[1] = 0;
356     (void) pipe(inpipe);
357
358     if (!(pid = fork())) {
359         const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
360         const char *name = rpmExpand("%{_gpg_name}", NULL);
361
362         (void) close(STDIN_FILENO);
363         (void) dup2(inpipe[0], 3);
364         (void) close(inpipe[1]);
365
366         if (gpg_path && *gpg_path != '%')
367             (void) dosetenv("GNUPGHOME", gpg_path, 1);
368         (void) execlp("gpg", "gpg",
369                "--batch", "--no-verbose", "--no-armor", "--passphrase-fd", "3",
370                "-u", name, "-sbo", sigfile, file,
371                NULL);
372         rpmError(RPMERR_EXEC, _("Couldn't exec gpg\n"));
373         _exit(RPMERR_EXEC);
374     }
375
376     fpipe = fdopen(inpipe[1], "w");
377     (void) close(inpipe[0]);
378     if (fpipe) {
379         fprintf(fpipe, "%s\n", (passPhrase ? passPhrase : ""));
380         (void) fclose(fpipe);
381     }
382
383     (void)waitpid(pid, &status, 0);
384     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
385         rpmError(RPMERR_SIGGEN, _("gpg failed\n"));
386         return 1;
387     }
388
389     if (stat(sigfile, &st)) {
390         /* GPG failed to write signature */
391         if (sigfile) (void) unlink(sigfile);  /* Just in case */
392         rpmError(RPMERR_SIGGEN, _("gpg failed to write signature\n"));
393         return 1;
394     }
395
396     *size = st.st_size;
397     rpmMessage(RPMMESS_DEBUG, _("GPG sig size: %d\n"), *size);
398     *sig = xmalloc(*size);
399
400     {   FD_t fd;
401         int rc = 0;
402         fd = Fopen(sigfile, "r.fdio");
403         if (fd != NULL && !Ferror(fd)) {
404             /*@-type@*/ /* FIX: eliminate timedRead @*/
405             rc = timedRead(fd, *sig, *size);
406             /*@=type@*/
407             if (sigfile) (void) unlink(sigfile);
408             (void) Fclose(fd);
409         }
410         if (rc != *size) {
411             *sig = _free(*sig);
412             rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
413             return 1;
414         }
415     }
416
417     rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of GPG sig\n"), *size);
418
419     return 0;
420 }
421
422 int rpmAddSignature(Header h, const char * file, int_32 sigTag,
423                 const char *passPhrase)
424 {
425     struct stat st;
426     int_32 size;
427     byte buf[16];
428     void *sig;
429     int ret = -1;
430
431     switch (sigTag) {
432     case RPMSIGTAG_SIZE:
433         (void) stat(file, &st);
434         size = st.st_size;
435         ret = 0;
436         (void) headerAddEntry(h, RPMSIGTAG_SIZE, RPM_INT32_TYPE, &size, 1);
437         break;
438     case RPMSIGTAG_MD5:
439         ret = mdbinfile(file, buf);
440         if (ret == 0)
441             (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, buf, 16);
442         break;
443     case RPMSIGTAG_PGP5:        /* XXX legacy */
444     case RPMSIGTAG_PGP:
445         rpmMessage(RPMMESS_VERBOSE, _("Generating signature using PGP.\n"));
446         ret = makePGPSignature(file, &sig, &size, passPhrase);
447         if (ret == 0)
448             (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, sig, size);
449         break;
450     case RPMSIGTAG_GPG:
451         rpmMessage(RPMMESS_VERBOSE, _("Generating signature using GPG.\n"));
452         ret = makeGPGSignature(file, &sig, &size, passPhrase);
453         if (ret == 0)
454             (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, sig, size);
455         break;
456     }
457
458     return ret;
459 }
460
461 static int checkPassPhrase(const char * passPhrase, const int sigTag)
462         /*@globals rpmGlobalMacroContext,
463                 fileSystem @*/
464         /*@modifies rpmGlobalMacroContext,
465                 fileSystem @*/
466 {
467     int passPhrasePipe[2];
468     int pid, status;
469     int fd;
470
471     passPhrasePipe[0] = passPhrasePipe[1] = 0;
472     (void) pipe(passPhrasePipe);
473     if (!(pid = fork())) {
474         (void) close(STDIN_FILENO);
475         (void) close(STDOUT_FILENO);
476         (void) close(passPhrasePipe[1]);
477         /*@-internalglobs@*/ /* FIX: shrug */
478         if (! rpmIsVerbose()) {
479             (void) close(STDERR_FILENO);
480         }
481         /*@=internalglobs@*/
482         if ((fd = open("/dev/null", O_RDONLY)) != STDIN_FILENO) {
483             (void) dup2(fd, STDIN_FILENO);
484             (void) close(fd);
485         }
486         if ((fd = open("/dev/null", O_WRONLY)) != STDOUT_FILENO) {
487             (void) dup2(fd, STDOUT_FILENO);
488             (void) close(fd);
489         }
490         (void) dup2(passPhrasePipe[0], 3);
491
492         switch (sigTag) {
493         case RPMSIGTAG_GPG:
494         {   const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
495             const char *name = rpmExpand("%{_gpg_name}", NULL);
496             if (gpg_path && *gpg_path != '%')
497                 (void) dosetenv("GNUPGHOME", gpg_path, 1);
498             (void) execlp("gpg", "gpg",
499                    "--batch", "--no-verbose", "--passphrase-fd", "3",
500                    "-u", name, "-so", "-",
501                    NULL);
502             rpmError(RPMERR_EXEC, _("Couldn't exec gpg\n"));
503             _exit(RPMERR_EXEC);
504         }   /*@notreached@*/ break;
505         case RPMSIGTAG_PGP5:    /* XXX legacy */
506         case RPMSIGTAG_PGP:
507         {   const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
508             const char *name = rpmExpand("+myname=\"%{_pgp_name}\"", NULL);
509             const char *path;
510             pgpVersion pgpVer;
511
512             (void) dosetenv("PGPPASSFD", "3", 1);
513             if (pgp_path && *pgp_path != '%')
514                 (void) dosetenv("PGPPATH", pgp_path, 1);
515
516             if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
517                 switch(pgpVer) {
518                 case PGP_2:
519                     (void) execlp(path, "pgp", "+batchmode=on", "+verbose=0",
520                         name, "-sf", NULL);
521                     /*@innerbreak@*/ break;
522                 case PGP_5:     /* XXX legacy */
523                     (void) execlp(path,"pgps", "+batchmode=on", "+verbose=0",
524                         name, "-f", NULL);
525                     /*@innerbreak@*/ break;
526                 case PGP_UNKNOWN:
527                 case PGP_NOTDETECTED:
528                     /*@innerbreak@*/ break;
529                 }
530             }
531             rpmError(RPMERR_EXEC, _("Couldn't exec pgp\n"));
532             _exit(RPMERR_EXEC);
533         }   /*@notreached@*/ break;
534         default: /* This case should have been screened out long ago. */
535             rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
536             _exit(RPMERR_SIGGEN);
537             /*@notreached@*/ break;
538         }
539     }
540
541     (void) close(passPhrasePipe[0]);
542     (void) write(passPhrasePipe[1], passPhrase, strlen(passPhrase));
543     (void) write(passPhrasePipe[1], "\n", 1);
544     (void) close(passPhrasePipe[1]);
545
546     (void)waitpid(pid, &status, 0);
547     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
548         return 1;
549     }
550
551     /* passPhrase is good */
552     return 0;
553 }
554
555 char * rpmGetPassPhrase(const char * prompt, const int sigTag)
556 {
557     char *pass;
558     int aok;
559
560     switch (sigTag) {
561     case RPMSIGTAG_GPG:
562       { const char *name = rpmExpand("%{_gpg_name}", NULL);
563         aok = (name && *name != '%');
564         name = _free(name);
565       }
566         if (!aok) {
567             rpmError(RPMERR_SIGGEN,
568                 _("You must set \"%%_gpg_name\" in your macro file\n"));
569             return NULL;
570         }
571         break;
572     case RPMSIGTAG_PGP5:        /* XXX legacy */
573     case RPMSIGTAG_PGP:
574       { const char *name = rpmExpand("%{_pgp_name}", NULL);
575         aok = (name && *name != '%');
576         name = _free(name);
577       }
578         if (!aok) {
579             rpmError(RPMERR_SIGGEN,
580                 _("You must set \"%%_pgp_name\" in your macro file\n"));
581             return NULL;
582         }
583         break;
584     default:
585         /* Currently the calling function (rpm.c:main) is checking this and
586          * doing a better job.  This section should never be accessed.
587          */
588         rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
589         return NULL;
590         /*@notreached@*/ break;
591     }
592
593     pass = /*@-unrecog@*/ getpass( (prompt ? prompt : "") ) /*@=unrecog@*/ ;
594
595     if (checkPassPhrase(pass, sigTag))
596         return NULL;
597
598     return pass;
599 }
600
601 static rpmVerifySignatureReturn
602 verifySizeSignature(/*@unused@*/ const char * fn,
603                 const byte * sig,
604                 /*@unused@*/ int siglen,
605                 const pgpDig dig, /*@out@*/ char * result)
606         /*@modifies *result @*/
607 {
608     char * t = result;
609     int_32 size = *(int_32 *)sig;
610     int res;
611
612     *t = '\0';
613     t = stpcpy(t, "Header+Payload size: ");
614
615     /*@-type@*/
616     if (size != dig->nbytes) {
617         res = RPMSIG_BAD;
618         sprintf(t, "BAD Expected(%d) != (%d)\n", size, dig->nbytes);
619     } else {
620         res = RPMSIG_OK;
621         sprintf(t, "OK (%d)\n", dig->nbytes);
622     }
623     /*@=type@*/
624
625     return res;
626 }
627
628 static rpmVerifySignatureReturn
629 verifyMD5Signature(/*@unused@*/ const char * fn, const byte * sig, int siglen,
630                 const pgpDig dig, /*@out@*/ char * result)
631         /*@modifies *result @*/
632 {
633     char * t = result;
634     byte * md5sum = NULL;
635     size_t md5len = 0;
636     int res;
637
638     /*@-type@*/
639     (void) rpmDigestFinal(rpmDigestDup(dig->md5ctx),
640                 (void **)&md5sum, &md5len, 0);
641     /*@=type@*/
642
643     *t = '\0';
644     t = stpcpy(t, "MD5 digest: ");
645
646     if (md5len != siglen || memcmp(md5sum, sig, md5len)) {
647         res = RPMSIG_BAD;
648         t = stpcpy(t, "BAD Expected(");
649         (void) pgpHexCvt(t, sig, siglen);
650         t += strlen(t);
651         t = stpcpy(t, ") != (");
652     } else {
653         res = RPMSIG_OK;
654         t = stpcpy(t, "OK (");
655     }
656     (void) pgpHexCvt(t, md5sum, md5len);
657     t += strlen(t);
658     t = stpcpy(t, ")\n");
659
660     md5sum = _free(md5sum);
661
662     return res;
663 }
664
665 static rpmVerifySignatureReturn
666 verifyPGPSignature(/*@unused@*/ const char * fn,
667                 /*@unused@*/ const byte * sig,
668                 /*@unused@*/ int siglen,
669                 const pgpDig dig, /*@out@*/ char * result)
670         /*@modifies *result */
671 {
672     /*@unchecked@*/ static const byte * pgppk = NULL;
673     /*@unchecked@*/ static size_t pgppklen = 0;
674     char * t = result;
675     int res, xx;
676
677     *t = '\0';
678     t = stpcpy(t, "V3 RSA/MD5 signature: ");
679
680     /* XXX retrieve by keyid from signature. */
681
682     /*@-globs -internalglobs -mods -modfilesys@*/
683     if (pgppk == NULL) {
684         const char * pkfn = rpmExpand("%{_pgp_pubkey}", NULL);
685         if (pgpReadPkts(pkfn, &pgppk, &pgppklen)) {
686             res = RPMSIG_NOKEY;
687             t = stpcpy( stpcpy( stpcpy(t, "NOKEY ("), pkfn), ")\n");
688             pkfn = _free(pkfn);
689             return res;
690         }
691         rpmMessage(RPMMESS_DEBUG,
692                 "========== PGP RSA/MD5 pubkey %s\n", pkfn);
693         xx = pgpPrtPkts(pgppk, pgppklen, NULL, rpmIsDebug());
694         pkfn = _free(pkfn);
695     }
696
697     /* XXX sanity check on pubkey and signature agreement. */
698
699 #ifdef  NOTYET
700     {   pgpPktSigV3 dsig = dig->signature.v3;
701         pgpPktKeyV3 dpk  = dig->pubkey.v3;
702         
703         if (dsig->pubkey_algo != dpk->pubkey_algo) {
704             res = RPMSIG_NOKEY;
705             t = stpcpy(t, "NOKEY\n");
706             return res;
707         }
708     }
709 #endif
710
711     /*@-nullpass@*/
712     xx = pgpPrtPkts(pgppk, pgppklen, dig, 0);
713     /*@=nullpass@*/
714     /*@=globs =internalglobs =mods =modfilesys@*/
715
716     /*@-type@*/
717     if (!rsavrfy(&dig->rsa_pk, &dig->rsahm, &dig->c)) {
718         res = RPMSIG_BAD;
719         t = stpcpy(t, "BAD\n");
720     } else {
721         res = RPMSIG_OK;
722         t = stpcpy(t, "OK\n");
723     }
724     /*@=type@*/
725
726     return res;
727 }
728
729 static rpmVerifySignatureReturn
730 verifyGPGSignature(/*@unused@*/ const char * fn,
731                 /*@unused@*/ const byte * sig,
732                 /*@unused@*/ int siglen,
733                 const pgpDig dig, /*@out@*/ char * result)
734         /*@modifies *result @*/
735 {
736     /*@unchecked@*/ static const byte * gpgpk = NULL;
737     /*@unchecked@*/ static size_t gpgpklen = 0;
738     char * t = result;
739     int res, xx;
740
741     *t = '\0';
742     t = stpcpy(t, "V3 DSA signature: ");
743
744     /* XXX retrieve by keyid from signature. */
745
746     /*@-globs -internalglobs -mods -modfilesys@*/
747     if (gpgpk == NULL) {
748         const char * pkfn = rpmExpand("%{_gpg_pubkey}", NULL);
749         if (pgpReadPkts(pkfn, &gpgpk, &gpgpklen)) {
750             res = RPMSIG_NOKEY;
751             t = stpcpy( stpcpy( stpcpy(t, "NOKEY ("), pkfn), ")\n");
752             pkfn = _free(pkfn);
753             return res;
754         }
755         rpmMessage(RPMMESS_DEBUG,
756                 "========== GPG DSA pubkey %s\n", pkfn);
757         xx = pgpPrtPkts(gpgpk, gpgpklen, NULL, rpmIsDebug());
758         pkfn = _free(pkfn);
759     }
760
761     /* XXX sanity check on pubkey and signature agreement. */
762
763 #ifdef  NOTYET
764     {   pgpPktSigV3 dsig = dig->signature.v3;
765         pgpPktKeyV4 dpk  = dig->pubkey.v4;
766         
767         if (dsig->pubkey_algo != dpk->pubkey_algo) {
768             res = RPMSIG_NOKEY;
769             t = stpcpy(t, "NOKEY\n");
770             return res;
771         }
772     }
773 #endif
774
775     /*@-nullpass@*/
776     xx = pgpPrtPkts(gpgpk, gpgpklen, dig, 0);
777     /*@=nullpass@*/
778     /*@=globs =internalglobs =mods =modfilesys@*/
779
780     /*@-type@*/
781     if (!dsavrfy(&dig->p, &dig->q, &dig->g, &dig->hm, &dig->y, &dig->r, &dig->s)) {
782         res = RPMSIG_BAD;
783         t = stpcpy(t, "BAD\n");
784     } else {
785         res = RPMSIG_OK;
786         t = stpcpy(t, "OK\n");
787     }
788     /*@=type@*/
789
790     return res;
791 }
792
793 rpmVerifySignatureReturn
794 rpmVerifySignature(const char * fn, int_32 sigTag, const void * sig,
795                 int siglen, const pgpDig dig, char * result)
796 {
797     int res;
798     switch (sigTag) {
799     case RPMSIGTAG_SIZE:
800         res = verifySizeSignature(fn, sig, siglen, dig, result);
801         break;
802     case RPMSIGTAG_MD5:
803         res = verifyMD5Signature(fn, sig, siglen, dig, result);
804         break;
805     case RPMSIGTAG_PGP5:        /* XXX legacy */
806     case RPMSIGTAG_PGP:
807         res = verifyPGPSignature(fn, sig, siglen, dig, result);
808         break;
809     case RPMSIGTAG_GPG:
810         res = verifyGPGSignature(fn, sig, siglen, dig, result);
811         break;
812     case RPMSIGTAG_LEMD5_1:
813     case RPMSIGTAG_LEMD5_2:
814         sprintf(result, "Broken MD5 digest: UNSUPPORTED\n");
815         res = RPMSIG_UNKNOWN;
816         break;
817     default:
818         sprintf(result, "Signature: UNKNOWN(%d)\n", sigTag);
819         res = RPMSIG_UNKNOWN;
820         break;
821     }
822     return res;
823 }