Sync with rpm-4_0 branch.
[platform/upstream/rpm.git] / lib / signature.c
1 /** \ingroup signature
2  * \file lib/signature.c
3  */
4
5 /* signature.c - RPM signature functions */
6
7 /* NOTES
8  *
9  * Things have been cleaned up wrt PGP.  We can now handle
10  * signatures of any length (which means you can use any
11  * size key you like).  We also honor PGPPATH finally.
12  */
13
14 #include "system.h"
15
16 #if HAVE_ASM_BYTEORDER_H
17 #include <asm/byteorder.h>
18 #endif
19
20 #include <rpmlib.h>
21 #include <rpmmacro.h>   /* XXX for rpmGetPath() */
22
23 #include "md5.h"
24 #include "misc.h"       /* XXX for dosetenv() and makeTempFile() */
25 #include "rpmlead.h"
26 #include "signature.h"
27 #include "debug.h"
28
29 /*@access Header@*/             /* XXX compared with NULL */
30 /*@access FD_t@*/               /* XXX compared with NULL */
31
32 typedef unsigned char byte;
33
34 typedef int (*md5func)(const char * fn, /*@out@*/ byte * digest);
35
36 int rpmLookupSignatureType(int action)
37 {
38     static int disabled = 0;
39     int rc = 0;
40
41     switch (action) {
42     case RPMLOOKUPSIG_DISABLE:
43         disabled = -2;
44         break;
45     case RPMLOOKUPSIG_ENABLE:
46         disabled = 0;
47         /*@fallthrough@*/
48     case RPMLOOKUPSIG_QUERY:
49         if (disabled)
50             break;      /* Disabled */
51       { const char *name = rpmExpand("%{_signature}", NULL);
52         if (!(name && *name != '%'))
53             rc = 0;
54         else if (!xstrcasecmp(name, "none"))
55             rc = 0;
56         else if (!xstrcasecmp(name, "pgp"))
57             rc = RPMSIGTAG_PGP;
58         else if (!xstrcasecmp(name, "pgp5"))    /* XXX legacy */
59             rc = RPMSIGTAG_PGP;
60         else if (!xstrcasecmp(name, "gpg"))
61             rc = RPMSIGTAG_GPG;
62         else
63             rc = -1;    /* Invalid %_signature spec in macro file */
64         name = _free(name);
65       } break;
66     }
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         if (!(pgpbin && pgpbin[0] != '%')) {
87           pgpbin = _free(pgpbin);
88           saved_pgp_version = -1;
89           return NULL;
90         }
91         pgpvbin = (char *)alloca(strlen(pgpbin) + sizeof("v"));
92         (void)stpcpy(stpcpy(pgpvbin, pgpbin), "v");
93
94         if (stat(pgpvbin, &st) == 0)
95           saved_pgp_version = PGP_5;
96         else if (stat(pgpbin, &st) == 0)
97           saved_pgp_version = PGP_2;
98         else
99           saved_pgp_version = PGP_NOTDETECTED;
100     }
101
102     if (pgpVer && pgpbin)
103         *pgpVer = saved_pgp_version;
104     return pgpbin;
105 }
106
107 /**
108  * Check package size.
109  * @todo rpmio: use fdSize rather than fstat(2) to get file size.
110  * @param fd                    package file handle
111  * @param siglen                signature header size
112  * @param pad                   signature padding
113  * @param datalen               length of header+payload
114  * @return                      rpmRC return code
115  */
116 static inline rpmRC checkSize(FD_t fd, int siglen, int pad, int datalen)
117         /*@modifies fileSystem @*/
118 {
119     struct stat st;
120     rpmRC rc;
121
122     if (fstat(Fileno(fd), &st))
123         return RPMRC_FAIL;
124
125     if (!S_ISREG(st.st_mode)) {
126         rpmMessage(RPMMESS_DEBUG,
127             _("file is not regular -- skipping size check\n"));
128         return RPMRC_OK;
129     }
130
131     rc = (((sizeof(struct rpmlead) + siglen + pad + datalen) - st.st_size)
132         ? RPMRC_BADSIZE : RPMRC_OK);
133
134     rpmMessage((rc == RPMRC_OK ? RPMMESS_DEBUG : RPMMESS_WARNING),
135         _("Expected size: %12d = lead(%d)+sigs(%d)+pad(%d)+data(%d)\n"),
136                 (int)sizeof(struct rpmlead)+siglen+pad+datalen,
137                 (int)sizeof(struct rpmlead), siglen, pad, datalen);
138     rpmMessage((rc == RPMRC_OK ? RPMMESS_DEBUG : RPMMESS_WARNING),
139         _("  Actual size: %12d\n"), (int)st.st_size);
140
141     return rc;
142 }
143
144 rpmRC rpmReadSignature(FD_t fd, Header * headerp, sigType sig_type)
145 {
146     byte buf[2048];
147     int sigSize, pad;
148     int_32 type, count;
149     int_32 *archSize;
150     Header h = NULL;
151     rpmRC rc = RPMRC_FAIL;              /* assume failure */
152
153     if (headerp)
154         *headerp = NULL;
155
156     switch (sig_type) {
157     case RPMSIGTYPE_NONE:
158         rpmMessage(RPMMESS_DEBUG, _("No signature\n"));
159         rc = RPMRC_OK;
160         break;
161     case RPMSIGTYPE_PGP262_1024:
162         rpmMessage(RPMMESS_DEBUG, _("Old PGP signature\n"));
163         /* These are always 256 bytes */
164         if (timedRead(fd, buf, 256) != 256)
165             break;
166         h = headerNew();
167         (void) headerAddEntry(h, RPMSIGTAG_PGP, RPM_BIN_TYPE, buf, 152);
168         rc = RPMRC_OK;
169         break;
170     case RPMSIGTYPE_MD5:
171     case RPMSIGTYPE_MD5_PGP:
172         rpmError(RPMERR_BADSIGTYPE,
173               _("Old (internal-only) signature!  How did you get that!?\n"));
174         break;
175     case RPMSIGTYPE_HEADERSIG:
176     case RPMSIGTYPE_DISABLE:
177         /* This is a new style signature */
178         h = headerRead(fd, HEADER_MAGIC_YES);
179         if (h == NULL)
180             break;
181
182         rc = RPMRC_OK;
183         sigSize = headerSizeof(h, HEADER_MAGIC_YES);
184
185         /* XXX Legacy headers have a HEADER_IMAGE tag added. */
186         if (headerIsEntry(h, RPMTAG_HEADERIMAGE))
187             sigSize -= (16 + 16);
188
189         pad = (8 - (sigSize % 8)) % 8; /* 8-byte pad */
190         if (sig_type == RPMSIGTYPE_HEADERSIG) {
191             if (! headerGetEntry(h, RPMSIGTAG_SIZE, &type,
192                                 (void **)&archSize, &count))
193                 break;
194             rc = checkSize(fd, sigSize, pad, *archSize);
195         }
196         if (pad && timedRead(fd, buf, pad) != pad)
197             rc = RPMRC_SHORTREAD;
198         break;
199     default:
200         break;
201     }
202
203     if (rc == 0 && headerp)
204         /*@-nullderef@*/
205         *headerp = h;
206         /*@=nullderef@*/
207     else if (h)
208         h = headerFree(h);
209
210     return rc;
211 }
212
213 int rpmWriteSignature(FD_t fd, Header h)
214 {
215     static byte buf[8] = "\000\000\000\000\000\000\000\000";
216     int sigSize, pad;
217     int rc;
218     
219     rc = headerWrite(fd, h, HEADER_MAGIC_YES);
220     if (rc)
221         return rc;
222
223     sigSize = headerSizeof(h, HEADER_MAGIC_YES);
224     pad = (8 - (sigSize % 8)) % 8;
225     if (pad) {
226         if (Fwrite(buf, sizeof(buf[0]), pad, fd) != pad)
227             rc = 1;
228     }
229     rpmMessage(RPMMESS_DEBUG, _("Signature: size(%d)+pad(%d)\n"), sigSize, pad);
230     return rc;
231 }
232
233 Header rpmNewSignature(void)
234 {
235     Header h = headerNew();
236     return h;
237 }
238
239 Header rpmFreeSignature(Header h)
240 {
241     return headerFree(h);
242 }
243
244 static int makePGPSignature(const char * file, /*@out@*/ void ** sig,
245                 /*@out@*/ int_32 * size, /*@null@*/ const char * passPhrase)
246         /*@modifies *sig, *size, fileSystem @*/
247 {
248     char * sigfile = alloca(1024);
249     int pid, status;
250     int inpipe[2];
251     struct stat st;
252
253     (void) stpcpy( stpcpy(sigfile, file), ".sig");
254
255     inpipe[0] = inpipe[1] = 0;
256     (void) pipe(inpipe);
257     
258     if (!(pid = fork())) {
259         const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
260         const char *name = rpmExpand("+myname=\"%{_pgp_name}\"", NULL);
261         const char *path;
262         pgpVersion pgpVer;
263
264         (void) close(STDIN_FILENO);
265         (void) dup2(inpipe[0], 3);
266         (void) close(inpipe[1]);
267
268         (void) dosetenv("PGPPASSFD", "3", 1);
269         if (pgp_path && *pgp_path != '%')
270             (void) dosetenv("PGPPATH", pgp_path, 1);
271
272         /* dosetenv("PGPPASS", passPhrase, 1); */
273
274         if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
275             switch(pgpVer) {
276             case PGP_2:
277                 (void) execlp(path, "pgp", "+batchmode=on", "+verbose=0", "+armor=off",
278                     name, "-sb", file, sigfile, NULL);
279                 break;
280             case PGP_5:
281                 (void) execlp(path,"pgps", "+batchmode=on", "+verbose=0", "+armor=off",
282                     name, "-b", file, "-o", sigfile, NULL);
283                 break;
284             case PGP_UNKNOWN:
285             case PGP_NOTDETECTED:
286                 break;
287             }
288         }
289         rpmError(RPMERR_EXEC, _("Couldn't exec pgp (%s)\n"),
290                         (path ? path : NULL));
291         _exit(RPMERR_EXEC);
292     }
293
294     (void) close(inpipe[0]);
295     if (passPhrase)
296         (void) write(inpipe[1], passPhrase, strlen(passPhrase));
297     (void) write(inpipe[1], "\n", 1);
298     (void) close(inpipe[1]);
299
300     (void)waitpid(pid, &status, 0);
301     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
302         rpmError(RPMERR_SIGGEN, _("pgp failed\n"));
303         return 1;
304     }
305
306     if (stat(sigfile, &st)) {
307         /* PGP failed to write signature */
308         if (sigfile) (void) unlink(sigfile);  /* Just in case */
309         rpmError(RPMERR_SIGGEN, _("pgp failed to write signature\n"));
310         return 1;
311     }
312
313     *size = st.st_size;
314     rpmMessage(RPMMESS_DEBUG, _("PGP sig size: %d\n"), *size);
315     *sig = xmalloc(*size);
316     
317     {   FD_t fd;
318         int rc = 0;
319         fd = Fopen(sigfile, "r.fdio");
320         if (fd != NULL && !Ferror(fd)) {
321             rc = timedRead(fd, *sig, *size);
322             if (sigfile) (void) unlink(sigfile);
323             (void) Fclose(fd);
324         }
325         if (rc != *size) {
326             *sig = _free(*sig);
327             rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
328             return 1;
329         }
330     }
331
332     rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of PGP sig\n"), *size);
333     
334     return 0;
335 }
336
337 /* This is an adaptation of the makePGPSignature function to use GPG instead
338  * of PGP to create signatures.  I think I've made all the changes necessary,
339  * but this could be a good place to start looking if errors in GPG signature
340  * creation crop up.
341  */
342 static int makeGPGSignature(const char * file, /*@out@*/ void ** sig,
343                 /*@out@*/ int_32 * size, /*@null@*/ const char * passPhrase)
344         /*@modifies *sig, *size, fileSystem @*/
345 {
346     char * sigfile = alloca(1024);
347     int pid, status;
348     int inpipe[2];
349     FILE * fpipe;
350     struct stat st;
351
352     (void) stpcpy( stpcpy(sigfile, file), ".sig");
353
354     inpipe[0] = inpipe[1] = 0;
355     (void) pipe(inpipe);
356     
357     if (!(pid = fork())) {
358         const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
359         const char *name = rpmExpand("%{_gpg_name}", NULL);
360
361         (void) close(STDIN_FILENO);
362         (void) dup2(inpipe[0], 3);
363         (void) close(inpipe[1]);
364
365         if (gpg_path && *gpg_path != '%')
366             (void) dosetenv("GNUPGHOME", gpg_path, 1);
367         (void) execlp("gpg", "gpg",
368                "--batch", "--no-verbose", "--no-armor", "--passphrase-fd", "3",
369                "-u", name, "-sbo", sigfile, file,
370                NULL);
371         rpmError(RPMERR_EXEC, _("Couldn't exec gpg\n"));
372         _exit(RPMERR_EXEC);
373     }
374
375     fpipe = fdopen(inpipe[1], "w");
376     (void) close(inpipe[0]);
377     if (fpipe) {
378         fprintf(fpipe, "%s\n", (passPhrase ? passPhrase : ""));
379         (void) fclose(fpipe);
380     }
381
382     (void)waitpid(pid, &status, 0);
383     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
384         rpmError(RPMERR_SIGGEN, _("gpg failed\n"));
385         return 1;
386     }
387
388     if (stat(sigfile, &st)) {
389         /* GPG failed to write signature */
390         if (sigfile) (void) unlink(sigfile);  /* Just in case */
391         rpmError(RPMERR_SIGGEN, _("gpg failed to write signature\n"));
392         return 1;
393     }
394
395     *size = st.st_size;
396     rpmMessage(RPMMESS_DEBUG, _("GPG sig size: %d\n"), *size);
397     *sig = xmalloc(*size);
398     
399     {   FD_t fd;
400         int rc = 0;
401         fd = Fopen(sigfile, "r.fdio");
402         if (fd != NULL && !Ferror(fd)) {
403             rc = timedRead(fd, *sig, *size);
404             if (sigfile) (void) unlink(sigfile);
405             (void) Fclose(fd);
406         }
407         if (rc != *size) {
408             *sig = _free(*sig);
409             rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
410             return 1;
411         }
412     }
413
414     rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of GPG sig\n"), *size);
415     
416     return 0;
417 }
418
419 int rpmAddSignature(Header h, const char * file, int_32 sigTag,
420                 const char *passPhrase)
421 {
422     struct stat st;
423     int_32 size;
424     byte buf[16];
425     void *sig;
426     int ret = -1;
427     
428     switch (sigTag) {
429     case RPMSIGTAG_SIZE:
430         (void) stat(file, &st);
431         size = st.st_size;
432         ret = 0;
433         (void) headerAddEntry(h, RPMSIGTAG_SIZE, RPM_INT32_TYPE, &size, 1);
434         break;
435     case RPMSIGTAG_MD5:
436         ret = mdbinfile(file, buf);
437         if (ret == 0)
438             (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, buf, 16);
439         break;
440     case RPMSIGTAG_PGP5:        /* XXX legacy */
441     case RPMSIGTAG_PGP:
442         rpmMessage(RPMMESS_VERBOSE, _("Generating signature using PGP.\n"));
443         ret = makePGPSignature(file, &sig, &size, passPhrase);
444         if (ret == 0)
445             (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, sig, size);
446         break;
447     case RPMSIGTAG_GPG:
448         rpmMessage(RPMMESS_VERBOSE, _("Generating signature using GPG.\n"));
449         ret = makeGPGSignature(file, &sig, &size, passPhrase);
450         if (ret == 0)
451             (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, sig, size);
452         break;
453     }
454
455     return ret;
456 }
457
458 static rpmVerifySignatureReturn
459 verifySizeSignature(const char * datafile, int_32 size, char * result)
460         /*@modifies *result, fileSystem @*/
461 {
462     struct stat st;
463
464     (void) stat(datafile, &st);
465     if (size != st.st_size) {
466         sprintf(result, "Header+Archive size mismatch.\n"
467                 "Expected %d, saw %d.\n",
468                 size, (int)st.st_size);
469         return RPMSIG_BAD;
470     }
471
472     sprintf(result, "Header+Archive size OK: %d bytes\n", size);
473     return RPMSIG_OK;
474 }
475
476 #define X(_x)   (unsigned)((_x) & 0xff)
477
478 static rpmVerifySignatureReturn
479 verifyMD5Signature(const char * datafile, const byte * sig, 
480                               char * result, md5func fn)
481         /*@modifies *result, fileSystem @*/
482 {
483     byte md5sum[16];
484
485     (void) fn(datafile, md5sum);
486     if (memcmp(md5sum, sig, 16)) {
487         sprintf(result, "MD5 sum mismatch\n"
488                 "Expected: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
489                 "%02x%02x%02x%02x%02x\n"
490                 "Saw     : %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
491                 "%02x%02x%02x%02x%02x\n",
492                 X(sig[0]),  X(sig[1]),  X(sig[2]),  X(sig[3]),
493                 X(sig[4]),  X(sig[5]),  X(sig[6]),  X(sig[7]),
494                 X(sig[8]),  X(sig[9]),  X(sig[10]), X(sig[11]),
495                 X(sig[12]), X(sig[13]), X(sig[14]), X(sig[15]),
496                 X(md5sum[0]),  X(md5sum[1]),  X(md5sum[2]),  X(md5sum[3]),
497                 X(md5sum[4]),  X(md5sum[5]),  X(md5sum[6]),  X(md5sum[7]),
498                 X(md5sum[8]),  X(md5sum[9]),  X(md5sum[10]), X(md5sum[11]),
499                 X(md5sum[12]), X(md5sum[13]), X(md5sum[14]), X(md5sum[15]) );
500         return RPMSIG_BAD;
501     }
502
503     sprintf(result, "MD5 sum OK: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
504                     "%02x%02x%02x%02x%02x\n",
505             X(md5sum[0]),  X(md5sum[1]),  X(md5sum[2]),  X(md5sum[3]),
506             X(md5sum[4]),  X(md5sum[5]),  X(md5sum[6]),  X(md5sum[7]),
507             X(md5sum[8]),  X(md5sum[9]),  X(md5sum[10]), X(md5sum[11]),
508             X(md5sum[12]), X(md5sum[13]), X(md5sum[14]), X(md5sum[15]) );
509
510     return RPMSIG_OK;
511 }
512
513 static rpmVerifySignatureReturn
514 verifyPGPSignature(const char * datafile, const void * sig, int count,
515                 char * result)
516         /*@modifies *result, fileSystem @*/
517 {
518     int pid, status, outpipe[2];
519 /*@only@*/ /*@null@*/ const char * sigfile = NULL;
520     byte buf[BUFSIZ];
521     FILE *file;
522     int res = RPMSIG_OK;
523     const char *path;
524     pgpVersion pgpVer;
525
526     /* What version do we have? */
527     if ((path = rpmDetectPGPVersion(&pgpVer)) == NULL) {
528         errno = ENOENT;
529         rpmError(RPMERR_EXEC, 
530                  _("Could not run pgp.  Use --nopgp to skip PGP checks.\n"));
531         _exit(RPMERR_EXEC);
532     }
533
534     /*
535      * Sad but true: pgp-5.0 returns exit value of 0 on bad signature.
536      * Instead we have to use the text output to detect a bad signature.
537      */
538     if (pgpVer == PGP_5)
539         res = RPMSIG_BAD;
540
541     /* Write out the signature */
542 #ifdef  DYING
543   { const char *tmppath = rpmGetPath("%{_tmppath}", NULL);
544     sigfile = tempnam(tmppath, "rpmsig");
545     tmppath = _free(tmppath);
546   }
547     sfd = Fopen(sigfile, "w.fdio");
548     if (sfd != NULL && !Ferror(sfd)) {
549         (void) Fwrite(sig, sizeof(char), count, sfd);
550         (void) Fclose(sfd);
551     }
552 #else
553     {   FD_t sfd;
554         if (!makeTempFile(NULL, &sigfile, &sfd)) {
555             (void) Fwrite(sig, sizeof(char), count, sfd);
556             (void) Fclose(sfd);
557             sfd = NULL;
558         }
559     }
560 #endif
561     if (sigfile == NULL)
562         return RPMSIG_BAD;
563
564     /* Now run PGP */
565     outpipe[0] = outpipe[1] = 0;
566     (void) pipe(outpipe);
567
568     if (!(pid = fork())) {
569         const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
570
571         (void) close(outpipe[0]);
572         (void) close(STDOUT_FILENO);    /* XXX unnecessary */
573         (void) dup2(outpipe[1], STDOUT_FILENO);
574
575         if (pgp_path && *pgp_path != '%')
576             (void) dosetenv("PGPPATH", pgp_path, 1);
577
578         switch (pgpVer) {
579         case PGP_5:
580             /* Some output (in particular "This signature applies to */
581             /* another message") is _always_ written to stderr; we   */
582             /* want to catch that output, so dup stdout to stderr:   */
583         {   int save_stderr = dup(2);
584             (void) dup2(1, 2);
585             (void) execlp(path, "pgpv", "+batchmode=on", "+verbose=0",
586                    /* Write "Good signature..." to stdout: */
587                    "+OutputInformationFD=1",
588                    /* Write "WARNING: ... is not trusted to... to stdout: */
589                    "+OutputWarningFD=1",
590                    sigfile, "-o", datafile, NULL);
591             /* Restore stderr so we can print the error message below. */
592             (void) dup2(save_stderr, 2);
593             (void) close(save_stderr);
594         }   break;
595         case PGP_2:
596             (void) execlp(path, "pgp", "+batchmode=on", "+verbose=0",
597                    sigfile, datafile, NULL);
598             break;
599         case PGP_UNKNOWN:
600         case PGP_NOTDETECTED:
601             break;
602         }
603
604         rpmError(RPMERR_EXEC, 
605                  _("Could not run pgp.  Use --nopgp to skip PGP checks.\n"));
606         _exit(RPMERR_EXEC);
607     }
608
609     (void) close(outpipe[1]);
610     file = fdopen(outpipe[0], "r");
611     result[0] = '\0';
612     if (file) {
613         while (fgets(buf, 1024, file)) {
614             if (strncmp("File '", buf, 6) &&
615                 strncmp("Text is assu", buf, 12) &&
616                 strncmp("This signature applies to another message", buf, 41) &&
617                 buf[0] != '\n') {
618                 strcat(result, buf);
619             }
620             if (!strncmp("WARNING: Can't find the right public key", buf, 40))
621                 res = RPMSIG_NOKEY;
622             else if (!strncmp("Signature by unknown keyid:", buf, 27))
623                 res = RPMSIG_NOKEY;
624             else if (!strncmp("WARNING: The signing key is not trusted", buf, 39))
625                 res = RPMSIG_NOTTRUSTED;
626             else if (!strncmp("Good signature", buf, 14))
627                 res = RPMSIG_OK;
628         }
629         (void) fclose(file);
630     }
631
632     (void) waitpid(pid, &status, 0);
633     if (sigfile) (void) unlink(sigfile);
634     sigfile = _free(sigfile);
635     if (!res && (!WIFEXITED(status) || WEXITSTATUS(status))) {
636         res = RPMSIG_BAD;
637     }
638     
639     return res;
640 }
641
642 static rpmVerifySignatureReturn
643 verifyGPGSignature(const char * datafile, const void * sig, int count,
644                 char * result)
645         /*@modifies *result, fileSystem @*/
646 {
647     int pid, status, outpipe[2];
648 /*@only@*/ /*@null@*/ const char * sigfile = NULL;
649     byte buf[BUFSIZ];
650     FILE *file;
651     int res = RPMSIG_OK;
652   
653     /* Write out the signature */
654 #ifdef  DYING
655   { const char *tmppath = rpmGetPath("%{_tmppath}", NULL);
656     sigfile = tempnam(tmppath, "rpmsig");
657     tmppath = _free(tmppath);
658   }
659     sfd = Fopen(sigfile, "w.fdio");
660     if (sfd != NULL && !Ferror(sfd)) {
661         (void) Fwrite(sig, sizeof(char), count, sfd);
662         (void) Fclose(sfd);
663     }
664 #else
665     {   FD_t sfd;
666         if (!makeTempFile(NULL, &sigfile, &sfd)) {
667             (void) Fwrite(sig, sizeof(char), count, sfd);
668             (void) Fclose(sfd);
669             sfd = NULL;
670         }
671     }
672 #endif
673     if (sigfile == NULL)
674         return RPMSIG_BAD;
675
676     /* Now run GPG */
677     outpipe[0] = outpipe[1] = 0;
678     (void) pipe(outpipe);
679
680     if (!(pid = fork())) {
681         const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
682
683         (void) close(outpipe[0]);
684         /* gpg version 0.9 sends its output to stderr. */
685         (void) dup2(outpipe[1], STDERR_FILENO);
686
687         if (gpg_path && *gpg_path != '%')
688             (void) dosetenv("GNUPGHOME", gpg_path, 1);
689
690         (void) execlp("gpg", "gpg",
691                "--batch", "--no-verbose", 
692                "--verify", sigfile, datafile,
693                NULL);
694         rpmError(RPMERR_EXEC, 
695                  _("Could not run gpg.  Use --nogpg to skip GPG checks.\n"));
696         _exit(RPMERR_EXEC);
697     }
698
699     (void) close(outpipe[1]);
700     file = fdopen(outpipe[0], "r");
701     result[0] = '\0';
702     if (file) {
703         while (fgets(buf, 1024, file)) {
704             strcat(result, buf);
705             if (!xstrncasecmp("gpg: Can't check signature: Public key not found", buf, 48)) {
706                 res = RPMSIG_NOKEY;
707             }
708         }
709         (void) fclose(file);
710     }
711   
712     (void) waitpid(pid, &status, 0);
713     if (sigfile) (void) unlink(sigfile);
714     sigfile = _free(sigfile);
715     if (!res && (!WIFEXITED(status) || WEXITSTATUS(status))) {
716         res = RPMSIG_BAD;
717     }
718     
719     return res;
720 }
721
722 static int checkPassPhrase(const char * passPhrase, const int sigTag)
723         /*@modifies fileSystem @*/
724 {
725     int passPhrasePipe[2];
726     int pid, status;
727     int fd;
728
729     passPhrasePipe[0] = passPhrasePipe[1] = 0;
730     (void) pipe(passPhrasePipe);
731     if (!(pid = fork())) {
732         (void) close(STDIN_FILENO);
733         (void) close(STDOUT_FILENO);
734         (void) close(passPhrasePipe[1]);
735         if (! rpmIsVerbose()) {
736             (void) close(STDERR_FILENO);
737         }
738         if ((fd = open("/dev/null", O_RDONLY)) != STDIN_FILENO) {
739             (void) dup2(fd, STDIN_FILENO);
740             (void) close(fd);
741         }
742         if ((fd = open("/dev/null", O_WRONLY)) != STDOUT_FILENO) {
743             (void) dup2(fd, STDOUT_FILENO);
744             (void) close(fd);
745         }
746         (void) dup2(passPhrasePipe[0], 3);
747
748         switch (sigTag) {
749         case RPMSIGTAG_GPG:
750         {   const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
751             const char *name = rpmExpand("%{_gpg_name}", NULL);
752             if (gpg_path && *gpg_path != '%')
753                 (void) dosetenv("GNUPGHOME", gpg_path, 1);
754             (void) execlp("gpg", "gpg",
755                    "--batch", "--no-verbose", "--passphrase-fd", "3",
756                    "-u", name, "-so", "-",
757                    NULL);
758             rpmError(RPMERR_EXEC, _("Couldn't exec gpg\n"));
759             _exit(RPMERR_EXEC);
760         }   /*@notreached@*/ break;
761         case RPMSIGTAG_PGP5:    /* XXX legacy */
762         case RPMSIGTAG_PGP:
763         {   const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
764             const char *name = rpmExpand("+myname=\"%{_pgp_name}\"", NULL);
765             const char *path;
766             pgpVersion pgpVer;
767
768             (void) dosetenv("PGPPASSFD", "3", 1);
769             if (pgp_path && *pgp_path != '%')
770                 (void) dosetenv("PGPPATH", pgp_path, 1);
771
772             if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
773                 switch(pgpVer) {
774                 case PGP_2:
775                     (void) execlp(path, "pgp", "+batchmode=on", "+verbose=0",
776                         name, "-sf", NULL);
777                     break;
778                 case PGP_5:     /* XXX legacy */
779                     (void) execlp(path,"pgps", "+batchmode=on", "+verbose=0",
780                         name, "-f", NULL);
781                     break;
782                 case PGP_UNKNOWN:
783                 case PGP_NOTDETECTED:
784                     break;
785                 }
786             }
787             rpmError(RPMERR_EXEC, _("Couldn't exec pgp\n"));
788             _exit(RPMERR_EXEC);
789         }   /*@notreached@*/ break;
790         default: /* This case should have been screened out long ago. */
791             rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
792             _exit(RPMERR_SIGGEN);
793             /*@notreached@*/ break;
794         }
795     }
796
797     (void) close(passPhrasePipe[0]);
798     (void) write(passPhrasePipe[1], passPhrase, strlen(passPhrase));
799     (void) write(passPhrasePipe[1], "\n", 1);
800     (void) close(passPhrasePipe[1]);
801
802     (void)waitpid(pid, &status, 0);
803     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
804         return 1;
805     }
806
807     /* passPhrase is good */
808     return 0;
809 }
810
811 char * rpmGetPassPhrase(const char * prompt, const int sigTag)
812 {
813     char *pass;
814     int aok;
815
816     switch (sigTag) {
817     case RPMSIGTAG_GPG:
818       { const char *name = rpmExpand("%{_gpg_name}", NULL);
819         aok = (name && *name != '%');
820         name = _free(name);
821       }
822         if (!aok) {
823             rpmError(RPMERR_SIGGEN,
824                 _("You must set \"%%_gpg_name\" in your macro file\n"));
825             return NULL;
826         }
827         break;
828     case RPMSIGTAG_PGP5:        /* XXX legacy */
829     case RPMSIGTAG_PGP: 
830       { const char *name = rpmExpand("%{_pgp_name}", NULL);
831         aok = (name && *name != '%');
832         name = _free(name);
833       }
834         if (!aok) {
835             rpmError(RPMERR_SIGGEN,
836                 _("You must set \"%%_pgp_name\" in your macro file\n"));
837             return NULL;
838         }
839         break;
840     default:
841         /* Currently the calling function (rpm.c:main) is checking this and
842          * doing a better job.  This section should never be accessed.
843          */
844         rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
845         return NULL;
846         /*@notreached@*/ break;
847     }
848
849     pass = /*@-unrecog@*/ getpass( (prompt ? prompt : "") ) /*@=unrecog@*/ ;
850
851     if (checkPassPhrase(pass, sigTag))
852         return NULL;
853
854     return pass;
855 }
856
857 rpmVerifySignatureReturn
858 rpmVerifySignature(const char * file, int_32 sigTag, const void * sig,
859                 int count, char * result)
860 {
861     switch (sigTag) {
862     case RPMSIGTAG_SIZE:
863         return verifySizeSignature(file, *(int_32 *)sig, result);
864         /*@notreached@*/ break;
865     case RPMSIGTAG_MD5:
866         return verifyMD5Signature(file, sig, result, mdbinfile);
867         /*@notreached@*/ break;
868     case RPMSIGTAG_LEMD5_1:
869     case RPMSIGTAG_LEMD5_2:
870         return verifyMD5Signature(file, sig, result, mdbinfileBroken);
871         /*@notreached@*/ break;
872     case RPMSIGTAG_PGP5:        /* XXX legacy */
873     case RPMSIGTAG_PGP:
874         return verifyPGPSignature(file, sig, count, result);
875         /*@notreached@*/ break;
876     case RPMSIGTAG_GPG:
877         return verifyGPGSignature(file, sig, count, result);
878         /*@notreached@*/ break;
879     default:
880         sprintf(result, "Do not know how to verify sig type %d\n", sigTag);
881         return RPMSIG_UNKNOWN;
882     }
883     /*@notreached@*/
884     return RPMSIG_OK;
885 }