GNU Linux-libre 6.8.7-gnu
[releases.git] / net / handshake / tlshd.c
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(info->nlhdr, GENL_HDRLEN);
65         int rem, len = nlmsg_attrlen(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(HANDSHAKE_F_REQ_SESSION, &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                                               KEY_LOOKUP_CREATE,
122                                               KEY_NEED_WRITE);
123         if (IS_ERR(process_keyring_ref)) {
124                 ret = PTR_ERR(process_keyring_ref);
125                 goto out;
126         }
127
128         keyring_ref = lookup_user_key(treq->th_keyring, KEY_LOOKUP_CREATE,
129                                       KEY_NEED_LINK);
130         if (IS_ERR(keyring_ref)) {
131                 ret = PTR_ERR(keyring_ref);
132                 goto out_put_key;
133         }
134
135         ret = key_link(key_ref_to_ptr(process_keyring_ref),
136                        key_ref_to_ptr(keyring_ref));
137
138         key_ref_put(keyring_ref);
139 out_put_key:
140         key_ref_put(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(msg, HANDSHAKE_A_ACCEPT_PEER_IDENTITY,
158                                 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(msg, HANDSHAKE_A_ACCEPT_CERTIFICATE);
173         if (!entry_attr)
174                 return -EMSGSIZE;
175
176         if (nla_put_s32(msg, HANDSHAKE_A_X509_CERT,
177                         treq->th_certificate) ||
178             nla_put_s32(msg, HANDSHAKE_A_X509_PRIVKEY,
179                         treq->th_privkey)) {
180                 nla_nest_cancel(msg, entry_attr);
181                 return -EMSGSIZE;
182         }
183
184         nla_nest_end(msg, 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(msg, HANDSHAKE_A_ACCEPT_SOCKFD, fd);
218         if (ret < 0)
219                 goto out_cancel;
220         ret = nla_put_u32(msg, HANDSHAKE_A_ACCEPT_MESSAGE_TYPE, treq->th_type);
221         if (ret < 0)
222                 goto out_cancel;
223         if (treq->th_peername) {
224                 ret = nla_put_string(msg, HANDSHAKE_A_ACCEPT_PEERNAME,
225                                      treq->th_peername);
226                 if (ret < 0)
227                         goto out_cancel;
228         }
229         if (treq->th_timeout_ms) {
230                 ret = nla_put_u32(msg, HANDSHAKE_A_ACCEPT_TIMEOUT, treq->th_timeout_ms);
231                 if (ret < 0)
232                         goto out_cancel;
233         }
234
235         ret = nla_put_u32(msg, HANDSHAKE_A_ACCEPT_AUTH_MODE,
236                           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(msg, hdr);
253         return genlmsg_reply(msg, info);
254
255 out_cancel:
256         genlmsg_cancel(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(&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(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(&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(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(&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(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(&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(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(&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(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(sock->sk);
442         if (!req)
443                 return;
444         if (!test_and_clear_bit(HANDSHAKE_F_REQ_SESSION, &req->hr_flags))
445                 return;
446         tls_alert_send(sock, TLS_ALERT_LEVEL_WARNING,
447                        TLS_ALERT_DESC_CLOSE_NOTIFY);
448 }
449 EXPORT_SYMBOL(tls_handshake_close);