blob: a31049aec757d6bfed8db1913c8bd5db8ee52a69 [file] [log] [blame]
diff --git a/lib/ssl/ssl.h b/lib/ssl/ssl.h
index 120c257..eb7f7ec 100644
--- a/lib/ssl/ssl.h
+++ b/lib/ssl/ssl.h
@@ -385,6 +385,27 @@ SSL_IMPORT SECStatus SSL_DHEGroupPrefSet(PRFileDesc *fd,
*/
SSL_IMPORT SECStatus SSL_EnableWeakDHEPrimeGroup(PRFileDesc *fd, PRBool enabled);
+/* SSLChannelBindingType enumerates the types of supported channel binding
+ * values. See RFC 5929. */
+typedef enum SSLChannelBindingType {
+ SSL_CHANNEL_BINDING_TLS_UNIQUE = 1,
+} SSLChannelBindingType;
+
+/* SSL_GetChannelBinding copies the requested channel binding value, as defined
+ * in RFC 5929, into |out|. The full length of the binding value is written
+ * into |*outLen|.
+ *
+ * At most |outLenMax| bytes of data are copied. If |outLenMax| is
+ * insufficient then the function returns SECFailure and sets the error to
+ * SEC_ERROR_OUTPUT_LEN, but |*outLen| is still set.
+ *
+ * This call will fail if made during a renegotiation. */
+SSL_IMPORT SECStatus SSL_GetChannelBinding(PRFileDesc *fd,
+ SSLChannelBindingType binding_type,
+ unsigned char *out,
+ unsigned int *outLen,
+ unsigned int outLenMax);
+
/* SSL Version Range API
**
** This API should be used to control SSL 3.0 & TLS support instead of the
diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c
index 2ae8ce9..ce92cf1 100644
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -13241,6 +13241,68 @@ ssl3_InitSocketPolicy(sslSocket *ss)
ss->ssl3.signatureAlgorithmCount = PR_ARRAY_SIZE(defaultSignatureAlgorithms);
}
+SECStatus
+ssl3_GetTLSUniqueChannelBinding(sslSocket *ss,
+ unsigned char *out,
+ unsigned int *outLen,
+ unsigned int outLenMax) {
+ PRBool isTLS;
+ int index = 0;
+ unsigned int len;
+ SECStatus rv = SECFailure;
+
+ *outLen = 0;
+
+ ssl_GetSSL3HandshakeLock(ss);
+
+ ssl_GetSpecReadLock(ss);
+ isTLS = (PRBool)(ss->ssl3.cwSpec->version > SSL_LIBRARY_VERSION_3_0);
+ ssl_ReleaseSpecReadLock(ss);
+
+ /* The tls-unique channel binding is the first Finished structure in the
+ * handshake. In the case of a resumption, that's the server's Finished.
+ * Otherwise, it's the client's Finished. */
+ len = ss->ssl3.hs.finishedBytes;
+
+ /* Sending or receiving a Finished message will set finishedBytes to a
+ * non-zero value. */
+ if (len == 0) {
+ PORT_SetError(SSL_ERROR_HANDSHAKE_NOT_COMPLETED);
+ goto loser;
+ }
+
+ /* If we are in the middle of a renegotiation then the channel binding
+ * value is poorly defined and depends on the direction that it will be
+ * used on. Therefore we simply return an error in this case. */
+ if (ss->firstHsDone && ss->ssl3.hs.ws != idle_handshake) {
+ PORT_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
+ goto loser;
+ }
+
+ /* If resuming, then we want the second Finished value in the array, which
+ * is the server's */
+ if (ss->ssl3.hs.isResuming)
+ index = 1;
+
+ *outLen = len;
+ if (outLenMax < len) {
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ goto loser;
+ }
+
+ if (isTLS) {
+ memcpy(out, &ss->ssl3.hs.finishedMsgs.tFinished[index], len);
+ } else {
+ memcpy(out, &ss->ssl3.hs.finishedMsgs.sFinished[index], len);
+ }
+
+ rv = SECSuccess;
+
+loser:
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ return rv;
+}
+
/* ssl3_config_match_init must have already been called by
* the caller of this function.
*/
diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h
index c286518..976330e 100644
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -1897,6 +1897,11 @@ extern PRBool ssl_GetSessionTicketKeysPKCS11(SECKEYPrivateKey *svrPrivKey,
extern SECStatus ssl3_ValidateNextProtoNego(const unsigned char* data,
unsigned int length);
+extern SECStatus ssl3_GetTLSUniqueChannelBinding(sslSocket *ss,
+ unsigned char *out,
+ unsigned int *outLen,
+ unsigned int outLenMax);
+
/* Construct a new NSPR socket for the app to use */
extern PRFileDesc *ssl_NewPRSocket(sslSocket *ss, PRFileDesc *fd);
extern void ssl_FreePRSocket(PRFileDesc *fd);
diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c
index efba686..c9a4493 100644
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -1540,6 +1540,28 @@ SSL_EnableWeakDHEPrimeGroup(PRFileDesc *fd, PRBool enabled)
return SECSuccess;
}
+SECStatus
+SSL_GetChannelBinding(PRFileDesc *fd,
+ SSLChannelBindingType binding_type,
+ unsigned char *out,
+ unsigned int *outLen,
+ unsigned int outLenMax) {
+ sslSocket *ss = ssl_FindSocket(fd);
+
+ if (!ss) {
+ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetChannelBinding",
+ SSL_GETPID(), fd));
+ return SECFailure;
+ }
+
+ if (binding_type != SSL_CHANNEL_BINDING_TLS_UNIQUE) {
+ PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
+ return SECFailure;
+ }
+
+ return ssl3_GetTLSUniqueChannelBinding(ss, out, outLen, outLenMax);
+}
+
#include "dhe-param.c"
static const SSLDHEGroupType ssl_default_dhe_groups[] = {