cog/Frameworks/JNetLib/jnetlib/sslconnection.cpp

325 lines
6.0 KiB
C++
Executable File

#include "netinc.h"
#include "util.h"
#include "connection.h"
#include "sslconnection.h"
#ifdef _JNETLIB_SSL_
SSL_CTX *sslContext = 0;
#ifdef _DEBUG
extern "C" void apps_ssl_info_callback (const SSL * s, int where, int ret)
{
/*
** DEBUG INFO HERE
*/
}
#endif
/*
** Well, you should probably change this based on like...
** well, you're level of trust heh
** For now, this basically trusts all certs :)
**
*/
extern "C" int verify_callback(int ok, X509_STORE_CTX * ctx)
{
/* For certificate verification */
int verify_depth = 0;
int verify_error = X509_V_OK;
char buf[1024];
X509 * err_cert = X509_STORE_CTX_get_current_cert(ctx);
int err = X509_STORE_CTX_get_error(ctx);
int depth = X509_STORE_CTX_get_error_depth(ctx);
X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
if (!ok)
{
if (verify_depth >= depth)
{
ok = 1;
verify_error = X509_V_OK;
}
else
{
ok = 0;
verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
}
}
switch (ctx->error)
{
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, sizeof(buf));
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
break;
}
return ok;
}
JNL_SSL_Connection::JNL_SSL_Connection(SSL* pssl, JNL_AsyncDNS *dns, int sendbufsize, int recvbufsize) : JNL_Connection(dns, sendbufsize, recvbufsize),
forceConnect(false)
{
m_ssl = pssl;
m_bsslinit = false;
if (m_ssl)
{
m_bcontextowned = false;
}
else
{
m_bcontextowned = true;
}
if (m_bcontextowned == false)
{
return ;
}
m_bsslinit = true;
/* See the SSL states in our own callback */
#ifdef _DEBUG
// SSL_CTX_set_info_callback(m_app_ctx, apps_ssl_info_callback);
#endif
/* Set the certificate verification callback */
//SSL_CTX_set_verify(sslContext, SSL_VERIFY_PEER, verify_callback);
/* Not sure what this does */
//SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_CLIENT);
if(sslContext == NULL)
{
SSL_METHOD * meth = NULL;
SSLeay_add_ssl_algorithms();
SSL_load_error_strings();
meth = SSLv23_client_method();
if ((sslContext = SSL_CTX_new(meth)) == NULL)
{
return;
}
}
return ;
}
int JNL_SSL_Connection::socket_connect()
{
int retval;
if (m_bcontextowned == false)
{
/*
** WTF?
*/
return -1;
}
if (m_ssl != NULL)
{
return -1;
}
retval = JNL_Connection::socket_connect();
if (retval != 0)
{
if (ERRNO != EINPROGRESS)
{
return retval; // benski> if the underlying socket hasn't connected yet, then we can't start the SSL connection
/*
** Joshua Teitelbaum 3/2/2006
** Fatal error here
*/
}
}
m_ssl = SSL_new(sslContext);
if (m_ssl == NULL)
{
return -1;
}
/* Tell that we are in connect mode */
SSL_set_connect_state(m_ssl);
/* Set socket descriptor with the socket we already have open */
if(SSL_set_fd(m_ssl, m_socket) != 0)
{
return -1;
}
return retval;
}
void JNL_SSL_Connection::socket_shutdown()
{
if (m_ssl)
SSL_shutdown(m_ssl);
JNL_Connection::socket_shutdown();
if (m_ssl)
{
SSL_free(m_ssl);
m_ssl = NULL;
}
return ;
}
void JNL_SSL_Connection::run(int max_send_bytes, int max_recv_bytes, int *bytes_sent, int *bytes_rcvd)
{
if (!m_bsslinit)
{
int rval = 0;
int e = 0;
rval = SSL_accept(m_ssl);
if (rval == -1)
{
e = SSL_get_error(m_ssl, rval);
if (!((e == SSL_ERROR_WANT_READ) || (e == SSL_ERROR_WANT_WRITE)))
{
m_state = STATE_ERROR;
}
return ;
}
else
{
m_bsslinit = true;
}
}
/**
** benski - march 2, 2006
**if the underlying socket didn't connected yet, we need to try the SSL connection again
*/
if (forceConnect)
{
if(init_ssl_connection() == false)
{
return;
}
}
JNL_Connection::run(max_send_bytes, max_recv_bytes, bytes_sent, bytes_rcvd);
}
/*
** init_ssl_connection:
** Returns true, meaning can continue
** Else, cannot continue with underlying run
** side effects:
** sets forceConnect
*/
bool JNL_SSL_Connection::init_ssl_connection()
{
if(m_ssl == NULL)
{
/*
** WTF?
** cascade up.
*/
return true;
}
int retval = SSL_connect(m_ssl);
if (retval < 0)
{
int err = SSL_get_error(m_ssl, retval);
switch (err)
{
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_CONNECT:
forceConnect = true;
break;
// fall through
default: // TODO: benski> MOST other errors are OK, (especially "want read" and "want write", but we need to think through all the scenarios here
forceConnect=false;
}
}
else if(retval)
{
/*
** success
*/
forceConnect = false;
}
/*
** If retval == 0
** socket is closed, or serious error occurred.
** cascade up.
*/
return forceConnect==false;
}
void JNL_SSL_Connection::on_socket_connected(void)
{
init_ssl_connection();
}
void JNL_SSL_Connection::connect(int s, struct sockaddr_in *loc)
{
/*
** Joshua Teitelbaum 2/01/2006
** No need to close
** This is the reason for divergence as well as setting
** the connect state
*/
m_socket = s;
m_remote_port = 0;
m_dns = NULL;
if (loc) *m_saddr = *loc;
else memset(m_saddr, 0, sizeof(m_saddr));
if (m_socket != -1)
{
SET_SOCK_BLOCK(m_socket, 0);
m_state = STATE_CONNECTED;
SSL_set_fd(m_ssl, m_socket);
}
else
{
m_errorstr = "invalid socket passed to connect";
m_state = STATE_ERROR;
}
}
int JNL_SSL_Connection::socket_recv(char *buf, int len, int options)
{
return SSL_read(m_ssl, buf, len);
}
int JNL_SSL_Connection::socket_send(char *buf, int len, int options)
{
return SSL_write(m_ssl, buf, len);
}
JNL_SSL_Connection::~JNL_SSL_Connection()
{
if (m_ssl)
{
SSL_free(m_ssl);
m_ssl = NULL;
}
}
#endif