xref: /aosp_15_r20/system/core/trusty/libtrusty/trusty.c (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "libtrusty"
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/ioctl.h>
26 #include <sys/socket.h>
27 #include <sys/uio.h>
28 #include <unistd.h>
29 
30 #include <linux/vm_sockets.h> /* must be after sys/socket.h */
31 #include <log/log.h>
32 
33 #include <trusty/ipc.h>
34 
strip_prefix(const char * str,const char * prefix)35 static const char* strip_prefix(const char* str, const char* prefix) {
36     size_t prefix_len = strlen(prefix);
37     if (strncmp(str, prefix, prefix_len) == 0) {
38         return str + prefix_len;
39     } else {
40         return NULL;
41     }
42 }
43 
44 static bool use_vsock_connection = false;
tipc_vsock_connect(const char * type_cid_port_str,const char * srv_name)45 static int tipc_vsock_connect(const char* type_cid_port_str, const char* srv_name) {
46     int ret;
47     const char* cid_port_str;
48     char* port_str;
49     char* end_str;
50     int socket_type;
51     if ((cid_port_str = strip_prefix(type_cid_port_str, "STREAM:"))) {
52         socket_type = SOCK_STREAM;
53     } else if ((cid_port_str = strip_prefix(type_cid_port_str, "SEQPACKET:"))) {
54         socket_type = SOCK_SEQPACKET;
55     } else {
56         /*
57          * Default to SOCK_STREAM if neither type is specified.
58          *
59          * TODO: use SOCK_SEQPACKET by default instead of SOCK_STREAM when SOCK_SEQPACKET is fully
60          * supported since it matches tipc better. At the moment SOCK_SEQPACKET is not supported by
61          * crosvm. It is also significantly slower since the Linux kernel implementation (as of
62          * v6.7-rc1) sends credit update packets every time it receives a data packet while the
63          * SOCK_STREAM version skips these unless the remaining buffer space is "low".
64          */
65         socket_type = SOCK_STREAM;
66         cid_port_str = type_cid_port_str;
67     }
68     long cid = strtol(cid_port_str, &port_str, 0);
69     if (port_str[0] != ':') {
70         ALOGE("%s: invalid VSOCK str, \"%s\", need cid:port missing : after cid\n", __func__,
71               cid_port_str);
72         return -EINVAL;
73     }
74     long port = strtol(port_str + 1, &end_str, 0);
75     if (end_str[0] != '\0') {
76         ALOGE("%s: invalid VSOCK str, \"%s\", need cid:port got %ld:%ld\n", __func__, cid_port_str,
77               cid, port);
78         return -EINVAL;
79     }
80     int fd = socket(AF_VSOCK, socket_type, 0);
81     if (fd < 0) {
82         ret = -errno;
83         ALOGE("%s: can't get vsock %ld:%ld socket for tipc service \"%s\" (err=%d)\n", __func__,
84               cid, port, srv_name, errno);
85         return ret < 0 ? ret : -1;
86     }
87     struct timeval connect_timeout = {.tv_sec = 60, .tv_usec = 0};
88     ret = setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_CONNECT_TIMEOUT, &connect_timeout,
89                      sizeof(connect_timeout));
90     if (ret) {
91         ALOGE("%s: vsock %ld:%ld: Failed to set connect timeout (err=%d)\n", __func__, cid, port,
92               errno);
93         /* failed to set longer timeout, but try to connect anyway */
94     }
95     struct sockaddr_vm sa = {
96             .svm_family = AF_VSOCK,
97             .svm_port = port,
98             .svm_cid = cid,
99     };
100     int retry = 10;
101     do {
102         ret = TEMP_FAILURE_RETRY(connect(fd, (struct sockaddr*)&sa, sizeof(sa)));
103         if (ret && (errno == ENODEV || errno == ESOCKTNOSUPPORT) && --retry) {
104             /*
105              * The kernel returns ESOCKTNOSUPPORT instead of ENODEV if the socket type is
106              * SOCK_SEQPACKET and the guest CID we are trying to connect to is not ready yet.
107              */
108             ALOGE("%s: Can't connect to vsock %ld:%ld for tipc service \"%s\" (err=%d) %d retries "
109                   "remaining\n",
110                   __func__, cid, port, srv_name, errno, retry);
111             sleep(1);
112         } else {
113             retry = 0;
114         }
115     } while (retry);
116     if (ret) {
117         ret = -errno;
118         ALOGE("%s: Can't connect to vsock %ld:%ld for tipc service \"%s\" (err=%d)\n", __func__,
119               cid, port, srv_name, errno);
120         close(fd);
121         return ret < 0 ? ret : -1;
122     }
123     /*
124      * TODO: Current vsock tipc bridge in trusty expects a port name in the
125      * first packet. We need to replace this with a protocol that also does DICE
126      * based authentication.
127      */
128     ret = TEMP_FAILURE_RETRY(write(fd, srv_name, strlen(srv_name)));
129     if (ret != strlen(srv_name)) {
130         ret = -errno;
131         ALOGE("%s: vsock %ld:%ld: failed to send tipc service name \"%s\" (err=%d)\n", __func__,
132               cid, port, srv_name, errno);
133         close(fd);
134         return ret < 0 ? ret : -1;
135     }
136     /*
137      * Work around lack of seq packet support. Read a status byte to prevent
138      * the caller from sending more data until srv_name has been read.
139      */
140     int8_t status;
141     ret = TEMP_FAILURE_RETRY(read(fd, &status, sizeof(status)));
142     if (ret != sizeof(status)) {
143         ALOGE("%s: vsock %ld:%ld: failed to read status byte for connect to tipc service name "
144               "\"%s\" (err=%d)\n",
145               __func__, cid, port, srv_name, errno);
146         close(fd);
147         return ret < 0 ? ret : -1;
148     }
149     use_vsock_connection = true;
150     return fd;
151 }
152 
tipc_vsock_send(int fd,const struct iovec * iov,int iovcnt,struct trusty_shm * shms,int shmcnt)153 static size_t tipc_vsock_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shms,
154                               int shmcnt) {
155     int ret;
156 
157     (void)shms;
158     if (shmcnt != 0) {
159         ALOGE("%s: vsock does not yet support passing fds\n", __func__);
160         return -ENOTSUP;
161     }
162     ret = TEMP_FAILURE_RETRY(writev(fd, iov, iovcnt));
163     if (ret < 0) {
164         ret = -errno;
165         ALOGE("%s: failed to send message (err=%d)\n", __func__, errno);
166         return ret < 0 ? ret : -1;
167     }
168 
169     return ret;
170 }
171 
tipc_connect(const char * dev_name,const char * srv_name)172 int tipc_connect(const char* dev_name, const char* srv_name) {
173     int fd;
174     int rc;
175 
176     const char* type_cid_port_str = strip_prefix(dev_name, "VSOCK:");
177     if (type_cid_port_str) {
178         return tipc_vsock_connect(type_cid_port_str, srv_name);
179     }
180 
181     fd = TEMP_FAILURE_RETRY(open(dev_name, O_RDWR));
182     if (fd < 0) {
183         rc = -errno;
184         ALOGE("%s: cannot open tipc device \"%s\": %s\n", __func__, dev_name, strerror(errno));
185         return rc < 0 ? rc : -1;
186     }
187 
188     rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_CONNECT, srv_name));
189     if (rc < 0) {
190         rc = -errno;
191         ALOGE("%s: can't connect to tipc service \"%s\" (err=%d)\n", __func__, srv_name, errno);
192         close(fd);
193         return rc < 0 ? rc : -1;
194     }
195 
196     ALOGV("%s: connected to \"%s\" fd %d\n", __func__, srv_name, fd);
197     return fd;
198 }
199 
tipc_send(int fd,const struct iovec * iov,int iovcnt,struct trusty_shm * shms,int shmcnt)200 ssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shms,
201                   int shmcnt) {
202     if (use_vsock_connection) {
203         return tipc_vsock_send(fd, iov, iovcnt, shms, shmcnt);
204     }
205     struct tipc_send_msg_req req;
206     req.iov = (__u64)iov;
207     req.iov_cnt = (__u64)iovcnt;
208     req.shm = (__u64)shms;
209     req.shm_cnt = (__u64)shmcnt;
210 
211     int rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_SEND_MSG, &req));
212     if (rc < 0) {
213         ALOGE("%s: failed to send message (err=%d)\n", __func__, rc);
214     }
215 
216     return rc;
217 }
218 
tipc_close(int fd)219 void tipc_close(int fd) {
220     close(fd);
221 }
222