mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-09 06:14:34 +02:00
splice_short grows its pipe with (MAX_FRAGS + 1) * 0x1000 so it can
queue one short vmsplice() buffer for each fragment before draining the
pipe. That assumes 4K pipe buffers.
On 64K-page kernels the request is rounded to 262144 bytes, which
provides only four pipe buffers. The fifth one-byte vmsplice() blocks in
pipe_wait_writable and the test times out before it reaches the TLS path.
Request enough bytes for the same number of pipe buffers using the
runtime page size, and assert that the kernel granted at least that much.
If an unprivileged run cannot raise the pipe above the system
pipe-max-size limit, skip the test because it cannot exercise the
intended path.
Fixes: 3667e9b442 ("selftests: tls: add test for short splice due to full skmsg")
Signed-off-by: Nirmoy Das <nirmoyd@nvidia.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260624134416.3235403-1-nirmoyd@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
3360 lines
84 KiB
C
3360 lines
84 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <error.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <linux/tls.h>
|
|
#include <linux/tcp.h>
|
|
#include <linux/socket.h>
|
|
|
|
#include <sys/epoll.h>
|
|
#include <sys/types.h>
|
|
#include <sys/sendfile.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "kselftest_harness.h"
|
|
|
|
#define TLS_PAYLOAD_MAX_LEN 16384
|
|
#define SOL_TLS 282
|
|
|
|
static int fips_enabled;
|
|
|
|
struct tls_crypto_info_keys {
|
|
union {
|
|
struct tls_crypto_info crypto_info;
|
|
struct tls12_crypto_info_aes_gcm_128 aes128;
|
|
struct tls12_crypto_info_chacha20_poly1305 chacha20;
|
|
struct tls12_crypto_info_sm4_gcm sm4gcm;
|
|
struct tls12_crypto_info_sm4_ccm sm4ccm;
|
|
struct tls12_crypto_info_aes_ccm_128 aesccm128;
|
|
struct tls12_crypto_info_aes_gcm_256 aesgcm256;
|
|
struct tls12_crypto_info_aria_gcm_128 ariagcm128;
|
|
struct tls12_crypto_info_aria_gcm_256 ariagcm256;
|
|
};
|
|
size_t len;
|
|
};
|
|
|
|
static void tls_crypto_info_init(uint16_t tls_version, uint16_t cipher_type,
|
|
struct tls_crypto_info_keys *tls12,
|
|
char key_generation)
|
|
{
|
|
memset(tls12, key_generation, sizeof(*tls12));
|
|
memset(tls12, 0, sizeof(struct tls_crypto_info));
|
|
|
|
switch (cipher_type) {
|
|
case TLS_CIPHER_CHACHA20_POLY1305:
|
|
tls12->len = sizeof(struct tls12_crypto_info_chacha20_poly1305);
|
|
tls12->chacha20.info.version = tls_version;
|
|
tls12->chacha20.info.cipher_type = cipher_type;
|
|
break;
|
|
case TLS_CIPHER_AES_GCM_128:
|
|
tls12->len = sizeof(struct tls12_crypto_info_aes_gcm_128);
|
|
tls12->aes128.info.version = tls_version;
|
|
tls12->aes128.info.cipher_type = cipher_type;
|
|
break;
|
|
case TLS_CIPHER_SM4_GCM:
|
|
tls12->len = sizeof(struct tls12_crypto_info_sm4_gcm);
|
|
tls12->sm4gcm.info.version = tls_version;
|
|
tls12->sm4gcm.info.cipher_type = cipher_type;
|
|
break;
|
|
case TLS_CIPHER_SM4_CCM:
|
|
tls12->len = sizeof(struct tls12_crypto_info_sm4_ccm);
|
|
tls12->sm4ccm.info.version = tls_version;
|
|
tls12->sm4ccm.info.cipher_type = cipher_type;
|
|
break;
|
|
case TLS_CIPHER_AES_CCM_128:
|
|
tls12->len = sizeof(struct tls12_crypto_info_aes_ccm_128);
|
|
tls12->aesccm128.info.version = tls_version;
|
|
tls12->aesccm128.info.cipher_type = cipher_type;
|
|
break;
|
|
case TLS_CIPHER_AES_GCM_256:
|
|
tls12->len = sizeof(struct tls12_crypto_info_aes_gcm_256);
|
|
tls12->aesgcm256.info.version = tls_version;
|
|
tls12->aesgcm256.info.cipher_type = cipher_type;
|
|
break;
|
|
case TLS_CIPHER_ARIA_GCM_128:
|
|
tls12->len = sizeof(struct tls12_crypto_info_aria_gcm_128);
|
|
tls12->ariagcm128.info.version = tls_version;
|
|
tls12->ariagcm128.info.cipher_type = cipher_type;
|
|
break;
|
|
case TLS_CIPHER_ARIA_GCM_256:
|
|
tls12->len = sizeof(struct tls12_crypto_info_aria_gcm_256);
|
|
tls12->ariagcm256.info.version = tls_version;
|
|
tls12->ariagcm256.info.cipher_type = cipher_type;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void memrnd(void *s, size_t n)
|
|
{
|
|
int *dword = s;
|
|
char *byte;
|
|
|
|
for (; n >= 4; n -= 4)
|
|
*dword++ = rand();
|
|
byte = (void *)dword;
|
|
while (n--)
|
|
*byte++ = rand();
|
|
}
|
|
|
|
static void ulp_sock_pair(struct __test_metadata *_metadata,
|
|
int *fd, int *cfd, bool *notls)
|
|
{
|
|
struct sockaddr_in addr;
|
|
socklen_t len;
|
|
int sfd, ret;
|
|
|
|
*notls = false;
|
|
len = sizeof(addr);
|
|
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
addr.sin_port = 0;
|
|
|
|
*fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
sfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
ret = bind(sfd, &addr, sizeof(addr));
|
|
ASSERT_EQ(ret, 0);
|
|
ret = listen(sfd, 10);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = getsockname(sfd, &addr, &len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = connect(*fd, &addr, sizeof(addr));
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
*cfd = accept(sfd, &addr, &len);
|
|
ASSERT_GE(*cfd, 0);
|
|
|
|
close(sfd);
|
|
|
|
ret = setsockopt(*fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
|
|
if (ret != 0) {
|
|
ASSERT_EQ(errno, ENOENT);
|
|
*notls = true;
|
|
printf("Failure setting TCP_ULP, testing without tls\n");
|
|
return;
|
|
}
|
|
|
|
ret = setsockopt(*cfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
|
|
ASSERT_EQ(ret, 0);
|
|
}
|
|
|
|
/* Produce a basic cmsg */
|
|
static int tls_send_cmsg(int fd, unsigned char record_type,
|
|
void *data, size_t len, int flags)
|
|
{
|
|
char cbuf[CMSG_SPACE(sizeof(char))];
|
|
int cmsg_len = sizeof(char);
|
|
struct cmsghdr *cmsg;
|
|
struct msghdr msg;
|
|
struct iovec vec;
|
|
|
|
vec.iov_base = data;
|
|
vec.iov_len = len;
|
|
memset(&msg, 0, sizeof(struct msghdr));
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = cbuf;
|
|
msg.msg_controllen = sizeof(cbuf);
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
cmsg->cmsg_level = SOL_TLS;
|
|
/* test sending non-record types. */
|
|
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;
|
|
|
|
return sendmsg(fd, &msg, flags);
|
|
}
|
|
|
|
static int __tls_recv_cmsg(struct __test_metadata *_metadata,
|
|
int fd, unsigned char *ctype,
|
|
void *data, size_t len, int flags)
|
|
{
|
|
char cbuf[CMSG_SPACE(sizeof(char))];
|
|
struct cmsghdr *cmsg;
|
|
struct msghdr msg;
|
|
struct iovec vec;
|
|
int n;
|
|
|
|
vec.iov_base = data;
|
|
vec.iov_len = len;
|
|
memset(&msg, 0, sizeof(struct msghdr));
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = cbuf;
|
|
msg.msg_controllen = sizeof(cbuf);
|
|
|
|
n = recvmsg(fd, &msg, flags);
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
EXPECT_NE(cmsg, NULL);
|
|
EXPECT_EQ(cmsg->cmsg_level, SOL_TLS);
|
|
EXPECT_EQ(cmsg->cmsg_type, TLS_GET_RECORD_TYPE);
|
|
if (ctype)
|
|
*ctype = *((unsigned char *)CMSG_DATA(cmsg));
|
|
|
|
return n;
|
|
}
|
|
|
|
static int tls_recv_cmsg(struct __test_metadata *_metadata,
|
|
int fd, unsigned char record_type,
|
|
void *data, size_t len, int flags)
|
|
{
|
|
unsigned char ctype;
|
|
int n;
|
|
|
|
n = __tls_recv_cmsg(_metadata, fd, &ctype, data, len, flags);
|
|
EXPECT_EQ(ctype, record_type);
|
|
|
|
return n;
|
|
}
|
|
|
|
FIXTURE(tls_basic)
|
|
{
|
|
int fd, cfd;
|
|
bool notls;
|
|
};
|
|
|
|
FIXTURE_SETUP(tls_basic)
|
|
{
|
|
ulp_sock_pair(_metadata, &self->fd, &self->cfd, &self->notls);
|
|
}
|
|
|
|
FIXTURE_TEARDOWN(tls_basic)
|
|
{
|
|
close(self->fd);
|
|
close(self->cfd);
|
|
}
|
|
|
|
/* Send some data through with ULP but no keys */
|
|
TEST_F(tls_basic, base_base)
|
|
{
|
|
char const *test_str = "test_read";
|
|
int send_len = 10;
|
|
char buf[10];
|
|
|
|
ASSERT_EQ(strlen(test_str) + 1, send_len);
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
};
|
|
|
|
TEST_F(tls_basic, bad_cipher)
|
|
{
|
|
struct tls_crypto_info_keys tls12;
|
|
|
|
tls12.crypto_info.version = 200;
|
|
tls12.crypto_info.cipher_type = TLS_CIPHER_AES_GCM_128;
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, sizeof(struct tls12_crypto_info_aes_gcm_128)), -1);
|
|
|
|
tls12.crypto_info.version = TLS_1_2_VERSION;
|
|
tls12.crypto_info.cipher_type = 50;
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, sizeof(struct tls12_crypto_info_aes_gcm_128)), -1);
|
|
|
|
tls12.crypto_info.version = TLS_1_2_VERSION;
|
|
tls12.crypto_info.cipher_type = 59;
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, sizeof(struct tls12_crypto_info_aes_gcm_128)), -1);
|
|
|
|
tls12.crypto_info.version = TLS_1_2_VERSION;
|
|
tls12.crypto_info.cipher_type = 10;
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, sizeof(struct tls12_crypto_info_aes_gcm_128)), -1);
|
|
|
|
tls12.crypto_info.version = TLS_1_2_VERSION;
|
|
tls12.crypto_info.cipher_type = 70;
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, sizeof(struct tls12_crypto_info_aes_gcm_128)), -1);
|
|
}
|
|
|
|
TEST_F(tls_basic, recseq_wrap)
|
|
{
|
|
struct tls_crypto_info_keys tls12;
|
|
char const *test_str = "test_read";
|
|
int send_len = 10;
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_128, &tls12, 0);
|
|
memset(&tls12.aes128.rec_seq, 0xff, sizeof(tls12.aes128.rec_seq));
|
|
|
|
ASSERT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
|
|
ASSERT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), -1);
|
|
EXPECT_EQ(errno, EBADMSG);
|
|
}
|
|
|
|
FIXTURE(tls)
|
|
{
|
|
int fd, cfd;
|
|
bool notls;
|
|
};
|
|
|
|
FIXTURE_VARIANT(tls)
|
|
{
|
|
uint16_t tls_version;
|
|
uint16_t cipher_type;
|
|
bool nopad, fips_non_compliant;
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 12_aes_gcm)
|
|
{
|
|
.tls_version = TLS_1_2_VERSION,
|
|
.cipher_type = TLS_CIPHER_AES_GCM_128,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 13_aes_gcm)
|
|
{
|
|
.tls_version = TLS_1_3_VERSION,
|
|
.cipher_type = TLS_CIPHER_AES_GCM_128,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 12_chacha)
|
|
{
|
|
.tls_version = TLS_1_2_VERSION,
|
|
.cipher_type = TLS_CIPHER_CHACHA20_POLY1305,
|
|
.fips_non_compliant = true,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 13_chacha)
|
|
{
|
|
.tls_version = TLS_1_3_VERSION,
|
|
.cipher_type = TLS_CIPHER_CHACHA20_POLY1305,
|
|
.fips_non_compliant = true,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 13_sm4_gcm)
|
|
{
|
|
.tls_version = TLS_1_3_VERSION,
|
|
.cipher_type = TLS_CIPHER_SM4_GCM,
|
|
.fips_non_compliant = true,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 13_sm4_ccm)
|
|
{
|
|
.tls_version = TLS_1_3_VERSION,
|
|
.cipher_type = TLS_CIPHER_SM4_CCM,
|
|
.fips_non_compliant = true,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 12_aes_ccm)
|
|
{
|
|
.tls_version = TLS_1_2_VERSION,
|
|
.cipher_type = TLS_CIPHER_AES_CCM_128,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 13_aes_ccm)
|
|
{
|
|
.tls_version = TLS_1_3_VERSION,
|
|
.cipher_type = TLS_CIPHER_AES_CCM_128,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 12_aes_gcm_256)
|
|
{
|
|
.tls_version = TLS_1_2_VERSION,
|
|
.cipher_type = TLS_CIPHER_AES_GCM_256,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 13_aes_gcm_256)
|
|
{
|
|
.tls_version = TLS_1_3_VERSION,
|
|
.cipher_type = TLS_CIPHER_AES_GCM_256,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 13_nopad)
|
|
{
|
|
.tls_version = TLS_1_3_VERSION,
|
|
.cipher_type = TLS_CIPHER_AES_GCM_128,
|
|
.nopad = true,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 12_aria_gcm)
|
|
{
|
|
.tls_version = TLS_1_2_VERSION,
|
|
.cipher_type = TLS_CIPHER_ARIA_GCM_128,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls, 12_aria_gcm_256)
|
|
{
|
|
.tls_version = TLS_1_2_VERSION,
|
|
.cipher_type = TLS_CIPHER_ARIA_GCM_256,
|
|
};
|
|
|
|
FIXTURE_SETUP(tls)
|
|
{
|
|
struct tls_crypto_info_keys tls12;
|
|
int one = 1;
|
|
int ret;
|
|
|
|
if (fips_enabled && variant->fips_non_compliant)
|
|
SKIP(return, "Unsupported cipher in FIPS mode");
|
|
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type,
|
|
&tls12, 0);
|
|
|
|
ulp_sock_pair(_metadata, &self->fd, &self->cfd, &self->notls);
|
|
|
|
if (self->notls)
|
|
return;
|
|
|
|
ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
if (variant->nopad) {
|
|
ret = setsockopt(self->cfd, SOL_TLS, TLS_RX_EXPECT_NO_PAD,
|
|
(void *)&one, sizeof(one));
|
|
ASSERT_EQ(ret, 0);
|
|
}
|
|
}
|
|
|
|
FIXTURE_TEARDOWN(tls)
|
|
{
|
|
close(self->fd);
|
|
close(self->cfd);
|
|
}
|
|
|
|
TEST_F(tls, sendfile)
|
|
{
|
|
int filefd = open("/proc/self/exe", O_RDONLY);
|
|
struct stat st;
|
|
|
|
EXPECT_GE(filefd, 0);
|
|
fstat(filefd, &st);
|
|
EXPECT_GE(sendfile(self->fd, filefd, 0, st.st_size), 0);
|
|
|
|
close(filefd);
|
|
}
|
|
|
|
TEST_F(tls, send_then_sendfile)
|
|
{
|
|
int filefd = open("/proc/self/exe", O_RDONLY);
|
|
char const *test_str = "test_send";
|
|
int to_send = strlen(test_str) + 1;
|
|
char recv_buf[10];
|
|
struct stat st;
|
|
char *buf;
|
|
|
|
EXPECT_GE(filefd, 0);
|
|
fstat(filefd, &st);
|
|
buf = (char *)malloc(st.st_size);
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, to_send, 0), to_send);
|
|
EXPECT_EQ(recv(self->cfd, recv_buf, to_send, MSG_WAITALL), to_send);
|
|
EXPECT_EQ(memcmp(test_str, recv_buf, to_send), 0);
|
|
|
|
EXPECT_GE(sendfile(self->fd, filefd, 0, st.st_size), 0);
|
|
EXPECT_EQ(recv(self->cfd, buf, st.st_size, MSG_WAITALL), st.st_size);
|
|
|
|
free(buf);
|
|
close(filefd);
|
|
}
|
|
|
|
static void chunked_sendfile(struct __test_metadata *_metadata,
|
|
struct _test_data_tls *self,
|
|
uint16_t chunk_size,
|
|
uint16_t extra_payload_size)
|
|
{
|
|
char buf[TLS_PAYLOAD_MAX_LEN];
|
|
uint16_t test_payload_size;
|
|
int size = 0;
|
|
int ret;
|
|
char filename[] = "/tmp/mytemp.XXXXXX";
|
|
int fd = mkstemp(filename);
|
|
off_t offset = 0;
|
|
|
|
unlink(filename);
|
|
ASSERT_GE(fd, 0);
|
|
EXPECT_GE(chunk_size, 1);
|
|
test_payload_size = chunk_size + extra_payload_size;
|
|
ASSERT_GE(TLS_PAYLOAD_MAX_LEN, test_payload_size);
|
|
memset(buf, 1, test_payload_size);
|
|
size = write(fd, buf, test_payload_size);
|
|
EXPECT_EQ(size, test_payload_size);
|
|
fsync(fd);
|
|
|
|
while (size > 0) {
|
|
ret = sendfile(self->fd, fd, &offset, chunk_size);
|
|
EXPECT_GE(ret, 0);
|
|
size -= ret;
|
|
}
|
|
|
|
EXPECT_EQ(recv(self->cfd, buf, test_payload_size, MSG_WAITALL),
|
|
test_payload_size);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
TEST_F(tls, multi_chunk_sendfile)
|
|
{
|
|
chunked_sendfile(_metadata, self, 4096, 4096);
|
|
chunked_sendfile(_metadata, self, 4096, 0);
|
|
chunked_sendfile(_metadata, self, 4096, 1);
|
|
chunked_sendfile(_metadata, self, 4096, 2048);
|
|
chunked_sendfile(_metadata, self, 8192, 2048);
|
|
chunked_sendfile(_metadata, self, 4096, 8192);
|
|
chunked_sendfile(_metadata, self, 8192, 4096);
|
|
chunked_sendfile(_metadata, self, 12288, 1024);
|
|
chunked_sendfile(_metadata, self, 12288, 2000);
|
|
chunked_sendfile(_metadata, self, 15360, 100);
|
|
chunked_sendfile(_metadata, self, 15360, 300);
|
|
chunked_sendfile(_metadata, self, 1, 4096);
|
|
chunked_sendfile(_metadata, self, 2048, 4096);
|
|
chunked_sendfile(_metadata, self, 2048, 8192);
|
|
chunked_sendfile(_metadata, self, 4096, 8192);
|
|
chunked_sendfile(_metadata, self, 1024, 12288);
|
|
chunked_sendfile(_metadata, self, 2000, 12288);
|
|
chunked_sendfile(_metadata, self, 100, 15360);
|
|
chunked_sendfile(_metadata, self, 300, 15360);
|
|
}
|
|
|
|
TEST_F(tls, recv_max)
|
|
{
|
|
unsigned int send_len = TLS_PAYLOAD_MAX_LEN;
|
|
char recv_mem[TLS_PAYLOAD_MAX_LEN];
|
|
char buf[TLS_PAYLOAD_MAX_LEN];
|
|
|
|
memrnd(buf, sizeof(buf));
|
|
|
|
EXPECT_GE(send(self->fd, buf, send_len, 0), 0);
|
|
EXPECT_NE(recv(self->cfd, recv_mem, send_len, 0), -1);
|
|
EXPECT_EQ(memcmp(buf, recv_mem, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, recv_small)
|
|
{
|
|
char const *test_str = "test_read";
|
|
int send_len = 10;
|
|
char buf[10];
|
|
|
|
send_len = strlen(test_str) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, msg_more)
|
|
{
|
|
char const *test_str = "test_read";
|
|
int send_len = 10;
|
|
char buf[10 * 2];
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, MSG_MORE), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, MSG_DONTWAIT), -1);
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len * 2, MSG_WAITALL),
|
|
send_len * 2);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, cmsg_msg_more)
|
|
{
|
|
char *test_str = "test_read";
|
|
char record_type = 100;
|
|
int send_len = 10;
|
|
|
|
/* we don't allow MSG_MORE with non-DATA records */
|
|
EXPECT_EQ(tls_send_cmsg(self->fd, record_type, test_str, send_len,
|
|
MSG_MORE), -1);
|
|
EXPECT_EQ(errno, EINVAL);
|
|
}
|
|
|
|
TEST_F(tls, msg_more_then_cmsg)
|
|
{
|
|
char *test_str = "test_read";
|
|
char record_type = 100;
|
|
int send_len = 10;
|
|
char buf[10 * 2];
|
|
int ret;
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, MSG_MORE), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, MSG_DONTWAIT), -1);
|
|
|
|
ret = tls_send_cmsg(self->fd, record_type, test_str, send_len, 0);
|
|
EXPECT_EQ(ret, send_len);
|
|
|
|
/* initial DATA record didn't get merged with the non-DATA record */
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len * 2, 0), send_len);
|
|
|
|
EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, record_type,
|
|
buf, sizeof(buf), MSG_WAITALL),
|
|
send_len);
|
|
}
|
|
|
|
TEST_F(tls, msg_more_unsent)
|
|
{
|
|
char const *test_str = "test_read";
|
|
int send_len = 10;
|
|
char buf[10];
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, MSG_MORE), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, MSG_DONTWAIT), -1);
|
|
}
|
|
|
|
TEST_F(tls, msg_eor)
|
|
{
|
|
char const *test_str = "test_read";
|
|
int send_len = 10;
|
|
char buf[10];
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, MSG_EOR), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, MSG_WAITALL), send_len);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, sendmsg_single)
|
|
{
|
|
struct msghdr msg;
|
|
|
|
char const *test_str = "test_sendmsg";
|
|
size_t send_len = 13;
|
|
struct iovec vec;
|
|
char buf[13];
|
|
|
|
vec.iov_base = (char *)test_str;
|
|
vec.iov_len = send_len;
|
|
memset(&msg, 0, sizeof(struct msghdr));
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
EXPECT_EQ(sendmsg(self->fd, &msg, 0), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, MSG_WAITALL), send_len);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
}
|
|
|
|
#define MAX_FRAGS 64
|
|
#define SEND_LEN 13
|
|
TEST_F(tls, sendmsg_fragmented)
|
|
{
|
|
char const *test_str = "test_sendmsg";
|
|
char buf[SEND_LEN * MAX_FRAGS];
|
|
struct iovec vec[MAX_FRAGS];
|
|
struct msghdr msg;
|
|
int i, frags;
|
|
|
|
for (frags = 1; frags <= MAX_FRAGS; frags++) {
|
|
for (i = 0; i < frags; i++) {
|
|
vec[i].iov_base = (char *)test_str;
|
|
vec[i].iov_len = SEND_LEN;
|
|
}
|
|
|
|
memset(&msg, 0, sizeof(struct msghdr));
|
|
msg.msg_iov = vec;
|
|
msg.msg_iovlen = frags;
|
|
|
|
EXPECT_EQ(sendmsg(self->fd, &msg, 0), SEND_LEN * frags);
|
|
EXPECT_EQ(recv(self->cfd, buf, SEND_LEN * frags, MSG_WAITALL),
|
|
SEND_LEN * frags);
|
|
|
|
for (i = 0; i < frags; i++)
|
|
EXPECT_EQ(memcmp(buf + SEND_LEN * i,
|
|
test_str, SEND_LEN), 0);
|
|
}
|
|
}
|
|
#undef MAX_FRAGS
|
|
#undef SEND_LEN
|
|
|
|
TEST_F(tls, sendmsg_large)
|
|
{
|
|
void *mem = malloc(16384);
|
|
size_t send_len = 16384;
|
|
size_t sends = 128;
|
|
struct msghdr msg;
|
|
size_t recvs = 0;
|
|
size_t sent = 0;
|
|
|
|
memset(&msg, 0, sizeof(struct msghdr));
|
|
while (sent++ < sends) {
|
|
struct iovec vec = { (void *)mem, send_len };
|
|
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
EXPECT_EQ(sendmsg(self->fd, &msg, 0), send_len);
|
|
}
|
|
|
|
while (recvs++ < sends) {
|
|
EXPECT_NE(recv(self->cfd, mem, send_len, 0), -1);
|
|
}
|
|
|
|
free(mem);
|
|
}
|
|
|
|
TEST_F(tls, sendmsg_multiple)
|
|
{
|
|
char const *test_str = "test_sendmsg_multiple";
|
|
struct iovec vec[5];
|
|
char *test_strs[5];
|
|
struct msghdr msg;
|
|
int total_len = 0;
|
|
int len_cmp = 0;
|
|
int iov_len = 5;
|
|
char *buf;
|
|
int i;
|
|
|
|
memset(&msg, 0, sizeof(struct msghdr));
|
|
for (i = 0; i < iov_len; i++) {
|
|
test_strs[i] = (char *)malloc(strlen(test_str) + 1);
|
|
snprintf(test_strs[i], strlen(test_str) + 1, "%s", test_str);
|
|
vec[i].iov_base = (void *)test_strs[i];
|
|
vec[i].iov_len = strlen(test_strs[i]) + 1;
|
|
total_len += vec[i].iov_len;
|
|
}
|
|
msg.msg_iov = vec;
|
|
msg.msg_iovlen = iov_len;
|
|
|
|
EXPECT_EQ(sendmsg(self->fd, &msg, 0), total_len);
|
|
buf = malloc(total_len);
|
|
EXPECT_NE(recv(self->cfd, buf, total_len, 0), -1);
|
|
for (i = 0; i < iov_len; i++) {
|
|
EXPECT_EQ(memcmp(test_strs[i], buf + len_cmp,
|
|
strlen(test_strs[i])),
|
|
0);
|
|
len_cmp += strlen(buf + len_cmp) + 1;
|
|
}
|
|
for (i = 0; i < iov_len; i++)
|
|
free(test_strs[i]);
|
|
free(buf);
|
|
}
|
|
|
|
TEST_F(tls, sendmsg_multiple_stress)
|
|
{
|
|
char const *test_str = "abcdefghijklmno";
|
|
struct iovec vec[1024];
|
|
char *test_strs[1024];
|
|
int iov_len = 1024;
|
|
int total_len = 0;
|
|
char buf[1 << 14];
|
|
struct msghdr msg;
|
|
int len_cmp = 0;
|
|
int i;
|
|
|
|
memset(&msg, 0, sizeof(struct msghdr));
|
|
for (i = 0; i < iov_len; i++) {
|
|
test_strs[i] = (char *)malloc(strlen(test_str) + 1);
|
|
snprintf(test_strs[i], strlen(test_str) + 1, "%s", test_str);
|
|
vec[i].iov_base = (void *)test_strs[i];
|
|
vec[i].iov_len = strlen(test_strs[i]) + 1;
|
|
total_len += vec[i].iov_len;
|
|
}
|
|
msg.msg_iov = vec;
|
|
msg.msg_iovlen = iov_len;
|
|
|
|
EXPECT_EQ(sendmsg(self->fd, &msg, 0), total_len);
|
|
EXPECT_NE(recv(self->cfd, buf, total_len, 0), -1);
|
|
|
|
for (i = 0; i < iov_len; i++)
|
|
len_cmp += strlen(buf + len_cmp) + 1;
|
|
|
|
for (i = 0; i < iov_len; i++)
|
|
free(test_strs[i]);
|
|
}
|
|
|
|
TEST_F(tls, splice_from_pipe)
|
|
{
|
|
int send_len = TLS_PAYLOAD_MAX_LEN;
|
|
char mem_send[TLS_PAYLOAD_MAX_LEN];
|
|
char mem_recv[TLS_PAYLOAD_MAX_LEN];
|
|
int p[2];
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
EXPECT_GE(write(p[1], mem_send, send_len), 0);
|
|
EXPECT_GE(splice(p[0], NULL, self->fd, NULL, send_len, 0), 0);
|
|
EXPECT_EQ(recv(self->cfd, mem_recv, send_len, MSG_WAITALL), send_len);
|
|
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, splice_more)
|
|
{
|
|
unsigned int f = SPLICE_F_NONBLOCK | SPLICE_F_MORE | SPLICE_F_GIFT;
|
|
int send_len = TLS_PAYLOAD_MAX_LEN;
|
|
char mem_send[TLS_PAYLOAD_MAX_LEN];
|
|
int i, send_pipe = 1;
|
|
int p[2];
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
EXPECT_GE(write(p[1], mem_send, send_len), 0);
|
|
for (i = 0; i < 32; i++)
|
|
EXPECT_EQ(splice(p[0], NULL, self->fd, NULL, send_pipe, f), 1);
|
|
}
|
|
|
|
TEST_F(tls, splice_from_pipe2)
|
|
{
|
|
int send_len = 16000;
|
|
char mem_send[16000];
|
|
char mem_recv[16000];
|
|
int p2[2];
|
|
int p[2];
|
|
|
|
memrnd(mem_send, sizeof(mem_send));
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
ASSERT_GE(pipe(p2), 0);
|
|
EXPECT_EQ(write(p[1], mem_send, 8000), 8000);
|
|
EXPECT_EQ(splice(p[0], NULL, self->fd, NULL, 8000, 0), 8000);
|
|
EXPECT_EQ(write(p2[1], mem_send + 8000, 8000), 8000);
|
|
EXPECT_EQ(splice(p2[0], NULL, self->fd, NULL, 8000, 0), 8000);
|
|
EXPECT_EQ(recv(self->cfd, mem_recv, send_len, MSG_WAITALL), send_len);
|
|
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, send_and_splice)
|
|
{
|
|
int send_len = TLS_PAYLOAD_MAX_LEN;
|
|
char mem_send[TLS_PAYLOAD_MAX_LEN];
|
|
char mem_recv[TLS_PAYLOAD_MAX_LEN];
|
|
char const *test_str = "test_read";
|
|
int send_len2 = 10;
|
|
char buf[10];
|
|
int p[2];
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
EXPECT_EQ(send(self->fd, test_str, send_len2, 0), send_len2);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len2, MSG_WAITALL), send_len2);
|
|
EXPECT_EQ(memcmp(test_str, buf, send_len2), 0);
|
|
|
|
EXPECT_GE(write(p[1], mem_send, send_len), send_len);
|
|
EXPECT_GE(splice(p[0], NULL, self->fd, NULL, send_len, 0), send_len);
|
|
|
|
EXPECT_EQ(recv(self->cfd, mem_recv, send_len, MSG_WAITALL), send_len);
|
|
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, splice_to_pipe)
|
|
{
|
|
int send_len = TLS_PAYLOAD_MAX_LEN;
|
|
char mem_send[TLS_PAYLOAD_MAX_LEN];
|
|
char mem_recv[TLS_PAYLOAD_MAX_LEN];
|
|
int p[2];
|
|
|
|
memrnd(mem_send, sizeof(mem_send));
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
|
|
EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), send_len);
|
|
EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
|
|
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, splice_cmsg_to_pipe)
|
|
{
|
|
char *test_str = "test_read";
|
|
char record_type = 100;
|
|
int send_len = 10;
|
|
char buf[10];
|
|
int p[2];
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
EXPECT_EQ(tls_send_cmsg(self->fd, 100, test_str, send_len, 0), 10);
|
|
EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), -1);
|
|
EXPECT_EQ(errno, EINVAL);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), -1);
|
|
EXPECT_EQ(errno, EIO);
|
|
EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, record_type,
|
|
buf, sizeof(buf), MSG_WAITALL),
|
|
send_len);
|
|
EXPECT_EQ(memcmp(test_str, buf, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, splice_dec_cmsg_to_pipe)
|
|
{
|
|
char *test_str = "test_read";
|
|
char record_type = 100;
|
|
int send_len = 10;
|
|
char buf[10];
|
|
int p[2];
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
EXPECT_EQ(tls_send_cmsg(self->fd, 100, test_str, send_len, 0), 10);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), -1);
|
|
EXPECT_EQ(errno, EIO);
|
|
EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), -1);
|
|
EXPECT_EQ(errno, EINVAL);
|
|
EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, record_type,
|
|
buf, sizeof(buf), MSG_WAITALL),
|
|
send_len);
|
|
EXPECT_EQ(memcmp(test_str, buf, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, recv_and_splice)
|
|
{
|
|
int send_len = TLS_PAYLOAD_MAX_LEN;
|
|
char mem_send[TLS_PAYLOAD_MAX_LEN];
|
|
char mem_recv[TLS_PAYLOAD_MAX_LEN];
|
|
int half = send_len / 2;
|
|
int p[2];
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
|
|
/* Recv hald of the record, splice the other half */
|
|
EXPECT_EQ(recv(self->cfd, mem_recv, half, MSG_WAITALL), half);
|
|
EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, half, SPLICE_F_NONBLOCK),
|
|
half);
|
|
EXPECT_EQ(read(p[0], &mem_recv[half], half), half);
|
|
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, peek_and_splice)
|
|
{
|
|
int send_len = TLS_PAYLOAD_MAX_LEN;
|
|
char mem_send[TLS_PAYLOAD_MAX_LEN];
|
|
char mem_recv[TLS_PAYLOAD_MAX_LEN];
|
|
int chunk = TLS_PAYLOAD_MAX_LEN / 4;
|
|
int n, i, p[2];
|
|
|
|
memrnd(mem_send, sizeof(mem_send));
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
for (i = 0; i < 4; i++)
|
|
EXPECT_EQ(send(self->fd, &mem_send[chunk * i], chunk, 0),
|
|
chunk);
|
|
|
|
EXPECT_EQ(recv(self->cfd, mem_recv, chunk * 5 / 2,
|
|
MSG_WAITALL | MSG_PEEK),
|
|
chunk * 5 / 2);
|
|
EXPECT_EQ(memcmp(mem_send, mem_recv, chunk * 5 / 2), 0);
|
|
|
|
n = 0;
|
|
while (n < send_len) {
|
|
i = splice(self->cfd, NULL, p[1], NULL, send_len - n, 0);
|
|
EXPECT_GT(i, 0);
|
|
n += i;
|
|
}
|
|
EXPECT_EQ(n, send_len);
|
|
EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
|
|
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, splice_to_pipe_small)
|
|
{
|
|
int send_len = TLS_PAYLOAD_MAX_LEN;
|
|
char mem_send[TLS_PAYLOAD_MAX_LEN];
|
|
char mem_recv[TLS_PAYLOAD_MAX_LEN];
|
|
size_t total = 0;
|
|
int p[2];
|
|
|
|
memrnd(mem_send, sizeof(mem_send));
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
|
|
/* Shrink pipe to 1 page (typically 4096 bytes) to force multiple
|
|
* splice iterations for a 16384-byte TLS record.
|
|
*/
|
|
EXPECT_GE(fcntl(p[1], F_SETPIPE_SZ, 4096), 4096);
|
|
|
|
EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
|
|
|
|
while (total < (size_t)send_len) {
|
|
ssize_t spliced, drained;
|
|
|
|
spliced = splice(self->cfd, NULL, p[1], NULL,
|
|
send_len - total, 0);
|
|
EXPECT_GT(spliced, 0);
|
|
if (spliced <= 0)
|
|
break;
|
|
|
|
drained = read(p[0], mem_recv + total, spliced);
|
|
EXPECT_EQ(drained, spliced);
|
|
if (drained <= 0)
|
|
break;
|
|
|
|
total += drained;
|
|
}
|
|
|
|
EXPECT_EQ(total, (size_t)send_len);
|
|
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
|
|
|
|
close(p[0]);
|
|
close(p[1]);
|
|
}
|
|
|
|
#define MAX_FRAGS 48
|
|
TEST_F(tls, splice_short)
|
|
{
|
|
struct iovec sendchar_iov;
|
|
char read_buf[0x10000];
|
|
char sendbuf[0x100];
|
|
char sendchar = 'S';
|
|
int pipefds[2];
|
|
int pipe_sz;
|
|
int ret;
|
|
int i;
|
|
|
|
sendchar_iov.iov_base = &sendchar;
|
|
sendchar_iov.iov_len = 1;
|
|
|
|
memset(sendbuf, 's', sizeof(sendbuf));
|
|
|
|
ASSERT_GE(pipe2(pipefds, O_NONBLOCK), 0);
|
|
pipe_sz = (MAX_FRAGS + 1) * getpagesize();
|
|
ret = fcntl(pipefds[0], F_SETPIPE_SZ, pipe_sz);
|
|
if (ret < 0 && errno == EPERM)
|
|
SKIP(return, "insufficient pipe capacity");
|
|
ASSERT_GE(ret, pipe_sz);
|
|
|
|
for (i = 0; i < MAX_FRAGS; i++)
|
|
ASSERT_GE(vmsplice(pipefds[1], &sendchar_iov, 1, 0), 0);
|
|
|
|
ASSERT_EQ(write(pipefds[1], sendbuf, sizeof(sendbuf)), sizeof(sendbuf));
|
|
|
|
EXPECT_EQ(splice(pipefds[0], NULL, self->fd, NULL, MAX_FRAGS + 0x1000, 0),
|
|
MAX_FRAGS + sizeof(sendbuf));
|
|
EXPECT_EQ(recv(self->cfd, read_buf, sizeof(read_buf), 0), MAX_FRAGS + sizeof(sendbuf));
|
|
EXPECT_EQ(recv(self->cfd, read_buf, sizeof(read_buf), MSG_DONTWAIT), -1);
|
|
EXPECT_EQ(errno, EAGAIN);
|
|
}
|
|
#undef MAX_FRAGS
|
|
|
|
TEST_F(tls, recvmsg_single)
|
|
{
|
|
char const *test_str = "test_recvmsg_single";
|
|
int send_len = strlen(test_str) + 1;
|
|
char buf[20];
|
|
struct msghdr hdr;
|
|
struct iovec vec;
|
|
|
|
memset(&hdr, 0, sizeof(hdr));
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
vec.iov_base = (char *)buf;
|
|
vec.iov_len = send_len;
|
|
hdr.msg_iovlen = 1;
|
|
hdr.msg_iov = &vec;
|
|
EXPECT_NE(recvmsg(self->cfd, &hdr, 0), -1);
|
|
EXPECT_EQ(memcmp(test_str, buf, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, recvmsg_single_max)
|
|
{
|
|
int send_len = TLS_PAYLOAD_MAX_LEN;
|
|
char send_mem[TLS_PAYLOAD_MAX_LEN];
|
|
char recv_mem[TLS_PAYLOAD_MAX_LEN];
|
|
struct iovec vec;
|
|
struct msghdr hdr;
|
|
|
|
memrnd(send_mem, sizeof(send_mem));
|
|
|
|
EXPECT_EQ(send(self->fd, send_mem, send_len, 0), send_len);
|
|
vec.iov_base = (char *)recv_mem;
|
|
vec.iov_len = TLS_PAYLOAD_MAX_LEN;
|
|
|
|
hdr.msg_iovlen = 1;
|
|
hdr.msg_iov = &vec;
|
|
EXPECT_NE(recvmsg(self->cfd, &hdr, 0), -1);
|
|
EXPECT_EQ(memcmp(send_mem, recv_mem, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, recvmsg_multiple)
|
|
{
|
|
unsigned int msg_iovlen = 1024;
|
|
struct iovec vec[1024];
|
|
char *iov_base[1024];
|
|
unsigned int iov_len = 16;
|
|
int send_len = 1 << 14;
|
|
char buf[1 << 14];
|
|
struct msghdr hdr;
|
|
int i;
|
|
|
|
memrnd(buf, sizeof(buf));
|
|
|
|
EXPECT_EQ(send(self->fd, buf, send_len, 0), send_len);
|
|
for (i = 0; i < msg_iovlen; i++) {
|
|
iov_base[i] = (char *)malloc(iov_len);
|
|
vec[i].iov_base = iov_base[i];
|
|
vec[i].iov_len = iov_len;
|
|
}
|
|
|
|
hdr.msg_iovlen = msg_iovlen;
|
|
hdr.msg_iov = vec;
|
|
EXPECT_NE(recvmsg(self->cfd, &hdr, 0), -1);
|
|
|
|
for (i = 0; i < msg_iovlen; i++)
|
|
free(iov_base[i]);
|
|
}
|
|
|
|
TEST_F(tls, single_send_multiple_recv)
|
|
{
|
|
unsigned int total_len = TLS_PAYLOAD_MAX_LEN * 2;
|
|
unsigned int send_len = TLS_PAYLOAD_MAX_LEN;
|
|
char send_mem[TLS_PAYLOAD_MAX_LEN * 2];
|
|
char recv_mem[TLS_PAYLOAD_MAX_LEN * 2];
|
|
|
|
memrnd(send_mem, sizeof(send_mem));
|
|
|
|
EXPECT_GE(send(self->fd, send_mem, total_len, 0), 0);
|
|
memset(recv_mem, 0, total_len);
|
|
|
|
EXPECT_NE(recv(self->cfd, recv_mem, send_len, 0), -1);
|
|
EXPECT_NE(recv(self->cfd, recv_mem + send_len, send_len, 0), -1);
|
|
EXPECT_EQ(memcmp(send_mem, recv_mem, total_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, multiple_send_single_recv)
|
|
{
|
|
unsigned int total_len = 2 * 10;
|
|
unsigned int send_len = 10;
|
|
char recv_mem[2 * 10];
|
|
char send_mem[10];
|
|
|
|
memrnd(send_mem, sizeof(send_mem));
|
|
|
|
EXPECT_GE(send(self->fd, send_mem, send_len, 0), 0);
|
|
EXPECT_GE(send(self->fd, send_mem, send_len, 0), 0);
|
|
memset(recv_mem, 0, total_len);
|
|
EXPECT_EQ(recv(self->cfd, recv_mem, total_len, MSG_WAITALL), total_len);
|
|
|
|
EXPECT_EQ(memcmp(send_mem, recv_mem, send_len), 0);
|
|
EXPECT_EQ(memcmp(send_mem, recv_mem + send_len, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, single_send_multiple_recv_non_align)
|
|
{
|
|
const unsigned int total_len = 15;
|
|
const unsigned int recv_len = 10;
|
|
char recv_mem[recv_len * 2];
|
|
char send_mem[total_len];
|
|
|
|
memrnd(send_mem, sizeof(send_mem));
|
|
|
|
EXPECT_GE(send(self->fd, send_mem, total_len, 0), 0);
|
|
memset(recv_mem, 0, total_len);
|
|
|
|
EXPECT_EQ(recv(self->cfd, recv_mem, recv_len, 0), recv_len);
|
|
EXPECT_EQ(recv(self->cfd, recv_mem + recv_len, recv_len, 0), 5);
|
|
EXPECT_EQ(memcmp(send_mem, recv_mem, total_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, recv_partial)
|
|
{
|
|
char const *test_str = "test_read_partial";
|
|
char const *test_str_first = "test_read";
|
|
char const *test_str_second = "_partial";
|
|
int send_len = strlen(test_str) + 1;
|
|
char recv_mem[18];
|
|
|
|
memset(recv_mem, 0, sizeof(recv_mem));
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
EXPECT_EQ(recv(self->cfd, recv_mem, strlen(test_str_first),
|
|
MSG_WAITALL), strlen(test_str_first));
|
|
EXPECT_EQ(memcmp(test_str_first, recv_mem, strlen(test_str_first)), 0);
|
|
memset(recv_mem, 0, sizeof(recv_mem));
|
|
EXPECT_EQ(recv(self->cfd, recv_mem, strlen(test_str_second),
|
|
MSG_WAITALL), strlen(test_str_second));
|
|
EXPECT_EQ(memcmp(test_str_second, recv_mem, strlen(test_str_second)),
|
|
0);
|
|
}
|
|
|
|
TEST_F(tls, recv_nonblock)
|
|
{
|
|
char buf[4096];
|
|
bool err;
|
|
|
|
EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_DONTWAIT), -1);
|
|
err = (errno == EAGAIN || errno == EWOULDBLOCK);
|
|
EXPECT_EQ(err, true);
|
|
}
|
|
|
|
TEST_F(tls, recv_peek)
|
|
{
|
|
char const *test_str = "test_read_peek";
|
|
int send_len = strlen(test_str) + 1;
|
|
char buf[15];
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, MSG_PEEK), send_len);
|
|
EXPECT_EQ(memcmp(test_str, buf, send_len), 0);
|
|
memset(buf, 0, sizeof(buf));
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
|
|
EXPECT_EQ(memcmp(test_str, buf, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, recv_peek_multiple)
|
|
{
|
|
char const *test_str = "test_read_peek";
|
|
int send_len = strlen(test_str) + 1;
|
|
unsigned int num_peeks = 100;
|
|
char buf[15];
|
|
int i;
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
for (i = 0; i < num_peeks; i++) {
|
|
EXPECT_NE(recv(self->cfd, buf, send_len, MSG_PEEK), -1);
|
|
EXPECT_EQ(memcmp(test_str, buf, send_len), 0);
|
|
memset(buf, 0, sizeof(buf));
|
|
}
|
|
EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
|
|
EXPECT_EQ(memcmp(test_str, buf, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, recv_peek_multiple_records)
|
|
{
|
|
char const *test_str = "test_read_peek_mult_recs";
|
|
char const *test_str_first = "test_read_peek";
|
|
char const *test_str_second = "_mult_recs";
|
|
int len;
|
|
char buf[64];
|
|
|
|
len = strlen(test_str_first);
|
|
EXPECT_EQ(send(self->fd, test_str_first, len, 0), len);
|
|
|
|
len = strlen(test_str_second) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str_second, len, 0), len);
|
|
|
|
len = strlen(test_str_first);
|
|
memset(buf, 0, len);
|
|
EXPECT_EQ(recv(self->cfd, buf, len, MSG_PEEK | MSG_WAITALL), len);
|
|
|
|
/* MSG_PEEK can only peek into the current record. */
|
|
len = strlen(test_str_first);
|
|
EXPECT_EQ(memcmp(test_str_first, buf, len), 0);
|
|
|
|
len = strlen(test_str) + 1;
|
|
memset(buf, 0, len);
|
|
EXPECT_EQ(recv(self->cfd, buf, len, MSG_WAITALL), len);
|
|
|
|
/* Non-MSG_PEEK will advance strparser (and therefore record)
|
|
* however.
|
|
*/
|
|
len = strlen(test_str) + 1;
|
|
EXPECT_EQ(memcmp(test_str, buf, len), 0);
|
|
|
|
/* MSG_MORE will hold current record open, so later MSG_PEEK
|
|
* will see everything.
|
|
*/
|
|
len = strlen(test_str_first);
|
|
EXPECT_EQ(send(self->fd, test_str_first, len, MSG_MORE), len);
|
|
|
|
len = strlen(test_str_second) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str_second, len, 0), len);
|
|
|
|
len = strlen(test_str) + 1;
|
|
memset(buf, 0, len);
|
|
EXPECT_EQ(recv(self->cfd, buf, len, MSG_PEEK | MSG_WAITALL), len);
|
|
|
|
len = strlen(test_str) + 1;
|
|
EXPECT_EQ(memcmp(test_str, buf, len), 0);
|
|
}
|
|
|
|
TEST_F(tls, recv_peek_large_buf_mult_recs)
|
|
{
|
|
char const *test_str = "test_read_peek_mult_recs";
|
|
char const *test_str_first = "test_read_peek";
|
|
char const *test_str_second = "_mult_recs";
|
|
int len;
|
|
char buf[64];
|
|
|
|
len = strlen(test_str_first);
|
|
EXPECT_EQ(send(self->fd, test_str_first, len, 0), len);
|
|
|
|
len = strlen(test_str_second) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str_second, len, 0), len);
|
|
|
|
len = strlen(test_str) + 1;
|
|
memset(buf, 0, len);
|
|
EXPECT_NE((len = recv(self->cfd, buf, len,
|
|
MSG_PEEK | MSG_WAITALL)), -1);
|
|
len = strlen(test_str) + 1;
|
|
EXPECT_EQ(memcmp(test_str, buf, len), 0);
|
|
}
|
|
|
|
TEST_F(tls, recv_lowat)
|
|
{
|
|
char send_mem[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
|
char recv_mem[20];
|
|
int lowat = 8;
|
|
|
|
EXPECT_EQ(send(self->fd, send_mem, 10, 0), 10);
|
|
EXPECT_EQ(send(self->fd, send_mem, 5, 0), 5);
|
|
|
|
memset(recv_mem, 0, 20);
|
|
EXPECT_EQ(setsockopt(self->cfd, SOL_SOCKET, SO_RCVLOWAT,
|
|
&lowat, sizeof(lowat)), 0);
|
|
EXPECT_EQ(recv(self->cfd, recv_mem, 1, MSG_WAITALL), 1);
|
|
EXPECT_EQ(recv(self->cfd, recv_mem + 1, 6, MSG_WAITALL), 6);
|
|
EXPECT_EQ(recv(self->cfd, recv_mem + 7, 10, 0), 8);
|
|
|
|
EXPECT_EQ(memcmp(send_mem, recv_mem, 10), 0);
|
|
EXPECT_EQ(memcmp(send_mem, recv_mem + 10, 5), 0);
|
|
}
|
|
|
|
TEST_F(tls, bidir)
|
|
{
|
|
char const *test_str = "test_read";
|
|
int send_len = 10;
|
|
char buf[10];
|
|
int ret;
|
|
|
|
if (!self->notls) {
|
|
struct tls_crypto_info_keys tls12;
|
|
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type,
|
|
&tls12, 0);
|
|
|
|
ret = setsockopt(self->fd, SOL_TLS, TLS_RX, &tls12,
|
|
tls12.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(self->cfd, SOL_TLS, TLS_TX, &tls12,
|
|
tls12.len);
|
|
ASSERT_EQ(ret, 0);
|
|
}
|
|
|
|
ASSERT_EQ(strlen(test_str) + 1, send_len);
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
EXPECT_EQ(send(self->cfd, test_str, send_len, 0), send_len);
|
|
EXPECT_NE(recv(self->fd, buf, send_len, 0), -1);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
};
|
|
|
|
TEST_F(tls, pollin)
|
|
{
|
|
char const *test_str = "test_poll";
|
|
struct pollfd fd = { 0, 0, 0 };
|
|
char buf[10];
|
|
int send_len = 10;
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
fd.fd = self->cfd;
|
|
fd.events = POLLIN;
|
|
|
|
EXPECT_EQ(poll(&fd, 1, 20), 1);
|
|
EXPECT_EQ(fd.revents & POLLIN, 1);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, MSG_WAITALL), send_len);
|
|
/* Test timing out */
|
|
EXPECT_EQ(poll(&fd, 1, 20), 0);
|
|
}
|
|
|
|
TEST_F(tls, poll_wait)
|
|
{
|
|
char const *test_str = "test_poll_wait";
|
|
int send_len = strlen(test_str) + 1;
|
|
struct pollfd fd = { 0, 0, 0 };
|
|
char recv_mem[15];
|
|
|
|
fd.fd = self->cfd;
|
|
fd.events = POLLIN;
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
/* Set timeout to inf. secs */
|
|
EXPECT_EQ(poll(&fd, 1, -1), 1);
|
|
EXPECT_EQ(fd.revents & POLLIN, 1);
|
|
EXPECT_EQ(recv(self->cfd, recv_mem, send_len, MSG_WAITALL), send_len);
|
|
}
|
|
|
|
TEST_F(tls, poll_wait_split)
|
|
{
|
|
struct pollfd fd = { 0, 0, 0 };
|
|
char send_mem[20] = {};
|
|
char recv_mem[15];
|
|
|
|
fd.fd = self->cfd;
|
|
fd.events = POLLIN;
|
|
/* Send 20 bytes */
|
|
EXPECT_EQ(send(self->fd, send_mem, sizeof(send_mem), 0),
|
|
sizeof(send_mem));
|
|
/* Poll with inf. timeout */
|
|
EXPECT_EQ(poll(&fd, 1, -1), 1);
|
|
EXPECT_EQ(fd.revents & POLLIN, 1);
|
|
EXPECT_EQ(recv(self->cfd, recv_mem, sizeof(recv_mem), MSG_WAITALL),
|
|
sizeof(recv_mem));
|
|
|
|
/* Now the remaining 5 bytes of record data are in TLS ULP */
|
|
fd.fd = self->cfd;
|
|
fd.events = POLLIN;
|
|
EXPECT_EQ(poll(&fd, 1, -1), 1);
|
|
EXPECT_EQ(fd.revents & POLLIN, 1);
|
|
EXPECT_EQ(recv(self->cfd, recv_mem, sizeof(recv_mem), 0),
|
|
sizeof(send_mem) - sizeof(recv_mem));
|
|
}
|
|
|
|
TEST_F(tls, blocking)
|
|
{
|
|
size_t data = 100000;
|
|
int res = fork();
|
|
|
|
EXPECT_NE(res, -1);
|
|
|
|
if (res) {
|
|
/* parent */
|
|
size_t left = data;
|
|
char buf[16384];
|
|
int status;
|
|
int pid2;
|
|
|
|
while (left) {
|
|
int res = send(self->fd, buf,
|
|
left > 16384 ? 16384 : left, 0);
|
|
|
|
EXPECT_GE(res, 0);
|
|
left -= res;
|
|
}
|
|
|
|
pid2 = wait(&status);
|
|
EXPECT_EQ(status, 0);
|
|
EXPECT_EQ(res, pid2);
|
|
} else {
|
|
/* child */
|
|
size_t left = data;
|
|
char buf[16384];
|
|
|
|
while (left) {
|
|
int res = recv(self->cfd, buf,
|
|
left > 16384 ? 16384 : left, 0);
|
|
|
|
EXPECT_GE(res, 0);
|
|
left -= res;
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(tls, nonblocking)
|
|
{
|
|
size_t data = 100000;
|
|
int sendbuf = 100;
|
|
int flags;
|
|
int res;
|
|
|
|
flags = fcntl(self->fd, F_GETFL, 0);
|
|
fcntl(self->fd, F_SETFL, flags | O_NONBLOCK);
|
|
fcntl(self->cfd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
/* Ensure nonblocking behavior by imposing a small send
|
|
* buffer.
|
|
*/
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_SOCKET, SO_SNDBUF,
|
|
&sendbuf, sizeof(sendbuf)), 0);
|
|
|
|
res = fork();
|
|
EXPECT_NE(res, -1);
|
|
|
|
if (res) {
|
|
/* parent */
|
|
bool eagain = false;
|
|
size_t left = data;
|
|
char buf[16384];
|
|
int status;
|
|
int pid2;
|
|
|
|
while (left) {
|
|
int res = send(self->fd, buf,
|
|
left > 16384 ? 16384 : left, 0);
|
|
|
|
if (res == -1 && errno == EAGAIN) {
|
|
eagain = true;
|
|
usleep(10000);
|
|
continue;
|
|
}
|
|
EXPECT_GE(res, 0);
|
|
left -= res;
|
|
}
|
|
|
|
EXPECT_TRUE(eagain);
|
|
pid2 = wait(&status);
|
|
|
|
EXPECT_EQ(status, 0);
|
|
EXPECT_EQ(res, pid2);
|
|
} else {
|
|
/* child */
|
|
bool eagain = false;
|
|
size_t left = data;
|
|
char buf[16384];
|
|
|
|
while (left) {
|
|
int res = recv(self->cfd, buf,
|
|
left > 16384 ? 16384 : left, 0);
|
|
|
|
if (res == -1 && errno == EAGAIN) {
|
|
eagain = true;
|
|
usleep(10000);
|
|
continue;
|
|
}
|
|
EXPECT_GE(res, 0);
|
|
left -= res;
|
|
}
|
|
EXPECT_TRUE(eagain);
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_mutliproc(struct __test_metadata *_metadata, struct _test_data_tls *self,
|
|
bool sendpg, unsigned int n_readers, unsigned int n_writers)
|
|
{
|
|
const unsigned int n_children = n_readers + n_writers;
|
|
const size_t data = 6 * 1000 * 1000;
|
|
const size_t file_sz = data / 100;
|
|
size_t read_bias, write_bias;
|
|
int i, fd, child_id;
|
|
char buf[file_sz];
|
|
pid_t pid;
|
|
|
|
/* Only allow multiples for simplicity */
|
|
ASSERT_EQ(!(n_readers % n_writers) || !(n_writers % n_readers), true);
|
|
read_bias = n_writers / n_readers ?: 1;
|
|
write_bias = n_readers / n_writers ?: 1;
|
|
|
|
/* prep a file to send */
|
|
fd = open("/tmp/", O_TMPFILE | O_RDWR, 0600);
|
|
ASSERT_GE(fd, 0);
|
|
|
|
memset(buf, 0xac, file_sz);
|
|
ASSERT_EQ(write(fd, buf, file_sz), file_sz);
|
|
|
|
/* spawn children */
|
|
for (child_id = 0; child_id < n_children; child_id++) {
|
|
pid = fork();
|
|
ASSERT_NE(pid, -1);
|
|
if (!pid)
|
|
break;
|
|
}
|
|
|
|
/* parent waits for all children */
|
|
if (pid) {
|
|
for (i = 0; i < n_children; i++) {
|
|
int status;
|
|
|
|
wait(&status);
|
|
EXPECT_EQ(status, 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Split threads for reading and writing */
|
|
if (child_id < n_readers) {
|
|
size_t left = data * read_bias;
|
|
char rb[8001];
|
|
|
|
while (left) {
|
|
int res;
|
|
|
|
res = recv(self->cfd, rb,
|
|
left > sizeof(rb) ? sizeof(rb) : left, 0);
|
|
|
|
ASSERT_GE(res, 0);
|
|
left -= res;
|
|
}
|
|
} else {
|
|
size_t left = data * write_bias;
|
|
|
|
while (left) {
|
|
int res;
|
|
|
|
ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
|
|
if (sendpg)
|
|
res = sendfile(self->fd, fd, NULL,
|
|
left > file_sz ? file_sz : left);
|
|
else
|
|
res = send(self->fd, buf,
|
|
left > file_sz ? file_sz : left, 0);
|
|
|
|
ASSERT_GE(res, 0);
|
|
left -= res;
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(tls, mutliproc_even)
|
|
{
|
|
test_mutliproc(_metadata, self, false, 6, 6);
|
|
}
|
|
|
|
TEST_F(tls, mutliproc_readers)
|
|
{
|
|
test_mutliproc(_metadata, self, false, 4, 12);
|
|
}
|
|
|
|
TEST_F(tls, mutliproc_writers)
|
|
{
|
|
test_mutliproc(_metadata, self, false, 10, 2);
|
|
}
|
|
|
|
TEST_F(tls, mutliproc_sendpage_even)
|
|
{
|
|
test_mutliproc(_metadata, self, true, 6, 6);
|
|
}
|
|
|
|
TEST_F(tls, mutliproc_sendpage_readers)
|
|
{
|
|
test_mutliproc(_metadata, self, true, 4, 12);
|
|
}
|
|
|
|
TEST_F(tls, mutliproc_sendpage_writers)
|
|
{
|
|
test_mutliproc(_metadata, self, true, 10, 2);
|
|
}
|
|
|
|
TEST_F(tls, control_msg)
|
|
{
|
|
char *test_str = "test_read";
|
|
char record_type = 100;
|
|
int send_len = 10;
|
|
char buf[10];
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
EXPECT_EQ(tls_send_cmsg(self->fd, record_type, test_str, send_len, 0),
|
|
send_len);
|
|
/* Should fail because we didn't provide a control message */
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), -1);
|
|
|
|
EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, record_type,
|
|
buf, sizeof(buf), MSG_WAITALL | MSG_PEEK),
|
|
send_len);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
|
|
/* Recv the message again without MSG_PEEK */
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, record_type,
|
|
buf, sizeof(buf), MSG_WAITALL),
|
|
send_len);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, control_msg_nomerge)
|
|
{
|
|
char *rec1 = "1111";
|
|
char *rec2 = "2222";
|
|
int send_len = 5;
|
|
char buf[15];
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
EXPECT_EQ(tls_send_cmsg(self->fd, 100, rec1, send_len, 0), send_len);
|
|
EXPECT_EQ(tls_send_cmsg(self->fd, 100, rec2, send_len, 0), send_len);
|
|
|
|
EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100, buf, sizeof(buf), MSG_PEEK), send_len);
|
|
EXPECT_EQ(memcmp(buf, rec1, send_len), 0);
|
|
|
|
EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100, buf, sizeof(buf), MSG_PEEK), send_len);
|
|
EXPECT_EQ(memcmp(buf, rec1, send_len), 0);
|
|
|
|
EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100, buf, sizeof(buf), 0), send_len);
|
|
EXPECT_EQ(memcmp(buf, rec1, send_len), 0);
|
|
|
|
EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100, buf, sizeof(buf), 0), send_len);
|
|
EXPECT_EQ(memcmp(buf, rec2, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, data_control_data)
|
|
{
|
|
char *rec1 = "1111";
|
|
char *rec2 = "2222";
|
|
char *rec3 = "3333";
|
|
int send_len = 5;
|
|
char buf[15];
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
EXPECT_EQ(send(self->fd, rec1, send_len, 0), send_len);
|
|
EXPECT_EQ(tls_send_cmsg(self->fd, 100, rec2, send_len, 0), send_len);
|
|
EXPECT_EQ(send(self->fd, rec3, send_len, 0), send_len);
|
|
|
|
EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_PEEK), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_PEEK), send_len);
|
|
}
|
|
|
|
TEST_F(tls, shutdown)
|
|
{
|
|
char const *test_str = "test_read";
|
|
int send_len = 10;
|
|
char buf[10];
|
|
|
|
ASSERT_EQ(strlen(test_str) + 1, send_len);
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
|
|
shutdown(self->fd, SHUT_RDWR);
|
|
shutdown(self->cfd, SHUT_RDWR);
|
|
}
|
|
|
|
TEST_F(tls, shutdown_unsent)
|
|
{
|
|
char const *test_str = "test_read";
|
|
int send_len = 10;
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, MSG_MORE), send_len);
|
|
|
|
shutdown(self->fd, SHUT_RDWR);
|
|
shutdown(self->cfd, SHUT_RDWR);
|
|
}
|
|
|
|
TEST_F(tls, shutdown_reuse)
|
|
{
|
|
struct sockaddr_in addr;
|
|
int ret;
|
|
|
|
shutdown(self->fd, SHUT_RDWR);
|
|
shutdown(self->cfd, SHUT_RDWR);
|
|
close(self->cfd);
|
|
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
addr.sin_port = 0;
|
|
|
|
ret = bind(self->fd, &addr, sizeof(addr));
|
|
EXPECT_EQ(ret, 0);
|
|
ret = listen(self->fd, 10);
|
|
EXPECT_EQ(ret, -1);
|
|
EXPECT_EQ(errno, EINVAL);
|
|
|
|
ret = connect(self->fd, &addr, sizeof(addr));
|
|
EXPECT_EQ(ret, -1);
|
|
EXPECT_EQ(errno, EISCONN);
|
|
}
|
|
|
|
TEST_F(tls, getsockopt)
|
|
{
|
|
struct tls_crypto_info_keys expect, get;
|
|
socklen_t len;
|
|
|
|
/* get only the version/cipher */
|
|
len = sizeof(struct tls_crypto_info);
|
|
memrnd(&get, sizeof(get));
|
|
EXPECT_EQ(getsockopt(self->fd, SOL_TLS, TLS_TX, &get, &len), 0);
|
|
EXPECT_EQ(len, sizeof(struct tls_crypto_info));
|
|
EXPECT_EQ(get.crypto_info.version, variant->tls_version);
|
|
EXPECT_EQ(get.crypto_info.cipher_type, variant->cipher_type);
|
|
|
|
/* get the full crypto_info */
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &expect, 0);
|
|
len = expect.len;
|
|
memrnd(&get, sizeof(get));
|
|
EXPECT_EQ(getsockopt(self->fd, SOL_TLS, TLS_TX, &get, &len), 0);
|
|
EXPECT_EQ(len, expect.len);
|
|
EXPECT_EQ(get.crypto_info.version, variant->tls_version);
|
|
EXPECT_EQ(get.crypto_info.cipher_type, variant->cipher_type);
|
|
EXPECT_EQ(memcmp(&get, &expect, expect.len), 0);
|
|
|
|
/* short get should fail */
|
|
len = sizeof(struct tls_crypto_info) - 1;
|
|
EXPECT_EQ(getsockopt(self->fd, SOL_TLS, TLS_TX, &get, &len), -1);
|
|
EXPECT_EQ(errno, EINVAL);
|
|
|
|
/* partial get of the cipher data should fail */
|
|
len = expect.len - 1;
|
|
EXPECT_EQ(getsockopt(self->fd, SOL_TLS, TLS_TX, &get, &len), -1);
|
|
EXPECT_EQ(errno, EINVAL);
|
|
}
|
|
|
|
TEST_F(tls, recv_efault)
|
|
{
|
|
char *rec1 = "1111111111";
|
|
char *rec2 = "2222222222";
|
|
struct msghdr hdr = {};
|
|
struct iovec iov[2];
|
|
char recv_mem[12];
|
|
int ret;
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
EXPECT_EQ(send(self->fd, rec1, 10, 0), 10);
|
|
EXPECT_EQ(send(self->fd, rec2, 10, 0), 10);
|
|
|
|
iov[0].iov_base = recv_mem;
|
|
iov[0].iov_len = sizeof(recv_mem);
|
|
iov[1].iov_base = NULL; /* broken iov to make process_rx_list fail */
|
|
iov[1].iov_len = 1;
|
|
|
|
hdr.msg_iovlen = 2;
|
|
hdr.msg_iov = iov;
|
|
|
|
EXPECT_EQ(recv(self->cfd, recv_mem, 1, 0), 1);
|
|
EXPECT_EQ(recv_mem[0], rec1[0]);
|
|
|
|
ret = recvmsg(self->cfd, &hdr, 0);
|
|
EXPECT_LE(ret, sizeof(recv_mem));
|
|
EXPECT_GE(ret, 9);
|
|
EXPECT_EQ(memcmp(rec1, recv_mem, 9), 0);
|
|
if (ret > 9)
|
|
EXPECT_EQ(memcmp(rec2, recv_mem + 9, ret - 9), 0);
|
|
}
|
|
|
|
#define TLS_RECORD_TYPE_HANDSHAKE 0x16
|
|
/* key_update, length 1, update_not_requested */
|
|
static const char key_update_msg[] = "\x18\x00\x00\x01\x00";
|
|
static void tls_send_keyupdate(struct __test_metadata *_metadata, int fd)
|
|
{
|
|
size_t len = sizeof(key_update_msg);
|
|
|
|
EXPECT_EQ(tls_send_cmsg(fd, TLS_RECORD_TYPE_HANDSHAKE,
|
|
(char *)key_update_msg, len, 0),
|
|
len);
|
|
}
|
|
|
|
static void tls_recv_keyupdate(struct __test_metadata *_metadata, int fd, int flags)
|
|
{
|
|
char buf[100];
|
|
|
|
EXPECT_EQ(tls_recv_cmsg(_metadata, fd, TLS_RECORD_TYPE_HANDSHAKE, buf, sizeof(buf), flags),
|
|
sizeof(key_update_msg));
|
|
EXPECT_EQ(memcmp(buf, key_update_msg, sizeof(key_update_msg)), 0);
|
|
}
|
|
|
|
/* set the key to 0 then 1 for RX, immediately to 1 for TX */
|
|
TEST_F(tls_basic, rekey_rx)
|
|
{
|
|
struct tls_crypto_info_keys tls12_0, tls12_1;
|
|
char const *test_str = "test_message";
|
|
int send_len = strlen(test_str) + 1;
|
|
char buf[20];
|
|
int ret;
|
|
|
|
if (self->notls)
|
|
return;
|
|
|
|
tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128,
|
|
&tls12_0, 0);
|
|
tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128,
|
|
&tls12_1, 1);
|
|
|
|
ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12_1, tls12_1.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12_0, tls12_0.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12_1, tls12_1.len);
|
|
EXPECT_EQ(ret, 0);
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
}
|
|
|
|
/* set the key to 0 then 1 for TX, immediately to 1 for RX */
|
|
TEST_F(tls_basic, rekey_tx)
|
|
{
|
|
struct tls_crypto_info_keys tls12_0, tls12_1;
|
|
char const *test_str = "test_message";
|
|
int send_len = strlen(test_str) + 1;
|
|
char buf[20];
|
|
int ret;
|
|
|
|
if (self->notls)
|
|
return;
|
|
|
|
tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128,
|
|
&tls12_0, 0);
|
|
tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128,
|
|
&tls12_1, 1);
|
|
|
|
ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12_0, tls12_0.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12_1, tls12_1.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12_1, tls12_1.len);
|
|
EXPECT_EQ(ret, 0);
|
|
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
|
|
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls_basic, disconnect)
|
|
{
|
|
char const *test_str = "test_message";
|
|
int send_len = strlen(test_str) + 1;
|
|
struct tls_crypto_info_keys key;
|
|
struct sockaddr_in addr;
|
|
char buf[20];
|
|
int ret;
|
|
|
|
if (self->notls)
|
|
return;
|
|
|
|
tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128,
|
|
&key, 0);
|
|
|
|
ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &key, key.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
/* Pre-queue the data so that setsockopt parses it but doesn't
|
|
* dequeue it from the TCP socket. recvmsg would dequeue.
|
|
*/
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
|
|
ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &key, key.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
addr.sin_family = AF_UNSPEC;
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
addr.sin_port = 0;
|
|
ret = connect(self->cfd, &addr, sizeof(addr));
|
|
EXPECT_EQ(ret, -1);
|
|
EXPECT_EQ(errno, EOPNOTSUPP);
|
|
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
|
|
}
|
|
|
|
TEST_F(tls, rekey)
|
|
{
|
|
char const *test_str_1 = "test_message_before_rekey";
|
|
char const *test_str_2 = "test_message_after_rekey";
|
|
struct tls_crypto_info_keys tls12;
|
|
int send_len;
|
|
char buf[100];
|
|
|
|
if (variant->tls_version != TLS_1_3_VERSION)
|
|
return;
|
|
|
|
/* initial send/recv */
|
|
send_len = strlen(test_str_1) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str_1, send_len, 0), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
|
|
EXPECT_EQ(memcmp(buf, test_str_1, send_len), 0);
|
|
|
|
/* update TX key */
|
|
tls_send_keyupdate(_metadata, self->fd);
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
|
|
|
|
/* send after rekey */
|
|
send_len = strlen(test_str_2) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str_2, send_len, 0), send_len);
|
|
|
|
/* can't receive the KeyUpdate without a control message */
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), -1);
|
|
|
|
/* get KeyUpdate */
|
|
tls_recv_keyupdate(_metadata, self->cfd, 0);
|
|
|
|
/* recv blocking -> -EKEYEXPIRED */
|
|
EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EKEYEXPIRED);
|
|
|
|
/* recv non-blocking -> -EKEYEXPIRED */
|
|
EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_DONTWAIT), -1);
|
|
EXPECT_EQ(errno, EKEYEXPIRED);
|
|
|
|
/* update RX key */
|
|
EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
|
|
|
|
/* recv after rekey */
|
|
EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
|
|
EXPECT_EQ(memcmp(buf, test_str_2, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, rekey_fail)
|
|
{
|
|
char const *test_str_1 = "test_message_before_rekey";
|
|
char const *test_str_2 = "test_message_after_rekey";
|
|
struct tls_crypto_info_keys tls12;
|
|
int send_len;
|
|
char buf[100];
|
|
|
|
/* initial send/recv */
|
|
send_len = strlen(test_str_1) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str_1, send_len, 0), send_len);
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
|
|
EXPECT_EQ(memcmp(buf, test_str_1, send_len), 0);
|
|
|
|
/* update TX key */
|
|
tls_send_keyupdate(_metadata, self->fd);
|
|
|
|
if (variant->tls_version != TLS_1_3_VERSION) {
|
|
/* just check that rekey is not supported and return */
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), -1);
|
|
EXPECT_EQ(errno, EBUSY);
|
|
return;
|
|
}
|
|
|
|
/* successful update */
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
|
|
|
|
/* invalid update: change of version */
|
|
tls_crypto_info_init(TLS_1_2_VERSION, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), -1);
|
|
EXPECT_EQ(errno, EINVAL);
|
|
|
|
/* invalid update (RX socket): change of version */
|
|
tls_crypto_info_init(TLS_1_2_VERSION, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), -1);
|
|
EXPECT_EQ(errno, EINVAL);
|
|
|
|
/* invalid update: change of cipher */
|
|
if (variant->cipher_type == TLS_CIPHER_AES_GCM_256)
|
|
tls_crypto_info_init(variant->tls_version, TLS_CIPHER_CHACHA20_POLY1305, &tls12, 1);
|
|
else
|
|
tls_crypto_info_init(variant->tls_version, TLS_CIPHER_AES_GCM_256, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), -1);
|
|
EXPECT_EQ(errno, EINVAL);
|
|
|
|
/* send after rekey, the invalid updates shouldn't have an effect */
|
|
send_len = strlen(test_str_2) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str_2, send_len, 0), send_len);
|
|
|
|
/* can't receive the KeyUpdate without a control message */
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), -1);
|
|
|
|
/* get KeyUpdate */
|
|
tls_recv_keyupdate(_metadata, self->cfd, 0);
|
|
|
|
/* recv blocking -> -EKEYEXPIRED */
|
|
EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EKEYEXPIRED);
|
|
|
|
/* recv non-blocking -> -EKEYEXPIRED */
|
|
EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_DONTWAIT), -1);
|
|
EXPECT_EQ(errno, EKEYEXPIRED);
|
|
|
|
/* update RX key */
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
|
|
|
|
/* recv after rekey */
|
|
EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
|
|
EXPECT_EQ(memcmp(buf, test_str_2, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, rekey_peek)
|
|
{
|
|
char const *test_str_1 = "test_message_before_rekey";
|
|
struct tls_crypto_info_keys tls12;
|
|
int send_len;
|
|
char buf[100];
|
|
|
|
if (variant->tls_version != TLS_1_3_VERSION)
|
|
return;
|
|
|
|
send_len = strlen(test_str_1) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str_1, send_len, 0), send_len);
|
|
|
|
/* update TX key */
|
|
tls_send_keyupdate(_metadata, self->fd);
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
|
|
|
|
EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_PEEK), send_len);
|
|
EXPECT_EQ(memcmp(buf, test_str_1, send_len), 0);
|
|
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
|
|
EXPECT_EQ(memcmp(buf, test_str_1, send_len), 0);
|
|
|
|
/* can't receive the KeyUpdate without a control message */
|
|
EXPECT_EQ(recv(self->cfd, buf, send_len, MSG_PEEK), -1);
|
|
|
|
/* peek KeyUpdate */
|
|
tls_recv_keyupdate(_metadata, self->cfd, MSG_PEEK);
|
|
|
|
/* get KeyUpdate */
|
|
tls_recv_keyupdate(_metadata, self->cfd, 0);
|
|
|
|
/* update RX key */
|
|
EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
|
|
}
|
|
|
|
TEST_F(tls, splice_rekey)
|
|
{
|
|
int send_len = TLS_PAYLOAD_MAX_LEN / 2;
|
|
char mem_send[TLS_PAYLOAD_MAX_LEN];
|
|
char mem_recv[TLS_PAYLOAD_MAX_LEN];
|
|
struct tls_crypto_info_keys tls12;
|
|
int p[2];
|
|
|
|
if (variant->tls_version != TLS_1_3_VERSION)
|
|
return;
|
|
|
|
memrnd(mem_send, sizeof(mem_send));
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
|
|
|
|
/* update TX key */
|
|
tls_send_keyupdate(_metadata, self->fd);
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
|
|
|
|
EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
|
|
|
|
EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, TLS_PAYLOAD_MAX_LEN, 0), send_len);
|
|
EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
|
|
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
|
|
|
|
/* can't splice the KeyUpdate */
|
|
EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, TLS_PAYLOAD_MAX_LEN, 0), -1);
|
|
EXPECT_EQ(errno, EINVAL);
|
|
|
|
/* peek KeyUpdate */
|
|
tls_recv_keyupdate(_metadata, self->cfd, MSG_PEEK);
|
|
|
|
/* get KeyUpdate */
|
|
tls_recv_keyupdate(_metadata, self->cfd, 0);
|
|
|
|
/* can't splice before updating the key */
|
|
EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, TLS_PAYLOAD_MAX_LEN, 0), -1);
|
|
EXPECT_EQ(errno, EKEYEXPIRED);
|
|
|
|
/* update RX key */
|
|
EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
|
|
|
|
EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, TLS_PAYLOAD_MAX_LEN, 0), send_len);
|
|
EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
|
|
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, rekey_peek_splice)
|
|
{
|
|
char const *test_str_1 = "test_message_before_rekey";
|
|
struct tls_crypto_info_keys tls12;
|
|
int send_len;
|
|
char buf[100];
|
|
char mem_recv[TLS_PAYLOAD_MAX_LEN];
|
|
int p[2];
|
|
|
|
if (variant->tls_version != TLS_1_3_VERSION)
|
|
return;
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
|
|
send_len = strlen(test_str_1) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str_1, send_len, 0), send_len);
|
|
|
|
/* update TX key */
|
|
tls_send_keyupdate(_metadata, self->fd);
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
|
|
|
|
EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_PEEK), send_len);
|
|
EXPECT_EQ(memcmp(buf, test_str_1, send_len), 0);
|
|
|
|
EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, TLS_PAYLOAD_MAX_LEN, 0), send_len);
|
|
EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
|
|
EXPECT_EQ(memcmp(mem_recv, test_str_1, send_len), 0);
|
|
}
|
|
|
|
TEST_F(tls, rekey_getsockopt)
|
|
{
|
|
struct tls_crypto_info_keys tls12;
|
|
struct tls_crypto_info_keys tls12_get;
|
|
socklen_t len;
|
|
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 0);
|
|
|
|
len = tls12.len;
|
|
EXPECT_EQ(getsockopt(self->fd, SOL_TLS, TLS_TX, &tls12_get, &len), 0);
|
|
EXPECT_EQ(len, tls12.len);
|
|
EXPECT_EQ(memcmp(&tls12_get, &tls12, tls12.len), 0);
|
|
|
|
len = tls12.len;
|
|
EXPECT_EQ(getsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12_get, &len), 0);
|
|
EXPECT_EQ(len, tls12.len);
|
|
EXPECT_EQ(memcmp(&tls12_get, &tls12, tls12.len), 0);
|
|
|
|
if (variant->tls_version != TLS_1_3_VERSION)
|
|
return;
|
|
|
|
tls_send_keyupdate(_metadata, self->fd);
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
|
|
|
|
tls_recv_keyupdate(_metadata, self->cfd, 0);
|
|
EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
|
|
|
|
len = tls12.len;
|
|
EXPECT_EQ(getsockopt(self->fd, SOL_TLS, TLS_TX, &tls12_get, &len), 0);
|
|
EXPECT_EQ(len, tls12.len);
|
|
EXPECT_EQ(memcmp(&tls12_get, &tls12, tls12.len), 0);
|
|
|
|
len = tls12.len;
|
|
EXPECT_EQ(getsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12_get, &len), 0);
|
|
EXPECT_EQ(len, tls12.len);
|
|
EXPECT_EQ(memcmp(&tls12_get, &tls12, tls12.len), 0);
|
|
}
|
|
|
|
TEST_F(tls, rekey_poll_pending)
|
|
{
|
|
char const *test_str = "test_message_after_rekey";
|
|
struct tls_crypto_info_keys tls12;
|
|
struct pollfd pfd = { };
|
|
int send_len;
|
|
int ret;
|
|
|
|
if (variant->tls_version != TLS_1_3_VERSION)
|
|
return;
|
|
|
|
/* update TX key */
|
|
tls_send_keyupdate(_metadata, self->fd);
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
|
|
|
|
/* get KeyUpdate */
|
|
tls_recv_keyupdate(_metadata, self->cfd, 0);
|
|
|
|
/* send immediately after rekey */
|
|
send_len = strlen(test_str) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
|
|
/* key hasn't been updated, expect cfd to be non-readable */
|
|
pfd.fd = self->cfd;
|
|
pfd.events = POLLIN;
|
|
EXPECT_EQ(poll(&pfd, 1, 0), 0);
|
|
|
|
ret = fork();
|
|
ASSERT_GE(ret, 0);
|
|
|
|
if (ret) {
|
|
int pid2, status;
|
|
|
|
/* wait before installing the new key */
|
|
sleep(1);
|
|
|
|
/* update RX key while poll() is sleeping */
|
|
EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
|
|
|
|
pid2 = wait(&status);
|
|
EXPECT_EQ(pid2, ret);
|
|
EXPECT_EQ(status, 0);
|
|
} else {
|
|
pfd.fd = self->cfd;
|
|
pfd.events = POLLIN;
|
|
EXPECT_EQ(poll(&pfd, 1, 5000), 1);
|
|
|
|
exit(!__test_passed(_metadata));
|
|
}
|
|
}
|
|
|
|
TEST_F(tls, rekey_poll_delay)
|
|
{
|
|
char const *test_str = "test_message_after_rekey";
|
|
struct tls_crypto_info_keys tls12;
|
|
struct pollfd pfd = { };
|
|
int send_len;
|
|
int ret;
|
|
|
|
if (variant->tls_version != TLS_1_3_VERSION)
|
|
return;
|
|
|
|
/* update TX key */
|
|
tls_send_keyupdate(_metadata, self->fd);
|
|
tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
|
|
EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
|
|
|
|
/* get KeyUpdate */
|
|
tls_recv_keyupdate(_metadata, self->cfd, 0);
|
|
|
|
ret = fork();
|
|
ASSERT_GE(ret, 0);
|
|
|
|
if (ret) {
|
|
int pid2, status;
|
|
|
|
/* wait before installing the new key */
|
|
sleep(1);
|
|
|
|
/* update RX key while poll() is sleeping */
|
|
EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
|
|
|
|
sleep(1);
|
|
send_len = strlen(test_str) + 1;
|
|
EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
|
|
|
|
pid2 = wait(&status);
|
|
EXPECT_EQ(pid2, ret);
|
|
EXPECT_EQ(status, 0);
|
|
} else {
|
|
pfd.fd = self->cfd;
|
|
pfd.events = POLLIN;
|
|
EXPECT_EQ(poll(&pfd, 1, 5000), 1);
|
|
exit(!__test_passed(_metadata));
|
|
}
|
|
}
|
|
|
|
struct raw_rec {
|
|
unsigned int plain_len;
|
|
unsigned char plain_data[100];
|
|
unsigned int cipher_len;
|
|
unsigned char cipher_data[128];
|
|
};
|
|
|
|
/* TLS 1.2, AES_CCM, data, seqno:0, plaintext: 'Hello world' */
|
|
static const struct raw_rec id0_data_l11 = {
|
|
.plain_len = 11,
|
|
.plain_data = {
|
|
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f,
|
|
0x72, 0x6c, 0x64,
|
|
},
|
|
.cipher_len = 40,
|
|
.cipher_data = {
|
|
0x17, 0x03, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0xa2, 0x33,
|
|
0xde, 0x8d, 0x94, 0xf0, 0x29, 0x6c, 0xb1, 0xaf,
|
|
0x6a, 0x75, 0xb2, 0x93, 0xad, 0x45, 0xd5, 0xfd,
|
|
0x03, 0x51, 0x57, 0x8f, 0xf9, 0xcc, 0x3b, 0x42,
|
|
},
|
|
};
|
|
|
|
/* TLS 1.2, AES_CCM, ctrl, seqno:0, plaintext: '' */
|
|
static const struct raw_rec id0_ctrl_l0 = {
|
|
.plain_len = 0,
|
|
.plain_data = {
|
|
},
|
|
.cipher_len = 29,
|
|
.cipher_data = {
|
|
0x16, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x38, 0x7b,
|
|
0xa6, 0x1c, 0xdd, 0xa7, 0x19, 0x33, 0xab, 0xae,
|
|
0x88, 0xe1, 0xd2, 0x08, 0x4f,
|
|
},
|
|
};
|
|
|
|
/* TLS 1.2, AES_CCM, data, seqno:0, plaintext: '' */
|
|
static const struct raw_rec id0_data_l0 = {
|
|
.plain_len = 0,
|
|
.plain_data = {
|
|
},
|
|
.cipher_len = 29,
|
|
.cipher_data = {
|
|
0x17, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x37, 0x90,
|
|
0x70, 0x45, 0x89, 0xfb, 0x5c, 0xc7, 0x89, 0x03,
|
|
0x68, 0x80, 0xd3, 0xd8, 0xcc,
|
|
},
|
|
};
|
|
|
|
/* TLS 1.2, AES_CCM, data, seqno:1, plaintext: 'Hello world' */
|
|
static const struct raw_rec id1_data_l11 = {
|
|
.plain_len = 11,
|
|
.plain_data = {
|
|
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f,
|
|
0x72, 0x6c, 0x64,
|
|
},
|
|
.cipher_len = 40,
|
|
.cipher_data = {
|
|
0x17, 0x03, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x1a, 0x9c,
|
|
0xd0, 0xa8, 0x9a, 0xd6, 0x69, 0xd6, 0x1a, 0xe3,
|
|
0xb5, 0x1f, 0x0d, 0x2c, 0xe2, 0x97, 0x46, 0xff,
|
|
0x2b, 0xcc, 0x5a, 0xc4, 0xa3, 0xb9, 0xef, 0xba,
|
|
},
|
|
};
|
|
|
|
/* TLS 1.2, AES_CCM, ctrl, seqno:1, plaintext: '' */
|
|
static const struct raw_rec id1_ctrl_l0 = {
|
|
.plain_len = 0,
|
|
.plain_data = {
|
|
},
|
|
.cipher_len = 29,
|
|
.cipher_data = {
|
|
0x16, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0xf0, 0xfe,
|
|
0xee, 0xd9, 0xe2, 0x5d, 0xc7, 0x11, 0x4c, 0xe6,
|
|
0xb4, 0x7e, 0xef, 0x40, 0x2b,
|
|
},
|
|
};
|
|
|
|
/* TLS 1.2, AES_CCM, data, seqno:1, plaintext: '' */
|
|
static const struct raw_rec id1_data_l0 = {
|
|
.plain_len = 0,
|
|
.plain_data = {
|
|
},
|
|
.cipher_len = 29,
|
|
.cipher_data = {
|
|
0x17, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xce, 0xfc, 0x86,
|
|
0xc8, 0xf0, 0x55, 0xf9, 0x47, 0x3f, 0x74, 0xdc,
|
|
0xc9, 0xbf, 0xfe, 0x5b, 0xb1,
|
|
},
|
|
};
|
|
|
|
/* TLS 1.2, AES_CCM, ctrl, seqno:2, plaintext: 'Hello world' */
|
|
static const struct raw_rec id2_ctrl_l11 = {
|
|
.plain_len = 11,
|
|
.plain_data = {
|
|
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f,
|
|
0x72, 0x6c, 0x64,
|
|
},
|
|
.cipher_len = 40,
|
|
.cipher_data = {
|
|
0x16, 0x03, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x02, 0xe5, 0x3d, 0x19,
|
|
0x3d, 0xca, 0xb8, 0x16, 0xb6, 0xff, 0x79, 0x87,
|
|
0x2a, 0x04, 0x11, 0x3d, 0xf8, 0x64, 0x5f, 0x36,
|
|
0x8b, 0xa8, 0xee, 0x4c, 0x6d, 0x62, 0xa5, 0x00,
|
|
},
|
|
};
|
|
|
|
/* TLS 1.2, AES_CCM, data, seqno:2, plaintext: 'Hello world' */
|
|
static const struct raw_rec id2_data_l11 = {
|
|
.plain_len = 11,
|
|
.plain_data = {
|
|
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f,
|
|
0x72, 0x6c, 0x64,
|
|
},
|
|
.cipher_len = 40,
|
|
.cipher_data = {
|
|
0x17, 0x03, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x02, 0xe5, 0x3d, 0x19,
|
|
0x3d, 0xca, 0xb8, 0x16, 0xb6, 0xff, 0x79, 0x87,
|
|
0x8e, 0xa1, 0xd0, 0xcd, 0x33, 0xb5, 0x86, 0x2b,
|
|
0x17, 0xf1, 0x52, 0x2a, 0x55, 0x62, 0x65, 0x11,
|
|
},
|
|
};
|
|
|
|
/* TLS 1.2, AES_CCM, ctrl, seqno:2, plaintext: '' */
|
|
static const struct raw_rec id2_ctrl_l0 = {
|
|
.plain_len = 0,
|
|
.plain_data = {
|
|
},
|
|
.cipher_len = 29,
|
|
.cipher_data = {
|
|
0x16, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x02, 0xdc, 0x5c, 0x0e,
|
|
0x41, 0xdd, 0xba, 0xd3, 0xcc, 0xcf, 0x6d, 0xd9,
|
|
0x06, 0xdb, 0x79, 0xe5, 0x5d,
|
|
},
|
|
};
|
|
|
|
/* TLS 1.2, AES_CCM, data, seqno:2, plaintext: '' */
|
|
static const struct raw_rec id2_data_l0 = {
|
|
.plain_len = 0,
|
|
.plain_data = {
|
|
},
|
|
.cipher_len = 29,
|
|
.cipher_data = {
|
|
0x17, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x02, 0xc3, 0xca, 0x26,
|
|
0x22, 0xe4, 0x25, 0xfb, 0x5f, 0x6d, 0xbf, 0x83,
|
|
0x30, 0x48, 0x69, 0x1a, 0x47,
|
|
},
|
|
};
|
|
|
|
FIXTURE(zero_len)
|
|
{
|
|
int fd, cfd;
|
|
bool notls;
|
|
};
|
|
|
|
FIXTURE_VARIANT(zero_len)
|
|
{
|
|
const struct raw_rec *recs[4];
|
|
ssize_t recv_ret[4];
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(zero_len, data_data_data)
|
|
{
|
|
.recs = { &id0_data_l11, &id1_data_l11, &id2_data_l11, },
|
|
.recv_ret = { 33, -EAGAIN, },
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(zero_len, data_0ctrl_data)
|
|
{
|
|
.recs = { &id0_data_l11, &id1_ctrl_l0, &id2_data_l11, },
|
|
.recv_ret = { 11, 0, 11, -EAGAIN, },
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(zero_len, 0data_0data_0data)
|
|
{
|
|
.recs = { &id0_data_l0, &id1_data_l0, &id2_data_l0, },
|
|
.recv_ret = { -EAGAIN, },
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(zero_len, 0data_0data_ctrl)
|
|
{
|
|
.recs = { &id0_data_l0, &id1_data_l0, &id2_ctrl_l11, },
|
|
.recv_ret = { 0, 11, -EAGAIN, },
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(zero_len, 0data_0data_0ctrl)
|
|
{
|
|
.recs = { &id0_data_l0, &id1_data_l0, &id2_ctrl_l0, },
|
|
.recv_ret = { 0, 0, -EAGAIN, },
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(zero_len, 0ctrl_0ctrl_0ctrl)
|
|
{
|
|
.recs = { &id0_ctrl_l0, &id1_ctrl_l0, &id2_ctrl_l0, },
|
|
.recv_ret = { 0, 0, 0, -EAGAIN, },
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(zero_len, 0data_0data_data)
|
|
{
|
|
.recs = { &id0_data_l0, &id1_data_l0, &id2_data_l11, },
|
|
.recv_ret = { 11, -EAGAIN, },
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(zero_len, data_0data_0data)
|
|
{
|
|
.recs = { &id0_data_l11, &id1_data_l0, &id2_data_l0, },
|
|
.recv_ret = { 11, -EAGAIN, },
|
|
};
|
|
|
|
FIXTURE_SETUP(zero_len)
|
|
{
|
|
struct tls_crypto_info_keys tls12;
|
|
int ret;
|
|
|
|
tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_CCM_128,
|
|
&tls12, 0);
|
|
|
|
ulp_sock_pair(_metadata, &self->fd, &self->cfd, &self->notls);
|
|
if (self->notls)
|
|
return;
|
|
|
|
/* Don't install keys on fd, we'll send raw records */
|
|
ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len);
|
|
ASSERT_EQ(ret, 0);
|
|
}
|
|
|
|
FIXTURE_TEARDOWN(zero_len)
|
|
{
|
|
close(self->fd);
|
|
close(self->cfd);
|
|
}
|
|
|
|
TEST_F(zero_len, test)
|
|
{
|
|
const struct raw_rec *const *rec;
|
|
unsigned char buf[128];
|
|
int rec_off;
|
|
int i;
|
|
|
|
for (i = 0; i < 4 && variant->recs[i]; i++)
|
|
EXPECT_EQ(send(self->fd, variant->recs[i]->cipher_data,
|
|
variant->recs[i]->cipher_len, 0),
|
|
variant->recs[i]->cipher_len);
|
|
|
|
rec = &variant->recs[0];
|
|
rec_off = 0;
|
|
for (i = 0; i < 4; i++) {
|
|
int j, ret;
|
|
|
|
ret = variant->recv_ret[i] >= 0 ? variant->recv_ret[i] : -1;
|
|
EXPECT_EQ(__tls_recv_cmsg(_metadata, self->cfd, NULL,
|
|
buf, sizeof(buf), MSG_DONTWAIT), ret);
|
|
if (ret == -1)
|
|
EXPECT_EQ(errno, -variant->recv_ret[i]);
|
|
if (variant->recv_ret[i] == -EAGAIN)
|
|
break;
|
|
|
|
for (j = 0; j < ret; j++) {
|
|
while (rec_off == (*rec)->plain_len) {
|
|
rec++;
|
|
rec_off = 0;
|
|
}
|
|
EXPECT_EQ(buf[j], (*rec)->plain_data[rec_off]);
|
|
rec_off++;
|
|
}
|
|
}
|
|
};
|
|
|
|
FIXTURE(tls_err)
|
|
{
|
|
int fd, cfd;
|
|
int fd2, cfd2;
|
|
bool notls;
|
|
};
|
|
|
|
FIXTURE_VARIANT(tls_err)
|
|
{
|
|
uint16_t tls_version;
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls_err, 12_aes_gcm)
|
|
{
|
|
.tls_version = TLS_1_2_VERSION,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(tls_err, 13_aes_gcm)
|
|
{
|
|
.tls_version = TLS_1_3_VERSION,
|
|
};
|
|
|
|
FIXTURE_SETUP(tls_err)
|
|
{
|
|
struct tls_crypto_info_keys tls12;
|
|
int ret;
|
|
|
|
tls_crypto_info_init(variant->tls_version, TLS_CIPHER_AES_GCM_128,
|
|
&tls12, 0);
|
|
|
|
ulp_sock_pair(_metadata, &self->fd, &self->cfd, &self->notls);
|
|
ulp_sock_pair(_metadata, &self->fd2, &self->cfd2, &self->notls);
|
|
if (self->notls)
|
|
return;
|
|
|
|
ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(self->cfd2, SOL_TLS, TLS_RX, &tls12, tls12.len);
|
|
ASSERT_EQ(ret, 0);
|
|
}
|
|
|
|
FIXTURE_TEARDOWN(tls_err)
|
|
{
|
|
close(self->fd);
|
|
close(self->cfd);
|
|
close(self->fd2);
|
|
close(self->cfd2);
|
|
}
|
|
|
|
TEST_F(tls_err, bad_rec)
|
|
{
|
|
char buf[64];
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
memset(buf, 0x55, sizeof(buf));
|
|
EXPECT_EQ(send(self->fd2, buf, sizeof(buf), 0), sizeof(buf));
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EMSGSIZE);
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), MSG_DONTWAIT), -1);
|
|
EXPECT_EQ(errno, EAGAIN);
|
|
}
|
|
|
|
TEST_F(tls_err, bad_auth)
|
|
{
|
|
char buf[128];
|
|
int n;
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
memrnd(buf, sizeof(buf) / 2);
|
|
EXPECT_EQ(send(self->fd, buf, sizeof(buf) / 2, 0), sizeof(buf) / 2);
|
|
n = recv(self->cfd, buf, sizeof(buf), 0);
|
|
EXPECT_GT(n, sizeof(buf) / 2);
|
|
|
|
buf[n - 1]++;
|
|
|
|
EXPECT_EQ(send(self->fd2, buf, n, 0), n);
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EBADMSG);
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EBADMSG);
|
|
}
|
|
|
|
TEST_F(tls_err, bad_in_large_read)
|
|
{
|
|
char txt[3][64];
|
|
char cip[3][128];
|
|
char buf[3 * 128];
|
|
int i, n;
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
/* Put 3 records in the sockets */
|
|
for (i = 0; i < 3; i++) {
|
|
memrnd(txt[i], sizeof(txt[i]));
|
|
EXPECT_EQ(send(self->fd, txt[i], sizeof(txt[i]), 0),
|
|
sizeof(txt[i]));
|
|
n = recv(self->cfd, cip[i], sizeof(cip[i]), 0);
|
|
EXPECT_GT(n, sizeof(txt[i]));
|
|
/* Break the third message */
|
|
if (i == 2)
|
|
cip[2][n - 1]++;
|
|
EXPECT_EQ(send(self->fd2, cip[i], n, 0), n);
|
|
}
|
|
|
|
/* We should be able to receive the first two messages */
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), sizeof(txt[0]) * 2);
|
|
EXPECT_EQ(memcmp(buf, txt[0], sizeof(txt[0])), 0);
|
|
EXPECT_EQ(memcmp(buf + sizeof(txt[0]), txt[1], sizeof(txt[1])), 0);
|
|
/* Third mesasge is bad */
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EBADMSG);
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EBADMSG);
|
|
}
|
|
|
|
TEST_F(tls_err, bad_cmsg)
|
|
{
|
|
char *test_str = "test_read";
|
|
int send_len = 10;
|
|
char cip[128];
|
|
char buf[128];
|
|
char txt[64];
|
|
int n;
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
/* Queue up one data record */
|
|
memrnd(txt, sizeof(txt));
|
|
EXPECT_EQ(send(self->fd, txt, sizeof(txt), 0), sizeof(txt));
|
|
n = recv(self->cfd, cip, sizeof(cip), 0);
|
|
EXPECT_GT(n, sizeof(txt));
|
|
EXPECT_EQ(send(self->fd2, cip, n, 0), n);
|
|
|
|
EXPECT_EQ(tls_send_cmsg(self->fd, 100, test_str, send_len, 0), 10);
|
|
n = recv(self->cfd, cip, sizeof(cip), 0);
|
|
cip[n - 1]++; /* Break it */
|
|
EXPECT_GT(n, send_len);
|
|
EXPECT_EQ(send(self->fd2, cip, n, 0), n);
|
|
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), sizeof(txt));
|
|
EXPECT_EQ(memcmp(buf, txt, sizeof(txt)), 0);
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EBADMSG);
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EBADMSG);
|
|
}
|
|
|
|
TEST_F(tls_err, timeo)
|
|
{
|
|
struct timeval tv = { .tv_usec = 10000, };
|
|
char buf[128];
|
|
int ret;
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
ret = setsockopt(self->cfd2, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = fork();
|
|
ASSERT_GE(ret, 0);
|
|
|
|
if (ret) {
|
|
usleep(1000); /* Give child a head start */
|
|
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EAGAIN);
|
|
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EAGAIN);
|
|
|
|
wait(&ret);
|
|
} else {
|
|
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
|
|
EXPECT_EQ(errno, EAGAIN);
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
TEST_F(tls_err, poll_partial_rec)
|
|
{
|
|
struct pollfd pfd = { };
|
|
ssize_t rec_len;
|
|
char rec[256];
|
|
char buf[128];
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
pfd.fd = self->cfd2;
|
|
pfd.events = POLLIN;
|
|
EXPECT_EQ(poll(&pfd, 1, 1), 0);
|
|
|
|
memrnd(buf, sizeof(buf));
|
|
EXPECT_EQ(send(self->fd, buf, sizeof(buf), 0), sizeof(buf));
|
|
rec_len = recv(self->cfd, rec, sizeof(rec), 0);
|
|
EXPECT_GT(rec_len, sizeof(buf));
|
|
|
|
/* Write 100B, not the full record ... */
|
|
EXPECT_EQ(send(self->fd2, rec, 100, 0), 100);
|
|
/* ... no full record should mean no POLLIN */
|
|
pfd.fd = self->cfd2;
|
|
pfd.events = POLLIN;
|
|
EXPECT_EQ(poll(&pfd, 1, 1), 0);
|
|
/* Now write the rest, and it should all pop out of the other end. */
|
|
EXPECT_EQ(send(self->fd2, rec + 100, rec_len - 100, 0), rec_len - 100);
|
|
pfd.fd = self->cfd2;
|
|
pfd.events = POLLIN;
|
|
EXPECT_EQ(poll(&pfd, 1, 1), 1);
|
|
EXPECT_EQ(recv(self->cfd2, rec, sizeof(rec), 0), sizeof(buf));
|
|
EXPECT_EQ(memcmp(buf, rec, sizeof(buf)), 0);
|
|
}
|
|
|
|
TEST_F(tls_err, epoll_partial_rec)
|
|
{
|
|
struct epoll_event ev, events[10];
|
|
ssize_t rec_len;
|
|
char rec[256];
|
|
char buf[128];
|
|
int epollfd;
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
epollfd = epoll_create1(0);
|
|
ASSERT_GE(epollfd, 0);
|
|
|
|
memset(&ev, 0, sizeof(ev));
|
|
ev.events = EPOLLIN;
|
|
ev.data.fd = self->cfd2;
|
|
ASSERT_GE(epoll_ctl(epollfd, EPOLL_CTL_ADD, self->cfd2, &ev), 0);
|
|
|
|
EXPECT_EQ(epoll_wait(epollfd, events, 10, 0), 0);
|
|
|
|
memrnd(buf, sizeof(buf));
|
|
EXPECT_EQ(send(self->fd, buf, sizeof(buf), 0), sizeof(buf));
|
|
rec_len = recv(self->cfd, rec, sizeof(rec), 0);
|
|
EXPECT_GT(rec_len, sizeof(buf));
|
|
|
|
/* Write 100B, not the full record ... */
|
|
EXPECT_EQ(send(self->fd2, rec, 100, 0), 100);
|
|
/* ... no full record should mean no POLLIN */
|
|
EXPECT_EQ(epoll_wait(epollfd, events, 10, 0), 0);
|
|
/* Now write the rest, and it should all pop out of the other end. */
|
|
EXPECT_EQ(send(self->fd2, rec + 100, rec_len - 100, 0), rec_len - 100);
|
|
EXPECT_EQ(epoll_wait(epollfd, events, 10, 0), 1);
|
|
EXPECT_EQ(recv(self->cfd2, rec, sizeof(rec), 0), sizeof(buf));
|
|
EXPECT_EQ(memcmp(buf, rec, sizeof(buf)), 0);
|
|
|
|
close(epollfd);
|
|
}
|
|
|
|
TEST_F(tls_err, poll_partial_rec_async)
|
|
{
|
|
struct pollfd pfd = { };
|
|
char token = '\0';
|
|
ssize_t rec_len;
|
|
char rec[256];
|
|
char buf[128];
|
|
int p[2];
|
|
int ret;
|
|
|
|
if (self->notls)
|
|
SKIP(return, "no TLS support");
|
|
|
|
ASSERT_GE(pipe(p), 0);
|
|
|
|
memrnd(buf, sizeof(buf));
|
|
EXPECT_EQ(send(self->fd, buf, sizeof(buf), 0), sizeof(buf));
|
|
rec_len = recv(self->cfd, rec, sizeof(rec), 0);
|
|
EXPECT_GT(rec_len, sizeof(buf));
|
|
|
|
ret = fork();
|
|
ASSERT_GE(ret, 0);
|
|
|
|
if (ret) {
|
|
int status, pid2;
|
|
|
|
close(p[1]);
|
|
usleep(1000); /* Give child a head start */
|
|
|
|
EXPECT_EQ(send(self->fd2, rec, 100, 0), 100);
|
|
|
|
EXPECT_EQ(read(p[0], &token, 1), 1); /* Barrier #1 */
|
|
|
|
EXPECT_EQ(send(self->fd2, rec + 100, rec_len - 100, 0),
|
|
rec_len - 100);
|
|
|
|
pid2 = wait(&status);
|
|
EXPECT_EQ(pid2, ret);
|
|
EXPECT_EQ(status, 0);
|
|
} else {
|
|
close(p[0]);
|
|
|
|
/* Child should sleep in poll(), never get a wake */
|
|
pfd.fd = self->cfd2;
|
|
pfd.events = POLLIN;
|
|
EXPECT_EQ(poll(&pfd, 1, 20), 0);
|
|
|
|
EXPECT_EQ(write(p[1], &token, 1), 1); /* Barrier #1 */
|
|
|
|
pfd.fd = self->cfd2;
|
|
pfd.events = POLLIN;
|
|
EXPECT_EQ(poll(&pfd, 1, 20), 1);
|
|
|
|
exit(!__test_passed(_metadata));
|
|
}
|
|
}
|
|
|
|
/* Use OOB+large send to trigger copy mode due to memory pressure.
|
|
* OOB causes a short read.
|
|
*/
|
|
TEST_F(tls_err, oob_pressure)
|
|
{
|
|
char buf[1<<16];
|
|
int i;
|
|
|
|
memrnd(buf, sizeof(buf));
|
|
|
|
EXPECT_EQ(send(self->fd2, buf, 5, MSG_OOB), 5);
|
|
EXPECT_EQ(send(self->fd2, buf, sizeof(buf), 0), sizeof(buf));
|
|
for (i = 0; i < 64; i++)
|
|
EXPECT_EQ(send(self->fd2, buf, 5, MSG_OOB), 5);
|
|
}
|
|
|
|
/*
|
|
* Parse a stream of TLS records and ensure that each record respects
|
|
* the specified @max_payload_len.
|
|
*/
|
|
static size_t parse_tls_records(struct __test_metadata *_metadata,
|
|
const __u8 *rx_buf, int rx_len, int overhead,
|
|
__u16 max_payload_len)
|
|
{
|
|
const __u8 *rec = rx_buf;
|
|
size_t total_plaintext_rx = 0;
|
|
const __u8 rec_header_len = 5;
|
|
|
|
while (rec < rx_buf + rx_len) {
|
|
__u16 record_payload_len;
|
|
__u16 plaintext_len;
|
|
|
|
/* Sanity check that it's a TLS header for application data */
|
|
ASSERT_EQ(rec[0], 23);
|
|
ASSERT_EQ(rec[1], 0x3);
|
|
ASSERT_EQ(rec[2], 0x3);
|
|
|
|
memcpy(&record_payload_len, rec + 3, 2);
|
|
record_payload_len = ntohs(record_payload_len);
|
|
ASSERT_GE(record_payload_len, overhead);
|
|
|
|
plaintext_len = record_payload_len - overhead;
|
|
total_plaintext_rx += plaintext_len;
|
|
|
|
/* Plaintext must not exceed the specified limit */
|
|
ASSERT_LE(plaintext_len, max_payload_len);
|
|
rec += rec_header_len + record_payload_len;
|
|
}
|
|
|
|
return total_plaintext_rx;
|
|
}
|
|
|
|
TEST(tls_12_tx_max_payload_len)
|
|
{
|
|
struct tls_crypto_info_keys tls12;
|
|
int cfd, ret, fd, overhead;
|
|
size_t total_plaintext_rx = 0;
|
|
__u8 tx[1024], rx[2000];
|
|
__u16 limit = 128;
|
|
__u16 opt = 0;
|
|
unsigned int optlen = sizeof(opt);
|
|
bool notls;
|
|
|
|
tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_CCM_128,
|
|
&tls12, 0);
|
|
|
|
ulp_sock_pair(_metadata, &fd, &cfd, ¬ls);
|
|
|
|
if (notls)
|
|
exit(KSFT_SKIP);
|
|
|
|
/* Don't install keys on fd, we'll parse raw records */
|
|
ret = setsockopt(cfd, SOL_TLS, TLS_TX, &tls12, tls12.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(cfd, SOL_TLS, TLS_TX_MAX_PAYLOAD_LEN, &limit,
|
|
sizeof(limit));
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = getsockopt(cfd, SOL_TLS, TLS_TX_MAX_PAYLOAD_LEN, &opt, &optlen);
|
|
EXPECT_EQ(ret, 0);
|
|
EXPECT_EQ(limit, opt);
|
|
EXPECT_EQ(optlen, sizeof(limit));
|
|
|
|
memset(tx, 0, sizeof(tx));
|
|
ASSERT_EQ(send(cfd, tx, sizeof(tx), 0), sizeof(tx));
|
|
close(cfd);
|
|
|
|
ret = recv(fd, rx, sizeof(rx), 0);
|
|
|
|
/*
|
|
* 16B tag + 8B IV -- record header (5B) is not counted but we'll
|
|
* need it to walk the record stream
|
|
*/
|
|
overhead = 16 + 8;
|
|
total_plaintext_rx = parse_tls_records(_metadata, rx, ret, overhead,
|
|
limit);
|
|
|
|
ASSERT_EQ(total_plaintext_rx, sizeof(tx));
|
|
close(fd);
|
|
}
|
|
|
|
TEST(tls_12_tx_max_payload_len_open_rec)
|
|
{
|
|
struct tls_crypto_info_keys tls12;
|
|
int cfd, ret, fd, overhead;
|
|
size_t total_plaintext_rx = 0;
|
|
__u8 tx[1024], rx[2000];
|
|
__u16 tx_partial = 256;
|
|
__u16 og_limit = 512, limit = 128;
|
|
bool notls;
|
|
|
|
tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_CCM_128,
|
|
&tls12, 0);
|
|
|
|
ulp_sock_pair(_metadata, &fd, &cfd, ¬ls);
|
|
|
|
if (notls)
|
|
exit(KSFT_SKIP);
|
|
|
|
/* Don't install keys on fd, we'll parse raw records */
|
|
ret = setsockopt(cfd, SOL_TLS, TLS_TX, &tls12, tls12.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(cfd, SOL_TLS, TLS_TX_MAX_PAYLOAD_LEN, &og_limit,
|
|
sizeof(og_limit));
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
memset(tx, 0, sizeof(tx));
|
|
ASSERT_EQ(send(cfd, tx, tx_partial, MSG_MORE), tx_partial);
|
|
|
|
/*
|
|
* Changing the payload limit with a pending open record should
|
|
* not be allowed.
|
|
*/
|
|
ret = setsockopt(cfd, SOL_TLS, TLS_TX_MAX_PAYLOAD_LEN, &limit,
|
|
sizeof(limit));
|
|
ASSERT_EQ(ret, -1);
|
|
ASSERT_EQ(errno, EBUSY);
|
|
|
|
ASSERT_EQ(send(cfd, tx + tx_partial, sizeof(tx) - tx_partial, MSG_EOR),
|
|
sizeof(tx) - tx_partial);
|
|
close(cfd);
|
|
|
|
ret = recv(fd, rx, sizeof(rx), 0);
|
|
|
|
/*
|
|
* 16B tag + 8B IV -- record header (5B) is not counted but we'll
|
|
* need it to walk the record stream
|
|
*/
|
|
overhead = 16 + 8;
|
|
total_plaintext_rx = parse_tls_records(_metadata, rx, ret, overhead,
|
|
og_limit);
|
|
ASSERT_EQ(total_plaintext_rx, sizeof(tx));
|
|
close(fd);
|
|
}
|
|
|
|
TEST(non_established) {
|
|
struct tls12_crypto_info_aes_gcm_256 tls12;
|
|
struct sockaddr_in addr;
|
|
int sfd, ret, fd;
|
|
socklen_t len;
|
|
|
|
len = sizeof(addr);
|
|
|
|
memset(&tls12, 0, sizeof(tls12));
|
|
tls12.info.version = TLS_1_2_VERSION;
|
|
tls12.info.cipher_type = TLS_CIPHER_AES_GCM_256;
|
|
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
addr.sin_port = 0;
|
|
|
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
sfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
ret = bind(sfd, &addr, sizeof(addr));
|
|
ASSERT_EQ(ret, 0);
|
|
ret = listen(sfd, 10);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
|
|
EXPECT_EQ(ret, -1);
|
|
/* TLS ULP not supported */
|
|
if (errno == ENOENT)
|
|
return;
|
|
EXPECT_EQ(errno, ENOTCONN);
|
|
|
|
ret = setsockopt(sfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
|
|
EXPECT_EQ(ret, -1);
|
|
EXPECT_EQ(errno, ENOTCONN);
|
|
|
|
ret = getsockname(sfd, &addr, &len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = connect(fd, &addr, sizeof(addr));
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
|
|
EXPECT_EQ(ret, -1);
|
|
EXPECT_EQ(errno, EEXIST);
|
|
|
|
close(fd);
|
|
close(sfd);
|
|
}
|
|
|
|
TEST(keysizes) {
|
|
struct tls12_crypto_info_aes_gcm_256 tls12;
|
|
int ret, fd, cfd;
|
|
bool notls;
|
|
|
|
memset(&tls12, 0, sizeof(tls12));
|
|
tls12.info.version = TLS_1_2_VERSION;
|
|
tls12.info.cipher_type = TLS_CIPHER_AES_GCM_256;
|
|
|
|
ulp_sock_pair(_metadata, &fd, &cfd, ¬ls);
|
|
|
|
if (!notls) {
|
|
ret = setsockopt(fd, SOL_TLS, TLS_TX, &tls12,
|
|
sizeof(tls12));
|
|
EXPECT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(cfd, SOL_TLS, TLS_RX, &tls12,
|
|
sizeof(tls12));
|
|
EXPECT_EQ(ret, 0);
|
|
}
|
|
|
|
close(fd);
|
|
close(cfd);
|
|
}
|
|
|
|
TEST(no_pad) {
|
|
struct tls12_crypto_info_aes_gcm_256 tls12;
|
|
int ret, fd, cfd, val;
|
|
socklen_t len;
|
|
bool notls;
|
|
|
|
memset(&tls12, 0, sizeof(tls12));
|
|
tls12.info.version = TLS_1_3_VERSION;
|
|
tls12.info.cipher_type = TLS_CIPHER_AES_GCM_256;
|
|
|
|
ulp_sock_pair(_metadata, &fd, &cfd, ¬ls);
|
|
|
|
if (notls)
|
|
exit(KSFT_SKIP);
|
|
|
|
ret = setsockopt(fd, SOL_TLS, TLS_TX, &tls12, sizeof(tls12));
|
|
EXPECT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(cfd, SOL_TLS, TLS_RX, &tls12, sizeof(tls12));
|
|
EXPECT_EQ(ret, 0);
|
|
|
|
val = 1;
|
|
ret = setsockopt(cfd, SOL_TLS, TLS_RX_EXPECT_NO_PAD,
|
|
(void *)&val, sizeof(val));
|
|
EXPECT_EQ(ret, 0);
|
|
|
|
len = sizeof(val);
|
|
val = 2;
|
|
ret = getsockopt(cfd, SOL_TLS, TLS_RX_EXPECT_NO_PAD,
|
|
(void *)&val, &len);
|
|
EXPECT_EQ(ret, 0);
|
|
EXPECT_EQ(val, 1);
|
|
EXPECT_EQ(len, 4);
|
|
|
|
val = 0;
|
|
ret = setsockopt(cfd, SOL_TLS, TLS_RX_EXPECT_NO_PAD,
|
|
(void *)&val, sizeof(val));
|
|
EXPECT_EQ(ret, 0);
|
|
|
|
len = sizeof(val);
|
|
val = 2;
|
|
ret = getsockopt(cfd, SOL_TLS, TLS_RX_EXPECT_NO_PAD,
|
|
(void *)&val, &len);
|
|
EXPECT_EQ(ret, 0);
|
|
EXPECT_EQ(val, 0);
|
|
EXPECT_EQ(len, 4);
|
|
|
|
close(fd);
|
|
close(cfd);
|
|
}
|
|
|
|
TEST(tls_v6ops) {
|
|
struct tls_crypto_info_keys tls12;
|
|
struct sockaddr_in6 addr, addr2;
|
|
int sfd, ret, fd;
|
|
socklen_t len, len2;
|
|
|
|
tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_128, &tls12, 0);
|
|
|
|
addr.sin6_family = AF_INET6;
|
|
addr.sin6_addr = in6addr_any;
|
|
addr.sin6_port = 0;
|
|
|
|
fd = socket(AF_INET6, SOCK_STREAM, 0);
|
|
sfd = socket(AF_INET6, SOCK_STREAM, 0);
|
|
|
|
ret = bind(sfd, &addr, sizeof(addr));
|
|
ASSERT_EQ(ret, 0);
|
|
ret = listen(sfd, 10);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
len = sizeof(addr);
|
|
ret = getsockname(sfd, &addr, &len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = connect(fd, &addr, sizeof(addr));
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
len = sizeof(addr);
|
|
ret = getsockname(fd, &addr, &len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
|
|
if (ret) {
|
|
ASSERT_EQ(errno, ENOENT);
|
|
SKIP(return, "no TLS support");
|
|
}
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(fd, SOL_TLS, TLS_TX, &tls12, tls12.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
ret = setsockopt(fd, SOL_TLS, TLS_RX, &tls12, tls12.len);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
len2 = sizeof(addr2);
|
|
ret = getsockname(fd, &addr2, &len2);
|
|
ASSERT_EQ(ret, 0);
|
|
|
|
EXPECT_EQ(len2, len);
|
|
EXPECT_EQ(memcmp(&addr, &addr2, len), 0);
|
|
|
|
close(fd);
|
|
close(sfd);
|
|
}
|
|
|
|
TEST(prequeue) {
|
|
struct tls_crypto_info_keys tls12;
|
|
char buf[20000], buf2[20000];
|
|
struct sockaddr_in addr;
|
|
int sfd, cfd, ret, fd;
|
|
socklen_t len;
|
|
|
|
len = sizeof(addr);
|
|
memrnd(buf, sizeof(buf));
|
|
|
|
tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_256, &tls12, 0);
|
|
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
addr.sin_port = 0;
|
|
|
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
sfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
ASSERT_EQ(bind(sfd, &addr, sizeof(addr)), 0);
|
|
ASSERT_EQ(listen(sfd, 10), 0);
|
|
ASSERT_EQ(getsockname(sfd, &addr, &len), 0);
|
|
ASSERT_EQ(connect(fd, &addr, sizeof(addr)), 0);
|
|
ASSERT_GE(cfd = accept(sfd, &addr, &len), 0);
|
|
close(sfd);
|
|
|
|
ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
|
|
if (ret) {
|
|
ASSERT_EQ(errno, ENOENT);
|
|
SKIP(return, "no TLS support");
|
|
}
|
|
|
|
ASSERT_EQ(setsockopt(fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
|
|
EXPECT_EQ(send(fd, buf, sizeof(buf), MSG_DONTWAIT), sizeof(buf));
|
|
|
|
ASSERT_EQ(setsockopt(cfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls")), 0);
|
|
ASSERT_EQ(setsockopt(cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
|
|
EXPECT_EQ(recv(cfd, buf2, sizeof(buf2), MSG_WAITALL), sizeof(buf2));
|
|
|
|
EXPECT_EQ(memcmp(buf, buf2, sizeof(buf)), 0);
|
|
|
|
close(fd);
|
|
close(cfd);
|
|
}
|
|
|
|
TEST(data_steal) {
|
|
struct tls_crypto_info_keys tls;
|
|
char buf[20000], buf2[20000];
|
|
struct sockaddr_in addr;
|
|
int sfd, cfd, ret, fd;
|
|
int pid, status;
|
|
socklen_t len;
|
|
|
|
len = sizeof(addr);
|
|
memrnd(buf, sizeof(buf));
|
|
|
|
tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_256, &tls, 0);
|
|
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
addr.sin_port = 0;
|
|
|
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
sfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
ASSERT_EQ(bind(sfd, &addr, sizeof(addr)), 0);
|
|
ASSERT_EQ(listen(sfd, 10), 0);
|
|
ASSERT_EQ(getsockname(sfd, &addr, &len), 0);
|
|
ASSERT_EQ(connect(fd, &addr, sizeof(addr)), 0);
|
|
ASSERT_GE(cfd = accept(sfd, &addr, &len), 0);
|
|
close(sfd);
|
|
|
|
ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
|
|
if (ret) {
|
|
ASSERT_EQ(errno, ENOENT);
|
|
SKIP(return, "no TLS support");
|
|
}
|
|
ASSERT_EQ(setsockopt(cfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls")), 0);
|
|
|
|
/* Spawn a child and get it into the read wait path of the underlying
|
|
* TCP socket (before kernel .recvmsg is replaced with the TLS one).
|
|
*/
|
|
pid = fork();
|
|
ASSERT_GE(pid, 0);
|
|
if (!pid) {
|
|
EXPECT_EQ(recv(cfd, buf, sizeof(buf) / 2 + 1, MSG_WAITALL),
|
|
sizeof(buf) / 2 + 1);
|
|
exit(!__test_passed(_metadata));
|
|
}
|
|
|
|
/* Send a sync byte and poll until it's consumed to ensure
|
|
* the child is in recv() before we proceed to install TLS.
|
|
*/
|
|
ASSERT_EQ(send(fd, buf, 1, 0), 1);
|
|
do {
|
|
usleep(500);
|
|
} while (recv(cfd, buf, 1, MSG_PEEK | MSG_DONTWAIT) == 1);
|
|
EXPECT_EQ(errno, EAGAIN);
|
|
|
|
ASSERT_EQ(setsockopt(fd, SOL_TLS, TLS_TX, &tls, tls.len), 0);
|
|
ASSERT_EQ(setsockopt(cfd, SOL_TLS, TLS_RX, &tls, tls.len), 0);
|
|
|
|
EXPECT_EQ(send(fd, buf, sizeof(buf), 0), sizeof(buf));
|
|
EXPECT_EQ(wait(&status), pid);
|
|
EXPECT_EQ(status, 0);
|
|
EXPECT_EQ(recv(cfd, buf2, sizeof(buf2), MSG_DONTWAIT), -1);
|
|
/* Don't check errno, the error will be different depending
|
|
* on what random bytes TLS interpreted as the record length.
|
|
*/
|
|
|
|
close(fd);
|
|
close(cfd);
|
|
}
|
|
|
|
static void __attribute__((constructor)) fips_check(void) {
|
|
int res;
|
|
FILE *f;
|
|
|
|
f = fopen("/proc/sys/crypto/fips_enabled", "r");
|
|
if (f) {
|
|
res = fscanf(f, "%d", &fips_enabled);
|
|
if (res != 1)
|
|
ksft_print_msg("ERROR: Couldn't read /proc/sys/crypto/fips_enabled\n");
|
|
fclose(f);
|
|
}
|
|
}
|
|
|
|
TEST_HARNESS_MAIN
|