--- a/apps/openssl/s_client.c +++ b/apps/openssl/s_client.c @@ -56,7 +56,7 @@ * [including the GNU Public Licence.] */ /* ==================================================================== - * Copyright (c) 1998-2006 The OpenSSL Project. All rights reserved. + * Copyright (c) 1998-2017 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -184,6 +184,7 @@ static void sc_usage(void); static void print_stuff(BIO * berr, SSL * con, int full); static int ocsp_resp_cb(SSL * s, void *arg); +static int ldap_ExtendedResponse_parse(const char *buf, long rem); static BIO *bio_c_out = NULL; static int c_quiet = 0; static int c_ign_eof = 0; @@ -234,7 +235,7 @@ BIO_printf(bio_err, " -starttls prot - use the STARTTLS command before starting TLS\n"); BIO_printf(bio_err, " for those protocols that support it, where\n"); BIO_printf(bio_err, " 'prot' defines which one to assume. Currently,\n"); - BIO_printf(bio_err, " only \"smtp\", \"lmtp\", \"pop3\", \"imap\", \"ftp\" and \"xmpp\"\n"); + BIO_printf(bio_err, " only \"smtp\", \"lmtp\", \"pop3\", \"imap\", \"ftp\", \"xmpp\" and \"ldap\"\n"); BIO_printf(bio_err, " are supported.\n"); BIO_printf(bio_err, " -xmpphost host - connect to this virtual host on the xmpp server\n"); BIO_printf(bio_err, " -sess_out arg - file to write SSL session to\n"); @@ -284,7 +285,8 @@ PROTO_POP3, PROTO_IMAP, PROTO_FTP, - PROTO_XMPP + PROTO_XMPP, + PROTO_LDAP }; int @@ -543,6 +545,8 @@ starttls_proto = PROTO_FTP; else if (strcmp(*argv, "xmpp") == 0) starttls_proto = PROTO_XMPP; + else if (strcmp(*argv, "ldap") == 0) + starttls_proto = PROTO_LDAP; else goto bad; } @@ -934,6 +938,72 @@ if (!strstr(sbuf, "value.sequence->data, + atyp->value.sequence->length); + (void)BIO_flush(sbio); + ASN1_TYPE_free(atyp); + + mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ); + if (mbuf_len < 0) { + BIO_printf(bio_err, "BIO_read failed\n"); + goto end; + } + result = ldap_ExtendedResponse_parse(mbuf, mbuf_len); + if (result < 0) { + BIO_printf(bio_err, "ldap_ExtendedResponse_parse failed\n"); + goto shut; + } else if (result > 0) { + BIO_printf(bio_err, "STARTTLS failed, LDAP Result Code: %i\n", + result); + goto shut; + } + mbuf_len = 0; } else if (proxy != NULL) { BIO_printf(sbio, "CONNECT %s HTTP/1.0\r\n\r\n", connect); mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ); @@ -1437,3 +1507,86 @@ return 1; } +static int ldap_ExtendedResponse_parse(const char *buf, long rem) +{ + const unsigned char *cur, *end; + long len; + int tag, xclass, inf, ret = -1; + + cur = (const unsigned char *)buf; + end = cur + rem; + + /* + * From RFC 4511: + * + * LDAPMessage ::= SEQUENCE { + * messageID MessageID, + * protocolOp CHOICE { + * ... + * extendedResp ExtendedResponse, + * ... }, + * controls [0] Controls OPTIONAL } + * + * ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + * COMPONENTS OF LDAPResult, + * responseName [10] LDAPOID OPTIONAL, + * responseValue [11] OCTET STRING OPTIONAL } + * + * LDAPResult ::= SEQUENCE { + * resultCode ENUMERATED { + * success (0), + * ... + * other (80), + * ... }, + * matchedDN LDAPDN, + * diagnosticMessage LDAPString, + * referral [3] Referral OPTIONAL } + */ + + /* pull SEQUENCE */ + inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); + if (inf != V_ASN1_CONSTRUCTED || tag != V_ASN1_SEQUENCE || + (rem = end - cur, len > rem)) { + BIO_printf(bio_err, "Unexpected LDAP response\n"); + goto end; + } + + rem = len; /* ensure that we don't overstep the SEQUENCE */ + + /* pull MessageID */ + inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); + if (inf != V_ASN1_UNIVERSAL || tag != V_ASN1_INTEGER || + (rem = end - cur, len > rem)) { + BIO_printf(bio_err, "No MessageID\n"); + goto end; + } + + cur += len; /* shall we check for MessageId match or just skip? */ + + /* pull [APPLICATION 24] */ + rem = end - cur; + inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); + if (inf != V_ASN1_CONSTRUCTED || xclass != V_ASN1_APPLICATION || + tag != 24) { + BIO_printf(bio_err, "Not ExtendedResponse\n"); + goto end; + } + + /* pull resultCode */ + rem = end - cur; + inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); + if (inf != V_ASN1_UNIVERSAL || tag != V_ASN1_ENUMERATED || len == 0 || + (rem = end - cur, len > rem)) { + BIO_printf(bio_err, "Not LDAPResult\n"); + goto end; + } + + /* len should always be one, but just in case... */ + for (ret = 0, inf = 0; inf < len; inf++) { + ret <<= 8; + ret |= cur[inf]; + } + /* There is more data, but we don't care... */ + end: + return ret; +}