net/handshake: Create a NETLINK service for handling handshake requests
When a kernel consumer needs a transport layer security session, it
first needs a handshake to negotiate and establish a session. This
negotiation can be done in user space via one of the several
existing library implementations, or it can be done in the kernel.
No in-kernel handshake implementations yet exist. In their absence,
we add a netlink service that can:
a. Notify a user space daemon that a handshake is needed.
b. Once notified, the daemon calls the kernel back via this
netlink service to get the handshake parameters, including an
open socket on which to establish the session.
c. Once the handshake is complete, the daemon reports the
session status and other information via a second netlink
operation. This operation marks that it is safe for the
kernel to use the open socket and the security session
established there.
The notification service uses a multicast group. Each handshake
mechanism (eg, tlshd) adopts its own group number so that the
handshake services are completely independent of one another. The
kernel can then tell via netlink_has_listeners() whether a handshake
service is active and prepared to handle a handshake request.
A new netlink operation, ACCEPT, acts like accept(2) in that it
instantiates a file descriptor in the user space daemon's fd table.
If this operation is successful, the reply carries the fd number,
which can be treated as an open and ready file descriptor.
While user space is performing the handshake, the kernel keeps its
muddy paws off the open socket. A second new netlink operation,
DONE, indicates that the user space daemon is finished with the
socket and it is safe for the kernel to use again. The operation
also indicates whether a session was established successfully.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-04-17 22:32:26 +08:00
|
|
|
/* 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 _INTERNAL_HANDSHAKE_H
|
|
|
|
#define _INTERNAL_HANDSHAKE_H
|
|
|
|
|
|
|
|
/* Per-net namespace context */
|
|
|
|
struct handshake_net {
|
|
|
|
spinlock_t hn_lock; /* protects next 3 fields */
|
|
|
|
int hn_pending;
|
|
|
|
int hn_pending_max;
|
|
|
|
struct list_head hn_requests;
|
|
|
|
|
|
|
|
unsigned long hn_flags;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum hn_flags_bits {
|
|
|
|
HANDSHAKE_F_NET_DRAINING,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct handshake_proto;
|
|
|
|
|
|
|
|
/* One handshake request */
|
|
|
|
struct handshake_req {
|
|
|
|
struct list_head hr_list;
|
|
|
|
struct rhash_head hr_rhash;
|
|
|
|
unsigned long hr_flags;
|
|
|
|
const struct handshake_proto *hr_proto;
|
|
|
|
struct sock *hr_sk;
|
|
|
|
void (*hr_odestruct)(struct sock *sk);
|
|
|
|
|
|
|
|
/* Always the last field */
|
|
|
|
char hr_priv[];
|
|
|
|
};
|
|
|
|
|
|
|
|
enum hr_flags_bits {
|
|
|
|
HANDSHAKE_F_REQ_COMPLETED,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Invariants for all handshake requests for one transport layer
|
|
|
|
* security protocol
|
|
|
|
*/
|
|
|
|
struct handshake_proto {
|
|
|
|
int hp_handler_class;
|
|
|
|
size_t hp_privsize;
|
2023-04-17 22:32:39 +08:00
|
|
|
unsigned long hp_flags;
|
net/handshake: Create a NETLINK service for handling handshake requests
When a kernel consumer needs a transport layer security session, it
first needs a handshake to negotiate and establish a session. This
negotiation can be done in user space via one of the several
existing library implementations, or it can be done in the kernel.
No in-kernel handshake implementations yet exist. In their absence,
we add a netlink service that can:
a. Notify a user space daemon that a handshake is needed.
b. Once notified, the daemon calls the kernel back via this
netlink service to get the handshake parameters, including an
open socket on which to establish the session.
c. Once the handshake is complete, the daemon reports the
session status and other information via a second netlink
operation. This operation marks that it is safe for the
kernel to use the open socket and the security session
established there.
The notification service uses a multicast group. Each handshake
mechanism (eg, tlshd) adopts its own group number so that the
handshake services are completely independent of one another. The
kernel can then tell via netlink_has_listeners() whether a handshake
service is active and prepared to handle a handshake request.
A new netlink operation, ACCEPT, acts like accept(2) in that it
instantiates a file descriptor in the user space daemon's fd table.
If this operation is successful, the reply carries the fd number,
which can be treated as an open and ready file descriptor.
While user space is performing the handshake, the kernel keeps its
muddy paws off the open socket. A second new netlink operation,
DONE, indicates that the user space daemon is finished with the
socket and it is safe for the kernel to use again. The operation
also indicates whether a session was established successfully.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-04-17 22:32:26 +08:00
|
|
|
|
|
|
|
int (*hp_accept)(struct handshake_req *req,
|
|
|
|
struct genl_info *info, int fd);
|
|
|
|
void (*hp_done)(struct handshake_req *req,
|
|
|
|
unsigned int status,
|
|
|
|
struct genl_info *info);
|
|
|
|
void (*hp_destroy)(struct handshake_req *req);
|
|
|
|
};
|
|
|
|
|
2023-04-17 22:32:39 +08:00
|
|
|
enum hp_flags_bits {
|
|
|
|
HANDSHAKE_F_PROTO_NOTIFY,
|
|
|
|
};
|
|
|
|
|
net/handshake: Create a NETLINK service for handling handshake requests
When a kernel consumer needs a transport layer security session, it
first needs a handshake to negotiate and establish a session. This
negotiation can be done in user space via one of the several
existing library implementations, or it can be done in the kernel.
No in-kernel handshake implementations yet exist. In their absence,
we add a netlink service that can:
a. Notify a user space daemon that a handshake is needed.
b. Once notified, the daemon calls the kernel back via this
netlink service to get the handshake parameters, including an
open socket on which to establish the session.
c. Once the handshake is complete, the daemon reports the
session status and other information via a second netlink
operation. This operation marks that it is safe for the
kernel to use the open socket and the security session
established there.
The notification service uses a multicast group. Each handshake
mechanism (eg, tlshd) adopts its own group number so that the
handshake services are completely independent of one another. The
kernel can then tell via netlink_has_listeners() whether a handshake
service is active and prepared to handle a handshake request.
A new netlink operation, ACCEPT, acts like accept(2) in that it
instantiates a file descriptor in the user space daemon's fd table.
If this operation is successful, the reply carries the fd number,
which can be treated as an open and ready file descriptor.
While user space is performing the handshake, the kernel keeps its
muddy paws off the open socket. A second new netlink operation,
DONE, indicates that the user space daemon is finished with the
socket and it is safe for the kernel to use again. The operation
also indicates whether a session was established successfully.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-04-17 22:32:26 +08:00
|
|
|
/* netlink.c */
|
|
|
|
int handshake_genl_notify(struct net *net, const struct handshake_proto *proto,
|
|
|
|
gfp_t flags);
|
|
|
|
struct nlmsghdr *handshake_genl_put(struct sk_buff *msg,
|
|
|
|
struct genl_info *info);
|
|
|
|
struct handshake_net *handshake_pernet(struct net *net);
|
|
|
|
|
|
|
|
/* request.c */
|
|
|
|
struct handshake_req *handshake_req_alloc(const struct handshake_proto *proto,
|
|
|
|
gfp_t flags);
|
|
|
|
int handshake_req_hash_init(void);
|
|
|
|
void handshake_req_hash_destroy(void);
|
|
|
|
void *handshake_req_private(struct handshake_req *req);
|
|
|
|
struct handshake_req *handshake_req_hash_lookup(struct sock *sk);
|
|
|
|
struct handshake_req *handshake_req_next(struct handshake_net *hn, int class);
|
|
|
|
int handshake_req_submit(struct socket *sock, struct handshake_req *req,
|
|
|
|
gfp_t flags);
|
|
|
|
void handshake_complete(struct handshake_req *req, unsigned int status,
|
|
|
|
struct genl_info *info);
|
|
|
|
bool handshake_req_cancel(struct sock *sk);
|
|
|
|
|
|
|
|
#endif /* _INTERNAL_HANDSHAKE_H */
|