Merge branch 'STABLE-BRANCH-2-2' into master
authorWerner Koch <wk@gnupg.org>
Tue, 27 Mar 2018 06:48:00 +0000 (08:48 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 27 Mar 2018 06:48:00 +0000 (08:48 +0200)
36 files changed:
agent/agent.h
agent/cache.c
agent/command-ssh.c
agent/command.c
agent/cvt-openpgp.c
agent/findkey.c
agent/genkey.c
agent/protect-tool.c
common/agent-opt.c
common/shareddefs.h
configure.ac
doc/Makefile.am
doc/gpg-agent.texi
doc/gpg.texi
doc/gpgsm.texi
g10/call-agent.c
g10/call-agent.h
g10/card-util.c
g10/gpg.c
g10/gpg.h
g10/keyedit.c
g10/misc.c
g10/options.h
g10/parse-packet.c
g10/tdbdump.c
g10/tdbio.c
g10/tdbio.h
g10/trustdb.c
scd/apdu.c
scd/app-openpgp.c
scd/ccid-driver.c
scd/scdaemon.c
sm/call-agent.c
sm/gpgsm.c
sm/gpgsm.h
sm/server.c

index 0d5cf4f..9fdbc76 100644 (file)
@@ -453,9 +453,9 @@ void initialize_module_cache (void);
 void deinitialize_module_cache (void);
 void agent_cache_housekeeping (void);
 void agent_flush_cache (void);
-int agent_put_cache (const char *key, cache_mode_t cache_mode,
+int agent_put_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode,
                      const char *data, int ttl);
-char *agent_get_cache (const char *key, cache_mode_t cache_mode);
+char *agent_get_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode);
 void agent_store_cache_hit (const char *key);
 
 
index ed5c97c..238b6e2 100644 (file)
@@ -58,6 +58,7 @@ struct cache_item_s {
   int ttl;  /* max. lifetime given in seconds, -1 one means infinite */
   struct secret_data_s *pw;
   cache_mode_t cache_mode;
+  int restricted;  /* The value of ctrl->restricted is part of the key.  */
   char key[1];
 };
 
@@ -202,8 +203,8 @@ housekeeping (void)
       if (r->pw && r->ttl >= 0 && r->accessed + r->ttl < current)
         {
           if (DBG_CACHE)
-            log_debug ("  expired '%s' (%ds after last access)\n",
-                       r->key, r->ttl);
+            log_debug ("  expired '%s'.%d (%ds after last access)\n",
+                       r->key, r->restricted, r->ttl);
           release_data (r->pw);
           r->pw = NULL;
           r->accessed = current;
@@ -224,8 +225,8 @@ housekeeping (void)
       if (r->pw && r->created + maxttl < current)
         {
           if (DBG_CACHE)
-            log_debug ("  expired '%s' (%lus after creation)\n",
-                       r->key, opt.max_cache_ttl);
+            log_debug ("  expired '%s'.%d (%lus after creation)\n",
+                       r->key, r->restricted, opt.max_cache_ttl);
           release_data (r->pw);
           r->pw = NULL;
           r->accessed = current;
@@ -233,15 +234,15 @@ housekeeping (void)
     }
 
   /* Third, make sure that we don't have too many items in the list.
-     Expire old and unused entries after 30 minutes */
+   * Expire old and unused entries after 30 minutes.  */
   for (rprev=NULL, r=thecache; r; )
     {
       if (!r->pw && r->ttl >= 0 && r->accessed + 60*30 < current)
         {
           ITEM r2 = r->next;
           if (DBG_CACHE)
-            log_debug ("  removed '%s' (mode %d) (slot not used for 30m)\n",
-                       r->key, r->cache_mode);
+            log_debug ("  removed '%s'.%d (mode %d) (slot not used for 30m)\n",
+                       r->key, r->restricted, r->cache_mode);
           xfree (r);
           if (!rprev)
             thecache = r2;
@@ -296,7 +297,7 @@ agent_flush_cache (void)
       if (r->pw)
         {
           if (DBG_CACHE)
-            log_debug ("  flushing '%s'\n", r->key);
+            log_debug ("  flushing '%s'.%d\n", r->key, r->restricted);
           release_data (r->pw);
           r->pw = NULL;
           r->accessed = 0;
@@ -326,20 +327,21 @@ cache_mode_equal (cache_mode_t a, cache_mode_t b)
    set infinite timeout.  CACHE_MODE is stored with the cache entry
    and used to select different timeouts.  */
 int
-agent_put_cache (const char *key, cache_mode_t cache_mode,
+agent_put_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode,
                  const char *data, int ttl)
 {
   gpg_error_t err = 0;
   ITEM r;
   int res;
+  int restricted = ctrl? ctrl->restricted : -1;
 
   res = npth_mutex_lock (&cache_lock);
   if (res)
     log_fatal ("failed to acquire cache mutex: %s\n", strerror (res));
 
   if (DBG_CACHE)
-    log_debug ("agent_put_cache '%s' (mode %d) requested ttl=%d\n",
-               key, cache_mode, ttl);
+    log_debug ("agent_put_cache '%s'.%d (mode %d) requested ttl=%d\n",
+               key, restricted, cache_mode, ttl);
   housekeeping ();
 
   if (!ttl)
@@ -358,6 +360,7 @@ agent_put_cache (const char *key, cache_mode_t cache_mode,
       if (((cache_mode != CACHE_MODE_USER
             && cache_mode != CACHE_MODE_NONCE)
            || cache_mode_equal (r->cache_mode, cache_mode))
+          && r->restricted == restricted
           && !strcmp (r->key, key))
         break;
     }
@@ -386,6 +389,7 @@ agent_put_cache (const char *key, cache_mode_t cache_mode,
       else
         {
           strcpy (r->key, key);
+          r->restricted = restricted;
           r->created = r->accessed = gnupg_get_time ();
           r->ttl = ttl;
           r->cache_mode = cache_mode;
@@ -415,13 +419,14 @@ agent_put_cache (const char *key, cache_mode_t cache_mode,
    make use of CACHE_MODE except for CACHE_MODE_NONCE and
    CACHE_MODE_USER.  */
 char *
-agent_get_cache (const char *key, cache_mode_t cache_mode)
+agent_get_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode)
 {
   gpg_error_t err;
   ITEM r;
   char *value = NULL;
   int res;
   int last_stored = 0;
+  int restricted = ctrl? ctrl->restricted : -1;
 
   if (cache_mode == CACHE_MODE_IGNORE)
     return NULL;
@@ -439,8 +444,8 @@ agent_get_cache (const char *key, cache_mode_t cache_mode)
     }
 
   if (DBG_CACHE)
-    log_debug ("agent_get_cache '%s' (mode %d)%s ...\n",
-               key, cache_mode,
+    log_debug ("agent_get_cache '%s'.%d (mode %d)%s ...\n",
+               key, ctrl->restricted, cache_mode,
                last_stored? " (stored cache key)":"");
   housekeeping ();
 
@@ -450,6 +455,7 @@ agent_get_cache (const char *key, cache_mode_t cache_mode)
           && ((cache_mode != CACHE_MODE_USER
                && cache_mode != CACHE_MODE_NONCE)
               || cache_mode_equal (r->cache_mode, cache_mode))
+          && r->restricted == restricted
           && !strcmp (r->key, key))
         {
           /* Note: To avoid races KEY may not be accessed anymore below.  */
@@ -472,8 +478,8 @@ agent_get_cache (const char *key, cache_mode_t cache_mode)
             {
               xfree (value);
               value = NULL;
-              log_error ("retrieving cache entry '%s' failed: %s\n",
-                         key, gpg_strerror (err));
+              log_error ("retrieving cache entry '%s'.%d failed: %s\n",
+                         key, restricted, gpg_strerror (err));
             }
           break;
         }
index 7155446..38934f4 100644 (file)
@@ -3145,7 +3145,7 @@ ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec,
     goto out;
 
   /* Cache this passphrase. */
-  err = agent_put_cache (key_grip, CACHE_MODE_SSH, pi->pin, ttl);
+  err = agent_put_cache (ctrl, key_grip, CACHE_MODE_SSH, pi->pin, ttl);
   if (err)
     goto out;
 
index e2486a5..a5baf4d 100644 (file)
@@ -199,14 +199,14 @@ clear_nonce_cache (ctrl_t ctrl)
 {
   if (ctrl->server_local->last_cache_nonce)
     {
-      agent_put_cache (ctrl->server_local->last_cache_nonce,
+      agent_put_cache (ctrl, ctrl->server_local->last_cache_nonce,
                        CACHE_MODE_NONCE, NULL, 0);
       xfree (ctrl->server_local->last_cache_nonce);
       ctrl->server_local->last_cache_nonce = NULL;
     }
   if (ctrl->server_local->last_passwd_nonce)
     {
-      agent_put_cache (ctrl->server_local->last_passwd_nonce,
+      agent_put_cache (ctrl, ctrl->server_local->last_passwd_nonce,
                        CACHE_MODE_NONCE, NULL, 0);
       xfree (ctrl->server_local->last_passwd_nonce);
       ctrl->server_local->last_passwd_nonce = NULL;
@@ -930,7 +930,7 @@ cmd_genkey (assuan_context_t ctx, char *line)
 
     }
   else if (passwd_nonce)
-    newpasswd = agent_get_cache (passwd_nonce, CACHE_MODE_NONCE);
+    newpasswd = agent_get_cache (ctrl, passwd_nonce, CACHE_MODE_NONCE);
 
   rc = agent_genkey (ctrl, cache_nonce, (char*)value, valuelen, no_protection,
                      newpasswd, opt_preset, &outbuf);
@@ -1179,7 +1179,7 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx,
   /* Here we have a little race by doing the cache check separately
      from the retrieval function.  Given that the cache flag is only a
      hint, it should not really matter.  */
-  pw = agent_get_cache (hexgrip, CACHE_MODE_NORMAL);
+  pw = agent_get_cache (ctrl, hexgrip, CACHE_MODE_NORMAL);
   cached = pw ? "1" : "-";
   xfree (pw);
 
@@ -1484,7 +1484,7 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
   if (!strcmp (desc, "X"))
     desc = NULL;
 
-  pw = cacheid ? agent_get_cache (cacheid, CACHE_MODE_USER) : NULL;
+  pw = cacheid ? agent_get_cache (ctrl, cacheid, CACHE_MODE_USER) : NULL;
   if (pw)
     {
       rc = send_back_passphrase (ctx, opt_data, pw);
@@ -1551,7 +1551,7 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
           if (!rc)
             {
               if (cacheid)
-                agent_put_cache (cacheid, CACHE_MODE_USER, response, 0);
+                agent_put_cache (ctrl, cacheid, CACHE_MODE_USER, response, 0);
               rc = send_back_passphrase (ctx, opt_data, response);
             }
           xfree (response);
@@ -1593,7 +1593,8 @@ cmd_clear_passphrase (assuan_context_t ctx, char *line)
   if (!*cacheid || strlen (cacheid) > 50)
     return set_error (GPG_ERR_ASS_PARAMETER, "invalid length of cacheID");
 
-  agent_put_cache (cacheid, opt_normal ? CACHE_MODE_NORMAL : CACHE_MODE_USER,
+  agent_put_cache (ctrl, cacheid,
+                   opt_normal ? CACHE_MODE_NORMAL : CACHE_MODE_USER,
                    NULL, 0);
 
   agent_clear_passphrase (ctrl, cacheid,
@@ -1770,7 +1771,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
               passwd_nonce = bin2hex (buf, 12, NULL);
             }
           if (passwd_nonce
-              && !agent_put_cache (passwd_nonce, CACHE_MODE_NONCE,
+              && !agent_put_cache (ctrl, passwd_nonce, CACHE_MODE_NONCE,
                                    passphrase, CACHE_TTL_NONCE))
             {
               assuan_write_status (ctx, "PASSWD_NONCE", passwd_nonce);
@@ -1785,7 +1786,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
       char *newpass = NULL;
 
       if (passwd_nonce)
-        newpass = agent_get_cache (passwd_nonce, CACHE_MODE_NONCE);
+        newpass = agent_get_cache (ctrl, passwd_nonce, CACHE_MODE_NONCE);
       err = agent_protect_and_store (ctrl, s_skey, &newpass);
       if (!err && passphrase)
         {
@@ -1800,7 +1801,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
               cache_nonce = bin2hex (buf, 12, NULL);
             }
           if (cache_nonce
-              && !agent_put_cache (cache_nonce, CACHE_MODE_NONCE,
+              && !agent_put_cache (ctrl, cache_nonce, CACHE_MODE_NONCE,
                                    passphrase, CACHE_TTL_NONCE))
             {
               assuan_write_status (ctx, "CACHE_NONCE", cache_nonce);
@@ -1820,7 +1821,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
                   passwd_nonce = bin2hex (buf, 12, NULL);
                 }
               if (passwd_nonce
-                  && !agent_put_cache (passwd_nonce, CACHE_MODE_NONCE,
+                  && !agent_put_cache (ctrl, passwd_nonce, CACHE_MODE_NONCE,
                                        newpass, CACHE_TTL_NONCE))
                 {
                   assuan_write_status (ctx, "PASSWD_NONCE", passwd_nonce);
@@ -1834,7 +1835,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
         {
          char hexgrip[40+1];
          bin2hex(grip, 20, hexgrip);
-         err = agent_put_cache (hexgrip, CACHE_MODE_ANY, newpass,
+         err = agent_put_cache (ctrl, hexgrip, CACHE_MODE_ANY, newpass,
                                  ctrl->cache_ttl_opt_preset);
         }
       xfree (newpass);
@@ -1939,7 +1940,7 @@ cmd_preset_passphrase (assuan_context_t ctx, char *line)
 
   if (!rc)
     {
-      rc = agent_put_cache (grip_clear, CACHE_MODE_ANY, passphrase, ttl);
+      rc = agent_put_cache (ctrl, grip_clear, CACHE_MODE_ANY, passphrase, ttl);
       if (opt_inquire)
        xfree (passphrase);
     }
@@ -2174,7 +2175,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
               cache_nonce = bin2hex (buf, 12, NULL);
             }
           if (cache_nonce
-              && !agent_put_cache (cache_nonce, CACHE_MODE_NONCE,
+              && !agent_put_cache (ctrl, cache_nonce, CACHE_MODE_NONCE,
                                    passphrase, CACHE_TTL_NONCE))
             assuan_write_status (ctx, "CACHE_NONCE", cache_nonce);
         }
@@ -2336,7 +2337,7 @@ cmd_export_key (assuan_context_t ctx, char *line)
               cache_nonce = bin2hex (buf, 12, NULL);
             }
           if (cache_nonce
-              && !agent_put_cache (cache_nonce, CACHE_MODE_NONCE,
+              && !agent_put_cache (ctrl, cache_nonce, CACHE_MODE_NONCE,
                                    passphrase, CACHE_TTL_NONCE))
             {
               assuan_write_status (ctx, "CACHE_NONCE", cache_nonce);
@@ -3101,6 +3102,21 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
          ctrl->s2k_count = 0;
         }
     }
+  else if (!strcmp (key, "pretend-request-origin"))
+    {
+      log_assert (!ctrl->restricted);
+      switch (parse_request_origin (value))
+        {
+        case REQUEST_ORIGIN_LOCAL:   ctrl->restricted = 0; break;
+        case REQUEST_ORIGIN_REMOTE:  ctrl->restricted = 1; break;
+        case REQUEST_ORIGIN_BROWSER: ctrl->restricted = 2; break;
+        default:
+          err = gpg_error (GPG_ERR_INV_VALUE);
+          /* Better pretend to be remote in case of a bad value.  */
+          ctrl->restricted = 1;
+          break;
+        }
+    }
   else
     err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
 
index c4d0334..06cd1c8 100644 (file)
@@ -951,7 +951,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist,
         {
           char *cache_value;
 
-          cache_value = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
+          cache_value = agent_get_cache (ctrl, cache_nonce, CACHE_MODE_NONCE);
           if (cache_value)
             {
               if (strlen (cache_value) < pi->max_length)
index e3e9a12..78c3b1a 100644 (file)
@@ -511,7 +511,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
     {
       char *pw;
 
-      pw = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
+      pw = agent_get_cache (ctrl, cache_nonce, CACHE_MODE_NONCE);
       if (pw)
         {
           rc = agent_unprotect (ctrl, *keybuf, pw, NULL, &result, &resultlen);
@@ -536,7 +536,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
       char *pw;
 
     retry:
-      pw = agent_get_cache (hexgrip, cache_mode);
+      pw = agent_get_cache (ctrl, hexgrip, cache_mode);
       if (pw)
         {
           rc = agent_unprotect (ctrl, *keybuf, pw, NULL, &result, &resultlen);
@@ -574,7 +574,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
              We can often avoid the passphrase entry in the second
              step.  We do this only in normal mode, so not to
              interfere with unrelated cache entries.  */
-          pw = agent_get_cache (NULL, cache_mode);
+          pw = agent_get_cache (ctrl, NULL, cache_mode);
           if (pw)
             {
               rc = agent_unprotect (ctrl, *keybuf, pw, NULL,
@@ -670,7 +670,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
       else
         {
           /* Passphrase is fine.  */
-          agent_put_cache (hexgrip, cache_mode, pi->pin,
+          agent_put_cache (ctrl, hexgrip, cache_mode, pi->pin,
                            lookup_ttl? lookup_ttl (hexgrip) : 0);
           agent_store_cache_hit (hexgrip);
           if (r_passphrase && *pi->pin)
index a3e37ee..d5c80d0 100644 (file)
@@ -468,7 +468,7 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce,
     passphrase = NULL;
   else
     {
-      passphrase_buffer = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
+      passphrase_buffer = agent_get_cache (ctrl, cache_nonce, CACHE_MODE_NONCE);
       passphrase = passphrase_buffer;
     }
 
@@ -528,7 +528,7 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce,
         }
       if (cache_nonce
           && !no_protection
-          && !agent_put_cache (cache_nonce, CACHE_MODE_NONCE,
+          && !agent_put_cache (ctrl, cache_nonce, CACHE_MODE_NONCE,
                                passphrase, ctrl->cache_ttl_opt_preset))
         agent_write_status (ctrl, "CACHE_NONCE", cache_nonce, NULL);
       if (preset && !no_protection)
@@ -538,7 +538,7 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce,
          if (gcry_pk_get_keygrip (s_private, grip))
            {
              bin2hex(grip, 20, hexgrip);
-             rc = agent_put_cache (hexgrip, CACHE_MODE_ANY, passphrase,
+             rc = agent_put_cache (ctrl, hexgrip, CACHE_MODE_ANY, passphrase,
                                     ctrl->cache_ttl_opt_preset);
            }
        }
index a193e49..ec7b476 100644 (file)
@@ -749,8 +749,9 @@ agent_key_available (const unsigned char *grip)
 }
 
 char *
-agent_get_cache (const char *key, cache_mode_t cache_mode)
+agent_get_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode)
 {
+  (void)ctrl;
   (void)key;
   (void)cache_mode;
   return NULL;
index b324482..6d9f9e7 100644 (file)
@@ -69,3 +69,38 @@ str_pinentry_mode (pinentry_mode_t mode)
     }
  return "?";
 }
+
+
+/* Parse VALUE and return an integer representing a request_origin_t.
+ * (-1) is returned for an invalid VALUE.  */
+int
+parse_request_origin (const char *value)
+{
+  int result;
+
+  if (!strcmp (value, "none") || !strcmp (value, "local"))
+    result = REQUEST_ORIGIN_LOCAL;
+  else if (!strcmp (value, "remote"))
+    result = REQUEST_ORIGIN_REMOTE;
+  else if (!strcmp (value, "browser"))
+    result = REQUEST_ORIGIN_BROWSER;
+  else
+    result = -1;
+
+  return result;
+}
+
+
+/* Return the string representation for the request origin.  Returns
+ * "?" for an invalid mode.  */
+const char *
+str_request_origin (request_origin_t mode)
+{
+  switch (mode)
+    {
+    case REQUEST_ORIGIN_LOCAL:   return "local";
+    case REQUEST_ORIGIN_REMOTE:  return "remote";
+    case REQUEST_ORIGIN_BROWSER: return "browser";
+    }
+ return "?";
+}
index 1594f66..4b14421 100644 (file)
@@ -39,10 +39,23 @@ typedef enum
 pinentry_mode_t;
 
 
+/* Values for the request origin.  */
+typedef enum
+  {
+    REQUEST_ORIGIN_LOCAL = 0,
+    REQUEST_ORIGIN_REMOTE,
+    REQUEST_ORIGIN_BROWSER
+  }
+request_origin_t;
+
+
 /*-- agent-opt.c --*/
 int parse_pinentry_mode (const char *value);
 const char *str_pinentry_mode (pinentry_mode_t mode);
 
+int parse_request_origin (const char *value);
+const char *str_request_origin (request_origin_t mode);
+
 
 
 #endif /*GNUPG_COMMON_SHAREDDEFS_H*/
index 7522b69..3096aee 100644 (file)
@@ -650,7 +650,7 @@ have_android_system=no
 use_simple_gettext=no
 use_ldapwrapper=yes
 mmap_needed=yes
-require_pipe_to_unblock_pselect=no
+require_pipe_to_unblock_pselect=yes
 case "${host}" in
     *-mingw32*)
         # special stuff for Windoze NT
@@ -665,6 +665,7 @@ case "${host}" in
         have_w32_system=yes
         require_iconv=no
         use_ldapwrapper=no  # Fixme: Do this only for CE.
+        require_pipe_to_unblock_pselect=no
         case "${host}" in
           *-mingw32ce*)
             have_w32ce_system=yes
index 21e3e45..aa16b77 100644 (file)
@@ -85,7 +85,7 @@ DVIPS = TEXINPUTS="$(srcdir)$(PATH_SEPARATOR)$$TEXINPUTS" dvips
 AM_MAKEINFOFLAGS = -I $(srcdir) --css-ref=/share/site.css
 
 YAT2M_OPTIONS = -I $(srcdir) \
-        --release "GnuPG @PACKAGE_VERSION@" --source "GNU Privacy Guard 2.1"
+        --release "GnuPG @PACKAGE_VERSION@" --source "GNU Privacy Guard 2.2"
 
 myman_sources = gnupg7.texi gpg.texi gpgsm.texi gpg-agent.texi \
                dirmngr.texi scdaemon.texi tools.texi wks.texi
index 4781bbd..bcce033 100644 (file)
@@ -1581,6 +1581,27 @@ option is valid for the entire session or until reset to 0.  This
 option is useful if the key is later used on boxes which are either
 much slower or faster than the actual box.
 
+@item pretend-request-origin
+This option switches the connection into a restricted mode which
+handles all further commands in the same way as they would be handled
+when originating from the extra or browser socket.  Note that this
+option is not available in the restricted mode.  Valid values for this
+option are:
+
+  @table @code
+  @item none
+  @itemx local
+  This is a NOP and leaves the connection in the standard way.
+
+  @item remote
+  Pretend to come from a remote origin in the same way as connections
+  from the @option{--extra-socket}.
+
+  @item browser
+  Pretend to come from a local web browser in the same way as connections
+  from the @option{--browser-socket}.
+  @end table
+
 @end table
 
 
index 8fea489..e32974b 100644 (file)
@@ -1126,7 +1126,9 @@ all affected self-signatures is set one second ahead.
 @opindex passwd
 Change the passphrase of the secret key belonging to the certificate
 specified as @var{user-id}.  This is a shortcut for the sub-command
-@code{passwd} of the edit key menu.
+@code{passwd} of the edit key menu.  When using together with the
+option @option{--dry-run} this will not actually change the passphrase
+but check that the current passphrase is correct.
 
 @end table
 
@@ -2213,8 +2215,8 @@ handy in case where an encrypted message contains a bogus key ID.
 @opindex skip-hidden-recipients
 @opindex no-skip-hidden-recipients
 During decryption skip all anonymous recipients.  This option helps in
-the case that people use the hidden recipients feature to hide there
-own encrypt-to key from others.  If oneself has many secret keys this
+the case that people use the hidden recipients feature to hide their
+own encrypt-to key from others.  If one has many secret keys this
 may lead to a major annoyance because all keys are tried in turn to
 decrypt something which was not really intended for it.  The drawback
 of this option is that it is currently not possible to decrypt a
@@ -3177,6 +3179,15 @@ are:
   Pinentry the user is not prompted again if he enters a bad password.
 @end table
 
+@item --request-origin @var{origin}
+@opindex request-origin
+Tell gpg to assume that the operation ultimately originated at
+@var{origin}.  Depending on the origin certain restrictions are applied
+and the Pinentry may include an extra note on the origin.  Supported
+values for @var{origin} are: @code{local} which is the default,
+@code{remote} to indicate a remote origin or @code{browser} for an
+operation requested by a web browser.
+
 @item --command-fd @var{n}
 @opindex command-fd
 This is a replacement for the deprecated shared-memory IPC mode.
index 93ae56e..1736ff1 100644 (file)
@@ -765,6 +765,15 @@ are:
   Pinentry the user is not prompted again if he enters a bad password.
 @end table
 
+@item --request-origin @var{origin}
+@opindex request-origin
+Tell gpgsm to assume that the operation ultimately originated at
+@var{origin}.  Depending on the origin certain restrictions are applied
+and the Pinentry may include an extra note on the origin.  Supported
+values for @var{origin} are: @code{local} which is the default,
+@code{remote} to indicate a remote origin or @code{browser} for an
+operation requested by a web browser.
+
 @item --no-common-certs-import
 @opindex no-common-certs-import
 Suppress the import of common certificates on keybox creation.
index b1f589b..1445f4e 100644 (file)
@@ -289,6 +289,23 @@ start_agent (ctrl_t ctrl, int flag_for_card)
                 }
             }
 
+          /* Pass on the request origin.  */
+          if (opt.request_origin)
+            {
+              char *tmp = xasprintf ("OPTION pretend-request-origin=%s",
+                                     str_request_origin (opt.request_origin));
+              rc = assuan_transact (agent_ctx, tmp,
+                               NULL, NULL, NULL, NULL, NULL, NULL);
+              xfree (tmp);
+              if (rc)
+                {
+                  log_error ("setting request origin '%s' failed: %s\n",
+                             str_request_origin (opt.request_origin),
+                             gpg_strerror (rc));
+                  write_status_error ("set_request_origin", rc);
+                }
+            }
+
           /* In DE_VS mode under Windows we require that the JENT RNG
            * is active.  */
 #ifdef HAVE_W32_SYSTEM
@@ -591,6 +608,8 @@ learn_status_cb (void *opaque, const char *line)
                     parm->extcap.ki = abool;
                   else if (!strcmp (p, "aac"))
                     parm->extcap.aac = abool;
+                  else if (!strcmp (p, "kdf"))
+                    parm->extcap.kdf = abool;
                   else if (!strcmp (p, "si"))
                     parm->status_indicator = strtoul (p2, NULL, 10);
                 }
index 53775c5..8de0d13 100644 (file)
@@ -67,6 +67,7 @@ struct agent_card_info_s
   struct {
     unsigned int ki:1;     /* Key import available.  */
     unsigned int aac:1;    /* Algorithm attributes are changeable.  */
+    unsigned int kdf:1;    /* KDF object to support PIN hashing available.  */
   } extcap;
   unsigned int status_indicator;
 };
index f8a1bb8..9b99751 100644 (file)
@@ -659,7 +659,7 @@ current_card_status (ctrl_t ctrl, estream_t fp,
 
 /* Print all available information for specific card with SERIALNO.
    Print all available information for current card when SERIALNO is NULL.
-   Or print llfor all cards when SERIALNO is "all".  */
+   Or print for all cards when SERIALNO is "all".  */
 void
 card_status (ctrl_t ctrl, estream_t fp, const char *serialno)
 {
@@ -1797,6 +1797,7 @@ factory_reset (void)
         scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
         scd apdu 00 e6 00 00
         scd apdu 00 44 00 00
+        scd reset
         /echo Card has been reset to factory defaults
 
       but tries to find out something about the card first.
@@ -1809,7 +1810,7 @@ factory_reset (void)
   else if (err)
     {
       log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (err));
-      return;
+      goto leave;
     }
 
   if (!termstate)
@@ -1859,10 +1860,16 @@ factory_reset (void)
          command because there is no machinery in scdaemon to catch
          the verify command and ask for the PIN when the "APDU"
          command is used. */
+      /* Here, the length of dummy wrong PIN is 32-byte, also
+         supporting authentication with KDF DO.  */
       for (i=0; i < 4; i++)
-        send_apdu ("00200081084040404040404040", "VERIFY", 0xffff);
+        send_apdu ("0020008120"
+                   "40404040404040404040404040404040"
+                   "40404040404040404040404040404040", "VERIFY", 0xffff);
       for (i=0; i < 4; i++)
-        send_apdu ("00200083084040404040404040", "VERIFY", 0xffff);
+        send_apdu ("0020008320"
+                   "40404040404040404040404040404040"
+                   "40404040404040404040404040404040", "VERIFY", 0xffff);
 
       /* Send terminate datafile command.  */
       err = send_apdu ("00e60000", "TERMINATE DF", 0x6985);
@@ -1878,8 +1885,16 @@ factory_reset (void)
 
   /* Finally we reset the card reader once more.  */
   err = send_apdu (NULL, "RESET", 0);
-  if (err)
-    goto leave;
+
+  /* Then, connect the card again.  */
+  if (!err)
+    {
+      char *serialno0;
+
+      err = agent_scd_serialno (&serialno0, NULL);
+      if (!err)
+        xfree (serialno0);
+    }
 
  leave:
   xfree (answer);
@@ -1887,6 +1902,104 @@ factory_reset (void)
 }
 
 
+#define USER_PIN_DEFAULT "123456"
+#define ADMIN_PIN_DEFAULT "12345678"
+#define KDF_DATA_LENGTH 110
+
+/* Generate KDF data.  */
+static gpg_error_t
+gen_kdf_data (unsigned char *data)
+{
+  const unsigned char h0[] = { 0x81, 0x01, 0x03,
+                               0x82, 0x01, 0x08,
+                               0x83, 0x04 };
+  const unsigned char h1[] = { 0x84, 0x08 };
+  const unsigned char h2[] = { 0x85, 0x08 };
+  const unsigned char h3[] = { 0x86, 0x08 };
+  const unsigned char h4[] = { 0x87, 0x20 };
+  const unsigned char h5[] = { 0x88, 0x20 };
+  unsigned char *p, *salt_user, *salt_admin;
+  unsigned char s2k_char;
+  unsigned int iterations;
+  unsigned char count_4byte[4];
+  gpg_error_t err = 0;
+
+  p = data;
+
+  s2k_char = encode_s2k_iterations (0);
+  iterations = S2K_DECODE_COUNT (s2k_char);
+  count_4byte[0] = (iterations >> 24) & 0xff;
+  count_4byte[1] = (iterations >> 16) & 0xff;
+  count_4byte[2] = (iterations >>  8) & 0xff;
+  count_4byte[3] = (iterations & 0xff);
+
+  memcpy (p, h0, sizeof h0);
+  p += sizeof h0;
+  memcpy (p, count_4byte, sizeof count_4byte);
+  p += sizeof count_4byte;
+  memcpy (p, h1, sizeof h1);
+  salt_user = (p += sizeof h1);
+  gcry_randomize (p, 8, GCRY_STRONG_RANDOM);
+  p += 8;
+  memcpy (p, h2, sizeof h2);
+  p += sizeof h2;
+  gcry_randomize (p, 8, GCRY_STRONG_RANDOM);
+  p += 8;
+  memcpy (p, h3, sizeof h3);
+  salt_admin = (p += sizeof h3);
+  gcry_randomize (p, 8, GCRY_STRONG_RANDOM);
+  p += 8;
+  memcpy (p, h4, sizeof h4);
+  p += sizeof h4;
+  err = gcry_kdf_derive (USER_PIN_DEFAULT, strlen (USER_PIN_DEFAULT),
+                         GCRY_KDF_ITERSALTED_S2K, DIGEST_ALGO_SHA256,
+                         salt_user, 8, iterations, 32, p);
+  p += 32;
+  if (!err)
+    {
+      memcpy (p, h5, sizeof h5);
+      p += sizeof h5;
+      err = gcry_kdf_derive (ADMIN_PIN_DEFAULT, strlen (ADMIN_PIN_DEFAULT),
+                             GCRY_KDF_ITERSALTED_S2K, DIGEST_ALGO_SHA256,
+                             salt_admin, 8, iterations, 32, p);
+    }
+
+  return err;
+}
+
+/* Setup KDF data object which is used for PIN authentication.  */
+static void
+kdf_setup (void)
+{
+  struct agent_card_info_s info;
+  gpg_error_t err;
+  unsigned char kdf_data[KDF_DATA_LENGTH];
+
+  memset (&info, 0, sizeof info);
+
+  err = agent_scd_getattr ("EXTCAP", &info);
+  if (err)
+    {
+      log_error (_("error getting card info: %s\n"), gpg_strerror (err));
+      return;
+    }
+
+  if (!info.extcap.kdf)
+    {
+      log_error (_("This command is not supported by this card\n"));
+      goto leave;
+    }
+
+  if (!(err = gen_kdf_data (kdf_data))
+      && !(err = agent_scd_setattr ("KDF", kdf_data, KDF_DATA_LENGTH, NULL)))
+    err = agent_scd_getattr ("KDF", &info);
+
+  if (err)
+    log_error (_("error for setup KDF: %s\n"), gpg_strerror (err));
+
+ leave:
+  agent_release_card_info (&info);
+}
 \f
 /* Data used by the command parser.  This needs to be outside of the
    function scope to allow readline based command completion.  */
@@ -1896,7 +2009,7 @@ enum cmdids
     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
     cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
-    cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET,
+    cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET, cmdKDFSETUP,
     cmdINVCMD
   };
 
@@ -1930,6 +2043,7 @@ static struct
     { "verify"  , cmdVERIFY, 0, N_("verify the PIN and list all data")},
     { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") },
     { "factory-reset", cmdFACTORYRESET, 1, N_("destroy all keys and data")},
+    { "kdf-setup", cmdKDFSETUP, 1, N_("setup KDF for PIN authentication")},
     /* Note, that we do not announce these command yet. */
     { "privatedo", cmdPRIVATEDO, 0, NULL },
     { "readcert", cmdREADCERT, 0, NULL },
@@ -2213,6 +2327,10 @@ card_edit (ctrl_t ctrl, strlist_t commands)
           factory_reset ();
           break;
 
+        case cmdKDFSETUP:
+          kdf_setup ();
+          break;
+
         case cmdQUIT:
           goto leave;
 
index cbac967..277b5db 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -428,6 +428,7 @@ enum cmd_and_opt_values
     oDisableSignerUID,
     oSender,
     oKeyOrigin,
+    oRequestOrigin,
 
     oNoop
   };
@@ -719,6 +720,7 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_s (oPassphraseFile,  "passphrase-file", "@"),
   ARGPARSE_s_i (oPassphraseRepeat,"passphrase-repeat", "@"),
   ARGPARSE_s_s (oPinentryMode,    "pinentry-mode", "@"),
+  ARGPARSE_s_s (oRequestOrigin,   "request-origin", "@"),
   ARGPARSE_s_i (oCommandFD, "command-fd", "@"),
   ARGPARSE_s_s (oCommandFile, "command-file", "@"),
   ARGPARSE_s_n (oQuickRandom, "debug-quick-random", "@"),
@@ -3158,6 +3160,12 @@ main (int argc, char **argv)
               log_error (_("invalid pinentry mode '%s'\n"), pargs.r.ret_str);
            break;
 
+          case oRequestOrigin:
+           opt.request_origin = parse_request_origin (pargs.r.ret_str);
+           if (opt.request_origin == -1)
+              log_error (_("invalid request origin '%s'\n"), pargs.r.ret_str);
+           break;
+
          case oCommandFD:
             opt.command_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 0);
            if (! gnupg_fd_valid (opt.command_fd))
index 03fe384..28a77b6 100644 (file)
--- a/g10/gpg.h
+++ b/g10/gpg.h
    correct value and may be of advantage if we ever have to do
    special things. */
 
+#ifdef HAVE_W32_SYSTEM
+# define WIN32_LEAN_AND_MEAN 1
+#endif
+
 #ifdef GPG_ERR_SOURCE_DEFAULT
 #error GPG_ERR_SOURCE_DEFAULT already defined
 #endif
index 2c33a29..2cb9bb2 100644 (file)
@@ -1134,8 +1134,10 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
           if (err)
             goto leave;
 
+          /* Note that when using --dry-run we don't change the
+           * passphrase but merely verify the current passphrase.  */
           desc = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_NORMAL, 1);
-          err = agent_passwd (ctrl, hexgrip, desc, 0,
+          err = agent_passwd (ctrl, hexgrip, desc, !!opt.dry_run,
                               &cache_nonce, &passwd_nonce);
           xfree (desc);
 
index 8c54793..1e6df5f 100644 (file)
@@ -42,6 +42,7 @@
 #include <time.h>
 #include <process.h>
 #ifdef HAVE_WINSOCK2_H
+# define WIN32_LEAN_AND_MEAN 1
 # include <winsock2.h>
 #endif
 #include <windows.h>
index 471aee7..f186225 100644 (file)
@@ -278,6 +278,7 @@ struct
 
   int passphrase_repeat;
   int pinentry_mode;
+  int request_origin;
 
   int unwrap_encryption;
   int only_sign_text_ids;
index e3ff432..e933abf 100644 (file)
@@ -971,10 +971,10 @@ skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial)
 }
 
 
-/* Read PKTLEN bytes form INP and return them in a newly allocated
  buffer.  In case of an error (including reading fewer than PKTLEN
  bytes from INP before EOF is returned), NULL is returned and an
  error message is logged.  */
+/* Read PKTLEN bytes from INP and return them in a newly allocated
* buffer.  In case of an error (including reading fewer than PKTLEN
* bytes from INP before EOF is returned), NULL is returned and an
* error message is logged.  */
 static void *
 read_rest (IOBUF inp, size_t pktlen)
 {
@@ -1741,6 +1741,8 @@ enum_sig_subpkt (const subpktarea_t * pktbuf, sigsubpkttype_t reqtype,
        }
       if (buflen < n)
        goto too_short;
+      if (!buflen)
+        goto no_type_byte;
       type = *buffer;
       if (type & 0x80)
        {
@@ -1815,6 +1817,13 @@ enum_sig_subpkt (const subpktarea_t * pktbuf, sigsubpkttype_t reqtype,
   if (start)
     *start = -1;
   return NULL;
+
+ no_type_byte:
+  if (opt.verbose)
+    log_info ("type octet missing in subpacket\n");
+  if (start)
+    *start = -1;
+  return NULL;
 }
 
 
index 37bf78b..73a6c2c 100644 (file)
@@ -191,7 +191,7 @@ import_ownertrust (ctrl_t ctrl, const char *fname )
        while (fprlen < MAX_FINGERPRINT_LEN)
            fpr[fprlen++] = 0;
 
-       rc = tdbio_search_trust_byfpr (fpr, &rec);
+       rc = tdbio_search_trust_byfpr (ctrl, fpr, &rec);
        if( !rc ) { /* found: update */
            if (rec.r.trust.ownertrust != otrust)
               {
index 7572b9a..fed0cf5 100644 (file)
@@ -105,16 +105,17 @@ struct cmp_xdir_struct
 /* The name of the trustdb file.  */
 static char *db_name;
 
-/* The handle for locking the trustdb file and a flag to record
-   whether a lock has been taken.  */
+/* The handle for locking the trustdb file and a counter to record how
+ * often this lock has been taken.  That counter is required becuase
+ * dotlock does not implemen recursive locks.  */
 static dotlock_t lockhandle;
-static int is_locked;
+static unsigned int is_locked;
 
 /* The file descriptor of the trustdb.  */
 static int  db_fd = -1;
 
 /* A flag indicating that a transaction is active.  */
-static int in_transaction;
+/* static int in_transaction;   Not yet used. */
 
 
 \f
@@ -125,7 +126,7 @@ static void create_hashtable (ctrl_t ctrl, TRUSTREC *vr, int type);
 \f
 /*
  * Take a lock on the trustdb file name.  I a lock file can't be
- * created the function terminates the process.  Excvept for a
+ * created the function terminates the process.  Except for a
  * different return code the function does nothing if the lock has
  * already been taken.
  *
@@ -135,6 +136,8 @@ static void create_hashtable (ctrl_t ctrl, TRUSTREC *vr, int type);
 static int
 take_write_lock (void)
 {
+  int rc;
+
   if (!lockhandle)
     lockhandle = dotlock_create (db_name, 0);
   if (!lockhandle)
@@ -144,12 +147,16 @@ take_write_lock (void)
     {
       if (dotlock_take (lockhandle, -1) )
         log_fatal ( _("can't lock '%s'\n"), db_name );
-      else
-        is_locked = 1;
-      return 0;
+      rc = 0;
     }
   else
-    return 1;
+    rc = 1;
+
+  if (opt.lock_once)
+    is_locked = 1;
+  else
+    is_locked++;
+  return rc;
 }
 
 
@@ -160,10 +167,22 @@ take_write_lock (void)
 static void
 release_write_lock (void)
 {
-  if (!opt.lock_once)
-    if (!dotlock_release (lockhandle))
-      is_locked = 0;
+  if (opt.lock_once)
+    return;  /* Don't care; here IS_LOCKED is fixed to 1.  */
+
+  if (!is_locked)
+    {
+      log_error ("Ooops, tdbio:release_write_lock with no lock held\n");
+      return;
+    }
+  if (--is_locked)
+    return;
+
+  if (dotlock_release (lockhandle))
+    log_error ("Oops, tdbio:release_write_locked failed\n");
 }
+
+
 \f
 /*************************************
  ************* record cache **********
@@ -329,6 +348,7 @@ put_record_into_cache (ulong recno, const char *data)
     }
 
   /* No clean entries: We have to flush some dirty entries.  */
+#if 0 /* Transactions are not yet used.  */
   if (in_transaction)
     {
       /* But we can't do this while in a transaction.  Thus we
@@ -352,6 +372,7 @@ put_record_into_cache (ulong recno, const char *data)
       log_info (_("trustdb transaction too large\n"));
       return GPG_ERR_RESOURCE_LIMIT;
     }
+#endif
 
   if (dirty_count)
     {
@@ -418,8 +439,10 @@ tdbio_sync()
 
     if( db_fd == -1 )
        open_db();
+#if 0 /* Transactions are not yet used.  */
     if( in_transaction )
        log_bug("tdbio: syncing while in transaction\n");
+#endif
 
     if( !cache_is_dirty )
        return 0;
@@ -560,7 +583,7 @@ tdbio_update_version_record (ctrl_t ctrl)
 
 /*
  * Create and write the trustdb version record.
- *
+ * This is called with the writelock activ.
  * Returns: 0 on success or an error code.
  */
 static int
@@ -951,10 +974,12 @@ tdbio_write_nextcheck (ctrl_t ctrl, ulong stamp)
  * Return: record number
  */
 static ulong
-get_trusthashrec(void)
+get_trusthashrec (ctrl_t ctrl)
 {
   static ulong trusthashtbl; /* Record number of the trust hashtable.  */
 
+  (void)ctrl;
+
   if (!trusthashtbl)
     {
       TRUSTREC vr;
@@ -965,6 +990,20 @@ get_trusthashrec(void)
         log_fatal (_("%s: error reading version record: %s\n"),
                    db_name, gpg_strerror (rc) );
 
+      if (!vr.r.ver.trusthashtbl)
+        {
+          /* Oops: the trustdb is corrupt because the hashtable is
+           * always created along with the version record.  However,
+           * if something went initially wrong it may happen that
+           * there is just the version record.  We try to fix it here.
+           * If we can't do that we return 0 - this is the version
+           * record and thus the actual read will detect the mismatch
+           * and bail out.  Note that create_hashtable updates VR.  */
+          take_write_lock ();
+          if (lseek (db_fd, 0, SEEK_END) == TRUST_RECORD_LEN)
+            create_hashtable (ctrl, &vr, 0);
+          release_write_lock ();
+        }
       trusthashtbl = vr.r.ver.trusthashtbl;
     }
 
@@ -1269,6 +1308,13 @@ lookup_hashtable (ulong table, const byte *key, size_t keylen,
   int msb;
   int level = 0;
 
+  if (!table)
+    {
+      rc = gpg_error (GPG_ERR_INV_RECORD);
+      log_error("lookup_hashtable failed: %s\n", "request for record 0");
+      return rc;
+    }
+
   hashrec = table;
  next_level:
   msb = key[level];
@@ -1358,7 +1404,7 @@ lookup_hashtable (ulong table, const byte *key, size_t keylen,
 static int
 update_trusthashtbl (ctrl_t ctrl, TRUSTREC *tr)
 {
-  return upd_hashtable (ctrl, get_trusthashrec (),
+  return upd_hashtable (ctrl, get_trusthashrec (ctrl),
                         tr->r.trust.fingerprint, 20, tr->recnum);
 }
 
@@ -1441,7 +1487,7 @@ tdbio_dump_record (TRUSTREC *rec, estream_t fp)
  * EXPECTED is not 0 reading any other record type will return an
  * error.
  *
- * Return: 0 on success, -1 on EOF, or an error code.
+ * Return: 0 on success or an error code.
  */
 int
 tdbio_read_record (ulong recnum, TRUSTREC *rec, int expected)
@@ -1466,7 +1512,7 @@ tdbio_read_record (ulong recnum, TRUSTREC *rec, int expected)
       n = read (db_fd, readbuf, TRUST_RECORD_LEN);
       if (!n)
         {
-          return -1; /* eof */
+          return gpg_error (GPG_ERR_EOF);
        }
       else if (n != TRUST_RECORD_LEN)
         {
@@ -1700,7 +1746,7 @@ tdbio_delete_record (ctrl_t ctrl, ulong recnum)
     ;
   else if (rec.rectype == RECTYPE_TRUST)
     {
-      rc = drop_from_hashtable (ctrl, get_trusthashrec(),
+      rc = drop_from_hashtable (ctrl, get_trusthashrec (ctrl),
                                 rec.r.trust.fingerprint, 20, rec.recnum);
     }
 
@@ -1746,20 +1792,14 @@ tdbio_new_recnum (ctrl_t ctrl)
       recnum = vr.r.ver.firstfree;
       rc = tdbio_read_record (recnum, &rec, RECTYPE_FREE);
       if (rc)
-        {
-          log_error (_("%s: error reading free record: %s\n"),
-                     db_name,  gpg_strerror (rc));
-          return rc;
-       }
+        log_fatal (_("%s: error reading free record: %s\n"),
+                   db_name, gpg_strerror (rc));
       /* Update dir record.  */
       vr.r.ver.firstfree = rec.r.free.next;
       rc = tdbio_write_record (ctrl, &vr);
       if (rc)
-        {
-          log_error (_("%s: error writing dir record: %s\n"),
-                     db_name, gpg_strerror (rc));
-          return rc;
-       }
+        log_fatal (_("%s: error writing dir record: %s\n"),
+                   db_name, gpg_strerror (rc));
       /* Zero out the new record.  */
       memset (&rec, 0, sizeof rec);
       rec.rectype = 0; /* Mark as unused record (actually already done
@@ -1776,7 +1816,7 @@ tdbio_new_recnum (ctrl_t ctrl)
       if (offset == (off_t)(-1))
         log_fatal ("trustdb: lseek to end failed: %s\n", strerror (errno));
       recnum = offset / TRUST_RECORD_LEN;
-      log_assert (recnum); /* this is will never be the first record */
+      log_assert (recnum); /* This will never be the first record */
       /* We must write a record, so that the next call to this
        * function returns another recnum.  */
       memset (&rec, 0, sizeof rec);
@@ -1798,13 +1838,13 @@ tdbio_new_recnum (ctrl_t ctrl)
             {
               rc = gpg_error_from_syserror ();
               log_error (_("trustdb rec %lu: write failed (n=%d): %s\n"),
-                         recnum, n, strerror (errno));
+                         recnum, n, gpg_strerror (rc));
            }
        }
 
       if (rc)
         log_fatal (_("%s: failed to append a record: %s\n"),
-                   db_name,    gpg_strerror (rc));
+                   db_name, gpg_strerror (rc));
     }
 
   return recnum ;
@@ -1828,12 +1868,12 @@ cmp_trec_fpr ( const void *fpr, const TRUSTREC *rec )
  * Return: 0 if found, GPG_ERR_NOT_FOUND, or another error code.
  */
 gpg_error_t
-tdbio_search_trust_byfpr (const byte *fingerprint, TRUSTREC *rec)
+tdbio_search_trust_byfpr (ctrl_t ctrl, const byte *fingerprint, TRUSTREC *rec)
 {
   int rc;
 
   /* Locate the trust record using the hash table */
-  rc = lookup_hashtable (get_trusthashrec(), fingerprint, 20,
+  rc = lookup_hashtable (get_trusthashrec (ctrl), fingerprint, 20,
                          cmp_trec_fpr, fingerprint, rec );
   return rc;
 }
@@ -1846,7 +1886,7 @@ tdbio_search_trust_byfpr (const byte *fingerprint, TRUSTREC *rec)
  * Return: 0 if found, GPG_ERR_NOT_FOUND, or another error code.
  */
 gpg_error_t
-tdbio_search_trust_bypk (PKT_public_key *pk, TRUSTREC *rec)
+tdbio_search_trust_bypk (ctrl_t ctrl, PKT_public_key *pk, TRUSTREC *rec)
 {
   byte fingerprint[MAX_FINGERPRINT_LEN];
   size_t fingerlen;
@@ -1854,7 +1894,7 @@ tdbio_search_trust_bypk (PKT_public_key *pk, TRUSTREC *rec)
   fingerprint_from_pk( pk, fingerprint, &fingerlen );
   for (; fingerlen < 20; fingerlen++)
     fingerprint[fingerlen] = 0;
-  return tdbio_search_trust_byfpr (fingerprint, rec);
+  return tdbio_search_trust_byfpr (ctrl, fingerprint, rec);
 }
 
 
index beaa308..267a379 100644 (file)
@@ -110,8 +110,10 @@ int tdbio_end_transaction(void);
 int tdbio_cancel_transaction(void);
 int tdbio_delete_record (ctrl_t ctrl, ulong recnum);
 ulong tdbio_new_recnum (ctrl_t ctrl);
-gpg_error_t tdbio_search_trust_byfpr (const byte *fingerprint, TRUSTREC *rec);
-gpg_error_t tdbio_search_trust_bypk (PKT_public_key *pk, TRUSTREC *rec);
+gpg_error_t tdbio_search_trust_byfpr (ctrl_t ctrl, const byte *fingerprint,
+                                      TRUSTREC *rec);
+gpg_error_t tdbio_search_trust_bypk (ctrl_t ctrl, PKT_public_key *pk,
+                                     TRUSTREC *rec);
 
 void tdbio_how_to_fix (void);
 void tdbio_invalid(void);
index 0a98c12..2c2d239 100644 (file)
@@ -649,7 +649,7 @@ read_trust_record (ctrl_t ctrl, PKT_public_key *pk, TRUSTREC *rec)
   int rc;
 
   init_trustdb (ctrl, 0);
-  rc = tdbio_search_trust_bypk (pk, rec);
+  rc = tdbio_search_trust_bypk (ctrl, pk, rec);
   if (rc)
     {
       if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
index 60270ed..66a16f8 100644 (file)
@@ -119,6 +119,7 @@ struct reader_table_s {
     pcsc_dword_t modify_ioctl;
     int pinmin;
     int pinmax;
+    pcsc_dword_t current_state;
   } pcsc;
 #ifdef USE_G10CODE_RAPDU
   struct {
@@ -228,6 +229,7 @@ static npth_mutex_t reader_table_lock;
 #define PCSC_E_READER_UNAVAILABLE      0x80100017
 #define PCSC_E_NO_SERVICE              0x8010001D
 #define PCSC_E_SERVICE_STOPPED         0x8010001E
+#define PCSC_W_RESET_CARD              0x80100068
 #define PCSC_W_REMOVED_CARD            0x80100069
 
 /* Fix pcsc-lite ABI incompatibility.  */
@@ -453,6 +455,7 @@ new_reader_slot (void)
   reader_table[reader].pcsc.modify_ioctl = 0;
   reader_table[reader].pcsc.pinmin = -1;
   reader_table[reader].pcsc.pinmax = -1;
+  reader_table[reader].pcsc.current_state = PCSC_STATE_UNAWARE;
 
   return reader;
 }
@@ -653,12 +656,12 @@ pcsc_get_status (int slot, unsigned int *status, int on_wire)
   (void)on_wire;
   memset (rdrstates, 0, sizeof *rdrstates);
   rdrstates[0].reader = reader_table[slot].rdrname;
-  rdrstates[0].current_state = PCSC_STATE_UNAWARE;
+  rdrstates[0].current_state = reader_table[slot].pcsc.current_state;
   err = pcsc_get_status_change (reader_table[slot].pcsc.context,
                                 0,
                                 rdrstates, 1);
   if (err == PCSC_E_TIMEOUT)
-    err = 0; /* Timeout is no error error here. */
+    err = 0; /* Timeout is no error here.  */
   if (err)
     {
       log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
@@ -666,24 +669,29 @@ pcsc_get_status (int slot, unsigned int *status, int on_wire)
       return pcsc_error_to_sw (err);
     }
 
-  /*   log_debug  */
-  /*     ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */
+  if ((rdrstates[0].event_state & PCSC_STATE_CHANGED))
+    reader_table[slot].pcsc.current_state =
+      (rdrstates[0].event_state & ~PCSC_STATE_CHANGED);
+
+  if (DBG_CARD_IO)
+    log_debug
+      ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n",
+       (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"",
+       (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"",
+       (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"",
+       (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"",
+       (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"",
+       (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"",
+       (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"",
+       (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"",
+       (rdrstates[0].event_state & PCSC_STATE_INUSE)? " inuse":"",
+       (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" );
 
   *status = 0;
-  if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
+  if ( (reader_table[slot].pcsc.current_state & PCSC_STATE_PRESENT) )
     {
       *status |= APDU_CARD_PRESENT;
-      if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+      if ( !(reader_table[slot].pcsc.current_state & PCSC_STATE_MUTE) )
         *status |= APDU_CARD_ACTIVE;
     }
 #ifndef HAVE_W32_SYSTEM
@@ -692,7 +700,7 @@ pcsc_get_status (int slot, unsigned int *status, int on_wire)
      mode.  */
   if ( (*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
        == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE)
-       && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
+       && !(reader_table[slot].pcsc.current_state & PCSC_STATE_INUSE) )
     *status |= APDU_CARD_USABLE;
 #else
   /* Some winscard drivers may set EXCLUSIVE and INUSE at the same
@@ -702,7 +710,11 @@ pcsc_get_status (int slot, unsigned int *status, int on_wire)
     *status |= APDU_CARD_USABLE;
 #endif
 
-  return 0;
+  if (!on_wire && (rdrstates[0].event_state & PCSC_STATE_CHANGED))
+    /* Event like sleep/resume occurs, which requires RESET.  */
+    return SW_HOST_NO_READER;
+  else
+    return 0;
 }
 
 
@@ -741,6 +753,14 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
     log_error ("pcsc_transmit failed: %s (0x%lx)\n",
                pcsc_error_string (err), err);
 
+  /* Handle fatal errors which require shutdown of reader.  */
+  if (err == PCSC_E_NOT_TRANSACTED || err == PCSC_W_RESET_CARD
+      || err == PCSC_W_REMOVED_CARD)
+    {
+      reader_table[slot].pcsc.current_state = PCSC_STATE_UNAWARE;
+      scd_kick_the_loop ();
+    }
+
   return pcsc_error_to_sw (err);
 }
 
index 54f04c6..dc775f7 100644 (file)
@@ -1018,7 +1018,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
 
       snprintf (tmp, sizeof tmp,
                 "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d "
-                "sm=%d si=%u dec=%d bt=%d",
+                "sm=%d si=%u dec=%d bt=%d kdf=%d",
                 app->app_local->extcap.get_challenge,
                 app->app_local->extcap.key_import,
                 app->app_local->extcap.change_force_chv,
@@ -1032,7 +1032,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
                  : 0),
                 app->app_local->status_indicator,
                 app->app_local->extcap.has_decrypt,
-                app->app_local->extcap.has_button);
+                app->app_local->extcap.has_button,
+                app->app_local->extcap.kdf_do);
       send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
       return 0;
     }
index 5046da5..f33a36c 100644 (file)
@@ -1467,7 +1467,8 @@ intr_cb (struct libusb_transfer *transfer)
 
   DEBUGOUT_1 ("CCID: interrupt callback %d\n", transfer->status);
 
-  if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT)
+  if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT
+      || transfer->status == LIBUSB_TRANSFER_NO_DEVICE)
     {
       int err;
 
index cebeea9..8f8a026 100644 (file)
@@ -393,7 +393,21 @@ cleanup (void)
     }
 }
 
-
+static void
+setup_signal_mask (void)
+{
+#ifndef HAVE_W32_SYSTEM
+  npth_sigev_init ();
+  npth_sigev_add (SIGHUP);
+  npth_sigev_add (SIGUSR1);
+  npth_sigev_add (SIGUSR2);
+  npth_sigev_add (SIGINT);
+  npth_sigev_add (SIGCONT);
+  npth_sigev_add (SIGTERM);
+  npth_sigev_fini ();
+  main_thread_pid = getpid ();
+#endif
+}
 
 int
 main (int argc, char **argv )
@@ -744,6 +758,7 @@ main (int argc, char **argv )
 #endif
 
       npth_init ();
+      setup_signal_mask ();
       gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
 
       /* If --debug-allow-core-dump has been given we also need to
@@ -884,6 +899,7 @@ main (int argc, char **argv )
       /* This is the child. */
 
       npth_init ();
+      setup_signal_mask ();
       gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
 
       /* Detach from tty and put process into a new session. */
@@ -1206,18 +1222,16 @@ start_connection_thread (void *arg)
 void
 scd_kick_the_loop (void)
 {
-  int ret;
-
   /* Kick the select loop.  */
 #ifdef HAVE_W32_SYSTEM
-  ret = SetEvent (the_event);
+  int ret = SetEvent (the_event);
   if (ret == 0)
     log_error ("SetEvent for scd_kick_the_loop failed: %s\n",
                w32_strerror (-1));
 #elif defined(HAVE_PSELECT_NO_EINTR)
   write (notify_fd, "", 1);
 #else
-  ret = kill (main_thread_pid, SIGCONT);
+  int ret = kill (main_thread_pid, SIGCONT);
   if (ret < 0)
     log_error ("SetEvent for scd_kick_the_loop failed: %s\n",
                gpg_strerror (gpg_error_from_syserror ()));
@@ -1292,16 +1306,6 @@ handle_connections (int listen_fd)
         events[0] = the_event = h2;
       }
   }
-#else
-  npth_sigev_init ();
-  npth_sigev_add (SIGHUP);
-  npth_sigev_add (SIGUSR1);
-  npth_sigev_add (SIGUSR2);
-  npth_sigev_add (SIGINT);
-  npth_sigev_add (SIGCONT);
-  npth_sigev_add (SIGTERM);
-  npth_sigev_fini ();
-  main_thread_pid = getpid ();
 #endif
 
   FD_ZERO (&fdset);
@@ -1348,6 +1352,8 @@ handle_connections (int listen_fd)
       FD_SET (pipe_fd[0], &read_fdset);
       if (max_fd < pipe_fd[0])
         max_fd = pipe_fd[0];
+#else
+      (void)max_fd;
 #endif
 
 #ifndef HAVE_W32_SYSTEM
index 772c9c3..20d879f 100644 (file)
@@ -179,6 +179,20 @@ start_agent (ctrl_t ctrl)
                            gpg_strerror (rc));
             }
 
+          /* Pass on the request origin.  */
+          if (opt.request_origin)
+            {
+              char *tmp = xasprintf ("OPTION pretend-request-origin=%s",
+                                     str_request_origin (opt.request_origin));
+              rc = assuan_transact (agent_ctx, tmp,
+                               NULL, NULL, NULL, NULL, NULL, NULL);
+              xfree (tmp);
+              if (rc)
+                log_error ("setting request origin '%s' failed: %s\n",
+                           str_request_origin (opt.request_origin),
+                           gpg_strerror (rc));
+            }
+
           /* In DE_VS mode under Windows we require that the JENT RNG
            * is active.  */
 #ifdef HAVE_W32_SYSTEM
index 982be58..beabb38 100644 (file)
@@ -125,6 +125,7 @@ enum cmd_and_opt_values {
 
   oPassphraseFD,
   oPinentryMode,
+  oRequestOrigin,
 
   oAssumeArmor,
   oAssumeBase64,
@@ -255,6 +256,7 @@ static ARGPARSE_OPTS opts[] = {
 
   ARGPARSE_s_i (oPassphraseFD,    "passphrase-fd", "@"),
   ARGPARSE_s_s (oPinentryMode,    "pinentry-mode", "@"),
+  ARGPARSE_s_s (oRequestOrigin,   "request-origin", "@"),
 
   ARGPARSE_s_n (oAssumeArmor, "assume-armor",
                 N_("assume input is in PEM format")),
@@ -1162,6 +1164,12 @@ main ( int argc, char **argv)
             log_error (_("invalid pinentry mode '%s'\n"), pargs.r.ret_str);
          break;
 
+        case oRequestOrigin:
+          opt.request_origin = parse_request_origin (pargs.r.ret_str);
+          if (opt.request_origin == -1)
+            log_error (_("invalid request origin '%s'\n"), pargs.r.ret_str);
+          break;
+
           /* Input encoding selection.  */
         case oAssumeArmor:
           ctrl.autodetect_encoding = 0;
index 3e2f95f..d3fbde5 100644 (file)
@@ -88,6 +88,7 @@ struct
   int with_key_screening; /* Option  --with-key-screening active.  */
 
   int pinentry_mode;
+  int request_origin;
 
   int armor;        /* force base64 armoring (see also ctrl.with_base64) */
   int no_armor;     /* don't try to figure out whether data is base64 armored*/
index 721f3fa..98505e2 100644 (file)
@@ -32,6 +32,7 @@
 #include "../common/sysutils.h"
 #include "../common/server-help.h"
 #include "../common/asshelp.h"
+#include "../common/shareddefs.h"
 
 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
 
@@ -289,6 +290,17 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
           ctrl->offline = i;
         }
     }
+  else if (!strcmp (key, "request-origin"))
+    {
+      if (!opt.request_origin)
+        {
+          int i = parse_request_origin (value);
+          if (i == -1)
+            err = gpg_error (GPG_ERR_INV_VALUE);
+          else
+            opt.request_origin = i;
+        }
+    }
   else
     err = gpg_error (GPG_ERR_UNKNOWN_OPTION);