OpenVPN 3 Core Library
Loading...
Searching...
No Matches
protostack.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12#ifndef OPENVPN_SSL_PROTOSTACK_H
13#define OPENVPN_SSL_PROTOSTACK_H
14
15#include <deque>
16#include <utility>
17
22#include <openvpn/time/time.hpp>
31
32// ProtoStackBase is designed to allow general-purpose protocols (including
33// but not limited to OpenVPN) to run over SSL, where the underlying transport
34// layer is unreliable, such as UDP. The OpenVPN protocol implementation in
35// proto.hpp (ProtoContext) layers on top of ProtoStackBase.
36// ProtoStackBase is independent of any particular SSL implementation, and
37// accepts the SSL object type as a template parameter.
38
39namespace openvpn {
40
41// PACKET type must define the following methods:
42//
43// Default constructor:
44// PACKET()
45//
46// Constructor for BufferPtr:
47// explicit PACKET(const BufferPtr& buf)
48//
49// Return cloned packet, including cloned buffer content:
50// PACKET clone() const
51//
52// Test if defined:
53// operator bool() const
54//
55// Return false if packet is raw, or true if packet is SSL ciphertext:
56// bool contains_tls_ciphertext() const
57//
58// Reset back to post-default-constructor state:
59// void reset()
60//
61// Return internal BufferPtr:
62// const BufferPtr& buffer_ptr() const
63//
64// Call frame.prepare on internal buffer:
65// void frame_prepare(const Frame& frame, const unsigned int context)
66
67template <typename PACKET, typename PARENT>
69{
70 public:
71 static constexpr size_t ovpn_sending_window = 6;
73
77
78 OPENVPN_SIMPLE_EXCEPTION(proto_stack_invalidated);
79 OPENVPN_SIMPLE_EXCEPTION(unknown_status_from_ssl_layer);
80
88
89 ProtoStackBase(SSLFactoryAPI &ssl_factory, // SSL factory object that can be used to generate new SSL sessions
90 TimePtr now_arg, // pointer to current time
91 const Time::Duration &tls_timeout_arg, // packet retransmit timeout
92 const Frame::Ptr &frame, // contains info on how to allocate and align buffers
93 const SessionStats::Ptr &stats_arg, // error statistics
94 bool psid_cookie_mode) // start the reliability layer at packet id 1, not 0
95
96 : tls_timeout(tls_timeout_arg),
97 ssl_(ssl_factory.ssl()),
98 frame_(frame),
99 stats(stats_arg),
100 now(now_arg),
101 rel_recv(ovpn_receiving_window, psid_cookie_mode ? 1 : 0),
102 rel_send(ovpn_sending_window, psid_cookie_mode ? 1 : 0)
103 {
104 }
105
106 // Start SSL handshake on underlying SSL connection object.
108 {
109 if (!invalidated())
110 {
112 ssl_started_ = true;
113 up_sequenced();
114 }
115 }
116
117 uint32_t get_tls_warnings() const
118 {
119 return ssl_->get_tls_warnings();
120 }
121
122 // Incoming ciphertext packet arriving from network,
123 // we will take ownership of pkt.
124 bool net_recv(PACKET &&pkt)
125 {
126 if (!invalidated())
127 return up_stack(pkt);
128 return false;
129 }
130
131 // Outgoing application-level cleartext packet ready to send
132 // (will be encrypted via SSL), we will take ownership
133 // of buf.
134 void app_send(BufferPtr &&buf)
135 {
136 if (!invalidated())
137 app_write_queue.push_back(std::move(buf));
138 }
139
140 // Outgoing raw packet ready to send (will NOT be encrypted
141 // via SSL, but will still be encapsulated, sequentialized,
142 // and tracked via reliability layer).
143 void raw_send(PACKET &&pkt)
144 {
145 if (!invalidated())
146 raw_write_queue.push_back(std::move(pkt));
147 }
148
149 // Write any pending data to network and update retransmit
150 // timer. Should be called as a final step after one or more
151 // net_recv, app_send, raw_send, or start_handshake calls.
152 void flush()
153 {
155 {
159 }
160 }
161
162 // Send pending ACKs back to sender for packets already received
164 {
165 if (!invalidated())
166 {
167 while (!xmit_acks.empty())
168 {
170
171 // encapsulate standalone ACK
172 parent().generate_ack(ack_send_buf);
173
174 // transmit it
175 parent().net_send(ack_send_buf, NET_SEND_ACK);
176 }
177 }
178 }
179
180 // Send any pending retransmissions
182 {
183 if (!invalidated() && *now >= next_retransmit_)
184 {
185 for (id_t i = rel_send.head_id(); i < rel_send.tail_id(); ++i)
186 {
188 if (m.ready_retransmit(*now))
189 {
190 // preserve original packet non-encapsulated
191 PACKET pkt = m.packet.clone();
192
193 // encapsulate packet
194 try
195 {
196 parent().encapsulate(m.id(), pkt);
197 }
198 catch (...)
199 {
201 throw;
202 }
203 parent().net_send(pkt, NET_SEND_RETRANSMIT);
205 }
206 }
208 }
209 }
210
211 // When should we next call retransmit()
213 {
214 if (!invalidated())
215 return next_retransmit_;
216 return Time::infinite();
217 }
218
219 // Has SSL handshake been started yet?
220 bool ssl_started() const
221 {
222 return ssl_started_;
223 }
224
225 // Was session invalidated by an exception?
226 bool invalidated() const
227 {
228 return invalidated_;
229 }
230
231 // Reason for invalidation
233 {
235 }
236
237 // Invalidate session
238 void invalidate(const Error::Type reason)
239 {
240 if (!invalidated_)
241 {
242 invalidated_ = true;
243 invalidation_reason_ = reason;
244 parent().invalidate_callback();
245 }
246 }
247
248 std::string ssl_handshake_details() const
249 {
250 return ssl_->ssl_handshake_details();
251 }
252
253 void export_key_material(OpenVPNStaticKey &key, const std::string &label) const
254 {
256 throw ErrorCode(Error::KEY_EXPANSION_ERROR, true, "TLS Keying material export error");
257 }
258
260 {
261 return ssl_->auth_cert();
262 }
263
264 private:
265 // Parent methods -- derived class must define these methods
266
267 // Encapsulate packet, use id as sequence number. If xmit_acks is non-empty,
268 // try to piggy-back ACK replies from xmit_acks to sender in encapsulated
269 // packet. Any exceptions thrown will invalidate session, i.e. this object
270 // can no longer be used.
271 //
272 // void encapsulate(id_t id, PACKET& pkt) = 0;
273
274 // Perform integrity check on packet. If packet is good, unencapsulate it and
275 // pass it into the rel_recv object. Any ACKs received for messages previously
276 // sent should be marked in rel_send. Message sequence number should be recorded
277 // in xmit_acks. Exceptions may be thrown here and they will be passed up to
278 // caller of net_recv and will not invalidate the session.
279 // Method should return true if packet was placed into rel_recv.
280 //
281 // bool decapsulate(PACKET& pkt) = 0;
282
283 // Generate a standalone ACK message in buf based on ACKs in xmit_acks
284 // (PACKET will be already be initialized by frame_prepare()).
285 //
286 // void generate_ack(PACKET& pkt) = 0;
287
288 // Transmit encapsulated ciphertext packet to peer. Method may not modify
289 // or take ownership of net_pkt or underlying data unless it copies it.
290 //
291 // void net_send(const PACKET& net_pkt, const NetSendType nstype) = 0;
292
293 // Pass cleartext data up to application, which make take
294 // ownership of to_app_buf via std::move.
295 //
296 // void app_recv(BufferPtr&& to_app_buf) = 0;
297
298 // Pass raw data up to application. A packet is considered to be raw
299 // if is_raw() method returns true. Method may take ownership
300 // of raw_pkt via std::move.
301 //
302 // void raw_recv(PACKET&& raw_pkt) = 0;
303
304 // called if session is invalidated by an error (optional)
305 //
306 // void invalidate_callback() {}
307
308 // END of parent methods
309
310 // get reference to parent for CRTP
311 PARENT &parent()
312 {
313 return *static_cast<PARENT *>(this);
314 }
315
318 {
319 // encapsulate SSL ciphertext packets
321 {
323 m.packet = PACKET(ssl_->read_ciphertext());
324
325 // encapsulate and send cloned packet, preserve original one for retransmit
326 PACKET pkt = m.packet.clone();
327
328 // encapsulate packet
329 try
330 {
331 parent().encapsulate(m.id(), pkt);
332 }
333 catch (...)
334 {
336 throw;
337 }
338
339 // transmit it
340 parent().net_send(pkt, NET_SEND_SSL);
341 }
342 }
343
346 {
347 try
348 {
350 }
351 catch (...)
352 {
353 }
354 }
355
356 // app data -> SSL -> protocol encapsulation -> reliability layer -> network
358 {
359 if (ssl_started_)
360 {
361 // push app-layer cleartext through SSL object
362 while (!app_write_queue.empty())
363 {
364 BufferPtr &buf = app_write_queue.front();
365 ssize_t size;
366 try
367 {
368 size = ssl_->write_cleartext_unbuffered(buf->data(), buf->size());
369 }
370 catch (...)
371 {
373 throw;
374 }
375 if (size == static_cast<ssize_t>(buf->size()))
376 app_write_queue.pop_front();
377 else if (size == SSLConst::SHOULD_RETRY)
378 break;
379 else if (size >= 0)
380 {
381 // partial write
382 app_write_queue.front()->advance(size);
383 break;
384 }
385 else
386 {
388 throw unknown_status_from_ssl_layer();
389 }
390 }
391
393 }
394 }
395
396 // raw app data -> protocol encapsulation -> reliability layer -> network
398 {
399 while (!raw_write_queue.empty() && rel_send.ready())
400 {
402 m.packet = raw_write_queue.front();
403 raw_write_queue.pop_front();
404
405 PACKET pkt = m.packet.clone();
406
407 // encapsulate packet
408 try
409 {
410 parent().encapsulate(m.id(), pkt);
411 }
412 catch (...)
413 {
415 throw;
416 }
417
418 // transmit it
419 parent().net_send(pkt, NET_SEND_RAW);
420 }
421 }
422
423 // network -> reliability layer -> protocol decapsulation -> SSL -> app
424 bool up_stack(PACKET &recv)
425 {
427 if (parent().decapsulate(recv))
428 {
429 up_sequenced();
430 return true;
431 }
432 return false;
433 }
434
435 // if a sequenced packet is available from reliability layer,
436 // move it up the stack
438 {
439 // is sequenced receive packet available?
440 while (rel_recv.ready())
441 {
443 if (!m.packet.contains_tls_ciphertext())
444 parent().raw_recv(std::move(m.packet));
445 else // SSL packet
446 {
447 if (ssl_started_)
448 ssl_->write_ciphertext(m.packet.buffer_ptr());
449 else
450 break;
451 }
453 }
454
455 // read cleartext data from SSL object
456 if (ssl_started_)
457 while (ssl_->read_cleartext_ready())
458 {
459 ssize_t size;
462 try
463 {
464 size = ssl_->read_cleartext(to_app_buf->data(), to_app_buf->max_size());
465 }
466 catch (const ExceptionCode &ec)
467 {
468 if (ec.is_tls_alert())
469 {
470 // The SSL library may have generated a TLS alert in case of error: send it out now.
471 // Use the nothrow() version of the function, since this best-effort only, and we
472 // also need `error()` below to get called, to invalidate the session.
474 }
475
476 // SSL fatal errors will invalidate the session
478 throw;
479 }
480 catch (...)
481 {
482 // SSL fatal errors will invalidate the session
484 throw;
485 }
486 if (size >= 0)
487 {
488 to_app_buf->set_size(size);
489
490 // pass cleartext data to app
491 parent().app_recv(std::move(to_app_buf));
492 }
493 else if (size == SSLConst::SHOULD_RETRY)
494 break;
495 else if (size == SSLConst::PEER_CLOSE_NOTIFY)
496 {
498 throw ErrorCode(Error::CLIENT_HALT, true, "SSL Close Notify received");
499 }
500 else
501 {
503 throw unknown_status_from_ssl_layer();
504 }
505 }
506 }
507
512
513 void error(const Error::Type reason)
514 {
515 if (stats)
516 stats->error(reason);
517 invalidate(reason);
518 }
519
520 private:
521 const Time::Duration tls_timeout;
525 bool invalidated_ = false;
527 bool ssl_started_ = false;
529 BufferPtr to_app_buf; // cleartext data decrypted by SSL that is to be passed to app via app_recv method
530 PACKET ack_send_buf; // only used for standalone ACKs to be sent to peer
531 std::deque<BufferPtr> app_write_queue;
532 std::deque<PACKET> raw_write_queue;
534
535 protected:
540};
541
542} // namespace openvpn
543
544#endif // OPENVPN_SSL_PROTOSTACK_H
bool is_tls_alert() const
Some errors may justify letting the underlying SSL library send out TLS alerts.
Definition excode.hpp:62
@ WRITE_ACK_STANDALONE
Definition frame.hpp:45
@ READ_SSL_CLEARTEXT
Definition frame.hpp:42
size_t prepare(const unsigned int context, Buffer &buf) const
Definition frame.hpp:263
unsigned char * raw_alloc()
bool net_recv(PACKET &&pkt)
OPENVPN_SIMPLE_EXCEPTION(unknown_status_from_ssl_layer)
Error::Type invalidation_reason_
ProtoStackBase(SSLFactoryAPI &ssl_factory, TimePtr now_arg, const Time::Duration &tls_timeout_arg, const Frame::Ptr &frame, const SessionStats::Ptr &stats_arg, bool psid_cookie_mode)
std::deque< PACKET > raw_write_queue
uint32_t get_tls_warnings() const
void raw_send(PACKET &&pkt)
bool up_stack(PACKET &recv)
Error::Type invalidation_reason() const
std::deque< BufferPtr > app_write_queue
const AuthCert::Ptr & auth_cert() const
void app_send(BufferPtr &&buf)
static constexpr size_t ovpn_sending_window
void export_key_material(OpenVPNStaticKey &key, const std::string &label) const
const Time::Duration tls_timeout
Time next_retransmit() const
OPENVPN_SIMPLE_EXCEPTION(proto_stack_invalidated)
void send_pending_ssl_ciphertext_packets_nothrow() noexcept
A version of send_pending_ssl_ciphertext_packets() that guarantees no exceptions.
void invalidate(const Error::Type reason)
static constexpr size_t ovpn_receiving_window
void error(const Error::Type reason)
std::string ssl_handshake_details() const
SessionStats::Ptr stats
void send_pending_ssl_ciphertext_packets()
If there are any pending SSL ciphertext packets, encapsulate and send them out.
static Ptr Create(ArgsT &&...args)
Creates a new instance of RcEnable with the given arguments.
Definition make_rc.hpp:43
bool empty() const
Definition relack.hpp:156
static constexpr size_t maximum_acks_ack_v1
Definition relack.hpp:31
void reset_retransmit(const Time &now, const Time::Duration &tls_timeout)
Definition relsend.hpp:50
bool ready_retransmit(const Time &now) const
Definition relsend.hpp:37
Message & ref_by_id(const id_t id)
Definition relsend.hpp:95
Message & send(const Time &now, const Time::Duration &tls_timeout)
Definition relsend.hpp:132
Time::Duration until_retransmit(const Time &now)
Definition relsend.hpp:101
virtual void start_handshake()=0
virtual void write_ciphertext(const BufferPtr &buf)=0
virtual const AuthCert::Ptr & auth_cert() const =0
virtual bool read_ciphertext_ready() const =0
virtual bool read_cleartext_ready() const =0
virtual std::string ssl_handshake_details() const =0
virtual bool export_keying_material(const std::string &label, unsigned char *dest, size_t size)=0
virtual BufferPtr read_ciphertext()=0
virtual ssize_t write_cleartext_unbuffered(const void *data, const size_t size)=0
virtual ssize_t read_cleartext(void *data, const size_t capacity)=0
uint32_t get_tls_warnings() const
Definition sslapi.hpp:73
virtual void error(const size_t type, const std::string *text=nullptr)
static TimeType infinite()
Definition time.hpp:254
@ KEY_EXPANSION_ERROR
Definition error.hpp:102
@ ENCAPSULATION_ERROR
Definition error.hpp:57
std::uint32_t id_t
Definition relcommon.hpp:22