mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 04:02:15 +02:00
Added HMAC and Packet ID integrity checks to reliable/SSL test.
This commit is contained in:
parent
16f2021000
commit
ee46876142
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user