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 else
217 return Time::infinite();
218 }
219
220 // Has SSL handshake been started yet?
221 bool ssl_started() const
222 {
223 return ssl_started_;
224 }
225
226 // Was session invalidated by an exception?
227 bool invalidated() const
228 {
229 return invalidated_;
230 }
231
232 // Reason for invalidation
234 {
236 }
237
238 // Invalidate session
239 void invalidate(const Error::Type reason)
240 {
241 if (!invalidated_)
242 {
243 invalidated_ = true;
244 invalidation_reason_ = reason;
245 parent().invalidate_callback();
246 }
247 }
248
249 std::string ssl_handshake_details() const
250 {
251 return ssl_->ssl_handshake_details();
252 }
253
254 void export_key_material(OpenVPNStaticKey &key, const std::string &label) const
255 {
257 throw ErrorCode(Error::KEY_EXPANSION_ERROR, true, "TLS Keying material export error");
258 }
259
261 {
262 return ssl_->auth_cert();
263 }
264
265 private:
266 // Parent methods -- derived class must define these methods
267
268 // Encapsulate packet, use id as sequence number. If xmit_acks is non-empty,
269 // try to piggy-back ACK replies from xmit_acks to sender in encapsulated
270 // packet. Any exceptions thrown will invalidate session, i.e. this object
271 // can no longer be used.
272 //
273 // void encapsulate(id_t id, PACKET& pkt) = 0;
274
275 // Perform integrity check on packet. If packet is good, unencapsulate it and
276 // pass it into the rel_recv object. Any ACKs received for messages previously
277 // sent should be marked in rel_send. Message sequence number should be recorded
278 // in xmit_acks. Exceptions may be thrown here and they will be passed up to
279 // caller of net_recv and will not invalidate the session.
280 // Method should return true if packet was placed into rel_recv.
281 //
282 // bool decapsulate(PACKET& pkt) = 0;
283
284 // Generate a standalone ACK message in buf based on ACKs in xmit_acks
285 // (PACKET will be already be initialized by frame_prepare()).
286 //
287 // void generate_ack(PACKET& pkt) = 0;
288
289 // Transmit encapsulated ciphertext packet to peer. Method may not modify
290 // or take ownership of net_pkt or underlying data unless it copies it.
291 //
292 // void net_send(const PACKET& net_pkt, const NetSendType nstype) = 0;
293
294 // Pass cleartext data up to application, which make take
295 // ownership of to_app_buf via std::move.
296 //
297 // void app_recv(BufferPtr&& to_app_buf) = 0;
298
299 // Pass raw data up to application. A packet is considered to be raw
300 // if is_raw() method returns true. Method may take ownership
301 // of raw_pkt via std::move.
302 //
303 // void raw_recv(PACKET&& raw_pkt) = 0;
304
305 // called if session is invalidated by an error (optional)
306 //
307 // void invalidate_callback() {}
308
309 // END of parent methods
310
311 // get reference to parent for CRTP
312 PARENT &parent()
313 {
314 return *static_cast<PARENT *>(this);
315 }
316
319 {
320 // encapsulate SSL ciphertext packets
322 {
324 m.packet = PACKET(ssl_->read_ciphertext());
325
326 // encapsulate and send cloned packet, preserve original one for retransmit
327 PACKET pkt = m.packet.clone();
328
329 // encapsulate packet
330 try
331 {
332 parent().encapsulate(m.id(), pkt);
333 }
334 catch (...)
335 {
337 throw;
338 }
339
340 // transmit it
341 parent().net_send(pkt, NET_SEND_SSL);
342 }
343 }
344
347 {
348 try
349 {
351 }
352 catch (...)
353 {
354 }
355 }
356
357 // app data -> SSL -> protocol encapsulation -> reliability layer -> network
359 {
360 if (ssl_started_)
361 {
362 // push app-layer cleartext through SSL object
363 while (!app_write_queue.empty())
364 {
365 BufferPtr &buf = app_write_queue.front();
366 ssize_t size;
367 try
368 {
369 size = ssl_->write_cleartext_unbuffered(buf->data(), buf->size());
370 }
371 catch (...)
372 {
374 throw;
375 }
376 if (size == static_cast<ssize_t>(buf->size()))
377 app_write_queue.pop_front();
378 else if (size == SSLConst::SHOULD_RETRY)
379 break;
380 else if (size >= 0)
381 {
382 // partial write
383 app_write_queue.front()->advance(size);
384 break;
385 }
386 else
387 {
389 throw unknown_status_from_ssl_layer();
390 }
391 }
392
394 }
395 }
396
397 // raw app data -> protocol encapsulation -> reliability layer -> network
399 {
400 while (!raw_write_queue.empty() && rel_send.ready())
401 {
403 m.packet = raw_write_queue.front();
404 raw_write_queue.pop_front();
405
406 PACKET pkt = m.packet.clone();
407
408 // encapsulate packet
409 try
410 {
411 parent().encapsulate(m.id(), pkt);
412 }
413 catch (...)
414 {
416 throw;
417 }
418
419 // transmit it
420 parent().net_send(pkt, NET_SEND_RAW);
421 }
422 }
423
424 // network -> reliability layer -> protocol decapsulation -> SSL -> app
425 bool up_stack(PACKET &recv)
426 {
428 if (parent().decapsulate(recv))
429 {
430 up_sequenced();
431 return true;
432 }
433 else
434 return false;
435 }
436
437 // if a sequenced packet is available from reliability layer,
438 // move it up the stack
440 {
441 // is sequenced receive packet available?
442 while (rel_recv.ready())
443 {
445 if (!m.packet.contains_tls_ciphertext())
446 parent().raw_recv(std::move(m.packet));
447 else // SSL packet
448 {
449 if (ssl_started_)
450 ssl_->write_ciphertext(m.packet.buffer_ptr());
451 else
452 break;
453 }
455 }
456
457 // read cleartext data from SSL object
458 if (ssl_started_)
459 while (ssl_->read_cleartext_ready())
460 {
461 ssize_t size;
464 try
465 {
466 size = ssl_->read_cleartext(to_app_buf->data(), to_app_buf->max_size());
467 }
468 catch (const ExceptionCode &ec)
469 {
470 if (ec.is_tls_alert())
471 {
472 // The SSL library may have generated a TLS alert in case of error: send it out now.
473 // Use the nothrow() version of the function, since this best-effort only, and we
474 // also need `error()` below to get called, to invalidate the session.
476 }
477
478 // SSL fatal errors will invalidate the session
480 throw;
481 }
482 catch (...)
483 {
484 // SSL fatal errors will invalidate the session
486 throw;
487 }
488 if (size >= 0)
489 {
490 to_app_buf->set_size(size);
491
492 // pass cleartext data to app
493 parent().app_recv(std::move(to_app_buf));
494 }
495 else if (size == SSLConst::SHOULD_RETRY)
496 break;
497 else if (size == SSLConst::PEER_CLOSE_NOTIFY)
498 {
500 throw ErrorCode(Error::CLIENT_HALT, true, "SSL Close Notify received");
501 }
502 else
503 {
505 throw unknown_status_from_ssl_layer();
506 }
507 }
508 }
509
514
515 void error(const Error::Type reason)
516 {
517 if (stats)
518 stats->error(reason);
519 invalidate(reason);
520 }
521
522 private:
523 const Time::Duration tls_timeout;
527 bool invalidated_ = false;
529 bool ssl_started_ = false;
531 BufferPtr to_app_buf; // cleartext data decrypted by SSL that is to be passed to app via app_recv method
532 PACKET ack_send_buf; // only used for standalone ACKs to be sent to peer
533 std::deque<BufferPtr> app_write_queue;
534 std::deque<PACKET> raw_write_queue;
536
537 protected:
542};
543
544} // namespace openvpn
545
546#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:69
size_t prepare(const unsigned int context, Buffer &buf) const
Definition frame.hpp:266
@ WRITE_ACK_STANDALONE
Definition frame.hpp:45
@ READ_SSL_CLEARTEXT
Definition frame.hpp:42
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
ReliableSendTemplate< PACKET > ReliableSend
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.
ReliableRecvTemplate< PACKET > ReliableRecv
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:256
@ KEY_EXPANSION_ERROR
Definition error.hpp:102
@ ENCAPSULATION_ERROR
Definition error.hpp:57
std::uint32_t id_t
Definition relcommon.hpp:22
Support deferred server-side state creation when client connects.
Definition ovpncli.cpp:95