![]() |
The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
|
Binary IO abstractions for TLS and DTLS connections. More...
#include <freeradius-devel/server/base.h>#include <freeradius-devel/tls/base.h>#include <freeradius-devel/tls/connection.h>#include <freeradius-devel/unlang/interpret.h>#include <freeradius-devel/bio/bio_priv.h>#include <freeradius-devel/bio/fd.h>#include <freeradius-devel/bio/tls.h>
Include dependency graph for tls.c:Go to the source code of this file.
Data Structures | |
| struct | fr_bio_tls_s |
| The TLS bio. More... | |
Functions | |
| fr_bio_tls_t * | fr_bio_tls_alloc (request_t *request, fr_bio_t *next, SSL_CTX *ssl_ctx, bool client, bool client_cert) |
| Allocate a TLS bio. | |
| static size_t | fr_bio_tls_clean_read (fr_bio_tls_t *my, void *buffer, size_t size) |
| Copy decrypted data which the caller has not yet read into the caller's buffer. | |
| void | fr_bio_tls_close_send (fr_bio_tls_t *my) |
Send a TLS close_notify alert to the peer. | |
| fr_tls_connection_t * | fr_bio_tls_conn (fr_bio_tls_t *my) |
The TLS connection, so the caller can set conn->name | |
| static void | fr_bio_tls_conn_close (fr_tls_connection_t *conn) |
| The TLS connection failed. | |
| static void | fr_bio_tls_conn_connected (fr_tls_connection_t *conn) |
| The TLS connection can carry application data. | |
| static ssize_t | fr_bio_tls_conn_write (fr_tls_connection_t *conn, uint8_t const *data, size_t size) |
| Write encrypted records to the next bio. | |
| int | fr_bio_tls_connect (fr_bio_tls_t *my) |
| Take the connection through every state which comes before application data. | |
| static int | fr_bio_tls_decrypt (fr_bio_tls_t *my) |
| Decrypt one record which is waiting in OpenSSL. | |
| static int | fr_bio_tls_destructor (fr_bio_tls_t *my) |
| Send any close_notify, stop the fd events, cancel the request, and then run the common bio destructor. | |
| static void | fr_bio_tls_el_error (UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, void *uctx) |
| The fd failed. | |
| static void | fr_bio_tls_el_read (UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx) |
| A record arrived on the fd, so hand the record to the TLS connection. | |
| static void | fr_bio_tls_el_write (UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx) |
| The fd is ready for writes. | |
| static int | fr_bio_tls_eof (fr_bio_t *bio) |
| Handle EOF from the next bio. | |
| static void | fr_bio_tls_events_delete (fr_bio_tls_t *my) |
| Stop owning the events of the fd. | |
| static void | fr_bio_tls_failed (fr_bio_tls_t *my, fr_tls_connection_fail_t reason, int error) |
| Stop the connection, and tell the application. | |
| static void | fr_bio_tls_fd_connected (fr_bio_t *fd_bio) |
| The fd is connected. | |
| static void | fr_bio_tls_fd_error (fr_bio_t *fd_bio) |
| The fd failed to connect. | |
| static int | fr_bio_tls_flush (fr_bio_tls_t *my) |
| Write any pending encrypted records to the next bio. | |
| static fr_bio_tls_t * | fr_bio_tls_from_fd (fr_bio_t *fd_bio) |
| Find the TLS bio from the fd bio. | |
| static int | fr_bio_tls_handshake_start (fr_bio_tls_t *my) |
| The fd is connected, so start the TLS connection. | |
| static void | fr_bio_tls_next_resume (fr_bio_t *next) |
| Tell the next bio that it can write again. | |
| static bool | fr_bio_tls_pending (fr_bio_tls_t *my) |
| Check whether OpenSSL holds data which the bio has not yet processed. | |
| static ssize_t | fr_bio_tls_read (fr_bio_t *bio, void *packet_ctx, void *buffer, size_t size) |
| Read encrypted data from the next bio, and return the decrypted application data. | |
| static ssize_t | fr_bio_tls_read_eof (fr_bio_t *bio, UNUSED void *packet_ctx, void *buffer, size_t size) |
| Return the decrypted data which remains after the next bio reached EOF, and then return EOF. | |
| static ssize_t | fr_bio_tls_read_wait (UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void *buffer, UNUSED size_t size) |
| Application data cannot move until the connection is connected. | |
| static int | fr_bio_tls_shutdown (fr_bio_t *bio) |
| Disconnect the bio from the fd. | |
| static ssize_t | fr_bio_tls_write (fr_bio_t *bio, void *packet_ctx, void const *buffer, size_t size) |
| static int | fr_bio_tls_write_blocked (fr_bio_t *bio) |
| Record that the next bio is blocked. | |
| static ssize_t | fr_bio_tls_write_data (fr_bio_tls_t *my, void const *buffer, size_t size) |
| Encrypt application data, and write the encrypted records to the next bio. | |
| static int | fr_bio_tls_write_resume (fr_bio_t *bio) |
| Write the encrypted records which were waiting for the next bio. | |
| static int | fr_bio_tls_write_wait (fr_bio_tls_t *my, bool wait) |
| Start or stop waiting for the fd to have room for more records. | |
| static ssize_t | fr_bio_tls_write_wait_connected (UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void const *buffer, size_t size) |
Variables | |
| static fr_event_update_t const | pause_write [] |
| static fr_event_update_t const | resume_write [] |
Binary IO abstractions for TLS and DTLS connections.
The TLS bio sits between the application and an fd bio, which carries the encrypted records. The TLS bio owns the whole life of the TLS connection:
fr_bio_tls_alloc() allocates the bio and the fr_tls_connection_t from the request, and sets conn->request. The request must already use an interpreter.fr_bio_tls_connect() takes the connection through every state which comes before application data: the connect() of the fd bio, the TLS or DTLS handshake, and the policies which run before and after the handshake. During these states the TLS bio reads and writes the fd bio, and owns the events of the fd in the event list of the interpreter.connected callback. From then on the application reads and writes the TLS bio in the same way as any other bio. A read takes encrypted data from the fd bio, passes the encrypted data through OpenSSL, and returns the decrypted application data. A write encrypts the application data, and writes the encrypted records to the fd bio.The next bio MUST be a fd bio. If the connection fails at any point, the TLS bio removes the events of the fd, and calls the failed callback. The failed callback must not free the bio, as the failure can happen inside a read or a write on the bio.
The bio and the fr_tls_connection_t are allocated from the request, so freeing the request frees both. Freeing the bio cancels the request, as the request has a frame which points to the connection.
After each read or write to the fd bio, the TLS bio calls fr_tls_connection_io_error() to decide what to do next: try again, wait, or stop. fr_tls_connection_io_error() also tells the TLS connection about EOF and fatal errors.
Definition in file tls.c.
| struct fr_bio_tls_s |
Collaboration diagram for fr_bio_tls_s:| Data Fields | ||
|---|---|---|
| uint8_t | buffer[SSL3_RT_MAX_PLAIN_LENGTH] | Encrypted data read from the next bio. |
| size_t | clean_len |
How much decrypted data is in clean_out. |
| size_t | clean_offset | How much of the decrypted data the caller has read. |
| bool | client | Act as the client, and connect to a server. |
| bool | client_cert | For a server, require a client certificate. |
| fr_tls_connection_t * | conn |
The TLS connection, which is a child of the bio.
|
| fr_event_list_t * | el | The event list of the interpreter. |
| bool | events |
The bio owns the events of the fd, which it has until the connection is connected. The event loop can remove the events by itself, e.g. at EOF, so the bio keeps no pointer. |
| int | fd | The fd of the next bio. |
| FR_BIO_COMMON | ||
| ssize_t | next_error | The error which the next bio returned from a write. |
| void * | packet_ctx |
Passed to the next bio when writing encrypted records. Set only while |
| SSL_CTX * | ssl_ctx | Used to create the TLS session. |
| bool | write_blocked |
The next bio is blocked. Encrypted records wait in the TLS connection until |
| bool | write_wait | The write event of the fd is active. |
| fr_bio_tls_t * fr_bio_tls_alloc | ( | request_t * | request, |
| fr_bio_t * | next, | ||
| SSL_CTX * | ssl_ctx, | ||
| bool | client, | ||
| bool | client_cert | ||
| ) |
Allocate a TLS bio.
The bio and the TLS connection are allocated from the request, so they cannot outlive the request. The request must already use an interpreter, as the connection state machine runs in a frame of the request, and the bio takes the event list from the interpreter. After allocating the bio, call fr_bio_tls_connect().
Use fr_bio_cb_set() to set the callbacks:
connected is called when the connection can carry application data. The fd events are gone, and the application can read and write the bio.failed is called when the connection fails, for any reason, at any time. The reason is in conn->failed, and conn->error holds the errno for system call failures. The callback must not free the bio, as the callback can run from inside a read or a write on the bio. The callback can instead arrange for the application to free the bio later, for example from the event loop.| [in] | request | to allocate the bio and the connection from, and which runs the connection. |
| [in] | next | the fd bio which carries the encrypted records. |
| [in] | ssl_ctx | to create the TLS session from. |
| [in] | client | true to connect to a server, false to accept a connection. |
| [in] | client_cert | for a server, whether to require a client certificate. Ignored for a client. |
NULL when the next bio is not an fd bio, when the request has no event list, or when an allocation fails Definition at line 1031 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:| void fr_bio_tls_close_send | ( | fr_bio_tls_t * | my | ) |
Send a TLS close_notify alert to the peer.
RFC 9846 Section 6.1 requires that a party sends a close_notify alert before closing its write side of the connection. A TCP connection can be closed without writing, but a TLS connection cannot.
The write is best effort. If the next bio is blocked, the alert stays in the TLS connection buffer, and the fd is closed anyway.
The alert is not sent when the TLS connection is not connected, when the TLS connection failed, when the bio chain already shut down, or when the fd is at EOF or is not open.
An application which frees the fd bio before the TLS bio MUST call this function first. Freeing the fd bio shuts down the whole chain, which stops writes in the TLS bio, so the TLS bio destructor can no longer send the alert.
| [in] | my | the TLS bio. |
Definition at line 957 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:| fr_tls_connection_t * fr_bio_tls_conn | ( | fr_bio_tls_t * | my | ) |
|
static |
The TLS connection failed.
The TLS connection runs the close callback only after a failure. After a failure the bio cannot read or write, and the application decides when to free the bio.
Definition at line 294 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
The TLS connection can carry application data.
The TLS connection calls this function once, when the connection enters the TLS_CONNECTION_CONNECTED state. The application owns the fd from now on, so the events of the fd go away before the application hears about the connection. The application can then read and write the bio.
Definition at line 277 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Write encrypted records to the next bio.
fr_tls_connection_write() calls this function once for each block of encrypted data in the TLS connection. For datagrams, each block is one datagram.
A partial write means that the fd bio is out of room, so we assume that writes are blocked, in the same way as fr_bio_mem_write_next(). For every other result, fr_tls_connection_io_error() decides what to do.
On EOF or a fatal error, fr_tls_connection_io_error() tells the TLS connection, which runs the failed callback of the application.
fr_bio_tls_write_resume() writes the data.next_error. Definition at line 198 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:| int fr_bio_tls_connect | ( | fr_bio_tls_t * | my | ) |
Take the connection through every state which comes before application data.
The connection connects the fd bio, runs the TLS handshake, and runs the policies which go with the handshake. A stream socket can take some time to connect, and the TLS bio waits for the connect in the event list. A datagram socket, or a socket which the caller accepted, is already connected, so the handshake starts before this function returns.
The caller must wait for the connected or failed callback. A callback can run before this function returns.
| [in] | my | the TLS bio. |
failed callback has already run. Definition at line 1102 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Decrypt one record which is waiting in OpenSSL.
Definition at line 500 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Send any close_notify, stop the fd events, cancel the request, and then run the common bio destructor.
The application is freeing the bio, so the application does not care if there is a failed write when sending the close_notify. Delete the FD events before any writes.
A failed write in the FD BIO causes it to close the FD.
The request has a frame which points at the TLS connection, and the TLS connection is freed with the bio. Cancelling the request unwinds the frames nad frees the connection. The interpreter can then free the request.
Definition at line 991 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
|
static |
A record arrived on the fd, so hand the record to the TLS connection.
Reading the fd is all this function does. Everything the record means to TLS is fr_tls_connection_recv()'s business.
Definition at line 746 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
The fd is ready for writes.
Run the connection state machine, which either flushes pending data, or keeps going with the next TLS state. The event loop calls this function only after a write blocked, see fr_bio_tls_write_wait().
fr_tls_connection_process() writes the rest of the encrypted records, and then checks the connection again. The check ends a connection whose handshake finished while a record was still waiting.
Definition at line 782 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Handle EOF from the next bio.
When the TLS bio still holds data for the application, this function sets the read function to fr_bio_tls_read_eof(). fr_bio_tls_read_eof() returns the remaining data, and then signals EOF.
fr_bio_eof() continues to the previous biosDefinition at line 689 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
The fd is connected, so start the TLS connection.
Create the TLS session, put the events of the fd in the event list, and start the connection state machine. The state machine runs in one frame of the request, which fr_tls_connection_push() pushes. The TLS bio does the IO for the state machine.
Definition at line 831 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
|
static |
|
static |
Read encrypted data from the next bio, and return the decrypted application data.
One record can hold more application data than the caller asked for. The rest of the data stays in the TLS connection, and the next read returns the rest. The bio asks OpenSSL to decrypt the next record only after the caller has read all of the data from the previous record.
Definition at line 559 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Disconnect the bio from the fd.
This function runs when a shutdown of the chain completes, and when the bio is freed. The events of the fd go away, and the application can no longer read or write. The TLS connection is a child of the bio, so the TLS connection stays valid until the bio is freed.
Definition at line 712 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Encrypt application data, and write the encrypted records to the next bio.
SSL_MODE_ENABLE_PARTIAL_WRITE is not set, so OpenSSL encrypts all of the data passed to SSL_write(). The application data therefore counts as written as soon as OpenSSL encrypts the data. If the next bio blocks, the encrypted records wait in the TLS connection, and the next write or fr_bio_tls_write_resume() writes the records later. Until the waiting records reach the next bio, the bio returns fr_bio_error(IO_WOULD_BLOCK) for new application data. The amount of waiting data therefore cannot grow without limit.
fr_bio_error(IO_WOULD_BLOCK) when encrypted records are still waiting for the next bioDefinition at line 382 of file tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
|
static |
Start or stop waiting for the fd to have room for more records.
The event of the fd is inserted with a write callback, and then the write event is suspended, as an fd which has room fires the write event on every pass of the event loop. The write event is active only while encrypted records are waiting for room.
A suspend reads the active callback and a resume reads the suspended callback, so a suspend and a resume have to alternate. write_wait records whether the write event is active.
Definition at line 122 of file tls.c.
Here is the caller graph for this function:
|
static |
|
static |
1.9.8