]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
openldap: implement STARTTLS
authorPatrick Monnerat <patrick@monnerat.net>
Tue, 30 Nov 2021 16:48:28 +0000 (17:48 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 6 Dec 2021 06:53:04 +0000 (07:53 +0100)
As this introduces use of CURLOPT_USE_SSL option for LDAP, also check
this option in ldap.c as it is not supported by this backend.

Closes #8065

docs/cmdline-opts/ssl-reqd.d
docs/cmdline-opts/ssl.d
docs/libcurl/opts/CURLOPT_USE_SSL.3
lib/ldap.c
lib/openldap.c

index 81e0ea794a1f49f80dbb803d0747f1020c47b89b..fb9a1aa838a6d9325fa7b946633fd0d827a145a2 100644 (file)
@@ -1,6 +1,6 @@
 Long: ssl-reqd
 Help: Require SSL/TLS
-Protocols: FTP IMAP POP3 SMTP
+Protocols: FTP IMAP POP3 SMTP LDAP
 Added: 7.20.0
 Category: tls
 Example: --ssl-reqd ftp://example.com
@@ -9,4 +9,8 @@ See-also: ssl insecure
 Require SSL/TLS for the connection.  Terminates the connection if the server
 does not support SSL/TLS.
 
+This option is handled in LDAP since version 7.81.0. It is fully supported
+by the openldap backend and rejected by the generic ldap backend if explicit
+TLS is required.
+
 This option was formerly known as --ftp-ssl-reqd.
index 96e4169aaa511944d058a8f8a384d40f4a2c888b..83cac5c9d620578657faae5b0cead233fdfce335 100644 (file)
@@ -1,6 +1,6 @@
 Long: ssl
 Help: Try SSL/TLS
-Protocols: FTP IMAP POP3 SMTP
+Protocols: FTP IMAP POP3 SMTP LDAP
 Added: 7.20.0
 Category: tls
 Example: --ssl pop3://example.com/
@@ -10,5 +10,11 @@ Try to use SSL/TLS for the connection. Reverts to a non-secure connection if
 the server does not support SSL/TLS. See also --ftp-ssl-control and --ssl-reqd
 for different levels of encryption required.
 
+This option is handled in LDAP since version 7.81.0. It is fully supported
+by the openldap backend and ignored by the generic ldap backend.
+
+Please note that a server may close the connection if the negotiation does
+not succeed.
+
 This option was formerly known as --ftp-ssl (Added in 7.11.0). That option
 name can still be used but will be removed in a future version.
index 767be1f363263b910dbf1a7f43a8153b4bb3d2b4..3fcc2dfc57526d87c8f581728a9268dca456e68d 100644 (file)
@@ -40,7 +40,8 @@ This is for enabling SSL/TLS when you use FTP, SMTP, POP3, IMAP etc.
 .IP CURLUSESSL_NONE
 do not attempt to use SSL.
 .IP CURLUSESSL_TRY
-Try using SSL, proceed as normal otherwise.
+Try using SSL, proceed as normal otherwise. Note that server may close the
+connection if the negotiation does not succeed.
 .IP CURLUSESSL_CONTROL
 Require SSL for the control connection or fail with \fICURLE_USE_SSL_FAILED\fP.
 .IP CURLUSESSL_ALL
@@ -48,7 +49,7 @@ Require SSL for all communication or fail with \fICURLE_USE_SSL_FAILED\fP.
 .SH DEFAULT
 CURLUSESSL_NONE
 .SH PROTOCOLS
-FTP, SMTP, POP3, IMAP
+FTP, SMTP, POP3, IMAP, LDAP
 .SH EXAMPLE
 .nf
 CURL *curl = curl_easy_init();
@@ -65,6 +66,7 @@ if(curl) {
 .SH AVAILABILITY
 Added in 7.11.0. This option was known as CURLOPT_FTP_SSL up to 7.16.4, and
 the constants were known as CURLFTPSSL_*
+Handled by LDAP since 7.81.0. Fully supported by the openldap backend only.
 .SH RETURN VALUE
 Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
 .SH "SEE ALSO"
index 1d9e44cc9c2fe17ff59e2dc011d5e49f33a75653..284f165ea32d722d2727c4c30d65b83271e0613d 100644 (file)
@@ -464,6 +464,11 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
 #endif
 #endif /* CURL_LDAP_USE_SSL */
   }
+  else if(data->set.use_ssl > CURLUSESSL_TRY) {
+    failf(data, "LDAP local: explicit TLS not supported");
+    result = CURLE_NOT_BUILT_IN;
+    goto quit;
+  }
   else {
     server = ldap_init(host, (int)conn->port);
     if(!server) {
index ba632d85bda760044397cd270f6d65bef39cda6b..003d0f8e59cab5b5f5879b2d556b94637da7ca41 100644 (file)
@@ -74,6 +74,8 @@
 typedef enum {
   OLDAP_STOP,           /* Do nothing state, stops the state machine */
   OLDAP_SSL,            /* Performing SSL handshake. */
+  OLDAP_STARTTLS,       /* STARTTLS request sent. */
+  OLDAP_TLS,            /* Performing TLS handshake. */
   OLDAP_BIND,           /* Simple bind reply. */
   OLDAP_BINDV2,         /* Simple bind reply in protocol version 2. */
   OLDAP_LAST            /* Never used */
@@ -194,6 +196,8 @@ static void state(struct Curl_easy *data, ldapstate newstate)
   static const char * const names[] = {
     "STOP",
     "SSL",
+    "STARTTLS",
+    "TLS",
     "BIND",
     "BINDV2",
     /* LAST */
@@ -256,6 +260,10 @@ static CURLcode oldap_setup_connection(struct Curl_easy *data,
   li->proto = proto;
   conn->proto.ldapc = li;
   connkeep(conn, "OpenLDAP default");
+
+  /* Clear the TLS upgraded flag */
+  conn->bits.tls_upgraded = FALSE;
+
   return CURLE_OK;
 }
 
@@ -297,7 +305,7 @@ static bool ssl_installed(struct connectdata *conn)
   return conn->proto.ldapc->recv != NULL;
 }
 
-static CURLcode oldap_ssl_connect(struct Curl_easy *data)
+static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
@@ -307,7 +315,7 @@ static CURLcode oldap_ssl_connect(struct Curl_easy *data)
   result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
                                         FIRSTSOCKET, &ssldone);
   if(!result) {
-    state(data, OLDAP_SSL);
+    state(data, newstate);
 
     if(ssldone) {
       Sockbuf *sb;
@@ -322,6 +330,20 @@ static CURLcode oldap_ssl_connect(struct Curl_easy *data)
 
   return result;
 }
+
+/* Send the STARTTLS request */
+static CURLcode oldap_perform_starttls(struct Curl_easy *data)
+{
+  CURLcode result = CURLE_OK;
+  struct ldapconninfo *li = data->conn->proto.ldapc;
+  int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
+
+  if(rc == LDAP_SUCCESS)
+    state(data, OLDAP_STARTTLS);
+  else
+    result = oldap_map_error(rc, CURLE_USE_SSL_FAILED);
+  return result;
+}
 #endif
 
 static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
@@ -364,7 +386,14 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
 
 #ifdef USE_SSL
   if(conn->handler->flags & PROTOPT_SSL)
-    return oldap_ssl_connect(data);
+    return oldap_ssl_connect(data, OLDAP_SSL);
+
+  if(data->set.use_ssl) {
+    CURLcode result = oldap_perform_starttls(data);
+
+    if(!result || data->set.use_ssl != CURLUSESSL_TRY)
+      return result;
+  }
 #endif
 
   /* Force bind even if anonymous bind is not needed in protocol version 3
@@ -409,7 +438,7 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
   int code = LDAP_SUCCESS;
   int rc;
 
-  if(li->state != OLDAP_SSL) {
+  if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) {
     /* Get response to last command. */
     rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
     if(!rc)
@@ -426,7 +455,11 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
       code = rc;
 
     /* If protocol version 3 is not supported, fallback to version 2. */
-    if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2) {
+    if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2
+#ifdef USE_SSL
+       && (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY)
+#endif
+       ) {
       static const int version = LDAP_VERSION2;
 
       ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
@@ -440,10 +473,33 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
 
 #ifdef USE_SSL
   case OLDAP_SSL:
-    result = oldap_ssl_connect(data);
+    result = oldap_ssl_connect(data, OLDAP_SSL);
     if(!result && ssl_installed(conn))
       result = oldap_perform_bind(data, OLDAP_BIND);
     break;
+  case OLDAP_STARTTLS:
+    if(code != LDAP_SUCCESS) {
+      if(data->set.use_ssl != CURLUSESSL_TRY)
+        result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
+      else
+        result = oldap_perform_bind(data, OLDAP_BIND);
+      break;
+    }
+    /* FALLTHROUGH */
+  case OLDAP_TLS:
+    result = oldap_ssl_connect(data, OLDAP_TLS);
+    if(result && data->set.use_ssl != CURLUSESSL_TRY)
+      result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
+    else if(ssl_installed(conn)) {
+      conn->bits.tls_upgraded = TRUE;
+      if(conn->bits.user_passwd)
+        result = oldap_perform_bind(data, OLDAP_BIND);
+      else {
+        state(data, OLDAP_STOP); /* Version 3 supported: no bind required */
+        result = CURLE_OK;
+      }
+    }
+    break;
 #endif
 
   case OLDAP_BIND: