diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index d8cb4748..734a6b2e 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -756,6 +756,14 @@ int ssl3_accept(SSL *s) s->init_num=0; + /* If we aren't retaining peer certificates then we can + * discard it now. */ + if (s->session->peer && s->ctx->retain_only_sha256_of_client_certs) + { + X509_free(s->session->peer); + s->session->peer = NULL; + } + if (s->renegotiate == 2) /* skipped if we just sent a HelloRequest */ { s->renegotiate=0; @@ -2811,6 +2819,7 @@ int ssl3_get_client_certificate(SSL *s) const unsigned char *p,*q; unsigned char *d; STACK_OF(X509) *sk=NULL; + SHA256_CTX sha256; n=s->method->ssl_get_message(s, SSL3_ST_SR_CERT_A, @@ -2872,6 +2881,17 @@ int ssl3_get_client_certificate(SSL *s) goto f_err; } + if (nc == 0 && s->ctx->retain_only_sha256_of_client_certs) + { + /* If this is the first certificate, and we don't want + * to keep peer certificates in memory, then we hash it + * right away. */ + SHA256_Init(&sha256); + SHA256_Update(&sha256, p, l); + SHA256_Final(s->session->peer_sha256, &sha256); + s->session->peer_sha256_valid = 1; + } + q=p; x=d2i_X509(NULL,&p,l); if (x == NULL) diff --git a/ssl/ssl.h b/ssl/ssl.h index 7221c9ea..b7ec5cf9 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -501,6 +501,7 @@ struct ssl_method_st * Ticket [10] EXPLICIT OCTET STRING, -- session ticket (clients only) * Compression_meth [11] EXPLICIT OCTET STRING, -- optional compression method * SRP_username [ 12 ] EXPLICIT OCTET STRING -- optional SRP username + * Peer SHA256 [13] EXPLICIT OCTET STRING, -- optional SHA256 hash of Peer certifiate * } * Look in ssl/ssl_asn1.c for more details * I'm using EXPLICIT tags so I can read the damn things using asn1parse :-). @@ -576,6 +577,8 @@ struct ssl_session_st size_t tlsext_ticklen; /* Session ticket length */ long tlsext_tick_lifetime_hint; /* Session lifetime hint in seconds */ #endif + char peer_sha256_valid; /* Non-zero if peer_sha256 is valid */ + unsigned char peer_sha256[SHA256_DIGEST_LENGTH]; /* SHA256 of peer certificate */ #ifndef OPENSSL_NO_TLSEXT /* Used by client: the proof for this session. * We store it outside the sess_cert structure, since the proof @@ -1034,6 +1037,10 @@ struct ssl_ctx_st unsigned int freelist_max_len; struct ssl3_buf_freelist_st *wbuf_freelist; struct ssl3_buf_freelist_st *rbuf_freelist; + /* retain_only_sha256_of_client_certs is true if we should compute the + * SHA256 hash of the peer's certifiate and then discard it to save + * memory and session space. Only effective on the server side. */ + char retain_only_sha256_of_client_certs; #endif #ifndef OPENSSL_NO_TLSEXT diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c index 267c3b20..45fad037 100644 --- a/ssl/ssl_asn1.c +++ b/ssl/ssl_asn1.c @@ -121,12 +121,13 @@ typedef struct ssl_session_asn1_st ASN1_OCTET_STRING psk_identity_hint; ASN1_OCTET_STRING psk_identity; #endif /* OPENSSL_NO_PSK */ + ASN1_OCTET_STRING peer_sha256; } SSL_SESSION_ASN1; int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp) { #define LSIZE2 (sizeof(long)*2) - int v1=0,v2=0,v3=0,v4=0,v5=0,v7=0,v8=0; + int v1=0,v2=0,v3=0,v4=0,v5=0,v7=0,v8=0,v13=0; unsigned char buf[4],ibuf1[LSIZE2],ibuf2[LSIZE2]; unsigned char ibuf3[LSIZE2],ibuf4[LSIZE2],ibuf5[LSIZE2]; #ifndef OPENSSL_NO_TLSEXT @@ -251,6 +252,13 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp) a.psk_identity.type=V_ASN1_OCTET_STRING; a.psk_identity.data=(unsigned char *)(in->psk_identity); } + + if (in->peer_sha256_valid) + { + a.peer_sha256.length = sizeof(in->peer_sha256); + a.peer_sha256.type = V_ASN1_OCTET_STRING; + a.peer_sha256.data = in->peer_sha256; + } #endif /* OPENSSL_NO_PSK */ M_ASN1_I2D_len(&(a.version), i2d_ASN1_INTEGER); @@ -264,7 +272,7 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp) M_ASN1_I2D_len_EXP_opt(&(a.time),i2d_ASN1_INTEGER,1,v1); if (in->timeout != 0L) M_ASN1_I2D_len_EXP_opt(&(a.timeout),i2d_ASN1_INTEGER,2,v2); - if (in->peer != NULL) + if (in->peer != NULL && in->peer_sha256_valid == 0) M_ASN1_I2D_len_EXP_opt(in->peer,i2d_X509,3,v3); M_ASN1_I2D_len_EXP_opt(&a.session_id_context,i2d_ASN1_OCTET_STRING,4,v4); if (in->verify_result != X509_V_OK) @@ -284,6 +292,8 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp) if (in->psk_identity) M_ASN1_I2D_len_EXP_opt(&(a.psk_identity), i2d_ASN1_OCTET_STRING,8,v8); #endif /* OPENSSL_NO_PSK */ + if (in->peer_sha256_valid) + M_ASN1_I2D_len_EXP_opt(&(a.peer_sha256),i2d_ASN1_OCTET_STRING,13,v13); M_ASN1_I2D_seq_total(); @@ -298,7 +308,7 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp) M_ASN1_I2D_put_EXP_opt(&(a.time),i2d_ASN1_INTEGER,1,v1); if (in->timeout != 0L) M_ASN1_I2D_put_EXP_opt(&(a.timeout),i2d_ASN1_INTEGER,2,v2); - if (in->peer != NULL) + if (in->peer != NULL && in->peer_sha256_valid == 0) M_ASN1_I2D_put_EXP_opt(in->peer,i2d_X509,3,v3); M_ASN1_I2D_put_EXP_opt(&a.session_id_context,i2d_ASN1_OCTET_STRING,4, v4); @@ -320,6 +330,8 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp) if (in->tlsext_tick) M_ASN1_I2D_put_EXP_opt(&(a.tlsext_tick), i2d_ASN1_OCTET_STRING,10,v10); #endif /* OPENSSL_NO_TLSEXT */ + if (in->peer_sha256_valid) + M_ASN1_I2D_put_EXP_opt(&(a.peer_sha256),i2d_ASN1_OCTET_STRING,13,v13); M_ASN1_I2D_finish(); } @@ -541,5 +553,16 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, ret->tlsext_tick=NULL; #endif /* OPENSSL_NO_TLSEXT */ + os.length=0; + os.data=NULL; + M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,13); + if (os.data && os.length == sizeof(ret->peer_sha256)) + { + memcpy(ret->peer_sha256, os.data, sizeof(ret->peer_sha256)); + ret->peer_sha256_valid = 1; + OPENSSL_free(os.data); + os.data = NULL; + } + M_ASN1_D2I_Finish(a,SSL_SESSION_free,SSL_F_D2I_SSL_SESSION); }