Add Next Protocol Negotiation.
[openssl.git] / apps / s_server.c
index 8a08a306959ac478b1c4520272b43e8f6ef947d3..d33996820060524286c6fb70ccb0e46e2e9cc7d4 100644 (file)
@@ -209,6 +209,8 @@ static int init_ssl_connection(SSL *s);
 static void print_stats(BIO *bp,SSL_CTX *ctx);
 static int generate_session_id(const SSL *ssl, unsigned char *id,
                                unsigned int *id_len);
+static void init_session_cache_ctx(SSL_CTX *sctx);
+static void free_sessions(void);
 #ifndef OPENSSL_NO_DH
 static DH *load_dh_param(const char *dhfile);
 static DH *get_dh512(void);
@@ -458,6 +460,7 @@ static void sv_usage(void)
 #endif
        BIO_printf(bio_err," -ssl2         - Just talk SSLv2\n");
        BIO_printf(bio_err," -ssl3         - Just talk SSLv3\n");
+       BIO_printf(bio_err," -tls1_1       - Just talk TLSv1_1\n");
        BIO_printf(bio_err," -tls1         - Just talk TLSv1\n");
        BIO_printf(bio_err," -dtls1        - Just talk DTLSv1\n");
        BIO_printf(bio_err," -timeout      - Enable timeouts\n");
@@ -466,6 +469,7 @@ static void sv_usage(void)
        BIO_printf(bio_err," -no_ssl2      - Just disable SSLv2\n");
        BIO_printf(bio_err," -no_ssl3      - Just disable SSLv3\n");
        BIO_printf(bio_err," -no_tls1      - Just disable TLSv1\n");
+       BIO_printf(bio_err," -no_tls1_1    - Just disable TLSv1.1\n");
 #ifndef OPENSSL_NO_DH
        BIO_printf(bio_err," -no_dhe       - Disable ephemeral DH\n");
 #endif
@@ -489,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
        }
 
@@ -826,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 **);
@@ -860,13 +885,17 @@ int MAIN(int argc, char *argv[])
        int s_dcert_format = FORMAT_PEM, s_dkey_format = FORMAT_PEM;
        X509 *s_cert = NULL, *s_dcert = NULL;
        EVP_PKEY *s_key = NULL, *s_dkey = NULL;
-       int no_cache = 0;
+       int no_cache = 0, ext_cache = 0;
 #ifndef OPENSSL_NO_TLSEXT
        EVP_PKEY *s_key2 = NULL;
        X509 *s_cert2 = NULL;
 #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 */
@@ -1005,6 +1034,8 @@ int MAIN(int argc, char *argv[])
                        }
                else if (strcmp(*argv,"-no_cache") == 0)
                        no_cache = 1;
+               else if (strcmp(*argv,"-ext_cache") == 0)
+                       ext_cache = 1;
                else if (args_verify(&argv, &argc, &badarg, bio_err, &vpm))
                        {
                        if (badarg)
@@ -1120,6 +1151,8 @@ int MAIN(int argc, char *argv[])
                        { off|=SSL_OP_NO_SSLv2; }
                else if (strcmp(*argv,"-no_ssl3") == 0)
                        { off|=SSL_OP_NO_SSLv3; }
+               else if (strcmp(*argv,"-no_tls1_1") == 0)
+                       { off|=SSL_OP_NO_TLSv1_1; }
                else if (strcmp(*argv,"-no_tls1") == 0)
                        { off|=SSL_OP_NO_TLSv1; }
                else if (strcmp(*argv,"-no_comp") == 0)
@@ -1137,6 +1170,8 @@ int MAIN(int argc, char *argv[])
                        { meth=SSLv3_server_method(); }
 #endif
 #ifndef OPENSSL_NO_TLS1
+               else if (strcmp(*argv,"-tls1_1") == 0)
+                       { meth=TLSv1_1_server_method(); }
                else if (strcmp(*argv,"-tls1") == 0)
                        { meth=TLSv1_server_method(); }
 #endif
@@ -1191,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)
@@ -1296,6 +1337,21 @@ bad:
                                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
                }
 
@@ -1396,6 +1452,8 @@ bad:
        if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback);
        if (no_cache)
                SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+       else if (ext_cache)
+               init_session_cache_ctx(ctx);
        else
                SSL_CTX_sess_set_cache_size(ctx,128);
 
@@ -1465,6 +1523,8 @@ bad:
 
                if (no_cache)
                        SSL_CTX_set_session_cache_mode(ctx2,SSL_SESS_CACHE_OFF);
+               else if (ext_cache)
+                       init_session_cache_ctx(ctx2);
                else
                        SSL_CTX_sess_set_cache_size(ctx2,128);
 
@@ -1476,6 +1536,11 @@ bad:
                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
@@ -1715,6 +1780,7 @@ end:
                OPENSSL_free(pass);
        if (dpass)
                OPENSSL_free(dpass);
+       free_sessions();
 #ifndef OPENSSL_NO_TLSEXT
        if (ctx2 != NULL) SSL_CTX_free(ctx2);
        if (s_cert2)
@@ -2159,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)
                {
@@ -2198,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)
@@ -2209,6 +2288,8 @@ static int init_ssl_connection(SSL *con)
                        con->kssl_ctx->client_princ);
                }
 #endif /* OPENSSL_NO_KRB5 */
+       BIO_printf(bio_s_out, "Secure Renegotiation IS%s supported\n",
+                     SSL_get_secure_renegotiation_support(con) ? "" : " NOT");
        return(1);
        }
 
@@ -2252,11 +2333,10 @@ static int www_body(char *hostname, int s, unsigned char *context)
        {
        char *buf=NULL;
        int ret=1;
-       int i,j,k,blank,dot;
+       int i,j,k,dot;
        SSL *con;
        const SSL_CIPHER *c;
        BIO *io,*ssl_bio,*sbio;
-       long total_bytes;
 
        buf=OPENSSL_malloc(bufsize);
        if (buf == NULL) return(0);
@@ -2307,7 +2387,6 @@ static int www_body(char *hostname, int s, unsigned char *context)
                }
        SSL_set_bio(con,sbio,sbio);
        SSL_set_accept_state(con);
-
        /* SSL_set_fd(con,s); */
        BIO_set_ssl(ssl_bio,con,BIO_CLOSE);
        BIO_push(io,ssl_bio);
@@ -2327,7 +2406,6 @@ static int www_body(char *hostname, int s, unsigned char *context)
                SSL_set_msg_callback_arg(con, bio_s_out);
                }
 
-       blank=0;
        for (;;)
                {
                if (hack)
@@ -2389,6 +2467,32 @@ static int www_body(char *hostname, int s, unsigned char *context)
                        STACK_OF(SSL_CIPHER) *sk;
                        static const char *space="                          ";
 
+               if (www == 1 && strncmp("GET /reneg", buf, 10) == 0)
+                       {
+                       if (strncmp("GET /renegcert", buf, 14) == 0)
+                               SSL_set_verify(con,
+                               SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,NULL);
+                       i=SSL_renegotiate(con);
+                       BIO_printf(bio_s_out, "SSL_renegotiate -> %d\n",i);
+                       i=SSL_do_handshake(con);
+                       if (i <= 0)
+                               {
+                               BIO_printf(bio_s_out, "SSL_do_handshake() Retval %d\n", SSL_get_error(con, i));
+                               ERR_print_errors(bio_err);
+                               goto err;
+                               }
+                       /* EVIL HACK! */
+                       con->state = SSL_ST_ACCEPT;
+                       i=SSL_do_handshake(con);
+                       BIO_printf(bio_s_out, "SSL_do_handshake -> %d\n",i);
+                       if (i <= 0)
+                               {
+                               BIO_printf(bio_s_out, "SSL_do_handshake() Retval %d\n", SSL_get_error(con, i));
+                               ERR_print_errors(bio_err);
+                               goto err;
+                               }
+                       }
+
                        BIO_puts(io,"HTTP/1.0 200 ok\r\nContent-type: text/html\r\n\r\n");
                        BIO_puts(io,"<HTML><BODY BGCOLOR=\"#ffffff\">\n");
                        BIO_puts(io,"<pre>\n");
@@ -2401,6 +2505,11 @@ static int www_body(char *hostname, int s, unsigned char *context)
                                }
                        BIO_puts(io,"\n");
 
+                       BIO_printf(io,
+                               "Secure Renegotiation IS%s supported\n",
+                               SSL_get_secure_renegotiation_support(con) ?
+                                                       "" : " NOT");
+
                        /* The following is evil and should not really
                         * be done */
                        BIO_printf(io,"Ciphers supported in s_server binary\n");
@@ -2557,7 +2666,6 @@ static int www_body(char *hostname, int s, unsigned char *context)
                                         BIO_puts(io,"HTTP/1.0 200 ok\r\nContent-type: text/plain\r\n\r\n");
                                 }
                        /* send the file */
-                       total_bytes=0;
                        for (;;)
                                {
                                i=BIO_read(file,buf,bufsize);
@@ -2687,3 +2795,115 @@ static int generate_session_id(const SSL *ssl, unsigned char *id,
                return 0;
        return 1;
        }
+
+/* By default s_server uses an in-memory cache which caches SSL_SESSION
+ * structures without any serialisation. This hides some bugs which only
+ * become apparent in deployed servers. By implementing a basic external
+ * session cache some issues can be debugged using s_server.
+ */
+
+typedef struct simple_ssl_session_st
+       {
+       unsigned char *id;
+       int idlen;
+       unsigned char *der;
+       int derlen;
+       struct simple_ssl_session_st *next;
+       } simple_ssl_session;
+
+static simple_ssl_session *first = NULL;
+
+static int add_session(SSL *ssl, SSL_SESSION *session)
+       {
+       simple_ssl_session *sess;
+       unsigned char *p;
+
+       sess = OPENSSL_malloc(sizeof(simple_ssl_session));
+
+       sess->idlen = session->session_id_length;
+       sess->derlen = i2d_SSL_SESSION(session, NULL);
+
+       sess->id = BUF_memdup(session->session_id, sess->idlen);
+
+       sess->der = OPENSSL_malloc(sess->derlen);
+       p = sess->der;
+       i2d_SSL_SESSION(session, &p);
+
+       sess->next = first;
+       first = sess;
+       BIO_printf(bio_err, "New session added to external cache\n");
+       return 0;
+       }
+
+static SSL_SESSION *get_session(SSL *ssl, unsigned char *id, int idlen,
+                                       int *do_copy)
+       {
+       simple_ssl_session *sess;
+       *do_copy = 0;
+       for (sess = first; sess; sess = sess->next)
+               {
+               if (idlen == sess->idlen && !memcmp(sess->id, id, idlen))
+                       {
+                       const unsigned char *p = sess->der;
+                       BIO_printf(bio_err, "Lookup session: cache hit\n");
+                       return d2i_SSL_SESSION(NULL, &p, sess->derlen);
+                       }
+               }
+       BIO_printf(bio_err, "Lookup session: cache miss\n");
+       return NULL;
+       }
+
+static void del_session(SSL_CTX *sctx, SSL_SESSION *session)
+       {
+       simple_ssl_session *sess, *prev = NULL;
+       unsigned char *id = session->session_id;
+       int idlen = session->session_id_length;
+       for (sess = first; sess; sess = sess->next)
+               {
+               if (idlen == sess->idlen && !memcmp(sess->id, id, idlen))
+                       {
+                       if(prev)
+                               prev->next = sess->next;
+                       else
+                               first = sess->next;
+                       OPENSSL_free(sess->id);
+                       OPENSSL_free(sess->der);
+                       OPENSSL_free(sess);
+                       return;
+                       }
+               prev = sess;
+               }
+       }
+
+static void init_session_cache_ctx(SSL_CTX *sctx)
+       {
+       SSL_CTX_set_session_cache_mode(sctx,
+                       SSL_SESS_CACHE_NO_INTERNAL|SSL_SESS_CACHE_SERVER);
+       SSL_CTX_sess_set_new_cb(sctx, add_session);
+        SSL_CTX_sess_set_get_cb(sctx, get_session);
+        SSL_CTX_sess_set_remove_cb(sctx, del_session);
+       }
+
+static void free_sessions(void)
+       {
+       simple_ssl_session *sess, *tsess;
+       for (sess = first; sess;)
+               {
+               OPENSSL_free(sess->id);
+               OPENSSL_free(sess->der);
+               tsess = sess;
+               sess = sess->next;
+               OPENSSL_free(tsess);
+               }
+       first = NULL;
+       }
+       
+
+
+
+
+
+
+       
+
+