Source code

Revision control

Copy as Markdown

Other Tools

/*
* Basic SSL handshake functions.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nssrenam.h"
#include "cert.h"
#include "secitem.h"
#include "sechash.h"
#include "cryptohi.h" /* for SGN_ funcs */
#include "keyhi.h" /* for SECKEY_ high level functions. */
#include "ssl.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "ssl3prot.h"
#include "sslerr.h"
#include "pk11func.h"
#include "prinit.h"
/*
** Put a string tag in the library so that we can examine an executable
** and see what kind of security it supports.
*/
const char *ssl_version = "SECURITY_VERSION:"
" +us"
" +export"
#ifdef TRACE
" +trace"
#endif
#ifdef DEBUG
" +debug"
#endif
;
/***********************************************************************
* Gathers in and handles records/messages until either the handshake is
* complete or application data is available.
*
* Called from ssl_Do1stHandshake() via function pointer ss->handshake.
* Caller must hold handshake lock.
* This function acquires and releases the RecvBufLock.
*
* returns SECSuccess for success.
* returns SECFailure on error, setting PR_WOULD_BLOCK_ERROR if only blocked.
*
* The gather functions called by ssl_GatherRecord1stHandshake are expected
* to return values interpreted as follows:
* 1 : the function completed without error.
* 0 : the function read EOF.
* -1 : read error, or PR_WOULD_BLOCK_ERROR, or handleRecord error.
*
* This code is similar to, and easily confused with, DoRecv() in sslsecur.c
*
* This function is called from ssl_Do1stHandshake().
* The following functions put ssl_GatherRecord1stHandshake into ss->handshake:
* ssl_BeginClientHandshake
* ssl3_RestartHandshakeAfterCertReq
* ssl3_RestartHandshakeAfterServerCert
* ssl_BeginServerHandshake
*/
SECStatus
ssl_GatherRecord1stHandshake(sslSocket *ss)
{
int rv;
PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss));
ssl_GetRecvBufLock(ss);
/* Wait for handshake to complete, or application data to arrive. */
rv = ssl3_GatherCompleteHandshake(ss, 0);
SSL_TRC(10, ("%d: SSL[%d]: handshake gathering, rv=%d",
SSL_GETPID(), ss->fd, rv));
ssl_ReleaseRecvBufLock(ss);
if (rv <= 0) {
if (rv == 0) {
/* EOF. Loser */
PORT_SetError(PR_END_OF_FILE_ERROR);
}
if (PORT_GetError() == PR_WOULD_BLOCK_ERROR) {
SSL_TRC(10, ("%d: SSL[%d]: handshake blocked (need %d)",
SSL_GETPID(), ss->fd, ss->gs.remainder));
}
return SECFailure; /* rv is < 0 here. */
}
ss->handshake = NULL;
return SECSuccess;
}
/* This function is called at the beginning of a handshake to ensure that at
* least one SSL/TLS version is enabled. */
static SECStatus
ssl_CheckConfigSanity(sslSocket *ss)
{
if (SSL_ALL_VERSIONS_DISABLED(&ss->vrange)) {
SSL_DBG(("%d: SSL[%d]: Can't handshake! all versions disabled.",
SSL_GETPID(), ss->fd));
PORT_SetError(SSL_ERROR_SSL_DISABLED);
return SECFailure;
}
return SECSuccess;
}
/* Sends out the initial client Hello message on the connection.
* Acquires and releases the socket's xmitBufLock.
*/
SECStatus
ssl_BeginClientHandshake(sslSocket *ss)
{
sslSessionID *sid = NULL;
SECStatus rv;
PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss));
ss->sec.isServer = PR_FALSE;
rv = ssl_CheckConfigSanity(ss);
if (rv != SECSuccess)
goto loser;
/* Get peer name of server */
rv = ssl_GetPeerInfo(ss);
if (rv < 0) {
#ifdef HPUX11
/*
* On some HP-UX B.11.00 systems, getpeername() occasionally
* fails with ENOTCONN after a successful completion of
* non-blocking connect. I found that if we do a write()
* and then retry getpeername(), it will work.
*/
if (PR_GetError() == PR_NOT_CONNECTED_ERROR) {
char dummy;
(void)PR_Write(ss->fd->lower, &dummy, 0);
rv = ssl_GetPeerInfo(ss);
if (rv < 0) {
goto loser;
}
}
#else
goto loser;
#endif
}
SSL_TRC(3, ("%d: SSL[%d]: sending client-hello", SSL_GETPID(), ss->fd));
/* If there's an sid set from an external cache, use it. */
if (ss->sec.ci.sid && ss->sec.ci.sid->cached == in_external_cache) {
sid = ss->sec.ci.sid;
SSL_TRC(3, ("%d: SSL[%d]: using external token", SSL_GETPID(), ss->fd));
} else if (!ss->opt.noCache) {
/* Try to find server in our session-id cache */
sid = ssl_LookupSID(ssl_Time(ss), &ss->sec.ci.peer,
ss->sec.ci.port, ss->peerID, ss->url);
}
if (sid) {
if (sid->version >= ss->vrange.min && sid->version <= ss->vrange.max) {
PORT_Assert(!ss->sec.localCert);
ss->sec.localCert = CERT_DupCertificate(sid->localCert);
} else {
ssl_UncacheSessionID(ss);
ssl_FreeSID(sid);
sid = NULL;
}
}
if (!sid) {
sid = ssl3_NewSessionID(ss, PR_FALSE);
if (!sid) {
goto loser;
}
/* This session is a dummy, which we don't want to resume. */
sid->u.ssl3.keys.resumable = PR_FALSE;
}
ss->sec.ci.sid = sid;
ss->gs.state = GS_INIT;
ss->handshake = ssl_GatherRecord1stHandshake;
/* ssl3_SendClientHello will override this if it succeeds. */
ss->version = SSL_LIBRARY_VERSION_3_0;
ssl_GetSSL3HandshakeLock(ss);
ssl_GetXmitBufLock(ss);
rv = ssl3_SendClientHello(ss, client_hello_initial);
ssl_ReleaseXmitBufLock(ss);
ssl_ReleaseSSL3HandshakeLock(ss);
return rv;
loser:
return SECFailure;
}
SECStatus
ssl_BeginServerHandshake(sslSocket *ss)
{
SECStatus rv;
ss->sec.isServer = PR_TRUE;
ss->ssl3.hs.ws = wait_client_hello;
rv = ssl_CheckConfigSanity(ss);
if (rv != SECSuccess)
goto loser;
ss->handshake = ssl_GatherRecord1stHandshake;
return SECSuccess;
loser:
return SECFailure;
}
/* This function doesn't really belong in this file.
** It's here to keep AIX compilers from optimizing it away,
** and not including it in the DSO.
*/
#include "nss.h"
extern const char __nss_ssl_version[];
PRBool
NSSSSL_VersionCheck(const char *importedVersion)
{
#define NSS_VERSION_VARIABLE __nss_ssl_version
#include "verref.h"
/*
* This is the secret handshake algorithm.
*
* This release has a simple version compatibility
* check algorithm. This release is not backward
* compatible with previous major releases. It is
* not compatible with future major, minor, or
* patch releases.
*/
return NSS_VersionCheck(importedVersion);
}
const char *
NSSSSL_GetVersion(void)
{
return NSS_VERSION;
}