| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved. | 
|  | 3 | * Copyright (c) 2016-2017, Dave Watson <davejwatson@fb.com>. All rights reserved. | 
|  | 4 | * | 
|  | 5 | * This software is available to you under a choice of one of two | 
|  | 6 | * licenses.  You may choose to be licensed under the terms of the GNU | 
|  | 7 | * General Public License (GPL) Version 2, available from the file | 
|  | 8 | * COPYING in the main directory of this source tree, or the | 
|  | 9 | * OpenIB.org BSD license below: | 
|  | 10 | * | 
|  | 11 | *     Redistribution and use in source and binary forms, with or | 
|  | 12 | *     without modification, are permitted provided that the following | 
|  | 13 | *     conditions are met: | 
|  | 14 | * | 
|  | 15 | *      - Redistributions of source code must retain the above | 
|  | 16 | *        copyright notice, this list of conditions and the following | 
|  | 17 | *        disclaimer. | 
|  | 18 | * | 
|  | 19 | *      - Redistributions in binary form must reproduce the above | 
|  | 20 | *        copyright notice, this list of conditions and the following | 
|  | 21 | *        disclaimer in the documentation and/or other materials | 
|  | 22 | *        provided with the distribution. | 
|  | 23 | * | 
|  | 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
|  | 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
|  | 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
|  | 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | 
|  | 28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | 
|  | 29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
|  | 30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
|  | 31 | * SOFTWARE. | 
|  | 32 | */ | 
|  | 33 |  | 
|  | 34 | #include <linux/module.h> | 
|  | 35 |  | 
|  | 36 | #include <net/tcp.h> | 
|  | 37 | #include <net/inet_common.h> | 
|  | 38 | #include <linux/highmem.h> | 
|  | 39 | #include <linux/netdevice.h> | 
|  | 40 | #include <linux/sched/signal.h> | 
| Atul Gupta | dd0bed1 | 2018-03-31 21:41:52 +0530 | [diff] [blame] | 41 | #include <linux/inetdevice.h> | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 42 |  | 
|  | 43 | #include <net/tls.h> | 
|  | 44 |  | 
|  | 45 | MODULE_AUTHOR("Mellanox Technologies"); | 
|  | 46 | MODULE_DESCRIPTION("Transport Layer Security Support"); | 
|  | 47 | MODULE_LICENSE("Dual BSD/GPL"); | 
|  | 48 |  | 
| Ilya Lesokhin | 6d88207 | 2017-11-13 10:22:45 +0200 | [diff] [blame] | 49 | enum { | 
| Boris Pismenny | c113187 | 2018-02-27 14:18:39 +0200 | [diff] [blame] | 50 | TLSV4, | 
|  | 51 | TLSV6, | 
|  | 52 | TLS_NUM_PROTS, | 
|  | 53 | }; | 
|  | 54 |  | 
|  | 55 | enum { | 
| Dave Watson | 5837158 | 2018-03-22 10:10:26 -0700 | [diff] [blame] | 56 | TLS_BASE, | 
| Ilya Lesokhin | 6d88207 | 2017-11-13 10:22:45 +0200 | [diff] [blame] | 57 | TLS_SW_TX, | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 58 | TLS_SW_RX, | 
|  | 59 | TLS_SW_RXTX, | 
| Atul Gupta | dd0bed1 | 2018-03-31 21:41:52 +0530 | [diff] [blame] | 60 | TLS_HW_RECORD, | 
| Ilya Lesokhin | 6d88207 | 2017-11-13 10:22:45 +0200 | [diff] [blame] | 61 | TLS_NUM_CONFIG, | 
|  | 62 | }; | 
|  | 63 |  | 
| Boris Pismenny | c113187 | 2018-02-27 14:18:39 +0200 | [diff] [blame] | 64 | static struct proto *saved_tcpv6_prot; | 
|  | 65 | static DEFINE_MUTEX(tcpv6_prot_mutex); | 
| Atul Gupta | dd0bed1 | 2018-03-31 21:41:52 +0530 | [diff] [blame] | 66 | static LIST_HEAD(device_list); | 
|  | 67 | static DEFINE_MUTEX(device_mutex); | 
| Boris Pismenny | c113187 | 2018-02-27 14:18:39 +0200 | [diff] [blame] | 68 | static struct proto tls_prots[TLS_NUM_PROTS][TLS_NUM_CONFIG]; | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 69 | static struct proto_ops tls_sw_proto_ops; | 
| Ilya Lesokhin | 6d88207 | 2017-11-13 10:22:45 +0200 | [diff] [blame] | 70 |  | 
|  | 71 | static inline void update_sk_prot(struct sock *sk, struct tls_context *ctx) | 
|  | 72 | { | 
| Boris Pismenny | c113187 | 2018-02-27 14:18:39 +0200 | [diff] [blame] | 73 | int ip_ver = sk->sk_family == AF_INET6 ? TLSV6 : TLSV4; | 
|  | 74 |  | 
| Dave Watson | 5837158 | 2018-03-22 10:10:26 -0700 | [diff] [blame] | 75 | sk->sk_prot = &tls_prots[ip_ver][ctx->conf]; | 
| Ilya Lesokhin | 6d88207 | 2017-11-13 10:22:45 +0200 | [diff] [blame] | 76 | } | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 77 |  | 
|  | 78 | int wait_on_pending_writer(struct sock *sk, long *timeo) | 
|  | 79 | { | 
|  | 80 | int rc = 0; | 
|  | 81 | DEFINE_WAIT_FUNC(wait, woken_wake_function); | 
|  | 82 |  | 
|  | 83 | add_wait_queue(sk_sleep(sk), &wait); | 
|  | 84 | while (1) { | 
|  | 85 | if (!*timeo) { | 
|  | 86 | rc = -EAGAIN; | 
|  | 87 | break; | 
|  | 88 | } | 
|  | 89 |  | 
|  | 90 | if (signal_pending(current)) { | 
|  | 91 | rc = sock_intr_errno(*timeo); | 
|  | 92 | break; | 
|  | 93 | } | 
|  | 94 |  | 
|  | 95 | if (sk_wait_event(sk, timeo, !sk->sk_write_pending, &wait)) | 
|  | 96 | break; | 
|  | 97 | } | 
|  | 98 | remove_wait_queue(sk_sleep(sk), &wait); | 
|  | 99 | return rc; | 
|  | 100 | } | 
|  | 101 |  | 
|  | 102 | int tls_push_sg(struct sock *sk, | 
|  | 103 | struct tls_context *ctx, | 
|  | 104 | struct scatterlist *sg, | 
|  | 105 | u16 first_offset, | 
|  | 106 | int flags) | 
|  | 107 | { | 
|  | 108 | int sendpage_flags = flags | MSG_SENDPAGE_NOTLAST; | 
|  | 109 | int ret = 0; | 
|  | 110 | struct page *p; | 
|  | 111 | size_t size; | 
|  | 112 | int offset = first_offset; | 
|  | 113 |  | 
|  | 114 | size = sg->length - offset; | 
|  | 115 | offset += sg->offset; | 
|  | 116 |  | 
|  | 117 | while (1) { | 
|  | 118 | if (sg_is_last(sg)) | 
|  | 119 | sendpage_flags = flags; | 
|  | 120 |  | 
|  | 121 | /* is sending application-limited? */ | 
|  | 122 | tcp_rate_check_app_limited(sk); | 
|  | 123 | p = sg_page(sg); | 
|  | 124 | retry: | 
|  | 125 | ret = do_tcp_sendpages(sk, p, offset, size, sendpage_flags); | 
|  | 126 |  | 
|  | 127 | if (ret != size) { | 
|  | 128 | if (ret > 0) { | 
|  | 129 | offset += ret; | 
|  | 130 | size -= ret; | 
|  | 131 | goto retry; | 
|  | 132 | } | 
|  | 133 |  | 
|  | 134 | offset -= sg->offset; | 
|  | 135 | ctx->partially_sent_offset = offset; | 
|  | 136 | ctx->partially_sent_record = (void *)sg; | 
|  | 137 | return ret; | 
|  | 138 | } | 
|  | 139 |  | 
|  | 140 | put_page(p); | 
|  | 141 | sk_mem_uncharge(sk, sg->length); | 
|  | 142 | sg = sg_next(sg); | 
|  | 143 | if (!sg) | 
|  | 144 | break; | 
|  | 145 |  | 
|  | 146 | offset = sg->offset; | 
|  | 147 | size = sg->length; | 
|  | 148 | } | 
|  | 149 |  | 
|  | 150 | clear_bit(TLS_PENDING_CLOSED_RECORD, &ctx->flags); | 
|  | 151 |  | 
|  | 152 | return 0; | 
|  | 153 | } | 
|  | 154 |  | 
|  | 155 | static int tls_handle_open_record(struct sock *sk, int flags) | 
|  | 156 | { | 
|  | 157 | struct tls_context *ctx = tls_get_ctx(sk); | 
|  | 158 |  | 
|  | 159 | if (tls_is_pending_open_record(ctx)) | 
|  | 160 | return ctx->push_pending_record(sk, flags); | 
|  | 161 |  | 
|  | 162 | return 0; | 
|  | 163 | } | 
|  | 164 |  | 
|  | 165 | int tls_proccess_cmsg(struct sock *sk, struct msghdr *msg, | 
|  | 166 | unsigned char *record_type) | 
|  | 167 | { | 
|  | 168 | struct cmsghdr *cmsg; | 
|  | 169 | int rc = -EINVAL; | 
|  | 170 |  | 
|  | 171 | for_each_cmsghdr(cmsg, msg) { | 
|  | 172 | if (!CMSG_OK(msg, cmsg)) | 
|  | 173 | return -EINVAL; | 
|  | 174 | if (cmsg->cmsg_level != SOL_TLS) | 
|  | 175 | continue; | 
|  | 176 |  | 
|  | 177 | switch (cmsg->cmsg_type) { | 
|  | 178 | case TLS_SET_RECORD_TYPE: | 
|  | 179 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(*record_type))) | 
|  | 180 | return -EINVAL; | 
|  | 181 |  | 
|  | 182 | if (msg->msg_flags & MSG_MORE) | 
|  | 183 | return -EINVAL; | 
|  | 184 |  | 
|  | 185 | rc = tls_handle_open_record(sk, msg->msg_flags); | 
|  | 186 | if (rc) | 
|  | 187 | return rc; | 
|  | 188 |  | 
|  | 189 | *record_type = *(unsigned char *)CMSG_DATA(cmsg); | 
|  | 190 | rc = 0; | 
|  | 191 | break; | 
|  | 192 | default: | 
|  | 193 | return -EINVAL; | 
|  | 194 | } | 
|  | 195 | } | 
|  | 196 |  | 
|  | 197 | return rc; | 
|  | 198 | } | 
|  | 199 |  | 
|  | 200 | int tls_push_pending_closed_record(struct sock *sk, struct tls_context *ctx, | 
|  | 201 | int flags, long *timeo) | 
|  | 202 | { | 
|  | 203 | struct scatterlist *sg; | 
|  | 204 | u16 offset; | 
|  | 205 |  | 
|  | 206 | if (!tls_is_partially_sent_record(ctx)) | 
|  | 207 | return ctx->push_pending_record(sk, flags); | 
|  | 208 |  | 
|  | 209 | sg = ctx->partially_sent_record; | 
|  | 210 | offset = ctx->partially_sent_offset; | 
|  | 211 |  | 
|  | 212 | ctx->partially_sent_record = NULL; | 
|  | 213 | return tls_push_sg(sk, ctx, sg, offset, flags); | 
|  | 214 | } | 
|  | 215 |  | 
|  | 216 | static void tls_write_space(struct sock *sk) | 
|  | 217 | { | 
|  | 218 | struct tls_context *ctx = tls_get_ctx(sk); | 
|  | 219 |  | 
|  | 220 | if (!sk->sk_write_pending && tls_is_pending_closed_record(ctx)) { | 
|  | 221 | gfp_t sk_allocation = sk->sk_allocation; | 
|  | 222 | int rc; | 
|  | 223 | long timeo = 0; | 
|  | 224 |  | 
|  | 225 | sk->sk_allocation = GFP_ATOMIC; | 
|  | 226 | rc = tls_push_pending_closed_record(sk, ctx, | 
|  | 227 | MSG_DONTWAIT | | 
|  | 228 | MSG_NOSIGNAL, | 
|  | 229 | &timeo); | 
|  | 230 | sk->sk_allocation = sk_allocation; | 
|  | 231 |  | 
|  | 232 | if (rc < 0) | 
|  | 233 | return; | 
|  | 234 | } | 
|  | 235 |  | 
|  | 236 | ctx->sk_write_space(sk); | 
|  | 237 | } | 
|  | 238 |  | 
|  | 239 | static void tls_sk_proto_close(struct sock *sk, long timeout) | 
|  | 240 | { | 
|  | 241 | struct tls_context *ctx = tls_get_ctx(sk); | 
|  | 242 | long timeo = sock_sndtimeo(sk, 0); | 
|  | 243 | void (*sk_proto_close)(struct sock *sk, long timeout); | 
|  | 244 |  | 
|  | 245 | lock_sock(sk); | 
| Ilya Lesokhin | ff45d82 | 2017-11-13 10:22:46 +0200 | [diff] [blame] | 246 | sk_proto_close = ctx->sk_proto_close; | 
|  | 247 |  | 
| Atul Gupta | dd0bed1 | 2018-03-31 21:41:52 +0530 | [diff] [blame] | 248 | if (ctx->conf == TLS_HW_RECORD) | 
|  | 249 | goto skip_tx_cleanup; | 
|  | 250 |  | 
| Dave Watson | 5837158 | 2018-03-22 10:10:26 -0700 | [diff] [blame] | 251 | if (ctx->conf == TLS_BASE) { | 
| Ilya Lesokhin | ff45d82 | 2017-11-13 10:22:46 +0200 | [diff] [blame] | 252 | kfree(ctx); | 
| Atul Gupta | dd0bed1 | 2018-03-31 21:41:52 +0530 | [diff] [blame] | 253 | ctx = NULL; | 
| Ilya Lesokhin | ff45d82 | 2017-11-13 10:22:46 +0200 | [diff] [blame] | 254 | goto skip_tx_cleanup; | 
|  | 255 | } | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 256 |  | 
|  | 257 | if (!tls_complete_pending_work(sk, ctx, 0, &timeo)) | 
|  | 258 | tls_handle_open_record(sk, 0); | 
|  | 259 |  | 
|  | 260 | if (ctx->partially_sent_record) { | 
|  | 261 | struct scatterlist *sg = ctx->partially_sent_record; | 
|  | 262 |  | 
|  | 263 | while (1) { | 
|  | 264 | put_page(sg_page(sg)); | 
|  | 265 | sk_mem_uncharge(sk, sg->length); | 
|  | 266 |  | 
|  | 267 | if (sg_is_last(sg)) | 
|  | 268 | break; | 
|  | 269 | sg++; | 
|  | 270 | } | 
|  | 271 | } | 
| Ilya Lesokhin | ff45d82 | 2017-11-13 10:22:46 +0200 | [diff] [blame] | 272 |  | 
| Dave Watson | dbe4255 | 2018-03-22 10:10:06 -0700 | [diff] [blame] | 273 | kfree(ctx->tx.rec_seq); | 
|  | 274 | kfree(ctx->tx.iv); | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 275 | kfree(ctx->rx.rec_seq); | 
|  | 276 | kfree(ctx->rx.iv); | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 277 |  | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 278 | if (ctx->conf == TLS_SW_TX || | 
|  | 279 | ctx->conf == TLS_SW_RX || | 
|  | 280 | ctx->conf == TLS_SW_RXTX) { | 
|  | 281 | tls_sw_free_resources(sk); | 
|  | 282 | } | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 283 |  | 
| Ilya Lesokhin | ff45d82 | 2017-11-13 10:22:46 +0200 | [diff] [blame] | 284 | skip_tx_cleanup: | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 285 | release_sock(sk); | 
|  | 286 | sk_proto_close(sk, timeout); | 
| Atul Gupta | dd0bed1 | 2018-03-31 21:41:52 +0530 | [diff] [blame] | 287 | /* free ctx for TLS_HW_RECORD, used by tcp_set_state | 
|  | 288 | * for sk->sk_prot->unhash [tls_hw_unhash] | 
|  | 289 | */ | 
|  | 290 | if (ctx && ctx->conf == TLS_HW_RECORD) | 
|  | 291 | kfree(ctx); | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 292 | } | 
|  | 293 |  | 
|  | 294 | static int do_tls_getsockopt_tx(struct sock *sk, char __user *optval, | 
|  | 295 | int __user *optlen) | 
|  | 296 | { | 
|  | 297 | int rc = 0; | 
|  | 298 | struct tls_context *ctx = tls_get_ctx(sk); | 
|  | 299 | struct tls_crypto_info *crypto_info; | 
|  | 300 | int len; | 
|  | 301 |  | 
|  | 302 | if (get_user(len, optlen)) | 
|  | 303 | return -EFAULT; | 
|  | 304 |  | 
|  | 305 | if (!optval || (len < sizeof(*crypto_info))) { | 
|  | 306 | rc = -EINVAL; | 
|  | 307 | goto out; | 
|  | 308 | } | 
|  | 309 |  | 
|  | 310 | if (!ctx) { | 
|  | 311 | rc = -EBUSY; | 
|  | 312 | goto out; | 
|  | 313 | } | 
|  | 314 |  | 
|  | 315 | /* get user crypto info */ | 
|  | 316 | crypto_info = &ctx->crypto_send; | 
|  | 317 |  | 
|  | 318 | if (!TLS_CRYPTO_INFO_READY(crypto_info)) { | 
|  | 319 | rc = -EBUSY; | 
|  | 320 | goto out; | 
|  | 321 | } | 
|  | 322 |  | 
| Matthias Rosenfelder | 5a3b886 | 2017-07-06 00:56:36 -0400 | [diff] [blame] | 323 | if (len == sizeof(*crypto_info)) { | 
| Dan Carpenter | ac55cd6 | 2017-06-23 13:15:44 +0300 | [diff] [blame] | 324 | if (copy_to_user(optval, crypto_info, sizeof(*crypto_info))) | 
|  | 325 | rc = -EFAULT; | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 326 | goto out; | 
|  | 327 | } | 
|  | 328 |  | 
|  | 329 | switch (crypto_info->cipher_type) { | 
|  | 330 | case TLS_CIPHER_AES_GCM_128: { | 
|  | 331 | struct tls12_crypto_info_aes_gcm_128 * | 
|  | 332 | crypto_info_aes_gcm_128 = | 
|  | 333 | container_of(crypto_info, | 
|  | 334 | struct tls12_crypto_info_aes_gcm_128, | 
|  | 335 | info); | 
|  | 336 |  | 
|  | 337 | if (len != sizeof(*crypto_info_aes_gcm_128)) { | 
|  | 338 | rc = -EINVAL; | 
|  | 339 | goto out; | 
|  | 340 | } | 
|  | 341 | lock_sock(sk); | 
| Boris Pismenny | a1dfa68 | 2018-02-14 10:46:06 +0200 | [diff] [blame] | 342 | memcpy(crypto_info_aes_gcm_128->iv, | 
| Dave Watson | dbe4255 | 2018-03-22 10:10:06 -0700 | [diff] [blame] | 343 | ctx->tx.iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 344 | TLS_CIPHER_AES_GCM_128_IV_SIZE); | 
| Dave Watson | dbe4255 | 2018-03-22 10:10:06 -0700 | [diff] [blame] | 345 | memcpy(crypto_info_aes_gcm_128->rec_seq, ctx->tx.rec_seq, | 
| Boris Pismenny | c410c19 | 2018-02-14 10:46:08 +0200 | [diff] [blame] | 346 | TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 347 | release_sock(sk); | 
| Dan Carpenter | ac55cd6 | 2017-06-23 13:15:44 +0300 | [diff] [blame] | 348 | if (copy_to_user(optval, | 
|  | 349 | crypto_info_aes_gcm_128, | 
|  | 350 | sizeof(*crypto_info_aes_gcm_128))) | 
|  | 351 | rc = -EFAULT; | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 352 | break; | 
|  | 353 | } | 
|  | 354 | default: | 
|  | 355 | rc = -EINVAL; | 
|  | 356 | } | 
|  | 357 |  | 
|  | 358 | out: | 
|  | 359 | return rc; | 
|  | 360 | } | 
|  | 361 |  | 
|  | 362 | static int do_tls_getsockopt(struct sock *sk, int optname, | 
|  | 363 | char __user *optval, int __user *optlen) | 
|  | 364 | { | 
|  | 365 | int rc = 0; | 
|  | 366 |  | 
|  | 367 | switch (optname) { | 
|  | 368 | case TLS_TX: | 
|  | 369 | rc = do_tls_getsockopt_tx(sk, optval, optlen); | 
|  | 370 | break; | 
|  | 371 | default: | 
|  | 372 | rc = -ENOPROTOOPT; | 
|  | 373 | break; | 
|  | 374 | } | 
|  | 375 | return rc; | 
|  | 376 | } | 
|  | 377 |  | 
|  | 378 | static int tls_getsockopt(struct sock *sk, int level, int optname, | 
|  | 379 | char __user *optval, int __user *optlen) | 
|  | 380 | { | 
|  | 381 | struct tls_context *ctx = tls_get_ctx(sk); | 
|  | 382 |  | 
|  | 383 | if (level != SOL_TLS) | 
|  | 384 | return ctx->getsockopt(sk, level, optname, optval, optlen); | 
|  | 385 |  | 
|  | 386 | return do_tls_getsockopt(sk, optname, optval, optlen); | 
|  | 387 | } | 
|  | 388 |  | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 389 | static int do_tls_setsockopt_conf(struct sock *sk, char __user *optval, | 
|  | 390 | unsigned int optlen, int tx) | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 391 | { | 
| Ilya Lesokhin | 196c31b | 2017-11-13 10:22:48 +0200 | [diff] [blame] | 392 | struct tls_crypto_info *crypto_info; | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 393 | struct tls_context *ctx = tls_get_ctx(sk); | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 394 | int rc = 0; | 
| Dave Watson | 5837158 | 2018-03-22 10:10:26 -0700 | [diff] [blame] | 395 | int conf; | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 396 |  | 
|  | 397 | if (!optval || (optlen < sizeof(*crypto_info))) { | 
|  | 398 | rc = -EINVAL; | 
|  | 399 | goto out; | 
|  | 400 | } | 
|  | 401 |  | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 402 | if (tx) | 
|  | 403 | crypto_info = &ctx->crypto_send; | 
|  | 404 | else | 
|  | 405 | crypto_info = &ctx->crypto_recv; | 
|  | 406 |  | 
| Ilya Lesokhin | 196c31b | 2017-11-13 10:22:48 +0200 | [diff] [blame] | 407 | /* Currently we don't support set crypto info more than one time */ | 
| Sabrina Dubroca | 877d17c | 2018-01-16 16:04:27 +0100 | [diff] [blame] | 408 | if (TLS_CRYPTO_INFO_READY(crypto_info)) { | 
|  | 409 | rc = -EBUSY; | 
| Ilya Lesokhin | 196c31b | 2017-11-13 10:22:48 +0200 | [diff] [blame] | 410 | goto out; | 
| Sabrina Dubroca | 877d17c | 2018-01-16 16:04:27 +0100 | [diff] [blame] | 411 | } | 
| Ilya Lesokhin | 196c31b | 2017-11-13 10:22:48 +0200 | [diff] [blame] | 412 |  | 
|  | 413 | rc = copy_from_user(crypto_info, optval, sizeof(*crypto_info)); | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 414 | if (rc) { | 
|  | 415 | rc = -EFAULT; | 
| Boris Pismenny | 257082e | 2018-02-14 10:46:07 +0200 | [diff] [blame] | 416 | goto err_crypto_info; | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 417 | } | 
|  | 418 |  | 
|  | 419 | /* check version */ | 
| Ilya Lesokhin | 196c31b | 2017-11-13 10:22:48 +0200 | [diff] [blame] | 420 | if (crypto_info->version != TLS_1_2_VERSION) { | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 421 | rc = -ENOTSUPP; | 
| Ilya Lesokhin | 196c31b | 2017-11-13 10:22:48 +0200 | [diff] [blame] | 422 | goto err_crypto_info; | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 423 | } | 
|  | 424 |  | 
| Ilya Lesokhin | 196c31b | 2017-11-13 10:22:48 +0200 | [diff] [blame] | 425 | switch (crypto_info->cipher_type) { | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 426 | case TLS_CIPHER_AES_GCM_128: { | 
|  | 427 | if (optlen != sizeof(struct tls12_crypto_info_aes_gcm_128)) { | 
|  | 428 | rc = -EINVAL; | 
| Sabrina Dubroca | 6db959c | 2018-01-16 16:04:28 +0100 | [diff] [blame] | 429 | goto err_crypto_info; | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 430 | } | 
| Ilya Lesokhin | 196c31b | 2017-11-13 10:22:48 +0200 | [diff] [blame] | 431 | rc = copy_from_user(crypto_info + 1, optval + sizeof(*crypto_info), | 
|  | 432 | optlen - sizeof(*crypto_info)); | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 433 | if (rc) { | 
|  | 434 | rc = -EFAULT; | 
|  | 435 | goto err_crypto_info; | 
|  | 436 | } | 
|  | 437 | break; | 
|  | 438 | } | 
|  | 439 | default: | 
|  | 440 | rc = -EINVAL; | 
| Sabrina Dubroca | 6db959c | 2018-01-16 16:04:28 +0100 | [diff] [blame] | 441 | goto err_crypto_info; | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 442 | } | 
|  | 443 |  | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 444 | /* currently SW is default, we will have ethtool in future */ | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 445 | if (tx) { | 
|  | 446 | rc = tls_set_sw_offload(sk, ctx, 1); | 
|  | 447 | if (ctx->conf == TLS_SW_RX) | 
|  | 448 | conf = TLS_SW_RXTX; | 
|  | 449 | else | 
|  | 450 | conf = TLS_SW_TX; | 
|  | 451 | } else { | 
|  | 452 | rc = tls_set_sw_offload(sk, ctx, 0); | 
|  | 453 | if (ctx->conf == TLS_SW_TX) | 
|  | 454 | conf = TLS_SW_RXTX; | 
|  | 455 | else | 
|  | 456 | conf = TLS_SW_RX; | 
|  | 457 | } | 
|  | 458 |  | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 459 | if (rc) | 
|  | 460 | goto err_crypto_info; | 
|  | 461 |  | 
| Dave Watson | 5837158 | 2018-03-22 10:10:26 -0700 | [diff] [blame] | 462 | ctx->conf = conf; | 
| Ilya Lesokhin | 6d88207 | 2017-11-13 10:22:45 +0200 | [diff] [blame] | 463 | update_sk_prot(sk, ctx); | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 464 | if (tx) { | 
|  | 465 | ctx->sk_write_space = sk->sk_write_space; | 
|  | 466 | sk->sk_write_space = tls_write_space; | 
|  | 467 | } else { | 
|  | 468 | sk->sk_socket->ops = &tls_sw_proto_ops; | 
|  | 469 | } | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 470 | goto out; | 
|  | 471 |  | 
|  | 472 | err_crypto_info: | 
|  | 473 | memset(crypto_info, 0, sizeof(*crypto_info)); | 
|  | 474 | out: | 
|  | 475 | return rc; | 
|  | 476 | } | 
|  | 477 |  | 
|  | 478 | static int do_tls_setsockopt(struct sock *sk, int optname, | 
|  | 479 | char __user *optval, unsigned int optlen) | 
|  | 480 | { | 
|  | 481 | int rc = 0; | 
|  | 482 |  | 
|  | 483 | switch (optname) { | 
|  | 484 | case TLS_TX: | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 485 | case TLS_RX: | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 486 | lock_sock(sk); | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 487 | rc = do_tls_setsockopt_conf(sk, optval, optlen, | 
|  | 488 | optname == TLS_TX); | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 489 | release_sock(sk); | 
|  | 490 | break; | 
|  | 491 | default: | 
|  | 492 | rc = -ENOPROTOOPT; | 
|  | 493 | break; | 
|  | 494 | } | 
|  | 495 | return rc; | 
|  | 496 | } | 
|  | 497 |  | 
|  | 498 | static int tls_setsockopt(struct sock *sk, int level, int optname, | 
|  | 499 | char __user *optval, unsigned int optlen) | 
|  | 500 | { | 
|  | 501 | struct tls_context *ctx = tls_get_ctx(sk); | 
|  | 502 |  | 
|  | 503 | if (level != SOL_TLS) | 
|  | 504 | return ctx->setsockopt(sk, level, optname, optval, optlen); | 
|  | 505 |  | 
|  | 506 | return do_tls_setsockopt(sk, optname, optval, optlen); | 
|  | 507 | } | 
|  | 508 |  | 
| Atul Gupta | dd0bed1 | 2018-03-31 21:41:52 +0530 | [diff] [blame] | 509 | static struct tls_context *create_ctx(struct sock *sk) | 
|  | 510 | { | 
|  | 511 | struct inet_connection_sock *icsk = inet_csk(sk); | 
|  | 512 | struct tls_context *ctx; | 
|  | 513 |  | 
|  | 514 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | 
|  | 515 | if (!ctx) | 
|  | 516 | return NULL; | 
|  | 517 |  | 
|  | 518 | icsk->icsk_ulp_data = ctx; | 
|  | 519 | return ctx; | 
|  | 520 | } | 
|  | 521 |  | 
|  | 522 | static int tls_hw_prot(struct sock *sk) | 
|  | 523 | { | 
|  | 524 | struct tls_context *ctx; | 
|  | 525 | struct tls_device *dev; | 
|  | 526 | int rc = 0; | 
|  | 527 |  | 
|  | 528 | mutex_lock(&device_mutex); | 
|  | 529 | list_for_each_entry(dev, &device_list, dev_list) { | 
|  | 530 | if (dev->feature && dev->feature(dev)) { | 
|  | 531 | ctx = create_ctx(sk); | 
|  | 532 | if (!ctx) | 
|  | 533 | goto out; | 
|  | 534 |  | 
|  | 535 | ctx->hash = sk->sk_prot->hash; | 
|  | 536 | ctx->unhash = sk->sk_prot->unhash; | 
|  | 537 | ctx->sk_proto_close = sk->sk_prot->close; | 
|  | 538 | ctx->conf = TLS_HW_RECORD; | 
|  | 539 | update_sk_prot(sk, ctx); | 
|  | 540 | rc = 1; | 
|  | 541 | break; | 
|  | 542 | } | 
|  | 543 | } | 
|  | 544 | out: | 
|  | 545 | mutex_unlock(&device_mutex); | 
|  | 546 | return rc; | 
|  | 547 | } | 
|  | 548 |  | 
|  | 549 | static void tls_hw_unhash(struct sock *sk) | 
|  | 550 | { | 
|  | 551 | struct tls_context *ctx = tls_get_ctx(sk); | 
|  | 552 | struct tls_device *dev; | 
|  | 553 |  | 
|  | 554 | mutex_lock(&device_mutex); | 
|  | 555 | list_for_each_entry(dev, &device_list, dev_list) { | 
|  | 556 | if (dev->unhash) | 
|  | 557 | dev->unhash(dev, sk); | 
|  | 558 | } | 
|  | 559 | mutex_unlock(&device_mutex); | 
|  | 560 | ctx->unhash(sk); | 
|  | 561 | } | 
|  | 562 |  | 
|  | 563 | static int tls_hw_hash(struct sock *sk) | 
|  | 564 | { | 
|  | 565 | struct tls_context *ctx = tls_get_ctx(sk); | 
|  | 566 | struct tls_device *dev; | 
|  | 567 | int err; | 
|  | 568 |  | 
|  | 569 | err = ctx->hash(sk); | 
|  | 570 | mutex_lock(&device_mutex); | 
|  | 571 | list_for_each_entry(dev, &device_list, dev_list) { | 
|  | 572 | if (dev->hash) | 
|  | 573 | err |= dev->hash(dev, sk); | 
|  | 574 | } | 
|  | 575 | mutex_unlock(&device_mutex); | 
|  | 576 |  | 
|  | 577 | if (err) | 
|  | 578 | tls_hw_unhash(sk); | 
|  | 579 | return err; | 
|  | 580 | } | 
|  | 581 |  | 
| Boris Pismenny | c113187 | 2018-02-27 14:18:39 +0200 | [diff] [blame] | 582 | static void build_protos(struct proto *prot, struct proto *base) | 
|  | 583 | { | 
| Dave Watson | 5837158 | 2018-03-22 10:10:26 -0700 | [diff] [blame] | 584 | prot[TLS_BASE] = *base; | 
|  | 585 | prot[TLS_BASE].setsockopt	= tls_setsockopt; | 
|  | 586 | prot[TLS_BASE].getsockopt	= tls_getsockopt; | 
|  | 587 | prot[TLS_BASE].close		= tls_sk_proto_close; | 
| Boris Pismenny | c113187 | 2018-02-27 14:18:39 +0200 | [diff] [blame] | 588 |  | 
| Dave Watson | 5837158 | 2018-03-22 10:10:26 -0700 | [diff] [blame] | 589 | prot[TLS_SW_TX] = prot[TLS_BASE]; | 
| Boris Pismenny | c113187 | 2018-02-27 14:18:39 +0200 | [diff] [blame] | 590 | prot[TLS_SW_TX].sendmsg		= tls_sw_sendmsg; | 
|  | 591 | prot[TLS_SW_TX].sendpage	= tls_sw_sendpage; | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 592 |  | 
|  | 593 | prot[TLS_SW_RX] = prot[TLS_BASE]; | 
|  | 594 | prot[TLS_SW_RX].recvmsg		= tls_sw_recvmsg; | 
|  | 595 | prot[TLS_SW_RX].close		= tls_sk_proto_close; | 
|  | 596 |  | 
|  | 597 | prot[TLS_SW_RXTX] = prot[TLS_SW_TX]; | 
|  | 598 | prot[TLS_SW_RXTX].recvmsg	= tls_sw_recvmsg; | 
|  | 599 | prot[TLS_SW_RXTX].close		= tls_sk_proto_close; | 
| Atul Gupta | dd0bed1 | 2018-03-31 21:41:52 +0530 | [diff] [blame] | 600 |  | 
|  | 601 | prot[TLS_HW_RECORD] = *base; | 
|  | 602 | prot[TLS_HW_RECORD].hash	= tls_hw_hash; | 
|  | 603 | prot[TLS_HW_RECORD].unhash	= tls_hw_unhash; | 
|  | 604 | prot[TLS_HW_RECORD].close	= tls_sk_proto_close; | 
| Boris Pismenny | c113187 | 2018-02-27 14:18:39 +0200 | [diff] [blame] | 605 | } | 
|  | 606 |  | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 607 | static int tls_init(struct sock *sk) | 
|  | 608 | { | 
| Boris Pismenny | c113187 | 2018-02-27 14:18:39 +0200 | [diff] [blame] | 609 | int ip_ver = sk->sk_family == AF_INET6 ? TLSV6 : TLSV4; | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 610 | struct tls_context *ctx; | 
|  | 611 | int rc = 0; | 
|  | 612 |  | 
| Atul Gupta | dd0bed1 | 2018-03-31 21:41:52 +0530 | [diff] [blame] | 613 | if (tls_hw_prot(sk)) | 
|  | 614 | goto out; | 
|  | 615 |  | 
| Ilya Lesokhin | d91c3e1 | 2018-01-16 15:31:52 +0200 | [diff] [blame] | 616 | /* The TLS ulp is currently supported only for TCP sockets | 
|  | 617 | * in ESTABLISHED state. | 
|  | 618 | * Supporting sockets in LISTEN state will require us | 
|  | 619 | * to modify the accept implementation to clone rather then | 
|  | 620 | * share the ulp context. | 
|  | 621 | */ | 
|  | 622 | if (sk->sk_state != TCP_ESTABLISHED) | 
|  | 623 | return -ENOTSUPP; | 
|  | 624 |  | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 625 | /* allocate tls context */ | 
| Atul Gupta | dd0bed1 | 2018-03-31 21:41:52 +0530 | [diff] [blame] | 626 | ctx = create_ctx(sk); | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 627 | if (!ctx) { | 
|  | 628 | rc = -ENOMEM; | 
|  | 629 | goto out; | 
|  | 630 | } | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 631 | ctx->setsockopt = sk->sk_prot->setsockopt; | 
|  | 632 | ctx->getsockopt = sk->sk_prot->getsockopt; | 
| Ilya Lesokhin | ff45d82 | 2017-11-13 10:22:46 +0200 | [diff] [blame] | 633 | ctx->sk_proto_close = sk->sk_prot->close; | 
| Ilya Lesokhin | 6d88207 | 2017-11-13 10:22:45 +0200 | [diff] [blame] | 634 |  | 
| Boris Pismenny | c113187 | 2018-02-27 14:18:39 +0200 | [diff] [blame] | 635 | /* Build IPv6 TLS whenever the address of tcpv6_prot changes */ | 
|  | 636 | if (ip_ver == TLSV6 && | 
|  | 637 | unlikely(sk->sk_prot != smp_load_acquire(&saved_tcpv6_prot))) { | 
|  | 638 | mutex_lock(&tcpv6_prot_mutex); | 
|  | 639 | if (likely(sk->sk_prot != saved_tcpv6_prot)) { | 
|  | 640 | build_protos(tls_prots[TLSV6], sk->sk_prot); | 
|  | 641 | smp_store_release(&saved_tcpv6_prot, sk->sk_prot); | 
|  | 642 | } | 
|  | 643 | mutex_unlock(&tcpv6_prot_mutex); | 
|  | 644 | } | 
|  | 645 |  | 
| Dave Watson | 5837158 | 2018-03-22 10:10:26 -0700 | [diff] [blame] | 646 | ctx->conf = TLS_BASE; | 
| Ilya Lesokhin | 6d88207 | 2017-11-13 10:22:45 +0200 | [diff] [blame] | 647 | update_sk_prot(sk, ctx); | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 648 | out: | 
|  | 649 | return rc; | 
|  | 650 | } | 
|  | 651 |  | 
| Atul Gupta | dd0bed1 | 2018-03-31 21:41:52 +0530 | [diff] [blame] | 652 | void tls_register_device(struct tls_device *device) | 
|  | 653 | { | 
|  | 654 | mutex_lock(&device_mutex); | 
|  | 655 | list_add_tail(&device->dev_list, &device_list); | 
|  | 656 | mutex_unlock(&device_mutex); | 
|  | 657 | } | 
|  | 658 | EXPORT_SYMBOL(tls_register_device); | 
|  | 659 |  | 
|  | 660 | void tls_unregister_device(struct tls_device *device) | 
|  | 661 | { | 
|  | 662 | mutex_lock(&device_mutex); | 
|  | 663 | list_del(&device->dev_list); | 
|  | 664 | mutex_unlock(&device_mutex); | 
|  | 665 | } | 
|  | 666 | EXPORT_SYMBOL(tls_unregister_device); | 
|  | 667 |  | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 668 | static struct tcp_ulp_ops tcp_tls_ulp_ops __read_mostly = { | 
|  | 669 | .name			= "tls", | 
| John Fastabend | b11a632 | 2018-02-05 10:17:43 -0800 | [diff] [blame] | 670 | .uid			= TCP_ULP_TLS, | 
|  | 671 | .user_visible		= true, | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 672 | .owner			= THIS_MODULE, | 
|  | 673 | .init			= tls_init, | 
|  | 674 | }; | 
|  | 675 |  | 
|  | 676 | static int __init tls_register(void) | 
|  | 677 | { | 
| Boris Pismenny | c113187 | 2018-02-27 14:18:39 +0200 | [diff] [blame] | 678 | build_protos(tls_prots[TLSV4], &tcp_prot); | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 679 |  | 
| Dave Watson | c46234e | 2018-03-22 10:10:35 -0700 | [diff] [blame] | 680 | tls_sw_proto_ops = inet_stream_ops; | 
|  | 681 | tls_sw_proto_ops.poll = tls_sw_poll; | 
|  | 682 | tls_sw_proto_ops.splice_read = tls_sw_splice_read; | 
|  | 683 |  | 
| Dave Watson | 3c4d755 | 2017-06-14 11:37:39 -0700 | [diff] [blame] | 684 | tcp_register_ulp(&tcp_tls_ulp_ops); | 
|  | 685 |  | 
|  | 686 | return 0; | 
|  | 687 | } | 
|  | 688 |  | 
|  | 689 | static void __exit tls_unregister(void) | 
|  | 690 | { | 
|  | 691 | tcp_unregister_ulp(&tcp_tls_ulp_ops); | 
|  | 692 | } | 
|  | 693 |  | 
|  | 694 | module_init(tls_register); | 
|  | 695 | module_exit(tls_unregister); |