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