Re: updated patch for Start TLS for LDAP clients (RFC 2830)

Seth Grover <[email protected]> Tue, 10 Dec 2019 15:09:00 -0700
Newsgroups gmane.network.stunnel.user
Message-ID <CAD13zvT9EF9Ohp0Kniw2+wBKkFuEOQBGuiyHQ3A+VD66ARS1PA@mail.gmail.com>
--===============8754002518155526001==
Content-Type: multipart/alternative; boundary="0000000000000c583e059960c38a"

--0000000000000c583e059960c38a
Content-Type: text/plain; charset="UTF-8"

On Tue, Dec 3, 2019 at 2:29 PM Seth Grover <[email protected]> wrote:

> ...
>
Back in 2013 Bart Dopheide submitted a patch to the mailing list to add LDAP
> StartTLS (elevate connection to TLS after initial connection is initiated)
> support to the list of supported protocols in protocol.c (
> https://www.stunnel.org/pipermail/stunnel-users/2013-November/004437.html).
> It doesn't look like this patch was ever accepted into stunnel.
>
> I've run into a similar requirement and have updated the patch to work
> against stunnel 5.56. In addition, there are a few other minor changes, the
> most significant being as follows. It would appear that Windows Active
> Directory servers do not implement the ldap extended response PDU in the
> same way as OpenLDAP (see this thread:
> https://www.openldap.org/lists/openldap-software/200401/msg00800.html).
> With this patch you can specify either "protocol = winldap" or "protocol =
> openldap" and have it work either way. I haven't modified the logic of
> Bart's original patch as far as OpenLDAP goes, but I have split the code
> path where applicable to handle the Windows case.
> ...
>
The patch is at the end of this message.
> ...
>

I apologize, but I had a stupid bug in the OpenLDAP portion of my patch
which I hadn't been able to test as I didn't have an OpenLDAP server
instance set up. On line 108 of my patch, this code:

resp_len = buffer_8;
>

should be changed to:

resp_len = buffer_8[0];
>

I set up an openldap instance in docker (
https://github.com/osixia/docker-openldap) and am now getting correct
results against both Active Directory and OpenLDAP. For completeness' sake,
I am including the full (corrected) patch again here:

diff -Nurp a/src/protocol.c b/src/protocol.c
--- a/src/protocol.c 2019-05-15 13:35:16.000000000 -0600
+++ b/src/protocol.c 2019-12-03 13:54:47.536940900 -0700
@@ -64,6 +64,8 @@ NOEXPORT char *pop3_server(CLI *, SERVIC
 NOEXPORT char *imap_client(CLI *, SERVICE_OPTIONS *, const PHASE);
 NOEXPORT char *imap_server(CLI *, SERVICE_OPTIONS *, const PHASE);
 NOEXPORT char *nntp_client(CLI *, SERVICE_OPTIONS *, const PHASE);
+NOEXPORT char *openldap_client(CLI *, SERVICE_OPTIONS *, const PHASE);
+NOEXPORT char *winldap_client(CLI *, SERVICE_OPTIONS *, const PHASE);
 NOEXPORT char *connect_server(CLI *, SERVICE_OPTIONS *, const PHASE);
 NOEXPORT char *connect_client(CLI *, SERVICE_OPTIONS *, const PHASE);
 #ifndef OPENSSL_NO_MD4
@@ -113,6 +115,14 @@ char *protocol(CLI *c, SERVICE_OPTIONS *
         return opt->option.client ?
             nntp_client(c, opt, phase) :
             "The 'nntp' protocol is not supported in the server mode";
+    if(!strcasecmp(opt->protocol, "openldap"))
+        return opt->option.client ?
+            openldap_client(c, opt, phase) :
+            "The 'openldap' protocol is not supported in the server mode";
+    if(!strcasecmp(opt->protocol, "winldap"))
+        return opt->option.client ?
+            winldap_client(c, opt, phase) :
+            "The 'winldap' protocol is not supported in the server mode";
     if(!strcasecmp(opt->protocol, "connect"))
         return opt->option.client ?
             connect_client(c, opt, phase) :
@@ -1119,6 +1129,182 @@ NOEXPORT char *nntp_client(CLI *c, SERVI
     return NULL;
 }

+/**************************************** LDAP, RFC 2830 */
+uint8_t ldap_startssl_message[0x1d + 2] =
+{
+  0x30,        /* tag = UNIVERSAL SEQUENCE */
+  0x1d,        /* len = 29 (the remaining number of bytes in this message)
*/
+  0x02,        /*   messageID */
+  0x01,        /*   len = 1 */
+  0x01,        /*   value = 1 (this is messageID 1) */
+               /*   --- */
+  0x77,        /*   protocolOp = APPLICATION (23) (=ExtendedRequest)
+                 *     0b01xxxxxx => APPLICATION
+                 *     0bxx1xxxxx => ?
+                 *     0xxxx10111 => 23
+               */
+  0x18,        /*   len = 24 */
+  0x80,        /*   type = requstName? */
+  0x16,        /*   len = 22 */
+  /* OID: 1.3.6.1.4.1.1466.20037 (=LDAP_START_TLS_OID)*/
+  '1', '.',
+  '3', '.',
+  '6', '.',
+  '1', '.',
+  '4', '.',
+  '1', '.',
+  '1', '4', '6', '6', '.',
+  '2', '0', '0', '3', '7'
+  /* No requestValue, as per RFC2830 (in 2.1: "The requestValue field is
absent") */
+};
+
+typedef enum {
+    LDAP_OPENLDAP,
+    LDAP_WINLDAP
+} LDAP_MODE;
+
+#define LDAP_UNIVERSAL_SEQUENCE                0x30
+#define LDAP_WINLDAP_FOUR_BYTE_LEN_FLAG        0x84
+#define LDAP_RESPONSE_MSG_ID_TYPE_INT          0x02
+#define LDAP_RESPONSE_EXPECTED_MSG_ID_LEN      0x01
+#define LDAP_RESPONSE_EXPECTED_MSG_ID          0x01
+#define LDAP_RESPONSE_EXT_RESP                 0x0a
+#define LDAP_RESPONSE_EXT_RESP_APPLICATION     0x78
+#define LDAP_RESPONSE_EXPECTED_ERR_LEN         0x01
+#define LDAP_RESPONSE_SUCCESS                  0x00
+
+NOEXPORT char *ldap_client(CLI *c, SERVICE_OPTIONS *opt, const PHASE
phase, const LDAP_MODE ldap_mode) {
+
+    /* thanks to these threads for help with these PDUs
+
https://www.stunnel.org/pipermail/stunnel-users/2013-November/004437.html
+
https://www.openldap.org/lists/openldap-software/200401/msg00800.html */
+
+    uint8_t buffer_8[1];
+    uint32_t buffer_32[1];
+    uint32_t resp_len;
+    uint8_t ldap_response[256];
+    uint8_t *resp_ptr;
+
+    (void)opt; /* squash the unused parameter warning */
+
+    if(phase!=PROTOCOL_MIDDLE)
+        return NULL;
+
+    /* send "Start TLS" request to AD server */
+    s_log(LOG_DEBUG, "Requesting LDAP Start TLS");
+    s_write(c, c->remote_fd.fd, ldap_startssl_message,
(size_t)ldap_startssl_message[1] + 2);
+
+    /* LDAP_UNIVERSAL_SEQUENCE (1 byte) */
+    s_read(c, c->remote_fd.fd, buffer_8, 1);
+    if(buffer_8[0] != LDAP_UNIVERSAL_SEQUENCE) {
+        s_log(LOG_ERR, "start tag is not UNIVERSAL SEQUENCE");
+        throw_exception(c, 1);
+    }
+
+    if(ldap_mode == LDAP_OPENLDAP) {
+      /* OpenLDAP - response length (1 byte) */
+      s_log(LOG_DEBUG, "Reading OpenLDAP message size (1 byte)");
+      s_read(c, c->remote_fd.fd, buffer_8, 1);
+      resp_len = buffer_8[0];
+
+    } else if(ldap_mode == LDAP_WINLDAP) {
+
+      /* WinLDAP - "response length is 4 bytes" flag -
LDAP_WINLDAP_FOUR_BYTE_LEN_FLAG (1-byte) */
+      s_read(c, c->remote_fd.fd, buffer_8, 1);
+      if(buffer_8[0] != LDAP_WINLDAP_FOUR_BYTE_LEN_FLAG) {
+          s_log(LOG_ERR, "LDAP message length flag is an unexpected
value");
+          throw_exception(c, 1);
+      }
+
+      /* WinLDAP - response length (4 bytes, network byte order) */
+      s_log(LOG_DEBUG, "Reading WinLDAP message size (4 bytes)");
+      s_read(c, c->remote_fd.fd, buffer_32, 4);
+      resp_len = ntohl(buffer_32[0]);
+
+    } else {
+      s_log(LOG_ERR, "Unsupported LDAP mode");
+      throw_exception(c, 1);
+    }
+
+    /* LDAP response message */
+    s_log(LOG_DEBUG, "Reading LDAP message (%u byte(s))", resp_len);
+    s_read(c, c->remote_fd.fd, ldap_response, resp_len);
+
+    resp_ptr = &ldap_response[0];
+
+    /* LDAP_RESPONSE_MSG_ID_TYPE_INT - 1 byte */
+    if(*resp_ptr != LDAP_RESPONSE_MSG_ID_TYPE_INT) {
+        s_log(LOG_ERR, "LDAP response has an incorrect message ID type");
+        throw_exception(c, 1);
+    }
+    resp_ptr++;
+
+    /* LDAP_RESPONSE_EXPECTED_MSG_ID_LEN - 1 byte */
+    if(*resp_ptr != LDAP_RESPONSE_EXPECTED_MSG_ID_LEN) {
+        s_log(LOG_ERR, "LDAP response has an unexpected message ID
length");
+        throw_exception(c, 1);
+    }
+    resp_ptr++;
+
+    /* LDAP_RESPONSE_EXPECTED_MSG_ID - 1 byte */
+    if(*resp_ptr != LDAP_RESPONSE_EXPECTED_MSG_ID) {
+        s_log(LOG_ERR, "LDAP response has an unexpected message ID");
+        throw_exception(c, 1);
+    }
+    resp_ptr++;
+
+    /* LDAP_RESPONSE_EXT_RESP_APPLICATION - 1 byte */
+    if(*resp_ptr != LDAP_RESPONSE_EXT_RESP_APPLICATION) {
+        s_log(LOG_ERR, "LDAP response protocolOp is not APPLICATION");
+        throw_exception(c, 1);
+    }
+    resp_ptr++;
+
+    if(ldap_mode == LDAP_WINLDAP) {
+      /* WinLDAP - "response length is 4 bytes" flag -
LDAP_WINLDAP_FOUR_BYTE_LEN_FLAG (1-byte) */
+      if(*resp_ptr != LDAP_WINLDAP_FOUR_BYTE_LEN_FLAG) {
+          s_log(LOG_ERR, "LDAP extendedResp length flag is an unexpected
value");
+          throw_exception(c, 1);
+      }
+      /* WinLDAP - extended response message length (4-bytes) */
+      resp_ptr += 5;
+
+    } else {
+      /* OpenLDAP - extended response message length (1-byte) */
+      resp_ptr++;
+    }
+
+    /* LDAP_RESPONSE_EXT_RESP - 1 byte */
+    if(*resp_ptr != LDAP_RESPONSE_EXT_RESP) {
+        s_log(LOG_ERR, "LDAP response type is not EXT_RESP");
+        throw_exception(c, 1);
+    }
+    resp_ptr++;
+
+    /* LDAP_RESPONSE_EXT_RESP - 1 byte */
+    if(*resp_ptr != LDAP_RESPONSE_EXPECTED_ERR_LEN) {
+        s_log(LOG_ERR, "LDAP response has an unexpected error code
length");
+        throw_exception(c, 1);
+    }
+    resp_ptr++;
+
+    if(*resp_ptr != LDAP_RESPONSE_SUCCESS) {
+        s_log(LOG_ERR, "LDAP response has indicated an error (%u)",
*resp_ptr);
+        throw_exception(c, 1);
+    }
+
+    return NULL;
+}
+
+
+NOEXPORT char *openldap_client(CLI *c, SERVICE_OPTIONS *opt, const PHASE
phase) {
+  return ldap_client(c, opt, phase, LDAP_OPENLDAP);
+}
+
+NOEXPORT char *winldap_client(CLI *c, SERVICE_OPTIONS *opt, const PHASE
phase) {
+  return ldap_client(c, opt, phase, LDAP_WINLDAP);
+}
+
 /**************************************** connect */

 NOEXPORT char *connect_server(CLI *c, SERVICE_OPTIONS *opt, const PHASE
phase) {

--0000000000000c583e059960c38a
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div class=3D"gmail_quote"><div dir=3D"ltr" class=3D"gmail=
_attr">On Tue, Dec 3, 2019 at 2:29 PM Seth Grover &lt;<a href=3D"mailto:Set=
[email protected]">[email protected]</a>&gt; wrote:<br></div><bloc=
kquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:=
1px solid rgb(204,204,204);padding-left:1ex"><div>...</div></blockquote><bl=
ockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-lef=
t:1px solid rgb(204,204,204);padding-left:1ex"><div>Back in 2013 Bart Dophe=
ide submitted a patch to the mailing list to add <span class=3D"gmail-il">L=
DAP</span>
 StartTLS (elevate connection to TLS after initial connection is=20
initiated) support to the list of supported protocols in protocol.c (<a hre=
f=3D"https://www.stunnel.org/pipermail/stunnel-users/2013-November/004437.h=
tml" target=3D"_blank">https://www.stunnel.org/pipermail/stunnel-users/2013=
-November/004437.html</a>). It doesn&#39;t look like this patch was ever ac=
cepted into stunnel.<br><br>I&#39;ve
 run into a similar requirement and have updated the patch to work=20
against stunnel 5.56. In addition, there are a few other minor changes,=20
the most significant being as follows. It would appear that Windows=20
Active Directory servers do not implement the <span class=3D"gmail-il">ldap=
</span> extended response PDU in the same way as OpenLDAP (see this thread:=
 <a href=3D"https://www.openldap.org/lists/openldap-software/200401/msg0080=
0.html" target=3D"_blank">https://www.openldap.org/lists/openldap-software/=
200401/msg00800.html</a>).
 With this patch you can specify either &quot;protocol =3D winldap&quot; or=
=20
&quot;protocol =3D openldap&quot; and have it work either way. I haven&#39;=
t modified=20
the logic of Bart&#39;s original patch as far as OpenLDAP goes, but I have=
=20
split the code path where applicable to handle the Windows case.<br>... <br=
></div></blockquote><blockquote class=3D"gmail_quote" style=3D"margin:0px 0=
px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div =
dir=3D"ltr">The patch is at the end of this message.<br>...<br></div></bloc=
kquote><div><br></div><div>I apologize, but I had a stupid bug in the OpenL=
DAP portion of my patch which I hadn&#39;t been able to test as I didn&#39;=
t have an OpenLDAP server instance set up. On line 108 of my patch, this co=
de:</div><div><br></div><blockquote class=3D"gmail_quote" style=3D"margin:0=
px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><=
div>resp_len =3D buffer_8;</div></blockquote><div><br></div><div>should be =
changed to:<br></div><div><br></div><blockquote class=3D"gmail_quote" style=
=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding=
-left:1ex"><div>resp_len =3D buffer_8[0];</div></blockquote><div><br></div>=
<div>I set up an openldap instance in docker (<a href=3D"https://github.com=
/osixia/docker-openldap">https://github.com/osixia/docker-openldap</a>) and=
 am now getting correct results against both Active Directory and OpenLDAP.=
 For completeness&#39; sake, I am including the full (corrected) patch agai=
n here:</div><div><br></div><div>diff -Nurp a/src/protocol.c b/src/protocol=
.c<br>--- a/src/protocol.c	2019-05-15 13:35:16.000000000 -0600<br>+++ b/src=
/protocol.c	2019-12-03 13:54:47.536940900 -0700<br>@@ -64,6 +64,8 @@ NOEXPO=
RT char *pop3_server(CLI *, SERVIC<br>=C2=A0NOEXPORT char *imap_client(CLI =
*, SERVICE_OPTIONS *, const PHASE);<br>=C2=A0NOEXPORT char *imap_server(CLI=
 *, SERVICE_OPTIONS *, const PHASE);<br>=C2=A0NOEXPORT char *nntp_client(CL=
I *, SERVICE_OPTIONS *, const PHASE);<br>+NOEXPORT char *openldap_client(CL=
I *, SERVICE_OPTIONS *, const PHASE);<br>+NOEXPORT char *winldap_client(CLI=
 *, SERVICE_OPTIONS *, const PHASE);<br>=C2=A0NOEXPORT char *connect_server=
(CLI *, SERVICE_OPTIONS *, const PHASE);<br>=C2=A0NOEXPORT char *connect_cl=
ient(CLI *, SERVICE_OPTIONS *, const PHASE);<br>=C2=A0#ifndef OPENSSL_NO_MD=
4<br>@@ -113,6 +115,14 @@ char *protocol(CLI *c, SERVICE_OPTIONS *<br>=C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return opt-&gt;option.client ?<br>=C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0nntp_client(c, opt, phase) :<br>=C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0&quot;The &#39;nntp&#39; proto=
col is not supported in the server mode&quot;;<br>+ =C2=A0 =C2=A0if(!strcas=
ecmp(opt-&gt;protocol, &quot;openldap&quot;))<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=
=A0return opt-&gt;option.client ?<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0openldap_client(c, opt, phase) :<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0&quot;The &#39;openldap&#39; protocol is not supported in the ser=
ver mode&quot;;<br>+ =C2=A0 =C2=A0if(!strcasecmp(opt-&gt;protocol, &quot;wi=
nldap&quot;))<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0return opt-&gt;option.client =
?<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0winldap_client(c, opt, phas=
e) :<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0&quot;The &#39;winldap&#=
39; protocol is not supported in the server mode&quot;;<br>=C2=A0 =C2=A0 =
=C2=A0if(!strcasecmp(opt-&gt;protocol, &quot;connect&quot;))<br>=C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0return opt-&gt;option.client ?<br>=C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0connect_client(c, opt, phase) :<br>@@ -1119,=
6 +1129,182 @@ NOEXPORT char *nntp_client(CLI *c, SERVI<br>=C2=A0 =C2=A0 =
=C2=A0return NULL;<br>=C2=A0}<br><br>+/************************************=
**** LDAP, RFC 2830 */<br>+uint8_t ldap_startssl_message[0x1d + 2] =3D<br>+=
{<br>+ =C2=A00x30, =C2=A0 =C2=A0 =C2=A0 =C2=A0/* tag =3D UNIVERSAL SEQUENCE=
 */<br>+ =C2=A00x1d, =C2=A0 =C2=A0 =C2=A0 =C2=A0/* len =3D 29 (the remainin=
g number of bytes in this message) */<br>+ =C2=A00x02, =C2=A0 =C2=A0 =C2=A0=
 =C2=A0/* =C2=A0 messageID */<br>+ =C2=A00x01, =C2=A0 =C2=A0 =C2=A0 =C2=A0/=
* =C2=A0 len =3D 1 */<br>+ =C2=A00x01, =C2=A0 =C2=A0 =C2=A0 =C2=A0/* =C2=A0=
 value =3D 1 (this is messageID 1) */<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 /* =C2=A0 --- */<br>+ =C2=A00x77, =C2=A0 =C2=A0 =C2=A0 =
=C2=A0/* =C2=A0 protocolOp =3D APPLICATION (23) (=3DExtendedRequest)<br>+ =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * =C2=A0 =C2=A0 0b0=
1xxxxxx =3D&gt; APPLICATION<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 * =C2=A0 =C2=A0 0bxx1xxxxx =3D&gt; ?<br>+ =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * =C2=A0 =C2=A0 0xxxx10111 =3D&gt; 2=
3<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 */<br>+ =C2=A00x18,=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* =C2=A0 len =3D 24 */<br>+ =C2=A00x80, =C2=A0=
 =C2=A0 =C2=A0 =C2=A0/* =C2=A0 type =3D requstName? */<br>+ =C2=A00x16, =C2=
=A0 =C2=A0 =C2=A0 =C2=A0/* =C2=A0 len =3D 22 */<br>+ =C2=A0/* OID: 1.3.6.1.=
4.1.1466.20037 (=3DLDAP_START_TLS_OID)*/<br>+ =C2=A0&#39;1&#39;, &#39;.&#39=
;,<br>+ =C2=A0&#39;3&#39;, &#39;.&#39;,<br>+ =C2=A0&#39;6&#39;, &#39;.&#39;=
,<br>+ =C2=A0&#39;1&#39;, &#39;.&#39;,<br>+ =C2=A0&#39;4&#39;, &#39;.&#39;,=
<br>+ =C2=A0&#39;1&#39;, &#39;.&#39;,<br>+ =C2=A0&#39;1&#39;, &#39;4&#39;, =
&#39;6&#39;, &#39;6&#39;, &#39;.&#39;,<br>+ =C2=A0&#39;2&#39;, &#39;0&#39;,=
 &#39;0&#39;, &#39;3&#39;, &#39;7&#39;<br>+ =C2=A0/* No requestValue, as pe=
r RFC2830 (in 2.1: &quot;The requestValue field is absent&quot;) */<br>+};<=
br>+<br>+typedef enum {<br>+ =C2=A0 =C2=A0LDAP_OPENLDAP,<br>+ =C2=A0 =C2=A0=
LDAP_WINLDAP<br>+} LDAP_MODE;<br>+<br>+#define LDAP_UNIVERSAL_SEQUENCE =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x30<br>+#define LDAP_W=
INLDAP_FOUR_BYTE_LEN_FLAG =C2=A0 =C2=A0 =C2=A0 =C2=A00x84<br>+#define LDAP_=
RESPONSE_MSG_ID_TYPE_INT =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x02<br>+#define=
 LDAP_RESPONSE_EXPECTED_MSG_ID_LEN =C2=A0 =C2=A0 =C2=A00x01<br>+#define LDA=
P_RESPONSE_EXPECTED_MSG_ID =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x01<br>+#defi=
ne LDAP_RESPONSE_EXT_RESP =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 0x0a<br>+#define LDAP_RESPONSE_EXT_RESP_APPLICATION =C2=A0 =C2=A0 0x=
78<br>+#define LDAP_RESPONSE_EXPECTED_ERR_LEN =C2=A0 =C2=A0 =C2=A0 =C2=A0 0=
x01<br>+#define LDAP_RESPONSE_SUCCESS =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A00x00<br>+<br>+NOEXPORT char *ldap_client(CLI *c,=
 SERVICE_OPTIONS *opt, const PHASE phase, const LDAP_MODE ldap_mode) {<br>+=
<br>+ =C2=A0 =C2=A0/* thanks to these threads for help with these PDUs<br>+=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 <a href=3D"https://www.stunnel.org/pipermail/s=
tunnel-users/2013-November/004437.html">https://www.stunnel.org/pipermail/s=
tunnel-users/2013-November/004437.html</a><br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 <a href=3D"https://www.openldap.org/lists/openldap-software/200401/msg0080=
0.html">https://www.openldap.org/lists/openldap-software/200401/msg00800.ht=
ml</a> */<br>+<br>+ =C2=A0 =C2=A0uint8_t buffer_8[1];<br>+ =C2=A0 =C2=A0uin=
t32_t buffer_32[1];<br>+ =C2=A0 =C2=A0uint32_t resp_len;<br>+ =C2=A0 =C2=A0=
uint8_t ldap_response[256];<br>+ =C2=A0 =C2=A0uint8_t *resp_ptr;<br>+<br>+ =
=C2=A0 =C2=A0(void)opt; /* squash the unused parameter warning */<br>+<br>+=
 =C2=A0 =C2=A0if(phase!=3DPROTOCOL_MIDDLE)<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0=
return NULL;<br>+<br>+ =C2=A0 =C2=A0/* send &quot;Start TLS&quot; request t=
o AD server */<br>+ =C2=A0 =C2=A0s_log(LOG_DEBUG, &quot;Requesting LDAP Sta=
rt TLS&quot;);<br>+ =C2=A0 =C2=A0s_write(c, c-&gt;remote_fd.fd, ldap_starts=
sl_message, (size_t)ldap_startssl_message[1] + 2);<br>+<br>+ =C2=A0 =C2=A0/=
* LDAP_UNIVERSAL_SEQUENCE (1 byte) */<br>+ =C2=A0 =C2=A0s_read(c, c-&gt;rem=
ote_fd.fd, buffer_8, 1);<br>+ =C2=A0 =C2=A0if(buffer_8[0] !=3D LDAP_UNIVERS=
AL_SEQUENCE) {<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0s_log(LOG_ERR, &quot;start t=
ag is not UNIVERSAL SEQUENCE&quot;);<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0throw_=
exception(c, 1);<br>+ =C2=A0 =C2=A0}<br>+<br>+ =C2=A0 =C2=A0if(ldap_mode =
=3D=3D LDAP_OPENLDAP) {<br>+ =C2=A0 =C2=A0 =C2=A0/* OpenLDAP - response len=
gth (1 byte) */<br>+ =C2=A0 =C2=A0 =C2=A0s_log(LOG_DEBUG, &quot;Reading Ope=
nLDAP message size (1 byte)&quot;);<br>+ =C2=A0 =C2=A0 =C2=A0s_read(c, c-&g=
t;remote_fd.fd, buffer_8, 1);<br>+ =C2=A0 =C2=A0 =C2=A0resp_len =3D buffer_=
8[0];<br>+<br>+ =C2=A0 =C2=A0} else if(ldap_mode =3D=3D LDAP_WINLDAP) {<br>=
+<br>+ =C2=A0 =C2=A0 =C2=A0/* WinLDAP - &quot;response length is 4 bytes&qu=
ot; flag - LDAP_WINLDAP_FOUR_BYTE_LEN_FLAG (1-byte) */<br>+ =C2=A0 =C2=A0 =
=C2=A0s_read(c, c-&gt;remote_fd.fd, buffer_8, 1);<br>+ =C2=A0 =C2=A0 =C2=A0=
if(buffer_8[0] !=3D LDAP_WINLDAP_FOUR_BYTE_LEN_FLAG) {<br>+ =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0s_log(LOG_ERR, &quot;LDAP message length flag is an une=
xpected value&quot;);<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0throw_exceptio=
n(c, 1);<br>+ =C2=A0 =C2=A0 =C2=A0}<br>+<br>+ =C2=A0 =C2=A0 =C2=A0/* WinLDA=
P - response length (4 bytes, network byte order) */<br>+ =C2=A0 =C2=A0 =C2=
=A0s_log(LOG_DEBUG, &quot;Reading WinLDAP message size (4 bytes)&quot;);<br=
>+ =C2=A0 =C2=A0 =C2=A0s_read(c, c-&gt;remote_fd.fd, buffer_32, 4);<br>+ =
=C2=A0 =C2=A0 =C2=A0resp_len =3D ntohl(buffer_32[0]);<br>+<br>+ =C2=A0 =C2=
=A0} else {<br>+ =C2=A0 =C2=A0 =C2=A0s_log(LOG_ERR, &quot;Unsupported LDAP =
mode&quot;);<br>+ =C2=A0 =C2=A0 =C2=A0throw_exception(c, 1);<br>+ =C2=A0 =
=C2=A0}<br>+<br>+ =C2=A0 =C2=A0/* LDAP response message */<br>+ =C2=A0 =C2=
=A0s_log(LOG_DEBUG, &quot;Reading LDAP message (%u byte(s))&quot;, resp_len=
);<br>+ =C2=A0 =C2=A0s_read(c, c-&gt;remote_fd.fd, ldap_response, resp_len)=
;<br>+<br>+ =C2=A0 =C2=A0resp_ptr =3D &amp;ldap_response[0];<br>+<br>+ =C2=
=A0 =C2=A0/* LDAP_RESPONSE_MSG_ID_TYPE_INT - 1 byte */<br>+ =C2=A0 =C2=A0if=
(*resp_ptr !=3D LDAP_RESPONSE_MSG_ID_TYPE_INT) {<br>+ =C2=A0 =C2=A0 =C2=A0 =
=C2=A0s_log(LOG_ERR, &quot;LDAP response has an incorrect message ID type&q=
uot;);<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0throw_exception(c, 1);<br>+ =C2=A0 =
=C2=A0}<br>+ =C2=A0 =C2=A0resp_ptr++;<br>+<br>+ =C2=A0 =C2=A0/* LDAP_RESPON=
SE_EXPECTED_MSG_ID_LEN - 1 byte */<br>+ =C2=A0 =C2=A0if(*resp_ptr !=3D LDAP=
_RESPONSE_EXPECTED_MSG_ID_LEN) {<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0s_log(LOG_=
ERR, &quot;LDAP response has an unexpected message ID length&quot;);<br>+ =
=C2=A0 =C2=A0 =C2=A0 =C2=A0throw_exception(c, 1);<br>+ =C2=A0 =C2=A0}<br>+ =
=C2=A0 =C2=A0resp_ptr++;<br>+<br>+ =C2=A0 =C2=A0/* LDAP_RESPONSE_EXPECTED_M=
SG_ID - 1 byte */<br>+ =C2=A0 =C2=A0if(*resp_ptr !=3D LDAP_RESPONSE_EXPECTE=
D_MSG_ID) {<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0s_log(LOG_ERR, &quot;LDAP respo=
nse has an unexpected message ID&quot;);<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0th=
row_exception(c, 1);<br>+ =C2=A0 =C2=A0}<br>+ =C2=A0 =C2=A0resp_ptr++;<br>+=
<br>+ =C2=A0 =C2=A0/* LDAP_RESPONSE_EXT_RESP_APPLICATION - 1 byte */<br>+ =
=C2=A0 =C2=A0if(*resp_ptr !=3D LDAP_RESPONSE_EXT_RESP_APPLICATION) {<br>+ =
=C2=A0 =C2=A0 =C2=A0 =C2=A0s_log(LOG_ERR, &quot;LDAP response protocolOp is=
 not APPLICATION&quot;);<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0throw_exception(c,=
 1);<br>+ =C2=A0 =C2=A0}<br>+ =C2=A0 =C2=A0resp_ptr++;<br>+<br>+ =C2=A0 =C2=
=A0if(ldap_mode =3D=3D LDAP_WINLDAP) {<br>+ =C2=A0 =C2=A0 =C2=A0/* WinLDAP =
- &quot;response length is 4 bytes&quot; flag - LDAP_WINLDAP_FOUR_BYTE_LEN_=
FLAG (1-byte) */<br>+ =C2=A0 =C2=A0 =C2=A0if(*resp_ptr !=3D LDAP_WINLDAP_FO=
UR_BYTE_LEN_FLAG) {<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s_log(LOG_ERR, &=
quot;LDAP extendedResp length flag is an unexpected value&quot;);<br>+ =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0throw_exception(c, 1);<br>+ =C2=A0 =C2=A0 =
=C2=A0}<br>+ =C2=A0 =C2=A0 =C2=A0/* WinLDAP - extended response message len=
gth (4-bytes) */<br>+ =C2=A0 =C2=A0 =C2=A0resp_ptr +=3D 5;<br>+<br>+ =C2=A0=
 =C2=A0} else {<br>+ =C2=A0 =C2=A0 =C2=A0/* OpenLDAP - extended response me=
ssage length (1-byte) */<br>+ =C2=A0 =C2=A0 =C2=A0resp_ptr++;<br>+ =C2=A0 =
=C2=A0}<br>+<br>+ =C2=A0 =C2=A0/* LDAP_RESPONSE_EXT_RESP - 1 byte */<br>+ =
=C2=A0 =C2=A0if(*resp_ptr !=3D LDAP_RESPONSE_EXT_RESP) {<br>+ =C2=A0 =C2=A0=
 =C2=A0 =C2=A0s_log(LOG_ERR, &quot;LDAP response type is not EXT_RESP&quot;=
);<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0throw_exception(c, 1);<br>+ =C2=A0 =C2=
=A0}<br>+ =C2=A0 =C2=A0resp_ptr++;<br>+<br>+ =C2=A0 =C2=A0/* LDAP_RESPONSE_=
EXT_RESP - 1 byte */<br>+ =C2=A0 =C2=A0if(*resp_ptr !=3D LDAP_RESPONSE_EXPE=
CTED_ERR_LEN) {<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0s_log(LOG_ERR, &quot;LDAP r=
esponse has an unexpected error code length&quot;);<br>+ =C2=A0 =C2=A0 =C2=
=A0 =C2=A0throw_exception(c, 1);<br>+ =C2=A0 =C2=A0}<br>+ =C2=A0 =C2=A0resp=
_ptr++;<br>+<br>+ =C2=A0 =C2=A0if(*resp_ptr !=3D LDAP_RESPONSE_SUCCESS) {<b=
r>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0s_log(LOG_ERR, &quot;LDAP response has indic=
ated an error (%u)&quot;, *resp_ptr);<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0throw=
_exception(c, 1);<br>+ =C2=A0 =C2=A0}<br>+<br>+ =C2=A0 =C2=A0return NULL;<b=
r>+}<br>+<br>+<br>+NOEXPORT char *openldap_client(CLI *c, SERVICE_OPTIONS *=
opt, const PHASE phase) {<br>+ =C2=A0return ldap_client(c, opt, phase, LDAP=
_OPENLDAP);<br>+}<br>+<br>+NOEXPORT char *winldap_client(CLI *c, SERVICE_OP=
TIONS *opt, const PHASE phase) {<br>+ =C2=A0return ldap_client(c, opt, phas=
e, LDAP_WINLDAP);<br>+}<br>+<br>=C2=A0/************************************=
**** connect */<br><br>=C2=A0NOEXPORT char *connect_server(CLI *c, SERVICE_=
OPTIONS *opt, const PHASE phase) {<br></div><div><br></div></div></div>

--0000000000000c583e059960c38a--

--===============8754002518155526001==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
stunnel-users mailing list
[email protected]
https://www.stunnel.org/cgi-bin/mailman/listinfo/stunnel-users

--===============8754002518155526001==--