dirmngr: Fix error handling.
[gnupg.git] / g10 / export.c
index e0699db..a7aecd6 100644 (file)
@@ -16,7 +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, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "status.h"
+#include "../common/status.h"
 #include "keydb.h"
-#include "util.h"
+#include "../common/util.h"
 #include "main.h"
-#include "i18n.h"
-#include "membuf.h"
-#include "host2net.h"
-#include "zb32.h"
-#include "recsel.h"
-#include "mbox-util.h"
-#include "init.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"
 
@@ -62,11 +62,13 @@ struct export_stats_s
 
 /* 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;
 
 
 
@@ -88,6 +90,8 @@ cleanup_export_globals (void)
 {
   recsel_release (export_keep_uid);
   export_keep_uid = NULL;
+  recsel_release (export_drop_subkey);
+  export_drop_subkey = NULL;
 }
 
 
@@ -112,6 +116,10 @@ parse_export_options(char *str,unsigned int *options,int noisy)
       {"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},
@@ -123,8 +131,18 @@ parse_export_options(char *str,unsigned int *options,int noisy)
       {NULL,0,NULL,NULL}
       /* add tags for include revoked and disabled? */
     };
+  int rc;
 
-  return parse_options(str,options,export_opts,noisy);
+  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;
 }
 
 
@@ -142,6 +160,14 @@ parse_export_options(char *str,unsigned int *options,int noisy)
  *                - 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)
@@ -153,6 +179,8 @@ parse_and_set_export_filter (const char *string)
 
   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);
 
@@ -219,16 +247,17 @@ export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
 /*
  * Export secret keys (to stdout or to --output FILE).
  *
- * Depending on opt.armor the output is armored.  If USERS is NULL,
- * all secret keys will be exported.  STATS is either an export stats
- * object for update or NULL.
+ * 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, export_stats_t stats)
+export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options,
+                export_stats_t stats)
 {
-  return do_export (ctrl, users, 1, 0, stats);
+  return do_export (ctrl, users, 1, options, stats);
 }
 
 
@@ -236,16 +265,18 @@ export_seckeys (ctrl_t ctrl, strlist_t users, export_stats_t 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.  If
- * USERS is NULL, all secret subkeys will be exported.  STATS is
- * either an export stats object for update or NULL.
+ * 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, export_stats_t stats)
+export_secsubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
+                   export_stats_t stats)
 {
-  return do_export (ctrl, users, 2, 0, stats);
+  return do_export (ctrl, users, 2, options, stats);
 }
 
 
@@ -994,7 +1025,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
           err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
           goto leave;
         }
-      /* Put the curve's OID into into the MPI array.  This requires
+      /* 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)
         {
@@ -1130,9 +1161,9 @@ print_status_exported (PKT_public_key *pk)
 /*
  * Receive a secret key from agent specified by HEXGRIP.
  *
- * Since the key data from agant is encrypted, decrypt it by CIPHERHD.
- * Then, parse the decrypted key data in transfer format, and put
- * secret parameters into PK.
+ * 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
@@ -1329,6 +1360,38 @@ apply_keep_uid_filter (kbnode_t keyblock, recsel_expr_t selector)
 }
 
 
+/*
+ * 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 (kbnode_t keyblock, recsel_expr_t selector)
+{
+  kbnode_t node;
+
+  for (node = keyblock->next; node; node = node->next )
+    {
+      if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+          || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+        {
+          if (recsel_select (selector, impex_filter_getval, node))
+            {
+              /*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. */
@@ -1371,7 +1434,7 @@ print_pka_or_dane_records (iobuf_t out, kbnode_t keyblock, PKT_public_key *pk,
         continue;
       uid = node->pkt->pkt.user_id;
 
-      if (uid->is_expired || uid->is_revoked)
+      if (uid->flags.expired || uid->flags.revoked)
         continue;
 
       xfree (mbox);
@@ -1472,6 +1535,7 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
   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)
@@ -1488,8 +1552,9 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
       if (node->pkt->pkttype == PKT_COMMENT)
         continue;
 
-      /* Make sure that ring_trust packets never get exported. */
-      if (node->pkt->pkttype == PKT_RING_TRUST)
+      /* Make sure that ring_trust packets are only exported in backup
+       * mode. */
+      if (node->pkt->pkttype == PKT_RING_TRUST && !(options & EXPORT_BACKUP))
         continue;
 
       /* If exact is set, then we only export what was requested
@@ -1907,11 +1972,9 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
         }
 
       /* Always do the cleaning on the public key part if requested.
-         Note that we don't yet set this option if we are exporting
-         secret keys.  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.  */
+       * 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 (keyblock, opt.verbose, (options&EXPORT_MINIMAL), NULL, NULL);
 
@@ -1922,6 +1985,13 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
           commit_kbnode (&keyblock);
         }
 
+      if (export_drop_subkey)
+        {
+          commit_kbnode (&keyblock);
+          apply_drop_subkey_filter (keyblock, export_drop_subkey);
+          commit_kbnode (&keyblock);
+        }
+
       /* And write it. */
       err = do_export_one_keyblock (ctrl, keyblock, keyid,
                                     out_help? out_help : out,
@@ -2139,6 +2209,48 @@ export_ssh_key (ctrl_t ctrl, const char *userid)
               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)