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>
This commit is contained in:
parent
3b3009ea8a
commit
2fd5532044
@ -16,7 +16,7 @@ definitions:
|
||||
type: enum
|
||||
name: handler-class
|
||||
value-start: 0
|
||||
entries: [ none, max ]
|
||||
entries: [ none, tlshd, max ]
|
||||
-
|
||||
type: enum
|
||||
name: msg-type
|
||||
@ -120,3 +120,5 @@ mcast-groups:
|
||||
list:
|
||||
-
|
||||
name: none
|
||||
-
|
||||
name: tlshd
|
||||
|
@ -36,6 +36,7 @@ Contents:
|
||||
scaling
|
||||
tls
|
||||
tls-offload
|
||||
tls-handshake
|
||||
nfc
|
||||
6lowpan
|
||||
6pack
|
||||
|
217
Documentation/networking/tls-handshake.rst
Normal file
217
Documentation/networking/tls-handshake.rst
Normal file
@ -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.
|
@ -8953,6 +8953,8 @@ L: kernel-tls-handshake@lists.linux.dev
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/netlink/specs/handshake.yaml
|
||||
F: Documentation/networking/tls-handshake.rst
|
||||
F: include/net/handshake.h
|
||||
F: include/trace/events/handshake.h
|
||||
F: net/handshake/
|
||||
|
||||
|
43
include/net/handshake.h
Normal file
43
include/net/handshake.h
Normal file
@ -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 */
|
@ -11,6 +11,7 @@
|
||||
|
||||
enum handshake_handler_class {
|
||||
HANDSHAKE_HANDLER_CLASS_NONE,
|
||||
HANDSHAKE_HANDLER_CLASS_TLSHD,
|
||||
HANDSHAKE_HANDLER_CLASS_MAX,
|
||||
};
|
||||
|
||||
@ -67,5 +68,6 @@ enum {
|
||||
};
|
||||
|
||||
#define HANDSHAKE_MCGRP_NONE "none"
|
||||
#define HANDSHAKE_MCGRP_TLSHD "tlshd"
|
||||
|
||||
#endif /* _UAPI_LINUX_HANDSHAKE_H */
|
||||
|
@ -8,4 +8,4 @@
|
||||
#
|
||||
|
||||
obj-y += handshake.o
|
||||
handshake-y := genl.o netlink.o request.o trace.o
|
||||
handshake-y := genl.o netlink.o request.o tlshd.o trace.o
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
/* HANDSHAKE_CMD_ACCEPT - do */
|
||||
static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HANDLER_CLASS + 1] = {
|
||||
[HANDSHAKE_A_ACCEPT_HANDLER_CLASS] = NLA_POLICY_MAX(NLA_U32, 1),
|
||||
[HANDSHAKE_A_ACCEPT_HANDLER_CLASS] = NLA_POLICY_MAX(NLA_U32, 2),
|
||||
};
|
||||
|
||||
/* HANDSHAKE_CMD_DONE - do */
|
||||
@ -42,6 +42,7 @@ static const struct genl_split_ops handshake_nl_ops[] = {
|
||||
|
||||
static const struct genl_multicast_group handshake_nl_mcgrps[] = {
|
||||
[HANDSHAKE_NLGRP_NONE] = { "none", },
|
||||
[HANDSHAKE_NLGRP_TLSHD] = { "tlshd", },
|
||||
};
|
||||
|
||||
struct genl_family handshake_nl_family __ro_after_init = {
|
||||
|
@ -16,6 +16,7 @@ int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
|
||||
enum {
|
||||
HANDSHAKE_NLGRP_NONE,
|
||||
HANDSHAKE_NLGRP_TLSHD,
|
||||
};
|
||||
|
||||
extern struct genl_family handshake_nl_family;
|
||||
|
417
net/handshake/tlshd.c
Normal file
417
net/handshake/tlshd.c
Normal file
@ -0,0 +1,417 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Establish a TLS session for a kernel socket consumer
|
||||
* using the tlshd user space handler.
|
||||
*
|
||||
* Author: Chuck Lever <chuck.lever@oracle.com>
|
||||
*
|
||||
* Copyright (c) 2021-2023, Oracle and/or its affiliates.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/key.h>
|
||||
|
||||
#include <net/sock.h>
|
||||
#include <net/handshake.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
#include <uapi/linux/keyctl.h>
|
||||
#include <uapi/linux/handshake.h>
|
||||
#include "handshake.h"
|
||||
|
||||
struct tls_handshake_req {
|
||||
void (*th_consumer_done)(void *data, int status,
|
||||
key_serial_t peerid);
|
||||
void *th_consumer_data;
|
||||
|
||||
int th_type;
|
||||
unsigned int th_timeout_ms;
|
||||
int th_auth_mode;
|
||||
key_serial_t th_keyring;
|
||||
key_serial_t th_certificate;
|
||||
key_serial_t th_privkey;
|
||||
|
||||
unsigned int th_num_peerids;
|
||||
key_serial_t th_peerid[5];
|
||||
};
|
||||
|
||||
static struct tls_handshake_req *
|
||||
tls_handshake_req_init(struct handshake_req *req,
|
||||
const struct tls_handshake_args *args)
|
||||
{
|
||||
struct tls_handshake_req *treq = handshake_req_private(req);
|
||||
|
||||
treq->th_timeout_ms = args->ta_timeout_ms;
|
||||
treq->th_consumer_done = args->ta_done;
|
||||
treq->th_consumer_data = args->ta_data;
|
||||
treq->th_keyring = args->ta_keyring;
|
||||
treq->th_num_peerids = 0;
|
||||
treq->th_certificate = TLS_NO_CERT;
|
||||
treq->th_privkey = TLS_NO_PRIVKEY;
|
||||
return treq;
|
||||
}
|
||||
|
||||
static void tls_handshake_remote_peerids(struct tls_handshake_req *treq,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct nlattr *head = nlmsg_attrdata(info->nlhdr, GENL_HDRLEN);
|
||||
int rem, len = nlmsg_attrlen(info->nlhdr, GENL_HDRLEN);
|
||||
struct nlattr *nla;
|
||||
unsigned int i;
|
||||
|
||||
i = 0;
|
||||
nla_for_each_attr(nla, head, len, rem) {
|
||||
if (nla_type(nla) == HANDSHAKE_A_DONE_REMOTE_AUTH)
|
||||
i++;
|
||||
}
|
||||
if (!i)
|
||||
return;
|
||||
treq->th_num_peerids = min_t(unsigned int, i,
|
||||
ARRAY_SIZE(treq->th_peerid));
|
||||
|
||||
i = 0;
|
||||
nla_for_each_attr(nla, head, len, rem) {
|
||||
if (nla_type(nla) == HANDSHAKE_A_DONE_REMOTE_AUTH)
|
||||
treq->th_peerid[i++] = nla_get_u32(nla);
|
||||
if (i >= treq->th_num_peerids)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tls_handshake_done - callback to handle a CMD_DONE request
|
||||
* @req: socket on which the handshake was performed
|
||||
* @status: session status code
|
||||
* @info: full results of session establishment
|
||||
*
|
||||
*/
|
||||
static void tls_handshake_done(struct handshake_req *req,
|
||||
unsigned int status, struct genl_info *info)
|
||||
{
|
||||
struct tls_handshake_req *treq = handshake_req_private(req);
|
||||
|
||||
treq->th_peerid[0] = TLS_NO_PEERID;
|
||||
if (info)
|
||||
tls_handshake_remote_peerids(treq, info);
|
||||
|
||||
treq->th_consumer_done(treq->th_consumer_data, -status,
|
||||
treq->th_peerid[0]);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_KEYS)
|
||||
static int tls_handshake_private_keyring(struct tls_handshake_req *treq)
|
||||
{
|
||||
key_ref_t process_keyring_ref, keyring_ref;
|
||||
int ret;
|
||||
|
||||
if (treq->th_keyring == TLS_NO_KEYRING)
|
||||
return 0;
|
||||
|
||||
process_keyring_ref = lookup_user_key(KEY_SPEC_PROCESS_KEYRING,
|
||||
KEY_LOOKUP_CREATE,
|
||||
KEY_NEED_WRITE);
|
||||
if (IS_ERR(process_keyring_ref)) {
|
||||
ret = PTR_ERR(process_keyring_ref);
|
||||
goto out;
|
||||
}
|
||||
|
||||
keyring_ref = lookup_user_key(treq->th_keyring, KEY_LOOKUP_CREATE,
|
||||
KEY_NEED_LINK);
|
||||
if (IS_ERR(keyring_ref)) {
|
||||
ret = PTR_ERR(keyring_ref);
|
||||
goto out_put_key;
|
||||
}
|
||||
|
||||
ret = key_link(key_ref_to_ptr(process_keyring_ref),
|
||||
key_ref_to_ptr(keyring_ref));
|
||||
|
||||
key_ref_put(keyring_ref);
|
||||
out_put_key:
|
||||
key_ref_put(process_keyring_ref);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static int tls_handshake_private_keyring(struct tls_handshake_req *treq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tls_handshake_put_peer_identity(struct sk_buff *msg,
|
||||
struct tls_handshake_req *treq)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < treq->th_num_peerids; i++)
|
||||
if (nla_put_u32(msg, HANDSHAKE_A_ACCEPT_PEER_IDENTITY,
|
||||
treq->th_peerid[i]) < 0)
|
||||
return -EMSGSIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tls_handshake_put_certificate(struct sk_buff *msg,
|
||||
struct tls_handshake_req *treq)
|
||||
{
|
||||
struct nlattr *entry_attr;
|
||||
|
||||
if (treq->th_certificate == TLS_NO_CERT &&
|
||||
treq->th_privkey == TLS_NO_PRIVKEY)
|
||||
return 0;
|
||||
|
||||
entry_attr = nla_nest_start(msg, HANDSHAKE_A_ACCEPT_CERTIFICATE);
|
||||
if (!entry_attr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (nla_put_u32(msg, HANDSHAKE_A_X509_CERT,
|
||||
treq->th_certificate) ||
|
||||
nla_put_u32(msg, HANDSHAKE_A_X509_PRIVKEY,
|
||||
treq->th_privkey)) {
|
||||
nla_nest_cancel(msg, entry_attr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
nla_nest_end(msg, entry_attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tls_handshake_accept - callback to construct a CMD_ACCEPT response
|
||||
* @req: handshake parameters to return
|
||||
* @info: generic netlink message context
|
||||
* @fd: file descriptor to be returned
|
||||
*
|
||||
* Returns zero on success, or a negative errno on failure.
|
||||
*/
|
||||
static int tls_handshake_accept(struct handshake_req *req,
|
||||
struct genl_info *info, int fd)
|
||||
{
|
||||
struct tls_handshake_req *treq = handshake_req_private(req);
|
||||
struct nlmsghdr *hdr;
|
||||
struct sk_buff *msg;
|
||||
int ret;
|
||||
|
||||
ret = tls_handshake_private_keyring(treq);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = -ENOMEM;
|
||||
msg = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
goto out;
|
||||
hdr = handshake_genl_put(msg, info);
|
||||
if (!hdr)
|
||||
goto out_cancel;
|
||||
|
||||
ret = -EMSGSIZE;
|
||||
ret = nla_put_u32(msg, HANDSHAKE_A_ACCEPT_SOCKFD, fd);
|
||||
if (ret < 0)
|
||||
goto out_cancel;
|
||||
ret = nla_put_u32(msg, HANDSHAKE_A_ACCEPT_MESSAGE_TYPE, treq->th_type);
|
||||
if (ret < 0)
|
||||
goto out_cancel;
|
||||
if (treq->th_timeout_ms) {
|
||||
ret = nla_put_u32(msg, HANDSHAKE_A_ACCEPT_TIMEOUT, treq->th_timeout_ms);
|
||||
if (ret < 0)
|
||||
goto out_cancel;
|
||||
}
|
||||
|
||||
ret = nla_put_u32(msg, HANDSHAKE_A_ACCEPT_AUTH_MODE,
|
||||
treq->th_auth_mode);
|
||||
if (ret < 0)
|
||||
goto out_cancel;
|
||||
switch (treq->th_auth_mode) {
|
||||
case HANDSHAKE_AUTH_PSK:
|
||||
ret = tls_handshake_put_peer_identity(msg, treq);
|
||||
if (ret < 0)
|
||||
goto out_cancel;
|
||||
break;
|
||||
case HANDSHAKE_AUTH_X509:
|
||||
ret = tls_handshake_put_certificate(msg, treq);
|
||||
if (ret < 0)
|
||||
goto out_cancel;
|
||||
break;
|
||||
}
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
return genlmsg_reply(msg, info);
|
||||
|
||||
out_cancel:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct handshake_proto tls_handshake_proto = {
|
||||
.hp_handler_class = HANDSHAKE_HANDLER_CLASS_TLSHD,
|
||||
.hp_privsize = sizeof(struct tls_handshake_req),
|
||||
|
||||
.hp_accept = tls_handshake_accept,
|
||||
.hp_done = tls_handshake_done,
|
||||
};
|
||||
|
||||
/**
|
||||
* tls_client_hello_anon - request an anonymous TLS handshake on a socket
|
||||
* @args: socket and handshake parameters for this request
|
||||
* @flags: memory allocation control flags
|
||||
*
|
||||
* Return values:
|
||||
* %0: Handshake request enqueue; ->done will be called when complete
|
||||
* %-ESRCH: No user agent is available
|
||||
* %-ENOMEM: Memory allocation failed
|
||||
*/
|
||||
int tls_client_hello_anon(const struct tls_handshake_args *args, gfp_t flags)
|
||||
{
|
||||
struct tls_handshake_req *treq;
|
||||
struct handshake_req *req;
|
||||
|
||||
req = handshake_req_alloc(&tls_handshake_proto, flags);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
treq = tls_handshake_req_init(req, args);
|
||||
treq->th_type = HANDSHAKE_MSG_TYPE_CLIENTHELLO;
|
||||
treq->th_auth_mode = HANDSHAKE_AUTH_UNAUTH;
|
||||
|
||||
return handshake_req_submit(args->ta_sock, req, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(tls_client_hello_anon);
|
||||
|
||||
/**
|
||||
* tls_client_hello_x509 - request an x.509-based TLS handshake on a socket
|
||||
* @args: socket and handshake parameters for this request
|
||||
* @flags: memory allocation control flags
|
||||
*
|
||||
* Return values:
|
||||
* %0: Handshake request enqueue; ->done will be called when complete
|
||||
* %-ESRCH: No user agent is available
|
||||
* %-ENOMEM: Memory allocation failed
|
||||
*/
|
||||
int tls_client_hello_x509(const struct tls_handshake_args *args, gfp_t flags)
|
||||
{
|
||||
struct tls_handshake_req *treq;
|
||||
struct handshake_req *req;
|
||||
|
||||
req = handshake_req_alloc(&tls_handshake_proto, flags);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
treq = tls_handshake_req_init(req, args);
|
||||
treq->th_type = HANDSHAKE_MSG_TYPE_CLIENTHELLO;
|
||||
treq->th_auth_mode = HANDSHAKE_AUTH_X509;
|
||||
treq->th_certificate = args->ta_my_cert;
|
||||
treq->th_privkey = args->ta_my_privkey;
|
||||
|
||||
return handshake_req_submit(args->ta_sock, req, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(tls_client_hello_x509);
|
||||
|
||||
/**
|
||||
* tls_client_hello_psk - request a PSK-based TLS handshake on a socket
|
||||
* @args: socket and handshake parameters for this request
|
||||
* @flags: memory allocation control flags
|
||||
*
|
||||
* Return values:
|
||||
* %0: Handshake request enqueue; ->done will be called when complete
|
||||
* %-EINVAL: Wrong number of local peer IDs
|
||||
* %-ESRCH: No user agent is available
|
||||
* %-ENOMEM: Memory allocation failed
|
||||
*/
|
||||
int tls_client_hello_psk(const struct tls_handshake_args *args, gfp_t flags)
|
||||
{
|
||||
struct tls_handshake_req *treq;
|
||||
struct handshake_req *req;
|
||||
unsigned int i;
|
||||
|
||||
if (!args->ta_num_peerids ||
|
||||
args->ta_num_peerids > ARRAY_SIZE(treq->th_peerid))
|
||||
return -EINVAL;
|
||||
|
||||
req = handshake_req_alloc(&tls_handshake_proto, flags);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
treq = tls_handshake_req_init(req, args);
|
||||
treq->th_type = HANDSHAKE_MSG_TYPE_CLIENTHELLO;
|
||||
treq->th_auth_mode = HANDSHAKE_AUTH_PSK;
|
||||
treq->th_num_peerids = args->ta_num_peerids;
|
||||
for (i = 0; i < args->ta_num_peerids; i++)
|
||||
treq->th_peerid[i] = args->ta_my_peerids[i];
|
||||
|
||||
return handshake_req_submit(args->ta_sock, req, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(tls_client_hello_psk);
|
||||
|
||||
/**
|
||||
* tls_server_hello_x509 - request a server TLS handshake on a socket
|
||||
* @args: socket and handshake parameters for this request
|
||||
* @flags: memory allocation control flags
|
||||
*
|
||||
* Return values:
|
||||
* %0: Handshake request enqueue; ->done will be called when complete
|
||||
* %-ESRCH: No user agent is available
|
||||
* %-ENOMEM: Memory allocation failed
|
||||
*/
|
||||
int tls_server_hello_x509(const struct tls_handshake_args *args, gfp_t flags)
|
||||
{
|
||||
struct tls_handshake_req *treq;
|
||||
struct handshake_req *req;
|
||||
|
||||
req = handshake_req_alloc(&tls_handshake_proto, flags);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
treq = tls_handshake_req_init(req, args);
|
||||
treq->th_type = HANDSHAKE_MSG_TYPE_SERVERHELLO;
|
||||
treq->th_auth_mode = HANDSHAKE_AUTH_X509;
|
||||
treq->th_certificate = args->ta_my_cert;
|
||||
treq->th_privkey = args->ta_my_privkey;
|
||||
|
||||
return handshake_req_submit(args->ta_sock, req, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(tls_server_hello_x509);
|
||||
|
||||
/**
|
||||
* tls_server_hello_psk - request a server TLS handshake on a socket
|
||||
* @args: socket and handshake parameters for this request
|
||||
* @flags: memory allocation control flags
|
||||
*
|
||||
* Return values:
|
||||
* %0: Handshake request enqueue; ->done will be called when complete
|
||||
* %-ESRCH: No user agent is available
|
||||
* %-ENOMEM: Memory allocation failed
|
||||
*/
|
||||
int tls_server_hello_psk(const struct tls_handshake_args *args, gfp_t flags)
|
||||
{
|
||||
struct tls_handshake_req *treq;
|
||||
struct handshake_req *req;
|
||||
|
||||
req = handshake_req_alloc(&tls_handshake_proto, flags);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
treq = tls_handshake_req_init(req, args);
|
||||
treq->th_type = HANDSHAKE_MSG_TYPE_SERVERHELLO;
|
||||
treq->th_auth_mode = HANDSHAKE_AUTH_PSK;
|
||||
treq->th_num_peerids = 1;
|
||||
treq->th_peerid[0] = args->ta_my_peerids[0];
|
||||
|
||||
return handshake_req_submit(args->ta_sock, req, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(tls_server_hello_psk);
|
||||
|
||||
/**
|
||||
* tls_handshake_cancel - cancel a pending handshake
|
||||
* @sk: socket on which there is an ongoing handshake
|
||||
*
|
||||
* Request cancellation races with request completion. To determine
|
||||
* who won, callers examine the return value from this function.
|
||||
*
|
||||
* Return values:
|
||||
* %true - Uncompleted handshake request was canceled
|
||||
* %false - Handshake request already completed or not found
|
||||
*/
|
||||
bool tls_handshake_cancel(struct sock *sk)
|
||||
{
|
||||
return handshake_req_cancel(sk);
|
||||
}
|
||||
EXPORT_SYMBOL(tls_handshake_cancel);
|
Loading…
Reference in New Issue
Block a user