X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=apps%2Fs_client.c;h=b48c3066a7149dcae68124f972808df803c2a3ae;hp=6e790cf8fb8b2192041aca79e3160866dd0a3d58;hb=0665b4edae3fec740ebe12fe2946d4cc9585ca86;hpb=9fa36f5f4b32557c20b2c2665d50b239dcc49b91 diff --git a/apps/s_client.c b/apps/s_client.c index 6e790cf8fb..b48c3066a7 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -96,6 +96,7 @@ static void print_stuff(BIO *berr, SSL *con, int full); #ifndef OPENSSL_NO_OCSP static int ocsp_resp_cb(SSL *s, void *arg); #endif +static int ldap_ExtendedResponse_parse(const char *buf, long rem); static int saved_errno; @@ -546,7 +547,7 @@ typedef enum OPTION_choice { OPT_SERVERINFO, OPT_STARTTLS, OPT_SERVERNAME, OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_SMTPHOST, OPT_ASYNC, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES, OPT_READ_BUF, - OPT_KEYLOG_FILE, + OPT_KEYLOG_FILE, OPT_EARLY_DATA, OPT_V_ENUM, OPT_X_ENUM, OPT_S_ENUM, @@ -731,6 +732,7 @@ const OPTIONS s_client_options[] = { {"ctlogfile", OPT_CTLOG_FILE, '<', "CT log list CONF file"}, #endif {"keylogfile", OPT_KEYLOG_FILE, '>', "Write TLS secrets to file"}, + {"early_data", OPT_EARLY_DATA, '<', "File to send as early data"}, {NULL, OPT_EOF, 0x00, NULL} }; @@ -748,7 +750,8 @@ typedef enum PROTOCOL_choice { PROTO_POSTGRES, PROTO_LMTP, PROTO_NNTP, - PROTO_SIEVE + PROTO_SIEVE, + PROTO_LDAP } PROTOCOL_CHOICE; static const OPT_PAIR services[] = { @@ -764,6 +767,7 @@ static const OPT_PAIR services[] = { {"lmtp", PROTO_LMTP}, {"nntp", PROTO_NNTP}, {"sieve", PROTO_SIEVE}, + {"ldap", PROTO_LDAP}, {NULL, 0} }; @@ -892,7 +896,7 @@ int s_client_main(int argc, char **argv) int c_status_req = 0; #endif BIO *bio_c_msg = NULL; - const char *keylog_file = NULL; + const char *keylog_file = NULL, *early_data_file = NULL; FD_ZERO(&readfds); FD_ZERO(&writefds); @@ -1368,6 +1372,9 @@ int s_client_main(int argc, char **argv) case OPT_KEYLOG_FILE: keylog_file = opt_arg(); break; + case OPT_EARLY_DATA: + early_data_file = opt_arg(); + break; } } if (count4or6 >= 2) { @@ -2281,6 +2288,109 @@ int s_client_main(int argc, char **argv) } } break; + case PROTO_LDAP: + { + /* StartTLS Operation according to RFC 4511 */ + static char ldap_tls_genconf[] = "asn1=SEQUENCE:LDAPMessage\n" + "[LDAPMessage]\n" + "messageID=INTEGER:1\n" + "extendedReq=EXPLICIT:23A,IMPLICIT:0C," + "FORMAT:ASCII,OCT:1.3.6.1.4.1.1466.20037\n"; + long errline = -1; + char *genstr = NULL; + int result = -1; + ASN1_TYPE *atyp = NULL; + BIO *ldapbio = BIO_new(BIO_s_mem()); + CONF *cnf = NCONF_new(NULL); + + if (cnf == NULL) { + BIO_free(ldapbio); + goto end; + } + BIO_puts(ldapbio, ldap_tls_genconf); + if (NCONF_load_bio(cnf, ldapbio, &errline) <= 0) { + BIO_free(ldapbio); + NCONF_free(cnf); + if (errline <= 0) { + BIO_printf(bio_err, "NCONF_load_bio failed\n"); + goto end; + } else { + BIO_printf(bio_err, "Error on line %ld\n", errline); + goto end; + } + } + BIO_free(ldapbio); + genstr = NCONF_get_string(cnf, "default", "asn1"); + if (genstr == NULL) { + NCONF_free(cnf); + BIO_printf(bio_err, "NCONF_get_string failed\n"); + goto end; + } + atyp = ASN1_generate_nconf(genstr, cnf); + if (atyp == NULL) { + NCONF_free(cnf); + BIO_printf(bio_err, "ASN1_generate_nconf failed\n"); + goto end; + } + NCONF_free(cnf); + + /* Send SSLRequest packet */ + BIO_write(sbio, atyp->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; + } + break; + } + + if (early_data_file != NULL + && SSL_get0_session(con) != NULL + && SSL_SESSION_get_max_early_data(SSL_get0_session(con)) > 0) { + BIO *edfile = BIO_new_file(early_data_file, "r"); + size_t readbytes, writtenbytes; + int finish = 0; + + if (edfile == NULL) { + BIO_printf(bio_err, "Cannot open early data file\n"); + goto shut; + } + + while (!finish) { + if (!BIO_read_ex(edfile, cbuf, BUFSIZZ, &readbytes)) + finish = 1; + + while (!SSL_write_early_data(con, cbuf, readbytes, &writtenbytes)) { + switch (SSL_get_error(con, 0)) { + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_ASYNC: + case SSL_ERROR_WANT_READ: + /* Just keep trying - busy waiting */ + continue; + default: + BIO_printf(bio_err, "Error writing early data\n"); + BIO_free(edfile); + goto shut; + } + } + } + + BIO_free(edfile); } for (;;) { @@ -2868,6 +2978,23 @@ static void print_stuff(BIO *bio, SSL *s, int full) } #endif + if (SSL_version(s) == TLS1_3_VERSION) { + switch (SSL_get_early_data_status(s)) { + case SSL_EARLY_DATA_NOT_SENT: + BIO_printf(bio, "Early data was not sent\n"); + break; + + case SSL_EARLY_DATA_REJECTED: + BIO_printf(bio, "Early data was rejected\n"); + break; + + case SSL_EARLY_DATA_ACCEPTED: + BIO_printf(bio, "Early data was accepted\n"); + break; + + } + } + SSL_SESSION_print(bio, SSL_get_session(s)); if (SSL_get_session(s) != NULL && keymatexportlabel != NULL) { BIO_printf(bio, "Keying material exporter:\n"); @@ -2920,4 +3047,86 @@ static int ocsp_resp_cb(SSL *s, void *arg) } # endif +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; + } + + /* 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; +} + #endif /* OPENSSL_NO_SOCK */