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