Added qualified signature features.
authorWerner Koch <wk@gnupg.org>
Sun, 13 Nov 2005 19:07:06 +0000 (19:07 +0000)
committerWerner Koch <wk@gnupg.org>
Sun, 13 Nov 2005 19:07:06 +0000 (19:07 +0000)
12 files changed:
sm/ChangeLog
sm/Makefile.am
sm/call-agent.c
sm/call-dirmngr.c
sm/certchain.c
sm/certcheck.c
sm/certdump.c
sm/gpgsm.h
sm/keylist.c
sm/qualified.c [new file with mode: 0644]
sm/sign.c
sm/verify.c

index aa9ac91..5c6c5eb 100644 (file)
@@ -1,8 +1,41 @@
+2005-11-13  Werner Koch  <wk@g10code.com>
+
+       * call-agent.c (gpgsm_agent_get_confirmation): New.
+
+       * keylist.c (list_cert_std): Print qualified status.
+       * qualified.c: New.
+       * certchain.c (gpgsm_validate_chain): Check for qualified
+       certificates.
+
+       * certchain.c (gpgsm_basic_cert_check): Release keydb handle when
+       no-chain-validation is used.
+
+2005-11-11  Werner Koch  <wk@g10code.com>
+
+       * keylist.c (print_capabilities): Print is_qualified status.
+
+2005-10-28  Werner Koch  <wk@g10code.com>
+
+       * certdump.c (pretty_print_sexp): New.
+       (gpgsm_print_name2): Use it here.  This allows proper printing of
+       DNS names as used with server certificates.
+
+2005-10-10  Werner Koch  <wk@g10code.com>
+
+       * keylist.c: Add pkaAdress OID as reference.
+
 2005-10-08  Marcus Brinkmann  <marcus@g10code.de>
 
        * Makefile.am (gpgsm_LDADD): Add ../gl/libgnu.a after
        ../common/libcommon.a.
 
+2005-09-13  Werner Koch  <wk@g10code.com>
+
+       * verify.c (gpgsm_verify): Print a note if the unknown algorithm
+       is MD2.
+       * sign.c (gpgsm_sign): Ditto.
+       * certcheck.c (gpgsm_check_cert_sig): Ditto.
+
 2005-09-08  Werner Koch  <wk@g10code.com>
 
        * export.c (popen_protect_tool): Add option --have-cert.  We
index 69a286e..aba2081 100644 (file)
@@ -49,7 +49,8 @@ gpgsm_SOURCES = \
        import.c \
        export.c \
        delete.c \
-       certreqgen.c 
+       certreqgen.c \
+       qualified.c
 
 
 gpgsm_LDADD = ../jnlib/libjnlib.a ../kbx/libkeybox.a   \
index c47f6b1..03a3a2e 100644 (file)
@@ -693,3 +693,24 @@ gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc)
   return map_assuan_err (rc);
 }
 
+
+\f
+/* Ask the agent to pop up a confirmation dialog with the text DESC
+   and an okay and cancel button.  */
+gpg_error_t
+gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc)
+{
+  int rc;
+  char *fpr;
+  char line[ASSUAN_LINELENGTH];
+
+  rc = start_agent (ctrl);
+  if (rc)
+    return rc;
+
+  snprintf (line, DIM(line)-1, "GET_CONFIRMATION %s", desc);
+  line[DIM(line)-1] = 0;
+
+  rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+  return map_assuan_err (rc);
+}
index ead117d..dd38a88 100644 (file)
@@ -276,7 +276,7 @@ inq_certificate (void *opaque, const char *line)
     {
       size_t n;
 
-      /* Send a certificate where a sourceKeyidentifier is included. */
+      /* Send a certificate where a sourceKeyIdentifier is included. */
       line += 12;
       while (*line == ' ')
         line++;
index 02e5b40..0c14fc8 100644 (file)
@@ -643,6 +643,9 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
   int any_no_crl = 0;
   int any_crl_too_old = 0;
   int any_no_policy_match = 0;
+  int is_qualified = -1; /* Indicates whether the certificate stems
+                            from a qualified root certificate.
+                            -1 = unknown, 0 = no, 1 = yes. */
   int lm = listmode;
 
   gnupg_get_isotime (current_time);
@@ -771,6 +774,53 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
           if (rc)
             goto leave;
 
+          
+          /* Set the flag for qualified signatures.  This flag is
+             deduced from a list of root certificates allowed for
+             qualified signatures. */
+          if (is_qualified == -1)
+            {
+              gpg_error_t err;
+              size_t buflen;
+              char buf[1];
+              
+              if (!ksba_cert_get_user_data (cert, "is_qualified", 
+                                            &buf, sizeof (buf),
+                                            &buflen) && buflen)
+                {
+                  /* We already checked this for this certificate,
+                     thus we simply take it from the user data. */
+                  is_qualified = !!*buf;
+                }    
+              else
+                {
+                  /* Need to consult the list of root certificates for
+                     qualified signatures. */
+                  err = gpgsm_is_in_qualified_list (ctrl, subject_cert);
+                  if (!err)
+                    is_qualified = 1;
+                  else if ( gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+                    is_qualified = 0;
+                  else
+                    log_error ("checking the list of qualified "
+                               "root certificates failed: %s\n",
+                               gpg_strerror (err));
+                  if ( is_qualified != -1 )
+                    {
+                      /* Cache the result but don't care toomuch about
+                         an error. */
+                      buf[0] = !!is_qualified;
+                      err = ksba_cert_set_user_data (subject_cert,
+                                                     "is_qualified", buf, 1);
+                      if (err)
+                        log_error ("set_user_data(is_qualified) failed: %s\n",
+                                   gpg_strerror (err)); 
+                    }
+                }
+            }
+
+
+          /* Check whether we really trust this root certificate. */
           rc = gpgsm_agent_istrusted (ctrl, subject_cert);
           if (!rc)
             ;
@@ -968,7 +1018,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
       keydb_search_reset (kh);
       subject_cert = issuer_cert;
       issuer_cert = NULL;
-    }
+    } /* End chain traversal. */
 
   if (!listmode)
     {
@@ -996,6 +1046,27 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
     }
   
  leave:
+  if (is_qualified != -1)
+    {
+      /* We figured something about the qualified signature capability
+         of the certificate under question.  Store the result as user
+         data in the certificate object.  We do this even if the
+         validation itself failed. */
+      /* Fixme: We should set this flag for all certificates in the
+         chain for optimizing reasons. */
+      char buf[1];
+      gpg_error_t err;
+
+      buf[0] = !!is_qualified;
+      err = ksba_cert_set_user_data (cert, "is_qualified", buf, 1);
+      if (err)
+        {
+          log_error ("set_user_data(is_qualified) failed: %s\n",
+                     gpg_strerror (err)); 
+          if (!rc)
+            rc = err;
+        }
+    }
   if (r_exptime)
     gnupg_copy_time (r_exptime, exptime);
   xfree (issuer);
@@ -1017,7 +1088,7 @@ gpgsm_basic_cert_check (ksba_cert_t cert)
   int rc = 0;
   char *issuer = NULL;
   char *subject = NULL;
-  KEYDB_HANDLE kh = keydb_new (0);
+  KEYDB_HANDLE kh;
   ksba_cert_t issuer_cert = NULL;
   
   if (opt.no_chain_validation)
@@ -1026,6 +1097,7 @@ gpgsm_basic_cert_check (ksba_cert_t cert)
       return 0;
     }
 
+  kh = keydb_new (0);
   if (!kh)
     {
       log_error (_("failed to allocated keyDB handle\n"));
index 84dfdb9..5fb3767 100644 (file)
@@ -168,6 +168,10 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
   if (!algo)
     {
       log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
+      if (algoid
+          && (  !strcmp (algoid, "1.2.840.113549.1.1.2")
+                ||!strcmp (algoid, "1.2.840.113549.2.2")))
+        log_info (_("(this is the MD2 algorithm)\n"));
       return gpg_error (GPG_ERR_GENERAL);
     }
   rc = gcry_md_open (&md, algo, 0);
index aae60e0..1f2ea7b 100644 (file)
@@ -484,6 +484,51 @@ print_dn_parts (FILE *fp, struct dn_array_s *dn, int translate)
 }
 
 
+/* Print the S-Expression in BUF, which has a valid length of BUFLEN,
+   as a human readable string in one line to FP. */
+static void
+pretty_print_sexp (FILE *fp, const unsigned char *buf, size_t buflen)
+{
+  size_t len;
+  gcry_sexp_t sexp;
+  char *result, *p;
+
+  if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
+    {
+      fputs (_("[Error - invalid encoding]"), fp);
+      return;
+    }
+  len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
+  assert (len);
+  result = xtrymalloc (len);
+  if (!result)
+    {
+      fputs (_("[Error - out of core]"), fp);
+      gcry_sexp_release (sexp);
+      return;
+    }
+  len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
+  assert (len);
+  for (p = result; len; len--, p++)
+    {
+      if (*p == '\n')
+        {
+          if (len > 1) /* Avoid printing the trailing LF. */
+            fputs ("\\n", fp);
+        }
+      else if (*p == '\r')
+        fputs ("\\r", fp);
+      else if (*p == '\v')
+        fputs ("\\v", fp);
+      else if (*p == '\t')
+        fputs ("\\t", fp);
+      else
+        putc (*p, fp);
+    }
+  xfree (result);
+  gcry_sexp_release (sexp);
+}
+
 
 void
 gpgsm_print_name2 (FILE *fp, const char *name, int translate)
@@ -507,7 +552,9 @@ gpgsm_print_name2 (FILE *fp, const char *name, int translate)
         }
     }
   else if (*s == '(')
-    fputs (_("[Error - unknown encoding]"), fp);
+    {
+      pretty_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
+    }
   else if (!((*s >= '0' && *s < '9')
              || (*s >= 'A' && *s <= 'Z')
              || (*s >= 'a' && *s <= 'z')))
@@ -576,7 +623,7 @@ format_name_writer (void *cookie, const char *buffer, size_t size)
 /* Format NAME which is expected to be in rfc2253 format into a better
    human readable format. Caller must free the returned string.  NULL
    is returned in case of an error.  With TRANSLATE set to true the
-   name will be translated to the native encodig.  Note that NAME is
+   name will be translated to the native encoding.  Note that NAME is
    internally always UTF-8 encoded. */
 char *
 gpgsm_format_name2 (const char *name, int translate)
@@ -658,7 +705,7 @@ gpgsm_format_keydesc (ksba_cert_t cert)
 
 
 #ifdef ENABLE_NLS
-  /* The Assuan agent protol requires us to transmit utf-8 strings */
+  /* The Assuan agent protocol requires us to transmit utf-8 strings */
   orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
 #ifdef HAVE_LANGINFO_CODESET
   if (!orig_codeset)
index 63d07a8..52eff35 100644 (file)
@@ -288,6 +288,10 @@ int gpgsm_decrypt (ctrl_t ctrl, int in_fd, FILE *out_fp);
 /*-- certreqgen.c --*/
 int gpgsm_genkey (ctrl_t ctrl, int in_fd, FILE *out_fp);
 
+/*-- qualified.c --*/
+gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert);
+gpg_error_t gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert);
+
 /*-- call-agent.c --*/
 int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc,
                         unsigned char *digest,
@@ -306,6 +310,7 @@ int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip);
 int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert);
 int gpgsm_agent_learn (ctrl_t ctrl);
 int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc);
+gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc);
 
 /*-- call-dirmngr.c --*/
 int gpgsm_dirmngr_isvalid (ctrl_t ctrl,
index 8a4eb3c..51a066d 100644 (file)
@@ -66,6 +66,9 @@ struct {
   { "1.3.6.1.5.5.7.3.11", "sbgpCertAAServerAuth" },
   { "1.3.6.1.5.5.7.3.13", "eapOverPPP" },
   { "1.3.6.1.5.5.7.3.14", "wlanSSID" },       
+
+  { "2.16.840.1.113730.4.1", "serverGatedCrypto.ns" }, /* Netscape. */
+  { "1.3.6.1.4.1.311.10.3.3", "serverGatedCrypto.ms"}, /* Microsoft. */
   { NULL, NULL }
 };
 
@@ -160,6 +163,9 @@ static struct {
   { "2.16.840.1.113730.1.12", "netscape-ssl-server-name" },
   { "2.16.840.1.113730.1.13", "netscape-comment" },
 
+  /* GnuPG extensions */
+  { "1.3.6.1.4.1.11591.2.1.1", "pkaAddress" },
+
   { NULL }
 };
 
@@ -207,6 +213,21 @@ print_capabilities (ksba_cert_t cert, FILE *fp)
 {
   gpg_error_t err;
   unsigned int use;
+  size_t buflen;
+  char buffer[1];
+
+  err = ksba_cert_get_user_data (cert, "is_qualified", 
+                                 &buffer, sizeof (buffer), &buflen);
+  if (!err && buflen)
+    {
+      if (*buffer)
+        putc ('q', fp);
+    }    
+  else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+    ; /* Don't know - will not get marked as 'q' */
+  else
+    log_debug ("get_user_data(is_qualified) failed: %s\n",
+               gpg_strerror (err)); 
 
   err = ksba_cert_get_key_usage (cert, &use);
   if (gpg_err_code (err) == GPG_ERR_NO_DATA)
@@ -1032,9 +1053,28 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
   fprintf (fp, "  fingerprint: %s\n", dn?dn:"error");
   xfree (dn);
 
+
+
   if (with_validation)
     {
+      gpg_error_t tmperr;
+      size_t buflen;
+      char buffer[1];
+      
       err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0);
+      tmperr = ksba_cert_get_user_data (cert, "is_qualified", 
+                                        &buffer, sizeof (buffer), &buflen);
+      if (!tmperr && buflen)
+        {
+          if (*buffer)
+            fputs ("  [qualified]\n", fp);
+        }    
+      else if (gpg_err_code (tmperr) == GPG_ERR_NOT_FOUND)
+        ; /* Don't know - will not get marked as 'q' */
+      else
+        log_debug ("get_user_data(is_qualified) failed: %s\n",
+                   gpg_strerror (tmperr)); 
+
       if (!err)
         fprintf (fp, "  [certificate is good]\n");
       else
diff --git a/sm/qualified.c b/sm/qualified.c
new file mode 100644 (file)
index 0000000..c4bd00f
--- /dev/null
@@ -0,0 +1,264 @@
+/* qualified.c - Routines related to qualified signatures
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ *
+ * 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
+ * (at your option) any later version.
+ *
+ * GnuPG 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 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
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#ifdef HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#endif
+
+#include "gpgsm.h"
+#include "i18n.h"
+#include <ksba.h>
+
+
+/* We open the file only once and keep the open file pointer as well
+   as the name of the file here.  Note that, a listname not equal to
+   NULL indicates that this module has been intialized and if the
+   LISTFP is also NULL, no list of qualified signatures exists. */
+static char *listname;
+static FILE *listfp;
+
+
+/* Read the trustlist and return entry by entry.  KEY must point to a
+   buffer of at least 41 characters. COUNTRY shall be a buffer of at
+   least 3 characters to receive the country code of that qualified
+   signature (i.e. "de" for German and "be" for Belgium).
+
+   Reading a valid entry returns 0, EOF is indicated by GPG_ERR_EOF
+   and any other error condition is indicated by the appropriate error
+   code. */
+static gpg_error_t
+read_list (char *key, char *country, int *lnr)
+{
+  gpg_error_t err;
+  int c, i, j;
+  char *p, line[256];
+
+  *key = 0;
+  *country = 0;
+
+  if (!listname)
+    {
+      listname = make_filename (GNUPG_DATADIR, "qualified.txt", NULL);
+      listfp = fopen (listname, "r");
+      if (!listfp && errno != ENOENT)
+        {
+          err = gpg_error_from_errno (errno);
+          log_error (_("can't open `%s': %s\n"), listname, gpg_strerror (err));
+          return err;
+        }
+    }
+
+  if (!listfp)
+    return gpg_error (GPG_ERR_EOF);
+
+  do
+    {
+      if (!fgets (line, DIM(line)-1, listfp) )
+        {
+          if (feof (listfp))
+            return gpg_error (GPG_ERR_EOF);
+          return gpg_error_from_errno (errno);
+        }
+
+      if (!*line || line[strlen(line)-1] != '\n')
+        {
+          /* Eat until end of line. */
+          while ( (c=getc (listfp)) != EOF && c != '\n')
+            ;
+          return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
+                                 : GPG_ERR_INCOMPLETE_LINE);
+        }
+      ++*lnr;
+      
+      /* Allow for empty lines and spaces */
+      for (p=line; spacep (p); p++)
+        ;
+    }
+  while (!*p || *p == '\n' || *p == '#');
+  
+  for (i=j=0; (p[i] == ':' || hexdigitp (p+i)) && j < 40; i++)
+    if ( p[i] != ':' )
+      key[j++] = p[i] >= 'a'? (p[i] & 0xdf): p[i];
+  key[j] = 0;
+  if (j != 40 || !(spacep (p+i) || p[i] == '\n'))
+    {
+      log_error (_("invalid formatted fingerprint in `%s', line %d\n"),
+                 listname, *lnr);
+      return gpg_error (GPG_ERR_BAD_DATA);
+    }
+  assert (p[i]);
+  i++;
+  while (spacep (p+i))
+    i++;
+  if ( p[i] >= 'a' && p[i] <= 'z' 
+       && p[i+1] >= 'a' && p[i+1] <= 'z' 
+       && (spacep (p+i+2) || p[i+2] == '\n'))
+    {
+      country[0] = p[i];
+      country[1] = p[i+1];
+      country[2] = 0;
+    }
+  else
+    {
+      log_error (_("invalid country code in `%s', line %d\n"), listname, *lnr);
+      return gpg_error (GPG_ERR_BAD_DATA);
+    }
+
+  return 0;
+}
+
+
+
+
+/* Check whether the certificate CERT is included in the list of
+   qualified certificates.  This list is similar to the "trustlist.txt"
+   as maintained by gpg-agent and includes fingerprints of root
+   certificates to be used for qualified (legally binding like
+   handwritten) signatures.  We keep this list system wide and not
+   per user because it is not a decision of the user. 
+
+   Returns: 0 if the certificate is included.  GPG_ERR_NOT_FOUND if it
+   is not in the liost or any other error (e.g. if no list of
+   qualified signatures is available. */
+gpg_error_t
+gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert)
+{
+  gpg_error_t err;
+  char *fpr;
+  char key[41];
+  char country[2];
+  int lnr = 0;
+
+  fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+  if (!fpr)
+    return gpg_error (GPG_ERR_GENERAL);
+
+  if (listfp)
+    rewind (listfp);
+  while (!(err = read_list (key, country, &lnr)))
+    {
+      if (!strcmp (key, fpr))
+        break;
+    }
+  if (gpg_err_code (err) == GPG_ERR_EOF)
+    err = gpg_error (GPG_ERR_NOT_FOUND);
+
+  xfree (fpr);
+  return err;
+}
+
+
+/* We know that CERT is a qualified certificate.  Ask the user for
+   consent to actually create a signature using this certificate.
+   Returns: 0 for yes, GPG_ERR_CANCEL for no or any otehr error
+   code. */
+gpg_error_t
+gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert)
+{
+  gpg_error_t err;
+  char *name, *subject, *buffer, *p;
+  const char *s;
+  char *orig_codeset = NULL;
+
+  name = ksba_cert_get_subject (cert, 0);
+  if (!name)
+    return gpg_error (GPG_ERR_GENERAL);
+  subject = gpgsm_format_name2 (name, 0);
+  ksba_free (name); name = NULL;
+
+#ifdef ENABLE_NLS
+  /* The Assuan agent protocol requires us to transmit utf-8 strings */
+  orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
+#ifdef HAVE_LANGINFO_CODESET
+  if (!orig_codeset)
+    orig_codeset = nl_langinfo (CODESET);
+#endif
+  if (orig_codeset)
+    { /* We only switch when we are able to restore the codeset later.
+         Note that bind_textdomain_codeset does only return on memory
+         errors but not if a codeset is not available.  Thus we don't
+         bother printing a diagnostic here. */
+      orig_codeset = xstrdup (orig_codeset);
+      if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
+        orig_codeset = NULL; 
+    }
+#endif
+
+  if (asprintf (&name,
+                _("You are about to create a signature using your "
+                  "certificate:\n"
+                  "\"%s\"\n"
+                  "This will create a qualified signature by law "
+                  "equated to a handwritten signature.\n\n"
+                  "Are you really sure that you want to do this?"),
+                subject? subject:"?"
+                ) < 0 )
+    err = gpg_error_from_errno (errno);
+  else
+    err = 0;
+
+#ifdef ENABLE_NLS
+  if (orig_codeset)
+    bind_textdomain_codeset (PACKAGE_GT, orig_codeset);
+#endif
+  xfree (orig_codeset);
+  xfree (subject);
+
+  if (err)
+    return err;
+
+  buffer = p = xtrymalloc (strlen (name) * 3 + 1);
+  if (!buffer)
+    {
+      err = gpg_error_from_errno (errno);
+      free (name);
+      return err;
+    }
+  for (s=name; *s; s++)
+    {
+      if (*s < ' ' || *s == '+')
+        {
+          sprintf (p, "%%%02X", *(unsigned char *)s);
+          p += 3;
+        }
+      else if (*s == ' ')
+        *p++ = '+';
+      else
+        *p++ = *s;
+    }
+  *p = 0;
+  free (name); 
+
+
+  err = gpgsm_agent_get_confirmation (ctrl, buffer);
+
+  xfree (buffer);
+  return err;
+}
index 3230a0e..d9a332c 100644 (file)
--- a/sm/sign.c
+++ b/sm/sign.c
@@ -426,6 +426,35 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
           goto leave;
         }
     }
+
+
+  /* Check whether one of the certificates is qualified.  Note that we
+     already validated the certificate and thus the user data stored
+     flag must be available. */
+  for (cl=signerlist; cl; cl = cl->next)
+    {
+      size_t buflen;
+      char buffer[1];
+      
+      err = ksba_cert_get_user_data (cl->cert, "is_qualified", 
+                                     &buffer, sizeof (buffer), &buflen);
+      if (err || !buflen)
+        {
+          log_error (_("checking for qualified certificate failed: %s\n"),
+                     gpg_strerror (err)); 
+          rc = err;
+          goto leave;
+        }
+      if (*buffer)
+        {
+          err = gpgsm_qualified_consent (ctrl, cl->cert);
+          if (err)
+            {
+              rc = err;
+              goto leave;
+            }
+        }
+    }
   
   /* Prepare hashing (actually we are figuring out what we have set above)*/
   rc = gcry_md_open (&data_md, 0, 0);
@@ -443,6 +472,10 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
       if (!algo)
         {
           log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
+          if (algoid
+              && (  !strcmp (algoid, "1.2.840.113549.1.1.2")
+                    ||!strcmp (algoid, "1.2.840.113549.2.2")))
+            log_info (_("(this is the MD2 algorithm)\n"));
           rc = gpg_error (GPG_ERR_BUG);
           goto leave;
         }
index 410e86d..f37cf4a 100644 (file)
@@ -179,8 +179,14 @@ gpgsm_verify (CTRL ctrl, int in_fd, int data_fd, FILE *out_fp)
             {
               algo = gcry_md_map_name (algoid);
               if (!algo)
-                log_error ("unknown hash algorithm `%s'\n",
-                           algoid? algoid:"?");
+                {
+                  log_error ("unknown hash algorithm `%s'\n",
+                             algoid? algoid:"?");
+                  if (algoid
+                      && (  !strcmp (algoid, "1.2.840.113549.1.1.2")
+                          ||!strcmp (algoid, "1.2.840.113549.2.2")))
+                    log_info (_("(this is the MD2 algorithm)\n"));
+                }
               else
                 gcry_md_enable (data_md, algo);
             }