Skip to content

Commit

Permalink
Add Next Protocol Negotiation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben Laurie committed Jul 28, 2010
1 parent b122e48 commit ee2ffc2
Show file tree
Hide file tree
Showing 18 changed files with 722 additions and 3 deletions.
6 changes: 6 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

Changes between 1.0.0 and 1.1.0 [xx XXX xxxx]

*) Add Next Protocol Negotiation,
http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-00. Can be
disabled with a no-npn flag to config or Configure. Code donated
by Google.
[Adam Langley <agl@google.com> and Ben Laurie]

*) Use type ossl_ssize_t instad of ssize_t which isn't available on
all platforms. Move ssize_t definition from e_os.h to the public
header file e_os2.h as it now appears in public header file cms.h
Expand Down
2 changes: 1 addition & 1 deletion Configure
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ my %table=(
"debug-ben", "gcc:-DBN_DEBUG -DREF_CHECK -DCONF_DEBUG -DBN_CTX_DEBUG -DCRYPTO_MDEBUG -DPEDANTIC -DDEBUG_SAFESTACK -O2 -pedantic -Wall -Wshadow -Werror -pipe::(unknown):::::bn86-elf.o co86-elf.o",
"debug-ben-openbsd","gcc:-DBN_DEBUG -DREF_CHECK -DCONF_DEBUG -DBN_CTX_DEBUG -DCRYPTO_MDEBUG -DPEDANTIC -DDEBUG_SAFESTACK -DOPENSSL_OPENBSD_DEV_CRYPTO -DOPENSSL_NO_ASM -O2 -pedantic -Wall -Wshadow -Werror -pipe::(unknown)::::",
"debug-ben-openbsd-debug","gcc:-DBN_DEBUG -DREF_CHECK -DCONF_DEBUG -DBN_CTX_DEBUG -DCRYPTO_MDEBUG -DPEDANTIC -DDEBUG_SAFESTACK -DOPENSSL_OPENBSD_DEV_CRYPTO -DOPENSSL_NO_ASM -g3 -O2 -pedantic -Wall -Wshadow -Werror -pipe::(unknown)::::",
"debug-ben-debug", "gcc:$gcc_devteam_warn -DBN_DEBUG -DCONF_DEBUG -DBN_CTX_DEBUG -DDEBUG_SAFESTACK -g3 -O2 -pipe::(unknown)::::::",
"debug-ben-debug", "gcc:$gcc_devteam_warn -DBN_DEBUG -DCONF_DEBUG -DDEBUG_SAFESTACK -g3 -O2 -pipe::(unknown)::::::",
"debug-ben-no-opt", "gcc: -Wall -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations -DDEBUG_SAFESTACK -DCRYPTO_MDEBUG -Werror -DL_ENDIAN -DTERMIOS -Wall -g3::(unknown)::::::",
"debug-ben-strict", "gcc:-DBN_DEBUG -DREF_CHECK -DCONF_DEBUG -DBN_CTX_DEBUG -DCRYPTO_MDEBUG -DCONST_STRICT -O2 -Wall -Wshadow -Werror -Wpointer-arith -Wcast-qual -Wwrite-strings -pipe::(unknown)::::::",
"debug-rse","cc:-DTERMIOS -DL_ENDIAN -pipe -O -g -ggdb3 -Wall::(unknown):::BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:${x86_elf_asm}",
Expand Down
43 changes: 43 additions & 0 deletions apps/apps.c
Original file line number Diff line number Diff line change
Expand Up @@ -3031,3 +3031,46 @@ int raw_write_stdout(const void *buf,int siz)
int raw_write_stdout(const void *buf,int siz)
{ return write(fileno(stdout),buf,siz); }
#endif

#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NPN)
/* next_protos_parse parses a comma separated list of strings into a string
* in a format suitable for passing to SSL_CTX_set_next_protos_advertised.
* outlen: (output) set to the length of the resulting buffer on success.
* in: a NUL termianted string like "abc,def,ghi"
*
* returns: a malloced buffer or NULL on failure.
*/
unsigned char *next_protos_parse(unsigned short *outlen, const char *in)
{
size_t len;
unsigned char *out;
size_t i, start = 0;

len = strlen(in);
if (len > 65535)
return NULL;

out = OPENSSL_malloc(strlen(in) + 1);
if (!out)
return NULL;

for (i = 0; i <= len; ++i)
{
if (i == len || in[i] == ',')
{
if (i - start > 255)
{
OPENSSL_free(out);
return NULL;
}
out[start] = i - start;
start = i + 1;
}
else
out[i+1] = in[i];
}

*outlen = len + 1;
return out;
}
#endif /* !OPENSSL_NO_TLSEXT && !OPENSSL_NO_NPN */
4 changes: 4 additions & 0 deletions apps/apps.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,3 +364,7 @@ int raw_write_stdout(const void *,int);
#define TM_STOP 1
double app_tminterval (int stop,int usertime);
#endif

#ifndef OPENSSL_NO_NPN
unsigned char *next_protos_parse(unsigned short *outlen, const char *in);
#endif
79 changes: 79 additions & 0 deletions apps/s_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@ static void sc_usage(void)
BIO_printf(bio_err," -tlsextdebug - hex dump of all TLS extensions received\n");
BIO_printf(bio_err," -status - request certificate status from server\n");
BIO_printf(bio_err," -no_ticket - disable use of RFC4507bis session tickets\n");
# ifndef OPENSSL_NO_NPN
BIO_printf(bio_err," -nextprotoneg arg - enable NPN extension, considering named protocols supported (comma-separated list)\n");
# endif
#endif
BIO_printf(bio_err," -legacy_renegotiation - enable use of legacy renegotiation (dangerous)\n");
}
Expand All @@ -367,6 +370,40 @@ static int MS_CALLBACK ssl_servername_cb(SSL *s, int *ad, void *arg)

return SSL_TLSEXT_ERR_OK;
}

# ifndef OPENSSL_NO_NPN
/* This the context that we pass to next_proto_cb */
typedef struct tlsextnextprotoctx_st {
unsigned char *data;
unsigned short len;
int status;
} tlsextnextprotoctx;

static tlsextnextprotoctx next_proto;

static int next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
{
tlsextnextprotoctx *ctx = arg;

if (!c_quiet)
{
/* We can assume that |in| is syntactically valid. */
unsigned i;
BIO_printf(bio_c_out, "Protocols advertised by server: ");
for (i = 0; i < inlen; )
{
if (i)
BIO_write(bio_c_out, ", ", 2);
BIO_write(bio_c_out, &in[i + 1], in[i]);
i += in[i] + 1;
}
BIO_write(bio_c_out, "\n", 1);
}

ctx->status = SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len);
return SSL_TLSEXT_ERR_OK;
}
# endif /* ndef OPENSSL_NO_NPN */
#endif

enum
Expand Down Expand Up @@ -430,6 +467,9 @@ int MAIN(int argc, char **argv)
char *servername = NULL;
tlsextctx tlsextcbp =
{NULL,0};
# ifndef OPENSSL_NO_NPN
const char *next_proto_neg_in = NULL;
# endif
#endif
char *sess_in = NULL;
char *sess_out = NULL;
Expand Down Expand Up @@ -661,6 +701,13 @@ int MAIN(int argc, char **argv)
#ifndef OPENSSL_NO_TLSEXT
else if (strcmp(*argv,"-no_ticket") == 0)
{ off|=SSL_OP_NO_TICKET; }
# ifndef OPENSSL_NO_NPN
else if (strcmp(*argv,"-nextprotoneg") == 0)
{
if (--argc < 1) goto bad;
next_proto_neg_in = *(++argv);
}
# endif
#endif
else if (strcmp(*argv,"-serverpref") == 0)
off|=SSL_OP_CIPHER_SERVER_PREFERENCE;
Expand Down Expand Up @@ -767,6 +814,21 @@ int MAIN(int argc, char **argv)
OpenSSL_add_ssl_algorithms();
SSL_load_error_strings();

#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NPN)
next_proto.status = -1;
if (next_proto_neg_in)
{
next_proto.data = next_protos_parse(&next_proto.len, next_proto_neg_in);
if (next_proto.data == NULL)
{
BIO_printf(bio_err, "Error parsing -nextprotoneg argument\n");
goto end;
}
}
else
next_proto.data = NULL;
#endif

#ifndef OPENSSL_NO_ENGINE
e = setup_engine(bio_err, engine_id, 1);
if (ssl_client_engine_id)
Expand Down Expand Up @@ -888,6 +950,11 @@ int MAIN(int argc, char **argv)
*/
if (socket_type == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1);

#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NPN)
if (next_proto.data)
SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto);
#endif

if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback);
if (cipher != NULL)
if(!SSL_CTX_set_cipher_list(ctx,cipher)) {
Expand Down Expand Up @@ -1747,6 +1814,18 @@ static void print_stuff(BIO *bio, SSL *s, int full)
BIO_printf(bio,"Expansion: %s\n",
expansion ? SSL_COMP_get_name(expansion) : "NONE");
#endif

#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NPN)
if (next_proto.status != -1) {
const unsigned char *proto;
unsigned int proto_len;
SSL_get0_next_proto_negotiated(s, &proto, &proto_len);
BIO_printf(bio, "Next protocol: (%d) ", next_proto.status);
BIO_write(bio, proto, proto_len);
BIO_write(bio, "\n", 1);
}
#endif

SSL_SESSION_print(bio,SSL_get_session(s));
BIO_printf(bio,"---\n");
if (peer != NULL)
Expand Down
66 changes: 65 additions & 1 deletion apps/s_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,9 +493,12 @@ static void sv_usage(void)
BIO_printf(bio_err," (default is %s)\n",TEST_CERT2);
BIO_printf(bio_err," -key2 arg - Private Key file to use for servername, in cert file if\n");
BIO_printf(bio_err," not specified (default is %s)\n",TEST_CERT2);
# ifndef OPENSSL_NO_NPN
BIO_printf(bio_err," -tlsextdebug - hex dump of all TLS extensions received\n");
# endif
BIO_printf(bio_err," -no_ticket - disable use of RFC4507bis session tickets\n");
BIO_printf(bio_err," -legacy_renegotiation - enable use of legacy renegotiation (dangerous)\n");
BIO_printf(bio_err," -nextprotoneg arg - set the advertised protocols for the NPN extension (comma-separated list)\n");
#endif
}

Expand Down Expand Up @@ -830,6 +833,24 @@ BIO_printf(err, "cert_status: received %d ids\n", sk_OCSP_RESPID_num(ids));
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
goto done;
}

# ifndef OPENSSL_NO_NPN
/* This is the context that we pass to next_proto_cb */
typedef struct tlsextnextprotoctx_st {
unsigned char *data;
unsigned int len;
} tlsextnextprotoctx;

static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, void *arg)
{
tlsextnextprotoctx *next_proto = arg;

*data = next_proto->data;
*len = next_proto->len;

return SSL_TLSEXT_ERR_OK;
}
# endif /* ndef OPENSSL_NO_NPN */
#endif

int MAIN(int, char **);
Expand Down Expand Up @@ -871,6 +892,10 @@ int MAIN(int argc, char *argv[])
#endif
#ifndef OPENSSL_NO_TLSEXT
tlsextctx tlsextcbp = {NULL, NULL, SSL_TLSEXT_ERR_ALERT_WARNING};
# ifndef OPENSSL_NO_NPN
const char *next_proto_neg_in = NULL;
tlsextnextprotoctx next_proto;
# endif
#endif
#ifndef OPENSSL_NO_PSK
/* by default do not send a PSK identity hint */
Expand Down Expand Up @@ -1201,7 +1226,13 @@ int MAIN(int argc, char *argv[])
if (--argc < 1) goto bad;
s_key_file2= *(++argv);
}

# ifndef OPENSSL_NO_NPN
else if (strcmp(*argv,"-nextprotoneg") == 0)
{
if (--argc < 1) goto bad;
next_proto_neg_in = *(++argv);
}
# endif
#endif
#if !defined(OPENSSL_NO_JPAKE) && !defined(OPENSSL_NO_PSK)
else if (strcmp(*argv,"-jpake") == 0)
Expand Down Expand Up @@ -1306,6 +1337,21 @@ int MAIN(int argc, char *argv[])
goto end;
}
}
# ifndef OPENSSL_NO_NPN
if (next_proto_neg_in)
{
unsigned short len;
next_proto.data = next_protos_parse(&len,
next_proto_neg_in);
if (next_proto.data == NULL)
goto end;
next_proto.len = len;
}
else
{
next_proto.data = NULL;
}
# endif
#endif
}

Expand Down Expand Up @@ -1490,6 +1536,11 @@ int MAIN(int argc, char *argv[])
if (vpm)
SSL_CTX_set1_param(ctx2, vpm);
}

# ifndef OPENSSL_NO_NPN
if (next_proto.data)
SSL_CTX_set_next_protos_advertised_cb(ctx, next_proto_cb, &next_proto);
# endif
#endif

#ifndef OPENSSL_NO_DH
Expand Down Expand Up @@ -2174,6 +2225,10 @@ static int init_ssl_connection(SSL *con)
X509 *peer;
long verify_error;
MS_STATIC char buf[BUFSIZ];
#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NPN)
const unsigned char *next_proto_neg;
unsigned next_proto_neg_len;
#endif

if ((i=SSL_accept(con)) <= 0)
{
Expand Down Expand Up @@ -2213,6 +2268,15 @@ static int init_ssl_connection(SSL *con)
BIO_printf(bio_s_out,"Shared ciphers:%s\n",buf);
str=SSL_CIPHER_get_name(SSL_get_current_cipher(con));
BIO_printf(bio_s_out,"CIPHER is %s\n",(str != NULL)?str:"(NONE)");
#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NPN)
SSL_get0_next_proto_negotiated(con, &next_proto_neg, &next_proto_neg_len);
if (next_proto_neg)
{
BIO_printf(bio_s_out,"NEXTPROTO is ");
BIO_write(bio_s_out, next_proto_neg, next_proto_neg_len);
BIO_printf(bio_s_out, "\n");
}
#endif
if (con->hit) BIO_printf(bio_s_out,"Reused session-id\n");
if (SSL_ctrl(con,SSL_CTRL_GET_FLAGS,0,NULL) &
TLS1_FLAGS_TLS_PADDING_BUG)
Expand Down
34 changes: 34 additions & 0 deletions ssl/s3_both.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,40 @@ int ssl3_send_finished(SSL *s, int a, int b, const char *sender, int slen)
return(ssl3_do_write(s,SSL3_RT_HANDSHAKE));
}

#ifndef OPENSSL_NO_NPN
/* ssl3_take_mac calculates the Finished MAC for the handshakes messages seen to far. */
static void ssl3_take_mac(SSL *s)
{
const char *sender;
int slen;

if (s->state & SSL_ST_CONNECT)
{
sender=s->method->ssl3_enc->server_finished_label;
slen=s->method->ssl3_enc->server_finished_label_len;
}
else
{
sender=s->method->ssl3_enc->client_finished_label;
slen=s->method->ssl3_enc->client_finished_label_len;
}

s->s3->tmp.peer_finish_md_len = s->method->ssl3_enc->final_finish_mac(s,
sender,slen,s->s3->tmp.peer_finish_md);
}
#endif

int ssl3_get_finished(SSL *s, int a, int b)
{
int al,i,ok;
long n;
unsigned char *p;

#ifdef OPENSSL_NO_NPN
/* the mac has already been generated when we received the
* change cipher spec message and is in s->s3->tmp.peer_finish_md
*/
#endif

n=s->method->ssl_get_message(s,
a,
Expand Down Expand Up @@ -514,6 +539,15 @@ long ssl3_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
s->init_num += i;
n -= i;
}

#ifndef OPENSSL_NO_NPN
/* If receiving Finished, record MAC of prior handshake messages for
* Finished verification. */
if (*s->init_buf->data == SSL3_MT_FINISHED)
ssl3_take_mac(s);
#endif

/* Feed this message into MAC computation. */
ssl3_finish_mac(s, (unsigned char *)s->init_buf->data, s->init_num + 4);
if (s->msg_callback)
s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data, (size_t)s->init_num + 4, s, s->msg_callback_arg);
Expand Down

0 comments on commit ee2ffc2

Please sign in to comment.