Commit | Line | Data |
---|---|---|
2fd55320 CL |
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. |