gpg: Auto-create revocation certificates.
authorWerner Koch <wk@gnupg.org>
Wed, 25 Jun 2014 18:25:28 +0000 (20:25 +0200)
committerWerner Koch <wk@gnupg.org>
Mon, 30 Jun 2014 14:40:55 +0000 (16:40 +0200)
* configure.ac (GNUPG_OPENPGP_REVOC_DIR): New config define.
* g10/revoke.c (create_revocation): Add arg "leadin".
(gen_standard_revoke): New.
* g10/openfile.c (get_openpgp_revocdir): New.
(open_outfile): Add MODE value 3.
* g10/keyid.c (hexfingerprint): New.
* g10/keygen.c (do_generate_keypair): Call gen_standard_revoke.
--

GnuPG-bug-id: 1042

configure.ac
doc/gpg.texi
g10/keydb.h
g10/keygen.c
g10/keyid.c
g10/main.h
g10/openfile.c
g10/passphrase.c
g10/revoke.c
tests/openpgp/Makefile.am

index e26e51b..02e02bb 100644 (file)
@@ -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
index 9c52282..5efc16e 100644 (file)
@@ -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.
 
index b21d955..0cf6ca1 100644 (file)
@@ -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);
 
index 35c1460..4509231 100644 (file)
@@ -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") );
index 9c94bd6..6ce6f32 100644 (file)
@@ -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;
+}
 
 
 \f
index ae0bc8c..e75f616 100644 (file)
@@ -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 );
index 901387d..5a43648 100644 (file)
@@ -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;
+}
index 280d8a9..9d3f497 100644 (file)
@@ -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)
 {
index 069453e..bf5e33b 100644 (file)
@@ -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;
 
index ea1d54f..4b1c219 100644 (file)
@@ -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