dirmngr: Fix memory leak.
[gnupg.git] / dirmngr / server.c
index 6d7723a..6094bc9 100644 (file)
@@ -1,15 +1,16 @@
-/* dirmngr.c - LDAP access
- *     Copyright (C) 2002 Klarälvdalens Datakonsult AB
- *      Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2011 g10 Code GmbH
+/* server.c - LDAP and Keyserver access server
+ * Copyright (C) 2002 Klarälvdalens Datakonsult AB
+ * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2011 g10 Code GmbH
+ * Copyright (C) 2014 Werner Koch
  *
- * This file is part of DirMngr.
+ * This file is part of GnuPG.
  *
- * DirMngr is free software; you can redistribute it and/or modify
+ * 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
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
- * DirMngr is distributed in the hope that it will be useful,
+ * 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.
 
 #include "crlcache.h"
 #include "crlfetch.h"
-#include "ldapserver.h"
+#if USE_LDAP
+# include "ldapserver.h"
+#endif
 #include "ocsp.h"
 #include "certcache.h"
 #include "validate.h"
 #include "misc.h"
-#include "ldap-wrapper.h"
+#if USE_LDAP
+# include "ldap-wrapper.h"
+#endif
 #include "ks-action.h"
 #include "ks-engine.h"  /* (ks_hkp_print_hosttable) */
 
@@ -120,6 +125,7 @@ release_ctrl_keyservers (ctrl_t ctrl)
 /* Helper to print a message while leaving a command.  */
 static gpg_error_t
 leave_cmd (assuan_context_t ctx, gpg_error_t err)
+
 {
   if (err)
     {
@@ -139,14 +145,44 @@ leave_cmd (assuan_context_t ctx, gpg_error_t err)
 /* A write handler used by es_fopencookie to write assuan data
    lines.  */
 static ssize_t
-data_line_cookie_write (void *cookie, const void *buffer, size_t size)
+data_line_cookie_write (void *cookie, const void *buffer_arg, size_t size)
 {
   assuan_context_t ctx = cookie;
+  const char *buffer = buffer_arg;
 
-  if (assuan_send_data (ctx, buffer, size))
+  if (opt.verbose && buffer && size)
     {
-      gpg_err_set_errno (EIO);
-      return -1;
+      /* Ease reading of output by sending a physical line at each LF.  */
+      const char *p;
+      size_t n, nbytes;
+
+      nbytes = size;
+      do
+        {
+          p = memchr (buffer, '\n', nbytes);
+          n = p ? (p - buffer) + 1 : nbytes;
+          if (assuan_send_data (ctx, buffer, n))
+            {
+              gpg_err_set_errno (EIO);
+              return -1;
+            }
+          buffer += n;
+          nbytes -= n;
+          if (nbytes && assuan_send_data (ctx, NULL, 0)) /* Flush line. */
+            {
+              gpg_err_set_errno (EIO);
+              return -1;
+            }
+        }
+      while (nbytes);
+    }
+  else
+    {
+      if (assuan_send_data (ctx, buffer, size))
+        {
+          gpg_err_set_errno (EIO);
+          return -1;
+        }
     }
 
   return size;
@@ -266,6 +302,32 @@ skip_options (char *line)
 }
 
 
+/* Return an error if the assuan context does not belong to the owner
+   of the process or to root.  On error FAILTEXT is set as Assuan
+   error string.  */
+static gpg_error_t
+check_owner_permission (assuan_context_t ctx, const char *failtext)
+{
+#ifdef HAVE_W32_SYSTEM
+  /* Under Windows the dirmngr is always run under the control of the
+     user.  */
+  (void)ctx;
+  (void)failtext;
+#else
+  gpg_err_code_t ec;
+  assuan_peercred_t cred;
+
+  ec = gpg_err_code (assuan_get_peercred (ctx, &cred));
+  if (!ec && cred->uid && cred->uid != getuid ())
+    ec = GPG_ERR_EPERM;
+  if (ec)
+    return set_error (ec, failtext);
+#endif
+  return 0;
+}
+
+
+
 /* Common code for get_cert_local and get_issuer_cert_local. */
 static ksba_cert_t
 do_get_cert_local (ctrl_t ctrl, const char *name, const char *command)
@@ -537,6 +599,7 @@ static const char hlp_ldapserver[] =
 static gpg_error_t
 cmd_ldapserver (assuan_context_t ctx, char *line)
 {
+#if USE_LDAP
   ctrl_t ctrl = assuan_get_pointer (ctx);
   ldap_server_t server;
   ldap_server_t *last_next_p;
@@ -555,6 +618,10 @@ cmd_ldapserver (assuan_context_t ctx, char *line)
     last_next_p = &(*last_next_p)->next;
   *last_next_p = server;
   return leave_cmd (ctx, 0);
+#else
+  (void)line;
+  return leave_cmd (ctx, gpg_error (GPG_ERR_NOT_IMPLEMENTED));
+#endif
 }
 
 
@@ -933,17 +1000,19 @@ static int
 lookup_cert_by_pattern (assuan_context_t ctx, char *line,
                         int single, int cache_only)
 {
-  ctrl_t ctrl = assuan_get_pointer (ctx);
   gpg_error_t err = 0;
   char *p;
   strlist_t sl, list = NULL;
   int truncated = 0, truncation_forced = 0;
   int count = 0;
   int local_count = 0;
+#if USE_LDAP
+  ctrl_t ctrl = assuan_get_pointer (ctx);
   unsigned char *value = NULL;
   size_t valuelen;
   struct ldapserver_iter ldapserver_iter;
   cert_fetch_context_t fetch_context;
+#endif /*USE_LDAP*/
   int any_no_data = 0;
 
   /* Break the line down into an STRLIST */
@@ -1002,6 +1071,7 @@ lookup_cert_by_pattern (assuan_context_t ctx, char *line,
 
   /* Loop over all configured servers unless we want only the
      certificates from the cache.  */
+#if USE_LDAP
   for (ldapserver_iter_begin (&ldapserver_iter, ctrl);
        !cache_only && !ldapserver_iter_end_p (&ldapserver_iter)
         && ldapserver_iter.server->host && !truncation_forced;
@@ -1094,6 +1164,7 @@ lookup_cert_by_pattern (assuan_context_t ctx, char *line,
 
       end_cert_fetch (fetch_context);
     }
+#endif /*USE_LDAP*/
 
  ready:
   if (truncated || truncation_forced)
@@ -1360,10 +1431,16 @@ cmd_validate (assuan_context_t ctx, char *line)
 
 \f
 static const char hlp_keyserver[] =
-  "KEYSERVER [--clear|--help] [<uri>]\n"
+  "KEYSERVER [<options>] [<uri>|<host>]\n"
+  "Options are:\n"
+  "  --help\n"
+  "  --clear      Remove all configured keyservers\n"
+  "  --resolve    Resolve HKP host names and rotate\n"
+  "  --hosttable  Print table of known hosts and pools\n"
+  "  --dead       Mark <host> as dead\n"
+  "  --alive      Mark <host> as alive\n"
   "\n"
   "If called without arguments list all configured keyserver URLs.\n"
-  "If called with option \"--clear\" remove all configured keyservers\n"
   "If called with an URI add this as keyserver.  Note that keyservers\n"
   "are configured on a per-session base.  A default keyserver may already be\n"
   "present, thus the \"--clear\" option must be used to get full control.\n"
@@ -1374,14 +1451,18 @@ static gpg_error_t
 cmd_keyserver (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
-  gpg_error_t err;
-  int clear_flag, add_flag, help_flag, host_flag;
+  gpg_error_t err = 0;
+  int clear_flag, add_flag, help_flag, host_flag, resolve_flag;
+  int dead_flag, alive_flag;
   uri_item_t item = NULL; /* gcc 4.4.5 is not able to detect that it
                              is always initialized.  */
 
   clear_flag = has_option (line, "--clear");
   help_flag = has_option (line, "--help");
-  host_flag = has_option (line, "--print-hosttable");
+  resolve_flag = has_option (line, "--resolve");
+  host_flag = has_option (line, "--hosttable");
+  dead_flag = has_option (line, "--dead");
+  alive_flag = has_option (line, "--alive");
   line = skip_options (line);
   add_flag = !!*line;
 
@@ -1391,12 +1472,45 @@ cmd_keyserver (assuan_context_t ctx, char *line)
       goto leave;
     }
 
-  if (host_flag)
+  if (resolve_flag)
+    {
+      err = ks_action_resolve (ctrl);
+      if (err)
+        goto leave;
+    }
+
+  if (alive_flag && dead_flag)
     {
-      ks_hkp_print_hosttable ();
-      err = 0;
+      err = set_error (GPG_ERR_ASS_PARAMETER, "no support for zombies");
       goto leave;
     }
+  if (dead_flag)
+    {
+      err = check_owner_permission (ctx, "no permission to use --dead");
+      if (err)
+        goto leave;
+    }
+  if (alive_flag || dead_flag)
+    {
+      if (!*line)
+        {
+          err = set_error (GPG_ERR_ASS_PARAMETER, "name of host missing");
+          goto leave;
+        }
+
+      err = ks_hkp_mark_host (ctrl, line, alive_flag);
+      if (err)
+        goto leave;
+    }
+
+  if (host_flag)
+    {
+      err = ks_hkp_print_hosttable (ctrl);
+      if (err)
+        goto leave;
+    }
+  if (resolve_flag || host_flag || alive_flag || dead_flag)
+    goto leave;
 
   if (add_flag)
     {
@@ -1472,7 +1586,6 @@ cmd_ks_search (assuan_context_t ctx, char *line)
           if (!sl)
             {
               err = gpg_error_from_syserror ();
-              free_strlist (list);
               goto leave;
             }
           sl->flags = 0;
@@ -1493,6 +1606,7 @@ cmd_ks_search (assuan_context_t ctx, char *line)
     }
 
  leave:
+  free_strlist (list);
   return leave_cmd (ctx, err);
 }
 
@@ -1502,7 +1616,8 @@ static const char hlp_ks_get[] =
   "KS_GET {<pattern>}\n"
   "\n"
   "Get the keys matching PATTERN from the configured OpenPGP keyservers\n"
-  "(see command KEYSERVER).  Each pattern should be a keyid or a fingerprint";
+  "(see command KEYSERVER).  Each pattern should be a keyid, a fingerprint,\n"
+  "or an exact name indicastes by the '=' prefix.";
 static gpg_error_t
 cmd_ks_get (assuan_context_t ctx, char *line)
 {
@@ -1532,7 +1647,6 @@ cmd_ks_get (assuan_context_t ctx, char *line)
           if (!sl)
             {
               err = gpg_error_from_syserror ();
-              free_strlist (list);
               goto leave;
             }
           sl->flags = 0;
@@ -1553,6 +1667,7 @@ cmd_ks_get (assuan_context_t ctx, char *line)
     }
 
  leave:
+  free_strlist (list);
   return leave_cmd (ctx, err);
 }
 
@@ -1680,7 +1795,10 @@ cmd_getinfo (assuan_context_t ctx, char *line)
     }
   else if (!strcmp (line, "socket_name"))
     {
-      const char *s = dirmngr_socket_name ();
+      const char *s = dirmngr_user_socket_name ();
+
+      if (!s)
+        s = dirmngr_sys_socket_name ();
 
       if (s)
         err = assuan_send_data (ctx, s, strlen (s));
@@ -1704,30 +1822,28 @@ static gpg_error_t
 cmd_killdirmngr (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
 
   (void)line;
 
   if (opt.system_daemon)
     {
       if (opt.system_service)
-        return set_error (GPG_ERR_NOT_SUPPORTED,
-                          "can't do that whilst running as system service");
-#ifndef HAVE_W32_SYSTEM
-      {
-        gpg_err_code_t ec;
-        assuan_peercred_t cred;
-
-        ec = gpg_err_code (assuan_get_peercred (ctx, &cred));
-        if (!ec && cred->uid)
-          ec = GPG_ERR_EPERM; /* Only root may terminate.  */
-        if (ec)
-          return set_error (ec, "no permission to kill this process");
-      }
-#endif
+        err = set_error (GPG_ERR_NOT_SUPPORTED,
+                         "can't do that whilst running as system service");
+      else
+        err = check_owner_permission (ctx,
+                                      "no permission to kill this process");
     }
+  else
+    err = 0;
 
-  ctrl->server_local->stopme = 1;
-  return gpg_error (GPG_ERR_EOF);
+  if (!err)
+    {
+      ctrl->server_local->stopme = 1;
+      err = gpg_error (GPG_ERR_EOF);
+    }
+  return err;
 }
 
 
@@ -1813,7 +1929,9 @@ reset_notify (assuan_context_t ctx, char *line)
   ctrl_t ctrl = assuan_get_pointer (ctx);
   (void)line;
 
+#if USE_LDAP
   ldapserver_list_free (ctrl->server_local->ldapservers);
+#endif /*USE_LDAP*/
   ctrl->server_local->ldapservers = NULL;
   return 0;
 }
@@ -1939,9 +2057,11 @@ start_command_handler (assuan_fd_t fd)
         }
     }
 
+#if USE_LDAP
   ldap_wrapper_connection_cleanup (ctrl);
 
   ldapserver_list_free (ctrl->server_local->ldapservers);
+#endif /*USE_LDAP*/
   ctrl->server_local->ldapservers = NULL;
 
   ctrl->server_local->assuan_ctx = NULL;
@@ -2031,7 +2151,7 @@ dirmngr_status_help (ctrl_t ctrl, const char *text)
   return err;
 }
 
-/* Send a tick progress indicator back.  Fixme: This is only does for
+/* Send a tick progress indicator back.  Fixme: This is only done for
    the currently active channel.  */
 gpg_error_t
 dirmngr_tick (ctrl_t ctrl)