Print status of CRL checks in the audit log.
[gnupg.git] / sm / call-dirmngr.c
index 4cbc019..33aebdf 100644 (file)
@@ -44,10 +44,18 @@ struct membuf {
 
 
 
+/* fixme: We need a context for each thread or serialize the access to
+   the dirmngr.  */
 static assuan_context_t dirmngr_ctx = NULL;
+static assuan_context_t dirmngr2_ctx = NULL;
+
+static int dirmngr_ctx_locked;
+static int dirmngr2_ctx_locked;
+
 static int force_pipe_server = 0;
 
 struct inq_certificate_parm_s {
+  ctrl_t ctrl;
   assuan_context_t ctx;
   ksba_cert_t cert;
   ksba_cert_t issuer_cert;
@@ -134,7 +142,7 @@ get_membuf (struct membuf *mb, size_t *len)
 }
 
 
-/* This fucntion prepares the dirmngr for a new session.  The
+/* This function prepares the dirmngr for a new session.  The
    audit-events option is used so that other dirmngr clients won't get
    disturbed by such events.  */
 static void
@@ -142,18 +150,14 @@ prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err)
 {
   struct keyserver_spec *server;
 
-  if (!ctrl->dirmngr_seen)
+  if (!err)
     {
-      ctrl->dirmngr_seen = 1;
-      if (!err)
-        {
-          err = assuan_transact (ctx, "OPTION audit-events=1",
-                                 NULL, NULL, NULL, NULL, NULL, NULL);
-          if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
-            err = 0;  /* Allow the use of old dirmngr versions.  */
-        }
-      audit_log_ok (ctrl->audit, AUDIT_DIRMNGR_READY, err);
+      err = assuan_transact (ctx, "OPTION audit-events=1",
+                            NULL, NULL, NULL, NULL, NULL, NULL);
+      if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
+       err = 0;  /* Allow the use of old dirmngr versions.  */
     }
+  audit_log_ok (ctrl->audit, AUDIT_DIRMNGR_READY, err);
 
   server = opt.keyserver;
   while (server)
@@ -162,7 +166,7 @@ prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err)
       char *user = server->user ? server->user : "";
       char *pass = server->pass ? server->pass : "";
       char *base = server->base ? server->base : "";
-
+      
       snprintf (line, DIM (line) - 1, "LDAPSERVER %s:%i:%s:%s:%s",
                server->host, server->port, user, pass, base);
       line[DIM (line) - 1] = 0;
@@ -180,7 +184,7 @@ prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err)
 /* Try to connect to the agent via socket or fork it off and work by
    pipes.  Handle the server's initial greeting */
 static int
-start_dirmngr (ctrl_t ctrl)
+start_dirmngr_ext (ctrl_t ctrl, assuan_context_t *ctx_r)
 {
   int rc;
   char *infostr, *p;
@@ -190,12 +194,9 @@ start_dirmngr (ctrl_t ctrl)
   if (opt.disable_dirmngr)
     return gpg_error (GPG_ERR_NO_DIRMNGR);
 
-  if (dirmngr_ctx)
-    {
-      prepare_dirmngr (ctrl, dirmngr_ctx, 0);
-      return 0; /* fixme: We need a context for each thread or serialize
-                   the access to the dirmngr */
-    }
+  if (*ctx_r)
+    return 0;
+
   /* Note: if you change this to multiple connections, you also need
      to take care of the implicit option sending caching. */
 
@@ -266,7 +267,7 @@ start_dirmngr (ctrl_t ctrl)
               log_error (_("malformed DIRMNGR_INFO environment variable\n"));
               xfree (infostr);
               force_pipe_server = 1;
-              return start_dirmngr (ctrl);
+              return start_dirmngr_ext (ctrl, ctx_r);
             }
           *p++ = 0;
           pid = atoi (p);
@@ -279,7 +280,7 @@ start_dirmngr (ctrl_t ctrl)
                          prot);
               xfree (infostr);
               force_pipe_server = 1;
-              return start_dirmngr (ctrl);
+              return start_dirmngr_ext (ctrl, ctx_r);
             }
         }
       else
@@ -297,7 +298,7 @@ start_dirmngr (ctrl_t ctrl)
         {
           log_info (_("can't connect to the dirmngr - trying fall back\n"));
           force_pipe_server = 1;
-          return start_dirmngr (ctrl);
+          return start_dirmngr_ext (ctrl, ctx_r);
         }
 #endif /*!HAVE_W32_SYSTEM*/
     }
@@ -309,7 +310,7 @@ start_dirmngr (ctrl_t ctrl)
       log_error ("can't connect to the dirmngr: %s\n", gpg_strerror (rc));
       return gpg_error (GPG_ERR_NO_DIRMNGR);
     }
-  dirmngr_ctx = ctx;
+  *ctx_r = ctx;
 
   if (DBG_ASSUAN)
     log_debug ("connection to dirmngr established\n");
@@ -317,6 +318,62 @@ start_dirmngr (ctrl_t ctrl)
 }
 
 
+static int
+start_dirmngr (ctrl_t ctrl)
+{
+  gpg_error_t err;
+
+  assert (! dirmngr_ctx_locked);
+  dirmngr_ctx_locked = 1;
+
+  err = start_dirmngr_ext (ctrl, &dirmngr_ctx);
+  /* We do not check ERR but the existance of a context because the
+     error might come from a failed command send to the dirmngr.
+     Fixme: Why don't we close the drimngr context if we encountered
+     an error in prepare_dirmngr?  */
+  if (!dirmngr_ctx)
+    dirmngr_ctx_locked = 0;
+  return err;  
+}
+
+
+static void
+release_dirmngr (ctrl_t ctrl)
+{
+  (void)ctrl;
+
+  if (!dirmngr_ctx_locked)
+    log_error ("WARNING: trying to release a non-locked dirmngr ctx\n");
+  dirmngr_ctx_locked = 0;
+}
+
+
+static int
+start_dirmngr2 (ctrl_t ctrl)
+{
+  gpg_error_t err;
+
+  assert (! dirmngr2_ctx_locked);
+  dirmngr2_ctx_locked = 1;
+
+  err = start_dirmngr_ext (ctrl, &dirmngr2_ctx);
+  if (!dirmngr2_ctx)
+    dirmngr2_ctx_locked = 0;
+  return err;
+}
+
+
+static void
+release_dirmngr2 (ctrl_t ctrl)
+{
+  (void)ctrl;
+
+  if (!dirmngr2_ctx_locked)
+    log_error ("WARNING: trying to release a non-locked dirmngr2 ctx\n");
+  dirmngr2_ctx_locked = 0;
+}
+
+
 \f
 /* Handle a SENDCERT inquiry. */
 static int
@@ -352,6 +409,33 @@ inq_certificate (void *opaque, const char *line)
       line += 14;
       issuer_mode = 1;
     }
+  else if (!strncmp (line, "ISTRUSTED", 9) && (line[9]==' ' || !line[9]))
+    {
+      /* The server is asking us whether the certificate is a trusted
+         root certificate.  */
+      const char *s;
+      size_t n;
+      char fpr[41];
+      struct rootca_flags_s rootca_flags;
+
+      line += 9;
+      while (*line == ' ')
+        line++;
+
+      for (s=line,n=0; hexdigitp (s); s++, n++)
+        ;
+      if (*s || n != 40)
+        return gpg_error (GPG_ERR_ASS_PARAMETER);
+      for (s=line, n=0; n < 40; s++, n++)
+        fpr[n] = (*s >= 'a')? (*s & 0xdf): *s;
+      fpr[n] = 0;
+      
+      if (!gpgsm_agent_istrusted (parm->ctrl, NULL, fpr, &rootca_flags))
+        rc = assuan_send_data (parm->ctx, "1", 1);
+      else
+        rc = 0;
+      return rc;
+    }
   else
     {
       log_error ("unsupported inquiry `%s'\n", line);
@@ -485,6 +569,7 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl,
       if (!certid)
         {
           log_error ("error getting the certificate ID\n");
+         release_dirmngr (ctrl);
           return gpg_error (GPG_ERR_GENERAL);
         }
     }
@@ -498,6 +583,7 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl,
     }
 
   parm.ctx = dirmngr_ctx;
+  parm.ctrl = ctrl;
   parm.cert = cert;
   parm.issuer_cert = issuer_cert;
 
@@ -584,6 +670,7 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl,
           ksba_cert_release (rspcert);
         }
     }
+  release_dirmngr (ctrl);
   return rc;
 }
 
@@ -660,7 +747,7 @@ pattern_from_strlist (strlist_t names)
   if (!pattern)
     return NULL;
 
-  for (n=0, sl=names; sl; sl = sl->next)
+  for (sl=names; sl; sl = sl->next)
     {
       for (s=sl->d; *s; s++)
         {
@@ -738,31 +825,62 @@ gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, int cache_only,
   char line[ASSUAN_LINELENGTH];
   struct lookup_parm_s parm;
   size_t len;
+  assuan_context_t ctx;
 
-  rc = start_dirmngr (ctrl);
-  if (rc)
-    return rc;
+  /* The lookup function can be invoked from the callback of a lookup
+     function, for example to walk the chain.  */
+  if (!dirmngr_ctx_locked)
+    {
+      rc = start_dirmngr (ctrl);
+      if (rc)
+       return rc;
+      ctx = dirmngr_ctx;
+    }
+  else if (!dirmngr2_ctx_locked)
+    {
+      rc = start_dirmngr2 (ctrl);
+      if (rc)
+       return rc;
+      ctx = dirmngr2_ctx;
+    }
+  else
+    {
+      log_fatal ("both dirmngr contexts are in use\n");
+    }
 
   pattern = pattern_from_strlist (names);
   if (!pattern)
-    return out_of_core ();
+    {
+      if (ctx == dirmngr_ctx)
+       release_dirmngr (ctrl);
+      else
+       release_dirmngr2 (ctrl);
+
+      return out_of_core ();
+    }
   snprintf (line, DIM(line)-1, "LOOKUP%s %s", 
             cache_only? " --cache-only":"", pattern);
   line[DIM(line)-1] = 0;
   xfree (pattern);
 
   parm.ctrl = ctrl;
-  parm.ctx = dirmngr_ctx;
+  parm.ctx = ctx;
   parm.cb = cb;
   parm.cb_value = cb_value;
   parm.error = 0;
   init_membuf (&parm.data, 4096);
 
-  rc = assuan_transact (dirmngr_ctx, line, lookup_cb, &parm,
+  rc = assuan_transact (ctx, line, lookup_cb, &parm,
                         NULL, NULL, lookup_status_cb, &parm);
   xfree (get_membuf (&parm.data, &len));
+
+  if (ctx == dirmngr_ctx)
+    release_dirmngr (ctrl);
+  else
+    release_dirmngr2 (ctrl);
+
   if (rc)
-    return rc;
+      return rc;
   return parm.error;
 }
 
@@ -774,6 +892,8 @@ gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, int cache_only,
 static int
 run_command_cb (void *opaque, const void *buffer, size_t length)
 {
+  (void)opaque;
+
   if (buffer)
     {
       if ( fwrite (buffer, length, 1, stdout) != 1 )
@@ -881,7 +1001,10 @@ gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command,
     len += 1 + 3*strlen (argv[i]); /* enough space for percent escaping */
   line = xtrymalloc (len);
   if (!line)
-    return out_of_core ();
+    {
+      release_dirmngr (ctrl);
+      return out_of_core ();
+    }
 
   p = stpcpy (line, command);
   for (i=0; i < argc; i++)
@@ -910,5 +1033,6 @@ gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command,
                         run_command_status_cb, ctrl);
   xfree (line);
   log_info ("response of dirmngr: %s\n", rc? gpg_strerror (rc): "okay");
+  release_dirmngr (ctrl);
   return rc;
 }