g10: Export cleartext keys as cleartext
[gnupg.git] / g10 / delkey.c
index efb9a50..966c571 100644 (file)
@@ -1,12 +1,13 @@
 /* delkey.c - delete keys
- * Copyright (C) 1998, 1999, 2000, 2001, 2002,
- *               2004 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004,
+ *               2005, 2006 Free Software Foundation, Inc.
+ * Copyright (C) 2014 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 2 of the License, or
+ * 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,
@@ -15,8 +16,7 @@
  * 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #include <ctype.h>
 
+#include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "errors.h"
+#include "status.h"
 #include "iobuf.h"
 #include "keydb.h"
-#include "memory.h"
 #include "util.h"
 #include "main.h"
 #include "trustdb.h"
@@ -40,6 +39,7 @@
 #include "ttyio.h"
 #include "status.h"
 #include "i18n.h"
+#include "call-agent.h"
 
 
 /****************
  * r_sec_avail will be set if a secret key is available and the public
  * key can't be deleted for that reason.
  */
-static int
-do_delete_key( const char *username, int secret, int *r_sec_avail )
+static gpg_error_t
+do_delete_key( const char *username, int secret, int force, int *r_sec_avail )
 {
-    int rc = 0;
-    KBNODE keyblock = NULL;
-    KBNODE node;
-    KEYDB_HANDLE hd = keydb_new (secret);
-    PKT_public_key *pk = NULL;
-    PKT_secret_key *sk = NULL;
-    u32 keyid[2];
-    int okay=0;
-    int yes;
-    KEYDB_SEARCH_DESC desc;
-    int exactmatch;
-
-    *r_sec_avail = 0;
-
-    /* search the userid */
-    classify_user_id (username, &desc);
-    exactmatch = (desc.mode == KEYDB_SEARCH_MODE_FPR
-                  || desc.mode == KEYDB_SEARCH_MODE_FPR16
-                  || desc.mode == KEYDB_SEARCH_MODE_FPR20);
-    rc = desc.mode? keydb_search (hd, &desc, 1):G10ERR_INV_USER_ID;
-    if (rc) {
-       log_error (_("key \"%s\" not found: %s\n"), username, g10_errstr (rc));
-       write_status_text( STATUS_DELETE_PROBLEM, "1" );
-       goto leave;
+  gpg_error_t err;
+  kbnode_t keyblock = NULL;
+  kbnode_t node, kbctx;
+  KEYDB_HANDLE hd;
+  PKT_public_key *pk = NULL;
+  u32 keyid[2];
+  int okay=0;
+  int yes;
+  KEYDB_SEARCH_DESC desc;
+  int exactmatch;
+
+  *r_sec_avail = 0;
+
+  hd = keydb_new ();
+  if (!hd)
+    return gpg_error_from_syserror ();
+
+  /* Search the userid.  */
+  err = classify_user_id (username, &desc, 1);
+  exactmatch = (desc.mode == KEYDB_SEARCH_MODE_FPR
+                || desc.mode == KEYDB_SEARCH_MODE_FPR16
+                || desc.mode == KEYDB_SEARCH_MODE_FPR20);
+  if (!err)
+    err = keydb_search (hd, &desc, 1, NULL);
+  if (err)
+    {
+      log_error (_("key \"%s\" not found: %s\n"), username, gpg_strerror (err));
+      write_status_text (STATUS_DELETE_PROBLEM, "1");
+      goto leave;
     }
 
-    /* read the keyblock */
-    rc = keydb_get_keyblock (hd, &keyblock );
-    if (rc) {
-       log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
-       goto leave;
+  /* Read the keyblock.  */
+  err = keydb_get_keyblock (hd, &keyblock);
+  if (err)
+    {
+      log_error (_("error reading keyblock: %s\n"), gpg_strerror (err) );
+      goto leave;
     }
 
-    /* get the keyid from the keyblock */
-    node = find_kbnode( keyblock, secret? PKT_SECRET_KEY:PKT_PUBLIC_KEY );
-    if( !node ) {
-       log_error("Oops; key not found anymore!\n");
-       rc = G10ERR_GENERAL;
-       goto leave;
+  /* Get the keyid from the keyblock.  */
+  node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
+  if (!node)
+    {
+      log_error ("Oops; key not found anymore!\n");
+      err = gpg_error (GPG_ERR_GENERAL);
+      goto leave;
     }
+  pk = node->pkt->pkt.public_key;
+  keyid_from_pk (pk, keyid);
 
-    if( secret ) {
-       sk = node->pkt->pkt.secret_key;
-       keyid_from_sk( sk, keyid );
+  if (!secret && !force)
+    {
+      if (have_secret_key_with_kid (keyid))
+        {
+          *r_sec_avail = 1;
+          err = gpg_error (GPG_ERR_EOF);
+          goto leave;
+        }
+      else
+        err = 0;
     }
-    else {
-       pk = node->pkt->pkt.public_key;
-       keyid_from_pk( pk, keyid );
-       rc = seckey_available( keyid );
-       if( !rc && !opt.expert ) {
-            *r_sec_avail = 1;
-            rc = -1;
-            goto leave;
-       }
-       else if( rc != G10ERR_NO_SECKEY ) {
-           log_error("%s: get secret key: %s\n", username, g10_errstr(rc) );
-       }
-       else
-           rc = 0;
+
+  if (secret && !have_secret_key_with_kid (keyid))
+    {
+      err = gpg_error (GPG_ERR_NOT_FOUND);
+      log_error (_("key \"%s\" not found\n"), username);
+      write_status_text (STATUS_DELETE_PROBLEM, "1");
+      goto leave;
     }
 
-    if( rc )
-       rc = 0;
-    else if (opt.batch && exactmatch)
-        okay++;
-    else if( opt.batch && secret )
-      {
-       log_error(_("can't do that in batchmode\n"));
-        log_info (_("(unless you specify the key by fingerprint)\n"));
-      }
-    else if( opt.batch && opt.answer_yes )
-       okay++;
-    else if( opt.batch )
-      {
-       log_error(_("can't do that in batchmode without \"--yes\"\n"));
-        log_info (_("(unless you specify the key by fingerprint)\n"));
-      }
-    else {
-        if( secret )
-            print_seckey_info( sk );
-        else
-            print_pubkey_info(NULL, pk );
-       tty_printf( "\n" );
-
-       yes = cpr_get_answer_is_yes( secret? "delete_key.secret.okay"
-                                          : "delete_key.okay",
-                             _("Delete this key from the keyring? (y/N) "));
-       if( !cpr_enabled() && secret && yes ) {
-           /* I think it is not required to check a passphrase; if
-            * the user is so stupid as to let others access his secret keyring
-            * (and has no backup) - it is up him to read some very
-            * basic texts about security.
-            */
-           yes = cpr_get_answer_is_yes("delete_key.secret.okay",
-                        _("This is a secret key! - really delete? (y/N) "));
+
+  if (opt.batch && exactmatch)
+    okay++;
+  else if (opt.batch && secret)
+    {
+      log_error(_("can't do this in batch mode\n"));
+      log_info (_("(unless you specify the key by fingerprint)\n"));
+    }
+  else if (opt.batch && opt.answer_yes)
+    okay++;
+  else if (opt.batch)
+    {
+      log_error(_("can't do this in batch mode without \"--yes\"\n"));
+      log_info (_("(unless you specify the key by fingerprint)\n"));
+    }
+  else
+    {
+      if (secret)
+        print_seckey_info (pk);
+      else
+        print_pubkey_info (NULL, pk );
+      tty_printf( "\n" );
+
+      yes = cpr_get_answer_is_yes
+        (secret? "delete_key.secret.okay": "delete_key.okay",
+         _("Delete this key from the keyring? (y/N) "));
+
+      if (!cpr_enabled() && secret && yes)
+        {
+          /* I think it is not required to check a passphrase; if the
+           * user is so stupid as to let others access his secret
+           * keyring (and has no backup) - it is up him to read some
+           * very basic texts about security.  */
+          yes = cpr_get_answer_is_yes
+            ("delete_key.secret.okay",
+             _("This is a secret key! - really delete? (y/N) "));
        }
-       if( yes )
-           okay++;
+
+      if (yes)
+        okay++;
     }
 
 
-    if( okay ) {
-       rc = keydb_delete_keyblock (hd);
-       if (rc) {
-           log_error (_("deleting keyblock failed: %s\n"), g10_errstr(rc) );
-           goto leave;
+  if (okay)
+    {
+      if (secret)
+       {
+          char *prompt;
+          gpg_error_t firsterr = 0;
+          char *hexgrip;
+
+          setup_main_keyids (keyblock);
+          for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); )
+            {
+              if (!(node->pkt->pkttype == PKT_PUBLIC_KEY
+                    || node->pkt->pkttype == PKT_PUBLIC_SUBKEY))
+                continue;
+
+              if (agent_probe_secret_key (NULL, node->pkt->pkt.public_key))
+                continue;  /* No secret key for that public (sub)key.  */
+
+              prompt = gpg_format_keydesc (node->pkt->pkt.public_key,
+                                           FORMAT_KEYDESC_DELKEY, 1);
+              err = hexkeygrip_from_pk (node->pkt->pkt.public_key, &hexgrip);
+              /* NB: We require --yes to advise the agent not to
+               * request a confirmation.  The rationale for this extra
+               * pre-caution is that since 2.1 the secret key may also
+               * be used for other protocols and thus deleting it from
+               * the gpg would also delete the key for other tools. */
+              if (!err)
+                err = agent_delete_key (NULL, hexgrip, prompt,
+                                        opt.answer_yes);
+              xfree (prompt);
+              xfree (hexgrip);
+              if (err)
+                {
+                  if (gpg_err_code (err) == GPG_ERR_KEY_ON_CARD)
+                    write_status_text (STATUS_DELETE_PROBLEM, "1");
+                  log_error (_("deleting secret %s failed: %s\n"),
+                             (node->pkt->pkttype == PKT_PUBLIC_KEY
+                              ? _("key"):_("subkey")),
+                             gpg_strerror (err));
+                  if (!firsterr)
+                    firsterr = err;
+                  if (gpg_err_code (err) == GPG_ERR_CANCELED
+                      || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
+                   {
+                     write_status_error ("delete_key.secret", err);
+                     break;
+                   }
+                }
+
+            }
+
+          err = firsterr;
+          if (firsterr)
+            goto leave;
+       }
+      else
+       {
+         err = keydb_delete_keyblock (hd);
+         if (err)
+            {
+              log_error (_("deleting keyblock failed: %s\n"),
+                         gpg_strerror (err));
+              goto leave;
+            }
        }
 
-       /* Note that the ownertrust being cleared will trigger a
-           revalidation_mark().  This makes sense - only deleting keys
-           that have ownertrust set should trigger this. */
+      /* Note that the ownertrust being cleared will trigger a
+        revalidation_mark().  This makes sense - only deleting keys
+        that have ownertrust set should trigger this. */
 
-        if (!secret && pk && clear_ownertrusts (pk)) {
+      if (!secret && pk && clear_ownertrusts (pk))
+        {
           if (opt.verbose)
             log_info (_("ownertrust information cleared\n"));
         }
     }
 
 leave:
-    keydb_release (hd);
-    release_kbnode (keyblock);
-    return rc;
+ leave:
+  keydb_release (hd);
+  release_kbnode (keyblock);
+  return err;
 }
 
 /****************
  * Delete a public or secret key from a keyring.
  */
-int
-delete_keys( STRLIST names, int secret, int allow_both )
+gpg_error_t
+delete_keys (strlist_t names, int secret, int allow_both)
 {
-    int rc, avail;
-
-    for(;names;names=names->next) {
-       rc = do_delete_key (names->d, secret, &avail );
-       if ( rc && avail ) { 
-        if ( allow_both ) {
-          rc = do_delete_key (names->d, 1, &avail );
-          if ( !rc )
-            rc = do_delete_key (names->d, 0, &avail );
-        }
-        else {
-          log_error(_(
-             "there is a secret key for public key \"%s\"!\n"),names->d);
-          log_info(_(
-             "use option \"--delete-secret-keys\" to delete it first.\n"));
-          write_status_text( STATUS_DELETE_PROBLEM, "2" );
-          return rc;
-        }
-       }
-
-       if(rc) {
-        log_error("%s: delete key failed: %s\n", names->d, g10_errstr(rc) );
-        return rc;
-       }
+  gpg_error_t err;
+  int avail;
+  int force = (!allow_both && !secret && opt.expert);
+
+  /* Force allows us to delete a public key even if a secret key
+     exists. */
+
+  for ( ;names ; names=names->next )
+    {
+      err = do_delete_key (names->d, secret, force, &avail);
+      if (err && avail)
+        {
+          if (allow_both)
+            {
+              err = do_delete_key (names->d, 1, 0, &avail);
+              if (!err)
+                err = do_delete_key (names->d, 0, 0, &avail);
+            }
+          else
+            {
+              log_error (_("there is a secret key for public key \"%s\"!\n"),
+                         names->d);
+              log_info(_("use option \"--delete-secret-keys\" to delete"
+                         " it first.\n"));
+              write_status_text (STATUS_DELETE_PROBLEM, "2");
+              return err;
+            }
+        }
+
+      if (err)
+        {
+          log_error ("%s: delete key failed: %s\n",
+                     names->d, gpg_strerror (err));
+          return err;
+        }
     }
 
-    return 0;
+  return 0;
 }