gpgsm: Allow sepcification of ldaps servers.
authorWerner Koch <wk@gnupg.org>
Sat, 9 Nov 2019 10:29:59 +0000 (11:29 +0100)
committerWerner Koch <wk@gnupg.org>
Sat, 9 Nov 2019 10:29:59 +0000 (11:29 +0100)
* sm/gpgsm.h (struct keyserver_spec): Add field use_ldaps.
* sm/gpgsm.c (parse_keyserver_line): Parse flags.
* sm/call-dirmngr.c (prepare_dirmngr): Send ldaps flag to the dirmngr.

* dirmngr/dirmngr.h (struct ldap_server_s): Add field use_ldaps.
* dirmngr/ldapserver.c (ldapserver_parse_one): Parse flags.
* dirmngr/ldap.c (start_cert_fetch_ldap): Call wrapper with --tls.

* dirmngr/dirmngr_ldap.c: New option --tls.
(fetch_ldap): Make use of that option.
--

There was no way to specify an LDAPS server in
dirmngr_ldapserver.socnf or with gpgsm's --keyserver option.  This
patch fixes this.  Eventually we should allow to replace host and port
by a partial URI in the same way ldap_initialize does it.  For backward
compatibility we do not yet do that.

Although the dirmngr code accepts an URL (eg. taken from a
certificate), I can't see how the scheme was ever used.  Thus the
patch also detects an ldaps scheme and uses this.  That part has not
been tested, though.

Signed-off-by: Werner Koch <wk@gnupg.org>
dirmngr/dirmngr.h
dirmngr/dirmngr_ldap.c
dirmngr/ldap.c
dirmngr/ldapserver.c
doc/dirmngr.texi
doc/gpgsm.texi
sm/call-dirmngr.c
sm/gpgsm.c
sm/gpgsm.h

index 9c26c09..b27b8e6 100644 (file)
@@ -50,6 +50,7 @@ struct ldap_server_s
   char *user;
   char *pass;
   char *base;
+  unsigned int use_ldaps:1;
 };
 typedef struct ldap_server_s *ldap_server_t;
 
index dd7e4bd..72d88b9 100644 (file)
@@ -123,6 +123,7 @@ enum
     oDN,
     oFilter,
     oAttr,
+    oTls,
 
     oOnlySearchTimeout,
     oLogWithPID
@@ -138,6 +139,7 @@ static ARGPARSE_OPTS opts[] = {
                                   " a record oriented format")},
   { oProxy,    "proxy",     2,
     N_("|NAME|ignore host part and connect through NAME")},
+  { oTls,      "tls",       0, N_("force a TLS connection")},
   { oHost,     "host",      2, N_("|NAME|connect to host NAME")},
   { oPort,     "port",      1, N_("|N|connect to port N")},
   { oUser,     "user",      2, N_("|NAME|use user NAME for authentication")},
@@ -163,6 +165,7 @@ struct my_opt_s
   my_ldap_timeval_t timeout;/* Timeout for the LDAP search functions.  */
   unsigned int alarm_timeout; /* And for the alarm based timeout.  */
   int multi;
+  int force_tls;
 
   estream_t outstream;    /* Send output to this stream.  */
 
@@ -287,6 +290,7 @@ ldap_wrapper_main (char **argv, estream_t outstream)
           myopt->pass = getenv ("DIRMNGR_LDAP_PASS");
           break;
         case oProxy: myopt->proxy = pargs.r.ret_str; break;
+        case oTls:  myopt->force_tls = 1; break;
         case oHost: myopt->host = pargs.r.ret_str; break;
         case oPort: myopt->port = pargs.r.ret_int; break;
         case oDN:   myopt->dn = pargs.r.ret_str; break;
@@ -622,12 +626,19 @@ fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp)
   attrs[1] = NULL;
   attr = attrs[0];
 
-  if (!port)
+  if (!port && myopt->force_tls)
+    port = 636;
+  else if (!port)
     port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389;
 
   if (myopt->verbose)
     {
       log_info (_("processing url '%s'\n"), url);
+      if (myopt->force_tls)
+        log_info ("forcing tls\n");
+      else
+        log_info ("not forcing tls\n");
+
       if (myopt->user)
         log_info (_("          user '%s'\n"), myopt->user);
       if (myopt->pass)
@@ -665,17 +676,48 @@ fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp)
       && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1])
     log_info (_("WARNING: using first attribute only\n"));
 
-
   set_timeout (myopt);
-  npth_unprotect ();
-  ld = my_ldap_init (host, port);
-  npth_protect ();
-  if (!ld)
+
+  if (myopt->force_tls
+      || (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps")))
     {
-      log_error (_("LDAP init to '%s:%d' failed: %s\n"),
-                 host, port, strerror (errno));
-      return -1;
+      char *uri;
+
+      uri = xtryasprintf ("ldaps://%s:%d", host, port);
+      if (!uri)
+        {
+          log_error (_("error allocating memory: %s\n"),
+                     gpg_strerror (gpg_error_from_syserror ()));
+          return -1;
+        }
+      ret = ldap_initialize (&ld, uri);
+      if (ret)
+        {
+          log_error (_("LDAP init to '%s' failed: %s\n"),
+                     uri, ldap_err2string (ret));
+          xfree (uri);
+          return -1;
+        }
+      else if (myopt->verbose)
+        log_info (_("LDAP init to '%s' done\n"), uri);
+      xfree (uri);
+    }
+  else
+    {
+      /* Keep the old way so to avoid regressions.  Eventually we
+       * should really consider the supplied scheme and use only
+       * ldap_initialize.  */
+      npth_unprotect ();
+      ld = my_ldap_init (host, port);
+      npth_protect ();
+      if (!ld)
+        {
+          log_error (_("LDAP init to '%s:%d' failed: %s\n"),
+                     host, port, strerror (errno));
+          return -1;
+        }
     }
+
   npth_unprotect ();
   /* Fixme:  Can we use MYOPT->user or is it shared with other theeads?.  */
   ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass);
index a04bb97..ad6b088 100644 (file)
@@ -525,7 +525,7 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context,
   int argc = 0;
   int argc_malloced = 0;
   char portbuf[30], timeoutbuf[30];
-
+  int use_ldaps = 0;
 
   *context = NULL;
 
@@ -554,7 +554,7 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context,
           goto leave;
         }
       base = server->base;
-
+      use_ldaps = server->use_ldaps;
     }
   else /* Use a default server. */
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
@@ -587,6 +587,8 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context,
       argv[argc++] = "--proxy";
       argv[argc++] = proxy;
     }
+  if (use_ldaps)
+    argv[argc++] = "--tls";
   if (host)
     {
       argv[argc++] = "--host";
index 913e94f..20a2bb1 100644 (file)
@@ -55,6 +55,7 @@ ldapserver_list_free (ldap_server_t servers)
    3. field: Username
    4. field: Password
    5. field: Base DN
+   6. field: Flags
 
    FILENAME and LINENO are used for diagnostic purposes only.
 */
@@ -64,9 +65,11 @@ ldapserver_parse_one (char *line,
 {
   char *p;
   char *endp;
+  const char *s;
   ldap_server_t server;
   int fieldno;
   int fail = 0;
+  int i;
 
   /* Parse the colon separated fields.  */
   server = xcalloc (1, sizeof *server);
@@ -115,6 +118,32 @@ ldapserver_parse_one (char *line,
            server->base = xstrdup (p);
          break;
 
+        case 6:
+          {
+            char **flags = NULL;
+
+            flags = strtokenize (p, ",");
+            if (!flags)
+              log_fatal ("strtokenize failed: %s\n",
+                         gpg_strerror (gpg_error_from_syserror ()));
+
+            for (i=0; (s = flags[i]); i++)
+              {
+                if (!*s)
+                  ;
+                else if (!ascii_strcasecmp (s, "ldaps"))
+                  server->use_ldaps = 1;
+                else if (!ascii_strcasecmp (s, "ldap"))
+                  server->use_ldaps = 0;
+                else
+                  log_info (_("%s:%u: ignoring unknown flag '%s'\n"),
+                            filename, lineno, s);
+              }
+
+            xfree (flags);
+          }
+          break;
+
        default:
          /* (We silently ignore extra fields.) */
          break;
index c841de7..a6fafbb 100644 (file)
@@ -406,10 +406,14 @@ client for its session. The default value for @var{file} is
 
 This server list file contains one LDAP server per line in the format
 
-@sc{hostname:port:username:password:base_dn}
+@sc{hostname:port:username:password:base_dn:flags}
 
 Lines starting with a  @samp{#} are comments.
 
+The only defined flag is @code{ldaps} to specify that a TLS
+connections shall be used.  Flags are comma delimited; unknown flags
+are ignored.
+
 Note that as usual all strings entered are expected to be UTF-8 encoded.
 Obviously this will lead to problems if the password has originally been
 encoded as Latin-1.  There is no other solution here than to put such a
index 0745f86..130b217 100644 (file)
@@ -356,13 +356,27 @@ Note that the @command{dirmngr} can in addition be configured with a
 default list of LDAP servers to be used after those configured with
 this option.  The syntax of @var{string} is:
 
-@sc{hostname:port:username:password:base_dn}
+@sc{hostname:port:username:password:base_dn:flags}
+
+The only defined flag is @code{ldaps} to specify that a TLS
+connections shall be used.  Flags are comma delimited; unknown flags
+are ignored.
 
 Note that all parts of that string are expected to be UTF-8 encoded.
 This may lead to problems if the @sc{password} has originally been
-encoded as Latin-1; in such a case better configure this LDAP server
+encoded as Latin-1; in such a case better configure tsuch an LDAP server
 using the global configuration of @command{dirmngr}.
 
+Here is an example which uses the default port, no username, no
+password, and requests a TLS connection:
+
+@c man:.RS
+@example
+--keyserver ldap.pca.dfn.de::::o=DFN-Verein,c=DE:ldaps
+@end example
+@c man:.RE
+
+
 @item --policy-file @var{filename}
 @opindex policy-file
 Change the default name of the policy file to @var{filename}.  The
index bff7dd6..f3fe1d6 100644 (file)
@@ -223,8 +223,9 @@ prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err)
       char *pass = server->pass ? server->pass : "";
       char *base = server->base ? server->base : "";
 
-      snprintf (line, DIM (line), "LDAPSERVER %s:%i:%s:%s:%s",
-               server->host, server->port, user, pass, base);
+      snprintf (line, DIM (line), "LDAPSERVER %s:%i:%s:%s:%s:%s",
+               server->host, server->port, user, pass, base,
+                server->use_ldaps? "ldaps":"");
 
       assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
       /* The code below is not required because we don't return an error.  */
index f583707..2cd3b0c 100644 (file)
@@ -817,9 +817,17 @@ parse_keyserver_line (char *line,
 {
   char *p;
   char *endp;
+  const char *s;
   struct keyserver_spec *server;
   int fieldno;
   int fail = 0;
+  int i;
+
+  if (!filename)
+    {
+      filename = "[cmd]";
+      lineno = 0;
+    }
 
   /* Parse the colon separated fields.  */
   server = xcalloc (1, sizeof *server);
@@ -833,7 +841,7 @@ parse_keyserver_line (char *line,
        {
        case 1:
          if (*p)
-           server->host = xstrdup (p);
+            server->host = xstrdup (p);
          else
            {
              log_error (_("%s:%u: no hostname given\n"),
@@ -868,6 +876,32 @@ parse_keyserver_line (char *line,
            server->base = xstrdup (p);
          break;
 
+        case 6:
+          {
+            char **flags = NULL;
+
+            flags = strtokenize (p, ",");
+            if (!flags)
+              log_fatal ("strtokenize failed: %s\n",
+                         gpg_strerror (gpg_error_from_syserror ()));
+
+            for (i=0; (s = flags[i]); i++)
+              {
+                if (!*s)
+                  ;
+                else if (!ascii_strcasecmp (s, "ldaps"))
+                  server->use_ldaps = 1;
+                else if (!ascii_strcasecmp (s, "ldap"))
+                  server->use_ldaps = 0;
+                else
+                  log_info (_("%s:%u: ignoring unknown flag '%s'\n"),
+                            filename, lineno, s);
+              }
+
+            xfree (flags);
+          }
+          break;
+
        default:
          /* (We silently ignore extra fields.) */
          break;
index 65fff85..43793dc 100644 (file)
@@ -48,6 +48,7 @@ struct keyserver_spec
   char *user;
   char *pass;
   char *base;
+  unsigned int use_ldaps:1;
 };