/* * Encrypt the file with the given userids (or ask if none is * supplied). Either FILENAME or FILEFD must be given, but not both. * The caller may provide a checked list of public keys in * PROVIDED_PKS; if not the function builds a list of keys on its own. * * Note that FILEFD is currently only used by cmd_encrypt in the * not yet finished server.c. */ int encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, int outputfd) { iobuf_t inp = NULL; iobuf_t out = NULL; PACKET pkt; PKT_plaintext *pt = NULL; DEK *symkey_dek = NULL; STRING2KEY *symkey_s2k = NULL; int rc = 0, rc2 = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t *afx = NULL; compress_filter_context_t zfx; text_filter_context_t tfx; progress_filter_context_t *pfx; PK_LIST pk_list; int do_compress; int compliant; if (filefd != -1 && filename) return gpg_error (GPG_ERR_INV_ARG); /* Both given. */ do_compress = !!opt.compress_algo; pfx = new_progress_context (); memset( &cfx, 0, sizeof cfx); memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); if (use_symkey && (rc=setup_symkey(&symkey_s2k,&symkey_dek))) { release_progress_context (pfx); return rc; } if (provided_keys) pk_list = provided_keys; else { if ((rc = build_pk_list (ctrl, remusr, &pk_list))) { release_progress_context (pfx); return rc; } } /* Prepare iobufs. */ #ifdef HAVE_W32_SYSTEM if (filefd == -1) inp = iobuf_open (filename); else { inp = NULL; gpg_err_set_errno (ENOSYS); } #else if (filefd == GNUPG_INVALID_FD) inp = iobuf_open (filename); else inp = iobuf_fdopen_nc (FD2INT(filefd), "rb"); #endif if (inp) iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { char xname[64]; rc = gpg_error_from_syserror (); if (filefd != -1) snprintf (xname, sizeof xname, "[fd %d]", filefd); else if (!filename) strcpy (xname, "[stdin]"); else *xname = 0; log_error (_("can't open '%s': %s\n"), *xname? xname : filename, gpg_strerror (rc) ); goto leave; } if (opt.verbose) log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp)); handle_progress (pfx, inp, filename); if (opt.textmode) iobuf_push_filter (inp, text_filter, &tfx); rc = open_outfile (outputfd, filename, opt.armor? 1:0, 0, &out); if (rc) goto leave; if (opt.armor) { afx = new_armor_context (); push_armor_filter (afx, out); } /* Create a session key. */ cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek); if (!opt.def_cipher_algo) { /* Try to get it from the prefs. */ cfx.dek->algo = select_algo_from_prefs (pk_list, PREFTYPE_SYM, -1, NULL); /* The only way select_algo_from_prefs can fail here is when mixing v3 and v4 keys, as v4 keys have an implicit preference entry for 3DES, and the pk_list cannot be empty. In this case, use 3DES anyway as it's the safest choice - perhaps the v3 key is being used in an OpenPGP implementation and we know that the implementation behind any v4 key can handle 3DES. */ if (cfx.dek->algo == -1) { cfx.dek->algo = CIPHER_ALGO_3DES; } /* In case 3DES has been selected, print a warning if any key does not have a preference for AES. This should help to indentify why encrypting to several recipients falls back to 3DES. */ if (opt.verbose && cfx.dek->algo == CIPHER_ALGO_3DES) warn_missing_aes_from_pklist (pk_list); } else { if (!opt.expert && (select_algo_from_prefs (pk_list, PREFTYPE_SYM, opt.def_cipher_algo, NULL) != opt.def_cipher_algo)) { log_info(_("WARNING: forcing symmetric cipher %s (%d)" " violates recipient preferences\n"), openpgp_cipher_algo_name (opt.def_cipher_algo), opt.def_cipher_algo); } cfx.dek->algo = opt.def_cipher_algo; } /* Check compliance. */ if (! gnupg_cipher_is_allowed (opt.compliance, 1, cfx.dek->algo, GCRY_CIPHER_MODE_CFB)) { log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), openpgp_cipher_algo_name (cfx.dek->algo), gnupg_compliance_option_string (opt.compliance)); rc = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { rc = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); write_status_error ("random-compliance", rc); goto leave; } compliant = gnupg_cipher_is_compliant (CO_DE_VS, cfx.dek->algo, GCRY_CIPHER_MODE_CFB); { pk_list_t pkr; for (pkr = pk_list; pkr; pkr = pkr->next) { PKT_public_key *pk = pkr->pk; unsigned int nbits = nbits_from_pk (pk); if (!gnupg_pk_is_compliant (opt.compliance, pk->pubkey_algo, pk->pkey, nbits, NULL)) log_info (_("WARNING: key %s is not suitable for encryption" " in %s mode\n"), keystr_from_pk (pk), gnupg_compliance_option_string (opt.compliance)); if (compliant && !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey, nbits, NULL)) compliant = 0; } } if (compliant) write_status_strings (STATUS_ENCRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS), NULL); cfx.dek->use_aead = use_aead (pk_list, cfx.dek->algo); if (!cfx.dek->use_aead) cfx.dek->use_mdc = !!use_mdc (pk_list, cfx.dek->algo); /* Only do the is-file-already-compressed check if we are using a * MDC or AEAD. This forces compressed files to be re-compressed if * we do not have a MDC to give some protection against chosen * ciphertext attacks. */ if (do_compress && (cfx.dek->use_mdc || cfx.dek->use_aead) && is_file_compressed (filename, &rc2)) { if (opt.verbose) log_info(_("'%s' already compressed\n"), filename); do_compress = 0; } if (rc2) { rc = rc2; goto leave; } make_session_key (cfx.dek); if (DBG_CRYPTO) log_printhex (cfx.dek->key, cfx.dek->keylen, "DEK is: "); rc = write_pubkey_enc_from_list (ctrl, pk_list, cfx.dek, out); if (rc) goto leave; /* We put the passphrase (if any) after any public keys as this * seems to be the most useful on the recipient side - there is no * point in prompting a user for a passphrase if they have the * secret key needed to decrypt. */ if (use_symkey && (rc = write_symkey_enc (symkey_s2k, cfx.dek->use_aead, symkey_dek, cfx.dek, out))) goto leave; if (!opt.no_literal) pt = setup_plaintext_name (filename, inp); /* Get the size of the file if possible, i.e., if it is a real file. */ if (filename && *filename && !iobuf_is_pipe_filename (filename) && !opt.textmode ) { off_t tmpsize; int overflow; if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) && !overflow && opt.verbose) log_info(_("WARNING: '%s' is an empty file\n"), filename ); /* We can't encode the length of very large files because OpenPGP uses only 32 bit for file sizes. So if the size of a file is larger than 2^32 minus some bytes for packet headers, we switch to partial length encoding. */ if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) ) filesize = tmpsize; else filesize = 0; } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { pt->timestamp = make_timestamp(); pt->mode = opt.mimemode? 'm' : opt.textmode ? 't' : 'b'; pt->len = filesize; pt->new_ctb = !pt->len; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0; } else cfx.datalen = filesize && !do_compress ? filesize : 0; /* Register the cipher filter. */ iobuf_push_filter (out, cfx.dek->use_aead? cipher_filter_aead /**/ : cipher_filter_cfb, &cfx); /* Register the compress filter. */ if (do_compress) { int compr_algo = opt.compress_algo; if (compr_algo == -1) { compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP, -1, NULL); if (compr_algo == -1) compr_algo = DEFAULT_COMPRESS_ALGO; /* Theoretically impossible to get here since uncompressed is implicit. */ } else if (!opt.expert && select_algo_from_prefs(pk_list, PREFTYPE_ZIP, compr_algo, NULL) != compr_algo) { log_info (_("WARNING: forcing compression algorithm %s (%d)" " violates recipient preferences\n"), compress_algo_to_string(compr_algo), compr_algo); } /* Algo 0 means no compression. */ if (compr_algo) { if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead)) zfx.new_ctb = 1; push_compress_filter (out,&zfx,compr_algo); } } /* Do the work. */ if (!opt.no_literal) { if ((rc = build_packet( out, &pkt ))) log_error ("build_packet failed: %s\n", gpg_strerror (rc)); } else { /* User requested not to create a literal packet, so we copy the plain data. */ byte copy_buffer[4096]; int bytes_copied; while ((bytes_copied = iobuf_read (inp, copy_buffer, 4096)) != -1) { rc = iobuf_write (out, copy_buffer, bytes_copied); if (rc) { log_error ("copying input to output failed: %s\n", gpg_strerror (rc)); break; } } wipememory (copy_buffer, 4096); /* Burn the buffer. */ } /* Finish the stuff. */ leave: iobuf_close (inp); if (rc) iobuf_cancel (out); else { iobuf_close (out); /* fixme: check returncode */ write_status (STATUS_END_ENCRYPTION); } if (pt) pt->buf = NULL; free_packet (&pkt, NULL); xfree (cfx.dek); xfree (symkey_dek); xfree (symkey_s2k); if (!provided_keys) release_pk_list (pk_list); release_armor_context (afx); release_progress_context (pfx); return rc; }
/* * Ask the GPG Agent for the passphrase. * Mode 0: Allow cached passphrase * 1: No cached passphrase; that is we are asking for a new passphrase * FIXME: Only partially implemented * * Note that TRYAGAIN_TEXT must not be translated. If CANCELED is not * NULL, the function does set it to 1 if the user canceled the * operation. If CACHEID is not NULL, it will be used as the cacheID * for the gpg-agent; if is NULL and a key fingerprint can be * computed, this will be used as the cacheid. */ static char * passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat, const char *tryagain_text, const char *custom_description, const char *custom_prompt, int *canceled) { int rc; char *atext = NULL; char *pw = NULL; PKT_public_key *pk = xmalloc_clear( sizeof *pk ); byte fpr[MAX_FINGERPRINT_LEN]; int have_fpr = 0; char *orig_codeset; char *my_prompt; char hexfprbuf[20*2+1]; const char *my_cacheid; int check = (mode == 1); if (canceled) *canceled = 0; #if MAX_FINGERPRINT_LEN < 20 #error agent needs a 20 byte fingerprint #endif memset (fpr, 0, MAX_FINGERPRINT_LEN ); if( keyid && get_pubkey( pk, keyid ) ) { if (pk) free_public_key( pk ); pk = NULL; /* oops: no key for some reason */ } orig_codeset = i18n_switchto_utf8 (); if (custom_description) atext = native_to_utf8 (custom_description); else if ( !mode && pk && keyid ) { char *uid; size_t uidlen; const char *algo_name = openpgp_pk_algo_name (pk->pubkey_algo); const char *timestr; char *maink; if ( !algo_name ) algo_name = "?"; #define KEYIDSTRING _(" (main key ID %s)") maink = xmalloc ( strlen (KEYIDSTRING) + keystrlen() + 20 ); if( keyid[2] && keyid[3] && keyid[0] != keyid[2] && keyid[1] != keyid[3] ) sprintf( maink, KEYIDSTRING, keystr(&keyid[2]) ); else *maink = 0; uid = get_user_id ( keyid, &uidlen ); timestr = strtimestamp (pk->timestamp); #undef KEYIDSTRING #define PROMPTSTRING _("Please enter the passphrase to unlock the" \ " secret key for the OpenPGP certificate:\n" \ "\"%.*s\"\n" \ "%u-bit %s key, ID %s,\n" \ "created %s%s.\n" ) atext = xmalloc ( 100 + strlen (PROMPTSTRING) + uidlen + 15 + strlen(algo_name) + keystrlen() + strlen (timestr) + strlen (maink) ); sprintf (atext, PROMPTSTRING, (int)uidlen, uid, nbits_from_pk (pk), algo_name, keystr(&keyid[0]), timestr, maink ); xfree (uid); xfree (maink); #undef PROMPTSTRING { size_t dummy; fingerprint_from_pk( pk, fpr, &dummy ); have_fpr = 1; } } else atext = xstrdup ( _("Enter passphrase\n") ); if (!mode && cacheid) my_cacheid = cacheid; else if (!mode && have_fpr) my_cacheid = bin2hex (fpr, 20, hexfprbuf); else my_cacheid = NULL; if (tryagain_text) tryagain_text = _(tryagain_text); my_prompt = custom_prompt ? native_to_utf8 (custom_prompt): NULL; rc = agent_get_passphrase (my_cacheid, tryagain_text, my_prompt, atext, repeat, check, &pw); xfree (my_prompt); xfree (atext); atext = NULL; i18n_switchback (orig_codeset); if (!rc) ; else if ( gpg_err_code (rc) == GPG_ERR_CANCELED ) { log_info (_("cancelled by user\n") ); if (canceled) *canceled = 1; } else { log_error (_("problem with the agent: %s\n"), gpg_strerror (rc)); /* Due to limitations in the API of the upper layers they consider an error as no passphrase entered. This works in most cases but not during key creation where this should definitely not happen and let it continue without requiring a passphrase. Given that now all the upper layers handle a cancel correctly, we simply set the cancel flag now for all errors from the agent. */ if (canceled) *canceled = 1; write_status_error ("get_passphrase", rc); } if (pk) free_public_key( pk ); if (rc) { xfree (pw); return NULL; } return pw; }
/* We don't want to use use_seskey yet because older gnupg versions can't handle it, and there isn't really any point unless we're making a message that can be decrypted by a public key or passphrase. */ static int encrypt_simple (const char *filename, int mode, int use_seskey) { iobuf_t inp, out; PACKET pkt; PKT_plaintext *pt = NULL; STRING2KEY *s2k = NULL; void *enckey = NULL; size_t enckeylen = 0; int rc = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t *afx = NULL; compress_filter_context_t zfx; text_filter_context_t tfx; progress_filter_context_t *pfx; int do_compress = !!default_compress_algo(); if (!gnupg_rng_is_compliant (opt.compliance)) { rc = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); write_status_error ("random-compliance", rc); return rc; } pfx = new_progress_context (); memset( &cfx, 0, sizeof cfx); memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); /* Prepare iobufs. */ inp = iobuf_open(filename); if (inp) iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { rc = gpg_error_from_syserror (); log_error(_("can't open '%s': %s\n"), filename? filename: "[stdin]", strerror(errno) ); release_progress_context (pfx); return rc; } handle_progress (pfx, inp, filename); if (opt.textmode) iobuf_push_filter( inp, text_filter, &tfx ); cfx.dek = NULL; if ( mode ) { int canceled; aead_algo_t aead_algo; s2k = xmalloc_clear( sizeof *s2k ); s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; cfx.dek = passphrase_to_dek (default_cipher_algo (), s2k, 1, 0, NULL, &canceled); if ( !cfx.dek || !cfx.dek->keylen ) { rc = gpg_error (canceled? GPG_ERR_CANCELED:GPG_ERR_INV_PASSPHRASE); xfree (cfx.dek); xfree (s2k); iobuf_close (inp); log_error (_("error creating passphrase: %s\n"), gpg_strerror (rc)); release_progress_context (pfx); return rc; } if (use_seskey && s2k->mode != 1 && s2k->mode != 3) { use_seskey = 0; log_info (_("can't use a symmetric ESK packet " "due to the S2K mode\n")); } /* See whether we want to use AEAD. */ aead_algo = use_aead (NULL, cfx.dek->algo); if ( use_seskey ) { DEK *dek = NULL; rc = encrypt_seskey (cfx.dek, aead_algo, &dek, &enckey, &enckeylen); if (rc) { xfree (cfx.dek); xfree (s2k); iobuf_close (inp); release_progress_context (pfx); return rc; } /* Replace key in DEK. */ xfree (cfx.dek); cfx.dek = dek; } if (aead_algo) cfx.dek->use_aead = aead_algo; else cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo); if (opt.verbose) log_info(_("using cipher %s.%s\n"), openpgp_cipher_algo_name (cfx.dek->algo), cfx.dek->use_aead? openpgp_aead_algo_name (cfx.dek->use_aead) /**/ : "CFB"); } if (do_compress && cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead) && is_file_compressed(filename, &rc)) { if (opt.verbose) log_info(_("'%s' already compressed\n"), filename); do_compress = 0; } if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, 0, &out ))) { iobuf_cancel (inp); xfree (cfx.dek); xfree (s2k); release_progress_context (pfx); return rc; } if ( opt.armor ) { afx = new_armor_context (); push_armor_filter (afx, out); } if ( s2k ) { /* Fixme: This is quite similar to write_symkey_enc. */ PKT_symkey_enc *enc = xmalloc_clear (sizeof *enc + enckeylen); enc->version = cfx.dek->use_aead ? 5 : 4; enc->cipher_algo = cfx.dek->algo; enc->aead_algo = cfx.dek->use_aead; enc->s2k = *s2k; if (enckeylen) { enc->seskeylen = enckeylen; memcpy (enc->seskey, enckey, enckeylen); } pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; if ((rc = build_packet( out, &pkt ))) log_error("build symkey packet failed: %s\n", gpg_strerror (rc) ); xfree (enc); xfree (enckey); enckey = NULL; } if (!opt.no_literal) pt = setup_plaintext_name (filename, inp); /* Note that PGP 5 has problems decrypting symmetrically encrypted data if the file length is in the inner packet. It works when only partial length headers are use. In the past, we always used partial body length here, but since PGP 2, PGP 6, and PGP 7 need the file length, and nobody should be using PGP 5 nowadays anyway, this is now set to the file length. Note also that this only applies to the RFC-1991 style symmetric messages, and not the RFC-2440 style. PGP 6 and 7 work with either partial length or fixed length with the new style messages. */ if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode ) { off_t tmpsize; int overflow; if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) && !overflow && opt.verbose) log_info(_("WARNING: '%s' is an empty file\n"), filename ); /* We can't encode the length of very large files because OpenPGP uses only 32 bit for file sizes. So if the size of a file is larger than 2^32 minus some bytes for packet headers, we switch to partial length encoding. */ if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) ) filesize = tmpsize; else filesize = 0; } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { /* Note that PT has been initialized above in !no_literal mode. */ pt->timestamp = make_timestamp(); pt->mode = opt.mimemode? 'm' : opt.textmode? 't' : 'b'; pt->len = filesize; pt->new_ctb = !pt->len; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0; } else { cfx.datalen = filesize && !do_compress ? filesize : 0; pkt.pkttype = 0; pkt.pkt.generic = NULL; } /* Register the cipher filter. */ if (mode) iobuf_push_filter (out, cfx.dek->use_aead? cipher_filter_aead /**/ : cipher_filter_cfb, &cfx ); /* Register the compress filter. */ if ( do_compress ) { if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead)) zfx.new_ctb = 1; push_compress_filter (out, &zfx, default_compress_algo()); } /* Do the work. */ if (!opt.no_literal) { if ( (rc = build_packet( out, &pkt )) ) log_error("build_packet failed: %s\n", gpg_strerror (rc) ); } else { /* User requested not to create a literal packet, so we copy the plain data. */ byte copy_buffer[4096]; int bytes_copied; while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) { log_error ("copying input to output failed: %s\n", gpg_strerror (rc) ); break; } wipememory (copy_buffer, 4096); /* burn buffer */ } /* Finish the stuff. */ iobuf_close (inp); if (rc) iobuf_cancel(out); else { iobuf_close (out); /* fixme: check returncode */ if (mode) write_status ( STATUS_END_ENCRYPTION ); } if (pt) pt->buf = NULL; free_packet (&pkt, NULL); xfree (enckey); xfree (cfx.dek); xfree (s2k); release_armor_context (afx); release_progress_context (pfx); return rc; }