Move to lclint-3.0.0.15, revisit and clean up annotations.
[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     buf[0] = 0;
157     switch (sig_type) {
158     case RPMSIGTYPE_NONE:
159         rpmMessage(RPMMESS_DEBUG, _("No signature\n"));
160         rc = RPMRC_OK;
161         break;
162     case RPMSIGTYPE_PGP262_1024:
163         rpmMessage(RPMMESS_DEBUG, _("Old PGP signature\n"));
164         /* These are always 256 bytes */
165         if (timedRead(fd, buf, 256) != 256)
166             break;
167         h = headerNew();
168         (void) headerAddEntry(h, RPMSIGTAG_PGP, RPM_BIN_TYPE, buf, 152);
169         rc = RPMRC_OK;
170         break;
171     case RPMSIGTYPE_MD5:
172     case RPMSIGTYPE_MD5_PGP:
173         rpmError(RPMERR_BADSIGTYPE,
174               _("Old (internal-only) signature!  How did you get that!?\n"));
175         break;
176     case RPMSIGTYPE_HEADERSIG:
177     case RPMSIGTYPE_DISABLE:
178         /* This is a new style signature */
179         h = headerRead(fd, HEADER_MAGIC_YES);
180         if (h == NULL)
181             break;
182
183         rc = RPMRC_OK;
184         sigSize = headerSizeof(h, HEADER_MAGIC_YES);
185
186         /* XXX Legacy headers have a HEADER_IMAGE tag added. */
187         if (headerIsEntry(h, RPMTAG_HEADERIMAGE))
188             sigSize -= (16 + 16);
189
190         pad = (8 - (sigSize % 8)) % 8; /* 8-byte pad */
191         if (sig_type == RPMSIGTYPE_HEADERSIG) {
192             if (! headerGetEntry(h, RPMSIGTAG_SIZE, &type,
193                                 (void **)&archSize, &count))
194                 break;
195             rc = checkSize(fd, sigSize, pad, *archSize);
196         }
197         if (pad && timedRead(fd, buf, pad) != pad)
198             rc = RPMRC_SHORTREAD;
199         break;
200     default:
201         break;
202     }
203
204     if (headerp && rc == 0)
205         *headerp = h;
206     else
207         h = headerFree(h);
208
209     return rc;
210 }
211
212 int rpmWriteSignature(FD_t fd, Header h)
213 {
214     static byte buf[8] = "\000\000\000\000\000\000\000\000";
215     int sigSize, pad;
216     int rc;
217     
218     rc = headerWrite(fd, h, HEADER_MAGIC_YES);
219     if (rc)
220         return rc;
221
222     sigSize = headerSizeof(h, HEADER_MAGIC_YES);
223     pad = (8 - (sigSize % 8)) % 8;
224     if (pad) {
225         if (Fwrite(buf, sizeof(buf[0]), pad, fd) != pad)
226             rc = 1;
227     }
228     rpmMessage(RPMMESS_DEBUG, _("Signature: size(%d)+pad(%d)\n"), sigSize, pad);
229     return rc;
230 }
231
232 Header rpmNewSignature(void)
233 {
234     Header h = headerNew();
235     return h;
236 }
237
238 Header rpmFreeSignature(Header h)
239 {
240     return headerFree(h);
241 }
242
243 static int makePGPSignature(const char * file, /*@out@*/ void ** sig,
244                 /*@out@*/ int_32 * size, /*@null@*/ const char * passPhrase)
245         /*@modifies *sig, *size, fileSystem @*/
246 {
247     char * sigfile = alloca(1024);
248     int pid, status;
249     int inpipe[2];
250     struct stat st;
251
252     (void) stpcpy( stpcpy(sigfile, file), ".sig");
253
254     inpipe[0] = inpipe[1] = 0;
255     (void) pipe(inpipe);
256     
257     if (!(pid = fork())) {
258         const char *pgp_path = rpmExpand("%{_pgp_path}", NULL);
259         const char *name = rpmExpand("+myname=\"%{_pgp_name}\"", NULL);
260         const char *path;
261         pgpVersion pgpVer;
262
263         (void) close(STDIN_FILENO);
264         (void) dup2(inpipe[0], 3);
265         (void) close(inpipe[1]);
266
267         (void) dosetenv("PGPPASSFD", "3", 1);
268         if (pgp_path && *pgp_path != '%')
269             (void) dosetenv("PGPPATH", pgp_path, 1);
270
271         /* dosetenv("PGPPASS", passPhrase, 1); */
272
273         if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
274             switch(pgpVer) {
275             case PGP_2:
276                 (void) execlp(path, "pgp", "+batchmode=on", "+verbose=0", "+armor=off",
277                     name, "-sb", file, sigfile, NULL);
278                 break;
279             case PGP_5:
280                 (void) execlp(path,"pgps", "+batchmode=on", "+verbose=0", "+armor=off",
281                     name, "-b", file, "-o", sigfile, NULL);
282                 break;
283             case PGP_UNKNOWN:
284             case PGP_NOTDETECTED:
285                 break;
286             }
287         }
288         rpmError(RPMERR_EXEC, _("Couldn't exec pgp (%s)\n"),
289                         (path ? path : NULL));
290         _exit(RPMERR_EXEC);
291     }
292
293     (void) close(inpipe[0]);
294     if (passPhrase)
295         (void) write(inpipe[1], passPhrase, strlen(passPhrase));
296     (void) write(inpipe[1], "\n", 1);
297     (void) close(inpipe[1]);
298
299     (void)waitpid(pid, &status, 0);
300     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
301         rpmError(RPMERR_SIGGEN, _("pgp failed\n"));
302         return 1;
303     }
304
305     if (stat(sigfile, &st)) {
306         /* PGP failed to write signature */
307         if (sigfile) (void) unlink(sigfile);  /* Just in case */
308         rpmError(RPMERR_SIGGEN, _("pgp failed to write signature\n"));
309         return 1;
310     }
311
312     *size = st.st_size;
313     rpmMessage(RPMMESS_DEBUG, _("PGP sig size: %d\n"), *size);
314     *sig = xmalloc(*size);
315     
316     {   FD_t fd;
317         int rc = 0;
318         fd = Fopen(sigfile, "r.fdio");
319         if (fd != NULL && !Ferror(fd)) {
320             rc = timedRead(fd, *sig, *size);
321             if (sigfile) (void) unlink(sigfile);
322             (void) Fclose(fd);
323         }
324         if (rc != *size) {
325             *sig = _free(*sig);
326             rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
327             return 1;
328         }
329     }
330
331     rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of PGP sig\n"), *size);
332     
333     return 0;
334 }
335
336 /* This is an adaptation of the makePGPSignature function to use GPG instead
337  * of PGP to create signatures.  I think I've made all the changes necessary,
338  * but this could be a good place to start looking if errors in GPG signature
339  * creation crop up.
340  */
341 static int makeGPGSignature(const char * file, /*@out@*/ void ** sig,
342                 /*@out@*/ int_32 * size, /*@null@*/ const char * passPhrase)
343         /*@modifies *sig, *size, fileSystem @*/
344 {
345     char * sigfile = alloca(1024);
346     int pid, status;
347     int inpipe[2];
348     FILE * fpipe;
349     struct stat st;
350
351     (void) stpcpy( stpcpy(sigfile, file), ".sig");
352
353     inpipe[0] = inpipe[1] = 0;
354     (void) pipe(inpipe);
355     
356     if (!(pid = fork())) {
357         const char *gpg_path = rpmExpand("%{_gpg_path}", NULL);
358         const char *name = rpmExpand("%{_gpg_name}", NULL);
359
360         (void) close(STDIN_FILENO);
361         (void) dup2(inpipe[0], 3);
362         (void) close(inpipe[1]);
363
364         if (gpg_path && *gpg_path != '%')
365             (void) dosetenv("GNUPGHOME", gpg_path, 1);
366         (void) execlp("gpg", "gpg",
367                "--batch", "--no-verbose", "--no-armor", "--passphrase-fd", "3",
368                "-u", name, "-sbo", sigfile, file,
369                NULL);
370         rpmError(RPMERR_EXEC, _("Couldn't exec gpg\n"));
371         _exit(RPMERR_EXEC);
372     }
373
374     fpipe = fdopen(inpipe[1], "w");
375     (void) close(inpipe[0]);
376     if (fpipe) {
377         fprintf(fpipe, "%s\n", (passPhrase ? passPhrase : ""));
378         (void) fclose(fpipe);
379     }
380
381     (void)waitpid(pid, &status, 0);
382     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
383         rpmError(RPMERR_SIGGEN, _("gpg failed\n"));
384         return 1;
385     }
386
387     if (stat(sigfile, &st)) {
388         /* GPG failed to write signature */
389         if (sigfile) (void) unlink(sigfile);  /* Just in case */
390         rpmError(RPMERR_SIGGEN, _("gpg failed to write signature\n"));
391         return 1;
392     }
393
394     *size = st.st_size;
395     rpmMessage(RPMMESS_DEBUG, _("GPG sig size: %d\n"), *size);
396     *sig = xmalloc(*size);
397     
398     {   FD_t fd;
399         int rc = 0;
400         fd = Fopen(sigfile, "r.fdio");
401         if (fd != NULL && !Ferror(fd)) {
402             rc = timedRead(fd, *sig, *size);
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 rpmVerifySignatureReturn
458 verifySizeSignature(const char * datafile, int_32 size, /*@out@*/ char * result)
459         /*@modifies *result, fileSystem @*/
460 {
461     struct stat st;
462
463     (void) stat(datafile, &st);
464     if (size != st.st_size) {
465         sprintf(result, "Header+Archive size mismatch.\n"
466                 "Expected %d, saw %d.\n",
467                 size, (int)st.st_size);
468         return RPMSIG_BAD;
469     }
470
471     sprintf(result, "Header+Archive size OK: %d bytes\n", size);
472     return RPMSIG_OK;
473 }
474
475 #define X(_x)   (unsigned)((_x) & 0xff)
476
477 static rpmVerifySignatureReturn
478 verifyMD5Signature(const char * datafile, const byte * sig, 
479                               /*@out@*/ char * result, md5func fn)
480         /*@modifies *result, fileSystem @*/
481 {
482     byte md5sum[16];
483
484     memset(md5sum, 0, sizeof(md5sum));
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                 /*@out@*/ 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                 /*@out@*/ 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 }