diff --git a/g10/dearmor.c b/g10/dearmor.c index 92239ccfd..839227a5b 100644 --- a/g10/dearmor.c +++ b/g10/dearmor.c @@ -1,131 +1,131 @@ /* dearmor.c - Armor utility * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG 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 3 of the License, or * (at your option) any later version. * * GnuPG 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, see . */ #include #include #include #include #include #include "gpg.h" #include "../common/status.h" #include "../common/iobuf.h" #include "../common/util.h" #include "filter.h" #include "packet.h" #include "options.h" #include "main.h" #include "../common/i18n.h" /**************** * Take an armor file and write it out without armor */ int dearmor_file( const char *fname ) { armor_filter_context_t *afx; IOBUF inp = NULL, out = NULL; int rc = 0; int c; afx = new_armor_context (); /* prepare iobufs */ inp = iobuf_open(fname); 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"), fname? fname: "[stdin]", strerror(errno) ); goto leave; } push_armor_filter ( afx, inp ); - if( (rc = open_outfile (-1, fname, 0, 0, &out)) ) + if( (rc = open_outfile (-1, fname, 0, 0, &out, 0)) ) goto leave; while( (c = iobuf_get(inp)) != -1 ) iobuf_put( out, c ); leave: if( rc ) iobuf_cancel(out); else iobuf_close(out); iobuf_close(inp); release_armor_context (afx); return rc; } /**************** * Take file and write it out with armor */ int enarmor_file( const char *fname ) { armor_filter_context_t *afx; IOBUF inp = NULL, out = NULL; int rc = 0; int c; afx = new_armor_context (); /* prepare iobufs */ inp = iobuf_open(fname); 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"), fname? fname: "[stdin]", strerror(errno) ); goto leave; } - if( (rc = open_outfile (-1, fname, 1, 0, &out )) ) + if( (rc = open_outfile (-1, fname, 1, 0, &out, 0 )) ) goto leave; afx->what = 4; afx->hdrlines = "Comment: Use \"gpg --dearmor\" for unpacking\n"; push_armor_filter ( afx, out ); while( (c = iobuf_get(inp)) != -1 ) iobuf_put( out, c ); leave: if( rc ) iobuf_cancel(out); else iobuf_close(out); iobuf_close(inp); release_armor_context (afx); return rc; } diff --git a/g10/encrypt.c b/g10/encrypt.c index c68d6d5d1..64952800b 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -1,1060 +1,1060 @@ /* encrypt.c - Main encryption driver * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, * 2006, 2009 Free Software Foundation, Inc. * Copyright (C) 2016 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG 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 3 of the License, or * (at your option) any later version. * * GnuPG 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, see . */ #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "../common/iobuf.h" #include "keydb.h" #include "../common/util.h" #include "main.h" #include "filter.h" #include "trustdb.h" #include "../common/i18n.h" #include "../common/status.h" #include "pkglue.h" #include "../common/compliance.h" static int encrypt_simple( const char *filename, int mode, int use_seskey ); static int write_pubkey_enc_from_list (ctrl_t ctrl, PK_LIST pk_list, DEK *dek, iobuf_t out); /**************** * Encrypt FILENAME with only the symmetric cipher. Take input from * stdin if FILENAME is NULL. */ int encrypt_symmetric (const char *filename) { return encrypt_simple( filename, 1, 0 ); } /**************** * Encrypt FILENAME as a literal data packet only. Take input from * stdin if FILENAME is NULL. */ int encrypt_store (const char *filename) { return encrypt_simple( filename, 0, 0 ); } /* *SESKEY contains the unencrypted session key ((*SESKEY)->KEY) and the algorithm that will be used to encrypt the contents of the SED packet ((*SESKEY)->ALGO). If *SESKEY is NULL, then a random session key that is appropriate for DEK->ALGO is generated and stored there. Encrypt that session key using DEK and store the result in ENCKEY, which must be large enough to hold (*SESKEY)->KEYLEN + 1 bytes. */ void encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey) { gcry_cipher_hd_t hd; byte buf[33]; log_assert ( dek->keylen <= 32 ); if (!*seskey) { *seskey=xmalloc_clear(sizeof(DEK)); (*seskey)->algo=dek->algo; make_session_key(*seskey); /*log_hexdump( "thekey", c->key, c->keylen );*/ } /* The encrypted session key is prefixed with a one-octet algorithm id. */ buf[0] = (*seskey)->algo; memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); /* We only pass already checked values to the following function, thus we consider any failure as fatal. */ if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) BUG (); if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) BUG (); gcry_cipher_setiv (hd, NULL, 0); gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0); gcry_cipher_close (hd); memcpy( enckey, buf, (*seskey)->keylen + 1 ); wipememory( buf, sizeof buf ); /* burn key */ } /* We try very hard to use a MDC */ int use_mdc (pk_list_t pk_list,int algo) { /* RFC-2440 don't has MDC */ if (RFC2440) return 0; /* --force-mdc overrides --disable-mdc */ if(opt.force_mdc) return 1; if(opt.disable_mdc) return 0; /* Do the keys really support MDC? */ if(select_mdc_from_pklist(pk_list)) return 1; /* The keys don't support MDC, so now we do a bit of a hack - if any of the AESes or TWOFISH are in the prefs, we assume that the user can handle a MDC. This is valid for PGP 7, which can handle MDCs though it will not generate them. 2440bis allows this, by the way. */ if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_AES,NULL)==CIPHER_ALGO_AES) return 1; if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_AES192,NULL)==CIPHER_ALGO_AES192) return 1; if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_AES256,NULL)==CIPHER_ALGO_AES256) return 1; if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_TWOFISH,NULL)==CIPHER_ALGO_TWOFISH) return 1; /* Last try. Use MDC for the modern ciphers. */ if (openpgp_cipher_get_algo_blklen (algo) != 8) return 1; if (opt.verbose) warn_missing_mdc_from_pklist (pk_list); return 0; /* No MDC */ } /* 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; byte enckey[33]; int rc = 0; int seskeylen = 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; 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")); } if ( use_seskey ) { DEK *dek = NULL; seskeylen = openpgp_cipher_get_algo_keylen (default_cipher_algo ()); encrypt_seskey( cfx.dek, &dek, enckey ); xfree( cfx.dek ); cfx.dek = dek; } if (opt.verbose) log_info(_("using cipher %s\n"), openpgp_cipher_algo_name (cfx.dek->algo)); cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo); } if (do_compress && cfx.dek && cfx.dek->use_mdc && 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 ))) + if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, 0, &out, 0 ))) { 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 ) { PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 ); enc->version = 4; enc->cipher_algo = cfx.dek->algo; enc->s2k = *s2k; if ( use_seskey && seskeylen ) { enc->seskeylen = seskeylen + 1; /* algo id */ memcpy (enc->seskey, enckey, seskeylen + 1 ); } 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); } 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, cipher_filter, &cfx ); /* Register the compress filter. */ if ( do_compress ) { if (cfx.dek && cfx.dek->use_mdc) 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 (cfx.dek); xfree (s2k); release_armor_context (afx); release_progress_context (pfx); return rc; } int setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek) { int canceled; *symkey_s2k=xmalloc_clear(sizeof(STRING2KEY)); (*symkey_s2k)->mode = opt.s2k_mode; (*symkey_s2k)->hash_algo = S2K_DIGEST_ALGO; *symkey_dek = passphrase_to_dek (opt.s2k_cipher_algo, *symkey_s2k, 1, 0, NULL, &canceled); if(!*symkey_dek || !(*symkey_dek)->keylen) { xfree(*symkey_dek); xfree(*symkey_s2k); return gpg_error (canceled?GPG_ERR_CANCELED:GPG_ERR_BAD_PASSPHRASE); } return 0; } static int write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek, iobuf_t out) { int rc, seskeylen = openpgp_cipher_get_algo_keylen (dek->algo); PKT_symkey_enc *enc; byte enckey[33]; PACKET pkt; enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1); encrypt_seskey(symkey_dek,&dek,enckey); enc->version = 4; enc->cipher_algo = opt.s2k_cipher_algo; enc->s2k = *symkey_s2k; enc->seskeylen = seskeylen + 1; /* algo id */ memcpy( enc->seskey, enckey, seskeylen + 1 ); pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; if ((rc=build_packet(out,&pkt))) log_error("build symkey_enc packet failed: %s\n",gpg_strerror (rc)); xfree(enc); return rc; } /* * 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); + rc = open_outfile (outputfd, filename, opt.armor? 1:0, 0, &out, 0); 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_mdc = use_mdc (pk_list,cfx.dek->algo); /* Only do the is-file-already-compressed check if we are using a MDC. 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 && 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 ("DEK is: ", cfx.dek->key, cfx.dek->keylen ); 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,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, cipher_filter, &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) 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; } /* * Filter to do a complete public key encryption. */ int encrypt_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len) { size_t size = *ret_len; encrypt_filter_context_t *efx = opaque; int rc = 0; if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */ { BUG(); /* not used */ } else if ( control == IOBUFCTRL_FLUSH ) /* encrypt */ { if ( !efx->header_okay ) { efx->cfx.dek = xmalloc_secure_clear ( sizeof *efx->cfx.dek ); if ( !opt.def_cipher_algo ) { /* Try to get it from the prefs. */ efx->cfx.dek->algo = select_algo_from_prefs (efx->pk_list, PREFTYPE_SYM, -1, NULL); if (efx->cfx.dek->algo == -1 ) { /* Because 3DES is implicitly in the prefs, this can only happen if we do not have any public keys in the list. */ efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO; } /* 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 && efx->cfx.dek->algo == CIPHER_ALGO_3DES) warn_missing_aes_from_pklist (efx->pk_list); } else { if (!opt.expert && select_algo_from_prefs (efx->pk_list,PREFTYPE_SYM, opt.def_cipher_algo, NULL) != opt.def_cipher_algo) log_info(_("forcing symmetric cipher %s (%d) " "violates recipient preferences\n"), openpgp_cipher_algo_name (opt.def_cipher_algo), opt.def_cipher_algo); efx->cfx.dek->algo = opt.def_cipher_algo; } efx->cfx.dek->use_mdc = use_mdc (efx->pk_list,efx->cfx.dek->algo); make_session_key ( efx->cfx.dek ); if (DBG_CRYPTO) log_printhex ("DEK is: ", efx->cfx.dek->key, efx->cfx.dek->keylen); rc = write_pubkey_enc_from_list (efx->ctrl, efx->pk_list, efx->cfx.dek, a); if (rc) return rc; if(efx->symkey_s2k && efx->symkey_dek) { rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek, efx->cfx.dek,a); if(rc) return rc; } iobuf_push_filter (a, cipher_filter, &efx->cfx); efx->header_okay = 1; } rc = iobuf_write (a, buf, size); } else if (control == IOBUFCTRL_FREE) { xfree (efx->symkey_dek); xfree (efx->symkey_s2k); } else if ( control == IOBUFCTRL_DESC ) { mem2str (buf, "encrypt_filter", *ret_len); } return rc; } /* * Write a pubkey-enc packet for the public key PK to OUT. */ int write_pubkey_enc (ctrl_t ctrl, PKT_public_key *pk, int throw_keyid, DEK *dek, iobuf_t out) { PACKET pkt; PKT_pubkey_enc *enc; int rc; gcry_mpi_t frame; print_pubkey_algo_note ( pk->pubkey_algo ); enc = xmalloc_clear ( sizeof *enc ); enc->pubkey_algo = pk->pubkey_algo; keyid_from_pk( pk, enc->keyid ); enc->throw_keyid = throw_keyid; /* Okay, what's going on: We have the session key somewhere in * the structure DEK and want to encode this session key in an * integer value of n bits. pubkey_nbits gives us the number of * bits we have to use. We then encode the session key in some * way and we get it back in the big intger value FRAME. Then * we use FRAME, the public key PK->PKEY and the algorithm * number PK->PUBKEY_ALGO and pass it to pubkey_encrypt which * returns the encrypted value in the array ENC->DATA. This * array has a size which depends on the used algorithm (e.g. 2 * for Elgamal). We don't need frame anymore because we have * everything now in enc->data which is the passed to * build_packet(). */ frame = encode_session_key (pk->pubkey_algo, dek, pubkey_nbits (pk->pubkey_algo, pk->pkey)); rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk, pk->pkey); gcry_mpi_release (frame); if (rc) log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) ); else { if ( opt.verbose ) { char *ustr = get_user_id_string_native (ctrl, enc->keyid); log_info (_("%s/%s encrypted for: \"%s\"\n"), openpgp_pk_algo_name (enc->pubkey_algo), openpgp_cipher_algo_name (dek->algo), ustr ); xfree (ustr); } /* And write it. */ init_packet (&pkt); pkt.pkttype = PKT_PUBKEY_ENC; pkt.pkt.pubkey_enc = enc; rc = build_packet (out, &pkt); if (rc) log_error ("build_packet(pubkey_enc) failed: %s\n", gpg_strerror (rc)); } free_pubkey_enc(enc); return rc; } /* * Write pubkey-enc packets from the list of PKs to OUT. */ static int write_pubkey_enc_from_list (ctrl_t ctrl, PK_LIST pk_list, DEK *dek, iobuf_t out) { if (opt.throw_keyids && (PGP6 || PGP7 || PGP8)) { log_info(_("option '%s' may not be used in %s mode\n"), "--throw-keyids", gnupg_compliance_option_string (opt.compliance)); compliance_failure(); } for ( ; pk_list; pk_list = pk_list->next ) { PKT_public_key *pk = pk_list->pk; int throw_keyid = (opt.throw_keyids || (pk_list->flags&1)); int rc = write_pubkey_enc (ctrl, pk, throw_keyid, dek, out); if (rc) return rc; } return 0; } void encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr) { int rc = 0; if (opt.outfile) { log_error(_("--output doesn't work for this command\n")); return; } if (!nfiles) { char line[2048]; unsigned int lno = 0; while ( fgets(line, DIM(line), stdin) ) { lno++; if (!*line || line[strlen(line)-1] != '\n') { log_error("input line %u too long or missing LF\n", lno); return; } line[strlen(line)-1] = '\0'; print_file_status(STATUS_FILE_START, line, 2); rc = encrypt_crypt (ctrl, -1, line, remusr, 0, NULL, -1); if (rc) log_error ("encryption of '%s' failed: %s\n", print_fname_stdin(line), gpg_strerror (rc) ); write_status( STATUS_FILE_DONE ); } } else { while (nfiles--) { print_file_status(STATUS_FILE_START, *files, 2); if ( (rc = encrypt_crypt (ctrl, -1, *files, remusr, 0, NULL, -1)) ) log_error("encryption of '%s' failed: %s\n", print_fname_stdin(*files), gpg_strerror (rc) ); write_status( STATUS_FILE_DONE ); files++; } } } diff --git a/g10/export.c b/g10/export.c index 8f6371b30..b194a2aed 100644 --- a/g10/export.c +++ b/g10/export.c @@ -1,2407 +1,2407 @@ /* export.c - Export keys in the OpenPGP defined format. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, * 2005, 2010 Free Software Foundation, Inc. * Copyright (C) 1998-2016 Werner Koch * * This file is part of GnuPG. * * GnuPG 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 3 of the License, or * (at your option) any later version. * * GnuPG 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, see . */ #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "keydb.h" #include "../common/util.h" #include "main.h" #include "../common/i18n.h" #include "../common/membuf.h" #include "../common/host2net.h" #include "../common/zb32.h" #include "../common/recsel.h" #include "../common/mbox-util.h" #include "../common/init.h" #include "trustdb.h" #include "call-agent.h" /* An object to keep track of subkeys. */ struct subkey_list_s { struct subkey_list_s *next; u32 kid[2]; }; typedef struct subkey_list_s *subkey_list_t; /* An object to track statistics for export operations. */ struct export_stats_s { ulong count; /* Number of processed keys. */ ulong secret_count; /* Number of secret keys seen. */ ulong exported; /* Number of actual exported keys. */ }; /* A global variable to store the selector created from * --export-filter keep-uid=EXPR. * --export-filter drop-subkey=EXPR. * * FIXME: We should put this into the CTRL object but that requires a * lot more changes right now. */ static recsel_expr_t export_keep_uid; static recsel_expr_t export_drop_subkey; /* Local prototypes. */ static int do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options, export_stats_t stats); static int do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, kbnode_t *keyblock_out, unsigned int options, export_stats_t stats, int *any); static gpg_error_t print_pka_or_dane_records /**/ (iobuf_t out, kbnode_t keyblock, PKT_public_key *pk, const void *data, size_t datalen, int print_pka, int print_dane); static void cleanup_export_globals (void) { recsel_release (export_keep_uid); export_keep_uid = NULL; recsel_release (export_drop_subkey); export_drop_subkey = NULL; } /* Option parser for export options. See parse_options fro details. */ int parse_export_options(char *str,unsigned int *options,int noisy) { struct parse_options export_opts[]= { {"export-local-sigs",EXPORT_LOCAL_SIGS,NULL, N_("export signatures that are marked as local-only")}, {"export-attributes",EXPORT_ATTRIBUTES,NULL, N_("export attribute user IDs (generally photo IDs)")}, {"export-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL, N_("export revocation keys marked as \"sensitive\"")}, {"export-clean",EXPORT_CLEAN,NULL, N_("remove unusable parts from key during export")}, {"export-minimal",EXPORT_MINIMAL|EXPORT_CLEAN,NULL, N_("remove as much as possible from key during export")}, {"export-pka", EXPORT_PKA_FORMAT, NULL, NULL }, {"export-dane", EXPORT_DANE_FORMAT, NULL, NULL }, {"backup", EXPORT_BACKUP, NULL, N_("use the GnuPG key backup format")}, {"export-backup", EXPORT_BACKUP, NULL, NULL }, /* Aliases for backward compatibility */ {"include-local-sigs",EXPORT_LOCAL_SIGS,NULL,NULL}, {"include-attributes",EXPORT_ATTRIBUTES,NULL,NULL}, {"include-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL,NULL}, /* dummy */ {"export-unusable-sigs",0,NULL,NULL}, {"export-clean-sigs",0,NULL,NULL}, {"export-clean-uids",0,NULL,NULL}, {NULL,0,NULL,NULL} /* add tags for include revoked and disabled? */ }; int rc; rc = parse_options (str, options, export_opts, noisy); if (rc && (*options & EXPORT_BACKUP)) { /* Alter other options we want or don't want for restore. */ *options |= (EXPORT_LOCAL_SIGS | EXPORT_ATTRIBUTES | EXPORT_SENSITIVE_REVKEYS); *options &= ~(EXPORT_CLEAN | EXPORT_MINIMAL | EXPORT_PKA_FORMAT | EXPORT_DANE_FORMAT); } return rc; } /* Parse and set an export filter from string. STRING has the format * "NAME=EXPR" with NAME being the name of the filter. Spaces before * and after NAME are not allowed. If this function is called several * times all expressions for the same NAME are concatenated. * Supported filter names are: * * - keep-uid :: If the expression evaluates to true for a certain * user ID packet, that packet and all it dependencies * will be exported. The expression may use these * variables: * * - uid :: The entire user ID. * - mbox :: The mail box part of the user ID. * - primary :: Evaluate to true for the primary user ID. * * - drop-subkey :: If the expression evaluates to true for a subkey * packet that subkey and all it dependencies will be * remove from the keyblock. The expression may use these * variables: * * - secret :: 1 for a secret subkey, else 0. * - key_algo :: Public key algorithm id */ gpg_error_t parse_and_set_export_filter (const char *string) { gpg_error_t err; /* Auto register the cleanup function. */ register_mem_cleanup_func (cleanup_export_globals); if (!strncmp (string, "keep-uid=", 9)) err = recsel_parse_expr (&export_keep_uid, string+9); else if (!strncmp (string, "drop-subkey=", 12)) err = recsel_parse_expr (&export_drop_subkey, string+12); else err = gpg_error (GPG_ERR_INV_NAME); return err; } /* Create a new export stats object initialized to zero. On error returns NULL and sets ERRNO. */ export_stats_t export_new_stats (void) { export_stats_t stats; return xtrycalloc (1, sizeof *stats); } /* Release an export stats object. */ void export_release_stats (export_stats_t stats) { xfree (stats); } /* Print export statistics using the status interface. */ void export_print_stats (export_stats_t stats) { if (!stats) return; if (is_status_enabled ()) { char buf[15*20]; snprintf (buf, sizeof buf, "%lu %lu %lu", stats->count, stats->secret_count, stats->exported ); write_status_text (STATUS_EXPORT_RES, buf); } } /* * Export public keys (to stdout or to --output FILE). * * Depending on opt.armor the output is armored. OPTIONS are defined * in main.h. If USERS is NULL, all keys will be exported. STATS is * either an export stats object for update or NULL. * * This function is the core of "gpg --export". */ int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats) { return do_export (ctrl, users, 0, options, stats); } /* * Export secret keys (to stdout or to --output FILE). * * Depending on opt.armor the output is armored. OPTIONS are defined * in main.h. If USERS is NULL, all secret keys will be exported. * STATS is either an export stats object for update or NULL. * * This function is the core of "gpg --export-secret-keys". */ int export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats) { return do_export (ctrl, users, 1, options, stats); } /* * Export secret sub keys (to stdout or to --output FILE). * * This is the same as export_seckeys but replaces the primary key by * a stub key. Depending on opt.armor the output is armored. OPTIONS * are defined in main.h. If USERS is NULL, all secret subkeys will * be exported. STATS is either an export stats object for update or * NULL. * * This function is the core of "gpg --export-secret-subkeys". */ int export_secsubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats) { return do_export (ctrl, users, 2, options, stats); } /* * Export a single key into a memory buffer. STATS is either an * export stats object for update or NULL. */ gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options, export_stats_t stats, kbnode_t *r_keyblock, void **r_data, size_t *r_datalen) { gpg_error_t err; iobuf_t iobuf; int any; strlist_t helplist; *r_keyblock = NULL; *r_data = NULL; *r_datalen = 0; helplist = NULL; if (!add_to_strlist_try (&helplist, keyspec)) return gpg_error_from_syserror (); iobuf = iobuf_temp (); err = do_export_stream (ctrl, iobuf, helplist, 0, r_keyblock, options, stats, &any); if (!err && !any) err = gpg_error (GPG_ERR_NOT_FOUND); if (!err) { const void *src; size_t datalen; iobuf_flush_temp (iobuf); src = iobuf_get_temp_buffer (iobuf); datalen = iobuf_get_temp_length (iobuf); if (!datalen) err = gpg_error (GPG_ERR_NO_PUBKEY); else if (!(*r_data = xtrymalloc (datalen))) err = gpg_error_from_syserror (); else { memcpy (*r_data, src, datalen); *r_datalen = datalen; } } iobuf_close (iobuf); free_strlist (helplist); if (err && *r_keyblock) { release_kbnode (*r_keyblock); *r_keyblock = NULL; } return err; } /* Export the keys identified by the list of strings in USERS. If Secret is false public keys will be exported. With secret true secret keys will be exported; in this case 1 means the entire secret keyblock and 2 only the subkeys. OPTIONS are the export options to apply. */ static int do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options, export_stats_t stats) { IOBUF out = NULL; int any, rc; armor_filter_context_t *afx = NULL; compress_filter_context_t zfx; memset( &zfx, 0, sizeof zfx); - rc = open_outfile (-1, NULL, 0, !!secret, &out ); + rc = open_outfile (-1, NULL, 0, !!secret, &out, 0 ); if (rc) return rc; if ( opt.armor && !(options & (EXPORT_PKA_FORMAT|EXPORT_DANE_FORMAT)) ) { afx = new_armor_context (); afx->what = secret? 5 : 1; push_armor_filter (afx, out); } rc = do_export_stream (ctrl, out, users, secret, NULL, options, stats, &any); if ( rc || !any ) iobuf_cancel (out); else iobuf_close (out); release_armor_context (afx); return rc; } /* Release an entire subkey list. */ static void release_subkey_list (subkey_list_t list) { while (list) { subkey_list_t tmp = list->next;; xfree (list); list = tmp; } } /* Returns true if NODE is a subkey and contained in LIST. */ static int subkey_in_list_p (subkey_list_t list, KBNODE node) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { u32 kid[2]; keyid_from_pk (node->pkt->pkt.public_key, kid); for (; list; list = list->next) if (list->kid[0] == kid[0] && list->kid[1] == kid[1]) return 1; } return 0; } /* Allocate a new subkey list item from NODE. */ static subkey_list_t new_subkey_list_item (KBNODE node) { subkey_list_t list = xcalloc (1, sizeof *list); if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) keyid_from_pk (node->pkt->pkt.public_key, list->kid); return list; } /* Helper function to check whether the subkey at NODE actually matches the description at DESC. The function returns true if the key under question has been specified by an exact specification (keyID or fingerprint) and does match the one at NODE. It is assumed that the packet at NODE is either a public or secret subkey. */ static int exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, KBNODE node) { u32 kid[2]; byte fpr[MAX_FINGERPRINT_LEN]; size_t fprlen; int result = 0; switch(desc->mode) { case KEYDB_SEARCH_MODE_SHORT_KID: case KEYDB_SEARCH_MODE_LONG_KID: keyid_from_pk (node->pkt->pkt.public_key, kid); break; case KEYDB_SEARCH_MODE_FPR16: case KEYDB_SEARCH_MODE_FPR20: case KEYDB_SEARCH_MODE_FPR: fingerprint_from_pk (node->pkt->pkt.public_key, fpr,&fprlen); break; default: break; } switch(desc->mode) { case KEYDB_SEARCH_MODE_SHORT_KID: if (desc->u.kid[1] == kid[1]) result = 1; break; case KEYDB_SEARCH_MODE_LONG_KID: if (desc->u.kid[0] == kid[0] && desc->u.kid[1] == kid[1]) result = 1; break; case KEYDB_SEARCH_MODE_FPR16: if (!memcmp (desc->u.fpr, fpr, 16)) result = 1; break; case KEYDB_SEARCH_MODE_FPR20: case KEYDB_SEARCH_MODE_FPR: if (!memcmp (desc->u.fpr, fpr, 20)) result = 1; break; default: break; } return result; } /* Return an error if the key represented by the S-expression S_KEY * and the OpenPGP key represented by PK do not use the same curve. */ static gpg_error_t match_curve_skey_pk (gcry_sexp_t s_key, PKT_public_key *pk) { gcry_sexp_t curve = NULL; gcry_sexp_t flags = NULL; char *curve_str = NULL; char *flag; const char *oidstr = NULL; gcry_mpi_t curve_as_mpi = NULL; gpg_error_t err; int is_eddsa = 0; int idx = 0; if (!(pk->pubkey_algo==PUBKEY_ALGO_ECDH || pk->pubkey_algo==PUBKEY_ALGO_ECDSA || pk->pubkey_algo==PUBKEY_ALGO_EDDSA)) return gpg_error (GPG_ERR_PUBKEY_ALGO); curve = gcry_sexp_find_token (s_key, "curve", 0); if (!curve) { log_error ("no reported curve\n"); return gpg_error (GPG_ERR_UNKNOWN_CURVE); } curve_str = gcry_sexp_nth_string (curve, 1); gcry_sexp_release (curve); curve = NULL; if (!curve_str) { log_error ("no curve name\n"); return gpg_error (GPG_ERR_UNKNOWN_CURVE); } oidstr = openpgp_curve_to_oid (curve_str, NULL); if (!oidstr) { log_error ("no OID known for curve '%s'\n", curve_str); xfree (curve_str); return gpg_error (GPG_ERR_UNKNOWN_CURVE); } xfree (curve_str); err = openpgp_oid_from_str (oidstr, &curve_as_mpi); if (err) return err; if (gcry_mpi_cmp (pk->pkey[0], curve_as_mpi)) { log_error ("curves do not match\n"); gcry_mpi_release (curve_as_mpi); return gpg_error (GPG_ERR_INV_CURVE); } gcry_mpi_release (curve_as_mpi); flags = gcry_sexp_find_token (s_key, "flags", 0); if (flags) { for (idx = 1; idx < gcry_sexp_length (flags); idx++) { flag = gcry_sexp_nth_string (flags, idx); if (flag && (strcmp ("eddsa", flag) == 0)) is_eddsa = 1; gcry_free (flag); } } if (is_eddsa != (pk->pubkey_algo == PUBKEY_ALGO_EDDSA)) { log_error ("disagreement about EdDSA\n"); err = gpg_error (GPG_ERR_INV_CURVE); } return err; } /* Return a canonicalized public key algoithms. This is used to compare different flavors of algorithms (e.g. ELG and ELG_E are considered the same). */ static enum gcry_pk_algos canon_pk_algo (enum gcry_pk_algos algo) { switch (algo) { case GCRY_PK_RSA: case GCRY_PK_RSA_E: case GCRY_PK_RSA_S: return GCRY_PK_RSA; case GCRY_PK_ELG: case GCRY_PK_ELG_E: return GCRY_PK_ELG; case GCRY_PK_ECC: case GCRY_PK_ECDSA: case GCRY_PK_ECDH: return GCRY_PK_ECC; default: return algo; } } /* Take a cleartext dump of a secret key in PK and change the * parameter array in PK to include the secret parameters. */ static gpg_error_t cleartext_secret_key_to_openpgp (gcry_sexp_t s_key, PKT_public_key *pk) { gpg_error_t err; gcry_sexp_t top_list; gcry_sexp_t key = NULL; char *key_type = NULL; enum gcry_pk_algos pk_algo; struct seckey_info *ski; int idx, sec_start; gcry_mpi_t pub_params[10] = { NULL }; /* we look for a private-key, then the first element in it tells us the type */ top_list = gcry_sexp_find_token (s_key, "private-key", 0); if (!top_list) goto bad_seckey; if (gcry_sexp_length(top_list) != 2) goto bad_seckey; key = gcry_sexp_nth (top_list, 1); if (!key) goto bad_seckey; key_type = gcry_sexp_nth_string(key, 0); pk_algo = gcry_pk_map_name (key_type); log_assert (!pk->seckey_info); pk->seckey_info = ski = xtrycalloc (1, sizeof *ski); if (!ski) { err = gpg_error_from_syserror (); goto leave; } switch (canon_pk_algo (pk_algo)) { case GCRY_PK_RSA: if (!is_RSA (pk->pubkey_algo)) goto bad_pubkey_algo; err = gcry_sexp_extract_param (key, NULL, "ne", &pub_params[0], &pub_params[1], NULL); for (idx=0; idx < 2 && !err; idx++) if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx])) err = gpg_error (GPG_ERR_BAD_PUBKEY); if (!err) { for (idx = 2; idx < 6 && !err; idx++) { gcry_mpi_release (pk->pkey[idx]); pk->pkey[idx] = NULL; } err = gcry_sexp_extract_param (key, NULL, "dpqu", &pk->pkey[2], &pk->pkey[3], &pk->pkey[4], &pk->pkey[5], NULL); } if (!err) { for (idx = 2; idx < 6; idx++) ski->csum += checksum_mpi (pk->pkey[idx]); } break; case GCRY_PK_DSA: if (!is_DSA (pk->pubkey_algo)) goto bad_pubkey_algo; err = gcry_sexp_extract_param (key, NULL, "pqgy", &pub_params[0], &pub_params[1], &pub_params[2], &pub_params[3], NULL); for (idx=0; idx < 4 && !err; idx++) if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx])) err = gpg_error (GPG_ERR_BAD_PUBKEY); if (!err) { gcry_mpi_release (pk->pkey[4]); pk->pkey[4] = NULL; err = gcry_sexp_extract_param (key, NULL, "x", &pk->pkey[4], NULL); } if (!err) ski->csum += checksum_mpi (pk->pkey[4]); break; case GCRY_PK_ELG: if (!is_ELGAMAL (pk->pubkey_algo)) goto bad_pubkey_algo; err = gcry_sexp_extract_param (key, NULL, "pgy", &pub_params[0], &pub_params[1], &pub_params[2], NULL); for (idx=0; idx < 3 && !err; idx++) if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx])) err = gpg_error (GPG_ERR_BAD_PUBKEY); if (!err) { gcry_mpi_release (pk->pkey[3]); pk->pkey[3] = NULL; err = gcry_sexp_extract_param (key, NULL, "x", &pk->pkey[3], NULL); } if (!err) ski->csum += checksum_mpi (pk->pkey[3]); break; case GCRY_PK_ECC: err = match_curve_skey_pk (key, pk); if (err) goto leave; if (!err) err = gcry_sexp_extract_param (key, NULL, "q", &pub_params[0], NULL); if (!err && (gcry_mpi_cmp(pk->pkey[1], pub_params[0]))) err = gpg_error (GPG_ERR_BAD_PUBKEY); sec_start = 2; if (pk->pubkey_algo == PUBKEY_ALGO_ECDH) sec_start += 1; if (!err) { gcry_mpi_release (pk->pkey[sec_start]); pk->pkey[sec_start] = NULL; err = gcry_sexp_extract_param (key, NULL, "d", &pk->pkey[sec_start], NULL); } if (!err) ski->csum += checksum_mpi (pk->pkey[sec_start]); break; default: pk->seckey_info = NULL; xfree (ski); err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); break; } leave: gcry_sexp_release (top_list); gcry_sexp_release (key); gcry_free (key_type); for (idx=0; idx < DIM(pub_params); idx++) gcry_mpi_release (pub_params[idx]); return err; bad_pubkey_algo: err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; bad_seckey: err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } /* Use the key transfer format given in S_PGP to create the secinfo structure in PK and change the parameter array in PK to include the secret parameters. */ static gpg_error_t transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk) { gpg_error_t err; gcry_sexp_t top_list; gcry_sexp_t list = NULL; char *curve = NULL; const char *value; size_t valuelen; char *string; int idx; int is_v4, is_protected; enum gcry_pk_algos pk_algo; int protect_algo = 0; char iv[16]; int ivlen = 0; int s2k_mode = 0; int s2k_algo = 0; byte s2k_salt[8]; u32 s2k_count = 0; int is_ecdh = 0; size_t npkey, nskey; gcry_mpi_t skey[10]; /* We support up to 9 parameters. */ int skeyidx = 0; struct seckey_info *ski; /* gcry_log_debugsxp ("transferkey", s_pgp); */ top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0); if (!top_list) goto bad_seckey; list = gcry_sexp_find_token (top_list, "version", 0); if (!list) goto bad_seckey; value = gcry_sexp_nth_data (list, 1, &valuelen); if (!value || valuelen != 1 || !(value[0] == '3' || value[0] == '4')) goto bad_seckey; is_v4 = (value[0] == '4'); gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "protection", 0); if (!list) goto bad_seckey; value = gcry_sexp_nth_data (list, 1, &valuelen); if (!value) goto bad_seckey; if (valuelen == 4 && !memcmp (value, "sha1", 4)) is_protected = 2; else if (valuelen == 3 && !memcmp (value, "sum", 3)) is_protected = 1; else if (valuelen == 4 && !memcmp (value, "none", 4)) is_protected = 0; else goto bad_seckey; if (is_protected) { string = gcry_sexp_nth_string (list, 2); if (!string) goto bad_seckey; protect_algo = gcry_cipher_map_name (string); xfree (string); value = gcry_sexp_nth_data (list, 3, &valuelen); if (!value || !valuelen || valuelen > sizeof iv) goto bad_seckey; memcpy (iv, value, valuelen); ivlen = valuelen; string = gcry_sexp_nth_string (list, 4); if (!string) goto bad_seckey; s2k_mode = strtol (string, NULL, 10); xfree (string); string = gcry_sexp_nth_string (list, 5); if (!string) goto bad_seckey; s2k_algo = gcry_md_map_name (string); xfree (string); value = gcry_sexp_nth_data (list, 6, &valuelen); if (!value || !valuelen || valuelen > sizeof s2k_salt) goto bad_seckey; memcpy (s2k_salt, value, valuelen); string = gcry_sexp_nth_string (list, 7); if (!string) goto bad_seckey; s2k_count = strtoul (string, NULL, 10); xfree (string); } /* Parse the gcrypt PK algo and check that it is okay. */ gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "algo", 0); if (!list) goto bad_seckey; string = gcry_sexp_nth_string (list, 1); if (!string) goto bad_seckey; pk_algo = gcry_pk_map_name (string); xfree (string); string = NULL; if (gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey) || gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey) || !npkey || npkey >= nskey) goto bad_seckey; /* Check that the pubkey algo matches the one from the public key. */ switch (canon_pk_algo (pk_algo)) { case GCRY_PK_RSA: if (!is_RSA (pk->pubkey_algo)) pk_algo = 0; /* Does not match. */ break; case GCRY_PK_DSA: if (!is_DSA (pk->pubkey_algo)) pk_algo = 0; /* Does not match. */ break; case GCRY_PK_ELG: if (!is_ELGAMAL (pk->pubkey_algo)) pk_algo = 0; /* Does not match. */ break; case GCRY_PK_ECC: if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA) ; else if (pk->pubkey_algo == PUBKEY_ALGO_ECDH) is_ecdh = 1; else if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA) ; else pk_algo = 0; /* Does not match. */ /* For ECC we do not have the domain parameters thus fix our info. */ npkey = 1; nskey = 2; break; default: pk_algo = 0; /* Oops. */ break; } if (!pk_algo) { err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } /* This check has to go after the ecc adjustments. */ if (nskey > PUBKEY_MAX_NSKEY) goto bad_seckey; /* Parse the key parameters. */ gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "skey", 0); if (!list) goto bad_seckey; for (idx=0;;) { int is_enc; value = gcry_sexp_nth_data (list, ++idx, &valuelen); if (!value && skeyidx >= npkey) break; /* Ready. */ /* Check for too many parameters. Note that depending on the protection mode and version number we may see less than NSKEY (but at least NPKEY+1) parameters. */ if (idx >= 2*nskey) goto bad_seckey; if (skeyidx >= DIM (skey)-1) goto bad_seckey; if (!value || valuelen != 1 || !(value[0] == '_' || value[0] == 'e')) goto bad_seckey; is_enc = (value[0] == 'e'); value = gcry_sexp_nth_data (list, ++idx, &valuelen); if (!value || !valuelen) goto bad_seckey; if (is_enc) { void *p = xtrymalloc (valuelen); if (!p) goto outofmem; memcpy (p, value, valuelen); skey[skeyidx] = gcry_mpi_set_opaque (NULL, p, valuelen*8); if (!skey[skeyidx]) goto outofmem; } else { if (gcry_mpi_scan (skey + skeyidx, GCRYMPI_FMT_STD, value, valuelen, NULL)) goto bad_seckey; } skeyidx++; } skey[skeyidx++] = NULL; gcry_sexp_release (list); list = NULL; /* We have no need for the CSUM value thus we don't parse it. */ /* list = gcry_sexp_find_token (top_list, "csum", 0); */ /* if (list) */ /* { */ /* string = gcry_sexp_nth_string (list, 1); */ /* if (!string) */ /* goto bad_seckey; */ /* desired_csum = strtoul (string, NULL, 10); */ /* xfree (string); */ /* } */ /* else */ /* desired_csum = 0; */ /* gcry_sexp_release (list); list = NULL; */ /* Get the curve name if any, */ list = gcry_sexp_find_token (top_list, "curve", 0); if (list) { curve = gcry_sexp_nth_string (list, 1); gcry_sexp_release (list); list = NULL; } gcry_sexp_release (top_list); top_list = NULL; /* log_debug ("XXX is_v4=%d\n", is_v4); */ /* log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); */ /* log_debug ("XXX is_protected=%d\n", is_protected); */ /* log_debug ("XXX protect_algo=%d\n", protect_algo); */ /* log_printhex ("XXX iv", iv, ivlen); */ /* log_debug ("XXX ivlen=%d\n", ivlen); */ /* log_debug ("XXX s2k_mode=%d\n", s2k_mode); */ /* log_debug ("XXX s2k_algo=%d\n", s2k_algo); */ /* log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt); */ /* log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); */ /* for (idx=0; skey[idx]; idx++) */ /* { */ /* int is_enc = gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE); */ /* log_info ("XXX skey[%d]%s:", idx, is_enc? " (enc)":""); */ /* if (is_enc) */ /* { */ /* void *p; */ /* unsigned int nbits; */ /* p = gcry_mpi_get_opaque (skey[idx], &nbits); */ /* log_printhex (NULL, p, (nbits+7)/8); */ /* } */ /* else */ /* gcry_mpi_dump (skey[idx]); */ /* log_printf ("\n"); */ /* } */ if (!is_v4 || is_protected != 2 ) { /* We only support the v4 format and a SHA-1 checksum. */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); goto leave; } /* We need to change the received parameters for ECC algorithms. The transfer format has the curve name and the parameters separate. We put them all into the SKEY array. */ if (canon_pk_algo (pk_algo) == GCRY_PK_ECC) { const char *oidstr; /* Assert that all required parameters are available. We also check that the array does not contain more parameters than needed (this was used by some beta versions of 2.1. */ if (!curve || !skey[0] || !skey[1] || skey[2]) { err = gpg_error (GPG_ERR_INTERNAL); goto leave; } oidstr = openpgp_curve_to_oid (curve, NULL); if (!oidstr) { log_error ("no OID known for curve '%s'\n", curve); err = gpg_error (GPG_ERR_UNKNOWN_CURVE); goto leave; } /* Put the curve's OID into the MPI array. This requires that we shift Q and D. For ECDH also insert the KDF parms. */ if (is_ecdh) { skey[4] = NULL; skey[3] = skey[1]; skey[2] = gcry_mpi_copy (pk->pkey[2]); } else { skey[3] = NULL; skey[2] = skey[1]; } skey[1] = skey[0]; skey[0] = NULL; err = openpgp_oid_from_str (oidstr, skey + 0); if (err) goto leave; /* Fixup the NPKEY and NSKEY to match OpenPGP reality. */ npkey = 2 + is_ecdh; nskey = 3 + is_ecdh; /* for (idx=0; skey[idx]; idx++) */ /* { */ /* log_info ("YYY skey[%d]:", idx); */ /* if (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE)) */ /* { */ /* void *p; */ /* unsigned int nbits; */ /* p = gcry_mpi_get_opaque (skey[idx], &nbits); */ /* log_printhex (NULL, p, (nbits+7)/8); */ /* } */ /* else */ /* gcry_mpi_dump (skey[idx]); */ /* log_printf ("\n"); */ /* } */ } /* Do some sanity checks. */ if (s2k_count > 255) { /* We expect an already encoded S2K count. */ err = gpg_error (GPG_ERR_INV_DATA); goto leave; } err = openpgp_cipher_test_algo (protect_algo); if (err) goto leave; err = openpgp_md_test_algo (s2k_algo); if (err) goto leave; /* Check that the public key parameters match. Note that since Libgcrypt 1.5 gcry_mpi_cmp handles opaque MPI correctly. */ for (idx=0; idx < npkey; idx++) if (gcry_mpi_cmp (pk->pkey[idx], skey[idx])) { err = gpg_error (GPG_ERR_BAD_PUBKEY); goto leave; } /* Check that the first secret key parameter in SKEY is encrypted and that there are no more secret key parameters. The latter is guaranteed by the v4 packet format. */ if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE)) goto bad_seckey; if (npkey+1 < DIM (skey) && skey[npkey+1]) goto bad_seckey; /* Check that the secret key parameters in PK are all set to NULL. */ for (idx=npkey; idx < nskey; idx++) if (pk->pkey[idx]) goto bad_seckey; /* Now build the protection info. */ pk->seckey_info = ski = xtrycalloc (1, sizeof *ski); if (!ski) { err = gpg_error_from_syserror (); goto leave; } ski->is_protected = 1; ski->sha1chk = 1; ski->algo = protect_algo; ski->s2k.mode = s2k_mode; ski->s2k.hash_algo = s2k_algo; log_assert (sizeof ski->s2k.salt == sizeof s2k_salt); memcpy (ski->s2k.salt, s2k_salt, sizeof s2k_salt); ski->s2k.count = s2k_count; log_assert (ivlen <= sizeof ski->iv); memcpy (ski->iv, iv, ivlen); ski->ivlen = ivlen; /* Store the protected secret key parameter. */ pk->pkey[npkey] = skey[npkey]; skey[npkey] = NULL; /* That's it. */ leave: gcry_free (curve); gcry_sexp_release (list); gcry_sexp_release (top_list); for (idx=0; idx < skeyidx; idx++) gcry_mpi_release (skey[idx]); return err; bad_seckey: err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; outofmem: err = gpg_error (GPG_ERR_ENOMEM); goto leave; } /* Print an "EXPORTED" status line. PK is the primary public key. */ static void print_status_exported (PKT_public_key *pk) { char *hexfpr; if (!is_status_enabled ()) return; hexfpr = hexfingerprint (pk, NULL, 0); write_status_text (STATUS_EXPORTED, hexfpr? hexfpr : "[?]"); xfree (hexfpr); } /* * Receive a secret key from agent specified by HEXGRIP. * * Since the key data from the agent is encrypted, decrypt it using * CIPHERHD context. Then, parse the decrypted key data into transfer * format, and put secret parameters into PK. * * If CLEARTEXT is 0, store the secret key material * passphrase-protected. Otherwise, store secret key material in the * clear. * * CACHE_NONCE_ADDR is used to share nonce for multple key retrievals. */ gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, int cleartext, char **cache_nonce_addr, const char *hexgrip, PKT_public_key *pk) { gpg_error_t err = 0; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; unsigned char *key = NULL; size_t keylen, realkeylen; gcry_sexp_t s_skey; char *prompt; if (opt.verbose) log_info ("key %s: asking agent for the secret parts\n", hexgrip); prompt = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_EXPORT,1); err = agent_export_key (ctrl, hexgrip, prompt, !cleartext, cache_nonce_addr, &wrappedkey, &wrappedkeylen, pk->keyid, pk->main_keyid, pk->pubkey_algo); xfree (prompt); if (err) goto unwraperror; if (wrappedkeylen < 24) { err = gpg_error (GPG_ERR_INV_LENGTH); goto unwraperror; } keylen = wrappedkeylen - 8; key = xtrymalloc_secure (keylen); if (!key) { err = gpg_error_from_syserror (); goto unwraperror; } err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen); if (err) goto unwraperror; realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err); if (!realkeylen) goto unwraperror; /* Invalid csexp. */ err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen); if (!err) { if (cleartext) err = cleartext_secret_key_to_openpgp (s_skey, pk); else err = transfer_format_to_openpgp (s_skey, pk); gcry_sexp_release (s_skey); } unwraperror: xfree (key); xfree (wrappedkey); if (err) { log_error ("key %s: error receiving key from agent:" " %s%s\n", hexgrip, gpg_strerror (err), gpg_err_code (err) == GPG_ERR_FULLY_CANCELED? "":_(" - skipped")); } return err; } /* Write KEYBLOCK either to stdout or to the file set with the * --output option. This is a simplified version of do_export_stream * which supports only a few export options. */ gpg_error_t write_keyblock_to_output (kbnode_t keyblock, int with_armor, unsigned int options) { gpg_error_t err; const char *fname; iobuf_t out; kbnode_t node; armor_filter_context_t *afx = NULL; iobuf_t out_help = NULL; PKT_public_key *pk = NULL; fname = opt.outfile? opt.outfile : "-"; if (is_secured_filename (fname) ) return gpg_error (GPG_ERR_EPERM); out = iobuf_create (fname, 0); if (!out) { err = gpg_error_from_syserror (); log_error(_("can't create '%s': %s\n"), fname, gpg_strerror (err)); return err; } if (opt.verbose) log_info (_("writing to '%s'\n"), iobuf_get_fname_nonnull (out)); if ((options & (EXPORT_PKA_FORMAT|EXPORT_DANE_FORMAT))) { with_armor = 0; out_help = iobuf_temp (); } if (with_armor) { afx = new_armor_context (); afx->what = 1; push_armor_filter (afx, out); } for (node = keyblock; node; node = node->next) { if (is_deleted_kbnode (node)) continue; if (node->pkt->pkttype == PKT_RING_TRUST) continue; /* Skip - they should not be here anyway. */ if (!pk && (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY)) pk = node->pkt->pkt.public_key; if ((options & EXPORT_BACKUP)) err = build_packet_and_meta (out_help? out_help : out, node->pkt); else err = build_packet (out_help? out_help : out, node->pkt); if (err) { log_error ("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (err) ); goto leave; } } err = 0; if (out_help && pk) { const void *data; size_t datalen; iobuf_flush_temp (out_help); data = iobuf_get_temp_buffer (out_help); datalen = iobuf_get_temp_length (out_help); err = print_pka_or_dane_records (out, keyblock, pk, data, datalen, (options & EXPORT_PKA_FORMAT), (options & EXPORT_DANE_FORMAT)); } leave: if (err) iobuf_cancel (out); else iobuf_close (out); iobuf_cancel (out_help); release_armor_context (afx); return err; } /* * Apply the keep-uid filter to the keyblock. The deleted nodes are * marked and thus the caller should call commit_kbnode afterwards. * KEYBLOCK must not have any blocks marked as deleted. */ static void apply_keep_uid_filter (ctrl_t ctrl, kbnode_t keyblock, recsel_expr_t selector) { kbnode_t node; struct impex_filter_parm_s parm; parm.ctrl = ctrl; for (node = keyblock->next; node; node = node->next ) { if (node->pkt->pkttype == PKT_USER_ID) { parm.node = node; if (!recsel_select (selector, impex_filter_getval, &parm)) { /* log_debug ("keep-uid: deleting '%s'\n", */ /* node->pkt->pkt.user_id->name); */ /* The UID packet and all following packets up to the * next UID or a subkey. */ delete_kbnode (node); for (; node->next && node->next->pkt->pkttype != PKT_USER_ID && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ; node = node->next) delete_kbnode (node->next); } /* else */ /* log_debug ("keep-uid: keeping '%s'\n", */ /* node->pkt->pkt.user_id->name); */ } } } /* * Apply the drop-subkey filter to the keyblock. The deleted nodes are * marked and thus the caller should call commit_kbnode afterwards. * KEYBLOCK must not have any blocks marked as deleted. */ static void apply_drop_subkey_filter (ctrl_t ctrl, kbnode_t keyblock, recsel_expr_t selector) { kbnode_t node; struct impex_filter_parm_s parm; parm.ctrl = ctrl; for (node = keyblock->next; node; node = node->next ) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { parm.node = node; if (recsel_select (selector, impex_filter_getval, &parm)) { /*log_debug ("drop-subkey: deleting a key\n");*/ /* The subkey packet and all following packets up to the * next subkey. */ delete_kbnode (node); for (; node->next && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ; node = node->next) delete_kbnode (node->next); } } } } /* Print DANE or PKA records for all user IDs in KEYBLOCK to OUT. The * data for the record is taken from (DATA,DATELEN). PK is the public * key packet with the primary key. */ static gpg_error_t print_pka_or_dane_records (iobuf_t out, kbnode_t keyblock, PKT_public_key *pk, const void *data, size_t datalen, int print_pka, int print_dane) { gpg_error_t err = 0; kbnode_t kbctx, node; PKT_user_id *uid; char *mbox = NULL; char hashbuf[32]; char *hash = NULL; char *domain; const char *s; unsigned int len; estream_t fp = NULL; char *hexdata = NULL; char *hexfpr; hexfpr = hexfingerprint (pk, NULL, 0); hexdata = bin2hex (data, datalen, NULL); if (!hexdata) { err = gpg_error_from_syserror (); goto leave; } ascii_strlwr (hexdata); fp = es_fopenmem (0, "rw,samethread"); if (!fp) { err = gpg_error_from_syserror (); goto leave; } for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { if (node->pkt->pkttype != PKT_USER_ID) continue; uid = node->pkt->pkt.user_id; if (uid->flags.expired || uid->flags.revoked) continue; xfree (mbox); mbox = mailbox_from_userid (uid->name); if (!mbox) continue; domain = strchr (mbox, '@'); *domain++ = 0; if (print_pka) { es_fprintf (fp, "$ORIGIN _pka.%s.\n; %s\n; ", domain, hexfpr); print_utf8_buffer (fp, uid->name, uid->len); es_putc ('\n', fp); gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox)); xfree (hash); hash = zb32_encode (hashbuf, 8*20); if (!hash) { err = gpg_error_from_syserror (); goto leave; } len = strlen (hexfpr)/2; es_fprintf (fp, "%s TYPE37 \\# %u 0006 0000 00 %02X %s\n\n", hash, 6 + len, len, hexfpr); } if (print_dane && hexdata) { es_fprintf (fp, "$ORIGIN _openpgpkey.%s.\n; %s\n; ", domain, hexfpr); print_utf8_buffer (fp, uid->name, uid->len); es_putc ('\n', fp); gcry_md_hash_buffer (GCRY_MD_SHA256, hashbuf, mbox, strlen (mbox)); xfree (hash); hash = bin2hex (hashbuf, 28, NULL); if (!hash) { err = gpg_error_from_syserror (); goto leave; } ascii_strlwr (hash); len = strlen (hexdata)/2; es_fprintf (fp, "%s TYPE61 \\# %u (\n", hash, len); for (s = hexdata; ;) { es_fprintf (fp, "\t%.64s\n", s); if (strlen (s) < 64) break; s += 64; } es_fputs ("\t)\n\n", fp); } } /* Make sure it is a string and write it. */ es_fputc (0, fp); { void *vp; if (es_fclose_snatch (fp, &vp, NULL)) { err = gpg_error_from_syserror (); goto leave; } fp = NULL; iobuf_writestr (out, vp); es_free (vp); } err = 0; leave: xfree (hash); xfree (mbox); es_fclose (fp); xfree (hexdata); xfree (hexfpr); return err; } /* Helper for do_export_stream which writes one keyblock to OUT. */ static gpg_error_t do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, iobuf_t out, int secret, unsigned int options, export_stats_t stats, int *any, KEYDB_SEARCH_DESC *desc, size_t ndesc, size_t descindex, gcry_cipher_hd_t cipherhd) { gpg_error_t err = gpg_error (GPG_ERR_NOT_FOUND); char *cache_nonce = NULL; subkey_list_t subkey_list = NULL; /* Track already processed subkeys. */ int skip_until_subkey = 0; int cleartext = 0; char *hexgrip = NULL; char *serialno = NULL; PKT_public_key *pk; u32 subkidbuf[2], *subkid; kbnode_t kbctx, node; /* NB: walk_kbnode skips packets marked as deleted. */ for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) { if (skip_until_subkey) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) skip_until_subkey = 0; else continue; } /* We used to use comment packets, but not any longer. In * case we still have comments on a key, strip them here * before we call build_packet(). */ if (node->pkt->pkttype == PKT_COMMENT) continue; /* Skip ring trust packets - they should not ne here anyway. */ if (node->pkt->pkttype == PKT_RING_TRUST) continue; /* If exact is set, then we only export what was requested * (plus the primary key, if the user didn't specifically * request it). */ if (desc[descindex].exact && node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { if (!exact_subkey_match_p (desc+descindex, node)) { /* Before skipping this subkey, check whether any * other description wants an exact match on a * subkey and include that subkey into the output * too. Need to add this subkey to a list so that * it won't get processed a second time. * * So the first step here is to check that list and * skip in any case if the key is in that list. * * We need this whole mess because the import * function of GnuPG < 2.1 is not able to merge * secret keys and thus it is useless to output them * as two separate keys and have import merge them. */ if (subkey_in_list_p (subkey_list, node)) skip_until_subkey = 1; /* Already processed this one. */ else { size_t j; for (j=0; j < ndesc; j++) if (j != descindex && desc[j].exact && exact_subkey_match_p (desc+j, node)) break; if (!(j < ndesc)) skip_until_subkey = 1; /* No other one matching. */ } } if (skip_until_subkey) continue; /* Mark this one as processed. */ { subkey_list_t tmp = new_subkey_list_item (node); tmp->next = subkey_list; subkey_list = tmp; } } if (node->pkt->pkttype == PKT_SIGNATURE) { /* Do not export packets which are marked as not * exportable. */ if (!(options & EXPORT_LOCAL_SIGS) && !node->pkt->pkt.signature->flags.exportable) continue; /* not exportable */ /* Do not export packets with a "sensitive" revocation key * unless the user wants us to. Note that we do export * these when issuing the actual revocation (see revoke.c). */ if (!(options & EXPORT_SENSITIVE_REVKEYS) && node->pkt->pkt.signature->revkey) { int i; for (i = 0; i < node->pkt->pkt.signature->numrevkeys; i++) if ((node->pkt->pkt.signature->revkey[i].class & 0x40)) break; if (i < node->pkt->pkt.signature->numrevkeys) continue; } } /* Don't export attribs? */ if (!(options & EXPORT_ATTRIBUTES) && node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->attrib_data) { /* Skip until we get to something that is not an attrib or a * signature on an attrib. */ while (kbctx->next && kbctx->next->pkt->pkttype == PKT_SIGNATURE) kbctx = kbctx->next; continue; } if (secret && (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) { pk = node->pkt->pkt.public_key; if (node->pkt->pkttype == PKT_PUBLIC_KEY) subkid = NULL; else { keyid_from_pk (pk, subkidbuf); subkid = subkidbuf; } if (pk->seckey_info) { log_error ("key %s: oops: seckey_info already set" " - skipped\n", keystr_with_sub (keyid, subkid)); skip_until_subkey = 1; continue; } xfree (hexgrip); err = hexkeygrip_from_pk (pk, &hexgrip); if (err) { log_error ("key %s: error computing keygrip: %s" " - skipped\n", keystr_with_sub (keyid, subkid), gpg_strerror (err)); skip_until_subkey = 1; err = 0; continue; } xfree (serialno); serialno = NULL; if (secret == 2 && node->pkt->pkttype == PKT_PUBLIC_KEY) { /* We are asked not to export the secret parts of the * primary key. Make up an error code to create the * stub. */ err = GPG_ERR_NOT_FOUND; } else err = agent_get_keyinfo (ctrl, hexgrip, &serialno, &cleartext); if ((!err && serialno) && secret == 2 && node->pkt->pkttype == PKT_PUBLIC_KEY) { /* It does not make sense to export a key with its * primary key on card using a non-key stub. Thus we * skip those keys when used with --export-secret-subkeys. */ log_info (_("key %s: key material on-card - skipped\n"), keystr_with_sub (keyid, subkid)); skip_until_subkey = 1; } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND || (!err && serialno)) { /* Create a key stub. */ struct seckey_info *ski; const char *s; pk->seckey_info = ski = xtrycalloc (1, sizeof *ski); if (!ski) { err = gpg_error_from_syserror (); goto leave; } ski->is_protected = 1; if (err) ski->s2k.mode = 1001; /* GNU dummy (no secret key). */ else { ski->s2k.mode = 1002; /* GNU-divert-to-card. */ for (s=serialno; sizeof (ski->ivlen) && *s && s[1]; ski->ivlen++, s += 2) ski->iv[ski->ivlen] = xtoi_2 (s); } if ((options & EXPORT_BACKUP)) err = build_packet_and_meta (out, node->pkt); else err = build_packet (out, node->pkt); if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY) { stats->exported++; print_status_exported (node->pkt->pkt.public_key); } } else if (!err) { err = receive_seckey_from_agent (ctrl, cipherhd, cleartext, &cache_nonce, hexgrip, pk); if (err) { if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) goto leave; skip_until_subkey = 1; err = 0; } else { if ((options & EXPORT_BACKUP)) err = build_packet_and_meta (out, node->pkt); else err = build_packet (out, node->pkt); if (node->pkt->pkttype == PKT_PUBLIC_KEY) { stats->exported++; print_status_exported (node->pkt->pkt.public_key); } } } else { log_error ("key %s: error getting keyinfo from agent: %s" " - skipped\n", keystr_with_sub (keyid, subkid), gpg_strerror (err)); skip_until_subkey = 1; err = 0; } xfree (pk->seckey_info); pk->seckey_info = NULL; { int i; for (i = pubkey_get_npkey (pk->pubkey_algo); i < pubkey_get_nskey (pk->pubkey_algo); i++) { gcry_mpi_release (pk->pkey[i]); pk->pkey[i] = NULL; } } } else /* Not secret or common packets. */ { if ((options & EXPORT_BACKUP)) err = build_packet_and_meta (out, node->pkt); else err = build_packet (out, node->pkt); if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY) { stats->exported++; print_status_exported (node->pkt->pkt.public_key); } } if (err) { log_error ("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (err)); goto leave; } if (!skip_until_subkey) *any = 1; } leave: release_subkey_list (subkey_list); xfree (serialno); xfree (hexgrip); xfree (cache_nonce); return err; } /* Export the keys identified by the list of strings in USERS to the stream OUT. If SECRET is false public keys will be exported. With secret true secret keys will be exported; in this case 1 means the entire secret keyblock and 2 only the subkeys. OPTIONS are the export options to apply. If KEYBLOCK_OUT is not NULL, AND the exit code is zero, a pointer to the first keyblock found and exported will be stored at this address; no other keyblocks are exported in this case. The caller must free the returned keyblock. If any key has been exported true is stored at ANY. */ static int do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, kbnode_t *keyblock_out, unsigned int options, export_stats_t stats, int *any) { gpg_error_t err = 0; PACKET pkt; kbnode_t keyblock = NULL; kbnode_t node; size_t ndesc, descindex; KEYDB_SEARCH_DESC *desc = NULL; KEYDB_HANDLE kdbhd; strlist_t sl; gcry_cipher_hd_t cipherhd = NULL; struct export_stats_s dummystats; iobuf_t out_help = NULL; if (!stats) stats = &dummystats; *any = 0; init_packet (&pkt); kdbhd = keydb_new (); if (!kdbhd) return gpg_error_from_syserror (); /* For the PKA and DANE format open a helper iobuf and for DANE * enforce some options. */ if ((options & (EXPORT_PKA_FORMAT | EXPORT_DANE_FORMAT))) { out_help = iobuf_temp (); if ((options & EXPORT_DANE_FORMAT)) options |= EXPORT_MINIMAL | EXPORT_CLEAN; } if (!users) { ndesc = 1; desc = xcalloc (ndesc, sizeof *desc); desc[0].mode = KEYDB_SEARCH_MODE_FIRST; } else { for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) ; desc = xmalloc ( ndesc * sizeof *desc); for (ndesc=0, sl=users; sl; sl = sl->next) { if (!(err=classify_user_id (sl->d, desc+ndesc, 1))) ndesc++; else log_error (_("key \"%s\" not found: %s\n"), sl->d, gpg_strerror (err)); } keydb_disable_caching (kdbhd); /* We are looping the search. */ /* It would be nice to see which of the given users did actually match one in the keyring. To implement this we need to have a found flag for each entry in desc. To set this flag we must check all those entries after a match to mark all matched one - currently we stop at the first match. To do this we need an extra flag to enable this feature. */ } #ifdef ENABLE_SELINUX_HACKS if (secret) { log_error (_("exporting secret keys not allowed\n")); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } #endif /* For secret key export we need to setup a decryption context. */ if (secret) { void *kek = NULL; size_t keklen; err = agent_keywrap_key (ctrl, 1, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Prepare a cipher context. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (!err) err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) { log_error ("error setting up an encryption context: %s\n", gpg_strerror (err)); goto leave; } xfree (kek); kek = NULL; } for (;;) { u32 keyid[2]; PKT_public_key *pk; err = keydb_search (kdbhd, desc, ndesc, &descindex); if (!users) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; if (err) break; /* Read the keyblock. */ release_kbnode (keyblock); keyblock = NULL; err = keydb_get_keyblock (kdbhd, &keyblock); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); goto leave; } node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("public key packet not found in keyblock - skipped\n"); continue; } stats->count++; setup_main_keyids (keyblock); /* gpg_format_keydesc needs it. */ pk = node->pkt->pkt.public_key; keyid_from_pk (pk, keyid); /* If a secret key export is required we need to check whether we have a secret key at all and if so create the seckey_info structure. */ if (secret) { if (agent_probe_any_secret_key (ctrl, keyblock)) continue; /* No secret key (neither primary nor subkey). */ /* No v3 keys with GNU mode 1001. */ if (secret == 2 && pk->version == 3) { log_info (_("key %s: PGP 2.x style key - skipped\n"), keystr (keyid)); continue; } /* The agent does not yet allow export of v3 packets. It is actually questionable whether we should allow them at all. */ if (pk->version == 3) { log_info ("key %s: PGP 2.x style key (v3) export " "not yet supported - skipped\n", keystr (keyid)); continue; } stats->secret_count++; } /* Always do the cleaning on the public key part if requested. * Note that both export-clean and export-minimal only apply to * UID sigs (0x10, 0x11, 0x12, and 0x13). A designated * revocation is never stripped, even with export-minimal set. */ if ((options & EXPORT_CLEAN)) clean_key (ctrl, keyblock, opt.verbose, (options&EXPORT_MINIMAL), NULL, NULL); if (export_keep_uid) { commit_kbnode (&keyblock); apply_keep_uid_filter (ctrl, keyblock, export_keep_uid); commit_kbnode (&keyblock); } if (export_drop_subkey) { commit_kbnode (&keyblock); apply_drop_subkey_filter (ctrl, keyblock, export_drop_subkey); commit_kbnode (&keyblock); } /* And write it. */ err = do_export_one_keyblock (ctrl, keyblock, keyid, out_help? out_help : out, secret, options, stats, any, desc, ndesc, descindex, cipherhd); if (err) break; if (keyblock_out) { *keyblock_out = keyblock; break; } if (out_help) { /* We want to write PKA or DANE records. OUT_HELP has the * keyblock and we print a record for each uid to OUT. */ const void *data; size_t datalen; iobuf_flush_temp (out_help); data = iobuf_get_temp_buffer (out_help); datalen = iobuf_get_temp_length (out_help); err = print_pka_or_dane_records (out, keyblock, pk, data, datalen, (options & EXPORT_PKA_FORMAT), (options & EXPORT_DANE_FORMAT)); if (err) goto leave; iobuf_close (out_help); out_help = iobuf_temp (); } } if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = 0; leave: iobuf_cancel (out_help); gcry_cipher_close (cipherhd); xfree(desc); keydb_release (kdbhd); if (err || !keyblock_out) release_kbnode( keyblock ); if( !*any ) log_info(_("WARNING: nothing exported\n")); return err; } static gpg_error_t key_to_sshblob (membuf_t *mb, const char *identifier, ...) { va_list arg_ptr; gpg_error_t err = 0; unsigned char nbuf[4]; unsigned char *buf; size_t buflen; gcry_mpi_t a; ulongtobuf (nbuf, (ulong)strlen (identifier)); put_membuf (mb, nbuf, 4); put_membuf_str (mb, identifier); if (!strncmp (identifier, "ecdsa-sha2-", 11)) { ulongtobuf (nbuf, (ulong)strlen (identifier+11)); put_membuf (mb, nbuf, 4); put_membuf_str (mb, identifier+11); } va_start (arg_ptr, identifier); while ((a = va_arg (arg_ptr, gcry_mpi_t))) { err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a); if (err) break; if (!strcmp (identifier, "ssh-ed25519") && buflen > 5 && buf[4] == 0x40) { /* We need to strip our 0x40 prefix. */ put_membuf (mb, "\x00\x00\x00\x20", 4); put_membuf (mb, buf+5, buflen-5); } else put_membuf (mb, buf, buflen); gcry_free (buf); } va_end (arg_ptr); return err; } /* Export the key identified by USERID in the SSH public key format. The function exports the latest subkey with Authentication capability unless the '!' suffix is used to export a specific key. */ gpg_error_t export_ssh_key (ctrl_t ctrl, const char *userid) { gpg_error_t err; kbnode_t keyblock = NULL; KEYDB_SEARCH_DESC desc; u32 latest_date; u32 curtime = make_timestamp (); kbnode_t latest_key, node; PKT_public_key *pk; const char *identifier = NULL; membuf_t mb; estream_t fp = NULL; struct b64state b64_state; const char *fname = "-"; init_membuf (&mb, 4096); /* We need to know whether the key has been specified using the exact syntax ('!' suffix). Thus we need to run a classify_user_id on our own. */ err = classify_user_id (userid, &desc, 1); /* Get the public key. */ if (!err) { getkey_ctx_t getkeyctx; err = get_pubkey_byname (ctrl, &getkeyctx, NULL, userid, &keyblock, NULL, 0 /* Only usable keys or given exact. */, 1 /* No AKL lookup. */); if (!err) { err = getkey_next (ctrl, getkeyctx, NULL, NULL); if (!err) err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); else if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) err = 0; } getkey_end (ctrl, getkeyctx); } if (err) { log_error (_("key \"%s\" not found: %s\n"), userid, gpg_strerror (err)); return err; } /* The finish_lookup code in getkey.c does not handle auth keys, thus we have to duplicate the code here to find the latest subkey. However, if the key has been found using an exact match ('!' notation) we use that key without any further checks and even allow the use of the primary key. */ latest_date = 0; latest_key = NULL; for (node = keyblock; node; node = node->next) { if ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_PUBLIC_KEY) && node->pkt->pkt.public_key->flags.exact) { latest_key = node; break; } } if (!latest_key) { for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype != PKT_PUBLIC_SUBKEY) continue; pk = node->pkt->pkt.public_key; if (DBG_LOOKUP) log_debug ("\tchecking subkey %08lX\n", (ulong) keyid_from_pk (pk, NULL)); if (!(pk->pubkey_usage & PUBKEY_USAGE_AUTH)) { if (DBG_LOOKUP) log_debug ("\tsubkey not usable for authentication\n"); continue; } if (!pk->flags.valid) { if (DBG_LOOKUP) log_debug ("\tsubkey not valid\n"); continue; } if (pk->flags.revoked) { if (DBG_LOOKUP) log_debug ("\tsubkey has been revoked\n"); continue; } if (pk->has_expired) { if (DBG_LOOKUP) log_debug ("\tsubkey has expired\n"); continue; } if (pk->timestamp > curtime && !opt.ignore_valid_from) { if (DBG_LOOKUP) log_debug ("\tsubkey not yet valid\n"); continue; } if (DBG_LOOKUP) log_debug ("\tsubkey might be fine\n"); /* In case a key has a timestamp of 0 set, we make sure that it is used. A better change would be to compare ">=" but that might also change the selected keys and is as such a more intrusive change. */ if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date)) { latest_date = pk->timestamp; latest_key = node; } } /* If no subkey was suitable check the primary key. */ if (!latest_key && (node = keyblock) && node->pkt->pkttype == PKT_PUBLIC_KEY) { pk = node->pkt->pkt.public_key; if (DBG_LOOKUP) log_debug ("\tchecking primary key %08lX\n", (ulong) keyid_from_pk (pk, NULL)); if (!(pk->pubkey_usage & PUBKEY_USAGE_AUTH)) { if (DBG_LOOKUP) log_debug ("\tprimary key not usable for authentication\n"); } else if (!pk->flags.valid) { if (DBG_LOOKUP) log_debug ("\tprimary key not valid\n"); } else if (pk->flags.revoked) { if (DBG_LOOKUP) log_debug ("\tprimary key has been revoked\n"); } else if (pk->has_expired) { if (DBG_LOOKUP) log_debug ("\tprimary key has expired\n"); } else if (pk->timestamp > curtime && !opt.ignore_valid_from) { if (DBG_LOOKUP) log_debug ("\tprimary key not yet valid\n"); } else { if (DBG_LOOKUP) log_debug ("\tprimary key is fine\n"); latest_date = pk->timestamp; latest_key = node; } } } if (!latest_key) { err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); log_error (_("key \"%s\" not found: %s\n"), userid, gpg_strerror (err)); goto leave; } pk = latest_key->pkt->pkt.public_key; if (DBG_LOOKUP) log_debug ("\tusing key %08lX\n", (ulong) keyid_from_pk (pk, NULL)); switch (pk->pubkey_algo) { case PUBKEY_ALGO_DSA: identifier = "ssh-dss"; err = key_to_sshblob (&mb, identifier, pk->pkey[0], pk->pkey[1], pk->pkey[2], pk->pkey[3], NULL); break; case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_S: identifier = "ssh-rsa"; err = key_to_sshblob (&mb, identifier, pk->pkey[1], pk->pkey[0], NULL); break; case PUBKEY_ALGO_ECDSA: { char *curveoid; const char *curve; curveoid = openpgp_oid_to_str (pk->pkey[0]); if (!curveoid) err = gpg_error_from_syserror (); else if (!(curve = openpgp_oid_to_curve (curveoid, 0))) err = gpg_error (GPG_ERR_UNKNOWN_CURVE); else { if (!strcmp (curve, "nistp256")) identifier = "ecdsa-sha2-nistp256"; else if (!strcmp (curve, "nistp384")) identifier = "ecdsa-sha2-nistp384"; else if (!strcmp (curve, "nistp521")) identifier = "ecdsa-sha2-nistp521"; if (!identifier) err = gpg_error (GPG_ERR_UNKNOWN_CURVE); else err = key_to_sshblob (&mb, identifier, pk->pkey[1], NULL); } xfree (curveoid); } break; case PUBKEY_ALGO_EDDSA: if (!openpgp_oid_is_ed25519 (pk->pkey[0])) err = gpg_error (GPG_ERR_UNKNOWN_CURVE); else { identifier = "ssh-ed25519"; err = key_to_sshblob (&mb, identifier, pk->pkey[1], NULL); } break; case PUBKEY_ALGO_ELGAMAL_E: case PUBKEY_ALGO_ELGAMAL: err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); break; default: err = GPG_ERR_PUBKEY_ALGO; break; } if (!identifier) goto leave; if (opt.outfile && *opt.outfile && strcmp (opt.outfile, "-")) fp = es_fopen ((fname = opt.outfile), "w"); else fp = es_stdout; if (!fp) { err = gpg_error_from_syserror (); log_error (_("error creating '%s': %s\n"), fname, gpg_strerror (err)); goto leave; } es_fprintf (fp, "%s ", identifier); err = b64enc_start_es (&b64_state, fp, ""); if (!err) { void *blob; size_t bloblen; blob = get_membuf (&mb, &bloblen); if (blob) { err = b64enc_write (&b64_state, blob, bloblen); xfree (blob); if (err) goto leave; } err = b64enc_finish (&b64_state); } if (err) goto leave; es_fprintf (fp, " openpgp:0x%08lX\n", (ulong)keyid_from_pk (pk, NULL)); if (es_ferror (fp)) err = gpg_error_from_syserror (); else { if (es_fclose (fp)) err = gpg_error_from_syserror (); fp = NULL; } if (err) log_error (_("error writing '%s': %s\n"), fname, gpg_strerror (err)); leave: es_fclose (fp); xfree (get_membuf (&mb, NULL)); release_kbnode (keyblock); return err; } diff --git a/g10/main.h b/g10/main.h index 87417ee37..5862cdf27 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,492 +1,492 @@ /* main.h * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG 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 3 of the License, or * (at your option) any later version. * * GnuPG 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, see . */ #ifndef G10_MAIN_H #define G10_MAIN_H #include "../common/types.h" #include "../common/iobuf.h" #include "keydb.h" #include "keyedit.h" #include "../common/util.h" /* It could be argued that the default cipher should be 3DES rather than AES128, and the default compression should be 0 (i.e. uncompressed) rather than 1 (zip). However, the real world issues of speed and size come into play here. */ #if GPG_USE_AES128 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES #elif GPG_USE_CAST5 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5 #else # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_3DES #endif #define DEFAULT_DIGEST_ALGO ((GNUPG)? DIGEST_ALGO_SHA256:DIGEST_ALGO_SHA1) #define DEFAULT_S2K_DIGEST_ALGO DIGEST_ALGO_SHA1 #ifdef HAVE_ZIP # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP #else # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_NONE #endif #define S2K_DIGEST_ALGO (opt.s2k_digest_algo?opt.s2k_digest_algo:DEFAULT_S2K_DIGEST_ALGO) /* Various data objects. */ typedef struct { ctrl_t ctrl; int header_okay; PK_LIST pk_list; DEK *symkey_dek; STRING2KEY *symkey_s2k; cipher_filter_context_t cfx; } encrypt_filter_context_t; struct groupitem { char *name; strlist_t values; struct groupitem *next; }; struct weakhash { enum gcry_md_algos algo; int rejection_shown; struct weakhash *next; }; /*-- gpg.c --*/ extern int g10_errors_seen; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) void g10_exit(int rc) __attribute__ ((noreturn)); #else void g10_exit(int rc); #endif void print_pubkey_algo_note (pubkey_algo_t algo); void print_cipher_algo_note (cipher_algo_t algo); void print_digest_algo_note (digest_algo_t algo); void print_digest_rejected_note (enum gcry_md_algos algo); void print_reported_error (gpg_error_t err, gpg_err_code_t skip_if_ec); void print_further_info (const char *format, ...) GPGRT_ATTR_PRINTF(1,2); void additional_weak_digest (const char* digestname); /*-- armor.c --*/ char *make_radix64_string( const byte *data, size_t len ); /*-- misc.c --*/ void trap_unaligned(void); void register_secured_file (const char *fname); void unregister_secured_file (const char *fname); int is_secured_file (int fd); int is_secured_filename (const char *fname); u16 checksum_u16( unsigned n ); u16 checksum( byte *p, unsigned n ); u16 checksum_mpi( gcry_mpi_t a ); u32 buffer_to_u32( const byte *buffer ); const byte *get_session_marker( size_t *rlen ); enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo); #define openpgp_cipher_open(_a,_b,_c,_d) \ gcry_cipher_open((_a),map_cipher_openpgp_to_gcry((_b)),(_c),(_d)) #define openpgp_cipher_get_algo_keylen(_a) \ gcry_cipher_get_algo_keylen(map_cipher_openpgp_to_gcry((_a))) #define openpgp_cipher_get_algo_blklen(_a) \ gcry_cipher_get_algo_blklen(map_cipher_openpgp_to_gcry((_a))) int openpgp_cipher_blocklen (cipher_algo_t algo); int openpgp_cipher_test_algo(cipher_algo_t algo); const char *openpgp_cipher_algo_name (cipher_algo_t algo); pubkey_algo_t map_pk_gcry_to_openpgp (enum gcry_pk_algos algo); int openpgp_pk_test_algo (pubkey_algo_t algo); int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use); int openpgp_pk_algo_usage ( int algo ); const char *openpgp_pk_algo_name (pubkey_algo_t algo); enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo); int openpgp_md_test_algo (digest_algo_t algo); const char *openpgp_md_algo_name (int algo); struct expando_args { PKT_public_key *pk; PKT_public_key *pksk; byte imagetype; int validity_info; const char *validity_string; const byte *namehash; }; char *pct_expando(const char *string,struct expando_args *args); void deprecated_warning(const char *configname,unsigned int configlineno, const char *option,const char *repl1,const char *repl2); void deprecated_command (const char *name); void obsolete_scdaemon_option (const char *configname, unsigned int configlineno, const char *name); int string_to_cipher_algo (const char *string); int string_to_digest_algo (const char *string); const char *compress_algo_to_string(int algo); int string_to_compress_algo(const char *string); int check_compress_algo(int algo); int default_cipher_algo(void); int default_compress_algo(void); void compliance_failure(void); struct parse_options { char *name; unsigned int bit; char **value; char *help; }; char *optsep(char **stringp); char *argsplit(char *string); int parse_options(char *str,unsigned int *options, struct parse_options *opts,int noisy); const char *get_libexecdir (void); int path_access(const char *file,int mode); int pubkey_get_npkey (pubkey_algo_t algo); int pubkey_get_nskey (pubkey_algo_t algo); int pubkey_get_nsig (pubkey_algo_t algo); int pubkey_get_nenc (pubkey_algo_t algo); /* Temporary helpers. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey ); int mpi_print (estream_t stream, gcry_mpi_t a, int mode); unsigned int ecdsa_qbits_from_Q (unsigned int qbits); /*-- cpr.c --*/ void set_status_fd ( int fd ); int is_status_enabled ( void ); void write_status ( int no ); void write_status_error (const char *where, gpg_error_t err); void write_status_errcode (const char *where, int errcode); void write_status_failure (const char *where, gpg_error_t err); void write_status_text ( int no, const char *text ); void write_status_printf (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3); void write_status_strings (int no, const char *text, ...) GPGRT_ATTR_SENTINEL(0); void write_status_buffer ( int no, const char *buffer, size_t len, int wrap ); void write_status_text_and_buffer ( int no, const char *text, const char *buffer, size_t len, int wrap ); void write_status_begin_signing (gcry_md_hd_t md); int cpr_enabled(void); char *cpr_get( const char *keyword, const char *prompt ); char *cpr_get_no_help( const char *keyword, const char *prompt ); char *cpr_get_utf8( const char *keyword, const char *prompt ); char *cpr_get_hidden( const char *keyword, const char *prompt ); void cpr_kill_prompt(void); int cpr_get_answer_is_yes_def (const char *keyword, const char *prompt, int def_yes); int cpr_get_answer_is_yes( const char *keyword, const char *prompt ); int cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ); int cpr_get_answer_okay_cancel (const char *keyword, const char *prompt, int def_answer); /*-- helptext.c --*/ void display_online_help( const char *keyword ); /*-- encode.c --*/ int setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek); void encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey); int use_mdc (pk_list_t pk_list,int algo); int encrypt_symmetric (const char *filename ); int encrypt_store (const char *filename ); int encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, int outputfd); void encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr); int encrypt_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); int write_pubkey_enc (ctrl_t ctrl, PKT_public_key *pk, int throw_keyid, DEK *dek, iobuf_t out); /*-- sign.c --*/ int sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, int do_encrypt, strlist_t remusr, const char *outfile ); int clearsign_file (ctrl_t ctrl, const char *fname, strlist_t locusr, const char *outfile); int sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr); /*-- sig-check.c --*/ void sig_check_dump_stats (void); /* SIG is a revocation signature. Check if any of PK's designated revokers generated it. If so, return 0. Note: this function (correctly) doesn't care if the designated revoker is revoked. */ int check_revocation_keys (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig); /* Check that the backsig BACKSIG from the subkey SUB_PK to its primary key MAIN_PK is valid. */ int check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk, PKT_signature *backsig); /* Check that the signature SIG over a key (e.g., a key binding or a key revocation) is valid. (To check signatures over data, use check_signature.) */ int check_key_signature (ctrl_t ctrl, kbnode_t root, kbnode_t sig, int *is_selfsig ); /* Like check_key_signature, but with the ability to specify some additional parameters and get back additional information. See the documentation for the implementation for details. */ int check_key_signature2 (ctrl_t ctrl, kbnode_t root, kbnode_t node, PKT_public_key *check_pk, PKT_public_key *ret_pk, int *is_selfsig, u32 *r_expiredate, int *r_expired); /* Returns whether SIGNER generated the signature SIG over the packet PACKET, which is a key, subkey or uid, and comes from the key block KB. If SIGNER is NULL, it is looked up based on the information in SIG. If not NULL, sets *IS_SELFSIG to indicate whether the signature is a self-signature and *RET_PK to a copy of the signer's key. */ gpg_error_t check_signature_over_key_or_uid (ctrl_t ctrl, PKT_public_key *signer, PKT_signature *sig, KBNODE kb, PACKET *packet, int *is_selfsig, PKT_public_key *ret_pk); /*-- delkey.c --*/ gpg_error_t delete_keys (ctrl_t ctrl, strlist_t names, int secret, int allow_both); /*-- keygen.c --*/ const char *get_default_pubkey_algo (void); u32 parse_expire_string(const char *string); u32 ask_expire_interval(int object,const char *def_expire); u32 ask_expiredate(void); unsigned int ask_key_flags (int algo, int subkey, unsigned int current); void quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr, const char *usagestr, const char *expirestr); void generate_keypair (ctrl_t ctrl, int full, const char *fname, const char *card_serialno, int card_backup_key); int keygen_set_std_prefs (const char *string,int personal); PKT_user_id *keygen_get_std_prefs (void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_key_flags (PKT_signature *sig, void *opaque); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ); int keygen_add_keyserver_url(PKT_signature *sig, void *opaque); int keygen_add_notations(PKT_signature *sig,void *opaque); int keygen_add_revkey(PKT_signature *sig, void *opaque); gpg_error_t make_backsig (ctrl_t ctrl, PKT_signature *sig, PKT_public_key *pk, PKT_public_key *sub_pk, PKT_public_key *sub_psk, u32 timestamp, const char *cache_nonce); gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr, const char *usagestr, const char *expirestr); #ifdef ENABLE_CARD_SUPPORT gpg_error_t generate_card_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock, int keyno, const char *serialno); #endif /*-- openfile.c --*/ int overwrite_filep( const char *fname ); char *make_outfile_name( const char *iname ); char *ask_outfile_name( const char *name, size_t namelen ); int open_outfile (int inp_fd, const char *iname, int mode, - int restrictedperm, iobuf_t *a); + int restrictedperm, iobuf_t *a, int no_outfile); char *get_matching_datafile (const char *sigfilename); iobuf_t open_sigfile (const char *sigfilename, progress_filter_context_t *pfx); void try_make_homedir( const char *fname ); char *get_openpgp_revocdir (const char *home); /*-- seskey.c --*/ void make_session_key( DEK *dek ); gcry_mpi_t encode_session_key( int openpgp_pk_algo, DEK *dek, unsigned nbits ); gcry_mpi_t encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo ); /*-- import.c --*/ struct import_stats_s; typedef struct import_stats_s *import_stats_t; struct import_filter_s; typedef struct import_filter_s *import_filter_t; typedef gpg_error_t (*import_screener_t)(kbnode_t keyblock, void *arg); int parse_import_options(char *str,unsigned int *options,int noisy); gpg_error_t parse_and_set_import_filter (const char *string); import_filter_t save_and_clear_import_filter (void); void restore_import_filter (import_filter_t filt); gpg_error_t read_key_from_file (ctrl_t ctrl, const char *fname, kbnode_t *r_keyblock); void import_keys (ctrl_t ctrl, char **fnames, int nnames, import_stats_t stats_hd, unsigned int options, int origin, const char *url); int import_keys_es_stream (ctrl_t ctrl, estream_t fp, import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url); gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname); import_stats_t import_new_stats_handle (void); void import_release_stats_handle (import_stats_t hd); void import_print_stats (import_stats_t hd); /* Communication for impex_filter_getval */ struct impex_filter_parm_s { ctrl_t ctrl; kbnode_t node; }; const char *impex_filter_getval (void *cookie, const char *propname); gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, kbnode_t sec_keyblock, int batch, int force); int collapse_uids( KBNODE *keyblock ); /*-- export.c --*/ struct export_stats_s; typedef struct export_stats_s *export_stats_t; export_stats_t export_new_stats (void); void export_release_stats (export_stats_t stats); void export_print_stats (export_stats_t stats); int parse_export_options(char *str,unsigned int *options,int noisy); gpg_error_t parse_and_set_export_filter (const char *string); int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); int export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); int export_secsubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options, export_stats_t stats, kbnode_t *r_keyblock, void **r_data, size_t *r_datalen); gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, int cleartext, char **cache_nonce_addr, const char *hexgrip, PKT_public_key *pk); gpg_error_t write_keyblock_to_output (kbnode_t keyblock, int with_armor, unsigned int options); gpg_error_t export_ssh_key (ctrl_t ctrl, const char *userid); /*-- dearmor.c --*/ int dearmor_file( const char *fname ); int enarmor_file( const char *fname ); /*-- revoke.c --*/ struct revocation_reason_info; int gen_standard_revoke (ctrl_t ctrl, PKT_public_key *psk, const char *cache_nonce); int gen_revoke (ctrl_t ctrl, const char *uname); int gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr); int revocation_reason_build_cb( PKT_signature *sig, void *opaque ); struct revocation_reason_info * ask_revocation_reason( int key_rev, int cert_rev, int hint ); struct revocation_reason_info * get_default_uid_revocation_reason(void); void release_revocation_reason_info( struct revocation_reason_info *reason ); /*-- keylist.c --*/ void public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode ); void secret_key_list (ctrl_t ctrl, strlist_t list ); void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, int no_validity); void print_fingerprint (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int mode); void print_revokers (estream_t fp, PKT_public_key *pk); void show_policy_url(PKT_signature *sig,int indent,int mode); void show_keyserver_url(PKT_signature *sig,int indent,int mode); void show_notation(PKT_signature *sig,int indent,int mode,int which); void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk); void set_attrib_fd(int fd); char *format_seckey_info (ctrl_t ctrl, PKT_public_key *pk); void print_seckey_info (ctrl_t ctrl, PKT_public_key *pk); void print_pubkey_info (ctrl_t ctrl, estream_t fp, PKT_public_key *pk); void print_card_key_info (estream_t fp, KBNODE keyblock); void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret); /*-- verify.c --*/ void print_file_status( int status, const char *name, int what ); int verify_signatures (ctrl_t ctrl, int nfiles, char **files ); int verify_files (ctrl_t ctrl, int nfiles, char **files ); int gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp); /*-- decrypt.c --*/ int decrypt_message (ctrl_t ctrl, const char *filename ); gpg_error_t decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd); void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]); /*-- plaintext.c --*/ int hash_datafiles( gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files, const char *sigfilename, int textmode); int hash_datafile_by_fd ( gcry_md_hd_t md, gcry_md_hd_t md2, int data_fd, int textmode ); PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf); /*-- server.c --*/ int gpg_server (ctrl_t); gpg_error_t gpg_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); #ifdef ENABLE_CARD_SUPPORT /*-- card-util.c --*/ void change_pin (int no, int allow_admin); void card_status (ctrl_t ctrl, estream_t fp, const char *serialno); void card_edit (ctrl_t ctrl, strlist_t commands); gpg_error_t card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock); int card_store_subkey (KBNODE node, int use); #endif #define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6)) /*-- migrate.c --*/ void migrate_secring (ctrl_t ctrl); #endif /*G10_MAIN_H*/ diff --git a/g10/openfile.c b/g10/openfile.c index 78f4dbbcb..03b114d58 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -1,421 +1,423 @@ /* openfile.c * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2009, * 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG 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 3 of the License, or * (at your option) any later version. * * GnuPG 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, see . */ #include #include #include #include #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "../common/ttyio.h" #include "options.h" #include "main.h" #include "../common/status.h" #include "../common/i18n.h" #ifdef HAVE_W32_SYSTEM #define NAME_OF_DEV_NULL "nul" #else #define NAME_OF_DEV_NULL "/dev/null" #endif #if defined (HAVE_DRIVE_LETTERS) || defined (__riscos__) #define CMP_FILENAME(a,b) ascii_strcasecmp( (a), (b) ) #else #define CMP_FILENAME(a,b) strcmp( (a), (b) ) #endif /* FIXME: Implement opt.interactive. */ /* * Check whether FNAME exists and ask if it's okay to overwrite an * existing one. * Returns: True: it's okay to overwrite or the file does not exist * False: Do not overwrite */ int overwrite_filep( const char *fname ) { if ( iobuf_is_pipe_filename (fname) ) return 1; /* Writing to stdout is always okay. */ if ( access( fname, F_OK ) ) return 1; /* Does not exist. */ if ( !compare_filenames (fname, NAME_OF_DEV_NULL) ) return 1; /* Does not do any harm. */ if (opt.answer_yes) return 1; if (opt.answer_no || opt.batch) return 0; /* Do not overwrite. */ tty_printf (_("File '%s' exists. "), fname); if (cpr_enabled ()) tty_printf ("\n"); if (cpr_get_answer_is_yes ("openfile.overwrite.okay", _("Overwrite? (y/N) ")) ) return 1; return 0; } /* * Strip known extensions from iname and return a newly allocated * filename. Return NULL if we can't do that. */ char * make_outfile_name (const char *iname) { size_t n; if (iobuf_is_pipe_filename (iname)) return xstrdup ("-"); n = strlen (iname); if (n > 4 && (!CMP_FILENAME(iname+n-4, EXTSEP_S GPGEXT_GPG) || !CMP_FILENAME(iname+n-4, EXTSEP_S "pgp") || !CMP_FILENAME(iname+n-4, EXTSEP_S "sig") || !CMP_FILENAME(iname+n-4, EXTSEP_S "asc"))) { char *buf = xstrdup (iname); buf[n-4] = 0; return buf; } else if (n > 5 && !CMP_FILENAME(iname+n-5, EXTSEP_S "sign")) { char *buf = xstrdup (iname); buf[n-5] = 0; return buf; } log_info (_("%s: unknown suffix\n"), iname); return NULL; } /* Ask for an output filename; use the given one as default. Return NULL if no file has been given or if it is not possible to ask the user. NAME is the template len which might contain enbedded Nuls. NAMELEN is its actual length. */ char * ask_outfile_name( const char *name, size_t namelen ) { size_t n; const char *s; char *prompt; char *fname; char *defname; if ( opt.batch ) return NULL; defname = name && namelen? make_printable_string (name, namelen, 0) : NULL; s = _("Enter new filename"); n = strlen(s) + (defname?strlen (defname):0) + 10; prompt = xmalloc (n); if (defname) snprintf (prompt, n, "%s [%s]: ", s, defname ); else snprintf (prompt, n, "%s: ", s ); tty_enable_completion(NULL); fname = cpr_get ("openfile.askoutname", prompt ); cpr_kill_prompt (); tty_disable_completion (); xfree (prompt); if ( !*fname ) { xfree (fname); fname = defname; defname = NULL; } xfree (defname); if (fname) trim_spaces (fname); return fname; } /* * Make an output filename for the inputfile INAME. * Returns an IOBUF and an errorcode * Mode 0 = use ".gpg" * 1 = use ".asc" * 2 = use ".sig" * 3 = use ".rev" * * If INP_FD is not -1 the function simply creates an IOBUF for that * file descriptor and ignore INAME and MODE. Note that INP_FD won't * be closed if the returned IOBUF is closed. With RESTRICTEDPERM a - * file will be created with mode 700 if possible. + * file will be created with mode 700 if possible. If NO_OUTFILE is + * true, don't use the outfile option even if it is set. */ int open_outfile (int inp_fd, const char *iname, int mode, int restrictedperm, - iobuf_t *a) + iobuf_t *a, int no_outfile) { int rc = 0; + const char outfile = no_outfile ? NULL : opt.outfile; *a = NULL; if (inp_fd != -1) { char xname[64]; *a = iobuf_fdopen_nc (inp_fd, "wb"); if (!*a) { rc = gpg_error_from_syserror (); snprintf (xname, sizeof xname, "[fd %d]", inp_fd); log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (rc)); } else if (opt.verbose) { snprintf (xname, sizeof xname, "[fd %d]", inp_fd); log_info (_("writing to '%s'\n"), xname); } } - else if (iobuf_is_pipe_filename (iname) && !opt.outfile) + else if (iobuf_is_pipe_filename (iname) && !outfile) { *a = iobuf_create (NULL, 0); if ( !*a ) { rc = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), "[stdout]", strerror(errno) ); } else if ( opt.verbose ) log_info (_("writing to stdout\n")); } else { char *buf = NULL; const char *name; if (opt.dry_run) name = NAME_OF_DEV_NULL; - else if (opt.outfile) - name = opt.outfile; + else if (outfile) + name = outfile; else { #ifdef USE_ONLY_8DOT3 if (opt.mangle_dos_filenames) { /* It is quite common for DOS systems to have only one dot in a filename. If we have something like this, we simple replace the suffix except in cases where the suffix is larger than 3 characters and not the same as the new one. We don't map the filenames to 8.3 because this is a duty of the file system. */ char *dot; const char *newsfx; newsfx = (mode==1 ? ".asc" : mode==2 ? ".sig" : mode==3 ? ".rev" : ".gpg"); buf = xmalloc (strlen(iname)+4+1); strcpy (buf, iname); dot = strchr (buf, '.' ); if ( dot && dot > buf && dot[1] && strlen(dot) <= 4 && CMP_FILENAME (newsfx, dot) ) strcpy (dot, newsfx); else if (dot && !dot[1]) /* Do not duplicate a dot. */ strcpy (dot, newsfx+1); else strcat (buf, newsfx); } if (!buf) #endif /* USE_ONLY_8DOT3 */ { buf = xstrconcat (iname, (mode==1 ? EXTSEP_S "asc" : mode==2 ? EXTSEP_S "sig" : mode==3 ? EXTSEP_S "rev" : /* */ EXTSEP_S GPGEXT_GPG), NULL); } name = buf; } rc = 0; while ( !overwrite_filep (name) ) { char *tmp = ask_outfile_name (NULL, 0); if ( !tmp || !*tmp ) { xfree (tmp); rc = gpg_error (GPG_ERR_EEXIST); break; } xfree (buf); name = buf = tmp; } if ( !rc ) { if (is_secured_filename (name) ) { *a = NULL; gpg_err_set_errno (EPERM); } else *a = iobuf_create (name, restrictedperm); if (!*a) { rc = gpg_error_from_syserror (); log_error(_("can't create '%s': %s\n"), name, strerror(errno) ); } else if( opt.verbose ) log_info (_("writing to '%s'\n"), name ); } xfree(buf); } if (*a) iobuf_ioctl (*a, IOBUF_IOCTL_NO_CACHE, 1, NULL); return rc; } /* Find a matching data file for the signature file SIGFILENAME and return it as a malloced string. If no matching data file is found, return NULL. */ char * get_matching_datafile (const char *sigfilename) { char *fname = NULL; size_t len; if (iobuf_is_pipe_filename (sigfilename)) return NULL; len = strlen (sigfilename); if (len > 4 && (!strcmp (sigfilename + len - 4, EXTSEP_S "sig") || (len > 5 && !strcmp(sigfilename + len - 5, EXTSEP_S "sign")) || !strcmp(sigfilename + len - 4, EXTSEP_S "asc"))) { fname = xstrdup (sigfilename); fname[len-(fname[len-1]=='n'?5:4)] = 0 ; if (access (fname, R_OK )) { /* Not found or other error. */ xfree (fname); fname = NULL; } } return fname; } /* * Try to open a file without the extension ".sig" or ".asc" * Return NULL if such a file is not available. */ iobuf_t open_sigfile (const char *sigfilename, progress_filter_context_t *pfx) { iobuf_t a = NULL; char *buf; buf = get_matching_datafile (sigfilename); if (buf) { a = iobuf_open (buf); if (a && is_secured_file (iobuf_get_fd (a))) { iobuf_close (a); a = NULL; gpg_err_set_errno (EPERM); } if (a) log_info (_("assuming signed data in '%s'\n"), buf); if (a && pfx) handle_progress (pfx, a, buf); xfree (buf); } return a; } void try_make_homedir (const char *fname) { const char *defhome = standard_homedir (); /* Create the directory only if the supplied directory name is the same as the default one. This way we avoid to create arbitrary directories when a non-default home directory is used. To cope with HOME, we do compare only the suffix if we see that the default homedir does start with a tilde. */ if ( opt.dry_run || opt.no_homedir_creation ) return; if ( #ifdef HAVE_W32_SYSTEM ( !compare_filenames (fname, defhome) ) #else ( *defhome == '~' && (strlen(fname) >= strlen (defhome+1) && !strcmp(fname+strlen(fname)-strlen(defhome+1), defhome+1 ) )) || (*defhome != '~' && !compare_filenames( fname, defhome ) ) #endif ) { if (gnupg_mkdir (fname, "-rwx")) log_fatal ( _("can't create directory '%s': %s\n"), fname, strerror(errno) ); else if (!opt.quiet ) log_info ( _("directory '%s' created\n"), fname ); } } /* Get and if needed create a string with the directory used to store openpgp revocations. */ char * get_openpgp_revocdir (const char *home) { char *fname; struct stat statbuf; fname = make_filename (home, GNUPG_OPENPGP_REVOC_DIR, NULL); if (stat (fname, &statbuf) && errno == ENOENT) { if (gnupg_mkdir (fname, "-rwx")) log_error (_("can't create directory '%s': %s\n"), fname, strerror (errno) ); else if (!opt.quiet) log_info (_("directory '%s' created\n"), fname); } return fname; } diff --git a/g10/revoke.c b/g10/revoke.c index 1dea6ae12..db3c49562 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -1,884 +1,884 @@ /* revoke.c - Create recovation certificates. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, * 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG 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 3 of the License, or * (at your option) any later version. * * GnuPG 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, see . */ #include #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "keydb.h" #include "../common/util.h" #include "main.h" #include "../common/ttyio.h" #include "../common/i18n.h" #include "call-agent.h" struct revocation_reason_info { int code; char *desc; }; int revocation_reason_build_cb( PKT_signature *sig, void *opaque ) { struct revocation_reason_info *reason = opaque; char *ud = NULL; byte *buffer; size_t buflen = 1; if(!reason) return 0; if( reason->desc ) { ud = native_to_utf8( reason->desc ); buflen += strlen(ud); } buffer = xmalloc( buflen ); *buffer = reason->code; if( ud ) { memcpy(buffer+1, ud, strlen(ud) ); xfree( ud ); } build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen ); xfree( buffer ); return 0; } /* Outputs a minimal pk (as defined by 2440) from a keyblock. A minimal pk consists of the public key packet and a user ID. We try and pick a user ID that has a uid signature, and include it if possible. */ static int export_minimal_pk(IOBUF out,KBNODE keyblock, PKT_signature *revsig,PKT_signature *revkey) { KBNODE node; PACKET pkt; PKT_user_id *uid=NULL; PKT_signature *selfsig=NULL; u32 keyid[2]; int rc; node=find_kbnode(keyblock,PKT_PUBLIC_KEY); if(!node) { log_error("key incomplete\n"); return GPG_ERR_GENERAL; } keyid_from_pk(node->pkt->pkt.public_key,keyid); pkt=*node->pkt; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); return rc; } init_packet(&pkt); pkt.pkttype=PKT_SIGNATURE; /* the revocation itself, if any. 2440 likes this to come first. */ if(revsig) { pkt.pkt.signature=revsig; rc=build_packet(out,&pkt); if(rc) { log_error("build_packet failed: %s\n", gpg_strerror (rc) ); return rc; } } /* If a revkey in a 1F sig is present, include it too */ if(revkey) { pkt.pkt.signature=revkey; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); return rc; } } while(!selfsig) { KBNODE signode; node=find_next_kbnode(node,PKT_USER_ID); if(!node) { /* We're out of user IDs - none were self-signed. */ if(uid) break; else { log_error(_("key %s has no user IDs\n"),keystr(keyid)); return GPG_ERR_GENERAL; } } if(node->pkt->pkt.user_id->attrib_data) continue; uid=node->pkt->pkt.user_id; signode=node; while((signode=find_next_kbnode(signode,PKT_SIGNATURE))) { if(keyid[0]==signode->pkt->pkt.signature->keyid[0] && keyid[1]==signode->pkt->pkt.signature->keyid[1] && IS_UID_SIG(signode->pkt->pkt.signature)) { selfsig=signode->pkt->pkt.signature; break; } } } pkt.pkttype=PKT_USER_ID; pkt.pkt.user_id=uid; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); return rc; } if(selfsig) { pkt.pkttype=PKT_SIGNATURE; pkt.pkt.signature=selfsig; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); return rc; } } return 0; } /**************** * Generate a revocation certificate for UNAME via a designated revoker */ int gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr) { int rc = 0; armor_filter_context_t *afx; PKT_public_key *pk = NULL; PKT_public_key *pk2 = NULL; PKT_signature *sig = NULL; IOBUF out = NULL; struct revocation_reason_info *reason = NULL; KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC desc; KBNODE keyblock=NULL,node; u32 keyid[2]; int i,any=0; SK_LIST sk_list=NULL; if( opt.batch ) { log_error(_("can't do this in batch mode\n")); return GPG_ERR_GENERAL; } afx = new_armor_context (); kdbhd = keydb_new (); if (!kdbhd) { rc = gpg_error_from_syserror (); goto leave; } rc = classify_user_id (uname, &desc, 1); if (!rc) rc = keydb_search (kdbhd, &desc, 1, NULL); if (rc) { log_error (_("key \"%s\" not found: %s\n"),uname, gpg_strerror (rc)); goto leave; } rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); goto leave; } /* To parse the revkeys */ merge_keys_and_selfsig (ctrl, keyblock); /* get the key from the keyblock */ node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); if( !node ) BUG (); pk=node->pkt->pkt.public_key; keyid_from_pk(pk,keyid); if(locusr) { rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_CERT); if(rc) goto leave; } /* Are we a designated revoker for this key? */ if(!pk->revkey && pk->numrevkeys) BUG(); for(i=0;inumrevkeys;i++) { SK_LIST list; free_public_key (pk2); pk2 = NULL; if(sk_list) { for(list=sk_list;list;list=list->next) { byte fpr[MAX_FINGERPRINT_LEN]; size_t fprlen; fingerprint_from_pk (list->pk, fpr, &fprlen); /* Don't get involved with keys that don't have 160 bit fingerprints */ if(fprlen!=20) continue; if(memcmp(fpr,pk->revkey[i].fpr,20)==0) break; } if (list) pk2 = copy_public_key (NULL, list->pk); else continue; } else { pk2 = xmalloc_clear (sizeof *pk2); rc = get_pubkey_byfprint (ctrl, pk2, NULL, pk->revkey[i].fpr, MAX_FINGERPRINT_LEN); } /* We have the revocation key. */ if(!rc) { PKT_signature *revkey = NULL; any = 1; print_pubkey_info (ctrl, NULL, pk); tty_printf ("\n"); tty_printf (_("To be revoked by:\n")); print_seckey_info (ctrl, pk2); if(pk->revkey[i].class&0x40) tty_printf(_("(This is a sensitive revocation key)\n")); tty_printf("\n"); rc = agent_probe_secret_key (ctrl, pk2); if (rc) { tty_printf (_("Secret key is not available.\n")); continue; } if( !cpr_get_answer_is_yes("gen_desig_revoke.okay", _("Create a designated revocation certificate for this key? (y/N) "))) continue; /* get the reason for the revocation (this is always v4) */ reason = ask_revocation_reason( 1, 0, 1 ); if( !reason ) continue; if( !opt.armor ) tty_printf(_("ASCII armored output forced.\n")); - if( (rc = open_outfile (-1, NULL, 0, 1, &out )) ) + if( (rc = open_outfile (-1, NULL, 0, 1, &out, 0 )) ) goto leave; afx->what = 1; afx->hdrlines = "Comment: A designated revocation certificate" " should follow\n"; push_armor_filter (afx, out); /* create it */ rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk2, 0x20, 0, 0, 0, revocation_reason_build_cb, reason, NULL); if( rc ) { log_error(_("make_keysig_packet failed: %s\n"), gpg_strerror (rc)); goto leave; } /* Spit out a minimal pk as well, since otherwise there is no way to know which key to attach this revocation to. Also include the direct key signature that contains this revocation key. We're allowed to include sensitive revocation keys along with a revocation, as this may be the only time the recipient has seen it. Note that this means that if we have multiple different sensitive revocation keys in a given direct key signature, we're going to include them all here. This is annoying, but the good outweighs the bad, since without including this a sensitive revoker can't really do their job. People should not include multiple sensitive revocation keys in one signature: 2440 says "Note that it may be appropriate to isolate this subpacket within a separate signature so that it is not combined with other subpackets that need to be exported." -dms */ while(!revkey) { KBNODE signode; signode=find_next_kbnode(node,PKT_SIGNATURE); if(!signode) break; node=signode; if(keyid[0]==signode->pkt->pkt.signature->keyid[0] && keyid[1]==signode->pkt->pkt.signature->keyid[1] && IS_KEY_SIG(signode->pkt->pkt.signature)) { int j; for(j=0;jpkt->pkt.signature->numrevkeys;j++) { if(pk->revkey[i].class== signode->pkt->pkt.signature->revkey[j].class && pk->revkey[i].algid== signode->pkt->pkt.signature->revkey[j].algid && memcmp(pk->revkey[i].fpr, signode->pkt->pkt.signature->revkey[j].fpr, MAX_FINGERPRINT_LEN)==0) { revkey=signode->pkt->pkt.signature; break; } } } } if(!revkey) BUG(); rc=export_minimal_pk(out,keyblock,sig,revkey); if(rc) goto leave; /* and issue a usage notice */ tty_printf(_("Revocation certificate created.\n")); break; } } if(!any) log_error(_("no revocation keys found for \"%s\"\n"),uname); leave: free_public_key (pk); free_public_key (pk2); if( sig ) free_seckey_enc( sig ); release_sk_list(sk_list); if( rc ) iobuf_cancel(out); else iobuf_close(out); release_revocation_reason_info( reason ); release_armor_context (afx); return rc; } /* Common core to create the revocation. FILENAME may be NULL to write to stdout or the filename given by --output. REASON describes the revocation reason. PSK is the public primary key - we expect that a corresponding secret key is available. KEYBLOCK is the entire KEYBLOCK which is used in PGP mode to write a minimal key and not just the naked revocation signature; it may be NULL. If LEADINTEXT is not NULL, it is written right before the (armored) output.*/ static int create_revocation (ctrl_t ctrl, const char *filename, struct revocation_reason_info *reason, PKT_public_key *psk, kbnode_t keyblock, const char *leadintext, int suffix, const char *cache_nonce) { int rc; iobuf_t out = NULL; armor_filter_context_t *afx; PKT_signature *sig = NULL; PACKET pkt; afx = new_armor_context (); - if ((rc = open_outfile (-1, filename, suffix, 1, &out))) + if ((rc = open_outfile (-1, filename, suffix, 1, &out, !!filename))) goto leave; if (leadintext ) iobuf_writestr (out, leadintext); afx->what = 1; afx->hdrlines = "Comment: This is a revocation certificate\n"; push_armor_filter (afx, out); rc = make_keysig_packet (ctrl, &sig, psk, NULL, NULL, psk, 0x20, 0, 0, 0, revocation_reason_build_cb, reason, cache_nonce); if (rc) { log_error (_("make_keysig_packet failed: %s\n"), gpg_strerror (rc)); goto leave; } if (keyblock && (PGP6 || PGP7 || PGP8)) { /* Use a minimal pk for PGPx mode, since PGP can't import bare revocation certificates. */ rc = export_minimal_pk (out, keyblock, sig, NULL); if (rc) goto leave; } else { init_packet (&pkt); pkt.pkttype = PKT_SIGNATURE; pkt.pkt.signature = sig; rc = build_packet (out, &pkt); if (rc) { log_error (_("build_packet failed: %s\n"), gpg_strerror (rc)); goto leave; } } leave: if (sig) free_seckey_enc (sig); if (rc) iobuf_cancel (out); else iobuf_close (out); release_armor_context (afx); return rc; } /* This function is used to generate a standard revocation certificate by gpg's interactive key generation function. The certificate is stored at a dedicated place in a slightly modified form to avoid an accidental import. PSK is the primary key; a corresponding secret key must be available. CACHE_NONCE is optional but can be used to help gpg-agent to avoid an extra passphrase prompt. */ int gen_standard_revoke (ctrl_t ctrl, PKT_public_key *psk, const char *cache_nonce) { int rc; estream_t memfp; struct revocation_reason_info reason; char *dir, *tmpstr, *fname; void *leadin; size_t len; u32 keyid[2]; int kl; char *orig_codeset; dir = get_openpgp_revocdir (gnupg_homedir ()); tmpstr = hexfingerprint (psk, NULL, 0); fname = xstrconcat (dir, DIRSEP_S, tmpstr, NULL); xfree (tmpstr); xfree (dir); keyid_from_pk (psk, keyid); memfp = es_fopenmem (0, "r+"); if (!memfp) log_fatal ("error creating memory stream\n"); orig_codeset = i18n_switchto_utf8 (); es_fprintf (memfp, "%s\n\n", _("This is a revocation certificate for the OpenPGP key:")); print_key_line (ctrl, memfp, psk, 0); if (opt.keyid_format != KF_NONE) print_fingerprint (ctrl, memfp, psk, 3); kl = opt.keyid_format == KF_NONE? 0 : keystrlen (); tmpstr = get_user_id (ctrl, keyid, &len); es_fprintf (memfp, "uid%*s%.*s\n\n", kl + 10, "", (int)len, tmpstr); xfree (tmpstr); es_fprintf (memfp, "%s\n\n%s\n\n%s\n\n:", _("A revocation certificate is a kind of \"kill switch\" to publicly\n" "declare that a key shall not anymore be used. It is not possible\n" "to retract such a revocation certificate once it has been published."), _("Use it to revoke this key in case of a compromise or loss of\n" "the secret key. However, if the secret key is still accessible,\n" "it is better to generate a new revocation certificate and give\n" "a reason for the revocation. For details see the description of\n" "of the gpg command \"--generate-revocation\" in the " "GnuPG manual."), _("To avoid an accidental use of this file, a colon has been inserted\n" "before the 5 dashes below. Remove this colon with a text editor\n" "before importing and publishing this revocation certificate.")); es_putc (0, memfp); i18n_switchback (orig_codeset); if (es_fclose_snatch (memfp, &leadin, NULL)) log_fatal ("error snatching memory stream\n"); reason.code = 0x00; /* No particular reason. */ reason.desc = NULL; rc = create_revocation (ctrl, fname, &reason, psk, NULL, leadin, 3, cache_nonce); if (!rc && !opt.quiet) log_info (_("revocation certificate stored as '%s.rev'\n"), fname); xfree (leadin); xfree (fname); return rc; } /**************** * Generate a revocation certificate for UNAME */ int gen_revoke (ctrl_t ctrl, const char *uname) { int rc = 0; PKT_public_key *psk; u32 keyid[2]; kbnode_t keyblock = NULL; kbnode_t node; KEYDB_HANDLE kdbhd; struct revocation_reason_info *reason = NULL; KEYDB_SEARCH_DESC desc; if( opt.batch ) { log_error(_("can't do this in batch mode\n")); return GPG_ERR_GENERAL; } /* Search the userid; we don't want the whole getkey stuff here. */ kdbhd = keydb_new (); if (!kdbhd) { rc = gpg_error_from_syserror (); goto leave; } rc = classify_user_id (uname, &desc, 1); if (!rc) rc = keydb_search (kdbhd, &desc, 1, NULL); if (rc) { if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) log_error (_("secret key \"%s\" not found\n"), uname); else log_error (_("secret key \"%s\" not found: %s\n"), uname, gpg_strerror (rc)); goto leave; } rc = keydb_get_keyblock (kdbhd, &keyblock ); if (rc) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); goto leave; } rc = keydb_search (kdbhd, &desc, 1, NULL); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) /* Not ambiguous. */ { } else if (rc == 0) /* Ambiguous. */ { char *info; /* TRANSLATORS: The %s prints a key specification which for example has been given at the command line. Several lines lines with secret key infos are printed after this message. */ log_error (_("'%s' matches multiple secret keys:\n"), uname); info = format_seckey_info (ctrl, keyblock->pkt->pkt.public_key); log_error (" %s\n", info); xfree (info); release_kbnode (keyblock); rc = keydb_get_keyblock (kdbhd, &keyblock); while (! rc) { info = format_seckey_info (ctrl, keyblock->pkt->pkt.public_key); log_info (" %s\n", info); xfree (info); release_kbnode (keyblock); keyblock = NULL; rc = keydb_search (kdbhd, &desc, 1, NULL); if (! rc) rc = keydb_get_keyblock (kdbhd, &keyblock); } rc = GPG_ERR_AMBIGUOUS_NAME; goto leave; } else { log_error (_("error searching the keyring: %s\n"), gpg_strerror (rc)); goto leave; } /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) BUG (); psk = node->pkt->pkt.public_key; rc = agent_probe_secret_key (NULL, psk); if (rc) { log_error (_("secret key \"%s\" not found: %s\n"), uname, gpg_strerror (rc)); goto leave; } keyid_from_pk (psk, keyid ); print_seckey_info (ctrl, psk); tty_printf("\n"); if (!cpr_get_answer_is_yes ("gen_revoke.okay", _("Create a revocation certificate for this key? (y/N) "))) { rc = 0; goto leave; } /* Get the reason for the revocation. */ reason = ask_revocation_reason (1, 0, 1); if (!reason) { /* User decided to cancel. */ rc = 0; goto leave; } if (!opt.armor) tty_printf (_("ASCII armored output forced.\n")); rc = create_revocation (ctrl, NULL, reason, psk, keyblock, NULL, 0, NULL); if (rc) goto leave; /* and issue a usage notice */ tty_printf (_( "Revocation certificate created.\n\n" "Please move it to a medium which you can hide away; if Mallory gets\n" "access to this certificate he can use it to make your key unusable.\n" "It is smart to print this certificate and store it away, just in case\n" "your media become unreadable. But have some caution: The print system of\n" "your machine might store the data and make it available to others!\n")); leave: release_kbnode (keyblock); keydb_release (kdbhd); release_revocation_reason_info( reason ); return rc; } struct revocation_reason_info * ask_revocation_reason( int key_rev, int cert_rev, int hint ) { int code=-1; char *description = NULL; struct revocation_reason_info *reason; const char *text_0 = _("No reason specified"); const char *text_1 = _("Key has been compromised"); const char *text_2 = _("Key is superseded"); const char *text_3 = _("Key is no longer used"); const char *text_4 = _("User ID is no longer valid"); const char *code_text = NULL; do { code=-1; xfree(description); description = NULL; tty_printf(_("Please select the reason for the revocation:\n")); tty_printf( " 0 = %s\n", text_0 ); if( key_rev ) tty_printf(" 1 = %s\n", text_1 ); if( key_rev ) tty_printf(" 2 = %s\n", text_2 ); if( key_rev ) tty_printf(" 3 = %s\n", text_3 ); if( cert_rev ) tty_printf(" 4 = %s\n", text_4 ); tty_printf( " Q = %s\n", _("Cancel") ); if( hint ) tty_printf(_("(Probably you want to select %d here)\n"), hint ); while(code==-1) { int n; char *answer = cpr_get("ask_revocation_reason.code", _("Your decision? ")); trim_spaces( answer ); cpr_kill_prompt(); if( *answer == 'q' || *answer == 'Q') return NULL; /* cancel */ if( hint && !*answer ) n = hint; else if(!digitp( answer ) ) n = -1; else n = atoi(answer); xfree(answer); if( n == 0 ) { code = 0x00; /* no particular reason */ code_text = text_0; } else if( key_rev && n == 1 ) { code = 0x02; /* key has been compromised */ code_text = text_1; } else if( key_rev && n == 2 ) { code = 0x01; /* key is superseded */ code_text = text_2; } else if( key_rev && n == 3 ) { code = 0x03; /* key is no longer used */ code_text = text_3; } else if( cert_rev && n == 4 ) { code = 0x20; /* uid is no longer valid */ code_text = text_4; } else tty_printf(_("Invalid selection.\n")); } tty_printf(_("Enter an optional description; " "end it with an empty line:\n") ); for(;;) { char *answer = cpr_get("ask_revocation_reason.text", "> " ); trim_trailing_ws( answer, strlen(answer) ); cpr_kill_prompt(); if( !*answer ) { xfree(answer); break; } { char *p = make_printable_string( answer, strlen(answer), 0 ); xfree(answer); answer = p; } if( !description ) description = xstrdup(answer); else { char *p = xmalloc( strlen(description) + strlen(answer) + 2 ); strcpy(stpcpy(stpcpy( p, description),"\n"),answer); xfree(description); description = p; } xfree(answer); } tty_printf(_("Reason for revocation: %s\n"), code_text ); if( !description ) tty_printf(_("(No description given)\n") ); else tty_printf("%s\n", description ); } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay", _("Is this okay? (y/N) ")) ); reason = xmalloc( sizeof *reason ); reason->code = code; reason->desc = description; return reason; } struct revocation_reason_info * get_default_uid_revocation_reason(void) { struct revocation_reason_info *reason; reason = xmalloc( sizeof *reason ); reason->code = 0x20; /* uid is no longer valid */ reason->desc = strdup(""); /* no text */ return reason; } void release_revocation_reason_info( struct revocation_reason_info *reason ) { if( reason ) { xfree( reason->desc ); xfree( reason ); } } diff --git a/g10/sign.c b/g10/sign.c index 4cf0cd39a..0e379bcc2 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -1,1662 +1,1662 @@ /* sign.c - sign data * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2007, 2010, 2012 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG 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 3 of the License, or * (at your option) any later version. * * GnuPG 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, see . */ #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "../common/iobuf.h" #include "keydb.h" #include "../common/util.h" #include "main.h" #include "filter.h" #include "../common/ttyio.h" #include "trustdb.h" #include "../common/status.h" #include "../common/i18n.h" #include "pkglue.h" #include "../common/sysutils.h" #include "call-agent.h" #include "../common/mbox-util.h" #include "../common/compliance.h" #ifdef HAVE_DOSISH_SYSTEM #define LF "\r\n" #else #define LF "\n" #endif static int recipient_digest_algo=0; /**************** * Create notations and other stuff. It is assumed that the stings in * STRLIST are already checked to contain only printable data and have * a valid NAME=VALUE format. */ static void mk_notation_policy_etc (PKT_signature *sig, PKT_public_key *pk, PKT_public_key *pksk) { const char *string; char *p = NULL; strlist_t pu = NULL; struct notation *nd = NULL; struct expando_args args; log_assert (sig->version >= 4); memset (&args, 0, sizeof(args)); args.pk = pk; args.pksk = pksk; /* Notation data. */ if (IS_SIG(sig) && opt.sig_notations) nd = opt.sig_notations; else if (IS_CERT(sig) && opt.cert_notations) nd = opt.cert_notations; if (nd) { struct notation *item; for (item = nd; item; item = item->next) { item->altvalue = pct_expando (item->value,&args); if (!item->altvalue) log_error (_("WARNING: unable to %%-expand notation " "(too large). Using unexpanded.\n")); } keygen_add_notations (sig, nd); for (item = nd; item; item = item->next) { xfree (item->altvalue); item->altvalue = NULL; } } /* Set policy URL. */ if (IS_SIG(sig) && opt.sig_policy_url) pu = opt.sig_policy_url; else if (IS_CERT(sig) && opt.cert_policy_url) pu = opt.cert_policy_url; for (; pu; pu = pu->next) { string = pu->d; p = pct_expando (string, &args); if (!p) { log_error(_("WARNING: unable to %%-expand policy URL " "(too large). Using unexpanded.\n")); p = xstrdup(string); } build_sig_subpkt (sig, (SIGSUBPKT_POLICY | ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0)), p, strlen (p)); xfree (p); } /* Preferred keyserver URL. */ if (IS_SIG(sig) && opt.sig_keyserver_url) pu = opt.sig_keyserver_url; for (; pu; pu = pu->next) { string = pu->d; p = pct_expando (string, &args); if (!p) { log_error (_("WARNING: unable to %%-expand preferred keyserver URL" " (too large). Using unexpanded.\n")); p = xstrdup (string); } build_sig_subpkt (sig, (SIGSUBPKT_PREF_KS | ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0)), p, strlen (p)); xfree (p); } /* Set signer's user id. */ if (IS_SIG (sig) && !opt.flags.disable_signer_uid) { char *mbox; /* For now we use the uid which was used to locate the key. */ if (pksk->user_id && (mbox = mailbox_from_userid (pksk->user_id->name))) { if (DBG_LOOKUP) log_debug ("setting Signer's UID to '%s'\n", mbox); build_sig_subpkt (sig, SIGSUBPKT_SIGNERS_UID, mbox, strlen (mbox)); xfree (mbox); } else if (opt.sender_list) { /* If a list of --sender was given we scan that list and use * the first one matching a user id of the current key. */ /* FIXME: We need to get the list of user ids for the PKSK * packet. That requires either a function to look it up * again or we need to extend the key packet struct to link * to the primary key which in turn could link to the user * ids. Too much of a change right now. Let's take just * one from the supplied list and hope that the caller * passed a matching one. */ build_sig_subpkt (sig, SIGSUBPKT_SIGNERS_UID, opt.sender_list->d, strlen (opt.sender_list->d)); } } } /* * Helper to hash a user ID packet. */ static void hash_uid (gcry_md_hd_t md, int sigversion, const PKT_user_id *uid) { byte buf[5]; (void)sigversion; if (uid->attrib_data) { buf[0] = 0xd1; /* Indicates an attribute packet. */ buf[1] = uid->attrib_len >> 24; /* Always use 4 length bytes. */ buf[2] = uid->attrib_len >> 16; buf[3] = uid->attrib_len >> 8; buf[4] = uid->attrib_len; } else { buf[0] = 0xb4; /* Indicates a userid packet. */ buf[1] = uid->len >> 24; /* Always use 4 length bytes. */ buf[2] = uid->len >> 16; buf[3] = uid->len >> 8; buf[4] = uid->len; } gcry_md_write( md, buf, 5 ); if (uid->attrib_data) gcry_md_write (md, uid->attrib_data, uid->attrib_len ); else gcry_md_write (md, uid->name, uid->len ); } /* * Helper to hash some parts from the signature */ static void hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig) { byte buf[6]; size_t n; gcry_md_putc (md, sig->version); gcry_md_putc (md, sig->sig_class); gcry_md_putc (md, sig->pubkey_algo); gcry_md_putc (md, sig->digest_algo); if (sig->hashed) { n = sig->hashed->len; gcry_md_putc (md, (n >> 8) ); gcry_md_putc (md, n ); gcry_md_write (md, sig->hashed->data, n ); n += 6; } else { gcry_md_putc (md, 0); /* Always hash the length of the subpacket. */ gcry_md_putc (md, 0); n = 6; } /* Add some magic. */ buf[0] = sig->version; buf[1] = 0xff; buf[2] = n >> 24; /* (n is only 16 bit, so this is always 0) */ buf[3] = n >> 16; buf[4] = n >> 8; buf[5] = n; gcry_md_write (md, buf, 6); } /* Perform the sign operation. If CACHE_NONCE is given the agent is advised to use that cached passphrase for the key. */ static int do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig, gcry_md_hd_t md, int mdalgo, const char *cache_nonce) { gpg_error_t err; byte *dp; char *hexgrip; if (pksk->timestamp > sig->timestamp ) { ulong d = pksk->timestamp - sig->timestamp; log_info (ngettext("key %s was created %lu second" " in the future (time warp or clock problem)\n", "key %s was created %lu seconds" " in the future (time warp or clock problem)\n", d), keystr_from_pk (pksk), d); if (!opt.ignore_time_conflict) return gpg_error (GPG_ERR_TIME_CONFLICT); } print_pubkey_algo_note (pksk->pubkey_algo); if (!mdalgo) mdalgo = gcry_md_get_algo (md); /* Check compliance. */ if (! gnupg_digest_is_allowed (opt.compliance, 1, mdalgo)) { log_error (_("digest algorithm '%s' may not be used in %s mode\n"), gcry_md_algo_name (mdalgo), gnupg_compliance_option_string (opt.compliance)); err = gpg_error (GPG_ERR_DIGEST_ALGO); goto leave; } if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pksk->pubkey_algo, pksk->pkey, nbits_from_pk (pksk), NULL)) { log_error (_("key %s may not be used for signing in %s mode\n"), keystr_from_pk (pksk), gnupg_compliance_option_string (opt.compliance)); err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { err = 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", err); goto leave; } print_digest_algo_note (mdalgo); dp = gcry_md_read (md, mdalgo); sig->digest_algo = mdalgo; sig->digest_start[0] = dp[0]; sig->digest_start[1] = dp[1]; mpi_release (sig->data[0]); sig->data[0] = NULL; mpi_release (sig->data[1]); sig->data[1] = NULL; err = hexkeygrip_from_pk (pksk, &hexgrip); if (!err) { char *desc; gcry_sexp_t s_sigval; desc = gpg_format_keydesc (ctrl, pksk, FORMAT_KEYDESC_NORMAL, 1); err = agent_pksign (NULL/*ctrl*/, cache_nonce, hexgrip, desc, pksk->keyid, pksk->main_keyid, pksk->pubkey_algo, dp, gcry_md_get_algo_dlen (mdalgo), mdalgo, &s_sigval); xfree (desc); if (err) ; else if (pksk->pubkey_algo == GCRY_PK_RSA || pksk->pubkey_algo == GCRY_PK_RSA_S) sig->data[0] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG); else if (openpgp_oid_is_ed25519 (pksk->pkey[0])) { sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_OPAQUE); sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_OPAQUE); } else { sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_USG); sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG); } gcry_sexp_release (s_sigval); } xfree (hexgrip); leave: if (err) log_error (_("signing failed: %s\n"), gpg_strerror (err)); else { if (opt.verbose) { char *ustr = get_user_id_string_native (ctrl, sig->keyid); log_info (_("%s/%s signature from: \"%s\"\n"), openpgp_pk_algo_name (pksk->pubkey_algo), openpgp_md_algo_name (sig->digest_algo), ustr); xfree (ustr); } } return err; } static int complete_sig (ctrl_t ctrl, PKT_signature *sig, PKT_public_key *pksk, gcry_md_hd_t md, const char *cache_nonce) { int rc; /* if (!(rc = check_secret_key (pksk, 0))) */ rc = do_sign (ctrl, pksk, sig, md, 0, cache_nonce); return rc; } /* Return true if the key seems to be on a version 1 OpenPGP card. This works by asking the agent and may fail if the card has not yet been used with the agent. */ static int openpgp_card_v1_p (PKT_public_key *pk) { gpg_error_t err; int result; /* Shortcut if we are not using RSA: The v1 cards only support RSA thus there is no point in looking any further. */ if (!is_RSA (pk->pubkey_algo)) return 0; if (!pk->flags.serialno_valid) { char *hexgrip; err = hexkeygrip_from_pk (pk, &hexgrip); if (err) { log_error ("error computing a keygrip: %s\n", gpg_strerror (err)); return 0; /* Ooops. */ } xfree (pk->serialno); agent_get_keyinfo (NULL, hexgrip, &pk->serialno, NULL); xfree (hexgrip); pk->flags.serialno_valid = 1; } if (!pk->serialno) result = 0; /* Error from a past agent_get_keyinfo or no card. */ else { /* The version number of the card is included in the serialno. */ result = !strncmp (pk->serialno, "D2760001240101", 14); } return result; } static int match_dsa_hash (unsigned int qbytes) { if (qbytes <= 20) return DIGEST_ALGO_SHA1; if (qbytes <= 28) return DIGEST_ALGO_SHA224; if (qbytes <= 32) return DIGEST_ALGO_SHA256; if (qbytes <= 48) return DIGEST_ALGO_SHA384; if (qbytes <= 66 ) /* 66 corresponds to 521 (64 to 512) */ return DIGEST_ALGO_SHA512; return DEFAULT_DIGEST_ALGO; /* DEFAULT_DIGEST_ALGO will certainly fail, but it's the best wrong answer we have if a digest larger than 512 bits is requested. */ } /* First try --digest-algo. If that isn't set, see if the recipient has a preferred algorithm (which is also filtered through --personal-digest-prefs). If we're making a signature without a particular recipient (i.e. signing, rather than signing+encrypting) then take the first algorithm in --personal-digest-prefs that is usable for the pubkey algorithm. If --personal-digest-prefs isn't set, then take the OpenPGP default (i.e. SHA-1). Note that Ed25519+EdDSA takes an input of arbitrary length and thus we don't enforce any particular algorithm like we do for standard ECDSA. However, we use SHA256 as the default algorithm. Possible improvement: Use the highest-ranked usable algorithm from the signing key prefs either before or after using the personal list? */ static int hash_for (PKT_public_key *pk) { if (opt.def_digest_algo) { return opt.def_digest_algo; } else if (recipient_digest_algo) { return recipient_digest_algo; } else if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA && openpgp_oid_is_ed25519 (pk->pkey[0])) { if (opt.personal_digest_prefs) return opt.personal_digest_prefs[0].value; else return DIGEST_ALGO_SHA256; } else if (pk->pubkey_algo == PUBKEY_ALGO_DSA || pk->pubkey_algo == PUBKEY_ALGO_ECDSA) { unsigned int qbytes = gcry_mpi_get_nbits (pk->pkey[1]); if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA) qbytes = ecdsa_qbits_from_Q (qbytes); qbytes = qbytes/8; /* It's a DSA key, so find a hash that is the same size as q or larger. If q is 160, assume it is an old DSA key and use a 160-bit hash unless --enable-dsa2 is set, in which case act like a new DSA key that just happens to have a 160-bit q (i.e. allow truncation). If q is not 160, by definition it must be a new DSA key. */ if (opt.personal_digest_prefs) { prefitem_t *prefs; if (qbytes != 20 || opt.flags.dsa2) { for (prefs=opt.personal_digest_prefs; prefs->type; prefs++) if (gcry_md_get_algo_dlen (prefs->value) >= qbytes) return prefs->value; } else { for (prefs=opt.personal_digest_prefs; prefs->type; prefs++) if (gcry_md_get_algo_dlen (prefs->value) == qbytes) return prefs->value; } } return match_dsa_hash(qbytes); } else if (openpgp_card_v1_p (pk)) { /* The sk lives on a smartcard, and old smartcards only handle SHA-1 and RIPEMD/160. Newer smartcards (v2.0) don't have this restriction anymore. Fortunately the serial number encodes the version of the card and thus we know that this key is on a v1 card. */ if(opt.personal_digest_prefs) { prefitem_t *prefs; for (prefs=opt.personal_digest_prefs;prefs->type;prefs++) if (prefs->value==DIGEST_ALGO_SHA1 || prefs->value==DIGEST_ALGO_RMD160) return prefs->value; } return DIGEST_ALGO_SHA1; } else if (opt.personal_digest_prefs) { /* It's not DSA, so we can use whatever the first hash algorithm is in the pref list */ return opt.personal_digest_prefs[0].value; } else return DEFAULT_DIGEST_ALGO; } static void print_status_sig_created (PKT_public_key *pk, PKT_signature *sig, int what) { byte array[MAX_FINGERPRINT_LEN]; char buf[100+MAX_FINGERPRINT_LEN*2]; size_t n; snprintf (buf, sizeof buf - 2*MAX_FINGERPRINT_LEN, "%c %d %d %02x %lu ", what, sig->pubkey_algo, sig->digest_algo, sig->sig_class, (ulong)sig->timestamp ); fingerprint_from_pk (pk, array, &n); bin2hex (array, n, buf + strlen (buf)); write_status_text( STATUS_SIG_CREATED, buf ); } /* * Loop over the secret certificates in SK_LIST and build the one pass * signature packets. OpenPGP says that the data should be bracket by * the onepass-sig and signature-packet; so we build these onepass * packet here in reverse order */ static int write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass ) { int skcount; SK_LIST sk_rover; for (skcount=0, sk_rover=sk_list; sk_rover; sk_rover = sk_rover->next) skcount++; for (; skcount; skcount--) { PKT_public_key *pk; PKT_onepass_sig *ops; PACKET pkt; int i, rc; for (i=0, sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { if (++i == skcount) break; } pk = sk_rover->pk; ops = xmalloc_clear (sizeof *ops); ops->sig_class = sigclass; ops->digest_algo = hash_for (pk); ops->pubkey_algo = pk->pubkey_algo; keyid_from_pk (pk, ops->keyid); ops->last = (skcount == 1); init_packet(&pkt); pkt.pkttype = PKT_ONEPASS_SIG; pkt.pkt.onepass_sig = ops; rc = build_packet (out, &pkt); free_packet (&pkt, NULL); if (rc) { log_error ("build onepass_sig packet failed: %s\n", gpg_strerror (rc)); return rc; } } return 0; } /* * Helper to write the plaintext (literal data) packet */ static int write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode) { PKT_plaintext *pt = NULL; u32 filesize; int rc = 0; if (!opt.no_literal) pt=setup_plaintext_name(fname,inp); /* try to calculate the length of the data */ if ( !iobuf_is_pipe_filename (fname) && *fname ) { off_t tmpsize; int overflow; if( !(tmpsize = iobuf_get_filelength(inp, &overflow)) && !overflow && opt.verbose) log_info (_("WARNING: '%s' is an empty file\n"), fname); /* 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; /* Because the text_filter modifies the length of the * data, it is not possible to know the used length * without a double read of the file - to avoid that * we simple use partial length packets. */ if ( ptmode == 't' || ptmode == 'u' || ptmode == 'm') filesize = 0; } else filesize = opt.set_filesize? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { PACKET pkt; /* Note that PT has been initialized above in no_literal mode. */ pt->timestamp = make_timestamp (); pt->mode = ptmode; pt->len = filesize; pt->new_ctb = !pt->len; pt->buf = inp; init_packet(&pkt); pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; /*cfx.datalen = filesize? calc_packet_length( &pkt ) : 0;*/ if( (rc = build_packet (out, &pkt)) ) log_error ("build_packet(PLAINTEXT) failed: %s\n", gpg_strerror (rc) ); pt->buf = NULL; free_packet (&pkt, NULL); } else { 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 */ } /* fixme: it seems that we never freed pt/pkt */ return rc; } /* * Write the signatures from the SK_LIST to OUT. HASH must be a non-finalized * hash which will not be changes here. */ static int write_signature_packets (ctrl_t ctrl, SK_LIST sk_list, IOBUF out, gcry_md_hd_t hash, int sigclass, u32 timestamp, u32 duration, int status_letter, const char *cache_nonce) { SK_LIST sk_rover; /* Loop over the certificates with secret keys. */ for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) { PKT_public_key *pk; PKT_signature *sig; gcry_md_hd_t md; int rc; pk = sk_rover->pk; /* Build the signature packet. */ sig = xtrycalloc (1, sizeof *sig); if (!sig) return gpg_error_from_syserror (); if (duration || opt.sig_policy_url || opt.sig_notations || opt.sig_keyserver_url) sig->version = 4; else sig->version = pk->version; keyid_from_pk (pk, sig->keyid); sig->digest_algo = hash_for (pk); sig->pubkey_algo = pk->pubkey_algo; if (timestamp) sig->timestamp = timestamp; else sig->timestamp = make_timestamp(); if (duration) sig->expiredate = sig->timestamp + duration; sig->sig_class = sigclass; if (gcry_md_copy (&md, hash)) BUG (); if (sig->version >= 4) { build_sig_subpkt_from_sig (sig, pk); mk_notation_policy_etc (sig, NULL, pk); } hash_sigversion_to_magic (md, sig); gcry_md_final (md); rc = do_sign (ctrl, pk, sig, md, hash_for (pk), cache_nonce); gcry_md_close (md); if (!rc) { /* Write the packet. */ PACKET pkt; init_packet (&pkt); pkt.pkttype = PKT_SIGNATURE; pkt.pkt.signature = sig; rc = build_packet (out, &pkt); if (!rc && is_status_enabled()) print_status_sig_created (pk, sig, status_letter); free_packet (&pkt, NULL); if (rc) log_error ("build signature packet failed: %s\n", gpg_strerror (rc)); } else xfree (sig); if (rc) return rc; } return 0; } /**************** * Sign the files whose names are in FILENAME. * If DETACHED has the value true, * make a detached signature. If FILENAMES->d is NULL read from stdin * and ignore the detached mode. Sign the file with all secret keys * which can be taken from LOCUSR, if this is NULL, use the default one * If ENCRYPTFLAG is true, use REMUSER (or ask if it is NULL) to encrypt the * signed data for these users. * If OUTFILE is not NULL; this file is used for output and the function * does not ask for overwrite permission; output is then always * uncompressed, non-armored and in binary mode. */ int sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, int encryptflag, strlist_t remusr, const char *outfile ) { const char *fname; armor_filter_context_t *afx; compress_filter_context_t zfx; md_filter_context_t mfx; text_filter_context_t tfx; progress_filter_context_t *pfx; encrypt_filter_context_t efx; IOBUF inp = NULL, out = NULL; PACKET pkt; int rc = 0; PK_LIST pk_list = NULL; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; int multifile = 0; u32 duration=0; pfx = new_progress_context (); afx = new_armor_context (); memset( &zfx, 0, sizeof zfx); memset( &mfx, 0, sizeof mfx); memset( &efx, 0, sizeof efx); efx.ctrl = ctrl; init_packet( &pkt ); if( filenames ) { fname = filenames->d; multifile = !!filenames->next; } else fname = NULL; if( fname && filenames->next && (!detached || encryptflag) ) log_bug("multiple files can only be detached signed"); if(encryptflag==2 && (rc=setup_symkey(&efx.symkey_s2k,&efx.symkey_dek))) goto leave; if (opt.ask_sig_expire && !opt.batch) duration = ask_expire_interval(1,opt.def_sig_expire); else duration = parse_expire_string(opt.def_sig_expire); /* Note: In the old non-agent version the following call used to unprotect the secret key. This is now done on demand by the agent. */ if( (rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG )) ) goto leave; if (encryptflag && (rc=build_pk_list (ctrl, remusr, &pk_list))) goto leave; /* prepare iobufs */ if( multifile ) /* have list of filenames */ inp = NULL; /* we do it later */ else { inp = iobuf_open(fname); 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"), fname? fname: "[stdin]", strerror(errno) ); goto leave; } handle_progress (pfx, inp, fname); } if( outfile ) { if (is_secured_filename ( outfile )) { out = NULL; gpg_err_set_errno (EPERM); } else out = iobuf_create (outfile, 0); if( !out ) { rc = gpg_error_from_syserror (); log_error(_("can't create '%s': %s\n"), outfile, strerror(errno) ); goto leave; } else if( opt.verbose ) log_info(_("writing to '%s'\n"), outfile ); } else if( (rc = open_outfile (-1, fname, - opt.armor? 1: detached? 2:0, 0, &out))) + opt.armor? 1: detached? 2:0, 0, &out, 0))) goto leave; /* prepare to calculate the MD over the input */ if( opt.textmode && !outfile && !multifile ) { memset( &tfx, 0, sizeof tfx); iobuf_push_filter( inp, text_filter, &tfx ); } if ( gcry_md_open (&mfx.md, 0, 0) ) BUG (); if (DBG_HASHING) gcry_md_debug (mfx.md, "sign"); /* If we're encrypting and signing, it is reasonable to pick the hash algorithm to use out of the recipient key prefs. This is best effort only, as in a DSA2 and smartcard world there are cases where we cannot please everyone with a single hash (DSA2 wants >160 and smartcards want =160). In the future this could be more complex with different hashes for each sk, but the current design requires a single hash for all SKs. */ if(pk_list) { if(opt.def_digest_algo) { if(!opt.expert && select_algo_from_prefs(pk_list,PREFTYPE_HASH, opt.def_digest_algo, NULL)!=opt.def_digest_algo) log_info(_("WARNING: forcing digest algorithm %s (%d)" " violates recipient preferences\n"), gcry_md_algo_name (opt.def_digest_algo), opt.def_digest_algo ); } else { int algo, smartcard=0; union pref_hint hint; hint.digest_length = 0; /* Of course, if the recipient asks for something unreasonable (like the wrong hash for a DSA key) then don't do it. Check all sk's - if any are DSA or live on a smartcard, then the hash has restrictions and we may not be able to give the recipient what they want. For DSA, pass a hint for the largest q we have. Note that this means that a q>160 key will override a q=160 key and force the use of truncation for the q=160 key. The alternative would be to ignore the recipient prefs completely and get a different hash for each DSA key in hash_for(). The override behavior here is more or less reasonable as it is under the control of the user which keys they sign with for a given message and the fact that the message with multiple signatures won't be usable on an implementation that doesn't understand DSA2 anyway. */ for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA || sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) { int temp_hashlen = (gcry_mpi_get_nbits (sk_rover->pk->pkey[1])); if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) temp_hashlen = ecdsa_qbits_from_Q (temp_hashlen); temp_hashlen = (temp_hashlen+7)/8; /* Pick a hash that is large enough for our largest q */ if (hint.digest_lengthpk->is_protected */ /* && sk_rover->pk->protect.s2k.mode == 1002) */ /* smartcard = 1; */ } /* Current smartcards only do 160-bit hashes. If we have to have a >160-bit hash, then we can't use the recipient prefs as we'd need both =160 and >160 at the same time and recipient prefs currently require a single hash for all signatures. All this may well have to change as the cards add algorithms. */ if (!smartcard || (smartcard && hint.digest_length==20)) if ( (algo= select_algo_from_prefs(pk_list,PREFTYPE_HASH,-1,&hint)) > 0) recipient_digest_algo=algo; } } for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); if( !multifile ) iobuf_push_filter( inp, md_filter, &mfx ); if( detached && !encryptflag) afx->what = 2; if( opt.armor && !outfile ) push_armor_filter (afx, out); if( encryptflag ) { efx.pk_list = pk_list; /* fixme: set efx.cfx.datalen if known */ iobuf_push_filter( out, encrypt_filter, &efx ); } if (opt.compress_algo && !outfile && !detached) { int compr_algo=opt.compress_algo; /* If not forced by user */ if(compr_algo==-1) { /* If we're not encrypting, then select_algo_from_prefs will fail and we'll end up with the default. If we are encrypting, select_algo_from_prefs cannot fail since there is an assumed preference for uncompressed data. Still, if it did fail, we'll also end up with the default. */ if((compr_algo= select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1) compr_algo=default_compress_algo(); } else if(!opt.expert && pk_list && 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 ) push_compress_filter(out,&zfx,compr_algo); } /* Write the one-pass signature packets if needed */ if (!detached) { rc = write_onepass_sig_packets (sk_list, out, opt.textmode && !outfile ? 0x01:0x00); if (rc) goto leave; } write_status_begin_signing (mfx.md); /* Setup the inner packet. */ if( detached ) { if( multifile ) { strlist_t sl; if( opt.verbose ) log_info(_("signing:") ); /* must walk reverse trough this list */ for( sl = strlist_last(filenames); sl; sl = strlist_prev( filenames, sl ) ) { inp = iobuf_open(sl->d); 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"), sl->d,strerror(errno)); goto leave; } handle_progress (pfx, inp, sl->d); if( opt.verbose ) log_printf (" '%s'", sl->d ); if(opt.textmode) { memset( &tfx, 0, sizeof tfx); iobuf_push_filter( inp, text_filter, &tfx ); } iobuf_push_filter( inp, md_filter, &mfx ); while( iobuf_get(inp) != -1 ) ; iobuf_close(inp); inp = NULL; } if( opt.verbose ) log_printf ("\n"); } else { /* read, so that the filter can calculate the digest */ while( iobuf_get(inp) != -1 ) ; } } else { rc = write_plaintext_packet (out, inp, fname, opt.textmode && !outfile ? (opt.mimemode? 'm':'t'):'b'); } /* catch errors from above */ if (rc) goto leave; /* write the signatures */ rc = write_signature_packets (ctrl, sk_list, out, mfx.md, opt.textmode && !outfile? 0x01 : 0x00, 0, duration, detached ? 'D':'S', NULL); if( rc ) goto leave; leave: if( rc ) iobuf_cancel(out); else { iobuf_close(out); if (encryptflag) write_status( STATUS_END_ENCRYPTION ); } iobuf_close(inp); gcry_md_close ( mfx.md ); release_sk_list( sk_list ); release_pk_list( pk_list ); recipient_digest_algo=0; release_progress_context (pfx); release_armor_context (afx); return rc; } /**************** * make a clear signature. note that opt.armor is not needed */ int clearsign_file (ctrl_t ctrl, const char *fname, strlist_t locusr, const char *outfile ) { armor_filter_context_t *afx; progress_filter_context_t *pfx; gcry_md_hd_t textmd = NULL; IOBUF inp = NULL, out = NULL; PACKET pkt; int rc = 0; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; u32 duration=0; pfx = new_progress_context (); afx = new_armor_context (); init_packet( &pkt ); if (opt.ask_sig_expire && !opt.batch) duration = ask_expire_interval (1,opt.def_sig_expire); else duration = parse_expire_string (opt.def_sig_expire); /* Note: In the old non-agent version the following call used to unprotect the secret key. This is now done on demand by the agent. */ if( (rc=build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG )) ) goto leave; /* prepare iobufs */ inp = iobuf_open(fname); 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"), fname? fname: "[stdin]", strerror(errno) ); goto leave; } handle_progress (pfx, inp, fname); if( outfile ) { if (is_secured_filename (outfile) ) { outfile = NULL; gpg_err_set_errno (EPERM); } else out = iobuf_create (outfile, 0); if( !out ) { rc = gpg_error_from_syserror (); log_error(_("can't create '%s': %s\n"), outfile, strerror(errno) ); goto leave; } else if( opt.verbose ) log_info(_("writing to '%s'\n"), outfile ); } - else if ((rc = open_outfile (-1, fname, 1, 0, &out))) + else if ((rc = open_outfile (-1, fname, 1, 0, &out, 0))) goto leave; iobuf_writestr(out, "-----BEGIN PGP SIGNED MESSAGE-----" LF ); { const char *s; int any = 0; byte hashs_seen[256]; memset( hashs_seen, 0, sizeof hashs_seen ); iobuf_writestr(out, "Hash: " ); for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { int i = hash_for (sk_rover->pk); if( !hashs_seen[ i & 0xff ] ) { s = gcry_md_algo_name ( i ); if( s ) { hashs_seen[ i & 0xff ] = 1; if( any ) iobuf_put(out, ',' ); iobuf_writestr(out, s ); any = 1; } } } log_assert(any); iobuf_writestr(out, LF ); } if( opt.not_dash_escaped ) iobuf_writestr( out, "NotDashEscaped: You need "GPG_NAME " to verify this message" LF ); iobuf_writestr(out, LF ); if ( gcry_md_open (&textmd, 0, 0) ) BUG (); for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) gcry_md_enable (textmd, hash_for(sk_rover->pk)); if ( DBG_HASHING ) gcry_md_debug ( textmd, "clearsign" ); copy_clearsig_text (out, inp, textmd, !opt.not_dash_escaped, opt.escape_from); /* fixme: check for read errors */ /* now write the armor */ afx->what = 2; push_armor_filter (afx, out); /* Write the signatures. */ rc = write_signature_packets (ctrl, sk_list, out, textmd, 0x01, 0, duration, 'C', NULL); if( rc ) goto leave; leave: if( rc ) iobuf_cancel(out); else iobuf_close(out); iobuf_close(inp); gcry_md_close ( textmd ); release_sk_list( sk_list ); release_progress_context (pfx); release_armor_context (afx); return rc; } /* * Sign and conventionally encrypt the given file. * FIXME: Far too much code is duplicated - revamp the whole file. */ int sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) { armor_filter_context_t *afx; progress_filter_context_t *pfx; compress_filter_context_t zfx; md_filter_context_t mfx; text_filter_context_t tfx; cipher_filter_context_t cfx; IOBUF inp = NULL, out = NULL; PACKET pkt; STRING2KEY *s2k = NULL; int rc = 0; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; int algo; u32 duration=0; int canceled; pfx = new_progress_context (); afx = new_armor_context (); memset( &zfx, 0, sizeof zfx); memset( &mfx, 0, sizeof mfx); memset( &tfx, 0, sizeof tfx); memset( &cfx, 0, sizeof cfx); init_packet( &pkt ); if (opt.ask_sig_expire && !opt.batch) duration = ask_expire_interval (1, opt.def_sig_expire); else duration = parse_expire_string (opt.def_sig_expire); /* Note: In the old non-agent version the following call used to unprotect the secret key. This is now done on demand by the agent. */ rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG); if (rc) goto leave; /* prepare iobufs */ inp = iobuf_open(fname); 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"), fname? fname: "[stdin]", strerror(errno) ); goto leave; } handle_progress (pfx, inp, fname); /* prepare key */ s2k = xmalloc_clear( sizeof *s2k ); s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; algo = default_cipher_algo(); if (!opt.quiet || !opt.batch) log_info (_("%s encryption will be used\n"), openpgp_cipher_algo_name (algo) ); cfx.dek = passphrase_to_dek (algo, s2k, 1, 1, NULL, &canceled); if (!cfx.dek || !cfx.dek->keylen) { rc = gpg_error (canceled?GPG_ERR_CANCELED:GPG_ERR_BAD_PASSPHRASE); log_error(_("error creating passphrase: %s\n"), gpg_strerror (rc) ); goto leave; } cfx.dek->use_mdc = use_mdc (NULL, cfx.dek->algo); /* now create the outfile */ - rc = open_outfile (-1, fname, opt.armor? 1:0, 0, &out); + rc = open_outfile (-1, fname, opt.armor? 1:0, 0, &out, 0); if (rc) goto leave; /* prepare to calculate the MD over the input */ if (opt.textmode) iobuf_push_filter (inp, text_filter, &tfx); if ( gcry_md_open (&mfx.md, 0, 0) ) BUG (); if ( DBG_HASHING ) gcry_md_debug (mfx.md, "symc-sign"); for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); iobuf_push_filter (inp, md_filter, &mfx); /* Push armor output filter */ if (opt.armor) push_armor_filter (afx, out); /* Write the symmetric key packet */ /*(current filters: armor)*/ { PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc ); enc->version = 4; enc->cipher_algo = cfx.dek->algo; enc->s2k = *s2k; 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); } /* Push the encryption filter */ iobuf_push_filter( out, cipher_filter, &cfx ); /* Push the compress filter */ if (default_compress_algo()) { if (cfx.dek && cfx.dek->use_mdc) zfx.new_ctb = 1; push_compress_filter (out, &zfx,default_compress_algo() ); } /* Write the one-pass signature packets */ /*(current filters: zip - encrypt - armor)*/ rc = write_onepass_sig_packets (sk_list, out, opt.textmode? 0x01:0x00); if (rc) goto leave; write_status_begin_signing (mfx.md); /* Pipe data through all filters; i.e. write the signed stuff */ /*(current filters: zip - encrypt - armor)*/ rc = write_plaintext_packet (out, inp, fname, opt.textmode ? (opt.mimemode?'m':'t'):'b'); if (rc) goto leave; /* Write the signatures */ /*(current filters: zip - encrypt - armor)*/ rc = write_signature_packets (ctrl, sk_list, out, mfx.md, opt.textmode? 0x01 : 0x00, 0, duration, 'S', NULL); if( rc ) goto leave; leave: if( rc ) iobuf_cancel(out); else { iobuf_close(out); write_status( STATUS_END_ENCRYPTION ); } iobuf_close(inp); release_sk_list( sk_list ); gcry_md_close( mfx.md ); xfree(cfx.dek); xfree(s2k); release_progress_context (pfx); release_armor_context (afx); return rc; } /**************** * Create a v4 signature in *RET_SIG. * * PK is the primary key to sign (required for all sigs) * UID is the user id to sign (required for 0x10..0x13, 0x30) * SUBPK is subkey to sign (required for 0x18, 0x19, 0x28) * * PKSK is the signing key * * SIGCLASS is the type of signature to create. * * DIGEST_ALGO is the digest algorithm. If it is 0 the function * selects an appropriate one. * * TIMESTAMP is the timestamp to use for the signature. 0 means "now" * * DURATION is the amount of time (in seconds) until the signature * expires. * * This function creates the following subpackets: issuer, created, * and expire (if duration is not 0). Additional subpackets can be * added using MKSUBPKT, which is called after these subpackets are * added and before the signature is generated. OPAQUE is passed to * MKSUBPKT. */ int make_keysig_packet (ctrl_t ctrl, PKT_signature **ret_sig, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *subpk, PKT_public_key *pksk, int sigclass, int digest_algo, u32 timestamp, u32 duration, int (*mksubpkt)(PKT_signature *, void *), void *opaque, const char *cache_nonce) { PKT_signature *sig; int rc=0; int sigversion; gcry_md_hd_t md; log_assert ((sigclass >= 0x10 && sigclass <= 0x13) || sigclass == 0x1F || sigclass == 0x20 || sigclass == 0x18 || sigclass == 0x19 || sigclass == 0x30 || sigclass == 0x28 ); sigversion = 4; if (sigversion < pksk->version) sigversion = pksk->version; if( !digest_algo ) { /* Basically, this means use SHA1 always unless the user specified something (use whatever they said), or it's DSA (use the best match). They still can't pick an inappropriate hash for DSA or the signature will fail. Note that this still allows the caller of make_keysig_packet to override the user setting if it must. */ if(opt.cert_digest_algo) digest_algo=opt.cert_digest_algo; else if(pksk->pubkey_algo == PUBKEY_ALGO_DSA) digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8); else if (pksk->pubkey_algo == PUBKEY_ALGO_ECDSA || pksk->pubkey_algo == PUBKEY_ALGO_EDDSA) { if (openpgp_oid_is_ed25519 (pksk->pkey[0])) digest_algo = DIGEST_ALGO_SHA256; else digest_algo = match_dsa_hash (ecdsa_qbits_from_Q (gcry_mpi_get_nbits (pksk->pkey[1]))/8); } else digest_algo = DEFAULT_DIGEST_ALGO; } if ( gcry_md_open (&md, digest_algo, 0 ) ) BUG (); /* Hash the public key certificate. */ hash_public_key( md, pk ); if( sigclass == 0x18 || sigclass == 0x19 || sigclass == 0x28 ) { /* hash the subkey binding/backsig/revocation */ hash_public_key( md, subpk ); } else if( sigclass != 0x1F && sigclass != 0x20 ) { /* hash the user id */ hash_uid (md, sigversion, uid); } /* and make the signature packet */ sig = xmalloc_clear( sizeof *sig ); sig->version = sigversion; sig->flags.exportable=1; sig->flags.revocable=1; keyid_from_pk (pksk, sig->keyid); sig->pubkey_algo = pksk->pubkey_algo; sig->digest_algo = digest_algo; if(timestamp) sig->timestamp=timestamp; else sig->timestamp=make_timestamp(); if(duration) sig->expiredate=sig->timestamp+duration; sig->sig_class = sigclass; build_sig_subpkt_from_sig (sig, pksk); mk_notation_policy_etc (sig, pk, pksk); /* Crucial that the call to mksubpkt comes LAST before the calls to finalize the sig as that makes it possible for the mksubpkt function to get a reliable pointer to the subpacket area. */ if (mksubpkt) rc = (*mksubpkt)( sig, opaque ); if( !rc ) { hash_sigversion_to_magic (md, sig); gcry_md_final (md); rc = complete_sig (ctrl, sig, pksk, md, cache_nonce); } gcry_md_close (md); if( rc ) free_seckey_enc( sig ); else *ret_sig = sig; return rc; } /**************** * Create a new signature packet based on an existing one. * Only user ID signatures are supported for now. * PK is the public key to work on. * PKSK is the key used to make the signature. * * TODO: Merge this with make_keysig_packet. */ gpg_error_t update_keysig_packet (ctrl_t ctrl, PKT_signature **ret_sig, PKT_signature *orig_sig, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *subpk, PKT_public_key *pksk, int (*mksubpkt)(PKT_signature *, void *), void *opaque) { PKT_signature *sig; gpg_error_t rc = 0; int digest_algo; gcry_md_hd_t md; if ((!orig_sig || !pk || !pksk) || (orig_sig->sig_class >= 0x10 && orig_sig->sig_class <= 0x13 && !uid) || (orig_sig->sig_class == 0x18 && !subpk)) return GPG_ERR_GENERAL; if ( opt.cert_digest_algo ) digest_algo = opt.cert_digest_algo; else digest_algo = orig_sig->digest_algo; if ( gcry_md_open (&md, digest_algo, 0 ) ) BUG (); /* Hash the public key certificate and the user id. */ hash_public_key( md, pk ); if( orig_sig->sig_class == 0x18 ) hash_public_key( md, subpk ); else hash_uid (md, orig_sig->version, uid); /* create a new signature packet */ sig = copy_signature (NULL, orig_sig); sig->digest_algo=digest_algo; /* We need to create a new timestamp so that new sig expiration calculations are done correctly... */ sig->timestamp=make_timestamp(); /* ... but we won't make a timestamp earlier than the existing one. */ { int tmout = 0; while(sig->timestamp<=orig_sig->timestamp) { if (++tmout > 5 && !opt.ignore_time_conflict) { rc = gpg_error (GPG_ERR_TIME_CONFLICT); goto leave; } gnupg_sleep (1); sig->timestamp=make_timestamp(); } } /* Note that already expired sigs will remain expired (with a duration of 1) since build-packet.c:build_sig_subpkt_from_sig detects this case. */ /* Put the updated timestamp into the sig. Note that this will automagically lower any sig expiration dates to correctly correspond to the differences in the timestamps (i.e. the duration will shrink). */ build_sig_subpkt_from_sig (sig, pksk); if (mksubpkt) rc = (*mksubpkt)(sig, opaque); if (!rc) { hash_sigversion_to_magic (md, sig); gcry_md_final (md); rc = complete_sig (ctrl, sig, pksk, md, NULL); } leave: gcry_md_close (md); if( rc ) free_seckey_enc (sig); else *ret_sig = sig; return rc; }