Fix detection of invalid signer keys.
authorWerner Koch <wk@gnupg.org>
Thu, 6 Aug 2009 17:17:18 +0000 (17:17 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 6 Aug 2009 17:17:18 +0000 (17:17 +0000)
Support the new INV_SGNR status code.

13 files changed:
NEWS
doc/ChangeLog
doc/gpgme.texi
doc/uiserver.texi
src/ChangeLog
src/engine-gpgsm.c
src/gpgme.h.in
src/op-support.c
src/sign.c
tests/ChangeLog
tests/Makefile.am
tests/gpg/t-sign.c
tests/run-sign.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index ef5654d28674868bba89a87b30c6b8a7a03238e6..0fc99f54377b2364d69a50f9c09986550930a549 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,12 @@ Noteworthy changes in version 1.2.1 (unreleased)
 
  * (none yet)
 
+ * Interface changes relative to the 1.1.7 release:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ GPGME_STATUS_INV_SGNR    NEW.
+ GPGME_STATUS_NO_SGNR     NEW.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 Noteworthy changes in version 1.2.0 (2009-06-18)
 ------------------------------------------------
 
index 5772e6824c48e07570a673d7f12f6ce9b6992763..34f30bd0158302ed09cb08281dec4effbf60166a 100644 (file)
@@ -1,3 +1,8 @@
+2009-07-21  Werner Koch  <wk@g10code.com>
+
+       * uiserver.texi (UI Server Encrypt): Add --expect-sign option to
+       PREP_ENCRYPT.
+
 2009-06-16  Marcus Brinkmann  <marcus@g10code.de>
 
        * gpgme.texi (Result Management): New section.
index a0bc20e8899bdae323948d3bd492f030122514e5..3b1c10fa84d0fd1aefbbc2df2602564eedfc7b42 100644 (file)
@@ -4777,7 +4777,7 @@ of a @code{gpgme_op_sign} operation.  The pointer is only valid if the
 last operation on the context was a @code{gpgme_op_sign},
 @code{gpgme_op_sign_start}, @code{gpgme_op_encrypt_sign} or
 @code{gpgme_op_encrypt_sign_start} operation.  If that operation
-failed, the function might return a @code{NULL} pointer, The returned
+failed, the function might return a @code{NULL} pointer. The returned
 pointer is only valid until the next operation is started on the
 context.
 @end deftypefun
index 7c0a1e9a4886f5e7ac8594ac807e6e2445255ee8..c6da5ad421125727a950d6ddf224f69ed39e3d16 100644 (file)
@@ -118,7 +118,7 @@ Use the CMS (PKCS#7) protocol (RFC-3852).
 To support automagically selection of the protocol depending on the
 selected keys, the server MAY implement the command:
 
-@deffn Command PREP_ENCRYPT [-@w{}-protocol=@var{name}]
+@deffn Command PREP_ENCRYPT [-@w{}-protocol=@var{name}] [-@w{}-expect-sign]
 
 This commands considers all recipients set so far and decides whether it
 is able to take input and start the actual decryption.  This is kind of
@@ -129,8 +129,15 @@ command is send.  The @option{--protocol} option is optional; if it is
 not given, the server should allow the user to select the protocol to be
 used based on the recipients given or by any other means.
 
+If @option{--expect-sign} is given the server should expect that the
+message will also be signed and use this hint to present a unified
+recipient and signer selection dialog if possible and desired.  A
+selected signer should then be cached for the expected SIGN command
+(which is expected in the same session but possible on another
+connection).
+
 If this command is given again before a successful @command{ENCRYPT}
-command, the second one takes effect. 
+command, the second one takes effect.
 
 Before sending the OK response the server shall tell the client the
 protocol to be used (either the one given by the argument or the one
index feebbdc961524b9fe4aae51bb7ef720d7b1191bc..330194988cd12090e47dc64c71ce48d2531c393a 100644 (file)
@@ -1,3 +1,16 @@
+2009-08-06  Werner Koch  <wk@g10code.com>
+
+       * op-support.c (_gpgme_parse_inv_recp): Allow for no fingerprint.
+
+       * engine-gpgsm.c (gpgsm_sign): Hook up the status func for the
+       SIGNER command.
+       * gpgme.h.in (GPGME_STATUS_INV_SGNR, GPGME_STATUS_NO_SGNR): New.
+       * sign.c (op_data_t): Add fields IGNORE_INV_RECP and INV_SGNR_SEEN.
+       (_gpgme_op_sign_init_result): Factor code out to ...
+       (sign_init_result): .. new.  Init new fields.
+       (sign_start): Use sign_init_result.
+       (_gpgme_sign_status_handler): Take care of the new INV_SGNR.
+
 2009-07-07  Werner Koch  <wk@g10code.com>
 
        * engine-gpgsm.c (struct engine_gpgsm): Add fields
index 4067b99b5a7dbb2ae21c0aef9b88a3aedd353e79..647fd93175e091b9a8e2f283f11452f55122df1a 100644 (file)
@@ -1870,7 +1870,7 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
       if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
        return gpg_error_from_errno (errno);
       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
-                                        NULL, NULL);
+                                         NULL, NULL);
       free (assuan_cmd);
       if (err)
        return err;
@@ -1885,7 +1885,8 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
 
           strcpy (stpcpy (buf, "SIGNER "), s);
           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
-                                             NULL, NULL);
+                                             gpgsm->status.fnc,
+                                             gpgsm->status.fnc_value);
        }
       else
         err = gpg_error (GPG_ERR_INV_VALUE);
index b9f76f56af58d070ca1c419245338ba8912236e8..0b42798cd80c6e5973330bc6ea6f7fb9065a4234 100644 (file)
@@ -485,7 +485,9 @@ typedef enum
     GPGME_STATUS_PKA_TRUST_BAD = 79,
     GPGME_STATUS_PKA_TRUST_GOOD = 80,
 
-    GPGME_STATUS_PLAINTEXT = 81
+    GPGME_STATUS_PLAINTEXT = 81,
+    GPGME_STATUS_INV_SGNR = 82,
+    GPGME_STATUS_NO_SGNR = 83
   }
 gpgme_status_code_t;
 
index c3ba7785bd2e15ef4510580728962d6b407c93e3..90e1283e2f30b6e1f76a85f81dac3f4081fb9ac4 100644 (file)
@@ -162,8 +162,8 @@ _gpgme_op_reset (gpgme_ctx_t ctx, int type)
 }
 
 \f
-/* Parse the INV_RECP status line in ARGS and return the result in
-   KEY.  */
+/* Parse the INV_RECP or INV-SNDR status line in ARGS and return the
+   result in KEY.  */
 gpgme_error_t
 _gpgme_parse_inv_recp (char *args, gpgme_invalid_key_t *key)
 {
@@ -177,7 +177,7 @@ _gpgme_parse_inv_recp (char *args, gpgme_invalid_key_t *key)
   inv_key->next = NULL;
   errno = 0;
   reason = strtol (args, &tail, 0);
-  if (errno || args == tail || *tail != ' ')
+  if (errno || args == tail || (*tail && *tail != ' '))
     {
       /* The crypto backend does not behave.  */
       free (inv_key);
@@ -236,7 +236,7 @@ _gpgme_parse_inv_recp (char *args, gpgme_invalid_key_t *key)
       break;
     }
 
-  while (*tail == ' ')
+  while (*tail && *tail == ' ')
     tail++;
   if (*tail)
     {
index 03007bdf8b183c92bbb2ba7367157d5ec3170791..1d3716fdf5b03a8b2596669309ab5fe5ad44cde0 100644 (file)
@@ -46,6 +46,10 @@ typedef struct
 
   /* Likewise for signature information.  */
   gpgme_new_signature_t *last_sig_p;
+
+  /* Flags used while processing the status lines.  */
+  unsigned int ignore_inv_recp:1;
+  unsigned int inv_sgnr_seen:1;
 } *op_data_t;
 
 
@@ -266,6 +270,12 @@ _gpgme_sign_status_handler (void *priv, gpgme_status_code_t code, char *args)
       break;
 
     case GPGME_STATUS_INV_RECP:
+      if (opd->inv_sgnr_seen && opd->ignore_inv_recp)
+        break; 
+      /* FALLTROUGH */
+    case GPGME_STATUS_INV_SGNR:
+      if (code == GPGME_STATUS_INV_SGNR)
+        opd->inv_sgnr_seen = 1;
       err = _gpgme_parse_inv_recp (args, opd->last_signer_p);
       if (err)
        return err;
@@ -297,8 +307,8 @@ sign_status_handler (void *priv, gpgme_status_code_t code, char *args)
 }
 
 
-gpgme_error_t
-_gpgme_op_sign_init_result (gpgme_ctx_t ctx)
+static gpgme_error_t
+sign_init_result (gpgme_ctx_t ctx, int ignore_inv_recp)
 {
   gpgme_error_t err;
   void *hook;
@@ -311,9 +321,17 @@ _gpgme_op_sign_init_result (gpgme_ctx_t ctx)
     return err;
   opd->last_signer_p = &opd->result.invalid_signers;
   opd->last_sig_p = &opd->result.signatures;
+  opd->ignore_inv_recp = !!ignore_inv_recp;
+  opd->inv_sgnr_seen = 0;
   return 0;
 }
 
+gpgme_error_t
+_gpgme_op_sign_init_result (gpgme_ctx_t ctx)
+{
+  return sign_init_result (ctx, 0);
+}
+
 
 static gpgme_error_t
 sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t plain,
@@ -325,7 +343,10 @@ sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t plain,
   if (err)
     return err;
 
-  err = _gpgme_op_sign_init_result (ctx);
+  /* If we are using the CMS protocol, we ignore the INV_RECP status
+     code if a newer GPGSM is in use.  GPGMS does not support combined
+     sign+encrypt and thus this can't harm.  */
+  err = sign_init_result (ctx, (ctx->protocol == GPGME_PROTOCOL_CMS));
   if (err)
     return err;
 
index 926771abb9fc6816edbeec00e8c9c366519b455e..e6b53d7919097ce0edc0fdd07c4f2dfc984b125b 100644 (file)
@@ -1,3 +1,7 @@
+2009-08-06  Werner Koch  <wk@g10code.com>
+
+       * run-sign.c: New.
+
 2009-07-07  Werner Koch  <wk@g10code.com>
 
        * run-keylist.c (main):  Add options --cms and --openpgp.  
index 81d7e548900573a92caafa90610ae574ec6e0a22..3e7b414bdb497c4e4cc1ea0dfed29d76598e9252 100644 (file)
@@ -32,7 +32,7 @@ LDADD = ../src/libgpgme.la @GPG_ERROR_LIBS@
 
 noinst_HEADERS = run-support.h
 
-noinst_PROGRAMS = $(TESTS) run-keylist run-export run-import
+noinst_PROGRAMS = $(TESTS) run-keylist run-export run-import run-sign
 
 
 if RUN_GPG_TESTS
index 3b71dc48b415fe65c6fd10471e84dfc39ce7924a..bdd3323b7856bf54ddd930c6c6f30aa56917ae70 100644 (file)
@@ -102,6 +102,17 @@ main (int argc, char **argv)
 
   gpgme_set_textmode (ctx, 1);
   gpgme_set_armor (ctx, 1);
+  
+#if 0
+  {
+    gpgme_key_t akey;
+    err = gpgme_get_key (ctx, "0x68697734", &akey, 0);
+    fail_if_err (err);
+    err = gpgme_signers_add (ctx, akey);
+    fail_if_err (err);
+    gpgme_key_unref (akey);
+  }
+#endif
 
   err = gpgme_data_new_from_mem (&in, "Hallo Leute\n", 12, 0);
   fail_if_err (err);
diff --git a/tests/run-sign.c b/tests/run-sign.c
new file mode 100644 (file)
index 0000000..7c3d51d
--- /dev/null
@@ -0,0 +1,187 @@
+/* run-sign.c  - Helper to perform a sign operation
+   Copyright (C) 2009 g10 Code GmbH
+
+   This file is part of GPGME.
+   GPGME is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+   
+   GPGME 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
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* We need to include config.h so that we know whether we are building
+   with large file system (LFS) support. */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <gpgme.h>
+
+#define PGM "run-sign"
+
+#include "run-support.h"
+
+
+static int verbose;
+
+
+static void
+print_result (gpgme_sign_result_t result, gpgme_sig_mode_t type)
+{
+  gpgme_invalid_key_t invkey;
+  gpgme_new_signature_t sig;
+
+  for (invkey = result->invalid_signers; invkey; invkey = invkey->next)
+    printf ("Signing key `%s' not used: %s <%s>\n", 
+            nonnull (invkey->fpr), 
+            gpg_strerror (invkey->reason), gpg_strsource (invkey->reason));
+  
+  for (sig = result->signatures; sig; sig = sig->next)
+    {
+      printf ("Key fingerprint: %s\n", nonnull (sig->fpr));
+      printf ("Signature type : %d\n", sig->type);
+      printf ("Public key algo: %d\n", sig->pubkey_algo);
+      printf ("Hash algo .....: %d\n", sig->hash_algo);
+      printf ("Creation time .: %ld\n", sig->timestamp);
+      printf ("Sig class .....: 0x%u\n", sig->sig_class);
+    }
+}
+
+
+
+static int
+show_usage (int ex)
+{
+  fputs ("usage: " PGM " [options] FILE\n\n"
+         "Options:\n"
+         "  --verbose        run in verbose mode\n"
+         "  --openpgp        use the OpenPGP protocol (default)\n"
+         "  --cms            use the CMS protocol\n"
+         "  --key NAME       use key NAME for signing\n"
+         , stderr);
+  exit (ex);
+}
+
+
+int 
+main (int argc, char **argv)
+{
+  int last_argc = -1;
+  gpgme_error_t err;
+  gpgme_ctx_t ctx;
+  const char *key_string = NULL;
+  gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP;
+  gpgme_sig_mode_t sigmode = GPGME_SIG_MODE_NORMAL;
+  gpgme_data_t in, out;
+  gpgme_sign_result_t result;
+
+  if (argc)
+    { argc--; argv++; }
+
+  while (argc && last_argc != argc )
+    {
+      last_argc = argc;
+      if (!strcmp (*argv, "--"))
+        {
+          argc--; argv++;
+          break;
+        }
+      else if (!strcmp (*argv, "--help"))
+        show_usage (0);
+      else if (!strcmp (*argv, "--verbose"))
+        {
+          verbose = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--openpgp"))
+        {
+          protocol = GPGME_PROTOCOL_OpenPGP;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--cms"))
+        {
+          protocol = GPGME_PROTOCOL_CMS;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--key"))
+        {
+          argc--; argv++;
+          if (!argc)
+            show_usage (1);
+          key_string = *argv;
+          argc--; argv++;
+        }
+      else if (!strncmp (*argv, "--", 2))
+        show_usage (1);
+      
+    }          
+  if (argc != 1)
+    show_usage (1);
+
+  init_gpgme (protocol);
+
+  err = gpgme_new (&ctx);
+  fail_if_err (err);
+  gpgme_set_protocol (ctx, protocol);
+  gpgme_set_armor (ctx, 1);
+
+  if (key_string)
+    {
+      gpgme_key_t akey;
+
+      err = gpgme_get_key (ctx, key_string, &akey, 1);
+      if (err)
+        {
+          fprintf (stderr, PGM ": error getting key `%s': %s\n",
+                   key_string, gpg_strerror (err));
+          exit (1);
+        }
+      err = gpgme_signers_add (ctx, akey);
+      fail_if_err (err);
+      gpgme_key_unref (akey);
+    }
+
+  err = gpgme_data_new_from_file (&in, *argv, 1);
+  if (err)
+    {
+      fprintf (stderr, PGM ": error reading `%s': %s\n",
+               *argv, gpg_strerror (err));
+      exit (1);
+    }
+
+  err = gpgme_data_new (&out);
+  fail_if_err (err);
+
+  err = gpgme_op_sign (ctx, in, out, sigmode);
+  result = gpgme_op_sign_result (ctx);
+  if (result)
+    print_result (result, sigmode);
+  if (err)
+    {
+      fprintf (stderr, PGM ": signing failed: %s\n", gpg_strerror (err));
+      exit (1);
+    }
+
+  fputs ("Begin Output:\n", stdout);
+  print_data (out);
+  fputs ("End Output.\n", stdout);
+  gpgme_data_release (out);
+
+  gpgme_data_release (in);
+
+  gpgme_release (ctx);
+  return 0;
+}