diff --git a/configure.ac b/configure.ac index e26e51b4a..02e02bb1e 100644 --- a/configure.ac +++ b/configure.ac @@ -444,7 +444,8 @@ AH_BOTTOM([ #else #define GNUPG_DEFAULT_HOMEDIR "~/.gnupg" #endif -#define GNUPG_PRIVATE_KEYS_DIR "private-keys-v1.d" +#define GNUPG_PRIVATE_KEYS_DIR "private-keys-v1.d" +#define GNUPG_OPENPGP_REVOC_DIR "openpgp-revocs.d" /* For some systems (DOS currently), we hardcode the path here. For POSIX systems the values are constructed by the Makefiles, so that diff --git a/doc/gpg.texi b/doc/gpg.texi index 9c52282cb..5efc16e86 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -3106,6 +3106,15 @@ files; They all live in in the current home directory (@pxref{option @item ~/.gnupg/secring.gpg.lock The lock file for the secret keyring. + @item ~/.gnupg/openpgp-revocs.d/ + This is the directory where gpg stores pre-generated revocation + certificates. It is suggested to backup those certificates and if the + primary private key is not stored on the disk to move them to an + external storage device. Anyone who can access theses files is able to + revoke the corresponding key. You may want to print them out. You + should backup all files in this directory and take care to keep this + backup closed away. + @item /usr[/local]/share/gnupg/options.skel The skeleton options file. diff --git a/g10/keydb.h b/g10/keydb.h index b21d9550d..0cf6ca110 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -288,6 +288,7 @@ const char *colon_datestr_from_pk (PKT_public_key *pk); const char *colon_datestr_from_sig (PKT_signature *sig); const char *colon_expirestr_from_sig (PKT_signature *sig); byte *fingerprint_from_pk( PKT_public_key *pk, byte *buf, size_t *ret_len ); +char *hexfingerprint (PKT_public_key *pk); gpg_error_t keygrip_from_pk (PKT_public_key *pk, unsigned char *array); gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip); diff --git a/g10/keygen.c b/g10/keygen.c index 35c146068..450923144 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -4000,6 +4000,8 @@ do_generate_keypair (struct para_data_s *para, update_ownertrust (pk, ((get_ownertrust (pk) & ~TRUST_MASK) | TRUST_ULTIMATE )); + gen_standard_revoke (pk); + if (!opt.batch) { tty_printf (_("public and secret key created and signed.\n") ); diff --git a/g10/keyid.c b/g10/keyid.c index 9c94bd6b2..6ce6f3277 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -772,6 +772,20 @@ fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len) } +/* Return an allocated buffer with the fingerprint of PK formatted as + a plain hexstring. */ +char * +hexfingerprint (PKT_public_key *pk) +{ + unsigned char fpr[MAX_FINGERPRINT_LEN]; + size_t len; + char *result; + + fingerprint_from_pk (pk, fpr, &len); + result = xmalloc (2 * len + 1); + bin2hex (fpr, len, result); + return result; +} diff --git a/g10/main.h b/g10/main.h index ae0bc8c26..e75f6168f 100644 --- a/g10/main.h +++ b/g10/main.h @@ -274,6 +274,7 @@ int open_outfile (int inp_fd, const char *iname, int mode, int restrictedperm, iobuf_t *a); iobuf_t open_sigfile( const char *iname, 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 ); @@ -317,6 +318,7 @@ int enarmor_file( const char *fname ); /*-- revoke.c --*/ struct revocation_reason_info; +int gen_standard_revoke (PKT_public_key *psk); int gen_revoke( const char *uname ); int gen_desig_revoke( const char *uname, strlist_t locusr); int revocation_reason_build_cb( PKT_signature *sig, void *opaque ); diff --git a/g10/openfile.c b/g10/openfile.c index 901387d31..5a436485b 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -174,9 +174,10 @@ ask_outfile_name( const char *name, size_t namelen ) * 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 ignorea INAME and MODE. Note that INP_FD won't + * 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. */ @@ -239,7 +240,8 @@ open_outfile (int inp_fd, const char *iname, int mode, int restrictedperm, const char *newsfx; newsfx = (mode==1 ? ".asc" : - mode==2 ? ".sig" : ".gpg"); + mode==2 ? ".sig" : + mode==3 ? ".rev" : ".gpg"); buf = xmalloc (strlen(iname)+4+1); strcpy (buf, iname); @@ -258,6 +260,7 @@ open_outfile (int inp_fd, const char *iname, int mode, int restrictedperm, buf = xstrconcat (iname, (mode==1 ? EXTSEP_S "asc" : mode==2 ? EXTSEP_S "sig" : + mode==3 ? EXTSEP_S "rev" : /* */ EXTSEP_S GPGEXT_GPG), NULL); } @@ -451,3 +454,24 @@ try_make_homedir (const char *fname) copy_options_file( 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/passphrase.c b/g10/passphrase.c index 280d8a9b5..9d3f497ba 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -633,7 +633,8 @@ emit_status_need_passphrase (u32 *keyid, u32 *mainkeyid, int pubkey_algo) /* Return an allocated utf-8 string describing the key PK. If ESCAPED is true spaces and control characters are percent or plus escaped. - MODE 0 is for the common prompt, MODE 1 for the import prompt. */ + MODE describes the use of the key description; use one of the + FORMAT_KEYDESC_ macros. */ char * gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped) { diff --git a/g10/revoke.c b/g10/revoke.c index 069453ebc..bf5e33b3f 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -436,12 +436,14 @@ gen_desig_revoke( const char *uname, strlist_t locusr ) 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 a minimal key and not - just the naked revocation signature; it may be NULL. */ + 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 (const char *filename, struct revocation_reason_info *reason, PKT_public_key *psk, - kbnode_t keyblock) + kbnode_t keyblock, + const char *leadintext, int suffix) { int rc; iobuf_t out = NULL; @@ -451,9 +453,12 @@ create_revocation (const char *filename, afx = new_armor_context (); - if ((rc = open_outfile (-1, filename, 0, 1, &out))) + if ((rc = open_outfile (-1, filename, suffix, 1, &out))) 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); @@ -502,6 +507,81 @@ create_revocation (const char *filename, } +/* 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. */ +int +gen_standard_revoke (PKT_public_key *psk) +{ + int rc; + estream_t memfp; + struct revocation_reason_info reason; + char *dir, *tmpstr, *fname; + void *leadin; + size_t len; + u32 keyid[2]; + char pkstrbuf[PUBKEY_STRING_SIZE]; + char *orig_codeset; + + dir = get_openpgp_revocdir (opt.homedir); + tmpstr = hexfingerprint (psk); + 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:")); + + es_fprintf (memfp, "pub %s/%s %s\n", + pubkey_string (psk, pkstrbuf, sizeof pkstrbuf), + keystr (keyid), + datestr_from_pk (psk)); + + print_fingerprint (memfp, psk, 3); + + tmpstr = get_user_id (keyid, &len); + es_fprintf (memfp, "uid%*s%.*s\n\n", + (int)keystrlen () + 10, "", + (int)len, tmpstr); + xfree (tmpstr); + + es_fprintf (memfp, "%s\n\n%s\n\n:", + _("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."), + _("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 making use of 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 (fname, &reason, psk, NULL, leadin, 3); + xfree (leadin); + xfree (fname); + + return rc; +} + + + /**************** * Generate a revocation certificate for UNAME */ @@ -582,7 +662,7 @@ gen_revoke (const char *uname) if (!opt.armor) tty_printf (_("ASCII armored output forced.\n")); - rc = create_revocation (NULL, reason, psk, keyblock); + rc = create_revocation (NULL, reason, psk, keyblock, NULL, 0); if (rc) goto leave; diff --git a/tests/openpgp/Makefile.am b/tests/openpgp/Makefile.am index ea1d54f8b..4b1c219f9 100644 --- a/tests/openpgp/Makefile.am +++ b/tests/openpgp/Makefile.am @@ -77,7 +77,7 @@ CLEANFILES = prepared.stamp x y yy z out err $(data_files) \ gnupg-test.stop pubring.gpg~ random_seed gpg-agent.log clean-local: - -rm -rf private-keys-v1.d + -rm -rf private-keys-v1.d openpgp-revocs.d # We need to depend on a couple of programs so that the tests don't