pike.git
/
lib
/
7.8
/
modules
/
SSL.pmod
/
connection.pike
version
»
Context lines:
10
20
40
80
file
none
3
pike.git/lib/7.8/modules/SSL.pmod/connection.pike:1:
+
#pike 7.8
+
//#pragma strict_types
-
+
//! SSL packet layer.
+
//!
+
//! @[SSL.connection] inherits @[SSL.handshake], and in addition to the state in
+
//! the handshake super class, it contains the current read and write
+
//! states, packet queues. This object is responsible for receiving and
+
//! sending packets, processing handshake packets, and providing a clear
+
//! text packages for some application.
+
+
// SSL/TLS Protocol Specification documents:
+
//
+
// SSL 2 http://wp.netscape.com/eng/security/SSL_2.html
+
// SSL 3.0 http://wp.netscape.com/eng/ssl3/draft302.txt
+
// (aka draft-freier-ssl-version3-02.txt).
+
// TLS 1.0 (SSL 3.1) RFC 2246 "The TLS Protocol Version 1.0".
+
// TLS 1.1 (SSL 3.2) draft-ietf-tls-rfc2246-bis
+
// Renegotiation RFC 5746 "Renegotiation Indication Extension".
+
+
#if constant(SSL.Cipher.CipherAlgorithm)
+
+
.state current_read_state;
+
.state current_write_state;
+
string left_over;
+
Packet packet;
+
+
int dying;
+
int closing; // Bitfield: 1 if a close is sent, 2 of one is received.
+
+
function(object,int|object,string:void) alert_callback;
+
+
import .Constants;
+
+
inherit .handshake;
+
+
#ifdef SSL3_DEBUG
+
#define SSL3_DEBUG_MSG(X ...) werror(X)
+
#else /*! SSL3_DEBUG */
+
#define SSL3_DEBUG_MSG(X ...)
+
#endif /* SSL3_DEBUG */
+
+
constant PRI_alert = 1;
+
constant PRI_urgent = 2;
+
constant PRI_application = 3;
+
+
inherit ADT.Queue : alert;
+
inherit ADT.Queue : urgent;
+
inherit ADT.Queue : application;
+
+
void create(int is_server, void|SSL.context ctx,
+
void|ProtocolVersion min_version,
+
void|ProtocolVersion max_version)
+
{
+
alert::create();
+
urgent::create();
+
application::create();
+
current_read_state = SSL.state(this);
+
current_write_state = SSL.state(this);
+
handshake::create(is_server, ctx, min_version, max_version);
+
}
+
+
//! Called with alert object, sequence number of bad packet,
+
//! and raw data as arguments, if a bad packet is received.
+
//!
+
//! Can be used to support a fallback redirect https->http.
+
void set_alert_callback(function(object,int|object,string:void) callback)
+
{
+
alert_callback = callback;
+
}
+
+
//! Low-level receive handler. Returns a packet, an alert, or zero if
+
//! more data is needed to get a complete packet.
+
protected object recv_packet(string data)
+
{
+
mixed res;
+
+
// SSL3_DEBUG_MSG("SSL.connection->recv_packet(%O)\n", data);
+
if (left_over || !packet)
+
{
+
packet = Packet(2048);
+
res = packet->recv( (left_over || "") + data, version[1]);
+
}
+
else
+
res = packet->recv(data, version[1]);
+
+
if (stringp(res))
+
{ /* Finished a packet */
+
left_over = [string]res;
+
if (current_read_state) {
+
SSL3_DEBUG_MSG("Decrypting packet.. version[1]="+version[1]+"\n");
+
return current_read_state->decrypt_packet(packet,version[1]);
+
} else {
+
SSL3_DEBUG_MSG("SSL.connection->recv_packet(): current_read_state is zero!\n");
+
return 0;
+
}
+
}
+
else /* Partial packet read, or error */
+
left_over = 0;
+
return res;
+
}
+
+
//! Queues a packet for write. Handshake and and change cipher
+
//! must use the same priority, so must application data and
+
//! close_notifies.
+
void send_packet(object packet, int|void priority)
+
{
+
if (closing & 1) {
+
SSL3_DEBUG_MSG("SSL.connection->send_packet: ignoring packet after close\n");
+
return;
+
}
+
+
if (packet->content_type == PACKET_alert &&
+
packet->description == ALERT_close_notify)
+
closing |= 1;
+
+
#ifdef SSL3_FRAGDEBUG
+
werror(" SSL.connection->send_packet: sizeof(packet)="+sizeof(packet)+"\n");
+
#endif
+
if (!priority)
+
priority = ([ PACKET_alert : PRI_alert,
+
PACKET_change_cipher_spec : PRI_urgent,
+
PACKET_handshake : PRI_urgent,
+
PACKET_application_data : PRI_application ])[packet->content_type];
+
SSL3_DEBUG_MSG("SSL.connection->send_packet: type %d, desc %d, pri %d, %O\n",
+
packet->content_type, packet->description, priority,
+
packet->fragment[..5]);
+
switch (priority)
+
{
+
default:
+
error( "Internal error\n" );
+
case PRI_alert:
+
alert::put(packet);
+
break;
+
case PRI_urgent:
+
urgent::put(packet);
+
break;
+
case PRI_application:
+
application::put(packet);
+
break;
+
}
+
+
}
+
+
//! Extracts data from the packet queues. Returns a string of data
+
//! to be written, "" if there are no pending packets, 1 of the
+
//! connection is being closed politely, and -1 if the connection
+
//! died unexpectedly.
+
//!
+
//! This function is intended to be called from an i/o write callback.
+
string|int to_write()
+
{
+
if (dying)
+
return -1;
+
+
object packet = alert::get() || urgent::get() || application::get();
+
if (!packet) {
+
return closing ? 1 : "";
+
}
+
+
SSL3_DEBUG_MSG("SSL.connection: writing packet of type %d, %O\n",
+
packet->content_type, packet->fragment[..6]);
+
if (packet->content_type == PACKET_alert)
+
{
+
if (packet->level == ALERT_fatal)
+
dying = 1;
+
}
+
string res = current_write_state->encrypt_packet(packet,version[1])->send();
+
if (packet->content_type == PACKET_change_cipher_spec)
+
current_write_state = pending_write_state;
+
return res;
+
}
+
+
//! Initiate close.
+
void send_close()
+
{
+
send_packet(Alert(ALERT_warning, ALERT_close_notify, version[1]),
+
PRI_application,);
+
}
+
+
//! Send an application data packet. If the data block is too large
+
//! then as much as possible of the beginning of it is sent. The size
+
//! of the sent data is returned.
+
int send_streaming_data (string data)
+
{
+
Packet packet = Packet();
+
packet->content_type = PACKET_application_data;
+
int size = sizeof ((packet->fragment = data[..PACKET_MAX_SIZE-1]));
+
send_packet (packet);
+
return size;
+
}
+
+
int handle_alert(string s)
+
{
+
int level = s[0];
+
int description = s[1];
+
if (! (ALERT_levels[level] && ALERT_descriptions[description]))
+
{
+
send_packet(Alert(ALERT_fatal, ALERT_unexpected_message, version[1],
+
"SSL.connection->handle_alert: invalid alert\n", backtrace()));
+
return -1;
+
}
+
if (level == ALERT_fatal)
+
{
+
SSL3_DEBUG_MSG("SSL.connection: Fatal alert %d\n", description);
+
return -1;
+
}
+
if (description == ALERT_close_notify)
+
{
+
SSL3_DEBUG_MSG("SSL.connection: Close notify alert %d\n", description);
+
closing |= 2;
+
return 1;
+
}
+
if (description == ALERT_no_certificate)
+
{
+
SSL3_DEBUG_MSG("SSL.connection: No certificate alert %d\n", description);
+
+
if ((certificate_state == CERT_requested) && (context->auth_level == AUTHLEVEL_ask))
+
{
+
certificate_state = CERT_no_certificate;
+
return 0;
+
} else {
+
send_packet(Alert(ALERT_fatal, ((certificate_state == CERT_requested)
+
? ALERT_handshake_failure
+
: ALERT_unexpected_message), version[1]));
+
return -1;
+
}
+
}
+
#ifdef SSL3_DEBUG
+
else
+
werror("SSL.connection: Received warning alert %d\n", description);
+
#endif
+
return 0;
+
}
+
+
int handle_change_cipher(int c)
+
{
+
if (!expect_change_cipher || (c != 1))
+
{
+
SSL3_DEBUG_MSG("SSL.connection: handle_change_cipher: Unexcepted message!");
+
send_packet(Alert(ALERT_fatal, ALERT_unexpected_message, version[1]));
+
return -1;
+
}
+
else
+
{
+
current_read_state = pending_read_state;
+
expect_change_cipher = 0;
+
return 0;
+
}
+
}
+
+
string alert_buffer = "";
+
string handshake_buffer = "";
+
+
//! Main receive handler. Returns a string of received application
+
//! data, or 1 if a close was received, or -1 if an error occurred.
+
//!
+
//! This function is intended to be called from an i/o read callback.
+
string|int got_data(string|int s)
+
{
+
if(!stringp(s)) {
+
return s;
+
}
+
+
if (closing & 2) {
+
return 1;
+
}
+
// If closing == 1 we continue to try to read a remote close
+
// message. That enables the caller to check for a clean close, and
+
// to get the leftovers after the SSL connection.
+
+
/* If alert_callback is called, this data is passed as an argument */
+
string alert_context = (left_over || "") + s;
+
+
string res = "";
+
object packet;
+
while (packet = recv_packet(s))
+
{
+
s = "";
+
+
if (packet->is_alert)
+
{ /* Reply alert */
+
SSL3_DEBUG_MSG("SSL.connection: Bad received packet\n");
+
send_packet(packet);
+
if (alert_callback)
+
alert_callback(packet, current_read_state->seq_num, alert_context);
+
if ((!packet) || (!this) || (packet->level == ALERT_fatal))
+
return -1;
+
if (alert_callback)
+
break;
+
}
+
else
+
{
+
SSL3_DEBUG_MSG("SSL.connection: received packet of type %d\n",
+
packet->content_type);
+
switch (packet->content_type)
+
{
+
case PACKET_alert:
+
{
+
SSL3_DEBUG_MSG("SSL.connection: ALERT\n");
+
+
int i;
+
int err = 0;
+
alert_buffer += packet->fragment;
+
for (i = 0;
+
!err && ((sizeof(alert_buffer) - i) >= 2);
+
i+= 2)
+
err = handle_alert(alert_buffer[i..i+1]);
+
+
alert_buffer = alert_buffer[i..];
+
if (err)
+
if (err > 0 && sizeof (res))
+
// If we get a close then we return the data we got so far.
+
return res;
+
else
+
return err;
+
break;
+
}
+
case PACKET_change_cipher_spec:
+
{
+
SSL3_DEBUG_MSG("SSL.connection: CHANGE_CIPHER_SPEC\n");
+
+
int i;
+
int err;
+
for (i = 0; (i < sizeof(packet->fragment)); i++)
+
{
+
err = handle_change_cipher(packet->fragment[i]);
+
SSL3_DEBUG_MSG("tried change_cipher: %d\n", err);
+
if (err)
+
return err;
+
}
+
break;
+
}
+
case PACKET_handshake:
+
{
+
SSL3_DEBUG_MSG("SSL.connection: HANDSHAKE\n");
+
+
if (handshake_finished && !secure_renegotiation) {
+
// Don't allow renegotiation in unsecure mode, to address
+
// http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2009-3555.
+
// For details see: http://www.g-sec.lu/practicaltls.pdf and
+
// RFC 5746.
+
send_packet (Alert (ALERT_warning, ALERT_no_renegotiation,
+
version[1]));
+
return -1;
+
}
+
if (expect_change_cipher)
+
{
+
/* No change_cipher message was received */
+
// FIXME: There's a bug somewhere since expect_change_cipher often
+
// remains set after the handshake is completed. The effect is that
+
// renegotiation doesn't work all the time.
+
//
+
// A side effect is that we are partly invulnerable to the
+
// renegotiation vulnerability mentioned above. It is however not
+
// safe to assume that, since there might be routes past this,
+
// maybe through the use of a version 2 hello message below.
+
send_packet(Alert(ALERT_fatal, ALERT_unexpected_message,
+
version[1]));
+
return -1;
+
}
+
int err, len;
+
handshake_buffer += packet->fragment;
+
+
while (sizeof(handshake_buffer) >= 4)
+
{
+
sscanf(handshake_buffer, "%*c%3c", len);
+
if (sizeof(handshake_buffer) < (len + 4))
+
break;
+
err = handle_handshake(handshake_buffer[0],
+
handshake_buffer[4..len + 3],
+
handshake_buffer[.. len + 3]);
+
handshake_buffer = handshake_buffer[len + 4..];
+
if (err < 0)
+
return err;
+
if (err > 0) {
+
handshake_finished = 1;
+
}
+
}
+
break;
+
}
+
case PACKET_application_data:
+
SSL3_DEBUG_MSG("SSL.connection: APPLICATION_DATA\n");
+
+
if (!handshake_finished)
+
{
+
send_packet(Alert(ALERT_fatal, ALERT_unexpected_message, version[1]));
+
return -1;
+
}
+
res += packet->fragment;
+
break;
+
case PACKET_V2:
+
{
+
SSL3_DEBUG_MSG("SSL.connection: V2\n");
+
+
if (handshake_finished) {
+
// Don't allow renegotiation using SSLv2 packets at all, to address
+
// http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2009-3555.
+
send_packet (Alert (ALERT_warning, ALERT_no_renegotiation,
+
version[1]));
+
return -1;
+
}
+
int err = handle_handshake(HANDSHAKE_hello_v2,
+
packet->fragment[1 .. ],
+
packet->fragment);
+
// FIXME: Can err ever be 1 here? In that case we're probably
+
// not returning the right value below.
+
if (err)
+
return err;
+
}
+
}
+
}
+
}
+
return closing & 2 ? 1 : res;
+
}
+
+
#else // constant(SSL.Cipher.CipherAlgorithm)
+
constant this_program_does_not_exist = 1;
+
#endif
Newline at end of file added.