Coverage Report

Created: 2023-09-25 06:33

/src/qubes-os/qubes-core-qrexec/libqrexec/remote.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * The Qubes OS Project, http://www.qubes-os.org
3
 *
4
 * Copyright (C) 2020 PaweĊ‚ Marczewski <pawel@invisiblethingslab.com>
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
 *
20
 */
21
22
#include <string.h>
23
#include <stdlib.h>
24
#include <stdio.h>
25
#include <inttypes.h>
26
#include <unistd.h>
27
#include <errno.h>
28
#include <sys/socket.h>
29
#include <limits.h>
30
#include <assert.h>
31
32
#include "libqrexec-utils.h"
33
34
int handle_remote_data(
35
    libvchan_t *data_vchan, int stdin_fd, int *status,
36
    struct buffer *stdin_buf, int data_protocol_version,
37
    bool replace_chars_stdout, bool replace_chars_stderr, bool is_service)
38
421
{
39
421
    struct msg_header hdr;
40
421
    const size_t max_len = max_data_chunk_size(data_protocol_version);
41
421
    char *buf;
42
421
    int rc = REMOTE_ERROR;
43
421
    bool msg_data_warned = false;
44
45
    /* do not receive any data if we have something already buffered */
46
421
    switch (flush_client_data(stdin_fd, stdin_buf)) {
47
421
        case WRITE_STDIN_OK:
48
421
            break;
49
0
        case WRITE_STDIN_BUFFERED:
50
0
            return REMOTE_OK;
51
0
        case WRITE_STDIN_ERROR:
52
0
            PERROR("write");
53
0
            return REMOTE_EOF;
54
421
    }
55
56
421
    buf = malloc(max_len);
57
421
    if (!buf) {
58
0
        PERROR("malloc");
59
0
        return REMOTE_ERROR;
60
0
    }
61
62
3.27k
    while (libvchan_data_ready(data_vchan) > 0) {
63
3.12k
        if (libvchan_recv(data_vchan, &hdr, sizeof(hdr)) != sizeof(hdr))
64
28
            goto out;
65
3.09k
        if (hdr.len > max_len) {
66
73
            LOG(ERROR, "Too big data chunk received: %" PRIu32 " > %zu",
67
73
                hdr.len, max_len);
68
73
            goto out;
69
73
        }
70
3.02k
        if (!read_vchan_all(data_vchan, buf, hdr.len))
71
37
            goto out;
72
73
2.98k
        switch (hdr.type) {
74
            /* handle both directions because this can be either server or client
75
             * of VM-VM connection */
76
481
            case MSG_DATA_STDIN:
77
1.52k
            case MSG_DATA_STDOUT:
78
1.52k
                if (hdr.type != (is_service ? MSG_DATA_STDIN : MSG_DATA_STDOUT) &&
79
1.52k
                        !msg_data_warned) {
80
25
                    LOG(ERROR, is_service ? "client sent MSG_DATA_STDOUT" : "service sent MSG_DATA_STDIN");
81
25
                    msg_data_warned = true;
82
25
                }
83
84
1.52k
                if (stdin_fd < 0)
85
                    /* discard the data */
86
0
                    continue;
87
1.52k
                if (hdr.len == 0) {
88
16
                    rc = REMOTE_EOF;
89
16
                    goto out;
90
1.50k
                } else {
91
1.50k
                    if (replace_chars_stdout)
92
0
                        do_replace_chars(buf, hdr.len);
93
1.50k
                    switch (write_stdin(stdin_fd, buf, hdr.len, stdin_buf)) {
94
1.50k
                        case WRITE_STDIN_OK:
95
1.50k
                            break;
96
0
                        case WRITE_STDIN_BUFFERED:
97
0
                            rc = REMOTE_OK;
98
0
                            goto out;
99
0
                        case WRITE_STDIN_ERROR:
100
0
                            if (!(errno == EPIPE || errno == ECONNRESET)) {
101
0
                                PERROR("write");
102
0
                            }
103
0
                            rc = REMOTE_EOF;
104
0
                            goto out;
105
1.50k
                    }
106
1.50k
                }
107
1.50k
                break;
108
1.50k
            case MSG_DATA_STDERR:
109
1.34k
                if (is_service) {
110
0
                    LOG(ERROR, "client sent MSG_DATA_STDERR");
111
0
                    continue;
112
0
                }
113
114
1.34k
                if (replace_chars_stderr)
115
1.34k
                    do_replace_chars(buf, hdr.len);
116
                /* stderr of remote service, log locally */
117
1.34k
                if (!write_all(2, buf, hdr.len)) {
118
0
                    PERROR("write");
119
                    /* only log the error */
120
0
                }
121
1.34k
                break;
122
15
            case MSG_DATA_EXIT_CODE:
123
15
                if (is_service) {
124
0
                    LOG(ERROR, "client sent MSG_DATA_EXIT_CODE");
125
0
                    continue;
126
0
                }
127
128
                /* remote process exited, so there is no sense to send any data
129
                 * to it */
130
15
                if (hdr.len < sizeof(*status)) {
131
3
                    LOG(ERROR, "MSG_DATA_EXIT_CODE too short: " PRIu32 " < %zu",
132
3
                        hdr.len, sizeof(*status));
133
3
                    *status = 255;
134
3
                } else
135
12
                    memcpy(status, buf, sizeof(*status));
136
15
                rc = REMOTE_EXITED;
137
15
                goto out;
138
100
            default:
139
100
                LOG(ERROR, "unknown msg %d", hdr.type);
140
100
                rc = REMOTE_ERROR;
141
100
                goto out;
142
2.98k
        }
143
2.98k
    }
144
152
    rc = REMOTE_OK;
145
421
out:
146
421
    free(buf);
147
421
    return rc;
148
152
}
149
150
int handle_input(
151
    libvchan_t *vchan, int fd, int msg_type,
152
    int data_protocol_version, struct prefix_data *prefix_data)
153
0
{
154
0
    const size_t max_len = max_data_chunk_size(data_protocol_version);
155
0
    char *buf;
156
0
    ssize_t len;
157
0
    struct msg_header hdr;
158
0
    int rc = REMOTE_ERROR, buf_space;
159
160
0
    buf = malloc(max_len);
161
0
    if (!buf) {
162
0
        PERROR("malloc");
163
0
        return REMOTE_ERROR;
164
0
    }
165
166
0
    static_assert(SSIZE_MAX >= INT_MAX, "can't happen on Linux");
167
0
    hdr.type = msg_type;
168
0
    while ((buf_space = libvchan_buffer_space(vchan)) > (int)sizeof(struct msg_header)) {
169
0
        len = buf_space - sizeof(struct msg_header);
170
0
        if ((size_t)len > max_len)
171
0
            len = max_len;
172
0
        if (prefix_data->len) {
173
0
            if ((size_t)len > prefix_data->len)
174
0
                len = prefix_data->len;
175
0
            memcpy(buf, prefix_data->data, len);
176
0
            prefix_data->data += len;
177
0
            prefix_data->len -= len;
178
0
        } else {
179
0
            len = read(fd, buf, len);
180
            /* If the other side of the socket is a process that is already dead,
181
             * read from such socket could fail with ECONNRESET instead of
182
             * just 0. */
183
0
            if (len < 0 && errno == ECONNRESET)
184
0
                len = 0;
185
0
            if (len < 0) {
186
0
                if (errno == EAGAIN || errno == EWOULDBLOCK)
187
0
                    rc = REMOTE_OK;
188
                /* otherwise keep rc = REMOTE_ERROR */
189
0
                goto out;
190
0
            }
191
0
        }
192
0
        hdr.len = (uint32_t)len;
193
        /* do not fail on sending EOF (think: close()), it will be handled just below */
194
0
        if (libvchan_send(vchan, &hdr, sizeof(hdr)) != sizeof(hdr) && hdr.len != 0)
195
0
            goto out;
196
197
0
        if (len && !write_vchan_all(vchan, buf, len))
198
0
            goto out;
199
200
0
        if (len == 0) {
201
0
            rc = REMOTE_EOF;
202
0
            goto out;
203
0
        }
204
0
    }
205
0
    rc = REMOTE_OK;
206
0
out:
207
0
    free(buf);
208
0
    return rc;
209
0
}
210
211
int send_exit_code(libvchan_t *vchan, int status)
212
0
{
213
0
    struct msg_header hdr;
214
215
0
    hdr.type = MSG_DATA_EXIT_CODE;
216
0
    hdr.len = sizeof(int);
217
0
    if (libvchan_send(vchan, &hdr, sizeof(hdr)) != sizeof(hdr)) {
218
0
        PERROR("send_exit_code hdr");
219
0
        return -1;
220
0
    }
221
0
    if (libvchan_send(vchan, &status, sizeof(status)) != sizeof(status)) {
222
0
        PERROR("send_exit_code status");
223
0
        return -1;
224
0
    }
225
0
    return 0;
226
0
}