![]() |
The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
|
TLS server test framework. More...
#include <freeradius-devel/server/base.h>#include <freeradius-devel/server/module_rlm.h>#include <freeradius-devel/io/listen.h>#include <freeradius-devel/io/thread.h>#include <freeradius-devel/tls/base.h>#include <freeradius-devel/tls/dtls.h>#include <freeradius-devel/tls/strerror.h>#include <freeradius-devel/tls/version.h>#include <freeradius-devel/tls/connection.h>#include <freeradius-devel/util/misc.h>#include <freeradius-devel/unlang/base.h>#include <freeradius-devel/unlang/function.h>#include <freeradius-devel/unlang/interpret.h>#include <freeradius-devel/util/socket.h>#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>#include <sys/wait.h>
Include dependency graph for unit_test_tls.c:Go to the source code of this file.
Data Structures | |
| struct | unit_test_tls_conf_t |
| struct | unit_test_tls_socket_t |
| The "unit_test_tls" section. More... | |
| struct | unit_test_tls_t |
| State of the test program. More... | |
Macros | |
| #define | EXIT_WITH_FAILURE |
Functions | |
| static void | _request_detach (request_t *request, UNUSED void *uctx) |
| static void | _request_done_detached (request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx) |
| static void | _request_done_external (request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx) |
| static void | _request_done_internal (request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx) |
| static void | _request_init_internal (request_t *request, void *uctx) |
| An internal request created by the interpreter has to run on ours. | |
| static void | _request_resume (request_t *request, UNUSED void *uctx) |
| static void | _request_runnable (request_t *request, void *uctx) |
| static bool | _request_scheduled (request_t const *request, UNUSED void *uctx) |
| static void | _request_yield (request_t *request, void *uctx) |
| static void | _tls_command_exit (fr_event_list_t *el, pid_t pid, int status, void *uctx) |
| Record that the command started by -e has exited. | |
| static void | _tls_connection_error (UNUSED fr_event_list_t *el, int fd, int flags, int fd_errno, void *uctx) |
| The connection failed at the socket level. | |
| static void | _tls_connection_read (UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx) |
| A record arrived on the connection, so hand the record to the connection. | |
| static void | _tls_connection_start (fr_event_list_t *el, void *uctx) |
| Add the connection to the event loop, and get it moving. | |
| static void | _tls_connection_write (UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx) |
| The socket is ready for writes. | |
| static void | _tls_runnable (UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx) |
| Drain the runnable heap once per pass of the event loop. | |
| static void | _tls_socket_accept (fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx) |
| Accept the connection once the listening socket is readable. | |
| static void | _tls_socket_accept_start (fr_event_list_t *el, void *uctx) |
| Start waiting for a connection, from inside the event loop. | |
| static void | _tls_socket_error (fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, UNUSED void *uctx) |
| Stop waiting for a connection if the listening socket fails. | |
| int | main (int argc, char *argv[]) |
| static unsigned char * | tls_alpn_build (TALLOC_CTX *ctx, char const *names) |
| Turn a comma separated list of names into the wire format ALPN uses. | |
| static fr_client_t * | tls_client_alloc (TALLOC_CTX *ctx, fr_ipaddr_t const *ipaddr) |
| Build an internal client. | |
| static int | tls_command_start (unit_test_tls_t *utt, char const *command) |
| Start the command named by -e. | |
| static void | tls_command_wait (unit_test_tls_t *utt) |
| Wait for the command started by -e to exit, so its output is complete. | |
| static int | tls_connection_run (unit_test_tls_t *utt) |
| Run one connection from the first byte to the last. | |
| static ssize_t | tls_connection_write (fr_tls_connection_t *conn, uint8_t const *data, size_t size) |
| Write whatever OpenSSL has produced out to the connection. | |
| static int | tls_connection_write_wait (unit_test_tls_t *utt, bool wait) |
| Start or stop waiting for the socket to have room for more records. | |
| static request_t * | tls_request_alloc (TALLOC_CTX *ctx, int fd, int socket_type) |
| Build the request the handshake runs under. | |
| static void | tls_request_close (fr_tls_connection_t *conn) |
| Stop the event loop, recording why. | |
| static void | tls_request_connected (fr_tls_connection_t *conn) |
| The connection is up, and can carry application data. | |
| static void | tls_request_failed (unit_test_tls_t *utt) |
| Record a failure this program cannot recover from, and stop. | |
| static void | tls_runnable_insert (unit_test_tls_t *utt, request_t *request) |
| Schedule a request, once. | |
| static int | tls_socket_accept (unit_test_tls_t *utt) |
| Wait for a connection, or for the command started by -e to exit without connecting. | |
| static int | tls_socket_accept_one (unit_test_tls_t *utt) |
| Accept a connection, if the listening socket has one queued. | |
| static int | tls_socket_connect (unit_test_tls_t *utt) |
| Connect to the server named by -s. | |
| static int | tls_socket_open (unit_test_tls_t *utt) |
| Open the listening socket described by the "unit_test_tls" section. | |
| static void | usage (main_config_t const *config, int status) |
Variables | |
| static fr_dict_t const * | dict_freeradius |
| static fr_dict_t const * | dict_tls |
| static fr_event_update_t const | pause_write [] |
| char const * | radiusd_version = RADIUSD_VERSION_BUILD("unit_test_tls") |
| static fr_event_update_t const | resume_write [] |
| static const conf_parser_t | unit_test_tls_config [] |
| fr_dict_autoload_t | unit_test_tls_dict [] |
| static const conf_parser_t | unit_test_tls_socket_config [] |
| The socket items, parsed once per transport subsection. | |
TLS server test framework.
This program loads the "tls" protocol and reads two top-level sections from unit_test_tls.conf. The "tls" section supplies the TLS server context, and takes exactly the items that a TLS configuration takes anywhere else in the server. The "unit_test_tls" section supplies what steers the test program itself, which is the address of the listening TCP socket and whether a client certificate is required.
One connection is accepted, and the TLS handshake for that connection is run to completion.
The handshake runs on a persistent asynchronous interpreter, the same way radiusd runs one. A persistent interpreter keeps the handshake state across the several rounds of a single connection.
The "virtual_server" item in the "tls" section names a virtual server. That virtual server supplies the "verify certificate", "load session", "store session" and "clear session" sections which the handshake calls.
To run the test, start unit_test_tls with the test configuration, then connect to the listening socket:
src/tests/tls/unit_test_tls.conf documents every configuration item.
Definition in file unit_test_tls.c.
| struct unit_test_tls_conf_t |
Definition at line 119 of file unit_test_tls.c.
Collaboration diagram for unit_test_tls_conf_t:| Data Fields | ||
|---|---|---|
| fr_tls_conf_t * | client |
The client { ... } subsection, used with -s. |
| uint32_t | drop_datagram |
Drop this datagram rather than sending it, counting from one. Zero drops nothing. |
| fr_ipaddr_t | ipaddr |
Address of the listening socket, copied from whichever subsection transport named. |
| uint16_t | port | Port of the listening socket, and the default port for -s. |
| bool | require_client_certificate | Whether the client has to present a certificate. |
| fr_tls_conf_t * | server |
The server { ... } subsection. |
| int | socket_type |
SOCK_STREAM or SOCK_DGRAM, from transport. |
| unit_test_tls_socket_t | tcp |
The tcp { ... } subsection. |
| char const * | transport | "tcp" or "udp", naming the subsection below which holds the socket. |
| unit_test_tls_socket_t | udp |
The udp { ... } subsection. |
| struct unit_test_tls_socket_t |
The "unit_test_tls" section.
The items at the top level steer the test program and do not configure TLS. The TLS configuration for each role is a subsection. fr_tls_server_config and fr_tls_client_config parse the subsections, so each subsection takes exactly the items that a tls { ... } section takes anywhere else in the server. One transport's socket, named by transport in the configuration
Definition at line 114 of file unit_test_tls.c.
Collaboration diagram for unit_test_tls_socket_t:| Data Fields | ||
|---|---|---|
| fr_ipaddr_t | ipaddr | Address of the listening socket. |
| uint16_t | port | Port of the listening socket. |
| struct unit_test_tls_t |
State of the test program.
The interpreter and the runnable heap live for the whole of the connection. A TLS handshake takes several rounds, and each round may yield while a virtual server section runs. The interpreter and the runnable heap therefore cannot be allocated once per round.
Definition at line 195 of file unit_test_tls.c.
Collaboration diagram for unit_test_tls_t:| Data Fields | ||
|---|---|---|
| fr_event_fd_t * | accept_ef | Read event for sockfd, while waiting in tls_socket_accept(). |
| bool | accepting | Waiting in tls_socket_accept(). |
| bool | alert | Reject the peer with a TLS alert, see -A. |
| unsigned char * | alpn |
Protocol list built by -L, in the wire format fr_tls_conf_t expects. NULL means no ALPN. |
| bool | alpn_required | Set by -l. |
| bool | app_data | Send one byte of application data once the handshake is done, see -P. |
| bool | client |
Act as the client and connect to a server, rather than accept a connection. Copied into every connection this program allocates. |
| char const * | command | Command to run, see -e. |
| fr_event_pid_t const * | command_ev | Waits for the command to exit. |
| bool | command_exited | The command has exited. |
| pid_t | command_pid | Command started by -e, or 0. |
| bool | command_waiting | Waiting in tls_command_wait(). |
| unit_test_tls_conf_t | conf | Parsed "unit_test_tls" section. |
| fr_tls_connection_t * | conn | State of the connection being run. |
| unsigned int | connections | How many connections have been run so far. |
| unsigned int | count | How many connections to run. |
| uint32_t | datagrams |
How many datagrams this connection has been asked to write, counting from one. Only used by |
| bool | done | Set once the connection has a result. |
| fr_event_fd_t * | ef | Read and write events for fd. |
| fr_event_list_t * | el | Event list everything runs on. |
| int | fd | Accepted or connected socket. |
| uint8_t * | first |
The datagram which named the peer, read from the listening socket before the connection's socket existed. Handed to the connection once the connection is running. |
| size_t | first_len |
Length of first. |
| bool | hold | Once connected, send nothing and stay connected, see -H. |
| unlang_interpret_t * | intp | Interpreter for the connection. |
| bool | invalid | Mark the session invalid mid-handshake, see -I. |
| bool | nonblock | Put the connection socket in non-blocking mode, see -B. |
| bool | reject | Reject the session once the handshake succeeds, see -R. |
| int | ret | Exit status. |
| fr_heap_t * | runnable | Requests the interpreter has marked runnable. |
| fr_ipaddr_t | server_ipaddr | Server named by -s. |
| uint16_t | server_port | Port from -s, or from the configuration. |
| int | sockfd | Listening socket. |
| SSL_CTX * | ssl_ctx | Context built from the "tls" section. |
| bool | write_blocked | True while the socket has no room for a record and the write event is active, see tls_connection_write_wait(). |
| int | yielded | How many requests are currently yielded. |
| #define EXIT_WITH_FAILURE |
Definition at line 85 of file unit_test_tls.c.
|
static |
|
static |
Definition at line 313 of file unit_test_tls.c.
|
static |
Definition at line 324 of file unit_test_tls.c.
|
static |
An internal request created by the interpreter has to run on ours.
The subrequests created for the "verify certificate" section and for the session cache sections reach this callback.
fr_tls_call_push() pushes each of them as a detachable subrequest, which the parent waits on rather than running inline, so the subrequest has to be scheduled here or nothing ever runs it.
Definition at line 297 of file unit_test_tls.c.
Here is the call graph for this function:Definition at line 358 of file unit_test_tls.c.
|
static |
|
static |
Definition at line 349 of file unit_test_tls.c.
|
static |
Record that the command started by -e has exited.
If the server is waiting for a connection, a command which exits has either connected already, and the connection is queued, or never will.
Definition at line 1116 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
The connection failed at the socket level.
Definition at line 670 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
A record arrived on the connection, so hand the record to the connection.
Reading the socket is all this program does here. Everything the record means to TLS is fr_tls_connection_recv()'s business.
Definition at line 619 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Add the connection to the event loop, and get it moving.
Runs from inside the event loop, see tls_connection_run().
Definition at line 1343 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
The socket 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 _tls_connection_write() only after a write blocked, see tls_connection_write_wait().
Definition at line 595 of file unit_test_tls.c.
Here is the caller graph for this function:
|
static |
Drain the runnable heap once per pass of the event loop.
A handshake round yields whenever the round runs a virtual server section. When that section finishes, unlang_interpret_mark_runnable() puts the request on the runnable heap, and this drains the heap again.
Definition at line 697 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Accept the connection once the listening socket is readable.
Definition at line 1036 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Start waiting for a connection, from inside the event loop.
See tls_connection_run() for why this runs from inside the event loop.
Definition at line 1059 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Stop waiting for a connection if the listening socket fails.
Definition at line 1048 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:| int main | ( | int | argc, |
| char * | argv[] | ||
| ) |
Definition at line 1643 of file unit_test_tls.c.
|
static |
Turn a comma separated list of names into the wire format ALPN uses.
Each name becomes one length octet followed by the name. There is no terminator, so the caller takes the length from the array itself.
| [in] | ctx | to allocate in. |
| [in] | names | comma separated, in order of preference. |
Definition at line 1604 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Build an internal client.
unit_test_tls accepts packets from anywhere (for now), and doesn't read "client" configuration sections.
The client is built by allocating a CONF_SECTION rather than by filling in fr_client_t manually, which allows fields to be examined by request.client().
"proto = tls" sets both the protocol and tls_required. It also fixes the secret at "radsec", so that is the secret set here.
Definition at line 1234 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Start the command named by -e.
A server starts the command once it is listening. A command which connects to the server connects to a socket which is already listening, so it can't be refused, and the server sees the command exit, so it never waits for a peer which has gone.
A client starts the command once its first connection succeeds.
Definition at line 1147 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Wait for the command started by -e to exit, so its output is complete.
Definition at line 1188 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Run one connection from the first byte to the last.
Everything which belongs to a single connection is allocated here and freed again at the end, so that the next connection starts clean. What survives is what the cache needs: the interpreter, the modules, and so the sessions a policy stored.
Definition at line 1391 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Write whatever OpenSSL has produced out to the connection.
The TLS session reads and writes memory BIOs, which are OpenSSL's in-memory I/O buffers, and never a socket. Writing each record to the socket is therefore the caller's job. The EAP code in src/lib/eap/tls.c writes records the same way.
Definition at line 511 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Start or stop waiting for the socket to have room for more records.
_tls_connection_start() inserts the write event with the socket and then suspends the write event, because a socket which has room fires the write event on every pass of the event loop. tls_connection_write_wait() resumes the write event only while a record is 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_blocked records whether the write event is active, so a suspend never follows a suspend.
Definition at line 489 of file unit_test_tls.c.
Here is the caller graph for this function:
|
static |
Build the request the handshake runs under.
The TLS code reads the control list for TLS-Session-Cert-File and TLS-Session-Require-Client-Certificate, and writes the negotiated version and cipher suite into the session-state list, so a real request is needed.
Definition at line 1276 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Stop the event loop, recording why.
The connection carries the result, so the exit status is decided here rather than by the state machine. A process exit status is this program's notion of a result, and no part of a TLS connection should have to know about one.
Definition at line 385 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
The connection is up, and can carry application data.
unit_test_tls has no application data to send, so a connection which reaches this point has done everything it was going to do. A program which did have data to send would send it here, and close when it was done.
Definition at line 404 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Record a failure this program cannot recover from, and stop.
tls_request_close() reads the result from the connection, so a failure has to reach the connection before the callback runs.
Definition at line 461 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Schedule a request, once.
A request reaches the heap from two directions: the interpreter creating it, and the interpreter marking it runnable again. Inserting a request which is already on the heap puts it there twice, and popping it then returns a request which the heap still holds, which the interpreter refuses to run.
Definition at line 281 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Wait for a connection, or for the command started by -e to exit without connecting.
Definition at line 1087 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Accept a connection, if the listening socket has one queued.
Definition at line 893 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Connect to the server named by -s.
Definition at line 1200 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Open the listening socket described by the "unit_test_tls" section.
Definition at line 810 of file unit_test_tls.c.
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Definition at line 2249 of file unit_test_tls.c.
|
static |
Definition at line 93 of file unit_test_tls.c.
|
static |
Definition at line 94 of file unit_test_tls.c.
|
static |
Definition at line 467 of file unit_test_tls.c.
| char const* radiusd_version = RADIUSD_VERSION_BUILD("unit_test_tls") |
Definition at line 91 of file unit_test_tls.c.
|
static |
Definition at line 472 of file unit_test_tls.c.
|
static |
Definition at line 152 of file unit_test_tls.c.
| fr_dict_autoload_t unit_test_tls_dict |
Definition at line 97 of file unit_test_tls.c.
|
static |
The socket items, parsed once per transport subsection.
Definition at line 142 of file unit_test_tls.c.
1.9.8