Skip to content

Commit 2fd5532

Browse files
chuckleverkuba-moo
authored andcommitted
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 <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 3b3009e commit 2fd5532

File tree

10 files changed

+689
-3
lines changed

10 files changed

+689
-3
lines changed

Documentation/netlink/specs/handshake.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ definitions:
1616
type: enum
1717
name: handler-class
1818
value-start: 0
19-
entries: [ none, max ]
19+
entries: [ none, tlshd, max ]
2020
-
2121
type: enum
2222
name: msg-type
@@ -120,3 +120,5 @@ mcast-groups:
120120
list:
121121
-
122122
name: none
123+
-
124+
name: tlshd

Documentation/networking/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Contents:
3636
scaling
3737
tls
3838
tls-offload
39+
tls-handshake
3940
nfc
4041
6lowpan
4142
6pack
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
=======================
4+
In-Kernel TLS Handshake
5+
=======================
6+
7+
Overview
8+
========
9+
10+
Transport Layer Security (TLS) is a Upper Layer Protocol (ULP) that runs
11+
over TCP. TLS provides end-to-end data integrity and confidentiality in
12+
addition to peer authentication.
13+
14+
The kernel's kTLS implementation handles the TLS record subprotocol, but
15+
does not handle the TLS handshake subprotocol which is used to establish
16+
a TLS session. Kernel consumers can use the API described here to
17+
request TLS session establishment.
18+
19+
There are several possible ways to provide a handshake service in the
20+
kernel. The API described here is designed to hide the details of those
21+
implementations so that in-kernel TLS consumers do not need to be
22+
aware of how the handshake gets done.
23+
24+
25+
User handshake agent
26+
====================
27+
28+
As of this writing, there is no TLS handshake implementation in the
29+
Linux kernel. To provide a handshake service, a handshake agent
30+
(typically in user space) is started in each network namespace where a
31+
kernel consumer might require a TLS handshake. Handshake agents listen
32+
for events sent from the kernel that indicate a handshake request is
33+
waiting.
34+
35+
An open socket is passed to a handshake agent via a netlink operation,
36+
which creates a socket descriptor in the agent's file descriptor table.
37+
If the handshake completes successfully, the handshake agent promotes
38+
the socket to use the TLS ULP and sets the session information using the
39+
SOL_TLS socket options. The handshake agent returns the socket to the
40+
kernel via a second netlink operation.
41+
42+
43+
Kernel Handshake API
44+
====================
45+
46+
A kernel TLS consumer initiates a client-side TLS handshake on an open
47+
socket by invoking one of the tls_client_hello() functions. First, it
48+
fills in a structure that contains the parameters of the request:
49+
50+
.. code-block:: c
51+
52+
struct tls_handshake_args {
53+
struct socket *ta_sock;
54+
tls_done_func_t ta_done;
55+
void *ta_data;
56+
unsigned int ta_timeout_ms;
57+
key_serial_t ta_keyring;
58+
key_serial_t ta_my_cert;
59+
key_serial_t ta_my_privkey;
60+
unsigned int ta_num_peerids;
61+
key_serial_t ta_my_peerids[5];
62+
};
63+
64+
The @ta_sock field references an open and connected socket. The consumer
65+
must hold a reference on the socket to prevent it from being destroyed
66+
while the handshake is in progress. The consumer must also have
67+
instantiated a struct file in sock->file.
68+
69+
70+
@ta_done contains a callback function that is invoked when the handshake
71+
has completed. Further explanation of this function is in the "Handshake
72+
Completion" sesction below.
73+
74+
The consumer can fill in the @ta_timeout_ms field to force the servicing
75+
handshake agent to exit after a number of milliseconds. This enables the
76+
socket to be fully closed once both the kernel and the handshake agent
77+
have closed their endpoints.
78+
79+
Authentication material such as x.509 certificates, private certificate
80+
keys, and pre-shared keys are provided to the handshake agent in keys
81+
that are instantiated by the consumer before making the handshake
82+
request. The consumer can provide a private keyring that is linked into
83+
the handshake agent's process keyring in the @ta_keyring field to prevent
84+
access of those keys by other subsystems.
85+
86+
To request an x.509-authenticated TLS session, the consumer fills in
87+
the @ta_my_cert and @ta_my_privkey fields with the serial numbers of
88+
keys containing an x.509 certificate and the private key for that
89+
certificate. Then, it invokes this function:
90+
91+
.. code-block:: c
92+
93+
ret = tls_client_hello_x509(args, gfp_flags);
94+
95+
The function returns zero when the handshake request is under way. A
96+
zero return guarantees the callback function @ta_done will be invoked
97+
for this socket. The function returns a negative errno if the handshake
98+
could not be started. A negative errno guarantees the callback function
99+
@ta_done will not be invoked on this socket.
100+
101+
102+
To initiate a client-side TLS handshake with a pre-shared key, use:
103+
104+
.. code-block:: c
105+
106+
ret = tls_client_hello_psk(args, gfp_flags);
107+
108+
However, in this case, the consumer fills in the @ta_my_peerids array
109+
with serial numbers of keys containing the peer identities it wishes
110+
to offer, and the @ta_num_peerids field with the number of array
111+
entries it has filled in. The other fields are filled in as above.
112+
113+
114+
To initiate an anonymous client-side TLS handshake use:
115+
116+
.. code-block:: c
117+
118+
ret = tls_client_hello_anon(args, gfp_flags);
119+
120+
The handshake agent presents no peer identity information to the remote
121+
during this type of handshake. Only server authentication (ie the client
122+
verifies the server's identity) is performed during the handshake. Thus
123+
the established session uses encryption only.
124+
125+
126+
Consumers that are in-kernel servers use:
127+
128+
.. code-block:: c
129+
130+
ret = tls_server_hello_x509(args, gfp_flags);
131+
132+
or
133+
134+
.. code-block:: c
135+
136+
ret = tls_server_hello_psk(args, gfp_flags);
137+
138+
The argument structure is filled in as above.
139+
140+
141+
If the consumer needs to cancel the handshake request, say, due to a ^C
142+
or other exigent event, the consumer can invoke:
143+
144+
.. code-block:: c
145+
146+
bool tls_handshake_cancel(sock);
147+
148+
This function returns true if the handshake request associated with
149+
@sock has been canceled. The consumer's handshake completion callback
150+
will not be invoked. If this function returns false, then the consumer's
151+
completion callback has already been invoked.
152+
153+
154+
Handshake Completion
155+
====================
156+
157+
When the handshake agent has completed processing, it notifies the
158+
kernel that the socket may be used by the consumer again. At this point,
159+
the consumer's handshake completion callback, provided in the @ta_done
160+
field in the tls_handshake_args structure, is invoked.
161+
162+
The synopsis of this function is:
163+
164+
.. code-block:: c
165+
166+
typedef void (*tls_done_func_t)(void *data, int status,
167+
key_serial_t peerid);
168+
169+
The consumer provides a cookie in the @ta_data field of the
170+
tls_handshake_args structure that is returned in the @data parameter of
171+
this callback. The consumer uses the cookie to match the callback to the
172+
thread waiting for the handshake to complete.
173+
174+
The success status of the handshake is returned via the @status
175+
parameter:
176+
177+
+------------+----------------------------------------------+
178+
| status | meaning |
179+
+============+==============================================+
180+
| 0 | TLS session established successfully |
181+
+------------+----------------------------------------------+
182+
| -EACCESS | Remote peer rejected the handshake or |
183+
| | authentication failed |
184+
+------------+----------------------------------------------+
185+
| -ENOMEM | Temporary resource allocation failure |
186+
+------------+----------------------------------------------+
187+
| -EINVAL | Consumer provided an invalid argument |
188+
+------------+----------------------------------------------+
189+
| -ENOKEY | Missing authentication material |
190+
+------------+----------------------------------------------+
191+
| -EIO | An unexpected fault occurred |
192+
+------------+----------------------------------------------+
193+
194+
The @peerid parameter contains the serial number of a key containing the
195+
remote peer's identity or the value TLS_NO_PEERID if the session is not
196+
authenticated.
197+
198+
A best practice is to close and destroy the socket immediately if the
199+
handshake failed.
200+
201+
202+
Other considerations
203+
--------------------
204+
205+
While a handshake is under way, the kernel consumer must alter the
206+
socket's sk_data_ready callback function to ignore all incoming data.
207+
Once the handshake completion callback function has been invoked, normal
208+
receive operation can be resumed.
209+
210+
Once a TLS session is established, the consumer must provide a buffer
211+
for and then examine the control message (CMSG) that is part of every
212+
subsequent sock_recvmsg(). Each control message indicates whether the
213+
received message data is TLS record data or session metadata.
214+
215+
See tls.rst for details on how a kTLS consumer recognizes incoming
216+
(decrypted) application data, alerts, and handshake packets once the
217+
socket has been promoted to use the TLS ULP.

MAINTAINERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8953,6 +8953,8 @@ L: [email protected]
89538953
89548954
S: Maintained
89558955
F: Documentation/netlink/specs/handshake.yaml
8956+
F: Documentation/networking/tls-handshake.rst
8957+
F: include/net/handshake.h
89568958
F: include/trace/events/handshake.h
89578959
F: net/handshake/
89588960

include/net/handshake.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Generic netlink HANDSHAKE service.
4+
*
5+
* Author: Chuck Lever <[email protected]>
6+
*
7+
* Copyright (c) 2023, Oracle and/or its affiliates.
8+
*/
9+
10+
#ifndef _NET_HANDSHAKE_H
11+
#define _NET_HANDSHAKE_H
12+
13+
enum {
14+
TLS_NO_KEYRING = 0,
15+
TLS_NO_PEERID = 0,
16+
TLS_NO_CERT = 0,
17+
TLS_NO_PRIVKEY = 0,
18+
};
19+
20+
typedef void (*tls_done_func_t)(void *data, int status,
21+
key_serial_t peerid);
22+
23+
struct tls_handshake_args {
24+
struct socket *ta_sock;
25+
tls_done_func_t ta_done;
26+
void *ta_data;
27+
unsigned int ta_timeout_ms;
28+
key_serial_t ta_keyring;
29+
key_serial_t ta_my_cert;
30+
key_serial_t ta_my_privkey;
31+
unsigned int ta_num_peerids;
32+
key_serial_t ta_my_peerids[5];
33+
};
34+
35+
int tls_client_hello_anon(const struct tls_handshake_args *args, gfp_t flags);
36+
int tls_client_hello_x509(const struct tls_handshake_args *args, gfp_t flags);
37+
int tls_client_hello_psk(const struct tls_handshake_args *args, gfp_t flags);
38+
int tls_server_hello_x509(const struct tls_handshake_args *args, gfp_t flags);
39+
int tls_server_hello_psk(const struct tls_handshake_args *args, gfp_t flags);
40+
41+
bool tls_handshake_cancel(struct sock *sk);
42+
43+
#endif /* _NET_HANDSHAKE_H */

include/uapi/linux/handshake.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
enum handshake_handler_class {
1313
HANDSHAKE_HANDLER_CLASS_NONE,
14+
HANDSHAKE_HANDLER_CLASS_TLSHD,
1415
HANDSHAKE_HANDLER_CLASS_MAX,
1516
};
1617

@@ -67,5 +68,6 @@ enum {
6768
};
6869

6970
#define HANDSHAKE_MCGRP_NONE "none"
71+
#define HANDSHAKE_MCGRP_TLSHD "tlshd"
7072

7173
#endif /* _UAPI_LINUX_HANDSHAKE_H */

net/handshake/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
#
99

1010
obj-y += handshake.o
11-
handshake-y := genl.o netlink.o request.o trace.o
11+
handshake-y := genl.o netlink.o request.o tlshd.o trace.o

net/handshake/genl.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
/* HANDSHAKE_CMD_ACCEPT - do */
1414
static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HANDLER_CLASS + 1] = {
15-
[HANDSHAKE_A_ACCEPT_HANDLER_CLASS] = NLA_POLICY_MAX(NLA_U32, 1),
15+
[HANDSHAKE_A_ACCEPT_HANDLER_CLASS] = NLA_POLICY_MAX(NLA_U32, 2),
1616
};
1717

1818
/* HANDSHAKE_CMD_DONE - do */
@@ -42,6 +42,7 @@ static const struct genl_split_ops handshake_nl_ops[] = {
4242

4343
static const struct genl_multicast_group handshake_nl_mcgrps[] = {
4444
[HANDSHAKE_NLGRP_NONE] = { "none", },
45+
[HANDSHAKE_NLGRP_TLSHD] = { "tlshd", },
4546
};
4647

4748
struct genl_family handshake_nl_family __ro_after_init = {

net/handshake/genl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info);
1616

1717
enum {
1818
HANDSHAKE_NLGRP_NONE,
19+
HANDSHAKE_NLGRP_TLSHD,
1920
};
2021

2122
extern struct genl_family handshake_nl_family;

0 commit comments

Comments
 (0)