Blame SOURCES/openssl-1.0.2k-starttls.patch

cfec1a
diff -up openssl-1.0.2k/apps/apps.c.starttls openssl-1.0.2k/apps/apps.c
cfec1a
--- openssl-1.0.2k/apps/apps.c.starttls	2017-01-26 14:22:03.000000000 +0100
cfec1a
+++ openssl-1.0.2k/apps/apps.c	2017-03-09 17:35:35.519765927 +0100
cfec1a
@@ -3277,3 +3277,11 @@ int raw_write_stdout(const void *buf, in
cfec1a
     return write(fileno_stdout(), buf, siz);
cfec1a
 }
cfec1a
 #endif
cfec1a
+
cfec1a
+void make_uppercase(char *string)
cfec1a
+{
cfec1a
+    int i;
cfec1a
+
cfec1a
+    for (i = 0; string[i] != '\0'; i++)
cfec1a
+        string[i] = toupper((unsigned char)string[i]);
cfec1a
+}
cfec1a
diff -up openssl-1.0.2k/apps/apps.h.starttls openssl-1.0.2k/apps/apps.h
cfec1a
--- openssl-1.0.2k/apps/apps.h.starttls	2017-03-09 17:35:28.632604234 +0100
cfec1a
+++ openssl-1.0.2k/apps/apps.h	2017-03-09 17:35:35.520765950 +0100
cfec1a
@@ -384,6 +384,8 @@ int raw_write_stdout(const void *, int);
cfec1a
 # define TM_STOP         1
cfec1a
 double app_tminterval(int stop, int usertime);
cfec1a
 
cfec1a
+void make_uppercase(char *string);
cfec1a
+
cfec1a
 # define OPENSSL_NO_SSL_INTERN
cfec1a
 
cfec1a
 #endif
cfec1a
diff -up openssl-1.0.2k/apps/s_client.c.starttls openssl-1.0.2k/apps/s_client.c
cfec1a
--- openssl-1.0.2k/apps/s_client.c.starttls	2017-03-09 17:35:28.684605455 +0100
cfec1a
+++ openssl-1.0.2k/apps/s_client.c	2017-03-09 17:52:59.153207946 +0100
cfec1a
@@ -134,7 +134,8 @@
cfec1a
  * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
cfec1a
  * OTHERWISE.
cfec1a
  */
cfec1a
-
cfec1a
+/* for strcasestr */
cfec1a
+#define _GNU_SOURCE
cfec1a
 #include <assert.h>
cfec1a
 #include <ctype.h>
cfec1a
 #include <stdio.h>
cfec1a
@@ -202,6 +203,7 @@ static char *krb5svc = NULL;
cfec1a
 
cfec1a
 #undef BUFSIZZ
cfec1a
 #define BUFSIZZ 1024*8
cfec1a
+#define S_CLIENT_IRC_READ_TIMEOUT 8
cfec1a
 
cfec1a
 extern int verify_depth;
cfec1a
 extern int verify_error;
cfec1a
@@ -228,6 +230,7 @@ static void print_stuff(BIO *berr, SSL *
cfec1a
 #ifndef OPENSSL_NO_TLSEXT
cfec1a
 static int ocsp_resp_cb(SSL *s, void *arg);
cfec1a
 #endif
cfec1a
+static int ldap_ExtendedResponse_parse(const char *buf, long rem);
cfec1a
 static BIO *bio_c_out = NULL;
cfec1a
 static BIO *bio_c_msg = NULL;
cfec1a
 static int c_quiet = 0;
cfec1a
@@ -402,8 +405,14 @@ static void sc_usage(void)
cfec1a
     BIO_printf(bio_err,
cfec1a
                "                 'prot' defines which one to assume.  Currently,\n");
cfec1a
     BIO_printf(bio_err,
cfec1a
-               "                 only \"smtp\", \"pop3\", \"imap\", \"ftp\" and \"xmpp\"\n");
cfec1a
-    BIO_printf(bio_err, "                 are supported.\n");
cfec1a
+               "                 only \"smtp\", \"pop3\", \"imap\", \"ftp\", \"xmpp\",\n");
cfec1a
+    BIO_printf(bio_err,
cfec1a
+               "                 \"xmpp-server\", \"irc\", \"postgres\", \"lmtp\", \"nntp\",\n");
cfec1a
+    BIO_printf(bio_err, "                 \"sieve\" and \"ldap\" are supported.\n");
cfec1a
+    BIO_printf(bio_err,
cfec1a
+               " -xmpphost host - Host to use with \"-starttls xmpp[-server]\"\n");
cfec1a
+    BIO_printf(bio_err,
cfec1a
+               " -name host     - Hostname to use for \"-starttls lmtp\" or \"-starttls smtp\"\n");
cfec1a
 #ifndef OPENSSL_NO_KRB5
cfec1a
     BIO_printf(bio_err, " -krb5svc arg  - Kerberos service name\n");
cfec1a
 #endif
cfec1a
@@ -657,7 +666,15 @@ enum {
cfec1a
     PROTO_POP3,
cfec1a
     PROTO_IMAP,
cfec1a
     PROTO_FTP,
cfec1a
-    PROTO_XMPP
cfec1a
+    PROTO_TELNET,
cfec1a
+    PROTO_XMPP,
cfec1a
+    PROTO_XMPP_SERVER,
cfec1a
+    PROTO_IRC,
cfec1a
+    PROTO_POSTGRES,
cfec1a
+    PROTO_LMTP,
cfec1a
+    PROTO_NNTP,
cfec1a
+    PROTO_SIEVE,
cfec1a
+    PROTO_LDAP
cfec1a
 };
cfec1a
 
cfec1a
 int MAIN(int, char **);
cfec1a
@@ -726,6 +743,8 @@ int MAIN(int argc, char **argv)
cfec1a
 #endif
cfec1a
     char *sess_in = NULL;
cfec1a
     char *sess_out = NULL;
cfec1a
+    char *xmpphost = NULL;
cfec1a
+    const char *ehlo = "openssl.client.net";
cfec1a
     struct sockaddr peer;
cfec1a
     int peerlen = sizeof(peer);
cfec1a
     int fallback_scsv = 0;
cfec1a
@@ -1097,8 +1116,32 @@ int MAIN(int argc, char **argv)
cfec1a
                 starttls_proto = PROTO_FTP;
cfec1a
             else if (strcmp(*argv, "xmpp") == 0)
cfec1a
                 starttls_proto = PROTO_XMPP;
cfec1a
+            else if (strcmp(*argv, "xmpp-server") == 0)
cfec1a
+                starttls_proto = PROTO_XMPP_SERVER;
cfec1a
+            else if (strcmp(*argv, "telnet") == 0)
cfec1a
+                starttls_proto = PROTO_TELNET;
cfec1a
+            else if (strcmp(*argv, "irc") == 0)
cfec1a
+                starttls_proto = PROTO_IRC;
cfec1a
+            else if (strcmp(*argv, "postgres") == 0)
cfec1a
+                starttls_proto = PROTO_POSTGRES;
cfec1a
+            else if (strcmp(*argv, "lmtp") == 0)
cfec1a
+                starttls_proto = PROTO_LMTP;
cfec1a
+            else if (strcmp(*argv, "nntp") == 0)
cfec1a
+                starttls_proto = PROTO_NNTP;
cfec1a
+            else if (strcmp(*argv, "sieve") == 0)
cfec1a
+                starttls_proto = PROTO_SIEVE;
cfec1a
+            else if (strcmp(*argv, "ldap") == 0)
cfec1a
+                starttls_proto = PROTO_LDAP;
cfec1a
             else
cfec1a
                 goto bad;
cfec1a
+        } else if (strcmp(*argv, "-xmpphost") == 0) {
cfec1a
+            if (--argc < 1)
cfec1a
+                goto bad;
cfec1a
+            xmpphost = *(++argv);
cfec1a
+        } else if (strcmp(*argv, "-name") == 0) {
cfec1a
+            if (--argc < 1)
cfec1a
+                goto bad;
cfec1a
+            ehlo = *(++argv);
cfec1a
         }
cfec1a
 #ifndef OPENSSL_NO_ENGINE
cfec1a
         else if (strcmp(*argv, "-engine") == 0) {
cfec1a
@@ -1599,19 +1642,24 @@ int MAIN(int argc, char **argv)
cfec1a
      * BIO into the chain that is removed again later on to not disturb the
cfec1a
      * rest of the s_client operation.
cfec1a
      */
cfec1a
-    if (starttls_proto == PROTO_SMTP) {
cfec1a
+    if (starttls_proto == PROTO_SMTP || starttls_proto == PROTO_LMTP) {
cfec1a
         int foundit = 0;
cfec1a
         BIO *fbio = BIO_new(BIO_f_buffer());
cfec1a
         BIO_push(fbio, sbio);
cfec1a
-        /* wait for multi-line response to end from SMTP */
cfec1a
+        /* Wait for multi-line response to end from LMTP or SMTP */
cfec1a
         do {
cfec1a
             mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
cfec1a
         }
cfec1a
         while (mbuf_len > 3 && mbuf[3] == '-');
cfec1a
-        /* STARTTLS command requires EHLO... */
cfec1a
-        BIO_printf(fbio, "EHLO openssl.client.net\r\n");
cfec1a
+        if (starttls_proto == PROTO_LMTP)
cfec1a
+            BIO_printf(fbio, "LHLO %s\r\n", ehlo);
cfec1a
+        else
cfec1a
+            BIO_printf(fbio, "EHLO %s\r\n", ehlo);
cfec1a
         (void)BIO_flush(fbio);
cfec1a
-        /* wait for multi-line response to end EHLO SMTP response */
cfec1a
+        /*
cfec1a
+         * Wait for multi-line response to end LHLO LMTP or EHLO SMTP
cfec1a
+         * response.
cfec1a
+         */
cfec1a
         do {
cfec1a
             mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
cfec1a
             if (strstr(mbuf, "STARTTLS"))
cfec1a
@@ -1630,10 +1678,15 @@ int MAIN(int argc, char **argv)
cfec1a
     } else if (starttls_proto == PROTO_POP3) {
cfec1a
         BIO_read(sbio, mbuf, BUFSIZZ);
cfec1a
         BIO_printf(sbio, "STLS\r\n");
cfec1a
-        BIO_read(sbio, sbuf, BUFSIZZ);
cfec1a
+        mbuf_len = BIO_read(sbio, sbuf, BUFSIZZ);
cfec1a
+        if (mbuf_len < 0) {
cfec1a
+            BIO_printf(bio_err, "BIO_read failed\n");
cfec1a
+            goto end;
cfec1a
+        }
cfec1a
     } else if (starttls_proto == PROTO_IMAP) {
cfec1a
         int foundit = 0;
cfec1a
         BIO *fbio = BIO_new(BIO_f_buffer());
cfec1a
+
cfec1a
         BIO_push(fbio, sbio);
cfec1a
         BIO_gets(fbio, mbuf, BUFSIZZ);
cfec1a
         /* STARTTLS command requires CAPABILITY... */
cfec1a
@@ -1669,27 +1722,287 @@ int MAIN(int argc, char **argv)
cfec1a
         BIO_printf(sbio, "AUTH TLS\r\n");
cfec1a
         BIO_read(sbio, sbuf, BUFSIZZ);
cfec1a
     }
cfec1a
-    if (starttls_proto == PROTO_XMPP) {
cfec1a
+    else if (starttls_proto == PROTO_XMPP || starttls_proto == PROTO_XMPP_SERVER) {
cfec1a
         int seen = 0;
cfec1a
         BIO_printf(sbio, "
cfec1a
                    "xmlns:stream='http://etherx.jabber.org/streams' "
cfec1a
-                   "xmlns='jabber:client' to='%s' version='1.0'>", host);
cfec1a
+                   "xmlns='jabber:%s' to='%s' version='1.0'>",
cfec1a
+                   starttls_proto == PROTO_XMPP ? "client" : "server",
cfec1a
+                   xmpphost ? xmpphost : host);
cfec1a
         seen = BIO_read(sbio, mbuf, BUFSIZZ);
cfec1a
+        if (seen < 0) {
cfec1a
+            BIO_printf(bio_err, "BIO_read failed\n");
cfec1a
+            goto end;
cfec1a
+        }
cfec1a
         mbuf[seen] = 0;
cfec1a
-        while (!strstr
cfec1a
-               (mbuf, "
cfec1a
-            if (strstr(mbuf, "/stream:features>"))
cfec1a
-                goto shut;
cfec1a
+        while (!strcasestr
cfec1a
+               (mbuf, "
cfec1a
+               && !strcasestr(mbuf,
cfec1a
+                              "
cfec1a
+        {
cfec1a
             seen = BIO_read(sbio, mbuf, BUFSIZZ);
cfec1a
+
cfec1a
+            if (seen <= 0)
cfec1a
+               goto shut;
cfec1a
+
cfec1a
             mbuf[seen] = 0;
cfec1a
         }
cfec1a
         BIO_printf(sbio,
cfec1a
                    "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
cfec1a
         seen = BIO_read(sbio, sbuf, BUFSIZZ);
cfec1a
+        if (seen < 0) {
cfec1a
+            BIO_printf(bio_err, "BIO_read failed\n");
cfec1a
+            goto shut;
cfec1a
+        }
cfec1a
         sbuf[seen] = 0;
cfec1a
         if (!strstr(sbuf, "
cfec1a
             goto shut;
cfec1a
         mbuf[0] = 0;
cfec1a
+    } else if (starttls_proto == PROTO_TELNET) {
cfec1a
+        static const unsigned char tls_do[] = {
cfec1a
+            /* IAC    DO   START_TLS */
cfec1a
+               255,   253, 46
cfec1a
+        };
cfec1a
+        static const unsigned char tls_will[] = {
cfec1a
+            /* IAC  WILL START_TLS */
cfec1a
+               255, 251, 46
cfec1a
+        };
cfec1a
+        static const unsigned char tls_follows[] = {
cfec1a
+            /* IAC  SB   START_TLS FOLLOWS IAC  SE */
cfec1a
+               255, 250, 46,       1,      255, 240
cfec1a
+        };
cfec1a
+        int bytes;
cfec1a
+
cfec1a
+        /* Telnet server should demand we issue START_TLS */
cfec1a
+        bytes = BIO_read(sbio, mbuf, BUFSIZZ);
cfec1a
+        if (bytes != 3 || memcmp(mbuf, tls_do, 3) != 0)
cfec1a
+            goto shut;
cfec1a
+        /* Agree to issue START_TLS and send the FOLLOWS sub-command */
cfec1a
+        BIO_write(sbio, tls_will, 3);
cfec1a
+        BIO_write(sbio, tls_follows, 6);
cfec1a
+        (void)BIO_flush(sbio);
cfec1a
+        /* Telnet server also sent the FOLLOWS sub-command */
cfec1a
+        bytes = BIO_read(sbio, mbuf, BUFSIZZ);
cfec1a
+        if (bytes != 6 || memcmp(mbuf, tls_follows, 6) != 0)
cfec1a
+            goto shut;
cfec1a
+    } else if (starttls_proto == PROTO_IRC) {
cfec1a
+        int numeric;
cfec1a
+        BIO *fbio = BIO_new(BIO_f_buffer());
cfec1a
+
cfec1a
+        BIO_push(fbio, sbio);
cfec1a
+        BIO_printf(fbio, "STARTTLS\r\n");
cfec1a
+        (void)BIO_flush(fbio);
cfec1a
+        width = SSL_get_fd(con) + 1;
cfec1a
+
cfec1a
+        do {
cfec1a
+            numeric = 0;
cfec1a
+
cfec1a
+            FD_ZERO(&readfds);
cfec1a
+            openssl_fdset(SSL_get_fd(con), &readfds);
cfec1a
+            timeout.tv_sec = S_CLIENT_IRC_READ_TIMEOUT;
cfec1a
+            timeout.tv_usec = 0;
cfec1a
+            /*
cfec1a
+             * If the IRCd doesn't respond within
cfec1a
+             * S_CLIENT_IRC_READ_TIMEOUT seconds, assume
cfec1a
+             * it doesn't support STARTTLS. Many IRCds
cfec1a
+             * will not give _any_ sort of response to a
cfec1a
+             * STARTTLS command when it's not supported.
cfec1a
+             */
cfec1a
+            if (!BIO_get_buffer_num_lines(fbio)
cfec1a
+                && !BIO_pending(fbio)
cfec1a
+                && !BIO_pending(sbio)
cfec1a
+                && select(width, (void *)&readfds, NULL, NULL,
cfec1a
+                          &timeout) < 1) {
cfec1a
+                BIO_printf(bio_err,
cfec1a
+                           "Timeout waiting for response (%d seconds).\n",
cfec1a
+                           S_CLIENT_IRC_READ_TIMEOUT);
cfec1a
+                break;
cfec1a
+            }
cfec1a
+
cfec1a
+            mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
cfec1a
+            if (mbuf_len < 1 || sscanf(mbuf, "%*s %d", &numeric) != 1)
cfec1a
+                break;
cfec1a
+            /* :example.net 451 STARTTLS :You have not registered */
cfec1a
+            /* :example.net 421 STARTTLS :Unknown command */
cfec1a
+            if ((numeric == 451 || numeric == 421)
cfec1a
+                && strstr(mbuf, "STARTTLS") != NULL) {
cfec1a
+                BIO_printf(bio_err, "STARTTLS not supported: %s", mbuf);
cfec1a
+                break;
cfec1a
+            }
cfec1a
+            if (numeric == 691) {
cfec1a
+                BIO_printf(bio_err, "STARTTLS negotiation failed: ");
cfec1a
+                ERR_print_errors(bio_err);
cfec1a
+                break;
cfec1a
+            }
cfec1a
+        } while (numeric != 670);
cfec1a
+
cfec1a
+        (void)BIO_flush(fbio);
cfec1a
+        BIO_pop(fbio);
cfec1a
+        BIO_free(fbio);
cfec1a
+        if (numeric != 670) {
cfec1a
+            BIO_printf(bio_err, "Server does not support STARTTLS.\n");
cfec1a
+            ret = 1;
cfec1a
+            goto shut;
cfec1a
+        }
cfec1a
+    } else if (starttls_proto == PROTO_POSTGRES) {
cfec1a
+        static const unsigned char ssl_request[] = {
cfec1a
+            /* Length        SSLRequest */
cfec1a
+               0, 0, 0, 8,   4, 210, 22, 47
cfec1a
+        };
cfec1a
+        int bytes;
cfec1a
+
cfec1a
+        /* Send SSLRequest packet */
cfec1a
+        BIO_write(sbio, ssl_request, 8);
cfec1a
+        (void)BIO_flush(sbio);
cfec1a
+
cfec1a
+        /* Reply will be a single S if SSL is enabled */
cfec1a
+        bytes = BIO_read(sbio, sbuf, BUFSIZZ);
cfec1a
+        if (bytes != 1 || sbuf[0] != 'S')
cfec1a
+            goto shut;
cfec1a
+    } else if (starttls_proto == PROTO_NNTP) {
cfec1a
+        int foundit = 0;
cfec1a
+        BIO *fbio = BIO_new(BIO_f_buffer());
cfec1a
+
cfec1a
+        BIO_push(fbio, sbio);
cfec1a
+        BIO_gets(fbio, mbuf, BUFSIZZ);
cfec1a
+        /* STARTTLS command requires CAPABILITIES... */
cfec1a
+        BIO_printf(fbio, "CAPABILITIES\r\n");
cfec1a
+        (void)BIO_flush(fbio);
cfec1a
+        /* wait for multi-line CAPABILITIES response */
cfec1a
+        do {
cfec1a
+            mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
cfec1a
+            if (strstr(mbuf, "STARTTLS"))
cfec1a
+                foundit = 1;
cfec1a
+        } while (mbuf_len > 1 && mbuf[0] != '.');
cfec1a
+        (void)BIO_flush(fbio);
cfec1a
+        BIO_pop(fbio);
cfec1a
+        BIO_free(fbio);
cfec1a
+        if (!foundit)
cfec1a
+            BIO_printf(bio_err,
cfec1a
+                       "Didn't find STARTTLS in server response,"
cfec1a
+                       " trying anyway...\n");
cfec1a
+        BIO_printf(sbio, "STARTTLS\r\n");
cfec1a
+        mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ);
cfec1a
+        if (mbuf_len < 0) {
cfec1a
+            BIO_printf(bio_err, "BIO_read failed\n");
cfec1a
+            goto end;
cfec1a
+        }
cfec1a
+        mbuf[mbuf_len] = '\0';
cfec1a
+        if (strstr(mbuf, "382") == NULL) {
cfec1a
+            BIO_printf(bio_err, "STARTTLS failed: %s", mbuf);
cfec1a
+            goto shut;
cfec1a
+        }
cfec1a
+    } else if (starttls_proto == PROTO_SIEVE) {
cfec1a
+        int foundit = 0;
cfec1a
+        BIO *fbio = BIO_new(BIO_f_buffer());
cfec1a
+
cfec1a
+        BIO_push(fbio, sbio);
cfec1a
+        /* wait for multi-line response to end from Sieve */
cfec1a
+        do {
cfec1a
+            mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
cfec1a
+            /*
cfec1a
+             * According to RFC 5804 ยง 1.7, capability
cfec1a
+             * is case-insensitive, make it uppercase
cfec1a
+             */
cfec1a
+            if (mbuf_len > 1 && mbuf[0] == '"') {
cfec1a
+                make_uppercase(mbuf);
cfec1a
+                if (strncmp(mbuf, "\"STARTTLS\"", 10) == 0)
cfec1a
+                    foundit = 1;
cfec1a
+            }
cfec1a
+        } while (mbuf_len > 1 && mbuf[0] == '"');
cfec1a
+        (void)BIO_flush(fbio);
cfec1a
+        BIO_pop(fbio);
cfec1a
+        BIO_free(fbio);
cfec1a
+        if (!foundit)
cfec1a
+            BIO_printf(bio_err,
cfec1a
+                       "Didn't find STARTTLS in server response,"
cfec1a
+                       " trying anyway...\n");
cfec1a
+        BIO_printf(sbio, "STARTTLS\r\n");
cfec1a
+        mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ);
cfec1a
+        if (mbuf_len < 0) {
cfec1a
+            BIO_printf(bio_err, "BIO_read failed\n");
cfec1a
+            goto end;
cfec1a
+        }
cfec1a
+        mbuf[mbuf_len] = '\0';
cfec1a
+        if (mbuf_len < 2) {
cfec1a
+            BIO_printf(bio_err, "STARTTLS failed: %s", mbuf);
cfec1a
+            goto shut;
cfec1a
+        }
cfec1a
+        /*
cfec1a
+         * According to RFC 5804 ยง 2.2, response codes are case-
cfec1a
+         * insensitive, make it uppercase but preserve the response.
cfec1a
+         */
cfec1a
+        strncpy(sbuf, mbuf, 2);
cfec1a
+        make_uppercase(sbuf);
cfec1a
+        if (strncmp(sbuf, "OK", 2) != 0) {
cfec1a
+            BIO_printf(bio_err, "STARTTLS not supported: %s", mbuf);
cfec1a
+            goto shut;
cfec1a
+        }
cfec1a
+    } else if (starttls_proto == PROTO_LDAP) {
cfec1a
+        /* StartTLS Operation according to RFC 4511 */
cfec1a
+        static char ldap_tls_genconf[] = "asn1=SEQUENCE:LDAPMessage\n"
cfec1a
+            "[LDAPMessage]\n"
cfec1a
+            "messageID=INTEGER:1\n"
cfec1a
+            "extendedReq=EXPLICIT:23A,IMPLICIT:0C,"
cfec1a
+            "FORMAT:ASCII,OCT:1.3.6.1.4.1.1466.20037\n";
cfec1a
+        long errline = -1;
cfec1a
+        char *genstr = NULL;
cfec1a
+        int result = -1;
cfec1a
+        ASN1_TYPE *atyp = NULL;
cfec1a
+        BIO *ldapbio = BIO_new(BIO_s_mem());
cfec1a
+        CONF *cnf = NCONF_new(NULL);
cfec1a
+
cfec1a
+        if (cnf == NULL) {
cfec1a
+            BIO_free(ldapbio);
cfec1a
+            goto end;
cfec1a
+        }
cfec1a
+        BIO_puts(ldapbio, ldap_tls_genconf);
cfec1a
+        if (NCONF_load_bio(cnf, ldapbio, &errline) <= 0) {
cfec1a
+            BIO_free(ldapbio);
cfec1a
+            NCONF_free(cnf);
cfec1a
+            if (errline <= 0) {
cfec1a
+                BIO_printf(bio_err, "NCONF_load_bio failed\n");
cfec1a
+                goto end;
cfec1a
+            } else {
cfec1a
+                BIO_printf(bio_err, "Error on line %ld\n", errline);
cfec1a
+                goto end;
cfec1a
+            }
cfec1a
+        }
cfec1a
+        BIO_free(ldapbio);
cfec1a
+        genstr = NCONF_get_string(cnf, "default", "asn1");
cfec1a
+        if (genstr == NULL) {
cfec1a
+            NCONF_free(cnf);
cfec1a
+            BIO_printf(bio_err, "NCONF_get_string failed\n");
cfec1a
+            goto end;
cfec1a
+        }
cfec1a
+        atyp = ASN1_generate_nconf(genstr, cnf);
cfec1a
+        if (atyp == NULL) {
cfec1a
+            NCONF_free(cnf);
cfec1a
+            BIO_printf(bio_err, "ASN1_generate_nconf failed\n");
cfec1a
+            goto end;
cfec1a
+        }
cfec1a
+        NCONF_free(cnf);
cfec1a
+        /* Send SSLRequest packet */
cfec1a
+        BIO_write(sbio, atyp->value.sequence->data,
cfec1a
+                  atyp->value.sequence->length);
cfec1a
+        (void)BIO_flush(sbio);
cfec1a
+        ASN1_TYPE_free(atyp);
cfec1a
+
cfec1a
+        mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ);
cfec1a
+        if (mbuf_len < 0) {
cfec1a
+            BIO_printf(bio_err, "BIO_read failed\n");
cfec1a
+            goto end;
cfec1a
+        }
cfec1a
+        result = ldap_ExtendedResponse_parse(mbuf, mbuf_len);
cfec1a
+        if (result < 0) {
cfec1a
+            BIO_printf(bio_err, "ldap_ExtendedResponse_parse failed\n");
cfec1a
+            goto shut;
cfec1a
+        } else if (result > 0) {
cfec1a
+            BIO_printf(bio_err, "STARTTLS failed, LDAP Result Code: %i\n",
cfec1a
+                       result);
cfec1a
+            goto shut;
cfec1a
+        }
cfec1a
+        mbuf_len = 0;
cfec1a
     }
cfec1a
 
cfec1a
     for (;;) {
cfec1a
@@ -1738,7 +2051,7 @@ int MAIN(int argc, char **argv)
cfec1a
                     full_log--;
cfec1a
 
cfec1a
                 if (starttls_proto) {
cfec1a
-                    BIO_printf(bio_err, "%s", mbuf);
cfec1a
+                    BIO_write(bio_err, mbuf, mbuf_len);
cfec1a
                     /* We don't need to know any more */
cfec1a
                     starttls_proto = PROTO_OFF;
cfec1a
                 }
cfec1a
@@ -2372,3 +2685,87 @@ static int ocsp_resp_cb(SSL *s, void *ar
cfec1a
 }
cfec1a
 
cfec1a
 #endif
cfec1a
+
cfec1a
+static int ldap_ExtendedResponse_parse(const char *buf, long rem)
cfec1a
+{
cfec1a
+    const unsigned char *cur, *end;
cfec1a
+    long len;
cfec1a
+    int tag, xclass, inf, ret = -1;
cfec1a
+
cfec1a
+    cur = (const unsigned char *)buf;
cfec1a
+    end = cur + rem;
cfec1a
+
cfec1a
+    /*
cfec1a
+     * From RFC 4511:
cfec1a
+     *
cfec1a
+     *    LDAPMessage ::= SEQUENCE {
cfec1a
+     *         messageID       MessageID,
cfec1a
+     *         protocolOp      CHOICE {
cfec1a
+     *              ...
cfec1a
+     *              extendedResp          ExtendedResponse,
cfec1a
+     *              ... },
cfec1a
+     *         controls       [0] Controls OPTIONAL }
cfec1a
+     *
cfec1a
+     *    ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
cfec1a
+     *         COMPONENTS OF LDAPResult,
cfec1a
+     *         responseName     [10] LDAPOID OPTIONAL,
cfec1a
+     *         responseValue    [11] OCTET STRING OPTIONAL }
cfec1a
+     *
cfec1a
+     *    LDAPResult ::= SEQUENCE {
cfec1a
+     *         resultCode         ENUMERATED {
cfec1a
+     *              success                      (0),
cfec1a
+     *              ...
cfec1a
+     *              other                        (80),
cfec1a
+     *              ...  },
cfec1a
+     *         matchedDN          LDAPDN,
cfec1a
+     *         diagnosticMessage  LDAPString,
cfec1a
+     *         referral           [3] Referral OPTIONAL }
cfec1a
+     */
cfec1a
+
cfec1a
+    /* pull SEQUENCE */
cfec1a
+    inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem);
cfec1a
+    if (inf != V_ASN1_CONSTRUCTED || tag != V_ASN1_SEQUENCE ||
cfec1a
+        (rem = end - cur, len > rem)) {
cfec1a
+        BIO_printf(bio_err, "Unexpected LDAP response\n");
cfec1a
+        goto end;
cfec1a
+    }
cfec1a
+
cfec1a
+    rem = len;  /* ensure that we don't overstep the SEQUENCE */
cfec1a
+
cfec1a
+    /* pull MessageID */
cfec1a
+    inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem);
cfec1a
+    if (inf != V_ASN1_UNIVERSAL || tag != V_ASN1_INTEGER ||
cfec1a
+        (rem = end - cur, len > rem)) {
cfec1a
+        BIO_printf(bio_err, "No MessageID\n");
cfec1a
+        goto end;
cfec1a
+    }
cfec1a
+
cfec1a
+    cur += len; /* shall we check for MessageId match or just skip? */
cfec1a
+
cfec1a
+    /* pull [APPLICATION 24] */
cfec1a
+    rem = end - cur;
cfec1a
+    inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem);
cfec1a
+    if (inf != V_ASN1_CONSTRUCTED || xclass != V_ASN1_APPLICATION ||
cfec1a
+        tag != 24) {
cfec1a
+        BIO_printf(bio_err, "Not ExtendedResponse\n");
cfec1a
+        goto end;
cfec1a
+    }
cfec1a
+
cfec1a
+    /* pull resultCode */
cfec1a
+    rem = end - cur;
cfec1a
+    inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem);
cfec1a
+    if (inf != V_ASN1_UNIVERSAL || tag != V_ASN1_ENUMERATED || len == 0 ||
cfec1a
+        (rem = end - cur, len > rem)) {
cfec1a
+        BIO_printf(bio_err, "Not LDAPResult\n");
cfec1a
+        goto end;
cfec1a
+    }
cfec1a
+
cfec1a
+    /* len should always be one, but just in case... */
cfec1a
+    for (ret = 0, inf = 0; inf < len; inf++) {
cfec1a
+        ret <<= 8;
cfec1a
+        ret |= cur[inf];
cfec1a
+    }
cfec1a
+    /* There is more data, but we don't care... */
cfec1a
+ end:
cfec1a
+    return ret;
cfec1a
+}
cfec1a
diff -up openssl-1.0.2k/doc/apps/s_client.pod.starttls openssl-1.0.2k/doc/apps/s_client.pod
cfec1a
--- openssl-1.0.2k/doc/apps/s_client.pod.starttls	2017-03-09 17:35:28.684605455 +0100
cfec1a
+++ openssl-1.0.2k/doc/apps/s_client.pod	2017-03-09 17:42:54.455070967 +0100
cfec1a
@@ -46,6 +46,8 @@ B<openssl> B<s_client>
cfec1a
 [B<-krb5svc service>]
cfec1a
 [B<-serverpref>]
cfec1a
 [B<-starttls protocol>]
cfec1a
+[B<-xmpphost hostname>]
cfec1a
+[B<-name hostname>]
cfec1a
 [B<-engine id>]
cfec1a
 [B<-tlsextdebug>]
cfec1a
 [B<-no_ticket>]
cfec1a
@@ -239,7 +241,20 @@ need keys for that principal in its keyt
cfec1a
 
cfec1a
 send the protocol-specific message(s) to switch to TLS for communication.
cfec1a
 B<protocol> is a keyword for the intended protocol.  Currently, the only
cfec1a
-supported keywords are "smtp", "pop3", "imap", and "ftp".
cfec1a
+supported keywords are "smtp", "pop3", "imap", "ftp", "xmpp", "xmpp-server",
cfec1a
+"irc", "postgres", "lmtp", "nntp", "sieve" and "ldap".
cfec1a
+
cfec1a
+=item B<-xmpphost hostname>
cfec1a
+
cfec1a
+This option, when used with "-starttls xmpp" or "-starttls xmpp-server",
cfec1a
+specifies the host for the "to" attribute of the stream element.
cfec1a
+If this option is not specified, then the host specified with "-connect"
cfec1a
+will be used.
cfec1a
+
cfec1a
+=item B<-name hostname>
cfec1a
+
cfec1a
+the host name to use with "-starttls smtp".
cfec1a
+If this option is not specified, the default "openssl.client.net" will be used.
cfec1a
 
cfec1a
 =item B<-tlsextdebug>
cfec1a