-
Notifications
You must be signed in to change notification settings - Fork 202
/
pal_streams.c
377 lines (332 loc) · 12.6 KB
/
pal_streams.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/* SPDX-License-Identifier: LGPL-3.0-or-later */
/* Copyright (C) 2014 Stony Brook University */
/*
* This file contains APIs to open, read, write and get attribute of streams.
*/
#include <asm/fcntl.h>
#include <asm/stat.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/types.h>
#include <stdalign.h>
#include <stdbool.h>
#include "api.h"
#include "asan.h"
#include "crypto.h"
#include "linux_socket.h"
#include "pal.h"
#include "pal_error.h"
#include "pal_internal.h"
#include "pal_linux.h"
#include "pal_linux_error.h"
#include "perm.h"
#define DUMMYPAYLOAD "dummypayload"
#define DUMMYPAYLOADSIZE (sizeof(DUMMYPAYLOAD))
static int g_log_fd = PAL_LOG_DEFAULT_FD;
struct hdl_header {
bool has_fd; /* true if PAL handle has a corresponding host file descriptor */
size_t data_size; /* total size of serialized PAL handle */
};
static ssize_t handle_serialize(PAL_HANDLE handle, void** data) {
int ret;
const void* field = NULL;
size_t field_size = 0;
bool free_field = false;
/* find a field to serialize (depends on the handle type); note that
* no handle type has more than one such field, and some have none */
switch (handle->hdr.type) {
case PAL_TYPE_PIPE:
case PAL_TYPE_PIPECLI:
/* session key is part of handle but need to serialize SSL context */
if (handle->pipe.ssl_ctx) {
free_field = true;
ret = _PalStreamSecureSave(handle->pipe.ssl_ctx, (const uint8_t**)&field,
&field_size);
if (ret < 0)
return PAL_ERROR_DENIED;
}
/* no need to serialize handshake_helper_thread_hdl */
break;
case PAL_TYPE_PIPESRV:
/* no need to serialize ssl_ctx and handshake_helper_thread_hdl */
break;
case PAL_TYPE_CONSOLE:
/* console (stdin/stdout/stderr) has no fields to serialize */
break;
case PAL_TYPE_DEV:
field = handle->dev.realpath;
field_size = strlen(handle->dev.realpath) + 1;
break;
case PAL_TYPE_FILE:
field = handle->file.realpath;
field_size = strlen(handle->file.realpath) + 1;
/* no need to serialize chunk_hashes & umem */
break;
case PAL_TYPE_DIR:
field = handle->dir.realpath;
field_size = strlen(handle->dir.realpath) + 1;
/* no need to serialize buf/ptr/end */
break;
case PAL_TYPE_SOCKET:
/* sock.ops field will be fixed in deserialize */
break;
case PAL_TYPE_PROCESS:
/* session key is part of handle but need to serialize SSL context */
if (handle->process.ssl_ctx) {
free_field = true;
ret = _PalStreamSecureSave(handle->process.ssl_ctx, (const uint8_t**)&field,
&field_size);
if (ret < 0)
return PAL_ERROR_DENIED;
}
break;
case PAL_TYPE_EVENTFD:
/* eventfds have no fields to serialize */
break;
default:
return PAL_ERROR_INVAL;
}
size_t hdl_size = handle_size(handle);
size_t buffer_size = hdl_size + field_size;
void* buffer = malloc(buffer_size);
if (!buffer) {
ret = PAL_ERROR_NOMEM;
goto out;
}
/* copy into buffer all handle fields and then serialized fields */
memcpy(buffer, handle, hdl_size);
if (field_size)
memcpy(buffer + hdl_size, field, field_size);
*data = buffer;
ret = buffer_size;
out:
if (free_field)
free((void*)field);
return ret;
}
static int handle_deserialize(PAL_HANDLE* handle, const void* data, size_t size, int host_fd) {
int ret;
size_t hdl_size = handle_size((PAL_HANDLE)data);
PAL_HANDLE hdl = malloc(hdl_size);
if (!hdl)
return PAL_ERROR_NOMEM;
memcpy(hdl, data, hdl_size);
/* update handle fields to point to correct contents */
assert(hdl_size <= size);
switch (hdl->hdr.type) {
case PAL_TYPE_PIPE:
case PAL_TYPE_PIPECLI:
/* session key is part of handle but need to deserialize SSL context */
hdl->pipe.fd = host_fd; /* correct host FD must be passed to SSL context */
ret = _PalStreamSecureInit(hdl, hdl->pipe.is_server, &hdl->pipe.session_key,
(LIB_SSL_CONTEXT**)&hdl->pipe.ssl_ctx,
(const uint8_t*)data + hdl_size, size - hdl_size);
if (ret < 0) {
free(hdl);
return PAL_ERROR_DENIED;
}
hdl->pipe.handshake_helper_thread_hdl = NULL;
break;
case PAL_TYPE_PIPESRV:
hdl->pipe.ssl_ctx = NULL;
hdl->pipe.handshake_helper_thread_hdl = NULL;
break;
case PAL_TYPE_CONSOLE:
break;
case PAL_TYPE_DEV: {
hdl->dev.realpath = alloc_and_copy((const char*)data + hdl_size, size - hdl_size);
if (!hdl->dev.realpath) {
free(hdl);
return PAL_ERROR_NOMEM;
}
break;
}
case PAL_TYPE_FILE: {
hdl->file.realpath = alloc_and_copy((const char*)data + hdl_size, size - hdl_size);
if (!hdl->file.realpath) {
free(hdl);
return PAL_ERROR_NOMEM;
}
hdl->file.chunk_hashes = hdl->file.umem = NULL; /* set up in below fixup function */
hdl->file.fd = host_fd; /* correct host FD must be set for below fixup function */
fixup_file_handle_after_deserialization(hdl);
break;
}
case PAL_TYPE_DIR: {
hdl->dir.realpath = alloc_and_copy((const char*)data + hdl_size, size - hdl_size);
if (!hdl->dir.realpath) {
free(hdl);
return PAL_ERROR_NOMEM;
}
hdl->dir.buf = hdl->dir.ptr = hdl->dir.end = NULL;
break;
}
case PAL_TYPE_SOCKET:
fixup_socket_handle_after_deserialization(hdl);
break;
case PAL_TYPE_PROCESS:
/* session key is part of handle but need to deserialize SSL context */
hdl->process.stream = host_fd; /* correct host FD must be passed to SSL context */
ret = _PalStreamSecureInit(hdl, hdl->process.is_server, &hdl->process.session_key,
(LIB_SSL_CONTEXT**)&hdl->process.ssl_ctx,
(const uint8_t*)data + hdl_size, size - hdl_size);
if (ret < 0) {
free(hdl);
return PAL_ERROR_DENIED;
}
break;
case PAL_TYPE_EVENTFD:
break;
default:
free(hdl);
return PAL_ERROR_BADHANDLE;
}
*handle = hdl;
return 0;
}
int _PalSendHandle(PAL_HANDLE target_process, PAL_HANDLE cargo) {
if (target_process->hdr.type != PAL_TYPE_PROCESS)
return PAL_ERROR_BADHANDLE;
/* serialize cargo handle into a blob hdl_data */
void* hdl_data = NULL;
ssize_t hdl_data_size = handle_serialize(cargo, &hdl_data);
if (hdl_data_size < 0)
return hdl_data_size;
ssize_t ret;
struct hdl_header hdl_hdr = {
.has_fd = cargo->flags & (PAL_HANDLE_FD_READABLE | PAL_HANDLE_FD_WRITABLE),
.data_size = hdl_data_size
};
int fd = target_process->process.stream;
/* first send hdl_hdr so recipient knows how many FDs were transferred + how large is cargo */
struct iovec iov = {
.iov_base = &hdl_hdr,
.iov_len = sizeof(struct hdl_header),
};
ret = ocall_send(fd, &iov, 1, NULL, 0, NULL, 0, 0);
if (ret < 0) {
free(hdl_data);
return unix_to_pal_error(ret);
}
/* construct ancillary data with FD-to-transfer in a control message */
alignas(struct cmsghdr) char control_buf[CMSG_SPACE(sizeof(int))] = { 0 };
struct cmsghdr* control_hdr = (struct cmsghdr*)control_buf;
control_hdr->cmsg_level = SOL_SOCKET;
control_hdr->cmsg_type = SCM_RIGHTS;
if (hdl_hdr.has_fd) {
/* XXX: change to `SAME_TYPE` once `PAL_HANDLE` uses `int` to store fds */
static_assert(sizeof(cargo->generic.fd) == sizeof(int), "required");
control_hdr->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(control_hdr), &cargo->generic.fd, sizeof(int));
} else {
control_hdr->cmsg_len = CMSG_LEN(0);
}
/* next send FD-to-transfer as ancillary data */
iov.iov_base = (void*)DUMMYPAYLOAD;
iov.iov_len = DUMMYPAYLOADSIZE;
ret = ocall_send(fd, &iov, 1, NULL, 0, control_hdr, control_hdr->cmsg_len, 0);
if (ret < 0) {
free(hdl_data);
return unix_to_pal_error(ret);
}
/* finally send the serialized cargo as payload (possibly encrypted) */
if (target_process->process.ssl_ctx) {
ret = _PalStreamSecureWrite(target_process->process.ssl_ctx, (uint8_t*)hdl_data,
hdl_hdr.data_size,
/*is_blocking=*/!target_process->process.nonblocking);
} else {
ret = ocall_write(fd, hdl_data, hdl_hdr.data_size);
ret = ret < 0 ? unix_to_pal_error(ret) : ret;
}
free(hdl_data);
return ret < 0 ? ret : 0;
}
int _PalReceiveHandle(PAL_HANDLE source_process, PAL_HANDLE* out_cargo) {
if (source_process->hdr.type != PAL_TYPE_PROCESS)
return PAL_ERROR_BADHANDLE;
ssize_t ret;
struct hdl_header hdl_hdr;
int fd = source_process->process.stream;
/* first receive hdl_hdr so that we know how many FDs were transferred + how large is cargo */
struct iovec iov = {
.iov_base = &hdl_hdr,
.iov_len = sizeof(hdl_hdr),
};
ret = ocall_recv(fd, &iov, 1, NULL, NULL, NULL, NULL, 0);
if (ret < 0)
return unix_to_pal_error(ret);
if ((size_t)ret != sizeof(hdl_hdr)) {
return PAL_ERROR_DENIED;
}
alignas(struct cmsghdr) char control_buf[CMSG_SPACE(sizeof(int))] = { 0 };
size_t control_buf_size = sizeof(control_buf);
/* next receive FDs-to-transfer as ancillary data */
char dummypayload[DUMMYPAYLOADSIZE];
iov.iov_base = dummypayload;
iov.iov_len = sizeof(dummypayload);
ret = ocall_recv(fd, &iov, 1, NULL, NULL, control_buf, &control_buf_size, MSG_CMSG_CLOEXEC);
if (ret < 0)
return unix_to_pal_error(ret);
if (control_buf_size < sizeof(struct cmsghdr)) {
return PAL_ERROR_DENIED;
}
/* finally receive the serialized cargo as payload (possibly encrypted) */
char hdl_data[hdl_hdr.data_size];
if (source_process->process.ssl_ctx) {
ret = _PalStreamSecureRead(source_process->process.ssl_ctx,
(uint8_t*)hdl_data, hdl_hdr.data_size,
/*is_blocking=*/!source_process->process.nonblocking);
} else {
ret = ocall_read(fd, hdl_data, hdl_hdr.data_size);
ret = ret < 0 ? unix_to_pal_error(ret) : ret;
}
if (ret < 0)
return ret;
struct cmsghdr* control_hdr = (struct cmsghdr*)control_buf;
if (control_hdr->cmsg_type != SCM_RIGHTS)
return PAL_ERROR_DENIED;
if (hdl_hdr.has_fd && control_hdr->cmsg_len != CMSG_LEN(sizeof(int))) {
return PAL_ERROR_DENIED;
}
int host_fd = -1;
if (hdl_hdr.has_fd) {
memcpy(&host_fd, CMSG_DATA(control_hdr), sizeof(int));
}
/* deserialize cargo handle from a blob hdl_data */
PAL_HANDLE handle = NULL;
ret = handle_deserialize(&handle, hdl_data, hdl_hdr.data_size, host_fd);
if (ret < 0)
return ret;
/* restore cargo handle's FD from the received FD-to-transfer */
if (hdl_hdr.has_fd) {
handle->generic.fd = host_fd;
} else {
handle->flags &= ~(PAL_HANDLE_FD_READABLE | PAL_HANDLE_FD_WRITABLE);
}
*out_cargo = handle;
return 0;
}
int _PalInitDebugStream(const char* path) {
int ret;
if (g_log_fd != PAL_LOG_DEFAULT_FD) {
ret = ocall_close(g_log_fd);
g_log_fd = PAL_LOG_DEFAULT_FD;
if (ret < 0)
return unix_to_pal_error(ret);
}
ret = ocall_open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, PERM_rw_______);
if (ret < 0)
return unix_to_pal_error(ret);
g_log_fd = ret;
return 0;
}
int _PalDebugLog(const void* buf, size_t size) {
if (g_log_fd < 0)
return PAL_ERROR_BADHANDLE;
// TODO: add retrying on EINTR
ssize_t ret = ocall_write(g_log_fd, buf, size);
if (ret < 0 || (size_t)ret != size) {
return ret < 0 ? unix_to_pal_error(ret) : PAL_ERROR_INTERRUPTED;
}
return 0;
}