Add some more accessors to SSL_SESSION.

Hopefully this is the last of it before we can hide the struct. We're
missing peer_sha256 accessors, and some test wants to mutate the ticket
in a test client.

Change-Id: I1a30fcc0a1e866d42acbc07a776014c9257f7c86
Reviewed-on: https://boringssl-review.googlesource.com/28268
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 4108c42..7e229e1 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1747,6 +1747,13 @@
                                             const uint8_t **out_ticket,
                                             size_t *out_len);
 
+// SSL_SESSION_set_ticket sets |session|'s ticket to |ticket|. It returns one on
+// success and zero on error. This function may be useful in writing tests but
+// otherwise should not be used.
+OPENSSL_EXPORT int SSL_SESSION_set_ticket(SSL_SESSION *session,
+                                          const uint8_t *ticket,
+                                          size_t ticket_len);
+
 // SSL_SESSION_get_ticket_lifetime_hint returns ticket lifetime hint of
 // |session| in seconds or zero if none was set.
 OPENSSL_EXPORT uint32_t
@@ -1761,6 +1768,19 @@
 OPENSSL_EXPORT const SSL_CIPHER *SSL_SESSION_get0_cipher(
     const SSL_SESSION *session);
 
+// SSL_SESSION_has_peer_sha256 returns one if |session| has a SHA-256 hash of
+// the peer's certificate retained and zero if the peer did not present a
+// certificate or if this was not enabled when |session| was created. See also
+// |SSL_CTX_set_retain_only_sha256_of_client_certs|.
+OPENSSL_EXPORT int SSL_SESSION_has_peer_sha256(const SSL_SESSION *session);
+
+// SSL_SESSION_get0_peer_sha256 sets |*out_ptr| and |*out_len| to the SHA-256
+// hash of the peer certificate retained in |session|, or NULL and zero if it
+// does not have one. See also |SSL_CTX_set_retain_only_sha256_of_client_certs|.
+OPENSSL_EXPORT void SSL_SESSION_get0_peer_sha256(const SSL_SESSION *session,
+                                                 const uint8_t **out_ptr,
+                                                 size_t *out_len);
+
 
 // Session caching.
 //
@@ -3588,7 +3608,8 @@
 // the SHA-256 hash of peer's certificate should be saved in memory and in the
 // session. This can save memory, ticket size and session cache space. If
 // enabled, |SSL_get_peer_certificate| will return NULL after the handshake
-// completes. See the |peer_sha256| field of |SSL_SESSION| for the hash.
+// completes. See |SSL_SESSION_has_peer_sha256| and
+// |SSL_SESSION_get0_peer_sha256| to query the hash.
 OPENSSL_EXPORT void SSL_set_retain_only_sha256_of_client_certs(SSL *ssl,
                                                                int enable);
 
@@ -3596,7 +3617,8 @@
 // only the SHA-256 hash of peer's certificate should be saved in memory and in
 // the session. This can save memory, ticket size and session cache space. If
 // enabled, |SSL_get_peer_certificate| will return NULL after the handshake
-// completes. See the |peer_sha256| field of |SSL_SESSION| for the hash.
+// completes. See |SSL_SESSION_has_peer_sha256| and
+// |SSL_SESSION_get0_peer_sha256| to query the hash.
 OPENSSL_EXPORT void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx,
                                                                    int enable);
 
diff --git a/ssl/ssl_session.cc b/ssl/ssl_session.cc
index 9ecadcf..d8f3bbd 100644
--- a/ssl/ssl_session.cc
+++ b/ssl/ssl_session.cc
@@ -988,6 +988,18 @@
   *out_len = session->tlsext_ticklen;
 }
 
+int SSL_SESSION_set_ticket(SSL_SESSION *session, const uint8_t *ticket,
+                           size_t ticket_len) {
+  uint8_t *copy = (uint8_t *)BUF_memdup(ticket, ticket_len);
+  if (copy == nullptr) {
+    return 0;
+  }
+  OPENSSL_free(session->tlsext_tick);
+  session->tlsext_tick = copy;
+  session->tlsext_ticklen = ticket_len;
+  return 1;
+}
+
 uint32_t SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session) {
   return session->tlsext_tick_lifetime_hint;
 }
@@ -996,6 +1008,21 @@
   return session->cipher;
 }
 
+int SSL_SESSION_has_peer_sha256(const SSL_SESSION *session) {
+  return session->peer_sha256_valid;
+}
+
+void SSL_SESSION_get0_peer_sha256(const SSL_SESSION *session,
+                                  const uint8_t **out_ptr, size_t *out_len) {
+  if (session->peer_sha256_valid) {
+    *out_ptr = session->peer_sha256;
+    *out_len = sizeof(session->peer_sha256);
+  } else {
+    *out_ptr = nullptr;
+    *out_len = 0;
+  }
+}
+
 SSL_SESSION *SSL_magic_pending_session_ptr(void) {
   return (SSL_SESSION *)&g_pending_session_magic;
 }
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 4a1497c..347f9da 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -1949,9 +1949,12 @@
   EXPECT_FALSE(peer);
 
   SSL_SESSION *session = SSL_get_session(server_.get());
-  EXPECT_TRUE(session->peer_sha256_valid);
+  EXPECT_TRUE(SSL_SESSION_has_peer_sha256(session));
 
-  EXPECT_EQ(Bytes(cert_sha256), Bytes(session->peer_sha256));
+  const uint8_t *peer_sha256;
+  size_t peer_sha256_len;
+  SSL_SESSION_get0_peer_sha256(session, &peer_sha256, &peer_sha256_len);
+  EXPECT_EQ(Bytes(cert_sha256), Bytes(peer_sha256, peer_sha256_len));
 }
 
 // Tests that our ClientHellos do not change unexpectedly. These are purely
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index ae0d2a1..cd9b770 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1591,7 +1591,7 @@
     }
   }
 
-  if (SSL_get_session(ssl)->peer_sha256_valid !=
+  if (!!SSL_SESSION_has_peer_sha256(SSL_get_session(ssl)) !=
       config->expect_sha256_client_cert) {
     fprintf(stderr,
             "Unexpected SHA-256 client cert state: expected:%d is_resume:%d.\n",
@@ -1600,12 +1600,30 @@
   }
 
   if (config->expect_sha256_client_cert &&
-      SSL_get_session(ssl)->certs != nullptr) {
+      SSL_SESSION_get0_peer_certificates(SSL_get_session(ssl)) != nullptr) {
     fprintf(stderr, "Have both client cert and SHA-256 hash: is_resume:%d.\n",
             is_resume);
     return false;
   }
 
+  const uint8_t *peer_sha256;
+  size_t peer_sha256_len;
+  SSL_SESSION_get0_peer_sha256(SSL_get_session(ssl), &peer_sha256,
+                               &peer_sha256_len);
+  if (SSL_SESSION_has_peer_sha256(SSL_get_session(ssl))) {
+    if (peer_sha256_len != 32) {
+      fprintf(stderr, "Peer SHA-256 hash had length %zu instead of 32\n",
+              peer_sha256_len);
+      return false;
+    }
+  } else {
+    if (peer_sha256_len != 0) {
+      fprintf(stderr, "Unexpected peer SHA-256 hash of length %zu\n",
+              peer_sha256_len);
+      return false;
+    }
+  }
+
   return true;
 }