/* * Copyright (C) 1996,1997 Michael R. Elkins * Copyright (c) 1998 Thomas Roessler * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * This file contains all of the PGP routines necessary to sign, encrypt, * verify and decrypt PGP messages in either the new PGP/MIME format, or * in the older Application/Pgp format. It also contains some code to * cache the user's passphrase for repeat use when decrypting or signing * a message. */ #include "mutt.h" #include "mutt_curses.h" #include "pgp.h" #include "mime.h" #include "parse.h" #include #include #include #include #include #include #include #ifdef _PGPPATH char PgpPass[STRING]; static time_t PgpExptime = 0; /* when does the cached passphrase expire? */ static struct pgp_vinfo pgp_vinfo[] = { { PGP_V2, "pgp2", &PgpV2, &PgpV2Pubring, &PgpV2Secring, &PgpV2Language, pgp_read_pubring, pgp_read_secring, pgp_v2_invoke_decode, pgp_v2_invoke_verify, pgp_v2_invoke_decrypt, pgp_v2_invoke_sign, pgp_v2_invoke_encrypt, pgp_v2_invoke_import, pgp_v2_invoke_export, pgp_v2_invoke_verify_key }, { PGP_V3, "pgp3", &PgpV3, &PgpV3Pubring, &PgpV3Secring, &PgpV3Language, pgp_read_pubring, pgp_read_secring, pgp_v3_invoke_decode, pgp_v3_invoke_verify, pgp_v3_invoke_decrypt, pgp_v3_invoke_sign, pgp_v3_invoke_encrypt, pgp_v3_invoke_import, pgp_v3_invoke_export, pgp_v3_invoke_verify_key }, { PGP_V3, "pgp5", &PgpV3, &PgpV3Pubring, &PgpV3Secring, &PgpV3Language, pgp_read_pubring, pgp_read_secring, pgp_v3_invoke_decode, pgp_v3_invoke_verify, pgp_v3_invoke_decrypt, pgp_v3_invoke_sign, pgp_v3_invoke_encrypt, pgp_v3_invoke_import, pgp_v3_invoke_export, pgp_v3_invoke_verify_key }, { PGP_GPG, "gpg", &PgpGpg, &PgpGpgDummy, &PgpGpgDummy, &PgpGpgDummy, gpg_read_pubring, gpg_read_secring, pgp_gpg_invoke_decode, pgp_gpg_invoke_verify, pgp_gpg_invoke_decrypt, pgp_gpg_invoke_sign, pgp_gpg_invoke_encrypt, pgp_gpg_invoke_import, pgp_gpg_invoke_export, pgp_gpg_invoke_verify_key }, { PGP_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; static struct { enum pgp_ops op; char **str; } pgp_opvers[] = { { PGP_DECODE, &PgpReceiveVersion }, { PGP_VERIFY, &PgpReceiveVersion }, { PGP_DECRYPT, &PgpReceiveVersion }, { PGP_SIGN, &PgpSendVersion }, { PGP_ENCRYPT, &PgpSendVersion }, { PGP_VERIFY_KEY, &PgpSendVersion }, { PGP_IMPORT, &PgpKeyVersion }, { PGP_EXPORT, &PgpKeyVersion }, { PGP_LAST_OP, NULL } }; void pgp_void_passphrase (void) { memset (PgpPass, 0, sizeof (PgpPass)); PgpExptime = 0; } int pgp_valid_passphrase (void) { time_t now = time (NULL); if (now < PgpExptime) return 1; /* just use the cached copy. */ pgp_void_passphrase (); if (mutt_get_password ("Enter PGP passphrase:", PgpPass, sizeof (PgpPass)) == 0) { PgpExptime = time (NULL) + PgpTimeout; return (1); } else { PgpExptime = 0; return (0); } /* not reached */ } void mutt_forget_passphrase (void) { pgp_void_passphrase (); mutt_message ("PGP passphrase forgotten."); } struct pgp_vinfo *pgp_get_vinfo(enum pgp_ops op) { int i; char *version = "default"; char msg[LONG_STRING]; for(i = 0; pgp_opvers[i].op != PGP_LAST_OP; i++) { if(pgp_opvers[i].op == op) { version = *pgp_opvers[i].str; break; } } if (!strcasecmp(version, "default")) version = PgpDefaultVersion; for(i = 0; pgp_vinfo[i].name; i++) { if(!strcasecmp(pgp_vinfo[i].name, version)) return &pgp_vinfo[i]; } snprintf(msg, sizeof(msg), "Unknown PGP version \"%s\".", version); mutt_error(msg); return NULL; } char *pgp_keyid(KEYINFO *k) { if((k->flags & KEYFLAG_SUBKEY) && k->mainkey) k = k->mainkey; return _pgp_keyid(k); } char *_pgp_keyid(KEYINFO *k) { if(option(OPTPGPLONGIDS)) return k->keyid; else return (k->keyid + 8); } /* ---------------------------------------------------------------------------- * Routines for handing PGP input. */ /* print the current time to avoid spoofing of the signature output */ static void pgp_current_time (STATE *s) { time_t t; char p[STRING]; state_puts ("[-- PGP output follows (current time: ", s); t = time (NULL); strfcpy (p, asctime (localtime (&t)), sizeof (p)); p[strlen (p) - 1] = 0; /* kill the newline */ state_puts (p, s); state_puts (") --]\n", s); } /* Support for the Application/PGP Content Type. */ void application_pgp_handler (BODY *m, STATE *s) { int needpass = -1, pgp_keyblock = 0; int clearsign = 0; long start_pos = 0; long bytes, last_pos, offset; char buf[HUGE_STRING]; char outfile[_POSIX_PATH_MAX]; char tmpfname[_POSIX_PATH_MAX]; FILE *pgpout = NULL, *pgpin, *pgperr; FILE *tmpfp; pid_t thepid; struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_DECODE); if(!pgp) return; fseek (s->fpin, m->offset, 0); last_pos = m->offset; for (bytes = m->length; bytes > 0;) { if (fgets (buf, sizeof (buf) - 1, s->fpin) == NULL) break; offset = ftell (s->fpin); bytes -= (offset - last_pos); /* don't rely on strlen(buf) */ last_pos = offset; if (strncmp ("-----BEGIN PGP ", buf, 15) == 0) { clearsign = 0; start_pos = last_pos; if (strcmp ("MESSAGE-----\n", buf + 15) == 0) needpass = 1; else if (strcmp ("SIGNED MESSAGE-----\n", buf + 15) == 0) { clearsign = 1; needpass = 0; } else if (!option(OPTDONTHANDLEPGPKEYS) && strcmp ("PUBLIC KEY BLOCK-----\n", buf + 15) == 0) { needpass = 0; pgp_keyblock =1; } else { if (s->prefix) state_puts (s->prefix, s); state_puts (buf, s); continue; } if(!clearsign || s->flags & M_VERIFY) { /* invoke PGP */ mutt_mktemp (outfile); if ((pgpout = safe_fopen (outfile, "w+")) == NULL) { mutt_perror (outfile); return; } mutt_mktemp (tmpfname); if ((tmpfp = safe_fopen(tmpfname, "w+")) == NULL) { mutt_perror(tmpfname); fclose(pgpout); pgpout = NULL; return; } fputs (buf, tmpfp); while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) { offset = ftell (s->fpin); bytes -= (offset - last_pos); /* don't rely on strlen(buf) */ last_pos = offset; fputs (buf, tmpfp); if ((needpass && strcmp ("-----END PGP MESSAGE-----\n", buf) == 0) || (!needpass && (strcmp ("-----END PGP SIGNATURE-----\n", buf) == 0 || strcmp ("-----END PGP PUBLIC KEY BLOCK-----\n",buf) == 0))) break; } fclose(tmpfp); if ((thepid = pgp->invoke_decode (pgp, &pgpin, NULL, &pgperr, -1, fileno (pgpout), -1, tmpfname, needpass)) == -1) { fclose (pgpout); pgpout = NULL; mutt_unlink(tmpfname); state_puts ("[-- Error: unable to create PGP subprocess --]\n", s); state_puts (buf, s); continue; } if (needpass) { if (!pgp_valid_passphrase ()) pgp_void_passphrase (); fputs (PgpPass, pgpin); fputc ('\n', pgpin); } fclose (pgpin); if (s->flags & M_DISPLAY) pgp_current_time (s); mutt_wait_filter (thepid); mutt_unlink(tmpfname); if (s->flags & M_DISPLAY) mutt_copy_stream(pgperr, s->fpout); fclose (pgperr); if (s->flags & M_DISPLAY) state_puts ("\n[-- End of PGP output --]\n\n", s); } if(s->flags & M_DISPLAY) { if (needpass) state_puts ("[-- BEGIN PGP MESSAGE --]\n\n", s); else if (pgp_keyblock) state_puts ("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n", s); else state_puts ("[-- BEGIN PGP SIGNED MESSAGE --]\n\n", s); } /* Use PGP's output if there was no clearsig signature. */ if(!clearsign) { fflush (pgpout); rewind (pgpout); while (fgets (buf, sizeof (buf) - 1, pgpout) != NULL) { if (s->prefix) state_puts (s->prefix, s); state_puts (buf, s); } } /* Close the temporary files iff they were created. * The condition used to be !clearsign || s->flags & M_VERIFY, * but gcc would complain then. */ if(pgpout) { fclose (pgpout); pgpout = NULL; mutt_unlink(outfile); } /* decode clearsign stuff */ if(clearsign) { /* rationale: We want PGP's error messages, but in the times * of PGP 5.0 we can't rely on PGP to do the dash * escape decoding - so we have to do this * ourselves. */ int armor_header = 1; int complete = 1; fseek(s->fpin, start_pos, SEEK_SET); bytes += (last_pos - start_pos); last_pos = start_pos; offset = start_pos; while(bytes > 0 && fgets(buf, sizeof(buf) - 1, s->fpin) != NULL) { offset = ftell(s->fpin); bytes -= (offset - last_pos); last_pos = offset; if(complete) { if (!strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n")) break; if(armor_header) { if(*buf == '\n') armor_header = 0; } else { if(s->prefix) state_puts(s->prefix, s); if(buf[0] == '-' && buf [1] == ' ') state_puts(buf + 2, s); else state_puts(buf, s); } } else { if(!armor_header) state_puts(buf, s); } complete = strchr(buf, '\n') != NULL; } if (complete && !strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n")) { while(bytes > 0 && fgets(buf, sizeof(buf) - 1, s->fpin) != NULL) { offset = ftell(s->fpin); bytes -= (offset - last_pos); last_pos = offset; if(complete && !strcmp(buf, "-----END PGP SIGNATURE-----\n")) break; complete = strchr(buf, '\n') != NULL; } } } if (s->flags & M_DISPLAY) { if (needpass) state_puts ("\n[-- END PGP MESSAGE --]\n", s); else if (pgp_keyblock) state_puts ("[-- END PGP PUBLIC KEY BLOCK --]\n", s); else state_puts ("\n[-- END PGP SIGNED MESSAGE --]\n", s); } } else { if (s->prefix) state_puts (s->prefix, s); state_puts (buf, s); } } if (needpass == -1) { state_puts ("[-- Error! Could not find beginning of PGP message! --]\n\n", s); return; } } int pgp_query (BODY *m) { char *p; int t = 0; /* Check for old-style APPLICATION/PGP messages */ if (m->type == TYPEAPPLICATION) { if (!strcasecmp (m->subtype, "pgp") || !strcasecmp (m->subtype, "x-pgp-message")) { if ((p = mutt_get_parameter ("x-action", m->parameter)) && (!strcasecmp (p, "sign") || !strcasecmp (p, "signclear"))) t |= PGPSIGN; else if((p = mutt_get_parameter ("format", m->parameter)) && !strcasecmp(p, "keys-only")) t |= PGPKEY; if ((p = mutt_get_parameter ("format", m->parameter)) && !strcasecmp (p, "keys-only")) t |= PGPKEY; if(!t) t |= PGPENCRYPT; /* not necessarily correct, but... */ } if (!strcasecmp (m->subtype, "pgp-signed")) t |= PGPSIGN; if (!strcasecmp (m->subtype, "pgp-keys")) t |= PGPKEY; } /* Check for PGP/MIME messages. */ if (m->type == TYPEMULTIPART) { if (strcasecmp (m->subtype, "signed") == 0 && (p = mutt_get_parameter("protocol", m->parameter)) && strcasecmp (p, "application/pgp-signature") == 0) t |= PGPSIGN; else if ((strcasecmp (m->subtype, "encrypted") == 0) && (p = mutt_get_parameter ("protocol", m->parameter)) && strcasecmp (p, "application/pgp-encrypted") == 0) t |= PGPENCRYPT; } if(m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) { BODY *p; for(p = m->parts; p; p = p->next) t |= pgp_query(p); } return t; } /* * This routine verifies a PGP/MIME signed body. */ void pgp_signed_handler (BODY *a, STATE *s) { FILE *fp, *pgpout, *pgperr; char tempfile[_POSIX_PATH_MAX], sigfile[_POSIX_PATH_MAX], pgperrfile[_POSIX_PATH_MAX]; int bytes; int hadcr; int c; pid_t thepid; struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_VERIFY); if(!pgp) return; a = a->parts; /* First do some error checking to make sure that this looks like a valid * multipart/signed body. */ if (a && a->next && a->next->type == TYPEAPPLICATION && a->next->subtype && strcasecmp (a->next->subtype, "pgp-signature") == 0) { if (s->flags & M_DISPLAY) { mutt_mktemp (tempfile); if(!(fp = safe_fopen (tempfile, "w"))) { mutt_perror(tempfile); return; } fseek (s->fpin, a->hdr_offset, 0); bytes = a->length + a->offset - a->hdr_offset; hadcr = 0; while (bytes > 0) { if((c = fgetc(s->fpin)) == EOF) break; bytes--; if(c == '\r') hadcr = 1; else { if(c == '\n' && !hadcr) fputc('\r', fp); hadcr = 0; } fputc(c, fp); } fclose (fp); /* Now grab the signature. Since signature data is required to be 7bit, * we don't have to worry about doing CTE decoding... */ snprintf (sigfile, sizeof (sigfile), "%s.asc", tempfile); if(!(fp = safe_fopen (sigfile, "w"))) { mutt_perror(sigfile); unlink(tempfile); return; } fseek (s->fpin, a->next->offset, 0); mutt_copy_bytes (s->fpin, fp, a->next->length); fclose (fp); mutt_mktemp(pgperrfile); if(!(pgperr = safe_fopen(pgperrfile, "w+"))) { mutt_perror(pgperrfile); unlink(tempfile); unlink(sigfile); return; } pgp_current_time (s); if((thepid = pgp->invoke_verify (pgp, NULL, &pgpout, NULL, -1, -1, fileno(pgperr), tempfile, sigfile)) != -1) { mutt_copy_stream(pgpout, s->fpout); fclose (pgpout); fflush(pgperr); rewind(pgperr); mutt_copy_stream(pgperr, s->fpout); fclose(pgperr); mutt_wait_filter (thepid); } state_puts ("[-- End of PGP output --]\n\n", s); mutt_unlink (tempfile); mutt_unlink (sigfile); mutt_unlink (pgperrfile); /* Now display the signed body */ state_puts ("[-- The following data is PGP/MIME signed --]\n\n", s); } mutt_body_handler (a, s); if (s->flags & M_DISPLAY) state_puts ("\n[-- End of PGP/MIME signed data --]\n", s); } else { dprint (1,(debugfile, "pgp_signed_handler: invalid format!\n")); state_puts ("[-- Error! This message does not comply with the PGP/MIME specification! --]\n\n", s); mutt_decode_attachment (a, s); /* just treat the data as text/plain... */ } } /* Extract pgp public keys from messages or attachments */ void pgp_extract_keys_from_messages (HEADER *h) { int i; STATE s; char tempfname[_POSIX_PATH_MAX]; struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_IMPORT); if(!pgp) return; if(h) { mutt_parse_mime_message(Context, h); if(h->pgp & PGPENCRYPT && !pgp_valid_passphrase()) return; } memset(&s, 0, sizeof(STATE)); mutt_mktemp(tempfname); if(!(s.fpout = safe_fopen(tempfname, "w"))) { mutt_perror(tempfname); return; } set_option(OPTDONTHANDLEPGPKEYS); if(!h) { for(i = 0; i < Context->vcount; i++) { if(Context->hdrs[Context->v2r[i]]->tagged) { mutt_parse_mime_message(Context, Context->hdrs[Context->v2r[i]]); if(Context->hdrs[Context->v2r[i]]->pgp & PGPENCRYPT && !pgp_valid_passphrase()) { fclose(s.fpout); goto bailout; } mutt_pipe_message_to_state(Context->hdrs[Context->v2r[i]], &s); } } } else { mutt_parse_mime_message(Context, h); if(h->pgp & PGPENCRYPT && !pgp_valid_passphrase()) { fclose(s.fpout); goto bailout; } mutt_pipe_message_to_state(h, &s); } fclose(s.fpout); endwin(); pgp->invoke_import(pgp, tempfname); mutt_any_key_to_continue(NULL); bailout: mutt_unlink(tempfname); unset_option(OPTDONTHANDLEPGPKEYS); } static void pgp_extract_keys_from_attachment(struct pgp_vinfo *pgp, FILE *fp, BODY *top) { STATE s; FILE *tempfp; char tempfname[_POSIX_PATH_MAX]; mutt_mktemp(tempfname); if(!(tempfp = safe_fopen(tempfname, "w"))) { mutt_perror(tempfname); return; } memset(&s, 0, sizeof(STATE)); s.fpin = fp; s.fpout = tempfp; mutt_body_handler(top, &s); fclose(tempfp); pgp->invoke_import(pgp, tempfname); mutt_any_key_to_continue(NULL); mutt_unlink(tempfname); } void pgp_extract_keys_from_attachment_list (FILE *fp, int tag, BODY *top) { struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_IMPORT); if(!pgp) return; if(!fp) { mutt_error("Internal error. Inform ."); return; } endwin(); set_option(OPTDONTHANDLEPGPKEYS); for(; top; top = top->next) { if(!tag || top->tagged) pgp_extract_keys_from_attachment (pgp, fp, top); if(!tag) break; } unset_option(OPTDONTHANDLEPGPKEYS); } BODY *pgp_decrypt_part (BODY *a, STATE *s, FILE *fpout) { char buf[LONG_STRING]; FILE *pgpin, *pgpout, *pgperr, *pgptmp; struct stat info; BODY *tattach; int len; char pgperrfile[_POSIX_PATH_MAX]; char pgptmpfile[_POSIX_PATH_MAX]; pid_t thepid; struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_DECRYPT); if(!pgp) return NULL; mutt_mktemp (pgperrfile); if ((pgperr = safe_fopen (pgperrfile, "w+")) == NULL) { mutt_perror (pgperrfile); return NULL; } unlink (pgperrfile); mutt_mktemp (pgptmpfile); if((pgptmp = safe_fopen (pgptmpfile, "w")) == NULL) { mutt_perror (pgptmpfile); fclose(pgperr); return NULL; } /* Position the stream at the beginning of the body, and send the data to * the temporary file. */ fseek (s->fpin, a->offset, 0); mutt_copy_bytes (s->fpin, pgptmp, a->length); fclose (pgptmp); if ((thepid = pgp->invoke_decrypt (pgp, &pgpin, &pgpout, NULL, -1, -1, fileno (pgperr), pgptmpfile)) == -1) { fclose (pgperr); unlink (pgptmpfile); if (s->flags & M_DISPLAY) state_puts ("[-- Error: could not create a PGP subprocess! --]\n\n", s); return (NULL); } /* send the PGP passphrase to the subprocess */ fputs (PgpPass, pgpin); fputc ('\n', pgpin); fclose(pgpin); /* Read the output from PGP, and make sure to change CRLF to LF, otherwise * read_mime_header has a hard time parsing the message. */ while (fgets (buf, sizeof (buf) - 1, pgpout) != NULL) { len = strlen (buf); if (len > 1 && buf[len - 2] == '\r') strcpy (buf + len - 2, "\n"); fputs (buf, fpout); } fclose (pgpout); mutt_wait_filter (thepid); mutt_unlink(pgptmpfile); if (s->flags & M_DISPLAY) { fflush (pgperr); rewind (pgperr); mutt_copy_stream (pgperr, s->fpout); state_puts ("[-- End of PGP output --]\n\n", s); } fclose (pgperr); fflush (fpout); rewind (fpout); if ((tattach = mutt_read_mime_header (fpout, 0)) != NULL) { /* * Need to set the length of this body part. */ fstat (fileno (fpout), &info); tattach->length = info.st_size - tattach->offset; /* See if we need to recurse on this MIME part. */ if (tattach->type == TYPEMULTIPART) { fseek (fpout, tattach->offset, 0); tattach->parts = mutt_parse_multipart (fpout, mutt_get_parameter ("boundary", tattach->parameter), tattach->offset + tattach->length, strcasecmp ("digest", tattach->subtype) == 0); } else if (tattach->type == TYPEMESSAGE) { fseek (fpout, tattach->offset, 0); tattach->parts = mutt_parse_messageRFC822 (fpout, tattach); } } return (tattach); } void pgp_encrypted_handler (BODY *a, STATE *s) { char tempfile[_POSIX_PATH_MAX]; FILE *fpout, *fpin; BODY *tattach; a = a->parts; if (!a || a->type != TYPEAPPLICATION || !a->subtype || strcasecmp ("pgp-encrypted", a->subtype) != 0 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype || strcasecmp ("octet-stream", a->next->subtype) != 0) { if (s->flags & M_DISPLAY) state_puts ("[-- Error: malformed PGP/MIME message --]\n\n", s); return; } /* * Move forward to the application/pgp-encrypted body. */ a = a->next; mutt_mktemp (tempfile); if ((fpout = safe_fopen (tempfile, "w+")) == NULL) { if (s->flags & M_DISPLAY) state_puts ("[-- Error: could not create temporary file --]\n", s); return; } if (s->flags & M_DISPLAY) pgp_current_time (s); if ((tattach = pgp_decrypt_part (a, s, fpout)) != NULL) { if (s->flags & M_DISPLAY) state_puts ("[-- The following data is PGP/MIME encrypted --]\n\n", s); fpin = s->fpin; s->fpin = fpout; mutt_body_handler (tattach, s); s->fpin = fpin; if (s->flags & M_DISPLAY) state_puts ("\n[-- End of PGP/MIME encrypted data --]\n", s); mutt_free_body (&tattach); } fclose (fpout); mutt_unlink(tempfile); } /* ---------------------------------------------------------------------------- * Routines for sending PGP/MIME messages. */ static void convert_to_7bit (BODY *a) { while (a) { if (a->type == TYPEMULTIPART) { if (a->encoding != ENC7BIT) { a->encoding = ENC7BIT; convert_to_7bit (a->parts); } } else if (a->type == TYPEMESSAGE && strcasecmp(a->subtype, "delivery-status")) { if(a->encoding != ENC7BIT) mutt_message_to_7bit(a, NULL); } else if (a->encoding == ENC8BIT) a->encoding = ENCQUOTEDPRINTABLE; else if (a->encoding == ENCBINARY) a->encoding = ENCBASE64; else if (a->content && (a->content->from || (a->content->space && option (OPTPGPSTRICTENC)))) a->encoding = ENCQUOTEDPRINTABLE; a = a->next; } } BODY *pgp_sign_message (BODY *a) { PARAMETER *p; BODY *t; char buffer[LONG_STRING]; char sigfile[_POSIX_PATH_MAX], signedfile[_POSIX_PATH_MAX]; FILE *pgpin, *pgpout, *pgperr, *fp, *sfp; int err = 0; int empty = 1; pid_t thepid; struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_SIGN); if(!pgp) return NULL; convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */ mutt_mktemp (sigfile); if ((fp = safe_fopen (sigfile, "w")) == NULL) { return (NULL); } mutt_mktemp (signedfile); if ((sfp = safe_fopen(signedfile, "w")) == NULL) { mutt_perror(signedfile); fclose(fp); unlink(sigfile); return NULL; } mutt_write_mime_header (a, sfp); fputc ('\n', sfp); mutt_write_mime_body (a, sfp); fclose(sfp); if((thepid = pgp->invoke_sign(pgp, &pgpin, &pgpout, &pgperr, -1, -1, -1, signedfile)) == -1) { mutt_perror("Can't open PGP subprocess!"); fclose(fp); unlink(sigfile); unlink(signedfile); return NULL; } fputs(PgpPass, pgpin); fputc('\n', pgpin); fclose(pgpin); /* * Read back the PGP signature. Also, change MESSAGE=>SIGNATURE as * recommended for future releases of PGP. */ while (fgets (buffer, sizeof (buffer) - 1, pgpout) != NULL) { if (strcmp ("-----BEGIN PGP MESSAGE-----\n", buffer) == 0) fputs ("-----BEGIN PGP SIGNATURE-----\n", fp); else if (strcmp("-----END PGP MESSAGE-----\n", buffer) == 0) fputs ("-----END PGP SIGNATURE-----\n", fp); else fputs (buffer, fp); empty = 0; /* got some output, so we're ok */ } /* check for errors from PGP */ err = 0; while (fgets (buffer, sizeof (buffer) - 1, pgperr) != NULL) { err = 1; fputs (buffer, stdout); } mutt_wait_filter (thepid); fclose (pgperr); fclose (pgpout); unlink (signedfile); if (fclose (fp) != 0) { mutt_perror ("fclose"); unlink (sigfile); return (NULL); } if (err) mutt_any_key_to_continue (NULL); if (empty) { unlink (sigfile); return (NULL); /* fatal error while signing */ } t = mutt_new_body (); t->type = TYPEMULTIPART; t->subtype = safe_strdup ("signed"); t->use_disp = 0; t->encoding = ENC7BIT; t->parameter = p = mutt_new_parameter (); p->attribute = safe_strdup ("protocol"); p->value = safe_strdup ("application/pgp-signature"); p->next = mutt_new_parameter (); p = p->next; p->attribute = safe_strdup ("micalg"); p->value = safe_strdup (PgpSignMicalg); p->next = mutt_new_parameter (); p = p->next; p->attribute = safe_strdup ("boundary"); p->value = mutt_generate_boundary (); t->parts = a; a = t; t->parts->next = mutt_new_body (); t = t->parts->next; t->type = TYPEAPPLICATION; t->subtype = safe_strdup ("pgp-signature"); t->filename = safe_strdup (sigfile); t->use_disp = 0; t->encoding = ENC7BIT; t->unlink = 1; /* ok to remove this file after sending. */ return (a); } /* This routine attempts to find the keyids of the recipients of a message. * It returns NULL if any of the keys can not be found. */ char *pgp_findKeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc) { char *key, *keylist = NULL; size_t keylist_size = 0; size_t keylist_used = 0; ADDRESS *tmp = NULL; ADDRESS **last = &tmp; ADDRESS *p; int i; KEYINFO *db; KEYINFO *k_info; struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_ENCRYPT); if(!pgp) return NULL; db = pgp->read_pubring(pgp); for (i = 0; i < 3; i++) { switch (i) { case 0: p = to; break; case 1: p = cc; break; case 2: p = bcc; break; default: abort (); } *last = rfc822_cpy_adr (p); while (*last) last = &((*last)->next); } tmp = mutt_remove_duplicates (tmp); for (p = tmp; p ; p = p->next) { if ((k_info = ki_getkeybyaddr (pgp, p, db, KEYFLAG_CANENCRYPT)) == NULL) { char buf[LONG_STRING]; snprintf (buf, sizeof (buf), "Enter keyID for %s: ", p->mailbox); if ((key = pgp_ask_for_key (pgp, db, buf, p->mailbox, KEYFLAG_CANENCRYPT, NULL)) == NULL) { pgp_close_keydb (&db); safe_free ((void **)&keylist); rfc822_free_address (&tmp); return NULL; } } else key = pgp_keyid(k_info); keylist_size += strlen (key) + 4; safe_realloc ((void **)&keylist, keylist_size); sprintf (keylist + keylist_used, "%s0x%s", keylist_used ? " " : "", key); keylist_used = strlen (keylist); } rfc822_free_address (&tmp); pgp_close_keydb (&db); return (keylist); } BODY *pgp_encrypt_message (BODY *a, char *keylist, int sign) { char buf[LONG_STRING]; char tempfile[_POSIX_PATH_MAX], pgperrfile[_POSIX_PATH_MAX]; char pgpinfile[_POSIX_PATH_MAX]; FILE *pgpin, *pgperr, *fpout, *fptmp; BODY *t; PARAMETER *p; int err = 0; int empty; pid_t thepid; struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_ENCRYPT); if(!pgp) return NULL; mutt_mktemp (tempfile); if ((fpout = safe_fopen (tempfile, "w+")) == NULL) { mutt_perror (tempfile); return (NULL); } mutt_mktemp (pgperrfile); if ((pgperr = safe_fopen (pgperrfile, "w+")) == NULL) { mutt_perror (pgperrfile); unlink(tempfile); fclose(fpout); return NULL; } unlink (pgperrfile); mutt_mktemp(pgpinfile); if((fptmp = safe_fopen(pgpinfile, "w")) == NULL) { mutt_perror(pgpinfile); unlink(tempfile); fclose(fpout); fclose(pgperr); return NULL; } if (sign) convert_to_7bit (a); mutt_write_mime_header (a, fptmp); fputc ('\n', fptmp); mutt_write_mime_body (a, fptmp); fclose(fptmp); if ((thepid = pgp->invoke_encrypt (pgp, &pgpin, NULL, NULL, -1, fileno (fpout), fileno (pgperr), pgpinfile, keylist, sign)) == -1) { fclose (pgperr); unlink(pgpinfile); return (NULL); } if (sign) { fputs (PgpPass, pgpin); fputc ('\n', pgpin); } fclose(pgpin); mutt_wait_filter (thepid); unlink(pgpinfile); fflush (fpout); rewind (fpout); empty = (fgetc (fpout) == EOF); fclose (fpout); fflush (pgperr); rewind (pgperr); while (fgets (buf, sizeof (buf) - 1, pgperr) != NULL) { err = 1; fputs (buf, stdout); } fclose (pgperr); /* pause if there is any error output from PGP */ if (err) mutt_any_key_to_continue (NULL); if (empty) { /* fatal error while trying to encrypt message */ unlink (tempfile); return (NULL); } t = mutt_new_body (); t->type = TYPEMULTIPART; t->subtype = safe_strdup ("encrypted"); t->encoding = ENC7BIT; t->use_disp = 0; t->parameter = p = mutt_new_parameter (); p->attribute = safe_strdup ("protocol"); p->value = safe_strdup ("application/pgp-encrypted"); p->next = mutt_new_parameter (); p = p->next; p->attribute = safe_strdup ("boundary"); p->value = mutt_generate_boundary (); t->parts = mutt_new_body (); t->parts->type = TYPEAPPLICATION; t->parts->subtype = safe_strdup ("pgp-encrypted"); t->parts->encoding = ENC7BIT; t->parts->use_disp = 0; t->parts->next = mutt_new_body (); t->parts->next->type = TYPEAPPLICATION; t->parts->next->subtype = safe_strdup ("octet-stream"); t->parts->next->encoding = ENC7BIT; t->parts->next->filename = safe_strdup (tempfile); t->parts->next->use_disp = 0; t->parts->next->unlink = 1; /* delete after sending the message */ mutt_free_body (&a); /* no longer needed! */ return (t); } int pgp_protect (HEADER *msg) { char *pgpkeylist = NULL; BODY *pbody = NULL; /* Do a quick check to make sure that we can find all of the encryption * keys if the user has requested this service. */ set_option (OPTPGPCHECKTRUST); if (msg->pgp & PGPENCRYPT) { if ((pgpkeylist = pgp_findKeys (msg->env->to, msg->env->cc, msg->env->bcc)) == NULL) return (-1); } if ((msg->pgp & PGPSIGN) && !pgp_valid_passphrase ()) return (-1); endwin (); if (msg->pgp & PGPENCRYPT) { pbody = pgp_encrypt_message (msg->content, pgpkeylist, msg->pgp & PGPSIGN); safe_free ((void **) &pgpkeylist); if (!pbody) return (-1); } else if (msg->pgp & PGPSIGN) { if ((pbody = pgp_sign_message (msg->content)) == NULL) return (-1); } msg->content = pbody; return 0; } #endif /* _PGPPATH */