mirror of
https://github.com/ukui/kernel.git
synced 2026-03-09 10:07:04 -07:00
Merge branch 'net-ktls'
Dave Watson says: ==================== net: kernel TLS This series adds support for kernel TLS encryption over TCP sockets. A standard TCP socket is converted to a TLS socket using a setsockopt. Only symmetric crypto is done in the kernel, as well as TLS record framing. The handshake remains in userspace, and the negotiated cipher keys/iv are provided to the TCP socket. We implemented support for this API in OpenSSL 1.1.0, the code is available at https://github.com/Mellanox/tls-openssl/tree/master It should work with any TLS library with similar modifications, a test tool using gnutls is here: https://github.com/Mellanox/tls-af_ktls_tool RFC patch to openssl: https://mta.openssl.org/pipermail/openssl-dev/2017-June/009384.html Changes from V2: * EXPORT_SYMBOL_GPL in patch 1 * Ensure cleanup code always called before sk_stream_kill_queues to avoid warnings Changes from V1: * EXPORT_SYMBOL GPL in patch 2 * Add link to OpenSSL patch & gnutls example in documentation patch. * sk_write_pending check was rolled in to wait_for_memory path, avoids special case and fixes lock inbalance issue. * Unify flag handling for sendmsg/sendfile Changes from RFC V2: * Generic ULP (upper layer protocol) framework instead of TLS specific setsockopts * Dropped Mellanox hardware patches, will come as separate series. Framework will work for both. RFC V2: http://www.mail-archive.com/netdev@vger.kernel.org/msg160317.html Changes from RFC V1: * Socket based on changing TCP proto_ops instead of crypto framework * Merged code with Mellanox's hardware tls offload * Zerocopy sendmsg support added - sendpage/sendfile is no longer necessary for zerocopy optimization RFC V1: http://www.mail-archive.com/netdev@vger.kernel.org/msg88021.html * Socket based on crypto userspace API framework, required two sockets in userspace, one encrypted, one unencrypted. Paper: https://netdevconf.org/1.2/papers/ktls.pdf ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
135
Documentation/networking/tls.txt
Normal file
135
Documentation/networking/tls.txt
Normal file
@@ -0,0 +1,135 @@
|
||||
Overview
|
||||
========
|
||||
|
||||
Transport Layer Security (TLS) is a Upper Layer Protocol (ULP) that runs over
|
||||
TCP. TLS provides end-to-end data integrity and confidentiality.
|
||||
|
||||
User interface
|
||||
==============
|
||||
|
||||
Creating a TLS connection
|
||||
-------------------------
|
||||
|
||||
First create a new TCP socket and set the TLS ULP.
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
setsockopt(sock, SOL_TCP, TCP_ULP, "tls", sizeof("tls"));
|
||||
|
||||
Setting the TLS ULP allows us to set/get TLS socket options. Currently
|
||||
only the symmetric encryption is handled in the kernel. After the TLS
|
||||
handshake is complete, we have all the parameters required to move the
|
||||
data-path to the kernel. There is a separate socket option for moving
|
||||
the transmit and the receive into the kernel.
|
||||
|
||||
/* From linux/tls.h */
|
||||
struct tls_crypto_info {
|
||||
unsigned short version;
|
||||
unsigned short cipher_type;
|
||||
};
|
||||
|
||||
struct tls12_crypto_info_aes_gcm_128 {
|
||||
struct tls_crypto_info info;
|
||||
unsigned char iv[TLS_CIPHER_AES_GCM_128_IV_SIZE];
|
||||
unsigned char key[TLS_CIPHER_AES_GCM_128_KEY_SIZE];
|
||||
unsigned char salt[TLS_CIPHER_AES_GCM_128_SALT_SIZE];
|
||||
unsigned char rec_seq[TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE];
|
||||
};
|
||||
|
||||
|
||||
struct tls12_crypto_info_aes_gcm_128 crypto_info;
|
||||
|
||||
crypto_info.info.version = TLS_1_2_VERSION;
|
||||
crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128;
|
||||
memcpy(crypto_info.iv, iv_write, TLS_CIPHER_AES_GCM_128_IV_SIZE);
|
||||
memcpy(crypto_info.rec_seq, seq_number_write,
|
||||
TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
|
||||
memcpy(crypto_info.key, cipher_key_write, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
|
||||
memcpy(crypto_info.salt, implicit_iv_write, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
|
||||
|
||||
setsockopt(sock, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info));
|
||||
|
||||
Sending TLS application data
|
||||
----------------------------
|
||||
|
||||
After setting the TLS_TX socket option all application data sent over this
|
||||
socket is encrypted using TLS and the parameters provided in the socket option.
|
||||
For example, we can send an encrypted hello world record as follows:
|
||||
|
||||
const char *msg = "hello world\n";
|
||||
send(sock, msg, strlen(msg));
|
||||
|
||||
send() data is directly encrypted from the userspace buffer provided
|
||||
to the encrypted kernel send buffer if possible.
|
||||
|
||||
The sendfile system call will send the file's data over TLS records of maximum
|
||||
length (2^14).
|
||||
|
||||
file = open(filename, O_RDONLY);
|
||||
fstat(file, &stat);
|
||||
sendfile(sock, file, &offset, stat.st_size);
|
||||
|
||||
TLS records are created and sent after each send() call, unless
|
||||
MSG_MORE is passed. MSG_MORE will delay creation of a record until
|
||||
MSG_MORE is not passed, or the maximum record size is reached.
|
||||
|
||||
The kernel will need to allocate a buffer for the encrypted data.
|
||||
This buffer is allocated at the time send() is called, such that
|
||||
either the entire send() call will return -ENOMEM (or block waiting
|
||||
for memory), or the encryption will always succeed. If send() returns
|
||||
-ENOMEM and some data was left on the socket buffer from a previous
|
||||
call using MSG_MORE, the MSG_MORE data is left on the socket buffer.
|
||||
|
||||
Send TLS control messages
|
||||
-------------------------
|
||||
|
||||
Other than application data, TLS has control messages such as alert
|
||||
messages (record type 21) and handshake messages (record type 22), etc.
|
||||
These messages can be sent over the socket by providing the TLS record type
|
||||
via a CMSG. For example the following function sends @data of @length bytes
|
||||
using a record of type @record_type.
|
||||
|
||||
/* send TLS control message using record_type */
|
||||
static int klts_send_ctrl_message(int sock, unsigned char record_type,
|
||||
void *data, size_t length)
|
||||
{
|
||||
struct msghdr msg = {0};
|
||||
int cmsg_len = sizeof(record_type);
|
||||
struct cmsghdr *cmsg;
|
||||
char buf[CMSG_SPACE(cmsg_len)];
|
||||
struct iovec msg_iov; /* Vector of data to send/receive into. */
|
||||
|
||||
msg.msg_control = buf;
|
||||
msg.msg_controllen = sizeof(buf);
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_TLS;
|
||||
cmsg->cmsg_type = TLS_SET_RECORD_TYPE;
|
||||
cmsg->cmsg_len = CMSG_LEN(cmsg_len);
|
||||
*CMSG_DATA(cmsg) = record_type;
|
||||
msg.msg_controllen = cmsg->cmsg_len;
|
||||
|
||||
msg_iov.iov_base = data;
|
||||
msg_iov.iov_len = length;
|
||||
msg.msg_iov = &msg_iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
return sendmsg(sock, &msg, 0);
|
||||
}
|
||||
|
||||
Control message data should be provided unencrypted, and will be
|
||||
encrypted by the kernel.
|
||||
|
||||
Integrating in to userspace TLS library
|
||||
---------------------------------------
|
||||
|
||||
At a high level, the kernel TLS ULP is a replacement for the record
|
||||
layer of a userspace TLS library.
|
||||
|
||||
A patchset to OpenSSL to use ktls as the record layer is here:
|
||||
|
||||
https://github.com/Mellanox/tls-openssl
|
||||
|
||||
An example of calling send directly after a handshake using
|
||||
gnutls. Since it doesn't implement a full record layer, control
|
||||
messages are not supported:
|
||||
|
||||
https://github.com/Mellanox/tls-af_ktls_tool
|
||||
10
MAINTAINERS
10
MAINTAINERS
@@ -8978,6 +8978,16 @@ F: net/ipv6/
|
||||
F: include/net/ip*
|
||||
F: arch/x86/net/*
|
||||
|
||||
NETWORKING [TLS]
|
||||
M: Ilya Lesokhin <ilyal@mellanox.com>
|
||||
M: Aviad Yehezkel <aviadye@mellanox.com>
|
||||
M: Dave Watson <davejwatson@fb.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: net/tls/*
|
||||
F: include/uapi/linux/tls.h
|
||||
F: include/net/tls.h
|
||||
|
||||
NETWORKING [IPSEC]
|
||||
M: Steffen Klassert <steffen.klassert@secunet.com>
|
||||
M: Herbert Xu <herbert@gondor.apana.org.au>
|
||||
|
||||
@@ -334,6 +334,7 @@ struct ucred {
|
||||
#define SOL_ALG 279
|
||||
#define SOL_NFC 280
|
||||
#define SOL_KCM 281
|
||||
#define SOL_TLS 282
|
||||
|
||||
/* IPX options */
|
||||
#define IPX_TYPE 1
|
||||
|
||||
@@ -75,6 +75,8 @@ struct inet_connection_sock_af_ops {
|
||||
* @icsk_pmtu_cookie Last pmtu seen by socket
|
||||
* @icsk_ca_ops Pluggable congestion control hook
|
||||
* @icsk_af_ops Operations which are AF_INET{4,6} specific
|
||||
* @icsk_ulp_ops Pluggable ULP control hook
|
||||
* @icsk_ulp_data ULP private data
|
||||
* @icsk_ca_state: Congestion control state
|
||||
* @icsk_retransmits: Number of unrecovered [RTO] timeouts
|
||||
* @icsk_pending: Scheduled timer event
|
||||
@@ -97,6 +99,8 @@ struct inet_connection_sock {
|
||||
__u32 icsk_pmtu_cookie;
|
||||
const struct tcp_congestion_ops *icsk_ca_ops;
|
||||
const struct inet_connection_sock_af_ops *icsk_af_ops;
|
||||
const struct tcp_ulp_ops *icsk_ulp_ops;
|
||||
void *icsk_ulp_data;
|
||||
unsigned int (*icsk_sync_mss)(struct sock *sk, u32 pmtu);
|
||||
__u8 icsk_ca_state:6,
|
||||
icsk_ca_setsockopt:1,
|
||||
|
||||
@@ -350,6 +350,8 @@ int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw);
|
||||
int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
|
||||
int tcp_sendpage(struct sock *sk, struct page *page, int offset, size_t size,
|
||||
int flags);
|
||||
ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
|
||||
size_t size, int flags);
|
||||
void tcp_release_cb(struct sock *sk);
|
||||
void tcp_wfree(struct sk_buff *skb);
|
||||
void tcp_write_timer_handler(struct sock *sk);
|
||||
@@ -1991,4 +1993,29 @@ static inline void tcp_listendrop(const struct sock *sk)
|
||||
|
||||
enum hrtimer_restart tcp_pace_kick(struct hrtimer *timer);
|
||||
|
||||
/*
|
||||
* Interface for adding Upper Level Protocols over TCP
|
||||
*/
|
||||
|
||||
#define TCP_ULP_NAME_MAX 16
|
||||
#define TCP_ULP_MAX 128
|
||||
#define TCP_ULP_BUF_MAX (TCP_ULP_NAME_MAX*TCP_ULP_MAX)
|
||||
|
||||
struct tcp_ulp_ops {
|
||||
struct list_head list;
|
||||
|
||||
/* initialize ulp */
|
||||
int (*init)(struct sock *sk);
|
||||
/* cleanup ulp */
|
||||
void (*release)(struct sock *sk);
|
||||
|
||||
char name[TCP_ULP_NAME_MAX];
|
||||
struct module *owner;
|
||||
};
|
||||
int tcp_register_ulp(struct tcp_ulp_ops *type);
|
||||
void tcp_unregister_ulp(struct tcp_ulp_ops *type);
|
||||
int tcp_set_ulp(struct sock *sk, const char *name);
|
||||
void tcp_get_available_ulp(char *buf, size_t len);
|
||||
void tcp_cleanup_ulp(struct sock *sk);
|
||||
|
||||
#endif /* _TCP_H */
|
||||
|
||||
237
include/net/tls.h
Normal file
237
include/net/tls.h
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2016-2017, Dave Watson <davejwatson@fb.com>. All rights reserved.
|
||||
*
|
||||
* This software is available to you under a choice of one of two
|
||||
* licenses. You may choose to be licensed under the terms of the GNU
|
||||
* General Public License (GPL) Version 2, available from the file
|
||||
* COPYING in the main directory of this source tree, or the
|
||||
* OpenIB.org BSD license below:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TLS_OFFLOAD_H
|
||||
#define _TLS_OFFLOAD_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <uapi/linux/tls.h>
|
||||
|
||||
|
||||
/* Maximum data size carried in a TLS record */
|
||||
#define TLS_MAX_PAYLOAD_SIZE ((size_t)1 << 14)
|
||||
|
||||
#define TLS_HEADER_SIZE 5
|
||||
#define TLS_NONCE_OFFSET TLS_HEADER_SIZE
|
||||
|
||||
#define TLS_CRYPTO_INFO_READY(info) ((info)->cipher_type)
|
||||
|
||||
#define TLS_RECORD_TYPE_DATA 0x17
|
||||
|
||||
#define TLS_AAD_SPACE_SIZE 13
|
||||
|
||||
struct tls_sw_context {
|
||||
struct crypto_aead *aead_send;
|
||||
|
||||
/* Sending context */
|
||||
char aad_space[TLS_AAD_SPACE_SIZE];
|
||||
|
||||
unsigned int sg_plaintext_size;
|
||||
int sg_plaintext_num_elem;
|
||||
struct scatterlist sg_plaintext_data[MAX_SKB_FRAGS];
|
||||
|
||||
unsigned int sg_encrypted_size;
|
||||
int sg_encrypted_num_elem;
|
||||
struct scatterlist sg_encrypted_data[MAX_SKB_FRAGS];
|
||||
|
||||
/* AAD | sg_plaintext_data | sg_tag */
|
||||
struct scatterlist sg_aead_in[2];
|
||||
/* AAD | sg_encrypted_data (data contain overhead for hdr&iv&tag) */
|
||||
struct scatterlist sg_aead_out[2];
|
||||
};
|
||||
|
||||
enum {
|
||||
TLS_PENDING_CLOSED_RECORD
|
||||
};
|
||||
|
||||
struct tls_context {
|
||||
union {
|
||||
struct tls_crypto_info crypto_send;
|
||||
struct tls12_crypto_info_aes_gcm_128 crypto_send_aes_gcm_128;
|
||||
};
|
||||
|
||||
void *priv_ctx;
|
||||
|
||||
u16 prepend_size;
|
||||
u16 tag_size;
|
||||
u16 overhead_size;
|
||||
u16 iv_size;
|
||||
char *iv;
|
||||
u16 rec_seq_size;
|
||||
char *rec_seq;
|
||||
|
||||
struct scatterlist *partially_sent_record;
|
||||
u16 partially_sent_offset;
|
||||
unsigned long flags;
|
||||
|
||||
u16 pending_open_record_frags;
|
||||
int (*push_pending_record)(struct sock *sk, int flags);
|
||||
void (*free_resources)(struct sock *sk);
|
||||
|
||||
void (*sk_write_space)(struct sock *sk);
|
||||
void (*sk_proto_close)(struct sock *sk, long timeout);
|
||||
|
||||
int (*setsockopt)(struct sock *sk, int level,
|
||||
int optname, char __user *optval,
|
||||
unsigned int optlen);
|
||||
int (*getsockopt)(struct sock *sk, int level,
|
||||
int optname, char __user *optval,
|
||||
int __user *optlen);
|
||||
};
|
||||
|
||||
int wait_on_pending_writer(struct sock *sk, long *timeo);
|
||||
int tls_sk_query(struct sock *sk, int optname, char __user *optval,
|
||||
int __user *optlen);
|
||||
int tls_sk_attach(struct sock *sk, int optname, char __user *optval,
|
||||
unsigned int optlen);
|
||||
|
||||
|
||||
int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx);
|
||||
int tls_sw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
|
||||
int tls_sw_sendpage(struct sock *sk, struct page *page,
|
||||
int offset, size_t size, int flags);
|
||||
void tls_sw_close(struct sock *sk, long timeout);
|
||||
|
||||
void tls_sk_destruct(struct sock *sk, struct tls_context *ctx);
|
||||
void tls_icsk_clean_acked(struct sock *sk);
|
||||
|
||||
int tls_push_sg(struct sock *sk, struct tls_context *ctx,
|
||||
struct scatterlist *sg, u16 first_offset,
|
||||
int flags);
|
||||
int tls_push_pending_closed_record(struct sock *sk, struct tls_context *ctx,
|
||||
int flags, long *timeo);
|
||||
|
||||
static inline bool tls_is_pending_closed_record(struct tls_context *ctx)
|
||||
{
|
||||
return test_bit(TLS_PENDING_CLOSED_RECORD, &ctx->flags);
|
||||
}
|
||||
|
||||
static inline int tls_complete_pending_work(struct sock *sk,
|
||||
struct tls_context *ctx,
|
||||
int flags, long *timeo)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (unlikely(sk->sk_write_pending))
|
||||
rc = wait_on_pending_writer(sk, timeo);
|
||||
|
||||
if (!rc && tls_is_pending_closed_record(ctx))
|
||||
rc = tls_push_pending_closed_record(sk, ctx, flags, timeo);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline bool tls_is_partially_sent_record(struct tls_context *ctx)
|
||||
{
|
||||
return !!ctx->partially_sent_record;
|
||||
}
|
||||
|
||||
static inline bool tls_is_pending_open_record(struct tls_context *tls_ctx)
|
||||
{
|
||||
return tls_ctx->pending_open_record_frags;
|
||||
}
|
||||
|
||||
static inline void tls_err_abort(struct sock *sk)
|
||||
{
|
||||
sk->sk_err = -EBADMSG;
|
||||
sk->sk_error_report(sk);
|
||||
}
|
||||
|
||||
static inline bool tls_bigint_increment(unsigned char *seq, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = len - 1; i >= 0; i--) {
|
||||
++seq[i];
|
||||
if (seq[i] != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return (i == -1);
|
||||
}
|
||||
|
||||
static inline void tls_advance_record_sn(struct sock *sk,
|
||||
struct tls_context *ctx)
|
||||
{
|
||||
if (tls_bigint_increment(ctx->rec_seq, ctx->rec_seq_size))
|
||||
tls_err_abort(sk);
|
||||
tls_bigint_increment(ctx->iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE,
|
||||
ctx->iv_size);
|
||||
}
|
||||
|
||||
static inline void tls_fill_prepend(struct tls_context *ctx,
|
||||
char *buf,
|
||||
size_t plaintext_len,
|
||||
unsigned char record_type)
|
||||
{
|
||||
size_t pkt_len, iv_size = ctx->iv_size;
|
||||
|
||||
pkt_len = plaintext_len + iv_size + ctx->tag_size;
|
||||
|
||||
/* we cover nonce explicit here as well, so buf should be of
|
||||
* size KTLS_DTLS_HEADER_SIZE + KTLS_DTLS_NONCE_EXPLICIT_SIZE
|
||||
*/
|
||||
buf[0] = record_type;
|
||||
buf[1] = TLS_VERSION_MINOR(ctx->crypto_send.version);
|
||||
buf[2] = TLS_VERSION_MAJOR(ctx->crypto_send.version);
|
||||
/* we can use IV for nonce explicit according to spec */
|
||||
buf[3] = pkt_len >> 8;
|
||||
buf[4] = pkt_len & 0xFF;
|
||||
memcpy(buf + TLS_NONCE_OFFSET,
|
||||
ctx->iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, iv_size);
|
||||
}
|
||||
|
||||
static inline struct tls_context *tls_get_ctx(const struct sock *sk)
|
||||
{
|
||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
||||
|
||||
return icsk->icsk_ulp_data;
|
||||
}
|
||||
|
||||
static inline struct tls_sw_context *tls_sw_ctx(
|
||||
const struct tls_context *tls_ctx)
|
||||
{
|
||||
return (struct tls_sw_context *)tls_ctx->priv_ctx;
|
||||
}
|
||||
|
||||
static inline struct tls_offload_context *tls_offload_ctx(
|
||||
const struct tls_context *tls_ctx)
|
||||
{
|
||||
return (struct tls_offload_context *)tls_ctx->priv_ctx;
|
||||
}
|
||||
|
||||
int tls_proccess_cmsg(struct sock *sk, struct msghdr *msg,
|
||||
unsigned char *record_type);
|
||||
|
||||
#endif /* _TLS_OFFLOAD_H */
|
||||
@@ -117,6 +117,7 @@ enum {
|
||||
#define TCP_SAVED_SYN 28 /* Get SYN headers recorded for connection */
|
||||
#define TCP_REPAIR_WINDOW 29 /* Get/set window parameters */
|
||||
#define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect */
|
||||
#define TCP_ULP 31 /* Attach a ULP to a TCP connection */
|
||||
|
||||
struct tcp_repair_opt {
|
||||
__u32 opt_code;
|
||||
|
||||
79
include/uapi/linux/tls.h
Normal file
79
include/uapi/linux/tls.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved.
|
||||
*
|
||||
* This software is available to you under a choice of one of two
|
||||
* licenses. You may choose to be licensed under the terms of the GNU
|
||||
* General Public License (GPL) Version 2, available from the file
|
||||
* COPYING in the main directory of this source tree, or the
|
||||
* OpenIB.org BSD license below:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_TLS_H
|
||||
#define _UAPI_LINUX_TLS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <net/tcp.h>
|
||||
|
||||
/* TLS socket options */
|
||||
#define TLS_TX 1 /* Set transmit parameters */
|
||||
|
||||
/* Supported versions */
|
||||
#define TLS_VERSION_MINOR(ver) ((ver) & 0xFF)
|
||||
#define TLS_VERSION_MAJOR(ver) (((ver) >> 8) & 0xFF)
|
||||
|
||||
#define TLS_VERSION_NUMBER(id) ((((id##_VERSION_MAJOR) & 0xFF) << 8) | \
|
||||
((id##_VERSION_MINOR) & 0xFF))
|
||||
|
||||
#define TLS_1_2_VERSION_MAJOR 0x3
|
||||
#define TLS_1_2_VERSION_MINOR 0x3
|
||||
#define TLS_1_2_VERSION TLS_VERSION_NUMBER(TLS_1_2)
|
||||
|
||||
/* Supported ciphers */
|
||||
#define TLS_CIPHER_AES_GCM_128 51
|
||||
#define TLS_CIPHER_AES_GCM_128_IV_SIZE 8
|
||||
#define TLS_CIPHER_AES_GCM_128_KEY_SIZE 16
|
||||
#define TLS_CIPHER_AES_GCM_128_SALT_SIZE 4
|
||||
#define TLS_CIPHER_AES_GCM_128_TAG_SIZE 16
|
||||
#define TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE 8
|
||||
|
||||
#define TLS_SET_RECORD_TYPE 1
|
||||
|
||||
struct tls_crypto_info {
|
||||
__u16 version;
|
||||
__u16 cipher_type;
|
||||
};
|
||||
|
||||
struct tls12_crypto_info_aes_gcm_128 {
|
||||
struct tls_crypto_info info;
|
||||
unsigned char iv[TLS_CIPHER_AES_GCM_128_IV_SIZE];
|
||||
unsigned char key[TLS_CIPHER_AES_GCM_128_KEY_SIZE];
|
||||
unsigned char salt[TLS_CIPHER_AES_GCM_128_SALT_SIZE];
|
||||
unsigned char rec_seq[TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE];
|
||||
};
|
||||
|
||||
#endif /* _UAPI_LINUX_TLS_H */
|
||||
@@ -55,6 +55,7 @@ menu "Networking options"
|
||||
|
||||
source "net/packet/Kconfig"
|
||||
source "net/unix/Kconfig"
|
||||
source "net/tls/Kconfig"
|
||||
source "net/xfrm/Kconfig"
|
||||
source "net/iucv/Kconfig"
|
||||
source "net/smc/Kconfig"
|
||||
|
||||
@@ -15,6 +15,7 @@ obj-$(CONFIG_LLC) += llc/
|
||||
obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ bpf/
|
||||
obj-$(CONFIG_NETFILTER) += netfilter/
|
||||
obj-$(CONFIG_INET) += ipv4/
|
||||
obj-$(CONFIG_TLS) += tls/
|
||||
obj-$(CONFIG_XFRM) += xfrm/
|
||||
obj-$(CONFIG_UNIX) += unix/
|
||||
obj-$(CONFIG_NET) += ipv6/
|
||||
|
||||
@@ -8,7 +8,7 @@ obj-y := route.o inetpeer.o protocol.o \
|
||||
inet_timewait_sock.o inet_connection_sock.o \
|
||||
tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \
|
||||
tcp_minisocks.o tcp_cong.o tcp_metrics.o tcp_fastopen.o \
|
||||
tcp_rate.o tcp_recovery.o \
|
||||
tcp_rate.o tcp_recovery.o tcp_ulp.o \
|
||||
tcp_offload.o datagram.o raw.o udp.o udplite.o \
|
||||
udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \
|
||||
fib_frontend.o fib_semantics.o fib_trie.o fib_notifier.o \
|
||||
|
||||
@@ -360,6 +360,25 @@ static int proc_tfo_blackhole_detect_timeout(struct ctl_table *table,
|
||||
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
|
||||
if (write && ret == 0)
|
||||
tcp_fastopen_active_timeout_reset();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int proc_tcp_available_ulp(struct ctl_table *ctl,
|
||||
int write,
|
||||
void __user *buffer, size_t *lenp,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct ctl_table tbl = { .maxlen = TCP_ULP_BUF_MAX, };
|
||||
int ret;
|
||||
|
||||
tbl.data = kmalloc(tbl.maxlen, GFP_USER);
|
||||
if (!tbl.data)
|
||||
return -ENOMEM;
|
||||
tcp_get_available_ulp(tbl.data, TCP_ULP_BUF_MAX);
|
||||
ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
|
||||
kfree(tbl.data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -685,6 +704,12 @@ static struct ctl_table ipv4_table[] = {
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_ms_jiffies,
|
||||
},
|
||||
{
|
||||
.procname = "tcp_available_ulp",
|
||||
.maxlen = TCP_ULP_BUF_MAX,
|
||||
.mode = 0444,
|
||||
.proc_handler = proc_tcp_available_ulp,
|
||||
},
|
||||
{
|
||||
.procname = "icmp_msgs_per_sec",
|
||||
.data = &sysctl_icmp_msgs_per_sec,
|
||||
|
||||
@@ -901,8 +901,8 @@ static int tcp_send_mss(struct sock *sk, int *size_goal, int flags)
|
||||
return mss_now;
|
||||
}
|
||||
|
||||
static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
|
||||
size_t size, int flags)
|
||||
ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
|
||||
size_t size, int flags)
|
||||
{
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
int mss_now, size_goal;
|
||||
@@ -1032,6 +1032,7 @@ out_err:
|
||||
}
|
||||
return sk_stream_error(sk, flags, err);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(do_tcp_sendpages);
|
||||
|
||||
int tcp_sendpage(struct sock *sk, struct page *page, int offset,
|
||||
size_t size, int flags)
|
||||
@@ -2482,6 +2483,24 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
case TCP_ULP: {
|
||||
char name[TCP_ULP_NAME_MAX];
|
||||
|
||||
if (optlen < 1)
|
||||
return -EINVAL;
|
||||
|
||||
val = strncpy_from_user(name, optval,
|
||||
min_t(long, TCP_ULP_NAME_MAX - 1,
|
||||
optlen));
|
||||
if (val < 0)
|
||||
return -EFAULT;
|
||||
name[val] = 0;
|
||||
|
||||
lock_sock(sk);
|
||||
err = tcp_set_ulp(sk, name);
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
default:
|
||||
/* fallthru */
|
||||
break;
|
||||
@@ -3038,6 +3057,16 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case TCP_ULP:
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
len = min_t(unsigned int, len, TCP_ULP_NAME_MAX);
|
||||
if (put_user(len, optlen))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(optval, icsk->icsk_ulp_ops->name, len))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case TCP_THIN_LINEAR_TIMEOUTS:
|
||||
val = tp->thin_lto;
|
||||
break;
|
||||
|
||||
@@ -1860,6 +1860,8 @@ void tcp_v4_destroy_sock(struct sock *sk)
|
||||
|
||||
tcp_cleanup_congestion_control(sk);
|
||||
|
||||
tcp_cleanup_ulp(sk);
|
||||
|
||||
/* Cleanup up the write buffer. */
|
||||
tcp_write_queue_purge(sk);
|
||||
|
||||
|
||||
@@ -185,3 +185,4 @@ void tcp_rate_check_app_limited(struct sock *sk)
|
||||
tp->app_limited =
|
||||
(tp->delivered + tcp_packets_in_flight(tp)) ? : 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tcp_rate_check_app_limited);
|
||||
|
||||
134
net/ipv4/tcp_ulp.c
Normal file
134
net/ipv4/tcp_ulp.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Pluggable TCP upper layer protocol support.
|
||||
*
|
||||
* Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2016-2017, Dave Watson <davejwatson@fb.com>. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include<linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <net/tcp.h>
|
||||
|
||||
static DEFINE_SPINLOCK(tcp_ulp_list_lock);
|
||||
static LIST_HEAD(tcp_ulp_list);
|
||||
|
||||
/* Simple linear search, don't expect many entries! */
|
||||
static struct tcp_ulp_ops *tcp_ulp_find(const char *name)
|
||||
{
|
||||
struct tcp_ulp_ops *e;
|
||||
|
||||
list_for_each_entry_rcu(e, &tcp_ulp_list, list) {
|
||||
if (strcmp(e->name, name) == 0)
|
||||
return e;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct tcp_ulp_ops *__tcp_ulp_find_autoload(const char *name)
|
||||
{
|
||||
const struct tcp_ulp_ops *ulp = NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
ulp = tcp_ulp_find(name);
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
if (!ulp && capable(CAP_NET_ADMIN)) {
|
||||
rcu_read_unlock();
|
||||
request_module("%s", name);
|
||||
rcu_read_lock();
|
||||
ulp = tcp_ulp_find(name);
|
||||
}
|
||||
#endif
|
||||
if (!ulp || !try_module_get(ulp->owner))
|
||||
ulp = NULL;
|
||||
|
||||
rcu_read_unlock();
|
||||
return ulp;
|
||||
}
|
||||
|
||||
/* Attach new upper layer protocol to the list
|
||||
* of available protocols.
|
||||
*/
|
||||
int tcp_register_ulp(struct tcp_ulp_ops *ulp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&tcp_ulp_list_lock);
|
||||
if (tcp_ulp_find(ulp->name)) {
|
||||
pr_notice("%s already registered or non-unique name\n",
|
||||
ulp->name);
|
||||
ret = -EEXIST;
|
||||
} else {
|
||||
list_add_tail_rcu(&ulp->list, &tcp_ulp_list);
|
||||
}
|
||||
spin_unlock(&tcp_ulp_list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tcp_register_ulp);
|
||||
|
||||
void tcp_unregister_ulp(struct tcp_ulp_ops *ulp)
|
||||
{
|
||||
spin_lock(&tcp_ulp_list_lock);
|
||||
list_del_rcu(&ulp->list);
|
||||
spin_unlock(&tcp_ulp_list_lock);
|
||||
|
||||
synchronize_rcu();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tcp_unregister_ulp);
|
||||
|
||||
/* Build string with list of available upper layer protocl values */
|
||||
void tcp_get_available_ulp(char *buf, size_t maxlen)
|
||||
{
|
||||
struct tcp_ulp_ops *ulp_ops;
|
||||
size_t offs = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(ulp_ops, &tcp_ulp_list, list) {
|
||||
offs += snprintf(buf + offs, maxlen - offs,
|
||||
"%s%s",
|
||||
offs == 0 ? "" : " ", ulp_ops->name);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void tcp_cleanup_ulp(struct sock *sk)
|
||||
{
|
||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
||||
|
||||
if (!icsk->icsk_ulp_ops)
|
||||
return;
|
||||
|
||||
if (icsk->icsk_ulp_ops->release)
|
||||
icsk->icsk_ulp_ops->release(sk);
|
||||
module_put(icsk->icsk_ulp_ops->owner);
|
||||
}
|
||||
|
||||
/* Change upper layer protocol for socket */
|
||||
int tcp_set_ulp(struct sock *sk, const char *name)
|
||||
{
|
||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
||||
const struct tcp_ulp_ops *ulp_ops;
|
||||
int err = 0;
|
||||
|
||||
if (icsk->icsk_ulp_ops)
|
||||
return -EEXIST;
|
||||
|
||||
ulp_ops = __tcp_ulp_find_autoload(name);
|
||||
if (!ulp_ops)
|
||||
err = -ENOENT;
|
||||
else
|
||||
err = ulp_ops->init(sk);
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
icsk->icsk_ulp_ops = ulp_ops;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
12
net/tls/Kconfig
Normal file
12
net/tls/Kconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# TLS configuration
|
||||
#
|
||||
config TLS
|
||||
tristate "Transport Layer Security support"
|
||||
depends on NET
|
||||
default m
|
||||
---help---
|
||||
Enable kernel support for TLS protocol. This allows symmetric
|
||||
encryption handling of the TLS protocol to be done in-kernel.
|
||||
|
||||
If unsure, say M.
|
||||
7
net/tls/Makefile
Normal file
7
net/tls/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for the TLS subsystem.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_TLS) += tls.o
|
||||
|
||||
tls-y := tls_main.o tls_sw.o
|
||||
487
net/tls/tls_main.c
Normal file
487
net/tls/tls_main.c
Normal file
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2016-2017, Dave Watson <davejwatson@fb.com>. All rights reserved.
|
||||
*
|
||||
* This software is available to you under a choice of one of two
|
||||
* licenses. You may choose to be licensed under the terms of the GNU
|
||||
* General Public License (GPL) Version 2, available from the file
|
||||
* COPYING in the main directory of this source tree, or the
|
||||
* OpenIB.org BSD license below:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <net/tcp.h>
|
||||
#include <net/inet_common.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/sched/signal.h>
|
||||
|
||||
#include <net/tls.h>
|
||||
|
||||
MODULE_AUTHOR("Mellanox Technologies");
|
||||
MODULE_DESCRIPTION("Transport Layer Security Support");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
static struct proto tls_base_prot;
|
||||
static struct proto tls_sw_prot;
|
||||
|
||||
int wait_on_pending_writer(struct sock *sk, long *timeo)
|
||||
{
|
||||
int rc = 0;
|
||||
DEFINE_WAIT_FUNC(wait, woken_wake_function);
|
||||
|
||||
add_wait_queue(sk_sleep(sk), &wait);
|
||||
while (1) {
|
||||
if (!*timeo) {
|
||||
rc = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (signal_pending(current)) {
|
||||
rc = sock_intr_errno(*timeo);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sk_wait_event(sk, timeo, !sk->sk_write_pending, &wait))
|
||||
break;
|
||||
}
|
||||
remove_wait_queue(sk_sleep(sk), &wait);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tls_push_sg(struct sock *sk,
|
||||
struct tls_context *ctx,
|
||||
struct scatterlist *sg,
|
||||
u16 first_offset,
|
||||
int flags)
|
||||
{
|
||||
int sendpage_flags = flags | MSG_SENDPAGE_NOTLAST;
|
||||
int ret = 0;
|
||||
struct page *p;
|
||||
size_t size;
|
||||
int offset = first_offset;
|
||||
|
||||
size = sg->length - offset;
|
||||
offset += sg->offset;
|
||||
|
||||
while (1) {
|
||||
if (sg_is_last(sg))
|
||||
sendpage_flags = flags;
|
||||
|
||||
/* is sending application-limited? */
|
||||
tcp_rate_check_app_limited(sk);
|
||||
p = sg_page(sg);
|
||||
retry:
|
||||
ret = do_tcp_sendpages(sk, p, offset, size, sendpage_flags);
|
||||
|
||||
if (ret != size) {
|
||||
if (ret > 0) {
|
||||
offset += ret;
|
||||
size -= ret;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
offset -= sg->offset;
|
||||
ctx->partially_sent_offset = offset;
|
||||
ctx->partially_sent_record = (void *)sg;
|
||||
return ret;
|
||||
}
|
||||
|
||||
put_page(p);
|
||||
sk_mem_uncharge(sk, sg->length);
|
||||
sg = sg_next(sg);
|
||||
if (!sg)
|
||||
break;
|
||||
|
||||
offset = sg->offset;
|
||||
size = sg->length;
|
||||
}
|
||||
|
||||
clear_bit(TLS_PENDING_CLOSED_RECORD, &ctx->flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tls_handle_open_record(struct sock *sk, int flags)
|
||||
{
|
||||
struct tls_context *ctx = tls_get_ctx(sk);
|
||||
|
||||
if (tls_is_pending_open_record(ctx))
|
||||
return ctx->push_pending_record(sk, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tls_proccess_cmsg(struct sock *sk, struct msghdr *msg,
|
||||
unsigned char *record_type)
|
||||
{
|
||||
struct cmsghdr *cmsg;
|
||||
int rc = -EINVAL;
|
||||
|
||||
for_each_cmsghdr(cmsg, msg) {
|
||||
if (!CMSG_OK(msg, cmsg))
|
||||
return -EINVAL;
|
||||
if (cmsg->cmsg_level != SOL_TLS)
|
||||
continue;
|
||||
|
||||
switch (cmsg->cmsg_type) {
|
||||
case TLS_SET_RECORD_TYPE:
|
||||
if (cmsg->cmsg_len < CMSG_LEN(sizeof(*record_type)))
|
||||
return -EINVAL;
|
||||
|
||||
if (msg->msg_flags & MSG_MORE)
|
||||
return -EINVAL;
|
||||
|
||||
rc = tls_handle_open_record(sk, msg->msg_flags);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*record_type = *(unsigned char *)CMSG_DATA(cmsg);
|
||||
rc = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tls_push_pending_closed_record(struct sock *sk, struct tls_context *ctx,
|
||||
int flags, long *timeo)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
u16 offset;
|
||||
|
||||
if (!tls_is_partially_sent_record(ctx))
|
||||
return ctx->push_pending_record(sk, flags);
|
||||
|
||||
sg = ctx->partially_sent_record;
|
||||
offset = ctx->partially_sent_offset;
|
||||
|
||||
ctx->partially_sent_record = NULL;
|
||||
return tls_push_sg(sk, ctx, sg, offset, flags);
|
||||
}
|
||||
|
||||
static void tls_write_space(struct sock *sk)
|
||||
{
|
||||
struct tls_context *ctx = tls_get_ctx(sk);
|
||||
|
||||
if (!sk->sk_write_pending && tls_is_pending_closed_record(ctx)) {
|
||||
gfp_t sk_allocation = sk->sk_allocation;
|
||||
int rc;
|
||||
long timeo = 0;
|
||||
|
||||
sk->sk_allocation = GFP_ATOMIC;
|
||||
rc = tls_push_pending_closed_record(sk, ctx,
|
||||
MSG_DONTWAIT |
|
||||
MSG_NOSIGNAL,
|
||||
&timeo);
|
||||
sk->sk_allocation = sk_allocation;
|
||||
|
||||
if (rc < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->sk_write_space(sk);
|
||||
}
|
||||
|
||||
static void tls_sk_proto_close(struct sock *sk, long timeout)
|
||||
{
|
||||
struct tls_context *ctx = tls_get_ctx(sk);
|
||||
long timeo = sock_sndtimeo(sk, 0);
|
||||
void (*sk_proto_close)(struct sock *sk, long timeout);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (!tls_complete_pending_work(sk, ctx, 0, &timeo))
|
||||
tls_handle_open_record(sk, 0);
|
||||
|
||||
if (ctx->partially_sent_record) {
|
||||
struct scatterlist *sg = ctx->partially_sent_record;
|
||||
|
||||
while (1) {
|
||||
put_page(sg_page(sg));
|
||||
sk_mem_uncharge(sk, sg->length);
|
||||
|
||||
if (sg_is_last(sg))
|
||||
break;
|
||||
sg++;
|
||||
}
|
||||
}
|
||||
ctx->free_resources(sk);
|
||||
kfree(ctx->rec_seq);
|
||||
kfree(ctx->iv);
|
||||
|
||||
sk_proto_close = ctx->sk_proto_close;
|
||||
kfree(ctx);
|
||||
|
||||
release_sock(sk);
|
||||
sk_proto_close(sk, timeout);
|
||||
}
|
||||
|
||||
static int do_tls_getsockopt_tx(struct sock *sk, char __user *optval,
|
||||
int __user *optlen)
|
||||
{
|
||||
int rc = 0;
|
||||
struct tls_context *ctx = tls_get_ctx(sk);
|
||||
struct tls_crypto_info *crypto_info;
|
||||
int len;
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
if (!optval || (len < sizeof(*crypto_info))) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ctx) {
|
||||
rc = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* get user crypto info */
|
||||
crypto_info = &ctx->crypto_send;
|
||||
|
||||
if (!TLS_CRYPTO_INFO_READY(crypto_info)) {
|
||||
rc = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len == sizeof(crypto_info)) {
|
||||
rc = copy_to_user(optval, crypto_info, sizeof(*crypto_info));
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (crypto_info->cipher_type) {
|
||||
case TLS_CIPHER_AES_GCM_128: {
|
||||
struct tls12_crypto_info_aes_gcm_128 *
|
||||
crypto_info_aes_gcm_128 =
|
||||
container_of(crypto_info,
|
||||
struct tls12_crypto_info_aes_gcm_128,
|
||||
info);
|
||||
|
||||
if (len != sizeof(*crypto_info_aes_gcm_128)) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
lock_sock(sk);
|
||||
memcpy(crypto_info_aes_gcm_128->iv, ctx->iv,
|
||||
TLS_CIPHER_AES_GCM_128_IV_SIZE);
|
||||
release_sock(sk);
|
||||
rc = copy_to_user(optval,
|
||||
crypto_info_aes_gcm_128,
|
||||
sizeof(*crypto_info_aes_gcm_128));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_tls_getsockopt(struct sock *sk, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (optname) {
|
||||
case TLS_TX:
|
||||
rc = do_tls_getsockopt_tx(sk, optval, optlen);
|
||||
break;
|
||||
default:
|
||||
rc = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tls_getsockopt(struct sock *sk, int level, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct tls_context *ctx = tls_get_ctx(sk);
|
||||
|
||||
if (level != SOL_TLS)
|
||||
return ctx->getsockopt(sk, level, optname, optval, optlen);
|
||||
|
||||
return do_tls_getsockopt(sk, optname, optval, optlen);
|
||||
}
|
||||
|
||||
static int do_tls_setsockopt_tx(struct sock *sk, char __user *optval,
|
||||
unsigned int optlen)
|
||||
{
|
||||
struct tls_crypto_info *crypto_info, tmp_crypto_info;
|
||||
struct tls_context *ctx = tls_get_ctx(sk);
|
||||
struct proto *prot = NULL;
|
||||
int rc = 0;
|
||||
|
||||
if (!optval || (optlen < sizeof(*crypto_info))) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = copy_from_user(&tmp_crypto_info, optval, sizeof(*crypto_info));
|
||||
if (rc) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check version */
|
||||
if (tmp_crypto_info.version != TLS_1_2_VERSION) {
|
||||
rc = -ENOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* get user crypto info */
|
||||
crypto_info = &ctx->crypto_send;
|
||||
|
||||
/* Currently we don't support set crypto info more than one time */
|
||||
if (TLS_CRYPTO_INFO_READY(crypto_info))
|
||||
goto out;
|
||||
|
||||
switch (tmp_crypto_info.cipher_type) {
|
||||
case TLS_CIPHER_AES_GCM_128: {
|
||||
if (optlen != sizeof(struct tls12_crypto_info_aes_gcm_128)) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rc = copy_from_user(
|
||||
crypto_info,
|
||||
optval,
|
||||
sizeof(struct tls12_crypto_info_aes_gcm_128));
|
||||
|
||||
if (rc) {
|
||||
rc = -EFAULT;
|
||||
goto err_crypto_info;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->sk_write_space = sk->sk_write_space;
|
||||
sk->sk_write_space = tls_write_space;
|
||||
|
||||
ctx->sk_proto_close = sk->sk_prot->close;
|
||||
|
||||
/* currently SW is default, we will have ethtool in future */
|
||||
rc = tls_set_sw_offload(sk, ctx);
|
||||
prot = &tls_sw_prot;
|
||||
if (rc)
|
||||
goto err_crypto_info;
|
||||
|
||||
sk->sk_prot = prot;
|
||||
goto out;
|
||||
|
||||
err_crypto_info:
|
||||
memset(crypto_info, 0, sizeof(*crypto_info));
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_tls_setsockopt(struct sock *sk, int optname,
|
||||
char __user *optval, unsigned int optlen)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (optname) {
|
||||
case TLS_TX:
|
||||
lock_sock(sk);
|
||||
rc = do_tls_setsockopt_tx(sk, optval, optlen);
|
||||
release_sock(sk);
|
||||
break;
|
||||
default:
|
||||
rc = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tls_setsockopt(struct sock *sk, int level, int optname,
|
||||
char __user *optval, unsigned int optlen)
|
||||
{
|
||||
struct tls_context *ctx = tls_get_ctx(sk);
|
||||
|
||||
if (level != SOL_TLS)
|
||||
return ctx->setsockopt(sk, level, optname, optval, optlen);
|
||||
|
||||
return do_tls_setsockopt(sk, optname, optval, optlen);
|
||||
}
|
||||
|
||||
static int tls_init(struct sock *sk)
|
||||
{
|
||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
||||
struct tls_context *ctx;
|
||||
int rc = 0;
|
||||
|
||||
/* allocate tls context */
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
icsk->icsk_ulp_data = ctx;
|
||||
ctx->setsockopt = sk->sk_prot->setsockopt;
|
||||
ctx->getsockopt = sk->sk_prot->getsockopt;
|
||||
sk->sk_prot = &tls_base_prot;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct tcp_ulp_ops tcp_tls_ulp_ops __read_mostly = {
|
||||
.name = "tls",
|
||||
.owner = THIS_MODULE,
|
||||
.init = tls_init,
|
||||
};
|
||||
|
||||
static int __init tls_register(void)
|
||||
{
|
||||
tls_base_prot = tcp_prot;
|
||||
tls_base_prot.setsockopt = tls_setsockopt;
|
||||
tls_base_prot.getsockopt = tls_getsockopt;
|
||||
|
||||
tls_sw_prot = tls_base_prot;
|
||||
tls_sw_prot.sendmsg = tls_sw_sendmsg;
|
||||
tls_sw_prot.sendpage = tls_sw_sendpage;
|
||||
tls_sw_prot.close = tls_sk_proto_close;
|
||||
|
||||
tcp_register_ulp(&tcp_tls_ulp_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit tls_unregister(void)
|
||||
{
|
||||
tcp_unregister_ulp(&tcp_tls_ulp_ops);
|
||||
}
|
||||
|
||||
module_init(tls_register);
|
||||
module_exit(tls_unregister);
|
||||
772
net/tls/tls_sw.c
Normal file
772
net/tls/tls_sw.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user