-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
net/handshake: Add a kernel API for requesting a TLSv1.3 handshake
To enable kernel consumers of TLS to request a TLS handshake, add support to net/handshake/ to request a handshake upcall. This patch also acts as a template for adding handshake upcall support for other kernel transport layer security providers. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
- Loading branch information
Chuck Lever
authored and
Jakub Kicinski
committed
Apr 20, 2023
1 parent
3b3009e
commit 2fd5532
Showing
10 changed files
with
689 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,7 @@ Contents: | |
scaling | ||
tls | ||
tls-offload | ||
tls-handshake | ||
nfc | ||
6lowpan | ||
6pack | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
.. SPDX-License-Identifier: GPL-2.0 | ||
======================= | ||
In-Kernel TLS Handshake | ||
======================= | ||
|
||
Overview | ||
======== | ||
|
||
Transport Layer Security (TLS) is a Upper Layer Protocol (ULP) that runs | ||
over TCP. TLS provides end-to-end data integrity and confidentiality in | ||
addition to peer authentication. | ||
|
||
The kernel's kTLS implementation handles the TLS record subprotocol, but | ||
does not handle the TLS handshake subprotocol which is used to establish | ||
a TLS session. Kernel consumers can use the API described here to | ||
request TLS session establishment. | ||
|
||
There are several possible ways to provide a handshake service in the | ||
kernel. The API described here is designed to hide the details of those | ||
implementations so that in-kernel TLS consumers do not need to be | ||
aware of how the handshake gets done. | ||
|
||
|
||
User handshake agent | ||
==================== | ||
|
||
As of this writing, there is no TLS handshake implementation in the | ||
Linux kernel. To provide a handshake service, a handshake agent | ||
(typically in user space) is started in each network namespace where a | ||
kernel consumer might require a TLS handshake. Handshake agents listen | ||
for events sent from the kernel that indicate a handshake request is | ||
waiting. | ||
|
||
An open socket is passed to a handshake agent via a netlink operation, | ||
which creates a socket descriptor in the agent's file descriptor table. | ||
If the handshake completes successfully, the handshake agent promotes | ||
the socket to use the TLS ULP and sets the session information using the | ||
SOL_TLS socket options. The handshake agent returns the socket to the | ||
kernel via a second netlink operation. | ||
|
||
|
||
Kernel Handshake API | ||
==================== | ||
|
||
A kernel TLS consumer initiates a client-side TLS handshake on an open | ||
socket by invoking one of the tls_client_hello() functions. First, it | ||
fills in a structure that contains the parameters of the request: | ||
|
||
.. code-block:: c | ||
struct tls_handshake_args { | ||
struct socket *ta_sock; | ||
tls_done_func_t ta_done; | ||
void *ta_data; | ||
unsigned int ta_timeout_ms; | ||
key_serial_t ta_keyring; | ||
key_serial_t ta_my_cert; | ||
key_serial_t ta_my_privkey; | ||
unsigned int ta_num_peerids; | ||
key_serial_t ta_my_peerids[5]; | ||
}; | ||
The @ta_sock field references an open and connected socket. The consumer | ||
must hold a reference on the socket to prevent it from being destroyed | ||
while the handshake is in progress. The consumer must also have | ||
instantiated a struct file in sock->file. | ||
|
||
|
||
@ta_done contains a callback function that is invoked when the handshake | ||
has completed. Further explanation of this function is in the "Handshake | ||
Completion" sesction below. | ||
|
||
The consumer can fill in the @ta_timeout_ms field to force the servicing | ||
handshake agent to exit after a number of milliseconds. This enables the | ||
socket to be fully closed once both the kernel and the handshake agent | ||
have closed their endpoints. | ||
|
||
Authentication material such as x.509 certificates, private certificate | ||
keys, and pre-shared keys are provided to the handshake agent in keys | ||
that are instantiated by the consumer before making the handshake | ||
request. The consumer can provide a private keyring that is linked into | ||
the handshake agent's process keyring in the @ta_keyring field to prevent | ||
access of those keys by other subsystems. | ||
|
||
To request an x.509-authenticated TLS session, the consumer fills in | ||
the @ta_my_cert and @ta_my_privkey fields with the serial numbers of | ||
keys containing an x.509 certificate and the private key for that | ||
certificate. Then, it invokes this function: | ||
|
||
.. code-block:: c | ||
ret = tls_client_hello_x509(args, gfp_flags); | ||
The function returns zero when the handshake request is under way. A | ||
zero return guarantees the callback function @ta_done will be invoked | ||
for this socket. The function returns a negative errno if the handshake | ||
could not be started. A negative errno guarantees the callback function | ||
@ta_done will not be invoked on this socket. | ||
|
||
|
||
To initiate a client-side TLS handshake with a pre-shared key, use: | ||
|
||
.. code-block:: c | ||
ret = tls_client_hello_psk(args, gfp_flags); | ||
However, in this case, the consumer fills in the @ta_my_peerids array | ||
with serial numbers of keys containing the peer identities it wishes | ||
to offer, and the @ta_num_peerids field with the number of array | ||
entries it has filled in. The other fields are filled in as above. | ||
|
||
|
||
To initiate an anonymous client-side TLS handshake use: | ||
|
||
.. code-block:: c | ||
ret = tls_client_hello_anon(args, gfp_flags); | ||
The handshake agent presents no peer identity information to the remote | ||
during this type of handshake. Only server authentication (ie the client | ||
verifies the server's identity) is performed during the handshake. Thus | ||
the established session uses encryption only. | ||
|
||
|
||
Consumers that are in-kernel servers use: | ||
|
||
.. code-block:: c | ||
ret = tls_server_hello_x509(args, gfp_flags); | ||
or | ||
|
||
.. code-block:: c | ||
ret = tls_server_hello_psk(args, gfp_flags); | ||
The argument structure is filled in as above. | ||
|
||
|
||
If the consumer needs to cancel the handshake request, say, due to a ^C | ||
or other exigent event, the consumer can invoke: | ||
|
||
.. code-block:: c | ||
bool tls_handshake_cancel(sock); | ||
This function returns true if the handshake request associated with | ||
@sock has been canceled. The consumer's handshake completion callback | ||
will not be invoked. If this function returns false, then the consumer's | ||
completion callback has already been invoked. | ||
|
||
|
||
Handshake Completion | ||
==================== | ||
|
||
When the handshake agent has completed processing, it notifies the | ||
kernel that the socket may be used by the consumer again. At this point, | ||
the consumer's handshake completion callback, provided in the @ta_done | ||
field in the tls_handshake_args structure, is invoked. | ||
|
||
The synopsis of this function is: | ||
|
||
.. code-block:: c | ||
typedef void (*tls_done_func_t)(void *data, int status, | ||
key_serial_t peerid); | ||
The consumer provides a cookie in the @ta_data field of the | ||
tls_handshake_args structure that is returned in the @data parameter of | ||
this callback. The consumer uses the cookie to match the callback to the | ||
thread waiting for the handshake to complete. | ||
|
||
The success status of the handshake is returned via the @status | ||
parameter: | ||
|
||
+------------+----------------------------------------------+ | ||
| status | meaning | | ||
+============+==============================================+ | ||
| 0 | TLS session established successfully | | ||
+------------+----------------------------------------------+ | ||
| -EACCESS | Remote peer rejected the handshake or | | ||
| | authentication failed | | ||
+------------+----------------------------------------------+ | ||
| -ENOMEM | Temporary resource allocation failure | | ||
+------------+----------------------------------------------+ | ||
| -EINVAL | Consumer provided an invalid argument | | ||
+------------+----------------------------------------------+ | ||
| -ENOKEY | Missing authentication material | | ||
+------------+----------------------------------------------+ | ||
| -EIO | An unexpected fault occurred | | ||
+------------+----------------------------------------------+ | ||
|
||
The @peerid parameter contains the serial number of a key containing the | ||
remote peer's identity or the value TLS_NO_PEERID if the session is not | ||
authenticated. | ||
|
||
A best practice is to close and destroy the socket immediately if the | ||
handshake failed. | ||
|
||
|
||
Other considerations | ||
-------------------- | ||
|
||
While a handshake is under way, the kernel consumer must alter the | ||
socket's sk_data_ready callback function to ignore all incoming data. | ||
Once the handshake completion callback function has been invoked, normal | ||
receive operation can be resumed. | ||
|
||
Once a TLS session is established, the consumer must provide a buffer | ||
for and then examine the control message (CMSG) that is part of every | ||
subsequent sock_recvmsg(). Each control message indicates whether the | ||
received message data is TLS record data or session metadata. | ||
|
||
See tls.rst for details on how a kTLS consumer recognizes incoming | ||
(decrypted) application data, alerts, and handshake packets once the | ||
socket has been promoted to use the TLS ULP. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-only */ | ||
/* | ||
* Generic netlink HANDSHAKE service. | ||
* | ||
* Author: Chuck Lever <chuck.lever@oracle.com> | ||
* | ||
* Copyright (c) 2023, Oracle and/or its affiliates. | ||
*/ | ||
|
||
#ifndef _NET_HANDSHAKE_H | ||
#define _NET_HANDSHAKE_H | ||
|
||
enum { | ||
TLS_NO_KEYRING = 0, | ||
TLS_NO_PEERID = 0, | ||
TLS_NO_CERT = 0, | ||
TLS_NO_PRIVKEY = 0, | ||
}; | ||
|
||
typedef void (*tls_done_func_t)(void *data, int status, | ||
key_serial_t peerid); | ||
|
||
struct tls_handshake_args { | ||
struct socket *ta_sock; | ||
tls_done_func_t ta_done; | ||
void *ta_data; | ||
unsigned int ta_timeout_ms; | ||
key_serial_t ta_keyring; | ||
key_serial_t ta_my_cert; | ||
key_serial_t ta_my_privkey; | ||
unsigned int ta_num_peerids; | ||
key_serial_t ta_my_peerids[5]; | ||
}; | ||
|
||
int tls_client_hello_anon(const struct tls_handshake_args *args, gfp_t flags); | ||
int tls_client_hello_x509(const struct tls_handshake_args *args, gfp_t flags); | ||
int tls_client_hello_psk(const struct tls_handshake_args *args, gfp_t flags); | ||
int tls_server_hello_x509(const struct tls_handshake_args *args, gfp_t flags); | ||
int tls_server_hello_psk(const struct tls_handshake_args *args, gfp_t flags); | ||
|
||
bool tls_handshake_cancel(struct sock *sk); | ||
|
||
#endif /* _NET_HANDSHAKE_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.