#pike __REAL_VERSION__ |
#pragma strict_types |
#require constant(SSL.Cipher) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "tls.h" |
|
import .Constants; |
|
private constant State = .State; |
private constant Session = .Session; |
private constant Context = .Context; |
private constant Buffer = .Buffer; |
|
Session session; |
Context context; |
|
array(State) pending_read_state = ({}); |
array(State) pending_write_state = ({}); |
|
|
|
int handshake_state; |
int previous_handshake; |
int reuse; |
|
constant CERT_none = 0; |
constant CERT_requested = 1; |
constant CERT_received = 2; |
constant CERT_no_certificate = 3; |
int certificate_state; |
|
int expect_change_cipher; |
|
|
|
int secure_renegotiation; |
string(8bit) client_verify_data = ""; |
string(8bit) server_verify_data = ""; |
|
|
|
|
.Cipher.KeyExchange ke; |
|
ProtocolVersion version; |
ProtocolVersion client_version; |
|
|
string(8bit) client_random; |
string(8bit) server_random; |
|
private constant Packet = .Packet; |
private constant Alert = .Alert; |
|
int(0..1) tickets_enabled = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
string(8bit) application_protocol; |
|
Alert alert(int(1..2) level, int(8bit) description, |
string|void message) |
{ |
return context->alert_factory(this, level, description, version, |
message); |
} |
|
Buffer get_signature_algorithms() |
{ |
Buffer sign_algs = Buffer(); |
foreach(context->get_signature_algorithms(), int signature_scheme) |
{ |
sign_algs->add_int(signature_scheme, 2); |
} |
return sign_algs; |
} |
|
#ifdef SSL3_PROFILING |
System.Timer timer = System.Timer(); |
float last_time = 0.0; |
void addRecord(int t,int s) { |
addRecord(sprintf("sender: %d type: %s", s, fmt_constant(t, "HANDSHAKE"))); |
} |
variant void addRecord(string label) { |
float stamp = timer->peek(); |
Stdio.stdout.write("time: %.6f (%.6f) %s\n", stamp, stamp-last_time, label); |
last_time = stamp; |
} |
#endif |
|
Buffer handshake_messages = Buffer(); |
protected void add_handshake_message(Buffer|Stdio.Buffer|string(8bit) data) |
{ |
handshake_messages->add(data); |
} |
|
Packet handshake_packet(int(8bit) type, |
string(8bit)|Buffer|object(Stdio.Buffer) data) |
{ |
#ifdef SSL3_PROFILING |
addRecord(type,1); |
#endif |
string(8bit) str = sprintf("%1c%3H", type, (string(8bit))data); |
add_handshake_message(str); |
|
|
return Packet(version, PACKET_handshake, str); |
} |
|
Packet change_cipher_packet() |
{ |
expect_change_cipher++; |
return Packet(version, PACKET_change_cipher_spec, "\001"); |
} |
|
string(8bit) hash_messages(string(8bit) sender, int|void len) |
{ |
switch( version ) |
{ |
case PROTOCOL_SSL_3_0: |
{ |
string(8bit) data = (string(8bit))handshake_messages + sender; |
return .Cipher.MACmd5(session->master_secret)->hash(data) + |
.Cipher.MACsha(session->master_secret)->hash(data); |
} |
case PROTOCOL_TLS_1_0: |
case PROTOCOL_TLS_1_1: |
return session->cipher_spec->prf(session->master_secret, sender, |
Crypto.MD5.hash(handshake_messages)+ |
Crypto.SHA1.hash(handshake_messages), |
len || 12); |
case PROTOCOL_TLS_1_2: |
default: |
return session->cipher_spec->prf(session->master_secret, sender, |
session->cipher_spec->hash |
->hash(handshake_messages), |
len || 12); |
} |
} |
|
Packet certificate_packet(array(string(8bit)) certificates) |
{ |
return handshake_packet(HANDSHAKE_certificate, |
Buffer()->add_string_array(certificates, 3, 3)); |
} |
|
Packet certificate_verify_packet(string(8bit)|void signature_context) |
{ |
SSL3_DEBUG_MSG("SSL.Connection: CERTIFICATE_VERIFY\n" |
"%O: handshake_messages: %d bytes.\n", |
this, sizeof(handshake_messages)); |
Buffer struct = Buffer(); |
|
if (signature_context) { |
|
session->cipher_spec->sign(session, |
signature_context + |
session->cipher_spec->hash |
->hash(handshake_messages), |
struct); |
} else { |
session->cipher_spec->sign(session, (string(8bit))handshake_messages, struct); |
} |
|
return handshake_packet(HANDSHAKE_certificate_verify, struct); |
} |
|
int(-1..0) validate_certificate_verify(Buffer input, |
string(8bit) signature_context) |
{ |
int(0..1) verification_ok; |
string(8bit) signed = (string(8bit))handshake_messages; |
if (version >= PROTOCOL_TLS_1_3) |
signed = signature_context + session->cipher_spec->hash->hash(signed); |
|
mixed err = catch { |
verification_ok = session->cipher_spec->verify( |
session, signed, input); |
}; |
#ifdef SSL3_DEBUG |
if (err) { |
master()->handle_error(err); |
} |
#endif |
err = UNDEFINED; |
COND_FATAL(!verification_ok, ALERT_unexpected_message, |
"Validation of CertificateVerify failed.\n"); |
return 0; |
} |
|
Packet heartbeat_packet(Buffer s) |
{ |
return Packet(version, PACKET_heartbeat, s->read()); |
} |
|
protected Crypto.AES heartbeat_encode; |
protected Crypto.AES heartbeat_decode; |
|
Packet heartbleed_packet() |
{ |
if (!heartbeat_encode) { |
|
|
|
|
|
|
heartbeat_encode = Crypto.AES(); |
heartbeat_decode = Crypto.AES(); |
string(8bit) heartbeat_key = random_string(16); |
heartbeat_encode->set_encrypt_key(heartbeat_key); |
heartbeat_decode->set_decrypt_key(heartbeat_key); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
Buffer hb_msg = Buffer(); |
hb_msg->add_int(HEARTBEAT_MESSAGE_request, 1); |
hb_msg->add_int(16, 2); |
int now = gethrtime(); |
hb_msg->add(heartbeat_encode->crypt(sprintf("%8c%8c", now, 0))); |
|
return heartbeat_packet(hb_msg); |
} |
|
|
private array(Standards.X509.TBSCertificate) |
verify_certificate_chain(array(string) certs) |
{ |
|
|
if((context->auth_level < AUTHLEVEL_require) && !sizeof(certs)) |
return ({}); |
|
|
|
if(context->auth_level == AUTHLEVEL_none) |
return ({ Standards.X509.decode_certificate(certs[0]) }); |
|
|
|
if(!sizeof(certs)) |
return 0; |
|
|
|
|
|
if(sizeof(context->authorities_cache)) |
{ |
string r=Standards.X509.decode_certificate(certs[-1])->issuer |
->get_der(); |
int issuer_known = 0; |
foreach(context->authorities_cache, string c) |
{ |
if(r == c) |
{ |
issuer_known = 1; |
break; |
} |
} |
|
if(issuer_known==0) |
{ |
return 0; |
} |
} |
|
|
|
mapping result = ([]); |
catch { |
result = Standards.X509.verify_certificate_chain(certs, |
context->trusted_issuers_cache, |
context->auth_level >= AUTHLEVEL_require, |
([ "verifier_algorithms" |
: context->verifier_algorithms ])); |
}; |
if( !result->verified ) return 0; |
|
if (session->server_name && sizeof([array](result->certificates || ({})))) { |
array(Standards.X509.TBSCertificate) certs = |
[array(Standards.X509.TBSCertificate)](result->certificates); |
Standards.X509.TBSCertificate cert = certs[-1]; |
array(string) globs = Standards.PKCS.Certificate. |
decode_distinguished_name(cert->subject)->commonName - ({ 0 }); |
if (cert->ext_subjectAltName_dNSName) { |
globs += cert->ext_subjectAltName_dNSName; |
} |
|
array(string) split_server_name = lower_case(session->server_name) / "."; |
|
result->verified = 0; |
|
OUTER: foreach (map(globs, lower_case);; string the_glob) { |
array(string) split_glob = the_glob / "."; |
|
if (sizeof(split_glob) != sizeof(split_server_name)) |
continue; |
|
foreach (split_glob; int i; string the_glob) { |
if (!glob(the_glob, split_server_name[i])) |
continue OUTER; |
} |
|
result->verified = 1; |
break; |
} |
} |
|
|
session->cert_data = result; |
|
if(result->verified) |
return [array(Standards.X509.TBSCertificate)]result->certificates; |
|
return 0; |
} |
|
|
|
|
|
|
int(0..1) handle_certificates(Buffer packet) |
{ |
|
|
Stdio.Buffer input = packet->read_hbuffer(3); |
array(string(8bit)) certs = ({ }); |
while(sizeof(input)) |
certs += ({ input->read_hstring(3) }); |
|
|
|
|
if(sizeof(packet)) |
{ |
send_packet(alert(ALERT_fatal, ALERT_unexpected_message, |
"Unknown additional data in packet.\n")); |
return 0; |
} |
|
|
array(Standards.X509.TBSCertificate) decoded = |
verify_certificate_chain(certs); |
if( !decoded ) |
{ |
send_packet(alert(ALERT_fatal, ALERT_bad_certificate, |
"Bad certificate chain.\n")); |
return 0; |
} |
if( !sizeof(certs) ) |
return 1; |
|
|
session->peer_certificate_chain = certs; |
|
session->peer_public_key = decoded[-1]->public_key->pkc; |
return 1; |
} |
|
|
void new_cipher_states(); |
|
|
|
void derive_master_secret(string(8bit) premaster_secret) |
{ |
SSL3_DEBUG_MSG("%O: derive_master_secret: %s (%s)\n", |
this, fmt_constant(handshake_state, "STATE"), |
fmt_version(version)); |
|
if (!sizeof(premaster_secret)) { |
|
session->master_secret = ""; |
} else if (session->extended_master_secret) { |
|
session->master_secret = premaster_secret; |
session->master_secret = hash_messages("extended master secret", 48); |
} else { |
session->master_secret = |
session->cipher_spec->prf(premaster_secret, "master secret", |
client_random + server_random, 48); |
} |
|
new_cipher_states(); |
} |
|
|
|
|
|
|
|
|
|
int(-1..1) handle_handshake(int type, Buffer input, Stdio.Buffer raw); |
|
|
|
|
|
protected void create(Context ctx) |
{ |
current_read_state = State(this); |
current_write_state = State(this); |
|
version = min([int]max(@ctx->supported_versions), PROTOCOL_TLS_1_2); |
context = ctx; |
} |
|
|
void shutdown() |
{ |
current_read_state = current_write_state = UNDEFINED; |
pending_read_state = pending_write_state = ({}); |
ke = UNDEFINED; |
alert_callback = UNDEFINED; |
} |
|
|
|
|
|
|
State current_read_state; |
State current_write_state; |
Stdio.Buffer read_buffer = Stdio.Buffer(); |
Packet packet; |
|
|
int sent; |
|
|
ConnectionState state = CONNECTION_handshaking; |
|
function(object,int|object,string:void) alert_callback; |
|
constant PRI_alert = 1; |
constant PRI_urgent = 2; |
constant PRI_application = 3; |
|
protected ADT.Queue alert_q = ADT.Queue(); |
protected ADT.Queue urgent_q = ADT.Queue(); |
protected ADT.Queue application_q = ADT.Queue(); |
|
|
string describe_state() |
{ |
if (!state) return "ready"; |
array(string) res = ({}); |
if (state & CONNECTION_handshaking) { |
res += ({ "handshaking(" + fmt_constant(handshake_state, "STATE") + ")" }); |
} |
if (state & CONNECTION_local_failing) { |
if (state & CONNECTION_local_fatal) { |
res += ({ "local_fatal" }); |
} else { |
res += ({ "local_failing" }); |
} |
} |
if (state & CONNECTION_local_closing) { |
if (state & CONNECTION_local_closed) { |
res += ({ "local_closed" }); |
} else { |
res += ({ "local_closing" }); |
} |
} |
if (state & CONNECTION_peer_fatal) res += ({ "peer_fatal" }); |
if (state & CONNECTION_peer_closed) res += ({ "peer_closed" }); |
return res * "|"; |
} |
|
protected string _sprintf(int t) |
{ |
if (t == 'O') return sprintf("SSL.Connection(%s)", describe_state()); |
} |
|
|
|
|
|
void set_alert_callback(function(object,int|object,string:void) callback) |
{ |
alert_callback = callback; |
} |
|
|
|
protected Packet recv_packet() |
{ |
if (!packet) |
packet = Packet(version, 2048); |
|
int res = packet->recv(read_buffer); |
|
switch(res) |
{ |
case 1: |
|
if (current_read_state) |
SSL3_DEBUG_MSG("SSL.Connection->recv_packet(): version=0x%x\n", |
version); |
return current_read_state->decrypt_packet(packet); |
case 0: |
SSL3_DEBUG_MSG("SSL.Connection->recv_packet(): current_read_state is zero!\n"); |
return 0; |
case -1: |
if( state & CONNECTION_handshaking ) |
{ |
|
|
|
|
|
SSL3_DEBUG_MSG("Initial incorrect data %O\n", |
((string)read_buffer[..25])); |
} |
return alert(ALERT_fatal, ALERT_unexpected_message); |
default: |
error("Internal error.\n"); |
} |
|
return 0; |
} |
|
|
|
|
void send_packet(Packet packet, int|void priority) |
{ |
if (state & (CONNECTION_local_closed | CONNECTION_local_failing)) { |
SSL3_DEBUG_MSG("send_packet: Ignoring packet after close/fail.\n"); |
return; |
} |
|
session->last_activity = time(1); |
|
if (packet->content_type == PACKET_alert) { |
if (packet->level == ALERT_fatal) { |
state = [int(0..0)|ConnectionState](state | CONNECTION_local_failing); |
} else if (packet->description == ALERT_close_notify) { |
state = [int(0..0)|ConnectionState](state | CONNECTION_local_closing); |
} |
} |
|
if (!priority) |
priority = ([ PACKET_alert : PRI_alert, |
PACKET_change_cipher_spec : PRI_urgent, |
PACKET_handshake : PRI_urgent, |
PACKET_heartbeat : PRI_urgent, |
PACKET_application_data : PRI_application |
])[packet->content_type]; |
|
if ((packet->content_type == PACKET_handshake) && |
(priority == PRI_application)) { |
|
|
expect_change_cipher = 0; |
certificate_state = 0; |
state = [int(0..0)|ConnectionState](state | CONNECTION_handshaking); |
handshake_state = STATE_wait_for_hello; |
previous_handshake = 0; |
} |
|
SSL3_DEBUG_MSG("SSL.Connection->send_packet: type %d, pri %d, %O\n", |
packet->content_type, priority, packet->fragment[..5]); |
switch (priority) |
{ |
default: |
error( "Internal error\n" ); |
case PRI_alert: |
alert_q->put(packet); |
break; |
case PRI_urgent: |
urgent_q->put(packet); |
break; |
case PRI_application: |
application_q->put(packet); |
break; |
} |
|
} |
|
|
|
|
|
|
int query_write_queue_size() |
{ |
return sizeof(alert_q) + sizeof(urgent_q) + sizeof(application_q); |
} |
|
|
|
|
|
|
|
|
|
int(-1..2) to_write(Stdio.Buffer output) |
{ |
if (state & CONNECTION_local_fatal) |
return -1; |
|
Packet packet = [object(Packet)](alert_q->get() || urgent_q->get() || |
application_q->get()); |
if (!packet) |
return !!(state & CONNECTION_local_closing); |
|
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) { |
state = [int(0..0)|ConnectionState](state | CONNECTION_local_fatal); |
current_read_state = UNDEFINED; |
pending_read_state = ({}); |
|
|
|
|
|
|
if (session) { |
context->purge_session(session); |
} |
} else if (packet->description == ALERT_close_notify) { |
state = [int(0..0)|ConnectionState](state | CONNECTION_local_closed); |
} |
} |
packet = current_write_state->encrypt_packet(packet, context); |
if (packet->content_type == PACKET_change_cipher_spec) { |
if (sizeof(pending_write_state)) { |
current_write_state = pending_write_state[0]; |
pending_write_state = pending_write_state[1..]; |
} else { |
error("Invalid Change Cipher Spec.\n"); |
} |
} |
|
packet->send(output); |
return 2; |
} |
|
|
void send_close() |
{ |
send_packet(alert(ALERT_warning, ALERT_close_notify, |
"Closing connection.\n"), PRI_application); |
} |
|
|
void send_renegotiate(); |
|
|
|
|
int send_streaming_data (string(8bit) data) |
{ |
int size = sizeof(data); |
if (!size) return 0; |
|
if ((!sent) && (version < PROTOCOL_TLS_1_1) && |
(session->cipher_spec->cipher_type == CIPHER_block) && |
(size>1)) |
{ |
|
|
|
|
send_packet(Packet(version, PACKET_application_data, data[..0])); |
data = data[1..]; |
} |
|
send_packet(Packet(version, PACKET_application_data, |
data[..session->max_packet_size-1]));; |
sent += size; |
return size; |
} |
|
|
|
|
|
|
|
|
|
|
protected int(-1..1) handle_alert(string s) |
{ |
|
int level = s[0]; |
int description = s[1]; |
COND_FATAL(!(ALERT_levels[level] && ALERT_descriptions[description]), |
ALERT_unexpected_message, "Invalid alert\n"); |
|
|
COND_FATAL((ALERT_deprecated[description] && |
ALERT_deprecated[description] < version), |
ALERT_unexpected_message, "Deprecated alert\n"); |
|
if (level == ALERT_fatal) |
{ |
SSL3_DEBUG_MSG("SSL.Connection: Fatal alert %O\n", |
ALERT_descriptions[description]); |
state = [int(0..0)|ConnectionState](state | CONNECTION_peer_fatal | |
CONNECTION_peer_closed); |
|
|
|
|
|
|
if (session) { |
context->purge_session(session); |
} |
return -1; |
} |
if (description == ALERT_close_notify) |
{ |
SSL3_DEBUG_MSG("SSL.Connection: %O\n", ALERT_descriptions[description]); |
state = [int(0..0)|ConnectionState](state | CONNECTION_peer_closed); |
return 1; |
} |
if (description == ALERT_no_certificate) |
{ |
SSL3_DEBUG_MSG("SSL.Connection: %O\n", ALERT_descriptions[description]); |
|
if ( (certificate_state == CERT_requested) && |
(context->auth_level == AUTHLEVEL_ask) ) |
{ |
certificate_state = CERT_no_certificate; |
return 0; |
} else { |
COND_FATAL(1, ((certificate_state == CERT_requested) |
? ALERT_handshake_failure |
: ALERT_unexpected_message), |
"Certificate required.\n"); |
} |
} |
#ifdef SSL3_DEBUG |
else |
werror("SSL.Connection: Received warning alert %O\n", |
ALERT_descriptions[description]); |
#endif |
return 0; |
} |
|
int(-1..0) handle_change_cipher(int c) |
{ |
COND_FATAL(!expect_change_cipher || (c != 1), |
ALERT_unexpected_message, "Unexpected change cipher!\n"); |
|
if (sizeof(pending_read_state)) { |
SSL3_DEBUG_MSG("%O: Changing read state.\n", this); |
current_read_state = pending_read_state[0]; |
pending_read_state = pending_read_state[1..]; |
} else { |
error("No new read state pending!\n"); |
} |
expect_change_cipher--; |
return 0; |
} |
|
void send_heartbeat() |
{ |
if ((state != CONNECTION_ready) || |
(session->heartbeat_mode != HEARTBEAT_MODE_peer_allowed_to_send)) { |
|
return; |
} |
|
Buffer hb_msg = Buffer(); |
hb_msg->add_int(HEARTBEAT_MESSAGE_request, 1); |
hb_msg->add_int(16, 2); |
int now = gethrtime(); |
hb_msg->add(heartbeat_encode->crypt(sprintf("%8c%8c", now, now))); |
|
int(0..) bytes = [int(0..)]max(0, 64 - sizeof(hb_msg)); |
hb_msg->add(random_string(bytes)); |
send_packet(heartbeat_packet(hb_msg)); |
} |
|
void handle_heartbeat(string(8bit) s) |
{ |
if (sizeof(s) < 19) return; |
Buffer hb_msg = Buffer(s); |
int hb_type = hb_msg->read_int(1); |
int hb_len = hb_msg->read_int(2); |
|
SSL3_DEBUG_MSG("SSL.Connection: Heartbeat %s (%d bytes)\n", |
fmt_constant(hb_type, "HEARTBEAT_MESSAGE"), hb_len); |
|
string(8bit) payload; |
int(0..) pad_len = 16; |
|
|
|
|
if ((hb_len < 0) || ((hb_len + 16) > sizeof(hb_msg))) { |
#ifdef SSL3_SIMULATE_HEARTBLEED |
payload = hb_msg->read(); |
if (sizeof(payload) < hb_len) { |
payload = payload + random_string(hb_len - sizeof(payload)); |
} else { |
payload = payload[..hb_len-1]; |
} |
#else |
return; |
#endif |
} else { |
payload = hb_msg->read(hb_len); |
pad_len = sizeof(hb_msg); |
} |
|
switch(hb_type) { |
case HEARTBEAT_MESSAGE_request: |
|
|
|
|
|
|
hb_msg = Buffer(); |
hb_msg->add_int(HEARTBEAT_MESSAGE_response, 1); |
hb_msg->add_int(hb_len, 2); |
hb_msg->add(payload); |
hb_msg->add(random_string(pad_len)); |
send_packet(heartbeat_packet(hb_msg)); |
break; |
case HEARTBEAT_MESSAGE_response: |
|
|
|
if ((sizeof(payload) == 16) && heartbeat_decode) { |
hb_msg = Buffer(heartbeat_decode->crypt(payload)); |
int a = hb_msg->read_int(8); |
int b = hb_msg->read_int(8); |
if (a != b) { |
if (!b) { |
|
send_packet(alert(ALERT_fatal, ALERT_insufficient_security, |
"Peer suffers from a bleeding heart.\n")); |
} |
break; |
} |
#ifdef SSL3_DEBUG |
int delta = gethrtime() - a; |
SSL3_DEBUG_MSG("SSL.Connection: Heartbeat roundtrip: %dus\n", delta); |
#endif |
} |
break; |
default: |
break; |
} |
} |
|
Stdio.Buffer handshake_buffer = Stdio.Buffer(); |
Stdio.Buffer alert_buffer = Stdio.Buffer(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string(8bit)|int(-1..1) got_data(string(8bit) data) |
{ |
if (state & (CONNECTION_peer_closed|CONNECTION_local_fatal)) { |
|
return 1; |
} |
|
|
|
|
session->last_activity = time(1); |
read_buffer->add(data); |
Stdio.Buffer.RewindKey read_buffer_key = read_buffer->rewind_key(); |
|
string(8bit) res = ""; |
Packet packet; |
while (packet = recv_packet()) |
{ |
if (packet->is_alert) |
{ |
|
|
|
|
SSL3_DEBUG_MSG("SSL.Connection: Bad received packet (%s)\n", |
fmt_constant([int]packet->description, "ALERT")); |
if (alert_callback) |
{ |
Stdio.Buffer.RewindKey here = read_buffer->rewind_key(); |
read_buffer_key->rewind(); |
alert_callback(packet, current_read_state->seq_num, |
(string)read_buffer); |
here->rewind(); |
} |
|
|
|
if (this && packet) |
send_packet(packet); |
|
return -1; |
} |
|
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"); |
|
COND_FATAL(!sizeof(packet->fragment), ALERT_unexpected_message, |
"Zero length Alert fragments not allowed.\n"); |
|
int(-1..1) err = 0; |
alert_buffer->add( packet->fragment ); |
while(!err && sizeof(alert_buffer)>1) |
err = handle_alert(alert_buffer->read(2)); |
|
if (err) |
{ |
if (err > 0 && sizeof (res)) |
{ |
|
|
return res; |
} |
return err; |
} |
break; |
} |
case PACKET_change_cipher_spec: |
{ |
SSL3_DEBUG_MSG("SSL.Connection: CHANGE_CIPHER_SPEC\n"); |
|
COND_FATAL(!sizeof(packet->fragment), ALERT_unexpected_message, |
"Zero length ChangeCipherSpec fragments not allowed.\n"); |
|
COND_FATAL(version >= PROTOCOL_TLS_1_3, ALERT_unexpected_message, |
"ChangeCipherSpec not allowed in TLS 1.3 and later.\n"); |
|
foreach(packet->fragment;; int c) |
{ |
int(-1..0) err = handle_change_cipher(c); |
SSL3_DEBUG_MSG("tried change_cipher: %d\n", err); |
if (err) |
return err; |
} |
break; |
} |
case PACKET_handshake: |
{ |
SSL3_DEBUG_MSG("SSL.Connection: HANDSHAKE\n"); |
|
COND_FATAL(!sizeof(packet->fragment), ALERT_unexpected_message, |
"Zero length Handshake fragments not allowed.\n"); |
|
|
|
|
|
COND_FATAL(!(state & CONNECTION_handshaking) && |
!secure_renegotiation, ALERT_no_renegotiation, |
"Renegotiation not supported in unsecure mode.\n"); |
|
COND_FATAL(!(state & CONNECTION_handshaking) && |
!context->enable_renegotiation, ALERT_no_renegotiation, |
"Renegotiation disabled by context.\n"); |
|
|
|
|
|
|
|
|
|
|
|
COND_FATAL(expect_change_cipher && (version < PROTOCOL_TLS_1_3), |
ALERT_unexpected_message, "Expected change cipher.\n"); |
|
int(-1..1) err; |
handshake_buffer->add( packet->fragment ); |
|
while (sizeof(handshake_buffer) >= 4) |
{ |
Stdio.Buffer.RewindKey key = handshake_buffer->rewind_key(); |
int type = handshake_buffer->read_int8(); |
Buffer input = Buffer(handshake_buffer->read_hbuffer(3)); |
if(!input) |
{ |
|
key->rewind(); |
break; |
} |
|
int len = 1+3+sizeof(input); |
key->rewind(); |
Stdio.Buffer raw = handshake_buffer->read_buffer(len); |
|
mixed exception = catch { |
err = handle_handshake(type, input, raw); |
COND_FATAL(err>=0 && sizeof(input), ALERT_record_overflow, |
sprintf("Extraneous handshake packet data (%O).\n", |
type)); |
}; |
if( exception ) |
{ |
if( objectp(exception) && ([object]exception)->buffer_error ) |
{ |
Error.Generic e = [object(Error.Generic)]exception; |
COND_FATAL(1, ALERT_decode_error, e->message()); |
} |
throw(exception); |
} |
if (err < 0) |
return err; |
if (err > 0) { |
state &= ~CONNECTION_handshaking; |
if ((version >= PROTOCOL_TLS_1_3) || expect_change_cipher) { |
|
COND_FATAL(sizeof(handshake_buffer), ALERT_unexpected_message, |
"Extraneous handshake packets.\n"); |
} |
COND_FATAL(sizeof(handshake_buffer) && !secure_renegotiation, |
ALERT_no_renegotiation, |
"Renegotiation not supported in unsecure mode.\n"); |
} |
} |
break; |
} |
case PACKET_application_data: |
SSL3_DEBUG_MSG("SSL.Connection: APPLICATION_DATA\n"); |
|
COND_FATAL(state & CONNECTION_handshaking, |
ALERT_unexpected_message, |
"Handshake not finished yet!\n"); |
|
res += packet->fragment; |
break; |
case PACKET_heartbeat: |
{ |
|
SSL3_DEBUG_MSG("SSL.Connection: Heartbeat.\n"); |
if (state != CONNECTION_ready) { |
|
|
|
break; |
} |
if (!session->heartbeat_mode) { |
|
|
|
|
|
send_packet(alert(ALERT_warning, ALERT_unexpected_message, |
"Heart beat mode not enabled.\n")); |
break; |
} |
|
mixed exception = catch { |
handle_heartbeat(packet->fragment); |
}; |
if( exception ) |
{ |
if( objectp(exception) && ([object]exception)->buffer_error ) |
{ |
Error.Generic e = [object(Error.Generic)]exception; |
COND_FATAL(1, ALERT_decode_error, e->message()); |
} |
throw(exception); |
} |
|
} |
break; |
default: |
COND_FATAL(state & CONNECTION_handshaking, |
ALERT_unexpected_message, |
"Unexpected message during handshake!\n"); |
|
|
|
|
SSL3_DEBUG_MSG("SSL.Connection: Ignoring packet of type %s\n", |
fmt_constant(packet->content_type, "PACKET")); |
break; |
} |
} |
|
if (sizeof(res)) return res; |
if (state & CONNECTION_peer_closed) return 1; |
return ""; |
} |
|
|