]> BookStack Code Mirror - bookstack/blobdiff - app/Access/LdapService.php
LDAP: Review, testing and update of LDAP TLS CA cert control
[bookstack] / app / Access / LdapService.php
index 56e7aba04f7db005564799578a587fd6d0eff460..e0d7f2615b3ef248d6163f3ae2a0df10406a8cbd 100644 (file)
@@ -209,10 +209,10 @@ class LdapService
             $this->ldap->setOption(null, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER);
         }
 
-        // Specify CA Cert file for LDAP.
+        // Configure any user-provided CA cert files for LDAP.
         // This option works globally and must be set before a connection is created.
-        if ($this->config['tls_cacertfile']) {
-            $this->ldap->setOption(null, LDAP_OPT_X_TLS_CACERTFILE, $this->config['tls_cacertfile']);
+        if ($this->config['tls_ca_cert']) {
+            $this->configureTlsCaCerts($this->config['tls_ca_cert']);
         }
 
         $ldapHost = $this->parseServerString($this->config['server']);
@@ -229,7 +229,14 @@ class LdapService
 
         // Start and verify TLS if it's enabled
         if ($this->config['start_tls']) {
-            $started = $this->ldap->startTls($ldapConnection);
+            try {
+                $started = $this->ldap->startTls($ldapConnection);
+            } catch (\Exception $exception) {
+                $error = $exception->getMessage() . ' :: ' . ldap_error($ldapConnection);
+                ldap_get_option($ldapConnection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $detail);
+                Log::info("LDAP STARTTLS failure: {$error} {$detail}");
+                throw new LdapException('Could not start TLS connection. Further details in the application log.');
+            }
             if (!$started) {
                 throw new LdapException('Could not start TLS connection');
             }
@@ -240,6 +247,33 @@ class LdapService
         return $this->ldapConnection;
     }
 
+    /**
+     * Configure TLS CA certs globally for ldap use.
+     * This will detect if the given path is a directory or file, and set the relevant
+     * LDAP TLS options appropriately otherwise throw an exception if no file/folder found.
+     *
+     * Note: When using a folder, certificates are expected to be correctly named by hash
+     * which can be done via the c_rehash utility.
+     *
+     * @throws LdapException
+     */
+    protected function configureTlsCaCerts(string $caCertPath): void
+    {
+        $errMessage = "Provided path [{$caCertPath}] for LDAP TLS CA certs could not be resolved to an existing location";
+        $path = realpath($caCertPath);
+        if ($path === false) {
+            throw new LdapException($errMessage);
+        }
+
+        if (is_dir($path)) {
+            $this->ldap->setOption(null, LDAP_OPT_X_TLS_CACERTDIR, $path);
+        } else if (is_file($path)) {
+            $this->ldap->setOption(null, LDAP_OPT_X_TLS_CACERTFILE, $path);
+        } else {
+            throw new LdapException($errMessage);
+        }
+    }
+
     /**
      * Parse an LDAP server string and return the host suitable for a connection.
      * Is flexible to formats such as 'ldap.example.com:8069' or 'ldaps://ldap.example.com'.