blob: b9ae6138aa0955ed61700f8bbcd6ad8993688130 [file] [log] [blame]
/*
* Copyright (c) 2016-2017, Linaro Limited
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <netdb.h>
#include <netinet/in.h>
#include <optee_msg_supplicant.h>
#include <poll.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <tee_client_api.h>
#include <teec_trace.h>
#include <tee_socket.h>
#include <tee_supplicant.h>
#include <unistd.h>
#include "handle.h"
#include "__tee_isocket_defines.h"
#include "__tee_ipsocket.h"
#include "__tee_tcpsocket_defines.h"
#include "__tee_tcpsocket_defines_extensions.h"
#include "__tee_udpsocket_defines.h"
#ifndef __aligned
#define __aligned(x) __attribute__((__aligned__(x)))
#endif
#include <linux/tee.h>
/*
* Used when checking how much data we have queued.
*
* For SOCK_DGRAM we try to be accurate up to 4096 bytes as
* that's our arbitrary chosen sensible upper size (with
* some margin). Larger size doesn't make much sense since
* anything larger than the MTU is bound to cause trouble
* on a congested network.
*
* For SOCK_STREAM we chose the same upper limit for
* simplicity. It doesn't matter if there's more queued,
* no data will be lost.
*/
#define SUPP_MAX_PEEK_LEN 4096
struct sock_instance {
uint32_t id;
struct handle_db db;
TAILQ_ENTRY(sock_instance) link;
};
static pthread_mutex_t sock_mutex = PTHREAD_MUTEX_INITIALIZER;
TAILQ_HEAD(, sock_instance) sock_instances =
TAILQ_HEAD_INITIALIZER(sock_instances);
static void sock_lock(void)
{
pthread_mutex_lock(&sock_mutex);
}
static void sock_unlock(void)
{
pthread_mutex_unlock(&sock_mutex);
}
static struct sock_instance *sock_instance_find(uint32_t instance_id)
{
struct sock_instance *si = NULL;
TAILQ_FOREACH(si, &sock_instances, link) {
if (si->id == instance_id)
return si;
}
return NULL;
}
static void *fd_to_handle_ptr(int fd)
{
uintptr_t ptr = 0;
assert(fd >= 0);
ptr = fd + 1;
return (void *)ptr;
}
static int handle_ptr_to_fd(void *ptr)
{
assert(ptr);
return (uintptr_t)ptr - 1;
}
static int sock_handle_get(uint32_t instance_id, int fd)
{
int handle = -1;
struct sock_instance *si = NULL;
sock_lock();
si = sock_instance_find(instance_id);
if (!si) {
si = calloc(1, sizeof(*si));
if (!si)
goto out;
si->id = instance_id;
TAILQ_INSERT_TAIL(&sock_instances, si, link);
}
handle = handle_get(&si->db, fd_to_handle_ptr(fd));
out:
sock_unlock();
return handle;
}
static int sock_handle_to_fd(uint32_t instance_id, uint32_t handle)
{
int fd = -1;
struct sock_instance *si = NULL;
sock_lock();
si = sock_instance_find(instance_id);
if (si)
fd = handle_ptr_to_fd(handle_lookup(&si->db, handle));
sock_unlock();
return fd;
}
static void sock_handle_put(uint32_t instance_id, uint32_t handle)
{
struct sock_instance *si = NULL;
sock_lock();
si = sock_instance_find(instance_id);
if (si)
handle_put(&si->db, handle);
sock_unlock();
}
static bool chk_pt(struct tee_ioctl_param *param, uint32_t type)
{
return (param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) == type;
}
static int fd_flags_add(int fd, int flags)
{
int val = 0;
val = fcntl(fd, F_GETFD, 0);
if (val == -1)
return -1;
val |= flags;
return fcntl(fd, F_SETFL, val);
}
static TEEC_Result sock_connect(uint32_t ip_vers, unsigned int protocol,
const char *server, uint16_t port, int *ret_fd)
{
TEEC_Result r = TEEC_ERROR_GENERIC;
struct addrinfo *res0 = NULL;
struct addrinfo *res = NULL;
int fd = -1;
char port_name[10] = { 0 };
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
snprintf(port_name, sizeof(port_name), "%" PRIu16, port);
switch (ip_vers) {
case TEE_IP_VERSION_DC:
hints.ai_family = AF_UNSPEC;
break;
case TEE_IP_VERSION_4:
hints.ai_family = AF_INET;
break;
case TEE_IP_VERSION_6:
hints.ai_family = AF_INET6;
break;
default:
return TEEC_ERROR_BAD_PARAMETERS;
}
if (protocol == TEE_ISOCKET_PROTOCOLID_TCP)
hints.ai_socktype = SOCK_STREAM;
else if (protocol == TEE_ISOCKET_PROTOCOLID_UDP)
hints.ai_socktype = SOCK_DGRAM;
else
return TEEC_ERROR_BAD_PARAMETERS;
if (getaddrinfo(server, port_name, &hints, &res0))
return TEE_ISOCKET_ERROR_HOSTNAME;
for (res = res0; res; res = res->ai_next) {
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (fd == -1) {
if (errno == ENOMEM || errno == ENOBUFS)
r = TEE_ISOCKET_ERROR_OUT_OF_RESOURCES;
else
r = TEEC_ERROR_GENERIC;
continue;
}
if (connect(fd, res->ai_addr, res->ai_addrlen)) {
if (errno == ETIMEDOUT)
r = TEE_ISOCKET_ERROR_TIMEOUT;
else
r = TEEC_ERROR_COMMUNICATION;
close(fd);
fd = -1;
continue;
}
if (fd_flags_add(fd, O_NONBLOCK)) {
close(fd);
fd = -1;
r = TEEC_ERROR_GENERIC;
break;
}
r = TEEC_SUCCESS;
break;
}
freeaddrinfo(res0);
*ret_fd = fd;
return r;
}
static TEEC_Result tee_socket_open(size_t num_params,
struct tee_ioctl_param *params)
{
TEEC_Result res = TEEC_ERROR_GENERIC;
int handle = 0;
int fd = 0;
uint32_t instance_id = 0;
char *server = NULL;
uint32_t ip_vers = 0;
uint16_t port = 0;
uint32_t protocol = 0;
if (num_params != 4 ||
!chk_pt(params + 0, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT) ||
!chk_pt(params + 1, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT) ||
!chk_pt(params + 2, TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT) ||
!chk_pt(params + 3, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT))
return TEEC_ERROR_BAD_PARAMETERS;
instance_id = params[0].b;
port = params[1].a;
protocol = params[1].b;
ip_vers = params[1].c;
server = tee_supp_param_to_va(params + 2);
if (!server || server[MEMREF_SIZE(params + 2) - 1] != '\0')
return TEE_ISOCKET_ERROR_HOSTNAME;
res = sock_connect(ip_vers, protocol, server, port, &fd);
if (res != TEEC_SUCCESS)
return res;
handle = sock_handle_get(instance_id, fd);
if (handle < 0) {
close(fd);
return TEEC_ERROR_OUT_OF_MEMORY;
}
params[3].a = handle;
return TEEC_SUCCESS;
}
static TEEC_Result tee_socket_close(size_t num_params,
struct tee_ioctl_param *params)
{
int handle = 0;
uint32_t instance_id = 0;
int fd = 0;
if (num_params != 1 ||
!chk_pt(params + 0, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT))
return TEEC_ERROR_BAD_PARAMETERS;
instance_id = params[0].b;
handle = params[0].c;
fd = sock_handle_to_fd(instance_id, handle);
if (fd < 0)
return TEEC_ERROR_BAD_PARAMETERS;
sock_handle_put(instance_id, handle);
if (close(fd)) {
EMSG("tee_socket_close: close(%d): %s", fd, strerror(errno));
return TEEC_ERROR_GENERIC;
}
return TEEC_SUCCESS;
}
static void sock_close_cb(int handle, void *ptr, void *arg)
{
struct sock_instance *si = arg;
int fd = handle_ptr_to_fd(ptr);
if (close(fd))
EMSG("sock_close_cb instance_id %d handle %d fd %d: %s",
si->id, handle, fd, strerror(errno));
}
static TEEC_Result tee_socket_close_all(size_t num_params,
struct tee_ioctl_param *params)
{
uint32_t instance_id = 0;
struct sock_instance *si = NULL;
if (num_params != 1 ||
!chk_pt(params + 0, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT))
return TEEC_ERROR_BAD_PARAMETERS;
instance_id = params[0].b;
sock_lock();
si = sock_instance_find(instance_id);
if (si)
handle_foreach_put(&si->db, sock_close_cb, si);
sock_unlock();
return TEEC_SUCCESS;
}
#define TS_NSEC_PER_SEC 1000000000
static void ts_add(const struct timespec *a, const struct timespec *b,
struct timespec *res)
{
res->tv_sec = a->tv_sec + b->tv_sec;
res->tv_nsec = a->tv_nsec + b->tv_nsec;
if (res->tv_nsec >= TS_NSEC_PER_SEC) {
res->tv_sec++;
res->tv_nsec -= TS_NSEC_PER_SEC;
}
}
static int ts_diff_to_polltimeout(const struct timespec *a,
const struct timespec *b)
{
struct timespec diff;
memset(&diff, 0, sizeof(diff));
diff.tv_sec = a->tv_sec - b->tv_sec;
diff.tv_nsec = a->tv_nsec - b->tv_nsec;
if (a->tv_nsec < b->tv_nsec) {
diff.tv_nsec += TS_NSEC_PER_SEC;
diff.tv_sec--;
}
if ((diff.tv_sec - 1) > (INT_MAX / 1000))
return INT_MAX;
return diff.tv_sec * 1000 + diff.tv_nsec / (TS_NSEC_PER_SEC / 1000);
}
static void ts_delay_from_millis(uint32_t millis, struct timespec *res)
{
res->tv_sec = millis / 1000;
res->tv_nsec = (millis % 1000) * (TS_NSEC_PER_SEC / 1000);
}
static TEEC_Result poll_with_timeout(struct pollfd *pfd, nfds_t nfds,
uint32_t timeout)
{
int to = 0;
int r = 0;
struct timespec now;
struct timespec until;
memset(&now, 0, sizeof(now));
memset(&until, 0, sizeof(until));
if (timeout == OPTEE_MRC_SOCKET_TIMEOUT_BLOCKING) {
to = -1;
} else {
struct timespec delay;
memset(&delay, 0, sizeof(delay));
ts_delay_from_millis(timeout, &delay);
if (clock_gettime(CLOCK_REALTIME, &now))
return TEEC_ERROR_GENERIC;
ts_add(&now, &delay, &until);
}
while (true) {
if (to != -1)
to = ts_diff_to_polltimeout(&until, &now);
r = poll(pfd, nfds, to);
if (!r)
return TEE_ISOCKET_ERROR_TIMEOUT;
if (r == -1) {
/*
* If we're interrupted by a signal treat
* recalculate the timeout (if needed) and wait
* again.
*/
if (errno == EINTR) {
if (to != -1 &&
clock_gettime(CLOCK_REALTIME, &now))
return TEEC_ERROR_GENERIC;
continue;
}
return TEEC_ERROR_BAD_PARAMETERS;
}
return TEEC_SUCCESS;
}
}
static TEEC_Result write_with_timeout(int fd, const void *buf, size_t *blen,
uint32_t timeout)
{
TEEC_Result res = TEEC_ERROR_GENERIC;
struct pollfd pfd = { .fd = fd, .events = POLLOUT };
ssize_t r = 0;
res = poll_with_timeout(&pfd, 1, timeout);
if (res != TEEC_SUCCESS)
return res;
r = write(fd, buf, *blen);
if (r == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
return TEE_ISOCKET_ERROR_TIMEOUT;
return TEEC_ERROR_BAD_PARAMETERS;
}
*blen = r;
return TEEC_SUCCESS;
}
static TEEC_Result tee_socket_send(size_t num_params,
struct tee_ioctl_param *params)
{
TEEC_Result res = TEEC_ERROR_GENERIC;
int handle = 0;
int fd = 0;
uint32_t instance_id = 0;
void *buf = NULL;
size_t bytes = 0;
if (num_params != 3 ||
!chk_pt(params + 0, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT) ||
!chk_pt(params + 1, TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT) ||
!chk_pt(params + 2, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT))
return TEEC_ERROR_BAD_PARAMETERS;
instance_id = params[0].b;
handle = params[0].c;
fd = sock_handle_to_fd(instance_id, handle);
if (fd < 0)
return TEEC_ERROR_BAD_PARAMETERS;
buf = tee_supp_param_to_va(params + 1);
bytes = MEMREF_SIZE(params + 1);
res = write_with_timeout(fd, buf, &bytes, params[2].a);
if (res == TEEC_SUCCESS)
params[2].b = bytes;
return res;
}
static ssize_t recv_with_out_flags(int fd, void *buf, size_t len, int inflags,
int *out_flags)
{
ssize_t r = 0;
while (true) {
struct iovec iov = { .iov_base = buf, .iov_len = len, };
struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, };
r = recvmsg(fd, &msg, inflags);
if (r < 0) {
/*
* If the syscall was just interrupted by a signal
* delivery, try again.
*/
if (errno == EINTR)
continue;
return r;
}
*out_flags = msg.msg_flags;
return r;
}
}
static TEEC_Result read_with_timeout(int fd, void *buf, size_t *blen,
uint32_t timeout)
{
TEEC_Result res = TEEC_ERROR_GENERIC;
struct pollfd pfd = { .fd = fd, .events = POLLIN };
int socktype = 0;
socklen_t l = sizeof(socktype);
size_t peek_len = 0;
int out_flags = 0;
ssize_t r = 0;
int e = 0;
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &socktype, &l))
return TEEC_ERROR_BAD_PARAMETERS;
if (*blen) {
/* If *blen == 0, the timeout parameter has no effect. */
res = poll_with_timeout(&pfd, 1, timeout);
if (res != TEEC_SUCCESS)
return res;
}
if ((socktype == SOCK_DGRAM && *blen < SUPP_MAX_PEEK_LEN) || !*blen) {
/* Check how much data we have queued. */
void *b = malloc(SUPP_MAX_PEEK_LEN);
if (!b)
return TEEC_ERROR_OUT_OF_MEMORY;
r = recv_with_out_flags(fd, b, SUPP_MAX_PEEK_LEN,
MSG_PEEK | MSG_DONTWAIT, &out_flags);
e = errno;
free(b);
if (r < 0)
goto err;
/*
* If the message was truncated we know that it's at least
* one byte larger.
*/
if (out_flags & MSG_TRUNC)
r++;
if (!*blen) {
*blen = r;
return TEEC_SUCCESS;
}
peek_len = r;
}
r = recv_with_out_flags(fd, buf, *blen, MSG_DONTWAIT, &out_flags);
if (r == -1) {
e = errno;
goto err;
}
if (socktype == SOCK_DGRAM && (out_flags & MSG_TRUNC)) {
/*
* The datagram has been truncated, return the best length
* we have to indicate that.
*/
if (peek_len > (size_t)r)
*blen = peek_len;
else
*blen = r + 1;
} else {
*blen = r;
}
return TEEC_SUCCESS;
err:
if (e == EAGAIN || e == EWOULDBLOCK) {
/*
* If *blen is supplied as 0 then we're not supposed wait
* for data so the call to poll has been skipped. In case
* there is no data available recvmsg() will return an
* error with errno set to EAGAIN or EWOULDBLOCK.
*/
if (!*blen)
return TEEC_SUCCESS;
return TEE_ISOCKET_ERROR_TIMEOUT;
}
return TEEC_ERROR_BAD_PARAMETERS;
}
static TEEC_Result tee_socket_recv(size_t num_params,
struct tee_ioctl_param *params)
{
TEEC_Result res = TEEC_ERROR_GENERIC;
int handle = 0;
int fd = 0;
uint32_t instance_id = 0;
void *buf = NULL;
size_t bytes = 0;
if (num_params != 3 ||
!chk_pt(params + 0, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT) ||
!chk_pt(params + 1, TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT) ||
!chk_pt(params + 2, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT))
return TEEC_ERROR_BAD_PARAMETERS;
instance_id = params[0].b;
handle = params[0].c;
fd = sock_handle_to_fd(instance_id, handle);
if (fd < 0)
return TEEC_ERROR_BAD_PARAMETERS;
buf = tee_supp_param_to_va(params + 1);
bytes = MEMREF_SIZE(params + 1);
res = read_with_timeout(fd, buf, &bytes, params[2].a);
if (res == TEEC_SUCCESS)
MEMREF_SIZE(params + 1) = bytes;
return res;
}
static TEEC_Result tee_socket_ioctl_tcp(int fd, uint32_t command,
void *buf, size_t *blen)
{
switch (command) {
case TEE_TCP_SET_RECVBUF:
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, buf, *blen))
return TEEC_ERROR_BAD_PARAMETERS;
return TEEC_SUCCESS;
case TEE_TCP_SET_SENDBUF:
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, buf, *blen))
return TEEC_ERROR_BAD_PARAMETERS;
return TEEC_SUCCESS;
default:
return TEEC_ERROR_NOT_SUPPORTED;
}
}
static TEEC_Result sa_set_port(struct sockaddr *sa, socklen_t slen,
uint16_t port)
{
if (sa->sa_family == AF_INET) {
struct sockaddr_in *sain = (void *)sa;
if (slen < (socklen_t)sizeof(*sain))
return TEEC_ERROR_BAD_PARAMETERS;
sain->sin_port = htons(port);
return TEEC_SUCCESS;
}
if (sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sain6 = (void *)sa;
if (slen < (socklen_t)sizeof(*sain6))
return TEEC_ERROR_BAD_PARAMETERS;
sain6->sin6_port = htons(port);
return TEEC_SUCCESS;
}
return TEEC_ERROR_BAD_PARAMETERS;
}
static TEEC_Result sa_get_port(struct sockaddr *sa, socklen_t slen,
uint16_t *port)
{
if (sa->sa_family == AF_INET) {
struct sockaddr_in *sain = (void *)sa;
if (slen < (socklen_t)sizeof(*sain))
return TEEC_ERROR_BAD_PARAMETERS;
*port = ntohs(sain->sin_port);
return TEEC_SUCCESS;
}
if (sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sain6 = (void *)sa;
if (slen < (socklen_t)sizeof(*sain6))
return TEEC_ERROR_BAD_PARAMETERS;
*port = ntohs(sain6->sin6_port);
return TEEC_SUCCESS;
}
return TEEC_ERROR_BAD_PARAMETERS;
}
static TEEC_Result udp_changeaddr(int fd, int family, const char *server,
uint16_t port)
{
TEEC_Result r = TEE_ISOCKET_ERROR_HOSTNAME;
struct addrinfo *res0 = NULL;
struct addrinfo *res = NULL;
char port_name[10] = { 0 };
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
snprintf(port_name, sizeof(port_name), "%" PRIu16, port);
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM;
if (getaddrinfo(server, port_name, &hints, &res0))
return TEE_ISOCKET_ERROR_HOSTNAME;
for (res = res0; res; res = res->ai_next) {
if (connect(fd, res->ai_addr, res->ai_addrlen)) {
if (errno == ETIMEDOUT)
r = TEE_ISOCKET_ERROR_TIMEOUT;
else
r = TEEC_ERROR_COMMUNICATION;
continue;
}
r = TEEC_SUCCESS;
break;
}
freeaddrinfo(res0);
return r;
}
static TEEC_Result tee_socket_ioctl_udp(int fd, uint32_t command,
void *buf, size_t *blen)
{
TEEC_Result res = TEEC_ERROR_GENERIC;
uint16_t port = 0;
struct sockaddr_storage sass;
struct sockaddr *sa = (struct sockaddr *)&sass;
socklen_t len = sizeof(sass);
memset(&sass, 0, sizeof(sass));
if (getpeername(fd, sa, &len))
return TEEC_ERROR_BAD_PARAMETERS;
switch (command) {
case TEE_UDP_CHANGEADDR:
res = sa_get_port(sa, len, &port);
if (res != TEEC_SUCCESS)
return res;
if (!blen || *((char *)buf + *blen - 1) != '\0')
return TEE_ISOCKET_ERROR_HOSTNAME;
return udp_changeaddr(fd, sa->sa_family, buf, port);
case TEE_UDP_CHANGEPORT:
if (*blen != sizeof(port))
return TEEC_ERROR_BAD_PARAMETERS;
memcpy(&port, buf, sizeof(port));
res = sa_set_port(sa, len, port);
if (res != TEEC_SUCCESS)
return res;
if (connect(fd, sa, len))
return TEEC_ERROR_GENERIC;
return TEEC_SUCCESS;
default:
return TEEC_ERROR_NOT_SUPPORTED;
}
}
static TEEC_Result tee_socket_ioctl(size_t num_params,
struct tee_ioctl_param *params)
{
TEEC_Result res = TEEC_ERROR_GENERIC;
int handle = 0;
int fd = 0;
uint32_t instance_id = 0;
uint32_t command = 0;
void *buf = NULL;
int socktype = 0;
socklen_t l = 0;
size_t sz = 0;
if (num_params != 3 ||
!chk_pt(params + 0, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT) ||
!chk_pt(params + 1, TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT) ||
!chk_pt(params + 2, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT))
return TEEC_ERROR_BAD_PARAMETERS;
instance_id = params[0].b;
handle = params[0].c;
command = params[2].a;
fd = sock_handle_to_fd(instance_id, handle);
if (fd < 0)
return TEEC_ERROR_BAD_PARAMETERS;
l = sizeof(socktype);
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &socktype, &l))
return TEEC_ERROR_BAD_PARAMETERS;
buf = tee_supp_param_to_va(params + 1);
switch (socktype) {
case SOCK_STREAM:
sz = MEMREF_SIZE(params + 1);
res = tee_socket_ioctl_tcp(fd, command, buf, &sz);
MEMREF_SIZE(params + 1) = sz;
return res;
case SOCK_DGRAM:
sz = MEMREF_SIZE(params + 1);
res = tee_socket_ioctl_udp(fd, command, buf, &sz);
MEMREF_SIZE(params + 1) = sz;
return res;
default:
return TEEC_ERROR_BAD_PARAMETERS;
}
}
TEEC_Result tee_socket_process(size_t num_params,
struct tee_ioctl_param *params)
{
if (!num_params || !tee_supp_param_is_value(params))
return TEEC_ERROR_BAD_PARAMETERS;
switch (params->a) {
case OPTEE_MRC_SOCKET_OPEN:
return tee_socket_open(num_params, params);
case OPTEE_MRC_SOCKET_CLOSE:
return tee_socket_close(num_params, params);
case OPTEE_MRC_SOCKET_CLOSE_ALL:
return tee_socket_close_all(num_params, params);
case OPTEE_MRC_SOCKET_SEND:
return tee_socket_send(num_params, params);
case OPTEE_MRC_SOCKET_RECV:
return tee_socket_recv(num_params, params);
case OPTEE_MRC_SOCKET_IOCTL:
return tee_socket_ioctl(num_params, params);
default:
return TEEC_ERROR_BAD_PARAMETERS;
}
}