1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Establish a TLS session for a kernel socket consumer |
4 | * using the tlshd user space handler. |
5 | * |
6 | * Author: Chuck Lever <chuck.lever@oracle.com> |
7 | * |
8 | * Copyright (c) 2021-2023, Oracle and/or its affiliates. |
9 | */ |
10 | |
11 | #include <linux/types.h> |
12 | #include <linux/socket.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/module.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/key.h> |
17 | |
18 | #include <net/sock.h> |
19 | #include <net/handshake.h> |
20 | #include <net/genetlink.h> |
21 | #include <net/tls_prot.h> |
22 | |
23 | #include <uapi/linux/keyctl.h> |
24 | #include <uapi/linux/handshake.h> |
25 | #include "handshake.h" |
26 | |
27 | struct tls_handshake_req { |
28 | void (*th_consumer_done)(void *data, int status, |
29 | key_serial_t peerid); |
30 | void *th_consumer_data; |
31 | |
32 | int th_type; |
33 | unsigned int th_timeout_ms; |
34 | int th_auth_mode; |
35 | const char *th_peername; |
36 | key_serial_t th_keyring; |
37 | key_serial_t th_certificate; |
38 | key_serial_t th_privkey; |
39 | |
40 | unsigned int th_num_peerids; |
41 | key_serial_t th_peerid[5]; |
42 | }; |
43 | |
44 | static struct tls_handshake_req * |
45 | tls_handshake_req_init(struct handshake_req *req, |
46 | const struct tls_handshake_args *args) |
47 | { |
48 | struct tls_handshake_req *treq = handshake_req_private(req); |
49 | |
50 | treq->th_timeout_ms = args->ta_timeout_ms; |
51 | treq->th_consumer_done = args->ta_done; |
52 | treq->th_consumer_data = args->ta_data; |
53 | treq->th_peername = args->ta_peername; |
54 | treq->th_keyring = args->ta_keyring; |
55 | treq->th_num_peerids = 0; |
56 | treq->th_certificate = TLS_NO_CERT; |
57 | treq->th_privkey = TLS_NO_PRIVKEY; |
58 | return treq; |
59 | } |
60 | |
61 | static void tls_handshake_remote_peerids(struct tls_handshake_req *treq, |
62 | struct genl_info *info) |
63 | { |
64 | struct nlattr *head = nlmsg_attrdata(nlh: info->nlhdr, GENL_HDRLEN); |
65 | int rem, len = nlmsg_attrlen(nlh: info->nlhdr, GENL_HDRLEN); |
66 | struct nlattr *nla; |
67 | unsigned int i; |
68 | |
69 | i = 0; |
70 | nla_for_each_attr(nla, head, len, rem) { |
71 | if (nla_type(nla) == HANDSHAKE_A_DONE_REMOTE_AUTH) |
72 | i++; |
73 | } |
74 | if (!i) |
75 | return; |
76 | treq->th_num_peerids = min_t(unsigned int, i, |
77 | ARRAY_SIZE(treq->th_peerid)); |
78 | |
79 | i = 0; |
80 | nla_for_each_attr(nla, head, len, rem) { |
81 | if (nla_type(nla) == HANDSHAKE_A_DONE_REMOTE_AUTH) |
82 | treq->th_peerid[i++] = nla_get_u32(nla); |
83 | if (i >= treq->th_num_peerids) |
84 | break; |
85 | } |
86 | } |
87 | |
88 | /** |
89 | * tls_handshake_done - callback to handle a CMD_DONE request |
90 | * @req: socket on which the handshake was performed |
91 | * @status: session status code |
92 | * @info: full results of session establishment |
93 | * |
94 | */ |
95 | static void tls_handshake_done(struct handshake_req *req, |
96 | unsigned int status, struct genl_info *info) |
97 | { |
98 | struct tls_handshake_req *treq = handshake_req_private(req); |
99 | |
100 | treq->th_peerid[0] = TLS_NO_PEERID; |
101 | if (info) |
102 | tls_handshake_remote_peerids(treq, info); |
103 | |
104 | if (!status) |
105 | set_bit(nr: HANDSHAKE_F_REQ_SESSION, addr: &req->hr_flags); |
106 | |
107 | treq->th_consumer_done(treq->th_consumer_data, -status, |
108 | treq->th_peerid[0]); |
109 | } |
110 | |
111 | #if IS_ENABLED(CONFIG_KEYS) |
112 | static int tls_handshake_private_keyring(struct tls_handshake_req *treq) |
113 | { |
114 | key_ref_t process_keyring_ref, keyring_ref; |
115 | int ret; |
116 | |
117 | if (treq->th_keyring == TLS_NO_KEYRING) |
118 | return 0; |
119 | |
120 | process_keyring_ref = lookup_user_key(KEY_SPEC_PROCESS_KEYRING, |
121 | flags: KEY_LOOKUP_CREATE, |
122 | need_perm: KEY_NEED_WRITE); |
123 | if (IS_ERR(ptr: process_keyring_ref)) { |
124 | ret = PTR_ERR(ptr: process_keyring_ref); |
125 | goto out; |
126 | } |
127 | |
128 | keyring_ref = lookup_user_key(id: treq->th_keyring, flags: KEY_LOOKUP_CREATE, |
129 | need_perm: KEY_NEED_LINK); |
130 | if (IS_ERR(ptr: keyring_ref)) { |
131 | ret = PTR_ERR(ptr: keyring_ref); |
132 | goto out_put_key; |
133 | } |
134 | |
135 | ret = key_link(keyring: key_ref_to_ptr(key_ref: process_keyring_ref), |
136 | key: key_ref_to_ptr(key_ref: keyring_ref)); |
137 | |
138 | key_ref_put(key_ref: keyring_ref); |
139 | out_put_key: |
140 | key_ref_put(key_ref: process_keyring_ref); |
141 | out: |
142 | return ret; |
143 | } |
144 | #else |
145 | static int tls_handshake_private_keyring(struct tls_handshake_req *treq) |
146 | { |
147 | return 0; |
148 | } |
149 | #endif |
150 | |
151 | static int tls_handshake_put_peer_identity(struct sk_buff *msg, |
152 | struct tls_handshake_req *treq) |
153 | { |
154 | unsigned int i; |
155 | |
156 | for (i = 0; i < treq->th_num_peerids; i++) |
157 | if (nla_put_u32(skb: msg, attrtype: HANDSHAKE_A_ACCEPT_PEER_IDENTITY, |
158 | value: treq->th_peerid[i]) < 0) |
159 | return -EMSGSIZE; |
160 | return 0; |
161 | } |
162 | |
163 | static int tls_handshake_put_certificate(struct sk_buff *msg, |
164 | struct tls_handshake_req *treq) |
165 | { |
166 | struct nlattr *entry_attr; |
167 | |
168 | if (treq->th_certificate == TLS_NO_CERT && |
169 | treq->th_privkey == TLS_NO_PRIVKEY) |
170 | return 0; |
171 | |
172 | entry_attr = nla_nest_start(skb: msg, attrtype: HANDSHAKE_A_ACCEPT_CERTIFICATE); |
173 | if (!entry_attr) |
174 | return -EMSGSIZE; |
175 | |
176 | if (nla_put_s32(skb: msg, attrtype: HANDSHAKE_A_X509_CERT, |
177 | value: treq->th_certificate) || |
178 | nla_put_s32(skb: msg, attrtype: HANDSHAKE_A_X509_PRIVKEY, |
179 | value: treq->th_privkey)) { |
180 | nla_nest_cancel(skb: msg, start: entry_attr); |
181 | return -EMSGSIZE; |
182 | } |
183 | |
184 | nla_nest_end(skb: msg, start: entry_attr); |
185 | return 0; |
186 | } |
187 | |
188 | /** |
189 | * tls_handshake_accept - callback to construct a CMD_ACCEPT response |
190 | * @req: handshake parameters to return |
191 | * @info: generic netlink message context |
192 | * @fd: file descriptor to be returned |
193 | * |
194 | * Returns zero on success, or a negative errno on failure. |
195 | */ |
196 | static int tls_handshake_accept(struct handshake_req *req, |
197 | struct genl_info *info, int fd) |
198 | { |
199 | struct tls_handshake_req *treq = handshake_req_private(req); |
200 | struct nlmsghdr *hdr; |
201 | struct sk_buff *msg; |
202 | int ret; |
203 | |
204 | ret = tls_handshake_private_keyring(treq); |
205 | if (ret < 0) |
206 | goto out; |
207 | |
208 | ret = -ENOMEM; |
209 | msg = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); |
210 | if (!msg) |
211 | goto out; |
212 | hdr = handshake_genl_put(msg, info); |
213 | if (!hdr) |
214 | goto out_cancel; |
215 | |
216 | ret = -EMSGSIZE; |
217 | ret = nla_put_s32(skb: msg, attrtype: HANDSHAKE_A_ACCEPT_SOCKFD, value: fd); |
218 | if (ret < 0) |
219 | goto out_cancel; |
220 | ret = nla_put_u32(skb: msg, attrtype: HANDSHAKE_A_ACCEPT_MESSAGE_TYPE, value: treq->th_type); |
221 | if (ret < 0) |
222 | goto out_cancel; |
223 | if (treq->th_peername) { |
224 | ret = nla_put_string(skb: msg, attrtype: HANDSHAKE_A_ACCEPT_PEERNAME, |
225 | str: treq->th_peername); |
226 | if (ret < 0) |
227 | goto out_cancel; |
228 | } |
229 | if (treq->th_timeout_ms) { |
230 | ret = nla_put_u32(skb: msg, attrtype: HANDSHAKE_A_ACCEPT_TIMEOUT, value: treq->th_timeout_ms); |
231 | if (ret < 0) |
232 | goto out_cancel; |
233 | } |
234 | |
235 | ret = nla_put_u32(skb: msg, attrtype: HANDSHAKE_A_ACCEPT_AUTH_MODE, |
236 | value: treq->th_auth_mode); |
237 | if (ret < 0) |
238 | goto out_cancel; |
239 | switch (treq->th_auth_mode) { |
240 | case HANDSHAKE_AUTH_PSK: |
241 | ret = tls_handshake_put_peer_identity(msg, treq); |
242 | if (ret < 0) |
243 | goto out_cancel; |
244 | break; |
245 | case HANDSHAKE_AUTH_X509: |
246 | ret = tls_handshake_put_certificate(msg, treq); |
247 | if (ret < 0) |
248 | goto out_cancel; |
249 | break; |
250 | } |
251 | |
252 | genlmsg_end(skb: msg, hdr); |
253 | return genlmsg_reply(skb: msg, info); |
254 | |
255 | out_cancel: |
256 | genlmsg_cancel(skb: msg, hdr); |
257 | out: |
258 | return ret; |
259 | } |
260 | |
261 | static const struct handshake_proto tls_handshake_proto = { |
262 | .hp_handler_class = HANDSHAKE_HANDLER_CLASS_TLSHD, |
263 | .hp_privsize = sizeof(struct tls_handshake_req), |
264 | .hp_flags = BIT(HANDSHAKE_F_PROTO_NOTIFY), |
265 | |
266 | .hp_accept = tls_handshake_accept, |
267 | .hp_done = tls_handshake_done, |
268 | }; |
269 | |
270 | /** |
271 | * tls_client_hello_anon - request an anonymous TLS handshake on a socket |
272 | * @args: socket and handshake parameters for this request |
273 | * @flags: memory allocation control flags |
274 | * |
275 | * Return values: |
276 | * %0: Handshake request enqueue; ->done will be called when complete |
277 | * %-ESRCH: No user agent is available |
278 | * %-ENOMEM: Memory allocation failed |
279 | */ |
280 | int tls_client_hello_anon(const struct tls_handshake_args *args, gfp_t flags) |
281 | { |
282 | struct tls_handshake_req *treq; |
283 | struct handshake_req *req; |
284 | |
285 | req = handshake_req_alloc(proto: &tls_handshake_proto, flags); |
286 | if (!req) |
287 | return -ENOMEM; |
288 | treq = tls_handshake_req_init(req, args); |
289 | treq->th_type = HANDSHAKE_MSG_TYPE_CLIENTHELLO; |
290 | treq->th_auth_mode = HANDSHAKE_AUTH_UNAUTH; |
291 | |
292 | return handshake_req_submit(sock: args->ta_sock, req, flags); |
293 | } |
294 | EXPORT_SYMBOL(tls_client_hello_anon); |
295 | |
296 | /** |
297 | * tls_client_hello_x509 - request an x.509-based TLS handshake on a socket |
298 | * @args: socket and handshake parameters for this request |
299 | * @flags: memory allocation control flags |
300 | * |
301 | * Return values: |
302 | * %0: Handshake request enqueue; ->done will be called when complete |
303 | * %-ESRCH: No user agent is available |
304 | * %-ENOMEM: Memory allocation failed |
305 | */ |
306 | int tls_client_hello_x509(const struct tls_handshake_args *args, gfp_t flags) |
307 | { |
308 | struct tls_handshake_req *treq; |
309 | struct handshake_req *req; |
310 | |
311 | req = handshake_req_alloc(proto: &tls_handshake_proto, flags); |
312 | if (!req) |
313 | return -ENOMEM; |
314 | treq = tls_handshake_req_init(req, args); |
315 | treq->th_type = HANDSHAKE_MSG_TYPE_CLIENTHELLO; |
316 | treq->th_auth_mode = HANDSHAKE_AUTH_X509; |
317 | treq->th_certificate = args->ta_my_cert; |
318 | treq->th_privkey = args->ta_my_privkey; |
319 | |
320 | return handshake_req_submit(sock: args->ta_sock, req, flags); |
321 | } |
322 | EXPORT_SYMBOL(tls_client_hello_x509); |
323 | |
324 | /** |
325 | * tls_client_hello_psk - request a PSK-based TLS handshake on a socket |
326 | * @args: socket and handshake parameters for this request |
327 | * @flags: memory allocation control flags |
328 | * |
329 | * Return values: |
330 | * %0: Handshake request enqueue; ->done will be called when complete |
331 | * %-EINVAL: Wrong number of local peer IDs |
332 | * %-ESRCH: No user agent is available |
333 | * %-ENOMEM: Memory allocation failed |
334 | */ |
335 | int tls_client_hello_psk(const struct tls_handshake_args *args, gfp_t flags) |
336 | { |
337 | struct tls_handshake_req *treq; |
338 | struct handshake_req *req; |
339 | unsigned int i; |
340 | |
341 | if (!args->ta_num_peerids || |
342 | args->ta_num_peerids > ARRAY_SIZE(treq->th_peerid)) |
343 | return -EINVAL; |
344 | |
345 | req = handshake_req_alloc(proto: &tls_handshake_proto, flags); |
346 | if (!req) |
347 | return -ENOMEM; |
348 | treq = tls_handshake_req_init(req, args); |
349 | treq->th_type = HANDSHAKE_MSG_TYPE_CLIENTHELLO; |
350 | treq->th_auth_mode = HANDSHAKE_AUTH_PSK; |
351 | treq->th_num_peerids = args->ta_num_peerids; |
352 | for (i = 0; i < args->ta_num_peerids; i++) |
353 | treq->th_peerid[i] = args->ta_my_peerids[i]; |
354 | |
355 | return handshake_req_submit(sock: args->ta_sock, req, flags); |
356 | } |
357 | EXPORT_SYMBOL(tls_client_hello_psk); |
358 | |
359 | /** |
360 | * tls_server_hello_x509 - request a server TLS handshake on a socket |
361 | * @args: socket and handshake parameters for this request |
362 | * @flags: memory allocation control flags |
363 | * |
364 | * Return values: |
365 | * %0: Handshake request enqueue; ->done will be called when complete |
366 | * %-ESRCH: No user agent is available |
367 | * %-ENOMEM: Memory allocation failed |
368 | */ |
369 | int tls_server_hello_x509(const struct tls_handshake_args *args, gfp_t flags) |
370 | { |
371 | struct tls_handshake_req *treq; |
372 | struct handshake_req *req; |
373 | |
374 | req = handshake_req_alloc(proto: &tls_handshake_proto, flags); |
375 | if (!req) |
376 | return -ENOMEM; |
377 | treq = tls_handshake_req_init(req, args); |
378 | treq->th_type = HANDSHAKE_MSG_TYPE_SERVERHELLO; |
379 | treq->th_auth_mode = HANDSHAKE_AUTH_X509; |
380 | treq->th_certificate = args->ta_my_cert; |
381 | treq->th_privkey = args->ta_my_privkey; |
382 | |
383 | return handshake_req_submit(sock: args->ta_sock, req, flags); |
384 | } |
385 | EXPORT_SYMBOL(tls_server_hello_x509); |
386 | |
387 | /** |
388 | * tls_server_hello_psk - request a server TLS handshake on a socket |
389 | * @args: socket and handshake parameters for this request |
390 | * @flags: memory allocation control flags |
391 | * |
392 | * Return values: |
393 | * %0: Handshake request enqueue; ->done will be called when complete |
394 | * %-ESRCH: No user agent is available |
395 | * %-ENOMEM: Memory allocation failed |
396 | */ |
397 | int tls_server_hello_psk(const struct tls_handshake_args *args, gfp_t flags) |
398 | { |
399 | struct tls_handshake_req *treq; |
400 | struct handshake_req *req; |
401 | |
402 | req = handshake_req_alloc(proto: &tls_handshake_proto, flags); |
403 | if (!req) |
404 | return -ENOMEM; |
405 | treq = tls_handshake_req_init(req, args); |
406 | treq->th_type = HANDSHAKE_MSG_TYPE_SERVERHELLO; |
407 | treq->th_auth_mode = HANDSHAKE_AUTH_PSK; |
408 | treq->th_num_peerids = 1; |
409 | treq->th_peerid[0] = args->ta_my_peerids[0]; |
410 | |
411 | return handshake_req_submit(sock: args->ta_sock, req, flags); |
412 | } |
413 | EXPORT_SYMBOL(tls_server_hello_psk); |
414 | |
415 | /** |
416 | * tls_handshake_cancel - cancel a pending handshake |
417 | * @sk: socket on which there is an ongoing handshake |
418 | * |
419 | * Request cancellation races with request completion. To determine |
420 | * who won, callers examine the return value from this function. |
421 | * |
422 | * Return values: |
423 | * %true - Uncompleted handshake request was canceled |
424 | * %false - Handshake request already completed or not found |
425 | */ |
426 | bool tls_handshake_cancel(struct sock *sk) |
427 | { |
428 | return handshake_req_cancel(sk); |
429 | } |
430 | EXPORT_SYMBOL(tls_handshake_cancel); |
431 | |
432 | /** |
433 | * tls_handshake_close - send a Closure alert |
434 | * @sock: an open socket |
435 | * |
436 | */ |
437 | void tls_handshake_close(struct socket *sock) |
438 | { |
439 | struct handshake_req *req; |
440 | |
441 | req = handshake_req_hash_lookup(sk: sock->sk); |
442 | if (!req) |
443 | return; |
444 | if (!test_and_clear_bit(nr: HANDSHAKE_F_REQ_SESSION, addr: &req->hr_flags)) |
445 | return; |
446 | tls_alert_send(sock, level: TLS_ALERT_LEVEL_WARNING, |
447 | description: TLS_ALERT_DESC_CLOSE_NOTIFY); |
448 | } |
449 | EXPORT_SYMBOL(tls_handshake_close); |
450 | |