Imported Upstream version 2.2.6
[platform/upstream/gpg2.git] / tools / gpgconf-comp.c
index 4dd10a4..b10b146 100644 (file)
@@ -1,6 +1,6 @@
 /* gpgconf-comp.c - Configuration utility for GnuPG.
- * Copyright (C) 2004, 2007, 2008, 2009, 2010,
- *               2011 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2007-2011 Free Software Foundation, Inc.
+ * Copyright (C) 2016 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +15,7 @@
  * General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with GnuPG; if not, see <http://www.gnu.org/licenses/>.
+ * along with GnuPG; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #if HAVE_CONFIG_H
 #endif
 
 /* For log_logv(), asctimestamp(), gnupg_get_time ().  */
-#include "util.h"
-#include "i18n.h"
-#include "exechelp.h"
+#include "../common/util.h"
+#include "../common/i18n.h"
+#include "../common/exechelp.h"
+#include "../common/sysutils.h"
+#include "../common/status.h"
 
-#include "gc-opt-flags.h"
+#include "../common/gc-opt-flags.h"
 #include "gpgconf.h"
 
 /* There is a problem with gpg 1.4 under Windows: --gpgconf-list
@@ -98,7 +100,7 @@ gc_error (int status, int errnum, const char *fmt, ...)
     {
       log_printf (NULL);
       log_printf ("fatal error (exit status %i)\n", status);
-      exit (status);
+      gpgconf_failure (gpg_error_from_errno (errnum));
     }
 }
 
@@ -148,7 +150,7 @@ typedef enum
 
 /* To be able to implement generic algorithms for the various
    backends, we collect all information about them in this struct.  */
-static struct
+static const struct
 {
   /* The name of the backend.  */
   const char *name;
@@ -255,7 +257,7 @@ typedef enum
 
 /* For every argument, we record some information about it in the
    following struct.  */
-static struct
+static const struct
 {
   /* For every argument type exists a basic argument type that can be
      used as a fallback for input and validation purposes.  */
@@ -327,7 +329,7 @@ typedef enum
   } gc_expert_level_t;
 
 /* A description for each expert level.  */
-static struct
+static const struct
 {
   const char *name;
 } gc_level[] =
@@ -360,7 +362,7 @@ static struct
 
 
 /* A human-readable description for each flag.  */
-static struct
+static const struct
 {
   const char *name;
 } gc_flag[] =
@@ -499,9 +501,16 @@ static gc_option_t gc_options_gpg_agent[] =
    { "enable-ssh-support", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
      "gnupg", "enable ssh support",
      GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+   { "ssh-fingerprint-digest",
+     GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT,
+     "gnupg", "|ALGO|use ALGO to show ssh fingerprints",
+     GC_ARG_TYPE_STRING, GC_BACKEND_GPG_AGENT },
    { "enable-putty-support", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
      "gnupg", "enable putty support",
      GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+   { "enable-extended-key-format", GC_OPT_FLAG_RUNTIME, GC_LEVEL_INVISIBLE,
+     NULL, NULL,
+     GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
 
    { "Debug",
      GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
@@ -542,6 +551,9 @@ static gc_option_t gc_options_gpg_agent[] =
      GC_LEVEL_ADVANCED,
      "gnupg", "allow passphrase to be prompted through Emacs",
      GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+   { "grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT,
+     "gnupg", NULL,
+     GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
    { "no-allow-external-cache", GC_OPT_FLAG_RUNTIME,
      GC_LEVEL_BASIC, "gnupg", "disallow the use of an external password cache",
      GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
@@ -551,16 +563,13 @@ static gc_option_t gc_options_gpg_agent[] =
    { "no-allow-loopback-pinentry", GC_OPT_FLAG_RUNTIME,
      GC_LEVEL_EXPERT, "gnupg", "disallow caller to override the pinentry",
      GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
-   { "no-grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT,
-     "gnupg", "do not grab keyboard and mouse",
-     GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
 
    { "Passphrase policy",
      GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
      "gnupg", N_("Options enforcing a passphrase policy") },
    { "enforce-passphrase-constraints", GC_OPT_FLAG_RUNTIME,
      GC_LEVEL_EXPERT, "gnupg",
-     N_("do not allow to bypass the passphrase policy"),
+     N_("do not allow bypassing the passphrase policy"),
      GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
    { "min-passphrase-len", GC_OPT_FLAG_RUNTIME,
      GC_LEVEL_ADVANCED, "gnupg",
@@ -703,13 +712,23 @@ static gc_option_t gc_options_gpg[] =
    { "group", GC_OPT_FLAG_LIST, GC_LEVEL_ADVANCED,
      "gnupg", N_("|SPEC|set up email aliases"),
      GC_ARG_TYPE_ALIAS_LIST, GC_BACKEND_GPG },
-   { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
-     "gnupg", "|FILE|read options from FILE",
+   { "options", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+     NULL, NULL,
      GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG },
+   { "compliance", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+     NULL, NULL,
+     GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
+   { "default-new-key-algo", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+     NULL, NULL,
+     GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
    { "default_pubkey_algo",
      (GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE,
      NULL, NULL,
      GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
+   { "trust-model",
+     GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+     NULL, NULL,
+     GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
 
 
    { "Debug",
@@ -737,6 +756,25 @@ static gc_option_t gc_options_gpg[] =
    { "auto-key-locate", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
      "gnupg", N_("|MECHANISMS|use MECHANISMS to locate keys by mail address"),
      GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
+   { "auto-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+     NULL, NULL, GC_ARG_TYPE_NONE, GC_BACKEND_GPG },
+   { "no-auto-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+     NULL, NULL, GC_ARG_TYPE_NONE, GC_BACKEND_GPG },
+   { "disable-dirmngr", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+     "gnupg", N_("disable all access to the dirmngr"),
+     GC_ARG_TYPE_NONE, GC_BACKEND_GPG },
+   { "max-cert-depth",
+     GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+     NULL, NULL,
+     GC_ARG_TYPE_UINT32, GC_BACKEND_GPG },
+   { "completes-needed",
+     GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+     NULL, NULL,
+     GC_ARG_TYPE_UINT32, GC_BACKEND_GPG },
+   { "marginals-needed",
+     GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+     NULL, NULL,
+     GC_ARG_TYPE_UINT32, GC_BACKEND_GPG },
 
 
    GC_OPTION_NULL
@@ -796,6 +834,9 @@ static gc_option_t gc_options_gpgsm[] =
      (GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE,
      NULL, NULL,
      GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM },
+   { "compliance", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+     NULL, NULL,
+     GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM },
 
    { "Debug",
      GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
@@ -816,6 +857,9 @@ static gc_option_t gc_options_gpgsm[] =
    { "disable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
      "gnupg", "never consult a CRL",
      GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+   { "enable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+     NULL, NULL,
+     GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
    { "disable-trusted-cert-crl-check", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
      "gnupg", N_("do not check CRLs for root certificates"),
      GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
@@ -880,6 +924,12 @@ static gc_option_t gc_options_dirmngr[] =
    { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
      "dirmngr", "|FILE|read options from FILE",
      GC_ARG_TYPE_FILENAME, GC_BACKEND_DIRMNGR },
+   { "resolver-timeout", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+     NULL, NULL,
+     GC_ARG_TYPE_INT32, GC_BACKEND_DIRMNGR },
+   { "nameserver", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+     NULL, NULL,
+     GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR },
 
    { "Debug",
      GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
@@ -909,11 +959,14 @@ static gc_option_t gc_options_dirmngr[] =
    { "force", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
      "dirmngr", "force loading of outdated CRLs",
      GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+   { "allow-version-check", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+     "dirmngr", "allow online software version check",
+     GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
 
    { "Tor",
      GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
      "gnupg", N_("Options controlling the use of Tor") },
-   { "use-tor", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+   { "use-tor", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
      "dirmngr", "route all network traffic via TOR",
       GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
 
@@ -1042,7 +1095,7 @@ typedef enum
 
 
 /* The information associated with each component.  */
-static struct
+static const struct
 {
   /* The name of this component.  Must not contain a colon (':')
      character.  */
@@ -1060,12 +1113,12 @@ static struct
   gc_option_t *options;
 } gc_component[] =
   {
-    { "gpg",      "gnupg", N_("GPG for OpenPGP"), gc_options_gpg },
-    { "gpg-agent","gnupg", N_("GPG Agent"), gc_options_gpg_agent },
-    { "scdaemon", "gnupg", N_("Smartcard Daemon"), gc_options_scdaemon },
-    { "gpgsm",    "gnupg", N_("GPG for S/MIME"), gc_options_gpgsm },
-    { "dirmngr",  "gnupg", N_("Key Acquirer"), gc_options_dirmngr },
-    { "pinentry", "gnupg", N_("PIN and Passphrase Entry"), gc_options_pinentry }
+    { "gpg",      "gnupg", N_("OpenPGP"), gc_options_gpg },
+    { "gpg-agent","gnupg", N_("Private Keys"), gc_options_gpg_agent },
+    { "scdaemon", "gnupg", N_("Smartcards"), gc_options_scdaemon },
+    { "gpgsm",    "gnupg", N_("S/MIME"), gc_options_gpgsm },
+    { "dirmngr",  "gnupg", N_("Network"), gc_options_dirmngr },
+    { "pinentry", "gnupg", N_("Passphrase Entry"), gc_options_pinentry }
   };
 
 
@@ -1084,37 +1137,81 @@ struct error_line_s
 
 
 \f
+
+/* Initialization and finalization.  */
+
+static void
+gc_option_free (gc_option_t *o)
+{
+  if (o == NULL || o->name == NULL)
+    return;
+
+  xfree (o->value);
+  gc_option_free (o + 1);
+}
+
+static void
+gc_components_free (void)
+{
+  int i;
+  for (i = 0; i < DIM (gc_component); i++)
+    gc_option_free (gc_component[i].options);
+}
+
+void
+gc_components_init (void)
+{
+  atexit (gc_components_free);
+}
+
+\f
+
 /* Engine specific support.  */
 static void
 gpg_agent_runtime_change (int killflag)
 {
-  gpg_error_t err;
+  gpg_error_t err = 0;
   const char *pgmname;
-  const char *argv[3];
-  pid_t pid;
+  const char *argv[5];
+  pid_t pid = (pid_t)(-1);
+  char *abs_homedir = NULL;
+  int i = 0;
 
   pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
-  argv[0] = "--no-autostart";
-  argv[1] = killflag? "KILLAGENT" : "RELOADAGENT";
-  argv[2] = NULL;
+  if (!gnupg_default_homedir_p ())
+    {
+      abs_homedir = make_absfilename_try (gnupg_homedir (), NULL);
+      if (!abs_homedir)
+        err = gpg_error_from_syserror ();
 
-  err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+      argv[i++] = "--homedir";
+      argv[i++] = abs_homedir;
+    }
+  argv[i++] = "--no-autostart";
+  argv[i++] = killflag? "KILLAGENT" : "RELOADAGENT";
+  argv[i++] = NULL;
+
+  if (!err)
+    err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
   if (!err)
     err = gnupg_wait_process (pgmname, pid, 1, NULL);
   if (err)
     gc_error (0, 0, "error running '%s %s': %s",
               pgmname, argv[1], gpg_strerror (err));
   gnupg_release_process (pid);
+  xfree (abs_homedir);
 }
 
 
 static void
 scdaemon_runtime_change (int killflag)
 {
-  gpg_error_t err;
+  gpg_error_t err = 0;
   const char *pgmname;
-  const char *argv[7];
-  pid_t pid;
+  const char *argv[9];
+  pid_t pid = (pid_t)(-1);
+  char *abs_homedir = NULL;
+  int i = 0;
 
   (void)killflag;  /* For scdaemon kill and reload are synonyms.  */
 
@@ -1124,45 +1221,70 @@ scdaemon_runtime_change (int killflag)
      obviously a race condition but that should not harm too much.  */
 
   pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
-  argv[0] = "-s";
-  argv[1] = "--no-autostart";
-  argv[2] = "GETINFO scd_running";
-  argv[3] = "/if ${! $?}";
-  argv[4] = "scd killscd";
-  argv[5] = "/end";
-  argv[6] = NULL;
+  if (!gnupg_default_homedir_p ())
+    {
+      abs_homedir = make_absfilename_try (gnupg_homedir (), NULL);
+      if (!abs_homedir)
+        err = gpg_error_from_syserror ();
+
+      argv[i++] = "--homedir";
+      argv[i++] = abs_homedir;
+    }
+  argv[i++] = "-s";
+  argv[i++] = "--no-autostart";
+  argv[i++] = "GETINFO scd_running";
+  argv[i++] = "/if ${! $?}";
+  argv[i++] = "scd killscd";
+  argv[i++] = "/end";
+  argv[i++] = NULL;
 
-  err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+  if (!err)
+    err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
   if (!err)
     err = gnupg_wait_process (pgmname, pid, 1, NULL);
   if (err)
     gc_error (0, 0, "error running '%s %s': %s",
               pgmname, argv[4], gpg_strerror (err));
   gnupg_release_process (pid);
+  xfree (abs_homedir);
 }
 
 
 static void
 dirmngr_runtime_change (int killflag)
 {
-  gpg_error_t err;
+  gpg_error_t err = 0;
   const char *pgmname;
-  const char *argv[4];
-  pid_t pid;
+  const char *argv[6];
+  pid_t pid = (pid_t)(-1);
+  char *abs_homedir = NULL;
 
   pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
   argv[0] = "--no-autostart";
   argv[1] = "--dirmngr";
   argv[2] = killflag? "KILLDIRMNGR" : "RELOADDIRMNGR";
-  argv[3] = NULL;
+  if (gnupg_default_homedir_p ())
+    argv[3] = NULL;
+  else
+    {
+      abs_homedir = make_absfilename_try (gnupg_homedir (), NULL);
+      if (!abs_homedir)
+        err = gpg_error_from_syserror ();
 
-  err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+      argv[3] = "--homedir";
+      argv[4] = abs_homedir;
+      argv[5] = NULL;
+    }
+
+  if (!err)
+    err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
   if (!err)
     err = gnupg_wait_process (pgmname, pid, 1, NULL);
   if (err)
     gc_error (0, 0, "error running '%s %s': %s",
               pgmname, argv[2], gpg_strerror (err));
   gnupg_release_process (pid);
+  xfree (abs_homedir);
 }
 
 
@@ -1176,12 +1298,20 @@ gc_component_launch (int component)
   int i;
   pid_t pid;
 
+  if (component < 0)
+    {
+      err = gc_component_launch (GC_COMPONENT_GPG_AGENT);
+      if (!err)
+        err = gc_component_launch (GC_COMPONENT_DIRMNGR);
+      return err;
+    }
+
   if (!(component == GC_COMPONENT_GPG_AGENT
         || component == GC_COMPONENT_DIRMNGR))
     {
       es_fputs (_("Component not suitable for launching"), es_stderr);
       es_putc ('\n', es_stderr);
-      exit (1);
+      gpgconf_failure (0);
     }
 
   pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
@@ -1217,7 +1347,16 @@ gc_component_kill (int component)
   for (backend = 0; backend < GC_BACKEND_NR; backend++)
     runtime[backend] = 0;
 
-  if (component >= 0)
+  if (component < 0)
+    {
+      for (component = 0; component < GC_COMPONENT_NR; component++)
+        {
+          option = gc_component[component].options;
+          for (; option && option->name; option++)
+            runtime[option->backend] = 1;
+        }
+    }
+  else
     {
       assert (component < GC_COMPONENT_NR);
       option = gc_component[component].options;
@@ -1246,7 +1385,7 @@ gc_component_reload (int component)
   for (backend = 0; backend < GC_BACKEND_NR; backend++)
     runtime[backend] = 0;
 
-  if (component == -1)
+  if (component < 0)
     {
       for (component = 0; component < GC_COMPONENT_NR; component++)
         {
@@ -1275,7 +1414,7 @@ gc_component_reload (int component)
 \f
 /* More or less Robust version of dgettext.  It has the side effect of
    switching the codeset to utf-8 because this is what we want to
-   output.  In theory it is posible to keep the original code set and
+   output.  In theory it is possible to keep the original code set and
    switch back for regular disgnostic output (redefine "_(" for that)
    but given the natur of this tool, being something invoked from
    other pograms, it does not make much sense.  */
@@ -1380,6 +1519,13 @@ gc_percent_escape (const char *src)
          *(dst++) = '2';
          *(dst++) = 'c';
        }
+      else if (*src == '\n')
+       {
+         /* The newline is problematic in a line-based format.  */
+         *(dst++) = '%';
+         *(dst++) = '0';
+         *(dst++) = 'a';
+       }
       else
        *(dst++) = *(src);
       src++;
@@ -1626,7 +1772,7 @@ gc_component_check_options (int component, estream_t out, const char *conf_file)
 
   result = 0;
   errlines = NULL;
-  err = gnupg_spawn_process (pgmname, argv, GPG_ERR_SOURCE_DEFAULT, NULL, 0,
+  err = gnupg_spawn_process (pgmname, argv, NULL, NULL, 0,
                              NULL, NULL, &errfp, &pid);
   if (err)
     result |= 1; /* Program could not be run.  */
@@ -1930,7 +2076,9 @@ get_config_filename (gc_component_t component, gc_backend_t backend)
 #elif defined(HAVE_DOSISH_SYSTEM)
   if (!(filename[0]
         && filename[1] == ':'
-        && (filename[2] == '/' || filename[2] == '\\')))
+        && (filename[2] == '/' || filename[2] == '\\')) /* x:\ or x:/ */
+      && !((filename[0] == '\\' && filename[1] == '\\')
+           || (filename[0] == '/' && filename[1] == '/'))) /* \\server */
 #else
   if (filename[0] != '/')
 #endif
@@ -1943,9 +2091,12 @@ get_config_filename (gc_component_t component, gc_backend_t backend)
 
 \f
 /* Retrieve the options for the component COMPONENT from backend
-   BACKEND, which we already know is a program-type backend.  */
+ * BACKEND, which we already know is a program-type backend.  With
+ * ONLY_INSTALLED set components which are not installed are silently
+ * ignored. */
 static void
-retrieve_options_from_program (gc_component_t component, gc_backend_t backend)
+retrieve_options_from_program (gc_component_t component, gc_backend_t backend,
+                               int only_installed)
 {
   gpg_error_t err;
   const char *pgmname;
@@ -1965,7 +2116,12 @@ retrieve_options_from_program (gc_component_t component, gc_backend_t backend)
   argv[0] = "--gpgconf-list";
   argv[1] = NULL;
 
-  err = gnupg_spawn_process (pgmname, argv, GPG_ERR_SOURCE_DEFAULT, NULL, 0,
+  if (only_installed && access (pgmname, X_OK))
+    {
+      return;  /* The component is not installed.  */
+    }
+
+  err = gnupg_spawn_process (pgmname, argv, NULL, NULL, 0,
                              NULL, &outfp, NULL, &pid);
   if (err)
     {
@@ -2059,8 +2215,11 @@ retrieve_options_from_program (gc_component_t component, gc_backend_t backend)
 
   config = es_fopen (config_filename, "r");
   if (!config)
-    gc_error (0, errno, "warning: can not open config file %s",
-             config_filename);
+    {
+      if (errno != ENOENT)
+        gc_error (0, errno, "warning: can not open config file %s",
+                  config_filename);
+    }
   else
     {
       while ((length = es_read_line (config, &line, &line_len, NULL)) > 0)
@@ -2125,7 +2284,7 @@ retrieve_options_from_program (gc_component_t component, gc_backend_t backend)
              if (!(option->flags & GC_OPT_FLAG_LIST))
                {
                  if (option->value)
-                   free (option->value);
+                   xfree (option->value);
                  option->value = opt_value;
                }
              else
@@ -2134,10 +2293,9 @@ retrieve_options_from_program (gc_component_t component, gc_backend_t backend)
                    option->value = opt_value;
                  else
                    {
-                     char *opt_val = opt_value;
-
-                     option->value = xasprintf ("%s,%s", option->value,
-                                                opt_val);
+                     char *old = option->value;
+                     option->value = xasprintf ("%s,%s", old, opt_value);
+                     xfree (old);
                      xfree (opt_value);
                    }
                }
@@ -2162,7 +2320,7 @@ retrieve_options_from_file (gc_component_t component, gc_backend_t backend)
   gc_option_t *list_option;
   gc_option_t *config_option;
   char *list_filename;
-  FILE *list_file;
+  gpgrt_stream_t list_file;
   char *line = NULL;
   size_t line_len = 0;
   ssize_t length;
@@ -2174,13 +2332,13 @@ retrieve_options_from_file (gc_component_t component, gc_backend_t backend)
   assert (!list_option->active);
 
   list_filename = get_config_filename (component, backend);
-  list_file = fopen (list_filename, "r");
+  list_file = gpgrt_fopen (list_filename, "r");
   if (!list_file)
     gc_error (0, errno, "warning: can not open list file %s", list_filename);
   else
     {
 
-      while ((length = read_line (list_file, &line, &line_len, NULL)) > 0)
+      while ((length = gpgrt_read_line (list_file, &line, &line_len, NULL)) > 0)
        {
          char *start;
          char *end;
@@ -2213,7 +2371,7 @@ retrieve_options_from_file (gc_component_t component, gc_backend_t backend)
          else
            list = xasprintf ("\"%s", gc_percent_escape (start));
        }
-      if (length < 0 || ferror (list_file))
+      if (length < 0 || gpgrt_ferror (list_file))
        gc_error (1, errno, "can not read list file %s", list_filename);
     }
 
@@ -2226,7 +2384,7 @@ retrieve_options_from_file (gc_component_t component, gc_backend_t backend)
   if (config_option->flags & GC_OPT_FLAG_NO_CHANGE)
     list_option->flags |= GC_OPT_FLAG_NO_CHANGE;
 
-  if (list_file && fclose (list_file))
+  if (list_file && gpgrt_fclose (list_file))
     gc_error (1, errno, "error closing %s", list_filename);
   xfree (line);
 }
@@ -2234,7 +2392,7 @@ retrieve_options_from_file (gc_component_t component, gc_backend_t backend)
 
 /* Retrieve the currently active options and their defaults from all
    involved backends for this component.  Using -1 for component will
-   retrieve all options from all components. */
+   retrieve all options from all installed components. */
 void
 gc_component_retrieve_options (int component)
 {
@@ -2243,9 +2401,6 @@ gc_component_retrieve_options (int component)
   gc_backend_t backend;
   gc_option_t *option;
 
-  if (component == GC_COMPONENT_PINENTRY)
-    return; /* Dummy module for now.  */
-
   for (backend = 0; backend < GC_BACKEND_NR; backend++)
     backend_seen[backend] = 0;
 
@@ -2258,6 +2413,9 @@ gc_component_retrieve_options (int component)
 
   do
     {
+      if (component == GC_COMPONENT_PINENTRY)
+        continue; /* Skip this dummy component.  */
+
       option = gc_component[component].options;
 
       while (option && option->name)
@@ -2276,7 +2434,8 @@ gc_component_retrieve_options (int component)
               assert (backend != GC_BACKEND_ANY);
 
               if (gc_backend[backend].program)
-                retrieve_options_from_program (component, backend);
+                retrieve_options_from_program (component, backend,
+                                               process_all);
               else
                 retrieve_options_from_file (component, backend);
             }
@@ -2290,11 +2449,13 @@ gc_component_retrieve_options (int component)
 
 \f
 /* Perform a simple validity check based on the type.  Return in
-   NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of
-   type GC_ARG_TYPE_NONE.  */
+ * NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of
+ * type GC_ARG_TYPE_NONE.  If VERBATIM is set the profile parsing mode
+ * is used. */
 static void
 option_check_validity (gc_option_t *option, unsigned long flags,
-                      char *new_value, unsigned long *new_value_nr)
+                      char *new_value, unsigned long *new_value_nr,
+                       int verbatim)
 {
   char *arg;
 
@@ -2348,17 +2509,17 @@ option_check_validity (gc_option_t *option, unsigned long flags,
   arg = new_value;
   do
     {
-      if (*arg == '\0' || *arg == ',')
+      if (*arg == '\0' || (*arg == ',' && !verbatim))
        {
          if (!(option->flags & GC_OPT_FLAG_ARG_OPT))
            gc_error (1, 0, "argument required for option %s", option->name);
 
-         if (*arg == ',' && !(option->flags & GC_OPT_FLAG_LIST))
+         if (*arg == ',' && !verbatim && !(option->flags & GC_OPT_FLAG_LIST))
            gc_error (1, 0, "list found for non-list option %s", option->name);
        }
       else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_STRING)
        {
-         if (*arg != '"')
+         if (*arg != '"' && !verbatim)
            gc_error (1, 0, "string argument for option %s must begin "
                      "with a quote (\") character", option->name);
 
@@ -2366,7 +2527,7 @@ option_check_validity (gc_option_t *option, unsigned long flags,
             we do not quote arguments in configuration files, and
             thus no argument is indistinguishable from the empty
             string.  */
-         if (arg[1] == '\0' || arg[1] == ',')
+         if (arg[1] == '\0' || (arg[1] == ',' && !verbatim))
            gc_error (1, 0, "empty string argument for option %s is "
                      "currently not allowed.  Please report this!",
                      option->name);
@@ -2383,7 +2544,7 @@ option_check_validity (gc_option_t *option, unsigned long flags,
            gc_error (1, errno, "invalid argument for option %s",
                      option->name);
 
-         if (*arg != '\0' && *arg != ',')
+         if (*arg != '\0' && (*arg != ',' || verbatim))
            gc_error (1, 0, "garbage after argument for option %s",
                      option->name);
        }
@@ -2399,11 +2560,11 @@ option_check_validity (gc_option_t *option, unsigned long flags,
            gc_error (1, errno, "invalid argument for option %s",
                      option->name);
 
-         if (*arg != '\0' && *arg != ',')
+         if (*arg != '\0' && (*arg != ',' || verbatim))
            gc_error (1, 0, "garbage after argument for option %s",
                      option->name);
        }
-      arg = strchr (arg, ',');
+      arg = verbatim? strchr (arg, ',') : NULL;
       if (arg)
        arg++;
     }
@@ -2417,18 +2578,18 @@ copy_file (const char *src_name, const char *dst_name)
 #define BUF_LEN 4096
   char buffer[BUF_LEN];
   int len;
-  FILE *src;
-  FILE *dst;
+  gpgrt_stream_t src;
+  gpgrt_stream_t dst;
 
-  src = fopen (src_name, "r");
+  src = gpgrt_fopen (src_name, "r");
   if (src == NULL)
     return -1;
 
-  dst = fopen (dst_name, "w");
+  dst = gpgrt_fopen (dst_name, "w");
   if (dst == NULL)
     {
       int saved_err = errno;
-      fclose (src);
+      gpgrt_fclose (src);
       gpg_err_set_errno (saved_err);
       return -1;
     }
@@ -2437,28 +2598,28 @@ copy_file (const char *src_name, const char *dst_name)
     {
       int written;
 
-      len = fread (buffer, 1, BUF_LEN, src);
+      len = gpgrt_fread (buffer, 1, BUF_LEN, src);
       if (len == 0)
        break;
-      written = fwrite (buffer, 1, len, dst);
+      written = gpgrt_fwrite (buffer, 1, len, dst);
       if (written != len)
        break;
     }
-  while (!feof (src) && !ferror (src) && !ferror (dst));
+  while (! gpgrt_feof (src) && ! gpgrt_ferror (src) && ! gpgrt_ferror (dst));
 
-  if (ferror (src) || ferror (dst) || !feof (src))
+  if (gpgrt_ferror (src) || gpgrt_ferror (dst) || ! gpgrt_feof (src))
     {
       int saved_errno = errno;
-      fclose (src);
-      fclose (dst);
+      gpgrt_fclose (src);
+      gpgrt_fclose (dst);
       unlink (dst_name);
       gpg_err_set_errno (saved_errno);
       return -1;
     }
 
-  if (fclose (dst))
+  if (gpgrt_fclose (dst))
     gc_error (1, errno, "error closing %s", dst_name);
-  if (fclose (src))
+  if (gpgrt_fclose (src))
     gc_error (1, errno, "error closing %s", src_name);
 
   return 0;
@@ -2467,7 +2628,20 @@ copy_file (const char *src_name, const char *dst_name)
 
 
 /* Create and verify the new configuration file for the specified
-   backend and component.  Returns 0 on success and -1 on error.  */
+ * backend and component.  Returns 0 on success and -1 on error.  This
+ * function may store pointers to malloced strings in SRC_FILENAMEP,
+ * DEST_FILENAMEP, and ORIG_FILENAMEP.  Those must be freed by the
+ * caller.  The strings refer to three versions of the configuration
+ * file:
+ *
+ * SRC_FILENAME:  The updated configuration is written to this file.
+ * DEST_FILENAME: Name of the configuration file read by the
+ *                component.
+ * ORIG_FILENAME: A backup of the previous configuration file.
+ *
+ * To apply the configuration change, rename SRC_FILENAME to
+ * DEST_FILENAME.  To revert to the previous configuration, rename
+ * ORIG_FILENAME to DEST_FILENAME.  */
 static int
 change_options_file (gc_component_t component, gc_backend_t backend,
                     char **src_filenamep, char **dest_filenamep,
@@ -2482,8 +2656,8 @@ change_options_file (gc_component_t component, gc_backend_t backend,
   ssize_t length;
   int res;
   int fd;
-  FILE *src_file = NULL;
-  FILE *dest_file = NULL;
+  gpgrt_stream_t src_file = NULL;
+  gpgrt_stream_t dest_file = NULL;
   char *src_filename;
   char *dest_filename;
   char *orig_filename;
@@ -2535,6 +2709,8 @@ change_options_file (gc_component_t component, gc_backend_t backend,
   if (res < 0 && errno != ENOENT)
     {
       xfree (dest_filename);
+      xfree (src_filename);
+      xfree (orig_filename);
       return -1;
     }
   if (res < 0)
@@ -2553,7 +2729,7 @@ change_options_file (gc_component_t component, gc_backend_t backend,
   fd = open (src_filename, O_CREAT | O_EXCL | O_WRONLY, 0644);
   if (fd < 0)
     return -1;
-  src_file = fdopen (fd, "w");
+  src_file = gpgrt_fdopen (fd, "w");
   res = errno;
   if (!src_file)
     {
@@ -2567,11 +2743,11 @@ change_options_file (gc_component_t component, gc_backend_t backend,
      process.  */
   if (orig_filename)
     {
-      dest_file = fopen (dest_filename, "r");
+      dest_file = gpgrt_fopen (dest_filename, "r");
       if (!dest_file)
        goto change_file_one_err;
 
-      while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+      while ((length = gpgrt_read_line (dest_file, &line, &line_len, NULL)) > 0)
        {
          int disable = 0;
          char *start;
@@ -2642,24 +2818,24 @@ change_options_file (gc_component_t component, gc_backend_t backend,
            {
              if (!in_marker)
                {
-                 fprintf (src_file,
+                 gpgrt_fprintf (src_file,
                           "# %s disabled this option here at %s\n",
                           GPGCONF_DISP_NAME, asctimestamp (gnupg_get_time ()));
-                 if (ferror (src_file))
+                 if (gpgrt_ferror (src_file))
                    goto change_file_one_err;
-                 fprintf (src_file, "# %s", line);
-                 if (ferror (src_file))
+                 gpgrt_fprintf (src_file, "# %s", line);
+                 if (gpgrt_ferror (src_file))
                    goto change_file_one_err;
                }
            }
          else
            {
-             fprintf (src_file, "%s", line);
-             if (ferror (src_file))
+             gpgrt_fprintf (src_file, "%s", line);
+             if (gpgrt_ferror (src_file))
                goto change_file_one_err;
            }
        }
-      if (length < 0 || ferror (dest_file))
+      if (length < 0 || gpgrt_ferror (dest_file))
        goto change_file_one_err;
     }
 
@@ -2670,8 +2846,8 @@ change_options_file (gc_component_t component, gc_backend_t backend,
         proceed.  Note that we first write a newline, this guards us
         against files which lack the newline at the end of the last
         line, while it doesn't hurt us in all other cases.  */
-      fprintf (src_file, "\n%s\n", marker);
-      if (ferror (src_file))
+      gpgrt_fprintf (src_file, "\n%s\n", marker);
+      if (gpgrt_ferror (src_file))
        goto change_file_one_err;
     }
 
@@ -2681,7 +2857,7 @@ change_options_file (gc_component_t component, gc_backend_t backend,
      followed by the rest of the original file.  */
   while (cur_arg)
     {
-      fprintf (src_file, "%s\n", cur_arg);
+      gpgrt_fprintf (src_file, "%s\n", cur_arg);
 
       /* Find next argument.  */
       if (arg)
@@ -2706,52 +2882,52 @@ change_options_file (gc_component_t component, gc_backend_t backend,
        cur_arg = NULL;
     }
 
-  fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ()));
-  if (ferror (src_file))
+  gpgrt_fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ()));
+  if (gpgrt_ferror (src_file))
     goto change_file_one_err;
 
   if (!in_marker)
     {
-      fprintf (src_file, "# %s edited this configuration file.\n",
+      gpgrt_fprintf (src_file, "# %s edited this configuration file.\n",
                GPGCONF_DISP_NAME);
-      if (ferror (src_file))
+      if (gpgrt_ferror (src_file))
        goto change_file_one_err;
-      fprintf (src_file, "# It will disable options before this marked "
+      gpgrt_fprintf (src_file, "# It will disable options before this marked "
               "block, but it will\n");
-      if (ferror (src_file))
+      if (gpgrt_ferror (src_file))
        goto change_file_one_err;
-      fprintf (src_file, "# never change anything below these lines.\n");
-      if (ferror (src_file))
+      gpgrt_fprintf (src_file, "# never change anything below these lines.\n");
+      if (gpgrt_ferror (src_file))
        goto change_file_one_err;
     }
   if (dest_file)
     {
-      while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+      while ((length = gpgrt_read_line (dest_file, &line, &line_len, NULL)) > 0)
        {
-         fprintf (src_file, "%s", line);
-         if (ferror (src_file))
+         gpgrt_fprintf (src_file, "%s", line);
+         if (gpgrt_ferror (src_file))
            goto change_file_one_err;
        }
-      if (length < 0 || ferror (dest_file))
+      if (length < 0 || gpgrt_ferror (dest_file))
        goto change_file_one_err;
     }
   xfree (line);
   line = NULL;
 
-  res = fclose (src_file);
+  res = gpgrt_fclose (src_file);
   if (res)
     {
       res = errno;
       close (fd);
       if (dest_file)
-       fclose (dest_file);
+       gpgrt_fclose (dest_file);
       gpg_err_set_errno (res);
       return -1;
     }
   close (fd);
   if (dest_file)
     {
-      res = fclose (dest_file);
+      res = gpgrt_fclose (dest_file);
       if (res)
        return -1;
     }
@@ -2762,22 +2938,36 @@ change_options_file (gc_component_t component, gc_backend_t backend,
   res = errno;
   if (src_file)
     {
-      fclose (src_file);
+      gpgrt_fclose (src_file);
       close (fd);
     }
   if (dest_file)
-    fclose (dest_file);
+    gpgrt_fclose (dest_file);
   gpg_err_set_errno (res);
   return -1;
 }
 
 
 /* Create and verify the new configuration file for the specified
-   backend and component.  Returns 0 on success and -1 on error.  */
+ * backend and component.  Returns 0 on success and -1 on error.  If
+ * VERBATIM is set the profile mode is used.  This function may store
+ * pointers to malloced strings in SRC_FILENAMEP, DEST_FILENAMEP, and
+ * ORIG_FILENAMEP.  Those must be freed by the caller.  The strings
+ * refer to three versions of the configuration file:
+ *
+ * SRC_FILENAME:  The updated configuration is written to this file.
+ * DEST_FILENAME: Name of the configuration file read by the
+ *                component.
+ * ORIG_FILENAME: A backup of the previous configuration file.
+ *
+ * To apply the configuration change, rename SRC_FILENAME to
+ * DEST_FILENAME.  To revert to the previous configuration, rename
+ * ORIG_FILENAME to DEST_FILENAME.  */
 static int
 change_options_program (gc_component_t component, gc_backend_t backend,
                        char **src_filenamep, char **dest_filenamep,
-                       char **orig_filenamep)
+                       char **orig_filenamep,
+                        int verbatim)
 {
   static const char marker[] = "###+++--- " GPGCONF_DISP_NAME " ---+++###";
   /* True if we are within the marker in the config file.  */
@@ -2788,8 +2978,8 @@ change_options_program (gc_component_t component, gc_backend_t backend,
   ssize_t length;
   int res;
   int fd;
-  FILE *src_file = NULL;
-  FILE *dest_file = NULL;
+  gpgrt_stream_t src_file = NULL;
+  gpgrt_stream_t dest_file = NULL;
   char *src_filename;
   char *dest_filename;
   char *orig_filename;
@@ -2809,7 +2999,12 @@ change_options_program (gc_component_t component, gc_backend_t backend,
   res = link (dest_filename, orig_filename);
 #endif
   if (res < 0 && errno != ENOENT)
-    return -1;
+    {
+      xfree (dest_filename);
+      xfree (src_filename);
+      xfree (orig_filename);
+      return -1;
+    }
   if (res < 0)
     {
       xfree (orig_filename);
@@ -2826,7 +3021,7 @@ change_options_program (gc_component_t component, gc_backend_t backend,
   fd = open (src_filename, O_CREAT | O_EXCL | O_WRONLY, 0644);
   if (fd < 0)
     return -1;
-  src_file = fdopen (fd, "w");
+  src_file = gpgrt_fdopen (fd, "w");
   res = errno;
   if (!src_file)
     {
@@ -2840,11 +3035,11 @@ change_options_program (gc_component_t component, gc_backend_t backend,
      process.  */
   if (orig_filename)
     {
-      dest_file = fopen (dest_filename, "r");
+      dest_file = gpgrt_fopen (dest_filename, "r");
       if (!dest_file)
        goto change_one_err;
 
-      while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+      while ((length = gpgrt_read_line (dest_file, &line, &line_len, NULL)) > 0)
        {
          int disable = 0;
          char *start;
@@ -2891,24 +3086,24 @@ change_options_program (gc_component_t component, gc_backend_t backend,
            {
              if (!in_marker)
                {
-                 fprintf (src_file,
+                 gpgrt_fprintf (src_file,
                           "# %s disabled this option here at %s\n",
                           GPGCONF_DISP_NAME, asctimestamp (gnupg_get_time ()));
-                 if (ferror (src_file))
+                 if (gpgrt_ferror (src_file))
                    goto change_one_err;
-                 fprintf (src_file, "# %s", line);
-                 if (ferror (src_file))
+                 gpgrt_fprintf (src_file, "# %s", line);
+                 if (gpgrt_ferror (src_file))
                    goto change_one_err;
                }
            }
          else
            {
-             fprintf (src_file, "%s", line);
-             if (ferror (src_file))
+             gpgrt_fprintf (src_file, "%s", line);
+             if (gpgrt_ferror (src_file))
                goto change_one_err;
            }
        }
-      if (length < 0 || ferror (dest_file))
+      if (length < 0 || gpgrt_ferror (dest_file))
        goto change_one_err;
     }
 
@@ -2919,8 +3114,8 @@ change_options_program (gc_component_t component, gc_backend_t backend,
         proceed.  Note that we first write a newline, this guards us
         against files which lack the newline at the end of the last
         line, while it doesn't hurt us in all other cases.  */
-      fprintf (src_file, "\n%s\n", marker);
-      if (ferror (src_file))
+      gpgrt_fprintf (src_file, "\n%s\n", marker);
+      if (gpgrt_ferror (src_file))
        goto change_one_err;
     }
   /* At this point, we have copied everything up to the end marker
@@ -2931,7 +3126,7 @@ change_options_program (gc_component_t component, gc_backend_t backend,
 
   /* We have to turn on UTF8 strings for GnuPG.  */
   if (backend == GC_BACKEND_GPG && ! utf8strings_seen)
-    fprintf (src_file, "utf8-strings\n");
+    gpgrt_fprintf (src_file, "utf8-strings\n");
 
   option = gc_component[component].options;
   while (option->name)
@@ -2946,16 +3141,16 @@ change_options_program (gc_component_t component, gc_backend_t backend,
            {
              if (*arg == '\0' || *arg == ',')
                {
-                 fprintf (src_file, "%s\n", option->name);
-                 if (ferror (src_file))
+                 gpgrt_fprintf (src_file, "%s\n", option->name);
+                 if (gpgrt_ferror (src_file))
                    goto change_one_err;
                }
              else if (gc_arg_type[option->arg_type].fallback
                       == GC_ARG_TYPE_NONE)
                {
                  assert (*arg == '1');
-                 fprintf (src_file, "%s\n", option->name);
-                 if (ferror (src_file))
+                 gpgrt_fprintf (src_file, "%s\n", option->name);
+                 if (gpgrt_ferror (src_file))
                    goto change_one_err;
 
                  arg++;
@@ -2965,16 +3160,21 @@ change_options_program (gc_component_t component, gc_backend_t backend,
                {
                  char *end;
 
-                 assert (*arg == '"');
-                 arg++;
-
-                 end = strchr (arg, ',');
-                 if (end)
-                   *end = '\0';
-
-                 fprintf (src_file, "%s %s\n", option->name,
-                          percent_deescape (arg));
-                 if (ferror (src_file))
+                  if (!verbatim)
+                    {
+                      log_assert (*arg == '"');
+                      arg++;
+
+                      end = strchr (arg, ',');
+                      if (end)
+                        *end = '\0';
+                    }
+                  else
+                    end = NULL;
+
+                 gpgrt_fprintf (src_file, "%s %s\n", option->name,
+                          verbatim? arg : percent_deescape (arg));
+                 if (gpgrt_ferror (src_file))
                    goto change_one_err;
 
                  if (end)
@@ -2989,8 +3189,8 @@ change_options_program (gc_component_t component, gc_backend_t backend,
                  if (end)
                    *end = '\0';
 
-                 fprintf (src_file, "%s %s\n", option->name, arg);
-                 if (ferror (src_file))
+                 gpgrt_fprintf (src_file, "%s %s\n", option->name, arg);
+                 if (gpgrt_ferror (src_file))
                    goto change_one_err;
 
                  if (end)
@@ -3007,52 +3207,52 @@ change_options_program (gc_component_t component, gc_backend_t backend,
       option++;
     }
 
-  fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ()));
-  if (ferror (src_file))
+  gpgrt_fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ()));
+  if (gpgrt_ferror (src_file))
     goto change_one_err;
 
   if (!in_marker)
     {
-      fprintf (src_file, "# %s edited this configuration file.\n",
+      gpgrt_fprintf (src_file, "# %s edited this configuration file.\n",
                GPGCONF_DISP_NAME);
-      if (ferror (src_file))
+      if (gpgrt_ferror (src_file))
        goto change_one_err;
-      fprintf (src_file, "# It will disable options before this marked "
+      gpgrt_fprintf (src_file, "# It will disable options before this marked "
               "block, but it will\n");
-      if (ferror (src_file))
+      if (gpgrt_ferror (src_file))
        goto change_one_err;
-      fprintf (src_file, "# never change anything below these lines.\n");
-      if (ferror (src_file))
+      gpgrt_fprintf (src_file, "# never change anything below these lines.\n");
+      if (gpgrt_ferror (src_file))
        goto change_one_err;
     }
   if (dest_file)
     {
-      while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+      while ((length = gpgrt_read_line (dest_file, &line, &line_len, NULL)) > 0)
        {
-         fprintf (src_file, "%s", line);
-         if (ferror (src_file))
+         gpgrt_fprintf (src_file, "%s", line);
+         if (gpgrt_ferror (src_file))
            goto change_one_err;
        }
-      if (length < 0 || ferror (dest_file))
+      if (length < 0 || gpgrt_ferror (dest_file))
        goto change_one_err;
     }
   xfree (line);
   line = NULL;
 
-  res = fclose (src_file);
+  res = gpgrt_fclose (src_file);
   if (res)
     {
       res = errno;
       close (fd);
       if (dest_file)
-       fclose (dest_file);
+       gpgrt_fclose (dest_file);
       gpg_err_set_errno (res);
       return -1;
     }
   close (fd);
   if (dest_file)
     {
-      res = fclose (dest_file);
+      res = gpgrt_fclose (dest_file);
       if (res)
        return -1;
     }
@@ -3063,25 +3263,26 @@ change_options_program (gc_component_t component, gc_backend_t backend,
   res = errno;
   if (src_file)
     {
-      fclose (src_file);
+      gpgrt_fclose (src_file);
       close (fd);
     }
   if (dest_file)
-    fclose (dest_file);
+    gpgrt_fclose (dest_file);
   gpg_err_set_errno (res);
   return -1;
 }
 
 
 /* Common code for gc_component_change_options and
-   gc_process_gpgconf_conf.  */
+ * gc_process_gpgconf_conf.  If VERBATIM is set the profile parsing
+ * mode is used.  */
 static void
 change_one_value (gc_option_t *option, int *runtime,
-                  unsigned long flags, char *new_value)
+                  unsigned long flags, char *new_value, int verbatim)
 {
   unsigned long new_value_nr = 0;
 
-  option_check_validity (option, flags, new_value, &new_value_nr);
+  option_check_validity (option, flags, new_value, &new_value_nr, verbatim);
 
   if (option->flags & GC_OPT_FLAG_RUNTIME)
     runtime[option->backend] = 1;
@@ -3115,11 +3316,13 @@ change_one_value (gc_option_t *option, int *runtime,
 
 /* Read the modifications from IN and apply them.  If IN is NULL the
    modifications are expected to already have been set to the global
-   table. */
+   table.  If VERBATIM is set the profile mode is used.  */
 void
-gc_component_change_options (int component, estream_t in, estream_t out)
+gc_component_change_options (int component, estream_t in, estream_t out,
+                             int verbatim)
 {
   int err = 0;
+  int block = 0;
   int runtime[GC_BACKEND_NR];
   char *src_filename[GC_BACKEND_NR];
   char *dest_filename[GC_BACKEND_NR];
@@ -3204,8 +3407,10 @@ gc_component_change_options (int component, estream_t in, estream_t out)
               continue;
             }
 
-          change_one_value (option, runtime, flags, new_value);
+          change_one_value (option, runtime, flags, new_value, 0);
         }
+      if (length < 0 || gpgrt_ferror (in))
+       gc_error (1, errno, "error reading stream 'in'");
     }
 
   /* Now that we have collected and locally verified the changes,
@@ -3228,7 +3433,8 @@ gc_component_change_options (int component, estream_t in, estream_t out)
          err = change_options_program (component, option->backend,
                                        &src_filename[option->backend],
                                        &dest_filename[option->backend],
-                                       &orig_filename[option->backend]);
+                                       &orig_filename[option->backend],
+                                        verbatim);
          if (! err)
            {
              /* External verification.  */
@@ -3256,6 +3462,14 @@ gc_component_change_options (int component, estream_t in, estream_t out)
       option++;
     }
 
+  /* We are trying to atomically commit all changes.  Unfortunately,
+     we cannot rely on gnupg_rename_file to manage the signals for us,
+     doing so would require us to pass NULL as BLOCK to any subsequent
+     call to it.  Instead, we just manage the signal handling
+     manually.  */
+  block = 1;
+  gnupg_block_all_signals ();
+
   if (! err && ! opt.dry_run)
     {
       int i;
@@ -3269,20 +3483,13 @@ gc_component_change_options (int component, estream_t in, estream_t out)
              assert (dest_filename[i]);
 
              if (orig_filename[i])
-               {
-#ifdef HAVE_W32_SYSTEM
-                 /* There is no atomic update on W32.  */
-                 err = unlink (dest_filename[i]);
-#endif /* HAVE_W32_SYSTEM */
-                 if (!err)
-                   err = rename (src_filename[i], dest_filename[i]);
-               }
+               err = gnupg_rename_file (src_filename[i], dest_filename[i], NULL);
              else
                {
 #ifdef HAVE_W32_SYSTEM
                  /* We skip the unlink if we expect the file not to
                     be there.  */
-                  err = rename (src_filename[i], dest_filename[i]);
+                  err = gnupg_rename_file (src_filename[i], dest_filename[i], NULL);
 #else /* HAVE_W32_SYSTEM */
                  /* This is a bit safer than rename() because we
                     expect DEST_FILENAME not to be there.  If it
@@ -3294,6 +3501,7 @@ gc_component_change_options (int component, estream_t in, estream_t out)
                }
              if (err)
                break;
+             xfree (src_filename[i]);
              src_filename[i] = NULL;
            }
        }
@@ -3321,13 +3529,7 @@ gc_component_change_options (int component, estream_t in, estream_t out)
                 a version of the file that is even newer than the one
                 we just installed.  */
              if (orig_filename[i])
-               {
-#ifdef HAVE_W32_SYSTEM
-                 /* There is no atomic update on W32.  */
-                 unlink (dest_filename[i]);
-#endif /* HAVE_W32_SYSTEM */
-                 rename (orig_filename[i], dest_filename[i]);
-               }
+               gnupg_rename_file (orig_filename[i], dest_filename[i], NULL);
              else
                unlink (dest_filename[i]);
            }
@@ -3357,16 +3559,20 @@ gc_component_change_options (int component, estream_t in, estream_t out)
 
        backup_filename = xasprintf ("%s.%s.bak",
                                      dest_filename[backend], GPGCONF_NAME);
-
-#ifdef HAVE_W32_SYSTEM
-       /* There is no atomic update on W32.  */
-       unlink (backup_filename);
-#endif /* HAVE_W32_SYSTEM */
-       rename (orig_filename[backend], backup_filename);
+       gnupg_rename_file (orig_filename[backend], backup_filename, NULL);
+       xfree (backup_filename);
       }
 
  leave:
+  if (block)
+    gnupg_unblock_all_signals ();
   xfree (line);
+  for (backend = 0; backend < GC_BACKEND_NR; backend++)
+    {
+      xfree (src_filename[backend]);
+      xfree (dest_filename[backend]);
+      xfree (orig_filename[backend]);
+    }
 }
 
 
@@ -3494,7 +3700,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
   char *line = NULL;
   size_t line_len = 0;
   ssize_t length;
-  FILE *config;
+  gpgrt_stream_t config;
   int lineno = 0;
   int in_rule = 0;
   int got_match = 0;
@@ -3511,7 +3717,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
   for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
     runtime[backend_id] = 0;
 
-  config = fopen (fname, "r");
+  config = gpgrt_fopen (fname, "r");
   if (!config)
     {
       /* Do not print an error if the file is not available, except
@@ -3525,7 +3731,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
       return result;
     }
 
-  while ((length = read_line (config, &line, &line_len, NULL)) > 0)
+  while ((length = gpgrt_read_line (config, &line, &line_len, NULL)) > 0)
     {
       char *key, *component, *option, *flags, *value;
       char *empty;
@@ -3554,6 +3760,10 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
             {
               gc_error (0, 0, "missing rule at '%s', line %d", fname, lineno);
               result = -1;
+              gpgconf_write_status (STATUS_WARNING,
+                                    "gpgconf.conf %d file '%s' line %d "
+                                    "missing rule",
+                                    GPG_ERR_SYNTAX, fname, lineno);
               continue;
             }
           *p++ = 0;
@@ -3583,6 +3793,10 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
         {
           gc_error (0, 0, "missing component at '%s', line %d",
                     fname, lineno);
+          gpgconf_write_status (STATUS_WARNING,
+                                "gpgconf.conf %d file '%s' line %d "
+                                " missing component",
+                                GPG_ERR_NO_NAME, fname, lineno);
           result = -1;
           continue;
         }
@@ -3594,6 +3808,10 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
         {
           gc_error (0, 0, "unknown component at '%s', line %d",
                     fname, lineno);
+          gpgconf_write_status (STATUS_WARNING,
+                                "gpgconf.conf %d file '%s' line %d "
+                                "unknown component",
+                                GPG_ERR_UNKNOWN_NAME, fname, lineno);
           result = -1;
         }
 
@@ -3606,6 +3824,10 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
         {
           gc_error (0, 0, "missing option at '%s', line %d",
                     fname, lineno);
+          gpgconf_write_status (STATUS_WARNING,
+                                "gpgconf.conf %d file '%s' line %d "
+                                "missing option",
+                                GPG_ERR_INV_NAME, fname, lineno);
           result = -1;
           continue;
         }
@@ -3618,6 +3840,10 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
             {
               gc_error (0, 0, "unknown option at '%s', line %d",
                         fname, lineno);
+              gpgconf_write_status (STATUS_WARNING,
+                                    "gpgconf.conf %d file '%s' line %d "
+                                    "unknown option",
+                                    GPG_ERR_UNKNOWN_OPTION, fname, lineno);
               result = -1;
             }
         }
@@ -3634,6 +3860,10 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
             {
               gc_error (0, 0, "syntax error in rule at '%s', line %d",
                         fname, lineno);
+              gpgconf_write_status (STATUS_WARNING,
+                                    "gpgconf.conf %d file '%s' line %d "
+                                    "syntax error in rule",
+                                    GPG_ERR_SYNTAX, fname, lineno);
               result = -1;
               continue;
             }
@@ -3736,7 +3966,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
 
           if (defaults)
             {
-              /* Here we explicitly allow to update the value again.  */
+              /* Here we explicitly allow updating the value again.  */
               if (newflags)
                 {
                   option_info->new_flags = 0;
@@ -3746,17 +3976,17 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
                   xfree (option_info->new_value);
                   option_info->new_value = NULL;
                 }
-              change_one_value (option_info, runtime, newflags, value);
+              change_one_value (option_info, runtime, newflags, value, 0);
             }
         }
     }
 
-  if (length < 0 || ferror (config))
+  if (length < 0 || gpgrt_ferror (config))
     {
       gc_error (0, errno, "error reading from '%s'", fname);
       result = -1;
     }
-  if (fclose (config))
+  if (gpgrt_fclose (config))
     gc_error (0, errno, "error closing '%s'", fname);
 
   xfree (line);
@@ -3771,7 +4001,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
 
       for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++)
         {
-          gc_component_change_options (component_id, NULL, NULL);
+          gc_component_change_options (component_id, NULL, NULL, 0);
         }
       opt.runtime = save_opt_runtime;
 
@@ -3786,3 +4016,210 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
   xfree (fname);
   return result;
 }
+
+
+/*
+ * Apply the profile FNAME to all known configure files.
+ */
+gpg_error_t
+gc_apply_profile (const char *fname)
+{
+  gpg_error_t err;
+  char *fname_buffer = NULL;
+  char *line = NULL;
+  size_t line_len = 0;
+  ssize_t length;
+  estream_t fp;
+  int lineno = 0;
+  int runtime[GC_BACKEND_NR];
+  int backend_id;
+  int component_id = -1;
+  int skip_section = 0;
+  int error_count = 0;
+  int newflags;
+
+  if (!fname)
+    fname = "-";
+
+  for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
+    runtime[backend_id] = 0;
+
+
+  if (!(!strcmp (fname, "-")
+        || strchr (fname, '/')
+#ifdef HAVE_W32_SYSTEM
+        || strchr (fname, '\\')
+#endif
+        || strchr (fname, '.')))
+    {
+      /* FNAME looks like a standard profile name.  Check whether one
+       * is installed and use that instead of the given file name.  */
+      fname_buffer = xstrconcat (gnupg_datadir (), DIRSEP_S,
+                                 fname, ".prf", NULL);
+      if (!access (fname_buffer, F_OK))
+        fname = fname_buffer;
+    }
+
+  fp = !strcmp (fname, "-")? es_stdin : es_fopen (fname, "r");
+  if (!fp)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
+      return err;
+    }
+
+  if (opt.verbose)
+    log_info ("applying profile '%s'\n", fname);
+
+  err = 0;
+  while ((length = es_read_line (fp, &line, &line_len, NULL)) > 0)
+    {
+      char *name, *flags, *value;
+      gc_option_t *option_info = NULL;
+      char *p;
+
+      lineno++;
+      name = line;
+      while (*name == ' ' || *name == '\t')
+        name++;
+      if (!*name || *name == '#' || *name == '\r' || *name == '\n')
+        continue;
+      trim_trailing_spaces (name);
+
+      /* Check whether this is a new section.  */
+      if (*name == '[')
+        {
+          name++;
+          skip_section = 0;
+          /* New section: Get the name of the component.  */
+          p = strchr (name, ']');
+          if (!p)
+            {
+              error_count++;
+              log_info ("%s:%d:%d: error: syntax error in section tag\n",
+                        fname, lineno, (int)(name - line));
+              skip_section = 1;
+              continue;
+            }
+          *p++ = 0;
+          if (*p)
+            log_info ("%s:%d:%d: warning: garbage after section tag\n",
+                      fname, lineno, (int)(p - line));
+
+          trim_spaces (name);
+          component_id = gc_component_find (name);
+          if (component_id < 0)
+            {
+              log_info ("%s:%d:%d: warning: skipping unknown section '%s'\n",
+                        fname, lineno, (int)(name - line), name );
+              skip_section = 1;
+            }
+          continue;
+        }
+
+      if (skip_section)
+        continue;
+      if (component_id < 0)
+        {
+          error_count++;
+          log_info ("%s:%d:%d: error: not in a valid section\n",
+                    fname, lineno, (int)(name - line));
+          skip_section = 1;
+          continue;
+        }
+
+      /* Parse the option name.  */
+      for (p = name; *p && !spacep (p); p++)
+        ;
+      *p++ = 0;
+      value = p;
+
+      option_info = find_option (component_id, name, GC_BACKEND_ANY);
+      if (!option_info)
+        {
+          error_count++;
+          log_info ("%s:%d:%d: error: unknown option '%s' in section '%s'\n",
+                    fname, lineno, (int)(name - line),
+                    name, gc_component[component_id].name);
+          continue;
+        }
+
+      /* Parse the optional flags. */
+      trim_spaces (value);
+      flags = value;
+      if (*flags == '[')
+        {
+          flags++;
+          p = strchr (flags, ']');
+          if (!p)
+            {
+              log_info ("%s:%d:%d: warning: invalid flag specification\n",
+                        fname, lineno, (int)(p - line));
+              continue;
+            }
+          *p++ = 0;
+          value = p;
+          trim_spaces (value);
+        }
+      else /* No flags given.  */
+        flags = NULL;
+
+      /* Set required defaults.  */
+      if (gc_arg_type[option_info->arg_type].fallback == GC_ARG_TYPE_NONE
+          && !*value)
+        value = "1";
+
+      /* Check and save this option.  */
+      newflags = 0;
+      if (flags && !strcmp (flags, "default"))
+        newflags |= GC_OPT_FLAG_DEFAULT;
+
+      if (newflags)
+        option_info->new_flags = 0;
+      if (*value)
+        {
+          xfree (option_info->new_value);
+          option_info->new_value = NULL;
+        }
+      change_one_value (option_info, runtime, newflags, value, 1);
+    }
+
+  if (length < 0 || es_ferror (fp))
+    {
+      err = gpg_error_from_syserror ();
+      error_count++;
+      log_error (_("%s:%u: read error: %s\n"),
+                 fname, lineno, gpg_strerror (err));
+    }
+  if (es_fclose (fp))
+    log_error (_("error closing '%s'\n"), fname);
+  if (error_count)
+    log_error (_("error parsing '%s'\n"), fname);
+
+  xfree (line);
+
+  /* If it all worked, process the options. */
+  if (!err)
+    {
+      /* We need to switch off the runtime update, so that we can do
+         it later all at once. */
+      int save_opt_runtime = opt.runtime;
+      opt.runtime = 0;
+
+      for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++)
+        {
+          gc_component_change_options (component_id, NULL, NULL, 1);
+        }
+      opt.runtime = save_opt_runtime;
+
+      if (opt.runtime)
+        {
+          for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
+            if (runtime[backend_id] && gc_backend[backend_id].runtime_change)
+              (*gc_backend[backend_id].runtime_change) (0);
+        }
+    }
+
+  xfree (fname_buffer);
+  return err;
+}