Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support configurable certificate settings in LDAP SSL/TLS connections #82

Closed
vaygr opened this issue Dec 16, 2014 · 8 comments
Closed
Assignees
Milestone

Comments

@vaygr
Copy link

vaygr commented Dec 16, 2014

Here's a long-awaiting patch that I found years ago and adapted for 1.3.3-1.3.5 (afaik).
Might require some additional work/close review, but it works for me as expected with 1.3.5 on Linux x86_64.

--- contrib/mod_ldap.c.orig 2013-11-24 04:45:28.000000000 +0400
+++ contrib/mod_ldap.c  2014-05-16 13:13:12.326617345 +0400
@@ -142,7 +142,14 @@
             *ldap_attr_memberuid = "memberUid",
             *ldap_attr_ftpquota = "ftpQuota",
             *ldap_attr_ftpquota_profiledn = "ftpQuotaProfileDN",
-            *ldap_attr_ssh_pubkey = "sshPublicKey";
+            *ldap_attr_ssh_pubkey = "sshPublicKey",
+            *ldap_tls_ca_cert_dir,
+            *ldap_tls_ca_cert_file,
+            *ldap_tls_cert_file,
+            *ldap_tls_cipher_suite,
+            *ldap_tls_crl_file,
+            *ldap_tls_dh_file,
+            *ldap_tls_key_file;
 #ifdef HAS_LDAP_INITIALIZE
 static char *ldap_server_url;
 #endif /* HAS_LDAP_INITIALIZE */
@@ -152,7 +159,9 @@
            ldap_forcedefaultuid = FALSE, ldap_forcedefaultgid = FALSE,
            ldap_forcegenhdir = FALSE, ldap_protocol_version = 3,
            ldap_dereference = LDAP_DEREF_NEVER,
-           ldap_search_scope = LDAP_SCOPE_SUBTREE;
+           ldap_search_scope = LDAP_SCOPE_SUBTREE,
+           ldap_tls_crl_check = -1,
+           ldap_tls_require_cert = -1;

 static struct timeval ldap_querytimeout_tv;
 #define PR_LDAP_QUERY_TIMEOUT_DEFAULT      5
@@ -196,6 +205,86 @@
   struct berval bindcred;
 #endif

+  if (ldap_tls_ca_cert_dir) {
+    res = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR, ldap_tls_ca_cert_dir);
+    if (res != LDAP_OPT_SUCCESS) {
+      pr_log_pri(PR_LOG_ERR, MOD_LDAP_VERSION ": pr_ldap_connect(): Setting LDAP_OPT_X_TLS_CACERTDIR option failed: %s", ldap_err2string(res));
+      pr_ldap_unbind();
+      return -1;
+    }
+    pr_log_debug(DEBUG3, MOD_LDAP_VERSION ": set LDAP_OPT_X_TLS_CACERTDIR to %s", ldap_tls_ca_cert_dir); 
+  }
+ 
+  if (ldap_tls_ca_cert_file) {
+    res = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_tls_ca_cert_file);
+    if (res != LDAP_OPT_SUCCESS) {
+      pr_log_pri(PR_LOG_ERR, MOD_LDAP_VERSION ": pr_ldap_connect(): Setting LDAP_OPT_X_TLS_CACERTFILE option failed: %s", ldap_err2string(res));
+      pr_ldap_unbind();
+      return -1;
+    }
+    pr_log_debug(DEBUG3, MOD_LDAP_VERSION ": set LDAP_OPT_X_TLS_CACERTFILE to %s", ldap_tls_ca_cert_file); 
+  }
+ 
+  if (ldap_tls_cert_file) {
+    res = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE, ldap_tls_cert_file);
+    if (res != LDAP_OPT_SUCCESS) {
+      pr_log_pri(PR_LOG_ERR, MOD_LDAP_VERSION ": pr_ldap_connect(): Setting LDAP_OPT_X_TLS_CERTFILE option failed: %s", ldap_err2string(res));
+      pr_ldap_unbind();
+      return -1;
+    }
+    pr_log_debug(DEBUG3, MOD_LDAP_VERSION ": set LDAP_OPT_X_TLS_CERTFILE to %s", ldap_tls_cert_file); 
+  }
+ 
+  if (ldap_tls_cipher_suite) {
+    res = ldap_set_option(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE, ldap_tls_cipher_suite);
+    if (res != LDAP_OPT_SUCCESS) {
+      pr_log_pri(PR_LOG_ERR, MOD_LDAP_VERSION ": pr_ldap_connect(): Setting LDAP_OPT_X_TLS_CIPHER_SUITE option failed: %s", ldap_err2string(res));
+      pr_ldap_unbind();
+      return -1;
+    }
+    pr_log_debug(DEBUG3, MOD_LDAP_VERSION ": set LDAP_OPT_X_TLS_CIPHER_SUITE to %s", ldap_tls_cipher_suite); 
+  }
+ 
+  if (ldap_tls_dh_file) {
+    res = ldap_set_option(NULL, LDAP_OPT_X_TLS_DHFILE, ldap_tls_dh_file);
+    if (res != LDAP_OPT_SUCCESS) {
+      pr_log_pri(PR_LOG_ERR, MOD_LDAP_VERSION ": pr_ldap_connect(): Setting LDAP_OPT_X_TLS_DHFILE option failed: %s", ldap_err2string(res));
+      pr_ldap_unbind();
+      return -1;
+    }
+    pr_log_debug(DEBUG3, MOD_LDAP_VERSION ": set LDAP_OPT_X_TLS_DHFILE version to %s", ldap_tls_dh_file); 
+  }
+ 
+  if (ldap_tls_key_file) {
+    res = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE, ldap_tls_key_file);
+    if (res != LDAP_OPT_SUCCESS) {
+      pr_log_pri(PR_LOG_ERR, MOD_LDAP_VERSION ": pr_ldap_connect(): Setting LDAP_OPT_X_TLS_KEYFILE option failed: %s", ldap_err2string(res));
+      pr_ldap_unbind();
+      return -1;
+    }
+    pr_log_debug(DEBUG3, MOD_LDAP_VERSION ": pr_ldap_connect(): set LDAP_OPT_X_TLS_KEYFILE to %s", ldap_tls_key_file); 
+  }
+
+  if (ldap_tls_crl_check != -1) {
+    res = ldap_set_option(NULL, LDAP_OPT_X_TLS_CRLCHECK, (void *)&ldap_tls_crl_check);
+    if (res != LDAP_OPT_SUCCESS) {
+      pr_log_pri(PR_LOG_ERR, MOD_LDAP_VERSION ": pr_ldap_connect(): Setting LDAP_OPT_X_TLS_CRLCHECK option failed: %s", ldap_err2string(res));
+      pr_ldap_unbind();
+      return -1;
+    }
+    pr_log_debug(DEBUG3, MOD_LDAP_VERSION ": set LDAP_OPT_X_TLS_CRLCHECK to %d", ldap_tls_crl_check); 
+  } 
+ 
+  if (ldap_tls_require_cert != -1) {
+    res = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, (void *)&ldap_tls_require_cert);
+    if (res != LDAP_OPT_SUCCESS) {
+      pr_log_pri(PR_LOG_ERR, MOD_LDAP_VERSION ": pr_ldap_connect(): Setting LDAP_OPT_X_TLS_REQUIRE_CERT option failed: %s", ldap_err2string(res));
+      pr_ldap_unbind();
+      return -1;
+    }
+    pr_log_debug(DEBUG3, MOD_LDAP_VERSION ": set LDAP_OPT_X_TLS_REQUIRE_CERT to %d", ldap_tls_require_cert); 
+  }
+
 #ifdef HAS_LDAP_INITIALIZE
   (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
     "attempting connection to URL %s",
@@ -2029,6 +2118,130 @@
   return PR_HANDLED(cmd);
 }

+MODRET
+set_ldap_tls_ca_cert_dir(cmd_rec *cmd)
+{
+  CHECK_ARGS(cmd, 1);
+  CHECK_CONF(cmd, CONF_ROOT | CONF_VIRTUAL | CONF_GLOBAL);
+
+  add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
+  return PR_HANDLED(cmd);
+}
+
+MODRET
+set_ldap_tls_ca_cert_file(cmd_rec *cmd)
+{
+  CHECK_ARGS(cmd, 1);
+  CHECK_CONF(cmd, CONF_ROOT | CONF_VIRTUAL | CONF_GLOBAL);
+
+  add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
+  return PR_HANDLED(cmd);
+}
+
+MODRET
+set_ldap_tls_cert_file(cmd_rec *cmd)
+{
+  CHECK_ARGS(cmd, 1);
+  CHECK_CONF(cmd, CONF_ROOT | CONF_VIRTUAL | CONF_GLOBAL);
+
+  add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
+  return PR_HANDLED(cmd);
+}
+
+MODRET
+set_ldap_tls_cipher_suite(cmd_rec *cmd)
+{
+  CHECK_ARGS(cmd, 1);
+  CHECK_CONF(cmd, CONF_ROOT | CONF_VIRTUAL | CONF_GLOBAL);
+
+  add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
+  return PR_HANDLED(cmd);
+}
+
+MODRET
+set_ldap_tls_crl_check(cmd_rec *cmd)
+{
+  int value;
+  config_rec *c;
+
+  CHECK_ARGS(cmd, 1);
+  CHECK_CONF(cmd, CONF_ROOT | CONF_VIRTUAL | CONF_GLOBAL);
+
+  if (strcasecmp(cmd->argv[1], "none") == 0) {
+    value = LDAP_OPT_X_TLS_CRL_NONE;
+  } else if (strcasecmp(cmd->argv[1], "peer") == 0) {
+    value = LDAP_OPT_X_TLS_CRL_PEER;
+  } else if (strcasecmp(cmd->argv[1], "all") == 0) {
+    value = LDAP_OPT_X_TLS_CRL_ALL;
+  } else {
+    CONF_ERROR(cmd, "LDAPTLSCrlCheck: expected a valid LDAP_OPT_X_TLS_CRLCHECK option (none, peer, all).");
+  }
+
+  c = add_config_param("LDAPTLSCrlCheck", 1, NULL);
+  c->argv[0] = pcalloc(c->pool, sizeof(int));
+  *((int *) c->argv[0]) = value;
+  return PR_HANDLED(cmd);
+}
+
+MODRET
+set_ldap_tls_dh_file(cmd_rec *cmd)
+{
+  CHECK_ARGS(cmd, 1);
+  CHECK_CONF(cmd, CONF_ROOT | CONF_VIRTUAL | CONF_GLOBAL);
+
+  add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
+  return PR_HANDLED(cmd);
+}
+
+MODRET
+set_ldap_tls_crl_file(cmd_rec *cmd)
+{
+  CHECK_ARGS(cmd, 1);
+  CHECK_CONF(cmd, CONF_ROOT | CONF_VIRTUAL | CONF_GLOBAL);
+
+  add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
+  return PR_HANDLED(cmd);
+}
+
+MODRET
+set_ldap_tls_key_file(cmd_rec *cmd)
+{
+  CHECK_ARGS(cmd, 1);
+  CHECK_CONF(cmd, CONF_ROOT | CONF_VIRTUAL | CONF_GLOBAL);
+
+  add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
+  return PR_HANDLED(cmd);
+}
+
+MODRET
+set_ldap_tls_require_cert(cmd_rec *cmd)
+{
+  int value;
+  config_rec *c;
+
+  CHECK_ARGS(cmd, 1);
+  CHECK_CONF(cmd, CONF_ROOT | CONF_VIRTUAL | CONF_GLOBAL);
+
+  if (strcasecmp(cmd->argv[1], "never") == 0) {
+    value = LDAP_OPT_X_TLS_NEVER;
+  } else if (strcasecmp(cmd->argv[1], "hard") == 0) {
+    value = LDAP_OPT_X_TLS_HARD;
+  } else if (strcasecmp(cmd->argv[1], "demand") == 0) {
+    value = LDAP_OPT_X_TLS_DEMAND;
+  } else if (strcasecmp(cmd->argv[1], "allow") == 0) {
+    value = LDAP_OPT_X_TLS_ALLOW;
+  } else if (strcasecmp(cmd->argv[1], "try") == 0) {
+    value = LDAP_OPT_X_TLS_TRY;   
+  } else {
+    CONF_ERROR(cmd, "LDAPTLSRequireCert: expected a valid LDAP_OPT_X_TLS_REQUIRE_CERT option (never, hard, demand, allow, try).");
+  }
+
+  c = add_config_param("LDAPTLSRequireCert", 1, NULL);
+  c->argv[0] = pcalloc(c->pool, sizeof(int));
+  *((int *) c->argv[0]) = value;
+  return PR_HANDLED(cmd);
+}
+
 /* Initialization routines
  */

@@ -2279,6 +2492,22 @@
     }
   }

+  ldap_tls_ca_cert_dir =  (char *)get_param_ptr(main_server->conf, "LDAPTLSCACertDir", FALSE);
+  ldap_tls_ca_cert_file = (char *)get_param_ptr(main_server->conf, "LDAPTLSCACertFile", FALSE);
+  ldap_tls_cert_file =    (char *)get_param_ptr(main_server->conf, "LDAPTLSCertFile", FALSE);
+  ldap_tls_cipher_suite = (char *)get_param_ptr(main_server->conf, "LDAPTLSCipherSuite", FALSE);
+  ldap_tls_crl_file =     (char *)get_param_ptr(main_server->conf, "LDAPTLSCrlFile", FALSE);
+  ldap_tls_dh_file =      (char *)get_param_ptr(main_server->conf, "LDAPTLSDHFile", FALSE);
+  ldap_tls_key_file =     (char *)get_param_ptr(main_server->conf, "LDAPTLSKeyFile", FALSE);
+  ptr = get_param_ptr(main_server->conf, "LDAPTLSCrlCheck", FALSE);
+  if (ptr) {
+    ldap_tls_crl_check = *((int *) ptr);
+  }
+  ptr = get_param_ptr(main_server->conf, "LDAPTLSRequireCert", FALSE);
+  if (ptr) {
+    ldap_tls_require_cert = *((int *) ptr);
+  }
+
   return 0;
 }

@@ -2309,7 +2538,15 @@
   { "LDAPServer",      set_ldapserver,         NULL },
   { "LDAPUsers",       set_ldapuserlookups,        NULL },
   { "LDAPUseTLS",      set_ldapusetls,         NULL },
-
+  { "LDAPTLSCACertDir",        set_ldap_tls_ca_cert_dir,   NULL },
+  { "LDAPTLSCACertFile",   set_ldap_tls_ca_cert_file,  NULL },
+  { "LDAPTLSCertFile",     set_ldap_tls_cert_file,     NULL },
+  { "LDAPTLSCipherSuite",  set_ldap_tls_cipher_suite,  NULL },
+  { "LDAPTLSCrlCheck",     set_ldap_tls_crl_check,     NULL },
+  { "LDAPTLSCrlFile",      set_ldap_tls_crl_file,      NULL },
+  { "LDAPTLSDHFile",       set_ldap_tls_dh_file,       NULL },
+  { "LDAPTLSKeyFile",      set_ldap_tls_key_file,      NULL },
+  { "LDAPTLSRequireCert",  set_ldap_tls_require_cert,  NULL },
   { NULL, NULL, NULL },
 };
@Castaglia
Copy link
Member

I like the idea of this patch; in a similar vein, I have received requests about how to configure SSL/TLS certs for e.g. mod_sql talking to the SQL database via SSL/TLS (see Bug#4200).

So now I'm wondering whether these sorts of settings can be reused and/or configured in mod_tls, and used by other modules such as mod_ldap, mod_sql, etc. What would you think of that idea?

@vaygr
Copy link
Author

vaygr commented Mar 30, 2015

I'm not sure it would be correct, because usually those certificates are different. Of course there are cases when someone use one certificate+key for everything on 200 boxes, but I wouldn't trust such "secure" network :-) And at least client certificates for LDAP and SQL database should not be the same.

@Castaglia
Copy link
Member

Agreed about using different certificates for SQL databases vs LDAP directories. For the recent 1.3.6rc2 release, I ended up enhancing the existing mod_sql directives to take optional TLS-related parameters (for cert, key, and CA). I'm thinking that this patch could be adjusted to do something similar for mod_ldap, e.g.:

LDAPServer ... ssl-ca:/path/to/ca.pem ssl-cert:/path/to/cert.pem ssl-key:/path/to/key.pem

Given the way that TLS stacks are moving away from CRLs and more towards OCSP, I'm thinking that the CRL-related tweaks may not be needed. At least not initially. Thoughts?

@Castaglia Castaglia self-assigned this Mar 19, 2016
@Castaglia Castaglia added this to the 1.3.7 milestone Feb 27, 2017
@silverl
Copy link

silverl commented Jul 12, 2017

What root certificates does mod_ldap check against? I'm having trouble using LDAPS against Active Directory using a server certificate generated from our enterprise CA. I would have assumed it would check the local list of root certs.

I'm on CentOS 7. I've added our trusted root certificate to the certificate store and proved it's in the ca-bundle.crt list following these directions.

I can confirm connectivity to my AD server using the openssl client, but mod_ldap fails to connect.

Thanks.

@Castaglia
Copy link
Member

@silverl Assuming your mod_ldap uses the OpenLDAP library, then you would look for your /etc/openldap/ldap.conf file; this is the configuration file for OpenLDAP client functionality (including the library-using applications like mod_ldap). In that ldap.conf, you would look for (and/or add) the TLS_CACERT and/or TLS_CACERTDIR directives, per the [OpenLDAP TLS configuration docs](http://www.openldap.org/doc/admin24/tls.html#TLS Configuration); see section 16.2.2.1 and 16.2.2.2.

@silverl
Copy link

silverl commented Jul 13, 2017

We can close this out. I figured it out. I believe I was having trouble due to specifying ldaps:// urls and also having LDAPUseTLS on.

I think the documentation could be made much more explicit in warning users NOT to enable LDAPUseTLS if you want to use ldaps:// URLs. The openldap libraries will throw an error or warning when TLS is already engaged and startTLS is attempted. I think this was causing the problem.

@Castaglia
Copy link
Member

@silverl Issue #946 should address the configuration issue you encountered.

@Castaglia Castaglia changed the title Certificate options support in LDAP SSL/TLS connections Support configurable certificate settings in LDAP SSL/TLS connections Mar 29, 2020
Castaglia added a commit that referenced this issue Mar 29, 2020
… the

`LDAPServer` directive, as opposed to the ldap.conf(5) file.
Castaglia added a commit that referenced this issue Mar 29, 2020
Issue #82: Implement support for configuring SSL-related settings via…
@Castaglia
Copy link
Member

Now supported in master, using a slightly different syntax (to allow for server-specific settings).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants