Main Page   Modules   Data Structures   File List   Data Fields   Globals   Related Pages  

lib/signature.c

Go to the documentation of this file.
00001 
00005 /* signature.c - RPM signature functions */
00006 
00007 /* NOTES
00008  *
00009  * Things have been cleaned up wrt PGP.  We can now handle
00010  * signatures of any length (which means you can use any
00011  * size key you like).  We also honor PGPPATH finally.
00012  */
00013 
00014 #include "system.h"
00015 
00016 #include "rpmio_internal.h"
00017 #include "rpmlib.h"
00018 #include "rpmmacro.h"   /* XXX for rpmGetPath() */
00019 
00020 #include "md5.h"
00021 #include "misc.h"       /* XXX for dosetenv() and makeTempFile() */
00022 #include "rpmlead.h"
00023 #include "signature.h"
00024 #include "debug.h"
00025 
00026 /*@access Header@*/             /* XXX compared with NULL */
00027 /*@access FD_t@*/               /* XXX compared with NULL */
00028 
00029 typedef int (*md5func)(const char * fn, /*@out@*/ byte * digest);
00030 
00031 int rpmLookupSignatureType(int action)
00032 {
00033     static int disabled = 0;
00034     int rc = 0;
00035 
00036     switch (action) {
00037     case RPMLOOKUPSIG_DISABLE:
00038         disabled = -2;
00039         break;
00040     case RPMLOOKUPSIG_ENABLE:
00041         disabled = 0;
00042         /*@fallthrough@*/
00043     case RPMLOOKUPSIG_QUERY:
00044         if (disabled)
00045             break;      /* Disabled */
00046       { const char *name = rpmExpand("%{?_signature}", NULL);
00047         if (!(name && *name != '\0'))
00048             rc = 0;
00049         else if (!xstrcasecmp(name, "none"))
00050             rc = 0;
00051         else if (!xstrcasecmp(name, "pgp"))
00052             rc = RPMSIGTAG_PGP;
00053         else if (!xstrcasecmp(name, "pgp5"))    /* XXX legacy */
00054             rc = RPMSIGTAG_PGP;
00055         else if (!xstrcasecmp(name, "gpg"))
00056             rc = RPMSIGTAG_GPG;
00057         else
00058             rc = -1;    /* Invalid %_signature spec in macro file */
00059         name = _free(name);
00060       } break;
00061     }
00062     return rc;
00063 }
00064 
00065 /* rpmDetectPGPVersion() returns the absolute path to the "pgp"  */
00066 /* executable of the requested version, or NULL when none found. */
00067 
00068 const char * rpmDetectPGPVersion(pgpVersion * pgpVer)
00069 {
00070     /* Actually this should support having more then one pgp version. */
00071     /* At the moment only one version is possible since we only       */
00072     /* have one %_pgpbin and one %_pgp_path.                          */
00073 
00074     static pgpVersion saved_pgp_version = PGP_UNKNOWN;
00075     const char *pgpbin = rpmGetPath("%{?_pgpbin}", NULL);
00076 
00077     if (saved_pgp_version == PGP_UNKNOWN) {
00078         char *pgpvbin;
00079         struct stat st;
00080 
00081         if (!(pgpbin && pgpbin[0] != '\0')) {
00082           pgpbin = _free(pgpbin);
00083           saved_pgp_version = -1;
00084           return NULL;
00085         }
00086         pgpvbin = (char *)alloca(strlen(pgpbin) + sizeof("v"));
00087         (void)stpcpy(stpcpy(pgpvbin, pgpbin), "v");
00088 
00089         if (stat(pgpvbin, &st) == 0)
00090           saved_pgp_version = PGP_5;
00091         else if (stat(pgpbin, &st) == 0)
00092           saved_pgp_version = PGP_2;
00093         else
00094           saved_pgp_version = PGP_NOTDETECTED;
00095     }
00096 
00097     if (pgpVer && pgpbin)
00098         *pgpVer = saved_pgp_version;
00099     return pgpbin;
00100 }
00101 
00111 static inline rpmRC checkSize(FD_t fd, int siglen, int pad, int datalen)
00112         /*@globals fileSystem @*/
00113         /*@modifies fileSystem @*/
00114 {
00115     struct stat st;
00116     int delta;
00117     rpmRC rc;
00118 
00119     if (fstat(Fileno(fd), &st))
00120         return RPMRC_FAIL;
00121 
00122     if (!S_ISREG(st.st_mode)) {
00123         rpmMessage(RPMMESS_DEBUG,
00124             _("file is not regular -- skipping size check\n"));
00125         return RPMRC_OK;
00126     }
00127 
00128     delta = (sizeof(struct rpmlead) + siglen + pad + datalen) - st.st_size;
00129     switch (delta) {
00130     case -32:   /* XXX rpm-4.0 packages */
00131     case 32:    /* XXX Legacy headers have a HEADER_IMAGE tag added. */
00132     case 0:
00133         rc = RPMRC_OK;
00134         break;
00135     default:
00136         rc = RPMRC_BADSIZE;
00137         break;
00138     }
00139 
00140     rpmMessage((rc == RPMRC_OK ? RPMMESS_DEBUG : RPMMESS_WARNING),
00141         _("Expected size: %12d = lead(%d)+sigs(%d)+pad(%d)+data(%d)\n"),
00142                 (int)sizeof(struct rpmlead)+siglen+pad+datalen,
00143                 (int)sizeof(struct rpmlead), siglen, pad, datalen);
00144     rpmMessage((rc == RPMRC_OK ? RPMMESS_DEBUG : RPMMESS_WARNING),
00145         _("  Actual size: %12d\n"), (int)st.st_size);
00146 
00147     return rc;
00148 }
00149 
00150 rpmRC rpmReadSignature(FD_t fd, Header * headerp, sigType sig_type)
00151 {
00152     byte buf[2048];
00153     int sigSize, pad;
00154     int_32 type, count;
00155     int_32 *archSize;
00156     Header h = NULL;
00157     rpmRC rc = RPMRC_FAIL;              /* assume failure */
00158 
00159     if (headerp)
00160         *headerp = NULL;
00161 
00162     buf[0] = 0;
00163     switch (sig_type) {
00164     case RPMSIGTYPE_NONE:
00165         rpmMessage(RPMMESS_DEBUG, _("No signature\n"));
00166         rc = RPMRC_OK;
00167         break;
00168     case RPMSIGTYPE_PGP262_1024:
00169         rpmMessage(RPMMESS_DEBUG, _("Old PGP signature\n"));
00170         /* These are always 256 bytes */
00171         if (timedRead(fd, buf, 256) != 256)
00172             break;
00173         h = headerNew();
00174         (void) headerAddEntry(h, RPMSIGTAG_PGP, RPM_BIN_TYPE, buf, 152);
00175         rc = RPMRC_OK;
00176         break;
00177     case RPMSIGTYPE_MD5:
00178     case RPMSIGTYPE_MD5_PGP:
00179         rpmError(RPMERR_BADSIGTYPE,
00180               _("Old (internal-only) signature!  How did you get that!?\n"));
00181         break;
00182     case RPMSIGTYPE_HEADERSIG:
00183     case RPMSIGTYPE_DISABLE:
00184         /* This is a new style signature */
00185         h = headerRead(fd, HEADER_MAGIC_YES);
00186         if (h == NULL)
00187             break;
00188 
00189         rc = RPMRC_OK;
00190         sigSize = headerSizeof(h, HEADER_MAGIC_YES);
00191 
00192         pad = (8 - (sigSize % 8)) % 8; /* 8-byte pad */
00193         if (sig_type == RPMSIGTYPE_HEADERSIG) {
00194             if (! headerGetEntry(h, RPMSIGTAG_SIZE, &type,
00195                                 (void **)&archSize, &count))
00196                 break;
00197             rc = checkSize(fd, sigSize, pad, *archSize);
00198         }
00199         if (pad && timedRead(fd, buf, pad) != pad)
00200             rc = RPMRC_SHORTREAD;
00201         break;
00202     default:
00203         break;
00204     }
00205 
00206     if (headerp && rc == RPMRC_OK)
00207         *headerp = h;
00208     else if (h)
00209         h = headerFree(h);
00210 
00211     return rc;
00212 }
00213 
00214 int rpmWriteSignature(FD_t fd, Header h)
00215 {
00216     static byte buf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
00217     int sigSize, pad;
00218     int rc;
00219 
00220     rc = headerWrite(fd, h, HEADER_MAGIC_YES);
00221     if (rc)
00222         return rc;
00223 
00224     sigSize = headerSizeof(h, HEADER_MAGIC_YES);
00225     pad = (8 - (sigSize % 8)) % 8;
00226     if (pad) {
00227         if (Fwrite(buf, sizeof(buf[0]), pad, fd) != pad)
00228             rc = 1;
00229     }
00230     rpmMessage(RPMMESS_DEBUG, _("Signature: size(%d)+pad(%d)\n"), sigSize, pad);
00231     return rc;
00232 }
00233 
00234 Header rpmNewSignature(void)
00235 {
00236     Header h = headerNew();
00237     return h;
00238 }
00239 
00240 Header rpmFreeSignature(Header h)
00241 {
00242     return headerFree(h);
00243 }
00244 
00245 static int makePGPSignature(const char * file, /*@out@*/ void ** sig,
00246                 /*@out@*/ int_32 * size, /*@null@*/ const char * passPhrase)
00247         /*@globals rpmGlobalMacroContext, fileSystem @*/
00248         /*@modifies *sig, *size, rpmGlobalMacroContext, fileSystem @*/
00249 {
00250     char * sigfile = alloca(1024);
00251     int pid, status;
00252     int inpipe[2];
00253     struct stat st;
00254     const char * cmd;
00255     char *const *av;
00256     int rc;
00257 
00258     (void) stpcpy( stpcpy(sigfile, file), ".sig");
00259 
00260     addMacro(NULL, "__plaintext_filename", NULL, file, -1);
00261     addMacro(NULL, "__signature_filename", NULL, sigfile, -1);
00262 
00263     inpipe[0] = inpipe[1] = 0;
00264     (void) pipe(inpipe);
00265 
00266     if (!(pid = fork())) {
00267         const char *pgp_path = rpmExpand("%{?_pgp_path}", NULL);
00268         const char *path;
00269         pgpVersion pgpVer;
00270 
00271         (void) close(STDIN_FILENO);
00272         (void) dup2(inpipe[0], 3);
00273         (void) close(inpipe[1]);
00274 
00275         (void) dosetenv("PGPPASSFD", "3", 1);
00276         if (pgp_path && *pgp_path != '\0')
00277             (void) dosetenv("PGPPATH", pgp_path, 1);
00278 
00279         /* dosetenv("PGPPASS", passPhrase, 1); */
00280 
00281         if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
00282             switch(pgpVer) {
00283             case PGP_2:
00284                 cmd = rpmExpand("%{?__pgp_sign_cmd}", NULL);
00285                 rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
00286                 if (!rc)
00287                     rc = execve(av[0], av+1, environ);
00288                 break;
00289             case PGP_5:
00290                 cmd = rpmExpand("%{?__pgp5_sign_cmd}", NULL);
00291                 rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
00292                 if (!rc)
00293                     rc = execve(av[0], av+1, environ);
00294                 break;
00295             case PGP_UNKNOWN:
00296             case PGP_NOTDETECTED:
00297                 errno = ENOENT;
00298                 break;
00299             }
00300         }
00301         rpmError(RPMERR_EXEC, _("Could not exec %s: %s\n"), "pgp",
00302                         strerror(errno));
00303         _exit(RPMERR_EXEC);
00304     }
00305 
00306     delMacro(NULL, "__plaintext_filename");
00307     delMacro(NULL, "__signature_filename");
00308 
00309     (void) close(inpipe[0]);
00310     if (passPhrase)
00311         (void) write(inpipe[1], passPhrase, strlen(passPhrase));
00312     (void) write(inpipe[1], "\n", 1);
00313     (void) close(inpipe[1]);
00314 
00315     (void)waitpid(pid, &status, 0);
00316     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
00317         rpmError(RPMERR_SIGGEN, _("pgp failed\n"));
00318         return 1;
00319     }
00320 
00321     if (stat(sigfile, &st)) {
00322         /* PGP failed to write signature */
00323         if (sigfile) (void) unlink(sigfile);  /* Just in case */
00324         rpmError(RPMERR_SIGGEN, _("pgp failed to write signature\n"));
00325         return 1;
00326     }
00327 
00328     *size = st.st_size;
00329     rpmMessage(RPMMESS_DEBUG, _("PGP sig size: %d\n"), *size);
00330     *sig = xmalloc(*size);
00331 
00332     {   FD_t fd;
00333         rc = 0;
00334         fd = Fopen(sigfile, "r.fdio");
00335         if (fd != NULL && !Ferror(fd)) {
00336             rc = timedRead(fd, *sig, *size);
00337             if (sigfile) (void) unlink(sigfile);
00338             (void) Fclose(fd);
00339         }
00340         if (rc != *size) {
00341             *sig = _free(*sig);
00342             rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
00343             return 1;
00344         }
00345     }
00346 
00347     rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of PGP sig\n"), *size);
00348 
00349     return 0;
00350 }
00351 
00352 /* This is an adaptation of the makePGPSignature function to use GPG instead
00353  * of PGP to create signatures.  I think I've made all the changes necessary,
00354  * but this could be a good place to start looking if errors in GPG signature
00355  * creation crop up.
00356  */
00357 static int makeGPGSignature(const char * file, /*@out@*/ void ** sig,
00358                 /*@out@*/ int_32 * size, /*@null@*/ const char * passPhrase)
00359         /*@globals rpmGlobalMacroContext, fileSystem @*/
00360         /*@modifies *sig, *size, rpmGlobalMacroContext, fileSystem @*/
00361 {
00362     char * sigfile = alloca(1024);
00363     int pid, status;
00364     int inpipe[2];
00365     FILE * fpipe;
00366     struct stat st;
00367     const char * cmd;
00368     char *const *av;
00369     int rc;
00370 
00371     (void) stpcpy( stpcpy(sigfile, file), ".sig");
00372 
00373     addMacro(NULL, "__plaintext_filename", NULL, file, -1);
00374     addMacro(NULL, "__signature_filename", NULL, sigfile, -1);
00375 
00376     inpipe[0] = inpipe[1] = 0;
00377     (void) pipe(inpipe);
00378 
00379     if (!(pid = fork())) {
00380         const char *gpg_path = rpmExpand("%{?_gpg_path}", NULL);
00381 
00382         (void) close(STDIN_FILENO);
00383         (void) dup2(inpipe[0], 3);
00384         (void) close(inpipe[1]);
00385 
00386         if (gpg_path && *gpg_path != '\0')
00387             (void) dosetenv("GNUPGHOME", gpg_path, 1);
00388 
00389         cmd = rpmExpand("%{?__gpg_sign_cmd}", NULL);
00390         rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
00391         if (!rc)
00392             rc = execve(av[0], av+1, environ);
00393 
00394         rpmError(RPMERR_EXEC, _("Could not exec %s: %s\n"), "gpg",
00395                         strerror(errno));
00396         _exit(RPMERR_EXEC);
00397     }
00398 
00399     delMacro(NULL, "__plaintext_filename");
00400     delMacro(NULL, "__signature_filename");
00401 
00402     fpipe = fdopen(inpipe[1], "w");
00403     (void) close(inpipe[0]);
00404     if (fpipe) {
00405         fprintf(fpipe, "%s\n", (passPhrase ? passPhrase : ""));
00406         (void) fclose(fpipe);
00407     }
00408 
00409     (void)waitpid(pid, &status, 0);
00410     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
00411         rpmError(RPMERR_SIGGEN, _("gpg failed\n"));
00412         return 1;
00413     }
00414 
00415     if (stat(sigfile, &st)) {
00416         /* GPG failed to write signature */
00417         if (sigfile) (void) unlink(sigfile);  /* Just in case */
00418         rpmError(RPMERR_SIGGEN, _("gpg failed to write signature\n"));
00419         return 1;
00420     }
00421 
00422     *size = st.st_size;
00423     rpmMessage(RPMMESS_DEBUG, _("GPG sig size: %d\n"), *size);
00424     *sig = xmalloc(*size);
00425 
00426     {   FD_t fd;
00427         int rc = 0;
00428         fd = Fopen(sigfile, "r.fdio");
00429         if (fd != NULL && !Ferror(fd)) {
00430             rc = timedRead(fd, *sig, *size);
00431             if (sigfile) (void) unlink(sigfile);
00432             (void) Fclose(fd);
00433         }
00434         if (rc != *size) {
00435             *sig = _free(*sig);
00436             rpmError(RPMERR_SIGGEN, _("unable to read the signature\n"));
00437             return 1;
00438         }
00439     }
00440 
00441     rpmMessage(RPMMESS_DEBUG, _("Got %d bytes of GPG sig\n"), *size);
00442 
00443     return 0;
00444 }
00445 
00446 int rpmAddSignature(Header h, const char * file, int_32 sigTag,
00447                 const char *passPhrase)
00448 {
00449     struct stat st;
00450     int_32 size;
00451     byte buf[16];
00452     void *sig;
00453     int ret = -1;
00454 
00455     switch (sigTag) {
00456     case RPMSIGTAG_SIZE:
00457         (void) stat(file, &st);
00458         size = st.st_size;
00459         ret = 0;
00460         (void) headerAddEntry(h, RPMSIGTAG_SIZE, RPM_INT32_TYPE, &size, 1);
00461         break;
00462     case RPMSIGTAG_MD5:
00463         ret = mdbinfile(file, buf);
00464         if (ret == 0)
00465             (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, buf, 16);
00466         break;
00467     case RPMSIGTAG_PGP5:        /* XXX legacy */
00468     case RPMSIGTAG_PGP:
00469         rpmMessage(RPMMESS_VERBOSE, _("Generating signature using PGP.\n"));
00470         ret = makePGPSignature(file, &sig, &size, passPhrase);
00471         if (ret == 0)
00472             (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, sig, size);
00473         break;
00474     case RPMSIGTAG_GPG:
00475         rpmMessage(RPMMESS_VERBOSE, _("Generating signature using GPG.\n"));
00476         ret = makeGPGSignature(file, &sig, &size, passPhrase);
00477         if (ret == 0)
00478             (void) headerAddEntry(h, sigTag, RPM_BIN_TYPE, sig, size);
00479         break;
00480     }
00481 
00482     return ret;
00483 }
00484 
00485 static rpmVerifySignatureReturn
00486 verifySizeSignature(const char * datafile, int_32 size, /*@out@*/ char * result)
00487         /*@globals fileSystem @*/
00488         /*@modifies *result, fileSystem @*/
00489 {
00490     struct stat st;
00491 
00492     (void) stat(datafile, &st);
00493     if (size != st.st_size) {
00494         sprintf(result, "Header+Archive size mismatch.\n"
00495                 "Expected %d, saw %d.\n",
00496                 size, (int)st.st_size);
00497         return RPMSIG_BAD;
00498     }
00499 
00500     sprintf(result, "Header+Archive size OK: %d bytes\n", size);
00501     return RPMSIG_OK;
00502 }
00503 
00504 #define X(_x)   (unsigned)((_x) & 0xff)
00505 
00506 static rpmVerifySignatureReturn
00507 verifyMD5Signature(const char * datafile, const byte * sig,
00508                               /*@out@*/ char * result, md5func fn)
00509         /*@globals fileSystem @*/
00510         /*@modifies *result, fileSystem @*/
00511 {
00512     byte md5sum[16];
00513 
00514     memset(md5sum, 0, sizeof(md5sum));
00515     (void) fn(datafile, md5sum);
00516     if (memcmp(md5sum, sig, 16)) {
00517         sprintf(result, "MD5 sum mismatch\n"
00518                 "Expected: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
00519                 "%02x%02x%02x%02x%02x\n"
00520                 "Saw     : %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
00521                 "%02x%02x%02x%02x%02x\n",
00522                 X(sig[0]),  X(sig[1]),  X(sig[2]),  X(sig[3]),
00523                 X(sig[4]),  X(sig[5]),  X(sig[6]),  X(sig[7]),
00524                 X(sig[8]),  X(sig[9]),  X(sig[10]), X(sig[11]),
00525                 X(sig[12]), X(sig[13]), X(sig[14]), X(sig[15]),
00526                 X(md5sum[0]),  X(md5sum[1]),  X(md5sum[2]),  X(md5sum[3]),
00527                 X(md5sum[4]),  X(md5sum[5]),  X(md5sum[6]),  X(md5sum[7]),
00528                 X(md5sum[8]),  X(md5sum[9]),  X(md5sum[10]), X(md5sum[11]),
00529                 X(md5sum[12]), X(md5sum[13]), X(md5sum[14]), X(md5sum[15]) );
00530         return RPMSIG_BAD;
00531     }
00532 
00533     sprintf(result, "MD5 sum OK: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
00534                     "%02x%02x%02x%02x%02x\n",
00535             X(md5sum[0]),  X(md5sum[1]),  X(md5sum[2]),  X(md5sum[3]),
00536             X(md5sum[4]),  X(md5sum[5]),  X(md5sum[6]),  X(md5sum[7]),
00537             X(md5sum[8]),  X(md5sum[9]),  X(md5sum[10]), X(md5sum[11]),
00538             X(md5sum[12]), X(md5sum[13]), X(md5sum[14]), X(md5sum[15]) );
00539 
00540     return RPMSIG_OK;
00541 }
00542 
00543 static rpmVerifySignatureReturn
00544 verifyPGPSignature(const char * datafile, const void * sig, int count,
00545                 /*@out@*/ char * result)
00546         /*@globals rpmGlobalMacroContext, fileSystem @*/
00547         /*@modifies *result, rpmGlobalMacroContext, fileSystem @*/
00548 {
00549     int pid, status, outpipe[2];
00550 /*@only@*/ /*@null@*/ const char * sigfile = NULL;
00551     byte buf[BUFSIZ];
00552     FILE *file;
00553     int res = RPMSIG_OK;
00554     const char *path;
00555     pgpVersion pgpVer;
00556     const char * cmd;
00557     char *const *av;
00558     int rc;
00559 
00560     /* What version do we have? */
00561     if ((path = rpmDetectPGPVersion(&pgpVer)) == NULL) {
00562         errno = ENOENT;
00563         rpmError(RPMERR_EXEC, ("Could not exec %s: %s\n"), "pgp",
00564                         strerror(errno));
00565         _exit(RPMERR_EXEC);
00566     }
00567 
00568     /*
00569      * Sad but true: pgp-5.0 returns exit value of 0 on bad signature.
00570      * Instead we have to use the text output to detect a bad signature.
00571      */
00572     if (pgpVer == PGP_5)
00573         res = RPMSIG_BAD;
00574 
00575     /* Write out the signature */
00576 #ifdef  DYING
00577   { const char *tmppath = rpmGetPath("%{_tmppath}", NULL);
00578     sigfile = tempnam(tmppath, "rpmsig");
00579     tmppath = _free(tmppath);
00580   }
00581     sfd = Fopen(sigfile, "w.fdio");
00582     if (sfd != NULL && !Ferror(sfd)) {
00583         (void) Fwrite(sig, sizeof(char), count, sfd);
00584         (void) Fclose(sfd);
00585     }
00586 #else
00587     {   FD_t sfd;
00588         if (!makeTempFile(NULL, &sigfile, &sfd)) {
00589             (void) Fwrite(sig, sizeof(char), count, sfd);
00590             (void) Fclose(sfd);
00591             sfd = NULL;
00592         }
00593     }
00594 #endif
00595     if (sigfile == NULL)
00596         return RPMSIG_BAD;
00597 
00598     addMacro(NULL, "__plaintext_filename", NULL, datafile, -1);
00599     addMacro(NULL, "__signature_filename", NULL, sigfile, -1);
00600 
00601     /* Now run PGP */
00602     outpipe[0] = outpipe[1] = 0;
00603     (void) pipe(outpipe);
00604 
00605     if (!(pid = fork())) {
00606         const char *pgp_path = rpmExpand("%{?_pgp_path}", NULL);
00607 
00608         (void) close(outpipe[0]);
00609         (void) close(STDOUT_FILENO);    /* XXX unnecessary */
00610         (void) dup2(outpipe[1], STDOUT_FILENO);
00611 
00612         if (pgp_path && *pgp_path != '\0')
00613             (void) dosetenv("PGPPATH", pgp_path, 1);
00614 
00615         switch (pgpVer) {
00616         case PGP_2:
00617             cmd = rpmExpand("%{?__pgp_verify_cmd}", NULL);
00618             rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
00619             if (!rc)
00620                 rc = execve(av[0], av+1, environ);
00621             break;
00622         case PGP_5:
00623             /* Some output (in particular "This signature applies to */
00624             /* another message") is _always_ written to stderr; we   */
00625             /* want to catch that output, so dup stdout to stderr:   */
00626         {   int save_stderr = dup(2);
00627             (void) dup2(1, 2);
00628 
00629             cmd = rpmExpand("%{?__pgp5_verify_cmd}", NULL);
00630             rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
00631             if (!rc)
00632                 rc = execve(av[0], av+1, environ);
00633 
00634             /* Restore stderr so we can print the error message below. */
00635             (void) dup2(save_stderr, 2);
00636             (void) close(save_stderr);
00637         }   break;
00638         case PGP_UNKNOWN:
00639         case PGP_NOTDETECTED:
00640             break;
00641         }
00642 
00643         rpmError(RPMERR_EXEC, _("Could not exec %s: %s\n"), "pgp",
00644                         strerror(errno));
00645         _exit(RPMERR_EXEC);
00646     }
00647 
00648     delMacro(NULL, "__plaintext_filename");
00649     delMacro(NULL, "__signature_filename");
00650 
00651     (void) close(outpipe[1]);
00652     file = fdopen(outpipe[0], "r");
00653     result[0] = '\0';
00654     if (file) {
00655         char *t = result;
00656         int nb = 8*BUFSIZ - 1;
00657         while (fgets(buf, 1024, file)) {
00658             if (strncmp("File '", buf, 6) &&
00659                 strncmp("Text is assu", buf, 12) &&
00660                 strncmp("This signature applies to another message", buf, 41) &&
00661                 buf[0] != '\n') {
00662                     nb -= strlen(buf);
00663                     if (nb > 0) t = stpncpy(t, buf, nb);
00664             }
00665             if (!strncmp("WARNING: Can't find the right public key", buf, 40))
00666                 res = RPMSIG_NOKEY;
00667             else if (!strncmp("Signature by unknown keyid:", buf, 27))
00668                 res = RPMSIG_NOKEY;
00669             else if (!strncmp("WARNING: The signing key is not trusted", buf, 39))
00670                 res = RPMSIG_NOTTRUSTED;
00671             else if (!strncmp("Good signature", buf, 14))
00672                 res = RPMSIG_OK;
00673         }
00674         (void) fclose(file);
00675         *t = '\0';
00676     }
00677 
00678     (void) waitpid(pid, &status, 0);
00679     if (sigfile) (void) unlink(sigfile);
00680     sigfile = _free(sigfile);
00681     if (!res && (!WIFEXITED(status) || WEXITSTATUS(status))) {
00682         res = RPMSIG_BAD;
00683     }
00684 
00685     return res;
00686 }
00687 
00688 static rpmVerifySignatureReturn
00689 do_verifyGPGSignature(const char *gpghome, const char *sigfile, const char *datafile, /*@out@*/ char *result)
00690         /*@globals rpmGlobalMacroContext, fileSystem @*/
00691         /*@modifies *result, rpmGlobalMacroContext, fileSystem @*/
00692 {
00693         int pid, outpipe[2];
00694   
00695         if ( !sigfile || !datafile )
00696                 return RPMSIG_BAD;
00697 
00698         /* Now run GPG */
00699         outpipe[0] = outpipe[1] = 0;
00700         pipe(outpipe);
00701 
00702         pid = fork();
00703 
00704         if ( pid < 0 )
00705         {
00706                 rpmError( RPMERR_FORK, _("Couldn't fork %s: %s"), "gpg", strerror(errno) );
00707                 return RPMSIG_BAD;
00708         } else
00709         {
00710                 if ( !pid )
00711                 {
00712                         /* child */
00713                         const char * cmd;
00714                         char *const *av;
00715                         int rc;
00716 
00717                         close( outpipe[0] );
00718                         /* gpg version 0.9 sends its output to stderr. */
00719                         dup2(outpipe[1], STDERR_FILENO);
00720 
00721                         addMacro(NULL, "__plaintext_filename", NULL, datafile, -1);
00722                         addMacro(NULL, "__signature_filename", NULL, sigfile, -1);
00723 
00724                         if ( rpm_close_all() ) {
00725                                 perror( "rpm_close_all" );
00726                                 _exit( -1 );
00727                         }
00728 
00729                         dosetenv( "LANG", "C", 1 );
00730                         dosetenv( "LANGUAGE", "C", 1 );
00731                         dosetenv( "LC_ALL", "C", 1 );
00732 
00733                         if ( gpghome && *gpghome )
00734                                 dosetenv( "GNUPGHOME", gpghome, 1 );
00735 
00736                         cmd = rpmExpand("%{?__gpg_verify_cmd}", NULL);
00737                         rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
00738                         if (!rc)
00739                                 rc = execv(av[0], av+1);
00740                         rpmError(RPMERR_EXEC, _("Could not exec %s: %s\n"), "gpg",
00741                                 strerror(errno));
00742                         _exit( RPMERR_EXEC );
00743                 } else
00744                 {
00745                         /* parent */
00746                         int res = RPMSIG_OK, status;
00747                         FILE *file = fdopen( outpipe[0], "r" );
00748                         unsigned char buf[BUFSIZ];
00749                         const char nokey[] = "gpg: Can't check signature: public key not found";
00750 
00751                         close(outpipe[1]);
00752                         result[0] = '\0';
00753 
00754                         if ( file )
00755                         {
00756                                 while ( fgets( buf, sizeof(buf), file ) )
00757                                 {
00758                                         strcat(result, buf);
00759                                         if ( !xstrncasecmp( nokey, buf, sizeof(nokey)-1 ) )
00760                                                 res = RPMSIG_NOKEY;
00761                                 }
00762 
00763                                 fclose(file);
00764                         }
00765 
00766                         while ( waitpid( pid, &status, 0 ) < 0 )
00767                         {
00768                                 if ( EINTR != errno )
00769                                 {
00770                                         rpmError( RPMERR_FORK, _("waitpid failure: %s"), strerror(errno) );
00771                                         return RPMSIG_BAD;
00772                                 }
00773                         }
00774 
00775                         if ( !res && (!WIFEXITED(status) || WEXITSTATUS(status)) )
00776                                 res = RPMSIG_BAD;
00777                         return res;
00778                 }
00779         }
00780 }
00781 
00782 static rpmVerifySignatureReturn
00783 verifyGPGSignature(const char * datafile, const void * sig, int count,
00784                 /*@out@*/ char * result)
00785         /*@globals rpmGlobalMacroContext, fileSystem @*/
00786         /*@modifies *result, rpmGlobalMacroContext, fileSystem @*/
00787 {
00788     char *sigfile = 0;
00789   
00790     /* Write out the signature */
00791     {   FD_t sfd;
00792         if (!makeTempFile(NULL, &sigfile, &sfd)) {
00793             (void) Fwrite(sig, sizeof(char), count, sfd);
00794             (void) Fclose(sfd);
00795             sfd = NULL;
00796         }
00797     }
00798 
00799     if (!sigfile)
00800     {
00801         rpmError(RPMERR_SCRIPT, _("Unable to open temp file."));
00802         return RPMSIG_BAD;
00803     }
00804 
00805     /* Now run GPG */
00806     {
00807         const char *gpg_path = rpmExpand( "%{?_internal_gpg_path}", NULL );
00808         const char *gpg_home = ( gpg_path && *gpg_path ) ? gpg_path : 0;
00809         rpmVerifySignatureReturn res = gpg_home ?
00810                 do_verifyGPGSignature( gpg_home, sigfile, datafile, result ) : RPMSIG_NOKEY;
00811         gpg_path = _free( gpg_path );
00812 
00813         if ( RPMSIG_NOKEY == res )
00814         {
00815             gpg_path = rpmExpand( "%{?_gpg_path}", NULL );
00816             gpg_home = ( gpg_path && *gpg_path ) ? gpg_path : 0;
00817             res = do_verifyGPGSignature( gpg_home, sigfile, datafile, result );
00818             gpg_path = _free( gpg_path );
00819         }
00820 
00821         unlink(sigfile);
00822         return res;
00823     }
00824 }
00825 
00826 static int checkPassPhrase(const char * passPhrase, const int sigTag)
00827         /*@globals rpmGlobalMacroContext, fileSystem @*/
00828         /*@modifies rpmGlobalMacroContext, fileSystem @*/
00829 {
00830     int passPhrasePipe[2];
00831     int pid, status;
00832     int fd;
00833     const char * cmd;
00834     char *const *av;
00835     int rc;
00836 
00837     passPhrasePipe[0] = passPhrasePipe[1] = 0;
00838     (void) pipe(passPhrasePipe);
00839     if (!(pid = fork())) {
00840         (void) close(STDIN_FILENO);
00841         (void) close(STDOUT_FILENO);
00842         (void) close(passPhrasePipe[1]);
00843         if (! rpmIsVerbose()) {
00844             (void) close(STDERR_FILENO);
00845         }
00846         if ((fd = open("/dev/null", O_RDONLY)) != STDIN_FILENO) {
00847             (void) dup2(fd, STDIN_FILENO);
00848             (void) close(fd);
00849         }
00850         if ((fd = open("/dev/null", O_WRONLY)) != STDOUT_FILENO) {
00851             (void) dup2(fd, STDOUT_FILENO);
00852             (void) close(fd);
00853         }
00854         (void) dup2(passPhrasePipe[0], 3);
00855 
00856         switch (sigTag) {
00857         case RPMSIGTAG_GPG:
00858         {   const char *gpg_path = rpmExpand("%{?_gpg_path}", NULL);
00859 
00860             if (gpg_path && *gpg_path != '\0')
00861                 (void) dosetenv("GNUPGHOME", gpg_path, 1);
00862 
00863             cmd = rpmExpand("%{?__gpg_check_password_cmd}", NULL);
00864             rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
00865             if (!rc)
00866                 rc = execve(av[0], av+1, environ);
00867 
00868             rpmError(RPMERR_EXEC, _("Could not exec %s: %s\n"), "gpg",
00869                         strerror(errno));
00870             _exit(RPMERR_EXEC);
00871         }   /*@notreached@*/ break;
00872         case RPMSIGTAG_PGP5:    /* XXX legacy */
00873         case RPMSIGTAG_PGP:
00874         {   const char *pgp_path = rpmExpand("%{?_pgp_path}", NULL);
00875             const char *path;
00876             pgpVersion pgpVer;
00877 
00878             (void) dosetenv("PGPPASSFD", "3", 1);
00879             if (pgp_path && *pgp_path != '\0')
00880                 (void) dosetenv("PGPPATH", pgp_path, 1);
00881 
00882             if ((path = rpmDetectPGPVersion(&pgpVer)) != NULL) {
00883                 switch(pgpVer) {
00884                 case PGP_2:
00885                     cmd = rpmExpand("%{?__pgp_check_password_cmd}", NULL);
00886                     rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
00887                     if (!rc)
00888                         rc = execve(av[0], av+1, environ);
00889                     break;
00890                 case PGP_5:     /* XXX legacy */
00891                     cmd = rpmExpand("%{?__pgp5_check_password_cmd}", NULL);
00892                     rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
00893                     if (!rc)
00894                         rc = execve(av[0], av+1, environ);
00895                     break;
00896                 case PGP_UNKNOWN:
00897                 case PGP_NOTDETECTED:
00898                     break;
00899                 }
00900             }
00901             rpmError(RPMERR_EXEC, _("Could not exec %s: %s\n"), "pgp",
00902                         strerror(errno));
00903             _exit(RPMERR_EXEC);
00904         }   /*@notreached@*/ break;
00905         default: /* This case should have been screened out long ago. */
00906             rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
00907             _exit(RPMERR_SIGGEN);
00908             /*@notreached@*/ break;
00909         }
00910     }
00911 
00912     (void) close(passPhrasePipe[0]);
00913     (void) write(passPhrasePipe[1], passPhrase, strlen(passPhrase));
00914     (void) write(passPhrasePipe[1], "\n", 1);
00915     (void) close(passPhrasePipe[1]);
00916 
00917     (void)waitpid(pid, &status, 0);
00918     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
00919         return 1;
00920     }
00921 
00922     /* passPhrase is good */
00923     return 0;
00924 }
00925 
00926 char * rpmGetPassPhrase(const char * prompt, const int sigTag)
00927 {
00928     char *pass;
00929     int aok;
00930 
00931     switch (sigTag) {
00932     case RPMSIGTAG_GPG:
00933       { const char *name = rpmExpand("%{?_gpg_name}", NULL);
00934         aok = (name && *name != '\0');
00935         name = _free(name);
00936       }
00937         if (!aok) {
00938             rpmError(RPMERR_SIGGEN,
00939                 _("You must set \"%%_gpg_name\" in your macro file\n"));
00940             return NULL;
00941         }
00942         break;
00943     case RPMSIGTAG_PGP5:        /* XXX legacy */
00944     case RPMSIGTAG_PGP:
00945       { const char *name = rpmExpand("%{?_pgp_name}", NULL);
00946         aok = (name && *name != '\0');
00947         name = _free(name);
00948       }
00949         if (!aok) {
00950             rpmError(RPMERR_SIGGEN,
00951                 _("You must set \"%%_pgp_name\" in your macro file\n"));
00952             return NULL;
00953         }
00954         break;
00955     default:
00956         /* Currently the calling function (rpm.c:main) is checking this and
00957          * doing a better job.  This section should never be accessed.
00958          */
00959         rpmError(RPMERR_SIGGEN, _("Invalid %%_signature spec in macro file\n"));
00960         return NULL;
00961         /*@notreached@*/ break;
00962     }
00963 
00964     pass = /*@-unrecog@*/ getpass( (prompt ? prompt : "") ) /*@=unrecog@*/ ;
00965 
00966     if (checkPassPhrase(pass, sigTag))
00967         return NULL;
00968 
00969     return pass;
00970 }
00971 
00972 rpmVerifySignatureReturn
00973 rpmVerifySignature(const char * file, int_32 sigTag, const void * sig,
00974                 int count, char * result)
00975 {
00976      rpmVerifySignatureReturn res;
00977 
00978     switch (sigTag) {
00979     case RPMSIGTAG_SIZE:
00980         res = verifySizeSignature(file, *(int_32 *)sig, result);
00981         break;
00982     case RPMSIGTAG_MD5:
00983         res = verifyMD5Signature(file, sig, result, mdbinfile);
00984         break;
00985     case RPMSIGTAG_PGP5:        /* XXX legacy */
00986     case RPMSIGTAG_PGP:
00987         res = verifyPGPSignature(file, sig, count, result);
00988         break;
00989     case RPMSIGTAG_GPG:
00990         res = verifyGPGSignature(file, sig, count, result);
00991         break;
00992     case RPMSIGTAG_LEMD5_1:
00993     case RPMSIGTAG_LEMD5_2:
00994         sprintf(result, _("Broken MD5 digest: UNSUPPORTED\n"));
00995         res = RPMSIG_UNKNOWN;
00996         break;
00997     default:
00998         sprintf(result, "Do not know how to verify sig type %d\n", sigTag);
00999         res = RPMSIG_UNKNOWN;
01000         break;
01001     }
01002     return res;
01003 }

Generated on Sun Feb 2 23:32:03 2003 for rpm by doxygen1.2.18