0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-19 19:52:15 +02:00

Added HMAC and Packet ID integrity checks to reliable/SSL test.

This commit is contained in:
James Yonan 2011-12-05 06:11:51 +00:00
parent 16f2021000
commit ee46876142
6 changed files with 84 additions and 82 deletions

View File

@ -19,6 +19,8 @@ namespace openvpn {
OPENVPN_SIMPLE_EXCEPTION(buffer_overflow);
OPENVPN_SIMPLE_EXCEPTION(buffer_index);
OPENVPN_SIMPLE_EXCEPTION(buffer_const_index);
OPENVPN_SIMPLE_EXCEPTION(buffer_push_front_headroom);
template <typename T>
class BufferType {
@ -108,7 +110,7 @@ namespace openvpn {
void push_front(const T& value)
{
if (!offset_)
throw buffer_underflow();
throw buffer_push_front_headroom();
--offset_;
++size_;
*data() = value;

View File

@ -171,41 +171,40 @@ namespace openvpn {
return outlen;
}
// like hmac except out/out_size range must be inside of in/in_size range.
void hmac2_gen(unsigned char *out, const size_t out_size,
// like hmac except hmac_out/hmac_out_size range must be inside of in/in_size range.
void hmac2_gen(unsigned char *hmac_out, const size_t hmac_out_size,
const unsigned char *in, const size_t in_size)
{
unsigned int outlen;
HMAC_CTX* c = hmac2_pre(out, out_size, in, in_size);
HMAC_Final (c, out, &outlen);
HMAC_CTX* c = hmac2_pre(hmac_out, hmac_out_size, in, in_size);
HMAC_Final (c, hmac_out, &outlen);
}
// verify the HMAC generated by hmac2_gen, return true if verified
bool hmac2_cmp(const unsigned char *out, const size_t out_size,
bool hmac2_cmp(const unsigned char *hmac_in, const size_t hmac_in_size,
const unsigned char *in, const size_t in_size)
{
unsigned char local_hmac[MAX_HMAC_SIZE];
unsigned int outlen;
HMAC_CTX* c = hmac2_pre(out, out_size, in, in_size);
HMAC_CTX* c = hmac2_pre(hmac_in, hmac_in_size, in, in_size);
HMAC_Final (c, local_hmac, &outlen);
return !std::memcmp(out, local_hmac, out_size);
return !std::memcmp(hmac_in, local_hmac, hmac_in_size);
}
private:
HMAC_CTX* hmac2_pre(const unsigned char *out, const size_t out_size,
HMAC_CTX* hmac2_pre(const unsigned char *hmac, const size_t hmac_size,
const unsigned char *in, const size_t in_size)
{
const size_t pre_size = out - in;
const size_t post_size = in_size - pre_size - out_size;
const size_t pre_size = hmac - in;
const size_t post_size = in_size - pre_size - hmac_size;
if (pre_size > in_size || post_size > in_size)
throw digest_output_buffer();
HMAC_CTX *c = ctx();
const unsigned int hmac_size = HMAC_size(c);
if (out_size != hmac_size)
if (hmac_size != HMAC_size(c))
throw digest_output_buffer();
HMAC_Init_ex (c, NULL, 0, NULL, NULL);
HMAC_Update (c, in, int(pre_size));
HMAC_Update (c, out + out_size, int(post_size));
HMAC_Update (c, hmac + hmac_size, int(post_size));
return c;
}

View File

@ -48,12 +48,18 @@ namespace openvpn {
SHORT_FORM = 0, // short form of ID (4 bytes)
LONG_FORM = 1, // long form of ID (8 bytes)
UNDEF = 0 // special undefined/null id_t value
UNDEF = 0, // special undefined/null id_t value
SMALLEST_LEGAL_TIME = 256, // time value must be at least this value
};
id_t id; // legal values are 1 through 2^32-1
time_t time; // converted to PacketID::net_time_t before transmission
bool is_valid() const
{
return id != UNDEF && time >= SMALLEST_LEGAL_TIME;
}
void reset()
{
id = id_t(0);
@ -96,19 +102,6 @@ namespace openvpn {
}
}
static void prepend_id(Buffer& buf, const id_t id)
{
const id_t net_id = htonl(id);
buf.prepend ((unsigned char *)&net_id, sizeof (net_id));
}
static id_t read_id(Buffer& buf)
{
id_t net_id;
buf.read ((unsigned char *)&net_id, sizeof (net_id));
return ntohl(net_id);
}
#ifdef PACKET_ID_EXTRA_LOG_INFO
std::string str() const
{
@ -317,10 +310,10 @@ namespace openvpn {
if (last_reap_ + SEQ_REAP_PERIOD <= now)
reap(now);
// packet ID==0 is invalid
if (!pin.id)
// test for invalid packet ID
if (!pin.is_valid())
{
debug_log (DEBUG_LOW, pin, "ID is 0", 0, now);
debug_log (DEBUG_LOW, pin, "PID is invalid", 0, now);
return false;
}

View File

@ -25,15 +25,17 @@ namespace openvpn {
id_t front() const { return data.front(); }
void pop_front() { data.pop_front(); }
// called to read incoming ACKs from buf and mark them as ACKed in rel_send
// Called to read incoming ACK IDs from buf and mark them as ACKed in rel_send.
// If live is false, read the ACK IDs, but don't modify rel_send.
template <typename REL_SEND>
static void ack(REL_SEND& rel_send, Buffer& buf)
static void ack(REL_SEND& rel_send, Buffer& buf, bool live)
{
const size_t len = buf.pop_front();
for (size_t i = 0; i < len; ++i)
{
const id_t id = PacketID::read_id(buf);
rel_send.ack(id);
const id_t id = read_id(buf);
if (live)
rel_send.ack(id);
}
}
@ -43,7 +45,7 @@ namespace openvpn {
const size_t len = buf.pop_front();
for (size_t i = 0; i < len; ++i)
{
const id_t id = PacketID::read_id(buf);
const id_t id = read_id(buf);
data.push_back(id);
}
}
@ -54,12 +56,25 @@ namespace openvpn {
const size_t len = std::min(data.size(), max_ack_list_);
for (size_t i = len; i > 0; --i)
{
PacketID::prepend_id(buf, data[i-1]);
prepend_id(buf, data[i-1]);
}
buf.push_front((unsigned char)len);
data.erase (data.begin(), data.begin()+len);
}
static void prepend_id(Buffer& buf, const id_t id)
{
const id_t net_id = htonl(id);
buf.prepend ((unsigned char *)&net_id, sizeof (net_id));
}
static id_t read_id(Buffer& buf)
{
id_t net_id;
buf.read ((unsigned char *)&net_id, sizeof (net_id));
return ntohl(net_id);
}
private:
size_t max_ack_list_; // Maximum number of ACKs placed in a single message by prepend_acklist()
std::deque<id_t> data;

View File

@ -30,19 +30,22 @@ namespace openvpn {
}
// Call with unsequenced packet off of the wire.
// Will return true if ACK for this packet ID
// should be returned to sender.
bool receive(const PACKET& packet, const id_t id)
// Will return receive flags below.
enum {
ACK_TO_SENDER = (1<<0), // ACK for this packet should be returned to sender
IN_WINDOW = (1<<1) // Packet is in-window (otherwise, packet is dropped)
};
unsigned int receive(const PACKET& packet, const id_t id)
{
if (window_.in_window(id))
{
Message& m = window_.ref_by_id(id);
m.id_ = id;
m.packet = packet;
return true;
return ACK_TO_SENDER|IN_WINDOW;
}
else
return window_.pre_window(id);
return window_.pre_window(id) ? ACK_TO_SENDER : 0;
}
// Return true if next_sequenced() is ready to return next message

View File

@ -55,13 +55,13 @@ namespace openvpn {
const size_t max_ack_list)
: ssl_(ctx.ssl()),
frame_(frame),
rel_recv(span),
rel_send(span),
xmit_acks(max_ack_list),
up_stack_reentry_level(0),
invalidate(false),
ssl_started_(false),
next_retransmit_(Time::infinite())
next_retransmit_(Time::infinite()),
rel_recv(span),
rel_send(span),
xmit_acks(max_ack_list)
{
}
@ -123,7 +123,7 @@ namespace openvpn {
ack_send_buf.frame_prepare(*frame_, Frame::WRITE_ACK_STANDALONE);
// encapsulate standalone ACK
generate_ack(now, ack_send_buf, xmit_acks);
generate_ack(now, ack_send_buf);
// transmit it
net_send(now, ack_send_buf);
@ -160,26 +160,25 @@ namespace openvpn {
private:
// VIRTUAL METHODS -- derived class must define these virtual methods
// Encapsulate packet, use id as sequence number, xmit_acks as ACKs
// in reply to sender (if non-NULL), any exceptions thrown will
// invalidate session, i.e. this object can no longer be used.
virtual void encapsulate(const Time now, id_t id, PACKET& pkt, ReliableAck& xmit_acks) = 0;
// Encapsulate packet, use id as sequence number. If xmit_acks is non-empty,
// try to piggy-back ACK replies from xmit_acks to sender in encapsulated
// packet. Any exceptions thrown will invalidate session, i.e. this object
// can no longer be used.
virtual void encapsulate(const Time now, id_t id, PACKET& pkt) = 0;
// Un-encapsulate packet, method should return sequence number,
// or PacketID::UNDEF if packet should be dropped.
// Any ACKs received for messages previously sent should be marked in
// rel_send, which can be accomplished by calling ReliableAck::ack().
// Exceptions may be thrown here and they will be passed up to
// caller of net_recv and will not invalidate the session, however
// the packet will be dropped.
virtual id_t decapsulate(const Time now, PACKET& pkt, ReliableSend& rel_send) = 0;
// Perform integrity check on packet. If packet is good, unencapsulate it and
// pass it into the rel_recv object. Any ACKs received for messages previously
// sent should be marked in rel_send. Message sequence number should be recorded
// in xmit_acks. Exceptions may be thrown here and they will be passed up to
// caller of net_recv and will not invalidate the session.
virtual void decapsulate(const Time now, PACKET& pkt) = 0;
// Generate a standalone ACK message in buf (PACKET will be already be initialized
// by frame_prepare()).
virtual void generate_ack(const Time now, PACKET& pkt, ReliableAck& xmit_acks) = 0;
// Generate a standalone ACK message in buf based on ACKs in xmit_acks
// (PACKET will be already be initialized by frame_prepare()).
virtual void generate_ack(const Time now, PACKET& pkt) = 0;
// Transmit encapsulated ciphertext packet to peer. Method may not modify
// or take ownership of net_buf underlying data unless it copies it.
// or take ownership of net_pkt or underlying data unless it copies it.
virtual void net_send(const Time now, const PACKET& net_pkt) = 0;
// Pass cleartext data up to application. Method may take ownership
@ -191,7 +190,7 @@ namespace openvpn {
// if is_raw() method returns true. Method may take ownership
// of raw_pkt underlying data as long as it resets raw_pkt so that
// a subsequent call to PACKET::frame_prepare will revert it to
// a ready-to-read state.
// a ready-to-use state.
virtual void raw_recv(const Time now, PACKET& raw_pkt) = 0;
// END of VIRTUAL METHODS
@ -227,7 +226,7 @@ namespace openvpn {
// encapsulate packet
try {
encapsulate(now, m.id(), m.packet, xmit_acks);
encapsulate(now, m.id(), m.packet);
}
catch (...)
{
@ -252,7 +251,7 @@ namespace openvpn {
// encapsulate packet
try {
encapsulate(now, m.id(), m.packet, xmit_acks);
encapsulate(now, m.id(), m.packet);
}
catch (...)
{
@ -269,19 +268,7 @@ namespace openvpn {
void up_stack(const Time now, PACKET& recv)
{
UseCount use_count(up_stack_reentry_level);
// decapsulate packet
{
const id_t id = decapsulate(now, recv, rel_send);
if (id != PacketID::UNDEF)
{
const bool should_ack = rel_recv.receive(recv, id);
if (should_ack)
xmit_acks.push_back(id);
}
}
// continue to move packet up the stack
decapsulate(now, recv);
up_sequenced(now);
}
@ -346,11 +333,9 @@ namespace openvpn {
next_retransmit_ = now + d;
}
private:
typename SSLContext::SSLPtr ssl_;
FramePtr frame_;
ReliableRecv rel_recv;
ReliableSend rel_send;
ReliableAck xmit_acks;
int up_stack_reentry_level;
bool invalidate;
bool ssl_started_;
@ -359,6 +344,11 @@ namespace openvpn {
PACKET ack_send_buf; // only used for standalone ACKs to be sent to peer
std::deque<BufferPtr> app_write_queue;
std::deque<PACKET> raw_write_queue;
protected:
ReliableRecv rel_recv;
ReliableSend rel_send;
ReliableAck xmit_acks;
};
} // namespace openvpn