diff options
-rw-r--r-- | base/nss_init.cc | 4 | ||||
-rw-r--r-- | build/linux/system.gyp | 3 | ||||
-rw-r--r-- | net/base/ssl_config_service.h | 11 | ||||
-rw-r--r-- | net/base/ssl_info.h | 20 | ||||
-rw-r--r-- | net/flip/flip_session.cc | 11 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.cc | 43 | ||||
-rw-r--r-- | net/third_party/nss/README.google | 5 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl.def | 7 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl.h | 12 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3con.c | 46 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3ext.c | 122 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3prot.h | 8 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslimpl.h | 24 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslsock.c | 74 |
14 files changed, 383 insertions, 7 deletions
diff --git a/base/nss_init.cc b/base/nss_init.cc index 24e3203..015adae 100644 --- a/base/nss_init.cc +++ b/base/nss_init.cc @@ -115,6 +115,7 @@ class NSSInitSingleton { NSS_SetDomesticPolicy(); +#if defined(USE_SYSTEM_SSL) // Use late binding to avoid scary but benign warning // "Symbol `SSL_ImplementedCiphers' has different size in shared object, // consider re-linking" @@ -124,6 +125,9 @@ class NSSInitSingleton { NOTREACHED() << "Can't get list of supported ciphers"; return; } +#else +#define pSSL_ImplementedCiphers SSL_ImplementedCiphers +#endif // Explicitly enable exactly those ciphers with keys of at least 80 bits for (int i = 0; i < SSL_NumImplementedCiphers; i++) { diff --git a/build/linux/system.gyp b/build/linux/system.gyp index 09e420a..eb36588 100644 --- a/build/linux/system.gyp +++ b/build/linux/system.gyp @@ -89,6 +89,9 @@ 'cflags': [ '<!@(<(pkg-config) --cflags nss)', ], + 'defines': [ + 'USE_SYSTEM_SSL', + ], }, 'link_settings': { 'ldflags': [ diff --git a/net/base/ssl_config_service.h b/net/base/ssl_config_service.h index a4d3cb4..45c1fc6 100644 --- a/net/base/ssl_config_service.h +++ b/net/base/ssl_config_service.h @@ -18,7 +18,8 @@ struct SSLConfig { // Default to SSL 2.0 off, SSL 3.0 on, and TLS 1.0 on. SSLConfig() : rev_checking_enabled(true), ssl2_enabled(false), ssl3_enabled(true), - tls1_enabled(true), send_client_cert(false), verify_ev_cert(false) { + tls1_enabled(true), send_client_cert(false), verify_ev_cert(false), + next_protos("\007http1.1") { } bool rev_checking_enabled; // True if server certificate revocation @@ -57,6 +58,14 @@ struct SSLConfig { bool verify_ev_cert; // True if we should verify the certificate for EV. + // The list of application level protocols supported. If set, this will + // enable Next Protocol Negotiation (if supported). This is a list of 8-bit + // length prefixed strings. The order of the protocols doesn't matter expect + // for one case: if the server supports Next Protocol Negotiation, but there + // is no overlap between the server's and client's protocol sets, then the + // first protocol in this list will be requested by the client. + std::string next_protos; + scoped_refptr<X509Certificate> client_cert; }; diff --git a/net/base/ssl_info.h b/net/base/ssl_info.h index 203b6de..5b637c7 100644 --- a/net/base/ssl_info.h +++ b/net/base/ssl_info.h @@ -5,6 +5,8 @@ #ifndef NET_BASE_SSL_INFO_H_ #define NET_BASE_SSL_INFO_H_ +#include <string> + #include "net/base/cert_status_flags.h" #include "net/base/x509_certificate.h" @@ -14,12 +16,25 @@ namespace net { // This is really a struct. All members are public. class SSLInfo { public: - SSLInfo() : cert_status(0), security_bits(-1) { } + // Next Protocol Negotiation (NPN) allows a TLS client and server to come to + // an agreement about the application level protocol to speak over a + // connection. See also the next_protos field in SSLConfig. + enum NextProtoStatus { + kNextProtoUnsupported = 0, // The server doesn't support NPN. + kNextProtoNegotiated = 1, // We agreed on a protocol, see next_proto + kNextProtoNoOverlap = 2, // No protocols in common. We requested + // |next_proto|. + }; + + SSLInfo() : cert_status(0), security_bits(-1), + next_proto_status(kNextProtoUnsupported) { } void Reset() { cert = NULL; security_bits = -1; cert_status = 0; + next_proto.clear(); + next_proto_status = kNextProtoUnsupported; } bool is_valid() const { return cert != NULL; } @@ -41,6 +56,9 @@ class SSLInfo { // 0 means the connection is not encrypted. // -1 means the security strength is unknown. int security_bits; + + NextProtoStatus next_proto_status; // One of kNextProto* + std::string next_proto; // The negotiated protocol, if any. }; } // namespace net diff --git a/net/flip/flip_session.cc b/net/flip/flip_session.cc index b1fe38c..31fe46a 100644 --- a/net/flip/flip_session.cc +++ b/net/flip/flip_session.cc @@ -181,6 +181,17 @@ FlipSession::FlipSession(const std::string& host, HttpNetworkSession* session) flip_framer_.set_visitor(this); session_->ssl_config_service()->GetSSLConfig(&ssl_config_); + + // TODO(agl): This is a temporary hack for testing reasons. In the medium + // term we'll want to use NPN for all HTTPS connections and use the protocol + // suggested. + // + // In the event that the server supports Next Protocol Negotiation, but + // doesn't support either of these protocols, we'll request the first + // protocol in the list. Because of that, HTTP is listed first because it's + // what we'll actually fallback to in the case that the server doesn't + // support SPDY. + ssl_config_.next_protos = "\007http1.1\004spdy"; } FlipSession::~FlipSession() { diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 155d3e5..865c6c6 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -317,6 +317,17 @@ int SSLClientSocketNSS::InitializeSSLOptions() { LOG(INFO) << "SSL_ENABLE_DEFLATE failed. Old system nss?"; #endif +#ifdef SSL_NEXT_PROTO_NEGOTIATED + if (!ssl_config_.next_protos.empty()) { + rv = SSL_SetNextProtoNego( + nss_fd_, + reinterpret_cast<const unsigned char *>(ssl_config_.next_protos.data()), + ssl_config_.next_protos.size()); + if (rv != SECSuccess) + LOG(INFO) << "SSL_SetNextProtoNego failed."; + } +#endif + rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); if (rv != SECSuccess) return ERR_UNEXPECTED; @@ -510,6 +521,38 @@ void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { ssl_info->cert_status = server_cert_verify_result_.cert_status; DCHECK(server_cert_ != NULL); ssl_info->cert = server_cert_; + +#ifdef SSL_NEXT_PROTO_NEGOTIATED + unsigned char npn_buf[255]; + unsigned npn_len; + int npn_status; + SECStatus rv = SSL_GetNextProto(nss_fd_, &npn_status, npn_buf, &npn_len, + sizeof(npn_buf)); + if (rv != SECSuccess) { + npn_status = SSL_NEXT_PROTO_NO_SUPPORT; + } + + if (npn_status == SSL_NEXT_PROTO_NO_SUPPORT) { + ssl_info->next_proto_status = SSLInfo::kNextProtoUnsupported; + ssl_info->next_proto.clear(); + } else { + ssl_info->next_proto = + std::string(reinterpret_cast<const char *>(npn_buf), npn_len); + switch (npn_status) { + case SSL_NEXT_PROTO_NEGOTIATED: + ssl_info->next_proto_status = SSLInfo::kNextProtoNegotiated; + break; + case SSL_NEXT_PROTO_NO_OVERLAP: + ssl_info->next_proto_status = SSLInfo::kNextProtoNoOverlap; + break; + default: + LOG(ERROR) << "Unknown npn_status: " << npn_status; + ssl_info->next_proto_status = SSLInfo::kNextProtoNoOverlap; + break; + } + } +#endif + LeaveFunction(""); } diff --git a/net/third_party/nss/README.google b/net/third_party/nss/README.google index b0d60f5..63f0bb4 100644 --- a/net/third_party/nss/README.google +++ b/net/third_party/nss/README.google @@ -3,7 +3,10 @@ This directory includes a copy of NSS's libssl from the CVS repo at: The snapshot was taken at Thu Nov 12 17:19:36 PST 2009. -There are no local patches. +Patches: + + * Next protocol negotiation support. + http://codereview.chromium.org/415005 The ssl/bodge directory contains files taken from the NSS repo that we required for building libssl outside of its usual build environment. diff --git a/net/third_party/nss/ssl/ssl.def b/net/third_party/nss/ssl/ssl.def index e4d4d1d..6a11804 100644 --- a/net/third_party/nss/ssl/ssl.def +++ b/net/third_party/nss/ssl/ssl.def @@ -139,3 +139,10 @@ SSL_CanBypass; ;+ local: ;+*; ;+}; +;+NSS_CHROMIUM { # Chromium experimental +;+ global: +SSL_GetNextProto; +SSL_SetNextProtoNego; +;+ local: +;+*; +;+}; diff --git a/net/third_party/nss/ssl/ssl.h b/net/third_party/nss/ssl/ssl.h index 7540796..b3be5fc 100644 --- a/net/third_party/nss/ssl/ssl.h +++ b/net/third_party/nss/ssl/ssl.h @@ -135,6 +135,18 @@ SSL_IMPORT SECStatus SSL_OptionSetDefault(PRInt32 option, PRBool on); SSL_IMPORT SECStatus SSL_OptionGetDefault(PRInt32 option, PRBool *on); SSL_IMPORT SECStatus SSL_CertDBHandleSet(PRFileDesc *fd, CERTCertDBHandle *dbHandle); +SSL_IMPORT SECStatus SSL_SetNextProtoNego(PRFileDesc *fd, + const unsigned char *data, + unsigned short length); +SSL_IMPORT SECStatus SSL_GetNextProto(PRFileDesc *fd, + int *state, + unsigned char *buf, + unsigned *length, + unsigned buf_len); +#define SSL_NEXT_PROTO_NO_SUPPORT 0 /* No peer support */ +#define SSL_NEXT_PROTO_NEGOTIATED 1 /* Mutual agreement */ +#define SSL_NEXT_PROTO_NO_OVERLAP 2 /* No protocol overlap found */ + /* ** Control ciphers that SSL uses. If on is non-zero then the named cipher ** is enabled, otherwise it is disabled. diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c index ff93bf4..f908382 100644 --- a/net/third_party/nss/ssl/ssl3con.c +++ b/net/third_party/nss/ssl/ssl3con.c @@ -85,6 +85,7 @@ static SECStatus ssl3_InitState( sslSocket *ss); static SECStatus ssl3_SendCertificate( sslSocket *ss); static SECStatus ssl3_SendEmptyCertificate( sslSocket *ss); static SECStatus ssl3_SendCertificateRequest(sslSocket *ss); +static SECStatus ssl3_SendNextProto( sslSocket *ss); static SECStatus ssl3_SendFinished( sslSocket *ss, PRInt32 flags); static SECStatus ssl3_SendServerHello( sslSocket *ss); static SECStatus ssl3_SendServerHelloDone( sslSocket *ss); @@ -5619,6 +5620,12 @@ ssl3_HandleServerHelloDone(sslSocket *ss) if (rv != SECSuccess) { goto loser; /* err code was set. */ } + + rv = ssl3_SendNextProto(ss); + if (rv != SECSuccess) { + goto loser; /* err code was set. */ + } + rv = ssl3_SendFinished(ss, 0); if (rv != SECSuccess) { goto loser; /* err code was set. */ @@ -7797,6 +7804,40 @@ ssl3_ComputeTLSFinished(ssl3CipherSpec *spec, } /* called from ssl3_HandleServerHelloDone + */ +static SECStatus +ssl3_SendNextProto(sslSocket *ss) +{ + SECStatus rv; + int padding_len; + static const unsigned char padding[32] = {0}; + + if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NO_SUPPORT) + return SECSuccess; + + PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + padding_len = 32 - ((ss->ssl3.nextProto.len + 2) % 32); + + rv = ssl3_AppendHandshakeHeader(ss, next_proto, ss->ssl3.nextProto.len + + 2 + padding_len); + if (rv != SECSuccess) { + return rv; /* error code set by AppendHandshakeHeader */ + } + rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.nextProto.data, + ss->ssl3.nextProto.len, 1); + if (rv != SECSuccess) { + return rv; /* error code set by AppendHandshake */ + } + rv = ssl3_AppendHandshakeVariable(ss, padding, padding_len, 1); + if (rv != SECSuccess) { + return rv; /* error code set by AppendHandshake */ + } + return rv; +} + +/* called from ssl3_HandleServerHelloDone * ssl3_HandleClientHello * ssl3_HandleFinished */ @@ -9072,6 +9113,11 @@ ssl3_DestroySSL3Info(sslSocket *ss) ssl3_DestroyCipherSpec(&ss->ssl3.specs[1]); ss->ssl3.initialized = PR_FALSE; + + if (ss->ssl3.nextProto.data) { + PORT_Free(ss->ssl3.nextProto.data); + ss->ssl3.nextProto.data = NULL; + } } /* End of ssl3con.c */ diff --git a/net/third_party/nss/ssl/ssl3ext.c b/net/third_party/nss/ssl/ssl3ext.c index 21bb818..0365d5a 100644 --- a/net/third_party/nss/ssl/ssl3ext.c +++ b/net/third_party/nss/ssl/ssl3ext.c @@ -229,6 +229,7 @@ static const ssl3HelloExtensionHandler clientHelloHandlers[] = { { ec_point_formats_xtn, &ssl3_HandleSupportedPointFormatsXtn }, #endif { session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn }, + { next_proto_neg_xtn, &ssl3_ServerHandleNextProtoNegoXtn }, { -1, NULL } }; @@ -236,6 +237,7 @@ static const ssl3HelloExtensionHandler serverHelloHandlers[] = { { server_name_xtn, &ssl3_HandleServerNameXtn }, /* TODO: add a handler for ec_point_formats_xtn */ { session_ticket_xtn, &ssl3_ClientHandleSessionTicketXtn }, + { next_proto_neg_xtn, &ssl3_ClientHandleNextProtoNegoXtn }, { -1, NULL } }; @@ -254,7 +256,8 @@ ssl3HelloExtensionSender clientHelloSenders[MAX_EXTENSIONS] = { { -1, NULL }, { -1, NULL }, #endif - { session_ticket_xtn, ssl3_SendSessionTicketXtn } + { session_ticket_xtn, ssl3_SendSessionTicketXtn }, + { next_proto_neg_xtn, ssl3_ClientSendNextProtoNegoXtn } }; static PRBool @@ -412,6 +415,123 @@ ssl3_SendSessionTicketXtn( return -1; } +/* handle an incoming Next Protocol Negotiation extension. */ +SECStatus +ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data) +{ + if (data->len != 0) { + /* Clients MUST send an empty NPN extension, if any. */ + return SECFailure; + } + + ss->ssl3.hs.nextProtoNego = PR_TRUE; + return SECSuccess; +} + +/* ssl3_ValidateNextProtoNego checks that the given block of data is valid: none + * of the length may be 0 and the sum of the lengths must equal the length of + * the block. */ +SECStatus +ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned short length) +{ + unsigned int offset = 0; + + while (offset < length) { + if (data[offset] == 0) { + return SECFailure; + } + offset += data[offset] + 1; + } + + if (offset > length) + return SECFailure; + + return SECSuccess; +} + +SECStatus +ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + unsigned int i, j; + SECStatus rv; + unsigned char *result; + + if (data->len == 0) { + /* The server supports the extension, but doesn't have any + * protocols configured. In this case we request our favoured + * protocol. */ + goto pick_first; + } + + rv = ssl3_ValidateNextProtoNego(data->data, data->len); + if (rv != SECSuccess) + return rv; + + /* For each protocol in server preference order, see if we support it. */ + for (i = 0; i < data->len; ) { + for (j = 0; j < ss->opt.nextProtoNego.len; ) { + if (data->data[i] == ss->opt.nextProtoNego.data[j] && + memcmp(&data->data[i+1], &ss->opt.nextProtoNego.data[j+1], + data->data[i]) == 0) { + /* We found a match */ + ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NEGOTIATED; + result = &data->data[i]; + goto found; + } + j += ss->opt.nextProtoNego.data[j] + 1; + } + + i += data->data[i] + 1; + } + + pick_first: + ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NO_OVERLAP; + result = ss->opt.nextProtoNego.data; + + found: + if (ss->ssl3.nextProto.data) + PORT_Free(ss->ssl3.nextProto.data); + ss->ssl3.nextProto.data = PORT_Alloc(result[0]); + PORT_Memcpy(ss->ssl3.nextProto.data, result + 1, result[0]); + ss->ssl3.nextProto.len = result[0]; + return SECSuccess; +} + +PRInt32 +ssl3_ClientSendNextProtoNegoXtn(sslSocket * ss, + PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + + /* Renegotiations do not send this extension. */ + if (ss->opt.nextProtoNego.len == 0 || ss->firstHsDone) { + return 0; + } + + extension_length = 4; + + if (append && maxBytes >= extension_length) { + SECStatus rv; + rv = ssl3_AppendHandshakeNumber(ss, next_proto_neg_xtn, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + goto loser; + TLSExtensionData *xtnData = &ss->xtnData; + xtnData->advertised[xtnData->numAdvertised++] = next_proto_neg_xtn; + } else if (maxBytes < extension_length) { + return 0; + } + + return extension_length; + + loser: + return -1; +} + /* * NewSessionTicket * Called from ssl3_HandleFinished diff --git a/net/third_party/nss/ssl/ssl3prot.h b/net/third_party/nss/ssl/ssl3prot.h index 139af0d..84d73a9 100644 --- a/net/third_party/nss/ssl/ssl3prot.h +++ b/net/third_party/nss/ssl/ssl3prot.h @@ -157,7 +157,8 @@ typedef enum { server_hello_done = 14, certificate_verify = 15, client_key_exchange = 16, - finished = 20 + finished = 20, + next_proto = 67 } SSL3HandshakeType; typedef struct { @@ -351,10 +352,11 @@ typedef enum { elliptic_curves_xtn = 10, ec_point_formats_xtn = 11, #endif - session_ticket_xtn = 35 + session_ticket_xtn = 35, + next_proto_neg_xtn = 13172 } ExtensionType; -#define MAX_EXTENSIONS 4 +#define MAX_EXTENSIONS 5 #define TLS_EX_SESS_TICKET_MAC_LENGTH 32 diff --git a/net/third_party/nss/ssl/sslimpl.h b/net/third_party/nss/ssl/sslimpl.h index 0b69910..0a7fbcf 100644 --- a/net/third_party/nss/ssl/sslimpl.h +++ b/net/third_party/nss/ssl/sslimpl.h @@ -317,6 +317,11 @@ typedef struct { #endif /* NSS_ENABLE_ECC */ typedef struct sslOptionsStr { + /* For clients, this is a validated list of protocols in preference order + * and wire format. For servers, this is the list of support protocols, + * also in wire format. */ + SECItem nextProtoNego; + unsigned int useSecurity : 1; /* 1 */ unsigned int useSocks : 1; /* 2 */ unsigned int requestCertificate : 1; /* 3 */ @@ -770,6 +775,7 @@ const ssl3CipherSuiteDef *suite_def; #ifdef NSS_ENABLE_ECC PRUint32 negotiatedECCurves; /* bit mask */ #endif /* NSS_ENABLE_ECC */ + PRBool nextProtoNego;/* Our peer has sent this extension */ } SSL3HandshakeState; @@ -811,6 +817,16 @@ struct ssl3StateStr { PRBool initialized; SSL3HandshakeState hs; ssl3CipherSpec specs[2]; /* one is current, one is pending. */ + + /* In a client: if the server supports Next Protocol Negotiation, then + * this is the protocol that was requested. + * In a server: this is the protocol that the client requested via Next + * Protocol Negotiation. + * + * In either case, if the data pointer is non-NULL, then it is malloced + * data. */ + SECItem nextProto; + int nextProtoState; /* See SSL_NEXT_PROTO_* defines */ }; typedef struct { @@ -1471,8 +1487,12 @@ extern SECStatus ssl3_HandleSupportedPointFormatsXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data); extern SECStatus ssl3_ClientHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); +extern SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, + PRUint16 ex_type, SECItem *data); extern SECStatus ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); +extern SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss, + PRUint16 ex_type, SECItem *data); /* ClientHello and ServerHello extension senders. * Note that not all extension senders are exposed here; only those that @@ -1486,6 +1506,10 @@ extern PRInt32 ssl3_SendSupportedCurvesXtn(sslSocket *ss, extern PRInt32 ssl3_SendSupportedPointFormatsXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes); #endif +extern PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +extern SECStatus ssl3_ValidateNextProtoNego(const unsigned char* data, + unsigned short length); /* call the registered extension handlers. */ extern SECStatus ssl3_HandleHelloExtensions(sslSocket *ss, diff --git a/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c index c173086..d0a70e5 100644 --- a/net/third_party/nss/ssl/sslsock.c +++ b/net/third_party/nss/ssl/sslsock.c @@ -163,6 +163,7 @@ static const sslSocketOps ssl_secure_ops = { /* SSL. */ ** default settings for socket enables */ static sslOptions ssl_defaults = { + { siBuffer, NULL, 0 }, /* nextProtoNego */ PR_TRUE, /* useSecurity */ PR_FALSE, /* useSocks */ PR_FALSE, /* requestCertificate */ @@ -434,6 +435,10 @@ ssl_DestroySocketContents(sslSocket *ss) ssl3_FreeKeyPair(ss->ephemeralECDHKeyPair); ss->ephemeralECDHKeyPair = NULL; } + if (ss->opt.nextProtoNego.data) { + PORT_Free(ss->opt.nextProtoNego.data); + ss->opt.nextProtoNego.data = NULL; + } } /* @@ -1247,6 +1252,75 @@ SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd) return fd; } +/* SSL_SetNextProtoNego sets the list of supported protocols for the given + * socket. The list is a series of 8-bit, length prefixed strings. */ +SECStatus +SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data, + unsigned short length) +{ + sslSocket *ss = ssl_FindSocket(fd); + + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetNextProtoNego", SSL_GETPID(), + fd)); + return SECFailure; + } + + if (ssl3_ValidateNextProtoNego(data, length) != SECSuccess) + return SECFailure; + + ssl_GetSSL3HandshakeLock(ss); + if (ss->opt.nextProtoNego.data) + PORT_Free(ss->opt.nextProtoNego.data); + ss->opt.nextProtoNego.data = PORT_Alloc(length); + if (!ss->opt.nextProtoNego.data) { + ssl_ReleaseSSL3HandshakeLock(ss); + return SECFailure; + } + memcpy(ss->opt.nextProtoNego.data, data, length); + ss->opt.nextProtoNego.len = length; + ss->opt.nextProtoNego.type = siBuffer; + ssl_ReleaseSSL3HandshakeLock(ss); + + return SECSuccess; +} + +/* SSL_GetNextProto reads the resulting Next Protocol Negotiation result for + * the given socket. It's only valid to call this once the handshake has + * completed. + * + * state is set to one of the SSL_NEXT_PROTO_* constants. The negotiated + * protocol, if any, is written into buf, which must be at least buf_len + * bytes long. If the negotiated protocol is longer than this, it is truncated. + * The number of bytes copied is written into length. + */ +SECStatus +SSL_GetNextProto(PRFileDesc *fd, int *state, unsigned char *buf, + unsigned int *length, unsigned int buf_len) +{ + sslSocket *ss = ssl_FindSocket(fd); + + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetNextProto", SSL_GETPID(), + fd)); + return SECFailure; + } + + *state = ss->ssl3.nextProtoState; + + if (ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT && + ss->ssl3.nextProto.data) { + *length = ss->ssl3.nextProto.len; + if (*length > buf_len) + *length = buf_len; + PORT_Memcpy(buf, ss->ssl3.nextProto.data, *length); + } else { + *length = 0; + } + + return SECSuccess; +} + /************************************************************************/ /* The following functions are the TOP LEVEL SSL functions. ** They all get called through the NSPRIOMethods table below. |