-
Henrik (Grubba) Grubbström authored
Rev: lib/modules/SSL.pmod/connection.pike:1.8
Henrik (Grubba) Grubbström authoredRev: lib/modules/SSL.pmod/connection.pike:1.8
connection.pike 7.36 KiB
/* $Id: connection.pike,v 1.8 1998/05/20 23:13:52 grubba Exp $
*
* SSL packet layer
*/
object current_read_state;
object current_write_state;
string left_over;
object packet;
int dying;
int closing;
function(object,int|object,string:void) alert_callback;
inherit "constants";
inherit "handshake";
constant Queue = ADT.queue;
constant State = SSL.state;
constant PRI_alert = 1;
constant PRI_urgent = 2;
constant PRI_application = 3;
inherit Queue : alert;
inherit Queue : urgent;
inherit Queue : application;
void create(int is_server)
{
handshake::create(is_server);
alert::create();
urgent::create();
application::create();
current_read_state = State(this_object());
current_write_state = State(this_object());
}
/* 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;
}
object recv_packet(string data)
{
mixed res;
#ifdef SSL3_DEBUG
// werror(sprintf("SSL.connection->recv_packet('%s')\n", data));
#endif
if (left_over || !packet)
{
packet = Packet(2048);
res = packet->recv( (left_over || "") + data);
}
else
res = packet->recv(data);
if (stringp(res))
{ /* Finished a packet */
left_over = res;
if (current_read_state) {
return current_read_state->decrypt_packet(packet);
} else {
#ifdef SSL3_DEBUG
werror(sprintf("SSL.connection->recv_packet(): current_read_state is zero!\n"));
#endif /* SSL3_DEBUG */
return 0;
}
}
else /* Partial packet read, or error */
left_over = 0;
return res;
}
/* 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 (!priority)
priority = ([ PACKET_alert : PRI_alert,
PACKET_change_cipher_spec : PRI_urgent,
PACKET_handshake : PRI_urgent,
PACKET_application_data : PRI_application ])[packet->content_type];
#ifdef SSL3_DEBUG
werror(sprintf("SSL.connection->send_packet: type %d, %d, '%s'\n",
packet->content_type, priority, packet->fragment[..5]));
#endif
switch (priority)
{
default:
throw( ({"SSL.connection->send_packet: internal error\n", backtrace() }) );
case PRI_alert:
alert::put(packet);
break;
case PRI_urgent:
urgent::put(packet);
break;
case PRI_application:
application::put(packet);
break;
}
}
/* Returns a string of data to be written, "" if there's no pending packets,
* 1 if the connection is being closed politely, and -1 if the connection
* died unexpectedly. */
string|int to_write()
{
if (dying)
return -1;
if (closing)
return 1;
object packet = alert::get() || urgent::get() || application::get();
if (!packet)
return "";
#ifdef SSL3_DEBUG
werror(sprintf("SSL.connection: writing packet of type %d, '%s'\n",
packet->content_type, packet->fragment[..6]));
#endif
if (packet->content_type == PACKET_alert)
{
if (packet->level == ALERT_fatal)
dying = 1;
else
if (packet->description == ALERT_close_notify)
closing = 1;
}
string res = current_write_state->encrypt_packet(packet)->send();
if (packet->content_type == PACKET_change_cipher_spec)
current_write_state = pending_write_state;
return res;
}
void send_close()
{
send_packet(Alert(ALERT_warning, ALERT_close_notify), PRI_application);
}
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,
"SSL.connection->handle_alert: invalid alert\n", backtrace()));
return -1;
}
if (level == ALERT_fatal)
{
#ifdef SSL3_DEBUG
werror(sprintf("SSL.connection: Fatal alert %d\n", description));
#endif
return -1;
}
if (description == ALERT_close_notify)
{
return 1;
}
if (description == ALERT_no_certificate)
{
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)));
return -1;
}
}
else
werror(sprintf("SSL.connection: Received warning alert %d\n", description));
return 0;
}
int handle_change_cipher(int c)
{
if (!expect_change_cipher || (c != 1))
{
send_packet(Alert(ALERT_fatal, ALERT_unexpected_message));
return -1;
}
else
{
current_read_state = pending_read_state;
expect_change_cipher = 0;
return 0;
}
}
string alert_buffer = "";
string handshake_buffer = "";
int handshake_finished = 0;
/* Returns a string of application data, 1 if connection was closed, or
* -1 if a fatal error occured */
string|int got_data(string s)
{
/* 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 */
#ifdef SSL3_DEBUG
werror("SSL.connection: Bad received packet\n");
#endif
send_packet(packet);
if (alert_callback)
alert_callback(packet, current_read_state->seq_num, alert_context);
if ((!packet) || (!this_object()) || (packet->level == ALERT_fatal))
return -1;
}
else
{
#ifdef SSL3_DEBUG
werror(sprintf("SSL.connection: received packet of type %d\n",
packet->content_type));
#endif
switch (packet->content_type)
{
case PACKET_alert:
{
int i;
mixed err = 0;
alert_buffer += packet->fragment;
for (i = 0;
!err && ((strlen(alert_buffer) - i) >= 2);
i+= 2)
err = handle_alert(alert_buffer[i..i+1]);
alert_buffer = alert_buffer[i..];
if (err)
return err;
break;
}
case PACKET_change_cipher_spec:
{
int i;
int err;
for (i = 0; (i < strlen(packet->fragment)); i++)
{
err = handle_change_cipher(packet->fragment[i]);
#ifdef SSL3_DEBUG
werror(sprintf("tried change_cipher: %d\n", err));
#endif
if (err)
return err;
}
break;
}
case PACKET_handshake:
{
if (expect_change_cipher)
{
/* No change_cipher message was recieved */
send_packet(Alert(ALERT_fatal, ALERT_unexpected_message));
return -1;
}
mixed err;
int len;
handshake_buffer += packet->fragment;
while (strlen(handshake_buffer) >= 4)
{
sscanf(handshake_buffer, "%*c%3c", len);
if (strlen(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:
if (!handshake_finished)
{
send_packet(Alert(ALERT_fatal, ALERT_unexpected_message));
return -1;
}
res += packet->fragment;
break;
case PACKET_V2:
{
mixed err = handle_handshake(HANDSHAKE_hello_v2,
packet->fragment[1 .. ],
packet->fragment);
if (err)
return err;
}
}
}
}
return res;
}
void server()
{
handshake_state = STATE_server_wait_for_hello;
}