static char rcsid[] = "@(#)$Id: pgp_decode.c,v 1.51 2007/08/18 06:48:24 hurtta Exp $"; /****************************************************************************** * The Elm (ME+) Mail System - $Revision: 1.51 $ $State: Exp $ * * Modified by: Kari Hurtta * (was hurtta+elm@ozone.FMI.FI) * * Initially written by: Michael Elkins , 1995 *****************************************************************************/ #include "def_melib.h" #include "s_me.h" #include "s_elm.h" DEBUG_VAR(Debug,__FILE__,"pgp"); #ifdef USE_PGP #include #include #ifndef ANSI_C extern int errno; #endif extern int pgp_keeppassfor; /* 5 minutes */ char pgp_passphrase[PGP_NUM][STRING]; int pgp_expires; static CONST char * pgp_names[PGP_NUM] = { "*none*", "PGP 2", "PGP 5", "GnuPG" }; /* if v >= pgp2 returns gives available pgp version */ enum pgp_version have_pgp (v) enum pgp_version v; { enum pgp_version return_value = v; switch(v) { case pgp_none: return_value = pgp_none; ;; case pgp2: { char * pgp2_path_val = give_dt_estr_as_str(&pgp2_path_e, "pgp2"); if (! pgp2_path_val || strcmp(pgp2_path_val,"none") == 0 || pgp2_path_val[0] == '\0') { return_value = pgp_none; } else if (pgp2_path_val[0] == '/') { if (-1 == access(pgp2_path_val,EXECUTE_ACCESS)) { int err = errno; lib_error(CATGETS(elm_msg_cat, MeSet, MePgpCantExecute, "Can't execute pgp: %.50s: %.30s"), pgp2_path_val, error_description(err)); DPRINT(Debug,5,(&Debug, "have_pgp: no access %s: %s\n",pgp2_path_val, error_description(err))); return_value = pgp_none; } } else { lib_error(CATGETS(elm_msg_cat, MeSet, MePgpPath, "PGP path must start with '/': %.60s"), pgp2_path_val); DPRINT(Debug,5,(&Debug, "have_pgp: bad path: %s\n",pgp2_path_val)); return_value = pgp_none; } if (return_value != pgp_none) break; return_value = pgp5; /* FALLTROUGH */ } case pgp5: try_pgp5: { /* give_dt_estr_as_str adds / to end */ char * pgp5_dir_val = give_dt_estr_as_str(&pgp5_dir_e, "pgp5-dir"); if (! pgp5_dir_val || strcmp(pgp5_dir_val,"none") == 0 || pgp5_dir_val[0] == '\0') { return_value = pgp_none; } else if (pgp5_dir_val[0] == '/') { char * path = elm_message(FRM("%spgpv"),pgp5_dir_val); if (-1 == access(path,EXECUTE_ACCESS)) { int err = errno; lib_error(CATGETS(elm_msg_cat, MeSet, MePgpCantExecute, "Can't execute pgp: %.50s: %.30s"), path, error_description(err)); DPRINT(Debug,5,(&Debug, "have_pgp: no access %s: %s\n",path, error_description(err))); return_value = pgp_none; } free(path); } else { lib_error(CATGETS(elm_msg_cat, MeSet, MePgpPath, "PGP path must start with '/': %.60s"), pgp5_dir_val); DPRINT(Debug,5,(&Debug, "have_pgp: bad path: %s\n",pgp5_dir_val)); return_value = pgp_none; } } break; case gpg: { char * gpg_path_val = give_dt_estr_as_str(&gpg_path_e, "gpg"); if (! gpg_path_val || strcmp(gpg_path_val,"none") == 0 || gpg_path_val[0] == '\0') { return_value = pgp_none; } else if (gpg_path_val[0] == '/') { if (-1 == access(gpg_path_val,EXECUTE_ACCESS)) { int err = errno; lib_error(CATGETS(elm_msg_cat, MeSet, MePgpCantExecute, "Can't execute pgp: %.50s: %.30s"), gpg_path_val, error_description(err)); DPRINT(Debug,5,(&Debug, "have_pgp: no access %s: %s\n",gpg_path_val, error_description(err))); return_value = pgp_none; } } else { lib_error(CATGETS(elm_msg_cat, MeSet, MePgpPath, "PGP path must start with '/': %.60s"), gpg_path_val); DPRINT(Debug,5,(&Debug, "have_pgp: bad path: %s\n",gpg_path_val)); return_value = pgp_none; } if (return_value != pgp_none) break; return_value = pgp5; goto try_pgp5; } } DPRINT(Debug,5,(&Debug, "have_pgp(%d)=%d\n",v,return_value)); return return_value; } int pgp_void_passphrase () { int i; enum pgp_version v; int X = 0; for (v = pgp_none; v < PGP_NUM; v++) { if (pgp_passphrase[v][0]) X++; for (i = 0 ; i < sizeof pgp_passphrase[v] ; i++) pgp_passphrase[v][i] = '\0'; pgp_expires = 0; } return X; } static int QueryExpirePassphrase P_((void)); static int QueryExpirePassphrase() { struct timeval now; /* negative implies never expire */ if (pgp_keeppassfor < 0) return(0); gettimeofday(&now, 0); if (now.tv_sec < pgp_expires) { pgp_expires = now.tv_sec + pgp_keeppassfor; return(0); } pgp_void_passphrase (); return(1); } static int GetPassphrase P_((enum pgp_version v)); static int GetPassphrase (v) enum pgp_version v; { char * buffer; struct timeval now; int r; DPRINT(Debug,2,(&Debug, "GetPassphrase(%d) -- pgp_names[%d]=%s\n", v,v,pgp_names[v])); buffer = lib_prompt(1, CATGETS(elm_msg_cat, MeSet, MePgpEnterPassphrase, "Please enter your %s passphrase: "), pgp_names[v]); if (!buffer) { DPRINT(Debug,2,(&Debug, "GetPassphrase=0 -- lib_prompt failed\n")); return 0; } strfcpy(pgp_passphrase[v],buffer,sizeof pgp_passphrase[v]); gettimeofday(&now, 0); if (pgp_keeppassfor > 0) pgp_expires = now.tv_sec + pgp_keeppassfor; free(buffer); r = pgp_passphrase[v][0] != '\0'; DPRINT(Debug,2,(&Debug, "GetPassphrase=%d\n",r)); return(r); } int pgp_goodPassphrase(v) enum pgp_version v; { if (pgp_passphrase[v][0] == '\0' || QueryExpirePassphrase()) return(GetPassphrase(v)); else return(1); } static void close_them P_((struct run_state *rs)); static void close_them(rs) struct run_state *rs; { int *array = rs->ext_init_data; close(array[0]); close(array[1]); if (array[2] != -1) close(array[2]); } /* opens up a PGP process as a child and returns its stdin and stdout */ int pgp_decrypt_init (fpin, fpout, opts, v, rs) FILE **fpin, **fpout; int opts; /* PGP_MESSAGE, PGP_SIGNED_MESSAGE, or PGP_PUBLIC_KEY */ enum pgp_version v; /* 'minimum version' */ struct run_state *rs; { int pgp_child_in[2]; int pgp_child_out[2]; int array[3]; CONST char * argv[10]; char * env[2]; int passpipe[2]; int usepass=FALSE; int code; DPRINT(Debug,2,(&Debug, "pgp_descrypt_init() called with opts=%d\n", opts)); rs->save_errno = 0; if (pipe(pgp_child_in) == -1) { rs->save_errno = errno; return(0); } if (pipe(pgp_child_out) == -1) { rs->save_errno = errno; close(pgp_child_in[0]); close(pgp_child_in[1]); return(0); } if ((opts & PGP_MESSAGE) && pgp_keeppass) { if (pipe(passpipe) == -1) { rs->save_errno = errno; close(pgp_child_in[0]); close(pgp_child_out[0]); close(pgp_child_in[1]); close(pgp_child_out[1]); return(0); } usepass = TRUE; } DPRINT(Debug,3,(&Debug, "usepass = %d.\n", usepass)); sr_call_Raw(OFF); sr_call_ClearScreen(); /* Tell the user why they are waiting */ if (opts & PGP_MESSAGE) { sr_call_Write_to_screen(CATGETS(elm_msg_cat, MeSet, MePgpDecryptMes, "Running pgp: Decrypting message...\n")); } else if (opts & PGP_SIGNED_MESSAGE) { sr_call_Write_to_screen(CATGETS(elm_msg_cat, MeSet, MePgpCheckSig, "Running pgp: Checking signature...\n")); } else if (opts & PGP_PUBLIC_KEY) { sr_call_Write_to_screen(CATGETS(elm_msg_cat, MeSet, MePgpExtractKeys, "Running pgp: Extracting keys...\n")); } else { sr_call_Write_to_screen(CATGETS(elm_msg_cat, MeSet, MePgpRun, "Running pgp ...\n")); } array[0] = pgp_child_in[1]; array[1] = pgp_child_out[0]; array[2] = -1; rs->ext_init_data = array; rs->ext_init = close_them; env[0] = NULL; rs->ext_env = env; switch(v) { case pgp2: case pgp5: if (usepass) { static char buffer[20]; elm_sfprintf(buffer, sizeof buffer,FRM("PGPPASSFD=%d"), passpipe[0]); env[0] = buffer; array[2] = passpipe[1]; } env[1] = NULL; } switch(v) { static char path[1000]; int n; case pgp2: n = 0; { char * pgp2_path_val = give_dt_estr_as_str(&pgp2_path_e, "pgp2"); if (! pgp2_path_val) return 0; argv[n++] = pgp2_path_val; } argv[n++] = "-f"; argv[n++] = "+verbose=0"; argv[n++] = "+KEEPBINARY=OFF"; if (usepass || opts == PGP_SIGNED_MESSAGE || opts == PGP_PUBLIC_KEY) argv[n++] = "+batchmode"; if (opts == PGP_PUBLIC_KEY) argv[n++] = "-ka"; argv[n] = NULL; break; case pgp5: n = 0; { /* give_dt_estr_as_str adds / to end */ char * pgp5_dir_val = give_dt_estr_as_str(&pgp5_dir_e, "pgp5-dir"); if (! pgp5_dir_val) return 0; elm_sfprintf(path, sizeof path,FRM("%spgpv"),pgp5_dir_val); } argv[n++] = path; if (opts != PGP_PUBLIC_KEY) argv[n++] = "-f"; argv[n++] = "+verbose=0"; if (usepass || opts == PGP_SIGNED_MESSAGE || opts == PGP_PUBLIC_KEY) argv[n++] = "+batchmode"; argv[n] = NULL; break; case gpg: n = 0; { char * gpg_path_val = give_dt_estr_as_str(&gpg_path_e, "gpg"); if (! gpg_path_val) return 0; argv[n++] = gpg_path_val; } if (usepass) { static char buffer[10]; argv[n++] = "--passphrase-fd"; elm_sfprintf(buffer, sizeof buffer,FRM("%d"), passpipe[0]); argv[n++] = buffer; array[2] = passpipe[1]; } if (usepass || opts == PGP_SIGNED_MESSAGE || opts == PGP_PUBLIC_KEY) argv[n++] = "--batch"; if (opts == PGP_PUBLIC_KEY) argv[n++] = "--import"; else argv[n++] = "--decrypt"; argv[n] = NULL; break; } code = start_run(rs, SY_RUN_STATE_INIT|SY_RUN_STATE_ENV , argv , pgp_child_in[0],pgp_child_out[1]); close (pgp_child_in[0]); close (pgp_child_out[1]); if (!code) return 0; /* now send the passphrase if needed */ if (usepass) { DPRINT(Debug,3,(&Debug, "pgp_decrypt_init: sending pgp passphrase.\n")); close (passpipe[0]); write (passpipe[1], pgp_passphrase[v], strlen(pgp_passphrase[v])); write (passpipe[1], "\n", 1); close (passpipe[1]); } if ((*fpin = fdopen(pgp_child_out[0], "r")) == 0) { int tmp; int err = errno; DPRINT(Debug,3,(&Debug, "pgp_decrypt_init: fdopen(%d) failed, errno=%d\n", pgp_child_out[0],err)); kill(rs->pid,SIGTERM); wait_end(rs,&tmp); rs->save_errno = err; sr_call_Raw(ON); return(0); } if ((*fpout = fdopen(pgp_child_in[1], "w")) == 0) { int err = errno; int tmp; DPRINT(Debug,3,(&Debug, "pgp_decrypt_init: fdopen(%d) failed, errno=%d\n", pgp_child_out[0],err)); kill(rs->pid,SIGTERM); wait_end(rs,&tmp); rs->save_errno = err; sr_call_Raw(ON); return(0); } return(code); } static int pgp_mime_opts P_((struct mime_param *s)); static int pgp_mime_opts (s) struct mime_param *s; { if (s) { CONST char *pv; pv = get_mime_param_compat(s,"format"); if (pv) { if (istrcmp (pv, "keys-only") == 0) return PGP_PUBLIC_KEY; } pv = get_mime_param_compat(s,"x-action"); if (pv) { if (istrcmp (pv, "encryptsign") == 0) return (PGP_MESSAGE | PGP_SIGNED_MESSAGE); else if (istrcmp (pv, "encrypt") == 0) return PGP_MESSAGE; else if (istrcmp (pv, "sign") == 0) return PGP_SIGNED_MESSAGE; else if (istrcmp (pv, "signclear") == 0) return PGP_SIGNED_MESSAGE; } } return PGP_MESSAGE; } enum pgp_version decode_pgp_version(c) char * c; { int se = give_dt_enumerate_as_int(&send_pgp_version); if (0 == strncmp(c,"2.",2)) { DPRINT(Debug,4,(&Debug, "decode_pgp_version: Version -- PGP 2\n")); return pgp2; } else if (0 == strncmp(c,"GnuPG",5)) { DPRINT(Debug,4,(&Debug, "decode_pgp_version: Version -- GnuPG\n")); return gpg; } else { /* give_dt_estr_as_str adds / to end */ char * pgp5_dir_val = give_dt_estr_as_str(&pgp5_dir_e, "pgp5-dir"); DPRINT(Debug,4,(&Debug, "decode_pgp_version: Version -- PGP 5?\n")); if (pgp5_dir_val && '/' == pgp5_dir_val[0]) return pgp5; else return (enum pgp_version) se; } } /* Returns -1 on failure otherwise same mask than mime_classify_media() */ #if ANSI_C mime_run_selector pgp_selector; #endif int pgp_selector(p,hdr) mime_t *p; struct header_rec * hdr; { int flags = 0; DPRINT(Debug,11,(&Debug, "pgp_selector(%p) -- type: %p=%s/%s\n", p, p->TYPE, get_major_type_name(p->TYPE), get_subtype_name(p->TYPE))); DPRINT(Debug,11,(&Debug, "pgp_selector(%p) = %d\n", p, flags)); return flags; } void pgp_decode (m, s_in, s_out, defcharset, mss, badtype) mime_t *m; in_state_t *s_in; out_state_t *s_out; charset_t defcharset; struct header_rec * mss; type_mismatch_prompt *badtype; { /* This procedure implements the de-facto standard for using PGP with MIME. * Content-Type: application/pgp * Required-Parameters: none * Optional parameters: format, x-action * format = mime | text | keys-only * mime : indicates that the signed/encrypted body contains a MIME * compliant body and should be parsed recursively. * text : [DEFAULT if there is no format option]. This option * means that the encrypted/signed data should be presented * to the user after processing, no additional processing * needed. * keys-only: * The data in the body represents public key data only * x-action = encryptsign | encrypt | sign * This keyword is meant to be helpful to the application, but is * not required, and may not even be necessary to look at. * * encryptsign : the application/pgp data is both signed and * encrypted. * encrypt : the data is encrypted only * sign : the data is signed only */ char buffer[LONG_STRING]; FILE *pgpout, *pgpin, *tmpfp=NULL, *decode_fp = NULL; int code = 0, inbody = FALSE, opts, len, raw, stat = -1, bytes = 0, nested = FALSE; /* PGP output should be parsed as a MIME body */ int skip_flag = 0; mime_t *tmpmt; enum pgp_version v = pgp2; int armor_header = 0; struct run_state RS; int was_binary = 0; in_state_t newstate2; enum pgp_version version; char *tmp; CONST char *pv; int was_text = 0; tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir"); if (!tmp) return; in_state_clear(&newstate2,STATE_in_file); raw = sr_call_RawState (); if ( (pv = get_mime_param_compat(m->TYPE_opts,"format")) && (istrcmp (pv, "mime") == 0)) { char * tempfile = elm_message(FRM("%selmPT%d"), tmp, getpid ()); nested = TRUE; DPRINT(Debug,3,(&Debug, "pgp_decode: format=mime\n")); if (NULL == (tmpfp = safeopen_rdwr(tempfile))) { lib_error(CATGETS(elm_msg_cat, MeSet, MePgpCantCreate, "Failed to create file for decoding.")); state_puts("[ ",s_out); state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpCantCreate, "Failed to create file for decoding.")); state_puts(" ]\n",s_out); free(tempfile); return; } unlink (tempfile); /* Filename is no longer needed ... */ free(tempfile); } else tmpfp = NULL; opts = pgp_mime_opts(m->TYPE_opts); buffer[0] = '\0'; if (opts & PGP_PUBLIC_KEY) { state_printf (s_out, CATGETS(elm_msg_cat, MeSet, MePgpPublicKeys, "(** This message contains PGP public key(s) **)\n")); state_putc('\n',s_out); } /* Decode Content-transfer-encoding */ if (NULL != (decode_fp = arrange_decoded(m,s_in,s_out,&newstate2,NULL))) { /* If arrange_decoded was treated this as text, don't consider * about binary PGP files -- that happens when type is text/x-pgp */ if (check_type_pattern) { if (!check_type_magic(m,&newstate2, s_out, badtype)) { DPRINT(Debug,11,(&Debug, "pgp_decode: mime_type_rejected\n")); goto FAILTYPE; } } /* 7bit and 8bit are only allowed for line orienteed types */ if (m->encoding == ENCODING_NONE || m->encoding == ENCODING_7BIT || m->encoding == ENCODING_8BIT) { DPRINT(Debug,11,(&Debug, "pgp_decode: textual encoding (%s)\n", ENCODING(m->encoding))); was_text = 1; } else was_text = give_text_type_code(m->TYPE); DPRINT(Debug,4,(&Debug, "pgp_decode: was_text = %d\n", was_text)); /* Print text before PGP armor * */ len = state_getl (buffer, sizeof (buffer), &newstate2); if ( len < 1) { state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpNoText1, "[ No text in PGP section. ]\n")); } else { int c = (unsigned char) buffer[0],i; if (!pgp_noarmor && opts != PGP_PUBLIC_KEY) { was_binary = 1; /* default assumption */ goto pgp_found; } if (!was_text) { /* Check if that is binary PGP file */ if (c & 0x80) { /* In PGP block have type byte which have higgest bit set * always. so if in first byte have higgest bit set assume * PGP binary file. */ DPRINT(Debug,4,(&Debug, "pgp_decode: first byte = %d -- assume binary PGP file\n", c)); was_binary = 1; goto pgp_found; } /* Another check */ for (i = 0; i < len; i++) { if (buffer[i] == 0) { DPRINT(Debug,4,(&Debug, "pgp_decode: byte idx=%d is zero -- binary file?\n", i)); state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpBinary, "[ Binary file, but does not look like PGP ]\n")); was_binary = 1; goto pgp_found; } } } if (strncmp(buffer, "-----BEGIN PGP", 14) == 0) goto pgp_found; state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpBefore, "[ There is text before PGP section. ]\n")); if (set_filter(m,s_out,NULL, mss ? mss->override_charset : NULL) ) { /* Character set filtering */ do { state_add_prefix(s_out); /* Take care of CRLF => LF conversion in here because * arrange_decoded() is treating application/pgp as * binary type (it need to treate it as binary because * it may be binary) */ if (!was_text && len > 1 && buffer[len - 1] == '\n' && buffer[len - 2] == '\r') { buffer[len - 2] = '\n'; buffer[len - 1] = '\0'; len--; } state_put(buffer,len,s_out); } while ((len = state_getl (buffer, sizeof (buffer), &newstate2)) > 0 && strncmp(buffer, "-----BEGIN PGP", 14) != 0); } else { DPRINT(Debug,4,(&Debug, "pgp_decode: Unsupported character set?\n")); } pgp_found: s_out -> filter = NULL; } } else { char * msg = NULL; if (opts & PGP_MESSAGE) msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpEncodedCantEncoding, "-- Start of PGP encoded section -- can't decode content-transfer-encoding\n")); else if (opts & PGP_SIGNED_MESSAGE) msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpSignedCantEncoding, "-- Start of PGP signed section -- can't decode content-transfer-encoding\n")); else msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpCantEncoding, "-- Start of PGP section -- can't decode content-transfer-encoding\n")); state_puts(msg,s_out); free(msg); return; } if ( len < 1) { char * msg = NULL; if (opts & PGP_MESSAGE) msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpNoTextEncoded, "[ No text in PGP encoded section ]\n")); else if (opts & PGP_SIGNED_MESSAGE) msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpNoTextSigned, "[ No text in PGP signed section ]\n")); else msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpNoText, "[ No text in PGP section ]\n")); state_puts(msg,s_out); free(msg); return; } if (!was_binary) { int l = in_state_ftell(&newstate2); int len1; char buf[STRING]; /* * On PGP 2 messages these is empty line immediately after * -----BEGIN PGP SIGNED MESSAGE---- * * If there is something other such as * Hash: SHA1 * PGP 2 does not understand message. */ while ((len1 = state_getl (buf, sizeof (buf), &newstate2)) > 0) { if ((len1 == 1 && buf[0] == '\n') || (len1 == 2 && buf[0] == '\r' && buf[1] == '\n')) break; if (opts == PGP_SIGNED_MESSAGE) { DPRINT(Debug,4,(&Debug, "pgp_decode: peek: Header on armor -- requires PGP 5 or GnuPG\n" )); v = gpg; armor_header++; break; } if (0 == strncmp("Version: ",buf,9)) { char *c = buf+9; v = decode_pgp_version(c); if (armor_header && pgp2 == v) v = gpg; } } /* Look also for -----BEGIN PGP SIGNATURE----- ... */ if (opts == PGP_SIGNED_MESSAGE && len1 > 0) { DPRINT(Debug,4,(&Debug, "pgp_decode: Looking for -----BEGIN PGP SIGNATURE\n")); while ((len1 = state_getl (buf, sizeof (buf), &newstate2)) > 0) { if (len1 > 24 && 0 == strncmp(buf, "-----BEGIN PGP SIGNATURE",24)) break; } while ((len1 = state_getl (buf, sizeof (buf), &newstate2)) > 0) { if ((len1 == 1 && buf[0] == '\n') || (len1 == 2 && buf[0] == '\r' && buf[1] == '\n')) break; if (0 == strncmp("Version: ",buf,9)) { char *c = buf+9; v = decode_pgp_version(c); if (armor_header && pgp2 == v) v = gpg; } } } in_state_fseek(&newstate2,l); } version = have_pgp(v); if (!version) { if (was_binary) { state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpNotAvailSkipping, "[ PGP not available, skipping... ]\n")); return; } else state_printf (s_out, CATGETS(elm_msg_cat, MeSet, MePgpNotAvailRawdata, "[ PGP not available, raw data follows ]\n")); goto fail; } if ((opts & PGP_MESSAGE) && pgp_keeppass) { if (!pgp_goodPassphrase(version)) { lib_error(CATGETS(elm_msg_cat, MeSet, MePgpBadPassphrase, "Decrypting message... Bad PGP passphrase.")); state_putc('[',s_out); state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpBadPassphrase, "Decrypting message... Bad PGP passphrase.")); state_puts("]\n",s_out); return; } } if (!pgp_decrypt_init (&pgpout, &pgpin, opts, version, &RS)) { if (was_binary) state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpInternalSkipping, "[ Internal error while calling pgp, skipping... ]\n")); else { state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpInternalRawdata, "[ Internal error while calling pgp, raw data follows ]\n")); fail: do { state_add_prefix(s_out); if (len > 1 && buffer[len - 1] == '\n' && buffer[len - 2] == '\r') { buffer[len - 2] = '\n'; buffer[len - 1] = '\0'; len--; } state_put(buffer,len,s_out); bytes += len; } while ((len = state_getl (buffer, sizeof (buffer), &newstate2)) > 0); state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpRawEnd, "[ End of raw data. ]\n")); } return; } do { fwrite(buffer,1,len,pgpin); bytes += len; } while ((len = state_getl (buffer, sizeof (buffer), &newstate2)) > 0); fclose (pgpin); code = run_already_done(&RS,&stat); if (code != 0) { char * msg = NULL; if (opts & PGP_MESSAGE) msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpStartEncoded1, "-- Start of PGP encoded section%s\n"), code < 0 || stat ? catgets(elm_msg_cat, MeSet, MePgpFail, ", PGP failed!") : "."); else if (opts & PGP_SIGNED_MESSAGE) msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpStartSigned1, "-- Start of PGP signed section%s\n"), code < 0 || stat ? catgets(elm_msg_cat, MeSet, MePgpFail, ", PGP failed!") : "."); else if (opts & PGP_PUBLIC_KEY) msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpStartOutput1, "-- Start of PGP output%s\n"), code < 0 || stat ? catgets(elm_msg_cat, MeSet, MePgpFail, ", PGP failed!") : "."); else msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpStart1, "-- Start of PGP section%s\n"), code < 0 || stat ? catgets(elm_msg_cat, MeSet, MePgpFail, ", PGP failed!") : "."); state_puts(msg,s_out); free(msg); } else { if (opts & PGP_MESSAGE) state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpStartEncoded, "-- Start of PGP encoded section.\n")); else if (opts & PGP_SIGNED_MESSAGE) state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpStartSigned, "-- Start of PGP signed section.\n")); else if (opts & PGP_PUBLIC_KEY) state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpStartOutput, "-- Start of PGP output.\n")); else state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpStart, "-- Start of PGP section.\n")); } bytes = 0; if (was_text && !nested) { if (set_filter(m,s_out,NULL, mss ? mss->override_charset : NULL)) { DPRINT(Debug,4,(&Debug, "pgp_decode: Filtering according of charset enabled.\n")); } else { DPRINT(Debug,4,(&Debug, "pgp_decode: Unsupported character set?\n")); skip_flag++; } } retry: while ((len = mail_gets (buffer, sizeof (buffer), pgpout)) > 0) { if (nested) { if (buffer[0] == '\n' || (buffer[0] == '\r' && buffer[1] == '\n')) inbody = TRUE; fputs (buffer, tmpfp); if (inbody) bytes += len; } else if (!skip_flag) { state_add_prefix(s_out); state_puts(buffer,s_out); } } s_out -> filter = NULL; if (ferror(pgpout) && EINTR == errno) { clearerr(pgpout); DPRINT(Debug,5,(&Debug, "Reading of result interrupted (EINTR) -- retrying\n")); if (0 == code) { code = run_already_done(&RS,&stat); if (0 != code) { DPRINT(Debug,5,(&Debug, "now pgp/gpg is completing\n")); } } goto retry; } fclose (pgpout); pgpout = NULL; if (nested) { struct in_state s2_in; in_state_clear(&s2_in,STATE_in_file); DPRINT(Debug,3,(&Debug, "pgp_decode: parsing decrypted data as MIME\n")); if (EOF == fflush(tmpfp)) { lib_error(CATGETS(elm_msg_cat, MeSet, MePgpErrorFlush, "Error when flushing temporary file.")); state_putc('[',s_out); state_printf(s_out, CATGETS(elm_msg_cat, MeSet, MePgpErrorFlush, "Error when flushing temporary file.")); state_puts("]\n",s_out); } rewind(tmpfp); /* Rewind it for reading */ tmpmt = mime_read_header (tmpfp, 0, defcharset, & (mss->header_error)); tmpmt->length = bytes; mime_parser_parse(tmpmt,defcharset,tmpfp, & (mss->header_error)); set_in_state_file(tmpfp,&s2_in); /* Pass NULL as message, because this is decryoted contetnt */ mime_decode (tmpmt, &s2_in, s_out, defcharset, NULL, badtype); mime_t_clear(tmpmt); in_state_destroy(&s2_in); fclose (tmpfp); } if (0 == code) code = wait_end(&RS,&stat); call_print_status_cooked(&RS,code < 0 ? -code : 0,stat); if (raw) sr_call_Raw (ON); if (opts & PGP_MESSAGE) elm_sfprintf(buffer,sizeof buffer, CATGETS(elm_msg_cat, MeSet, MePgpEndEncoded, "-- End of PGP encoded section%s\n"), code < 0 || stat ? catgets(elm_msg_cat, MeSet, MePgpFail, ", PGP failed!") : "."); else if (opts & PGP_SIGNED_MESSAGE) elm_sfprintf(buffer,sizeof buffer, CATGETS(elm_msg_cat, MeSet, MePgpEndSigned, "-- End of PGP signed section%s\n"), code < 0 || stat ? catgets(elm_msg_cat, MeSet, MePgpFail, ", PGP failed!") : "."); else if (opts & PGP_PUBLIC_KEY) elm_sfprintf(buffer,sizeof buffer, CATGETS(elm_msg_cat, MeSet, MePgpEndOutput, "-- End of PGP output%s\n"), code < 0 || stat ? catgets(elm_msg_cat, MeSet, MePgpFail, ", PGP failed!") : "."); else elm_sfprintf(buffer,sizeof buffer, CATGETS(elm_msg_cat, MeSet, MePgpEnd, "-- End of PGP section%s\n"), code < 0 || stat ? catgets(elm_msg_cat, MeSet, MePgpFail, ", PGP failed!") : "."); state_puts(buffer,s_out); FAILTYPE: in_state_destroy(&newstate2); if (decode_fp) /* in_state_destroy does not close file!! */ fclose(decode_fp); } void pgp_SG_decoder(body,sign,state_in,state_out,micalg, defcharset, mss, badtype) mime_t *body; mime_t *sign; in_state_t *state_in; out_state_t *state_out; const char *micalg; charset_t defcharset; struct header_rec *mss; type_mismatch_prompt *badtype; { int exit_code = -1; FILE *binary_fp; char *body_name = NULL; in_state_t newstate2; char * buffer; enum pgp_version v = pgp2; enum pgp_version version; in_state_clear(&newstate2,STATE_in_file); if (0 == istrcmp(micalg,"pgp-md5")) { DPRINT(Debug,4,(&Debug, "pgp_SG_decoder: micalg=%s, PGP 2\n", micalg)); v = pgp2; } else { DPRINT(Debug,4,(&Debug, "pgp_SG_decoder: micalg=%s, GPG\n", micalg)); v = gpg; } if (NULL != (binary_fp = arrange_binary(body,state_in,state_out,&newstate2, &body_name))) { in_state_t newstate3; FILE *decode_fp = NULL; char *sign_name = NULL; in_state_clear(&newstate3,STATE_in_file); if (in_state_fseek(state_in,sign->offset) != 0) { lib_error(CATGETS(elm_msg_cat, MeSet, MePgpSGSeekFail, "pgp_SG_decoder: seek failed")); state_putc('[',state_out); state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MePgpSGSeekFail, "pgp_SG_decoder: seek failed")); state_puts("]\n",state_out); } if (NULL != (decode_fp = arrange_decoded(sign,state_in,state_out,&newstate3, &sign_name))) { int i = 0; int len1; char buf[STRING]; while ((len1 = state_getl (buf, sizeof (buf), &newstate3)) > 0) { if (i == 0) { if (strncmp(buf, "-----BEGIN PGP", 14) != 0) break; i++; continue; } if ((len1 == 1 && buf[0] == '\n') || (len1 == 2 && buf[0] == '\r' && buf[1] == '\n')) break; if (0 == strncmp("Version: ",buf,9)) { char *c = buf+9; v = decode_pgp_version(c); } } in_state_fseek(&newstate3,0); if ((version=have_pgp(v))) { struct run_state RS; int stat; CONST char * argv[10]; int raw = sr_call_RawState (); switch(version) { static char path[1000]; case pgp2: { char * pgp2_path_val = give_dt_estr_as_str(&pgp2_path_e, "pgp2"); if (! pgp2_path_val) return; argv[0] = pgp2_path_val; } argv[1] = "+batchmode"; argv[2] = "+TEXTMODE=off"; argv[3] = "+CHARSET=noconv"; argv[4] = sign_name; argv[5] = body_name; argv[6] = NULL; break; case pgp5: { /* give_dt_estr_as_str adds / to end */ char * pgp5_dir_val = give_dt_estr_as_str(&pgp5_dir_e, "pgp5-dir"); if (! pgp5_dir_val) return; elm_sfprintf(path, sizeof path,FRM("%spgpv"), pgp5_dir_val); } argv[0] = path; argv[1] = "+batchmode"; argv[2] = "+TEXTMODE=off"; argv[3] = "+CHARSET=noconv"; argv[4] = sign_name; argv[5] = body_name; argv[6] = NULL; break; case gpg: { char * gpg_path_val = give_dt_estr_as_str(&gpg_path_e, "gpg"); if (! gpg_path_val) return; argv[0] = gpg_path_val; } argv[1] = "--batch"; argv[2] = "--verify"; argv[3] = sign_name; argv[4] = body_name; argv[5] = NULL; break; } sr_call_Raw(OFF); sr_call_ClearScreen(); sr_call_Write_to_screen(CATGETS(elm_msg_cat, MeSet, MePgpCheckSig, "Running pgp: Checking signature...\n")); stat = start_run(&RS,0,argv,-1,-1); if (stat) stat = wait_end(&RS,&exit_code); call_print_status_cooked(&RS,stat < 0 ? -stat : 0,exit_code); if (raw) sr_call_Raw (ON); if (stat) { if (state_out->displaying) { state_puts("\n[",state_out); if (exit_code == 0) state_printf(state_out, CATGETS(elm_msg_cat, MeSet,MePgpSigOK, "Checking application/pgp-signature: OK")); else state_printf(state_out, CATGETS(elm_msg_cat, MeSet,MePgpSigFAILURE, "Checking application/pgp-signature: FAILURE")); state_puts("]\n\n",state_out); } else if (exit_code != 0) { state_puts("\n",state_out); state_printf(state_out, CATGETS(elm_msg_cat, MeSet,MePgpSigFAILURE, "Checking application/pgp-signature: FAILURE")); state_puts("\n",state_out); } } else { if (RS.save_errno) { lib_error(CATGETS(elm_msg_cat, ElmSet, ElmFailErrno, "Failed: %.30s: %.40s"), argv[0],error_description(RS.save_errno)); } else lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantStart, "Can't start %.30s"), argv[0]); } } unlink(sign_name); free(sign_name); in_state_destroy(&newstate3); if (decode_fp) /* in_state_destroy does not close file!! */ fclose(decode_fp); } unlink(body_name); free(body_name); in_state_destroy(&newstate2); if (binary_fp) /* in_state_destroy does not close file!! */ fclose(binary_fp); } state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MePgpStartSigned, "-- Start of PGP signed section.\n")); /* Pass mss as message, because this is NOT decrypted (only signed) */ mime_decode(body,state_in,state_out, defcharset, mss, badtype); buffer = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpEnd, "-- End of PGP section%s\n"), exit_code ? catgets(elm_msg_cat, MeSet, MePgpFail, ", PGP failed!") : "."); state_puts(buffer,state_out); free(buffer); } void pgp_EC_decoder(init,data,state_in,state_out, defcharset, mss, badtype) mime_t *init; mime_t *data; in_state_t *state_in; out_state_t *state_out; charset_t defcharset; struct header_rec *mss; type_mismatch_prompt *badtype; { char * tempfile = NULL; FILE *decode_fp; FILE *tmpfp; in_state_t newstate2; char *tmp; tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir"); in_state_clear(&newstate2,STATE_in_file); if (in_state_fseek(state_in,data->offset) != 0) { lib_error(CATGETS(elm_msg_cat, MeSet, MePgpECSeekFail, "pgp_EC_decoder: seek failed")); state_putc('[',state_out); state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MePgpECSeekFail, "pgp_EC_decoder: seek failed")); state_puts("]\n",state_out); } tempfile = elm_message(FRM("%selmPT%d"), tmp, getpid ()); if (NULL == (tmpfp = safeopen_rdwr(tempfile))) { lib_error(CATGETS(elm_msg_cat, MeSet, MePgpCantCreate, "Failed to create file for decoding.")); state_putc('[',state_out); state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MePgpCantCreate, "Failed to create file for decoding.")); state_puts("]\n",state_out); free(tempfile); return; } unlink (tempfile); /* Filename is no longer needed ... */ free(tempfile); tempfile = NULL; if (NULL != (decode_fp = arrange_decoded(data,state_in,state_out,&newstate2,NULL))) { int i = 0,len1; char buf[STRING]; enum pgp_version v = pgp2; enum pgp_version version; struct in_state s2_in; struct run_state RS; FILE *pgpout,*pgpin; int code; mime_t *tmpmt; int stat; long bytes = 0; int inbody = FALSE; int raw = sr_call_RawState (); in_state_clear(&s2_in,STATE_in_file); while ((len1 = state_getl (buf, sizeof (buf), &newstate2)) > 0) { if (i == 0) { if (strncmp(buf, "-----BEGIN PGP", 14) != 0) break; i++; continue; } if ((len1 == 1 && buf[0] == '\n') || (len1 == 2 && buf[0] == '\r' && buf[1] == '\n')) break; if (0 == strncmp("Version: ",buf,9)) { char *c = buf+9; v = decode_pgp_version(c); } } in_state_fseek(&newstate2,0); version = have_pgp(v); if (!version) { state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MePgpNotAvailSkipping, "[ PGP not available, skipping... ]\n")); return; } if (!pgp_goodPassphrase(version)) { lib_error(CATGETS(elm_msg_cat, MeSet, MePgpBadPassphrase, "Decrypting message... Bad PGP passphrase.")); state_puts("[ ",state_out); state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MePgpBadPassphrase, "Decrypting message... Bad PGP passphrase.")); state_puts(" ]\n",state_out); return; } if (!pgp_decrypt_init (&pgpout, &pgpin, PGP_MESSAGE, version, &RS)) { state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MePgpInternalSkipping, "[ Internal error while calling pgp, skipping... ]\n")); } while ((len1 = state_getl (buf, sizeof (buf), &newstate2)) > 0) { fwrite(buf,1,len1,pgpin); } fclose (pgpin); code = run_already_done(&RS,&stat); if (code != 0) { char * msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpStartEncoded1, "-- Start of PGP encoded section%s\n"), code < 0 || stat ? catgets(elm_msg_cat, MeSet, MePgpFail, ", PGP failed!") : "."); state_puts(msg,state_out); free(msg); } else state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MePgpStartEncoded, "-- Start of PGP encoded section.\n")); bytes = 0; retry: while ((len1 = mail_gets (buf, sizeof (buf), pgpout)) > 0) { if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) inbody = TRUE; fputs (buf, tmpfp); if (inbody) bytes += len1; } if (ferror(pgpout) && EINTR == errno) { clearerr(pgpout); DPRINT(Debug,5,(&Debug, "Reading of result interrupted (EINTR) -- retrying\n")); if (0 == code) { code = run_already_done(&RS,&stat); if (0 != code) { DPRINT(Debug,5,(&Debug, "now pgp/gpg is completing\n")); } } goto retry; } fclose(pgpout); if (EOF == fflush(tmpfp)) { lib_error(CATGETS(elm_msg_cat, MeSet, MePgpErrorFlush, "Error when flushing temporary file.")); state_puts(" [",state_out); state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MePgpErrorFlush, "Error when flushing temporary file.")); state_puts(" ]\n",state_out); } rewind(tmpfp); /* Rewind it for reading */ tmpmt = mime_read_header (tmpfp, 0, defcharset, &(mss->header_error)); tmpmt->length = bytes; mime_parser_parse(tmpmt,defcharset,tmpfp, & (mss->header_error)); set_in_state_file(tmpfp,&s2_in); /* Pass NULL as message, because this is encrypted section */ mime_decode (tmpmt, &s2_in, state_out, defcharset, NULL, badtype); mime_t_clear(tmpmt); in_state_destroy(&s2_in); if (0 == code) code = wait_end(&RS,&stat); call_print_status_cooked(&RS,code < 0 ? -code : 0,stat); if (raw) sr_call_Raw (ON); elm_sfprintf(buf,sizeof buf, CATGETS(elm_msg_cat, MeSet, MePgpEndEncoded, "-- End of PGP encoded section%s\n"), code < 0 || stat ? catgets(elm_msg_cat, MeSet, MePgpFail, ", PGP failed!") : "."); state_puts(buf,state_out); in_state_destroy(&newstate2); if (decode_fp) /* in_state_destroy does not close file!! */ fclose(decode_fp); } else { char * msg = elm_message(CATGETS(elm_msg_cat, MeSet, MePgpEncodedCantEncoding, "-- Start of PGP encoded section -- can't decode content-transfer-encoding\n")); state_puts(msg,state_out); free(msg); } fclose (tmpfp); } #endif /* USE_PGP */ /* * Local Variables: * mode:c * c-basic-offset:4 * buffer-file-coding-system: iso-8859-1 * End: */