Coverage Report

Created: 2024-11-12 06:22

/src/qubes-os/qubes-core-qrexec/daemon/qrexec-daemon.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * The Qubes OS Project, http://www.qubes-os.org
3
 *
4
 * Copyright (C) 2010  Rafal Wojtczuk  <rafal@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 <inttypes.h>
23
#include <stdarg.h>
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <sys/syscall.h>
27
#include <unistd.h>
28
#include <signal.h>
29
#include <errno.h>
30
#include <fcntl.h>
31
#include <sys/stat.h>
32
#include <sys/wait.h>
33
#include <sys/socket.h>
34
#include <sys/un.h>
35
#include <err.h>
36
#include <string.h>
37
#include <assert.h>
38
#include <getopt.h>
39
#include "qrexec.h"
40
#include "libqrexec-utils.h"
41
#include "../libqrexec/ioall.h"
42
#include "qrexec-daemon-common.h"
43
44
0
#define QREXEC_MIN_VERSION QREXEC_PROTOCOL_V2
45
0
#define QREXEC_SOCKET_PATH "/run/qubes/policy.sock"
46
47
#ifdef COVERAGE
48
void __gcov_dump(void);
49
void __gcov_reset(void);
50
#endif
51
0
static _Noreturn void daemon__exit(int status) {
52
#ifdef COVERAGE
53
    __gcov_dump();
54
#endif
55
0
    _exit(status);
56
0
}
57
58
enum client_state {
59
    CLIENT_INVALID = 0, // table slot not used
60
    CLIENT_HELLO, // waiting for client hello
61
    CLIENT_CMDLINE, // waiting for cmdline from client
62
    CLIENT_RUNNING // waiting for client termination (to release vchan port)
63
};
64
65
enum vchan_port_state {
66
    VCHAN_PORT_UNUSED = -1
67
};
68
69
struct _client {
70
    int state;    // enum client_state
71
};
72
73
enum policy_response {
74
    RESPONSE_PENDING,
75
    RESPONSE_ALLOW,
76
    RESPONSE_DENY,
77
    RESPONSE_MALFORMED,
78
};
79
80
struct _policy_pending {
81
    pid_t pid;
82
    struct service_params params;
83
    enum policy_response response_sent;
84
};
85
86
332
#define VCHAN_BASE_DATA_PORT (VCHAN_BASE_PORT+1)
87
88
/*
89
   The "clients" array is indexed by client's fd.
90
   Thus its size must be equal MAX_FDS; defining MAX_CLIENTS for clarity.
91
   */
92
93
10.3k
#define MAX_CLIENTS MAX_FDS
94
static struct _client clients[MAX_CLIENTS]; // data on all qrexec_client connections
95
96
static struct _policy_pending policy_pending[MAX_CLIENTS];
97
static int policy_pending_max = -1;
98
99
/* indexed with vchan port number relative to VCHAN_BASE_DATA_PORT; stores
100
 * either VCHAN_PORT_* or remote domain id for used port */
101
static int used_vchan_ports[MAX_CLIENTS];
102
103
/* notify client (close its connection) when connection initiated by it was
104
 * terminated - used by qrexec-policy to cleanup (disposable) VM; indexed with
105
 * vchan port number relative to VCHAN_BASE_DATA_PORT; stores fd of given
106
 * client or -1 if none requested */
107
static int vchan_port_notify_client[MAX_CLIENTS];
108
109
static int max_client_fd = -1;    // current max fd of all clients; so that we need not to scan all the "clients" table
110
static int qrexec_daemon_unix_socket_fd;  // /var/run/qubes/qrexec.xid descriptor
111
static const char *default_user = "user";
112
static const char default_user_keyword[] = "DEFAULT:";
113
0
#define default_user_keyword_len_without_colon (sizeof(default_user_keyword)-2)
114
115
static int opt_quiet = 0;
116
117
static const char *policy_program = QREXEC_POLICY_PROGRAM;
118
119
#ifdef __GNUC__
120
#  define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
121
#else
122
#  define UNUSED(x) UNUSED_ ## x
123
#endif
124
125
static volatile int child_exited;
126
static volatile int terminate_requested;
127
128
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
129
#include "../fuzz/fuzz.h"
130
#else
131
static
132
#endif
133
libvchan_t *vchan;
134
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
135
static
136
#endif
137
int protocol_version;
138
139
static const char *remote_domain_name;  // guess what
140
static const char *remote_domain_uuid;
141
static int remote_domain_id;
142
143
static void unlink_or_exit(const char *path)
144
0
{
145
0
    int v = unlink(path);
146
0
    if (v != 0 && !(v == -1 && errno == ENOENT))
147
0
        err(1, "unlink(%s)", path);
148
0
}
149
150
static char __attribute__((format(printf, 1, 2))) *xasprintf(const char *fmt, ...)
151
0
{
152
0
    va_list x;
153
0
    char *res;
154
0
    va_start(x, fmt);
155
0
    int r = vasprintf(&res, fmt, x);
156
0
    va_end(x);
157
0
    if (r < 0)
158
0
        abort();
159
0
    return res;
160
0
}
161
162
static void unlink_qrexec_socket(void)
163
0
{
164
0
    char *socket_name;
165
0
    const char *p[2] = {remote_domain_name, remote_domain_uuid};
166
0
    int i;
167
168
0
    for (i = 0; i < 2; ++i) {
169
0
        char *link_to_socket_name = xasprintf("qrexec.%s%s", i > 0 ? "uuid:" : "", p[i]);
170
0
        unlink_or_exit(link_to_socket_name);
171
0
        free(link_to_socket_name);
172
0
    }
173
0
    if (asprintf(&socket_name, "qrexec.%d", remote_domain_id) < 0)
174
0
        abort();
175
0
    unlink_or_exit(socket_name);
176
0
    free(socket_name);
177
0
}
178
179
static void handle_vchan_error(const char *op)
180
67
{
181
67
    LOG(ERROR, "Error while vchan %s, exiting", op);
182
67
    exit(1);
183
67
}
184
185
static int create_qrexec_socket(int domid, const char *domname, const char *domuuid)
186
0
{
187
    /* When running as root, make the socket accessible; perms on /var/run/qubes still apply */
188
0
    umask(0);
189
190
0
    const char *p[2] = { domuuid, domname };
191
0
    char *socket_address = xasprintf("qrexec.%d", domid);
192
0
    for (int i = 0; i < 2; ++i) {
193
0
        if (p[i] == NULL)
194
0
            continue;
195
0
        char *link_to_socket_name = xasprintf("qrexec.%s%s", i ? "" : "uuid:", p[i]);
196
0
        unlink_or_exit(link_to_socket_name);
197
0
        if (symlink(socket_address, link_to_socket_name)) {
198
0
            PERROR("symlink(%s,%s)", socket_address, link_to_socket_name);
199
0
        }
200
0
        free(link_to_socket_name);
201
0
    }
202
0
    int fd = get_server_socket(socket_address);
203
0
    umask(0077);
204
0
    return fd;
205
0
}
206
207
0
#define MAX_STARTUP_TIME_DEFAULT 60
208
209
static void incompatible_protocol_error_message(
210
        const char *domain_name, int remote_version)
211
0
{
212
0
    char text[1024];
213
0
    int ret;
214
0
    struct stat buf;
215
0
    ret=stat("/usr/bin/kdialog", &buf);
216
0
#define KDIALOG_CMD "kdialog --title 'Qrexec daemon' --sorry "
217
0
#define ZENITY_CMD "zenity --title 'Qrexec daemon' --warning --text "
218
0
    snprintf(text, sizeof(text),
219
0
            "%s"
220
0
            "'Domain %s uses incompatible qrexec protocol (%d instead of %d). "
221
0
            "You need to update either dom0 or VM packages.\n"
222
0
            "To access this VM console do not close this error message and run:\n"
223
0
            "sudo xl console -t pv %s'",
224
0
            ret==0 ? KDIALOG_CMD : ZENITY_CMD,
225
0
            domain_name, remote_version, QREXEC_PROTOCOL_VERSION, domain_name);
226
0
#undef KDIALOG_CMD
227
0
#undef ZENITY_CMD
228
    /* silence -Wunused-result */
229
0
    ret = system(text);
230
0
}
231
232
static int handle_agent_hello(libvchan_t *ctrl, const char *domain_name)
233
0
{
234
0
    struct msg_header hdr;
235
0
    struct peer_info info;
236
0
    int actual_version;
237
238
0
    if (libvchan_recv(ctrl, &hdr, sizeof(hdr)) != sizeof(hdr)) {
239
0
        LOG(ERROR, "Failed to read agent HELLO hdr");
240
0
        return -1;
241
0
    }
242
243
0
    if (hdr.type != MSG_HELLO || hdr.len != sizeof(info)) {
244
0
        LOG(ERROR, "Invalid HELLO packet received: type %d, len %d", hdr.type, hdr.len);
245
0
        return -1;
246
0
    }
247
248
0
    if (libvchan_recv(ctrl, &info, sizeof(info)) != sizeof(info)) {
249
0
        LOG(ERROR, "Failed to read agent HELLO body");
250
0
        return -1;
251
0
    }
252
253
0
    actual_version = info.version < QREXEC_PROTOCOL_VERSION ? info.version : QREXEC_PROTOCOL_VERSION;
254
255
0
    if (actual_version < QREXEC_MIN_VERSION) {
256
0
        LOG(ERROR, "Incompatible agent protocol version (remote %d, local %d)", info.version, QREXEC_PROTOCOL_VERSION);
257
0
        incompatible_protocol_error_message(domain_name, info.version);
258
0
        return -1;
259
0
    }
260
261
    /* send own HELLO */
262
    /* those messages are the same as received from agent, but set it again for
263
     * readability */
264
0
    hdr.type = MSG_HELLO;
265
0
    hdr.len = sizeof(info);
266
0
    info.version = QREXEC_PROTOCOL_VERSION;
267
268
0
    if (libvchan_send(ctrl, &hdr, sizeof(hdr)) != sizeof(hdr)) {
269
0
        LOG(ERROR, "Failed to send HELLO hdr to agent");
270
0
        return -1;
271
0
    }
272
273
0
    if (libvchan_send(ctrl, &info, sizeof(info)) != sizeof(info)) {
274
0
        LOG(ERROR, "Failed to send HELLO hdr to agent");
275
0
        return -1;
276
0
    }
277
278
0
    return actual_version;
279
0
}
280
281
static void signal_handler(int sig);
282
283
/* do the preparatory tasks, needed before entering the main event loop */
284
static void init(int xid, bool opt_direct)
285
0
{
286
0
    char qrexec_error_log_name[256];
287
0
    int logfd;
288
0
    int i;
289
0
    pid_t pid;
290
0
    int startup_timeout = MAX_STARTUP_TIME_DEFAULT;
291
0
    const char *startup_timeout_str = NULL;
292
293
0
    if (xid <= 0) {
294
0
        LOG(ERROR, "domain id=0?");
295
0
        exit(1);
296
0
    }
297
0
    startup_timeout_str = getenv("QREXEC_STARTUP_TIMEOUT");
298
0
    if (startup_timeout_str) {
299
0
        startup_timeout = atoi(startup_timeout_str);
300
0
        if (startup_timeout <= 0)
301
            // invalid or negative number
302
0
            startup_timeout = MAX_STARTUP_TIME_DEFAULT;
303
0
    }
304
305
0
    int pipes[2] = { -1, -1 };
306
0
    bool have_timeout = getenv("QREXEC_STARTUP_NOWAIT") == NULL;
307
0
    if (!opt_direct) {
308
0
        if (have_timeout && pipe2(pipes, O_CLOEXEC))
309
0
            err(1, "pipe2()");
310
0
        switch (pid=fork()) {
311
0
            case -1:
312
0
                PERROR("fork");
313
0
                exit(1);
314
0
            case 0:
315
0
                if (have_timeout)
316
0
                    close(pipes[0]);
317
0
                break;
318
0
            default:
319
0
                if (!have_timeout)
320
0
                    exit(0);
321
0
                close(pipes[1]);
322
0
                if (!opt_quiet)
323
0
                    LOG(ERROR, "Waiting for VM's qrexec agent.");
324
0
                struct pollfd fds[1] = {{ .fd = pipes[0], .events = POLLIN | POLLHUP, .revents = 0 }};
325
0
                for (;;) {
326
0
                    int res = poll(fds, 1, 1000);
327
0
                    if (res < 0)
328
0
                        err(1, "poll()");
329
0
                    if (res) {
330
0
                        char buf[1];
331
0
                        ssize_t bytes = read(pipes[0], buf, sizeof buf);
332
0
                        if (bytes < 0)
333
0
                            err(1, "read()");
334
0
                        if (bytes == 0) {
335
0
                            LOG(ERROR, "Connection to the VM failed");
336
0
                            exit(1);
337
0
                        }
338
0
                        switch (buf[0]) {
339
0
                        case 0:
340
0
                            if (!opt_quiet)
341
0
                                LOG(INFO, "Connected to VM");
342
0
                            exit(0);
343
0
                        case 1:
344
0
                            LOG(ERROR, "Cannot connect to '%s' qrexec agent for %d seconds, giving up", remote_domain_name, startup_timeout);
345
0
                            exit(3);
346
0
                        default:
347
0
                            abort();
348
0
                        }
349
0
                    }
350
0
                    if (!opt_quiet)
351
0
                        fprintf(stderr, ".");
352
0
                }
353
0
        }
354
0
    }
355
356
357
0
    if (chdir(socket_dir) < 0) {
358
0
        PERROR("chdir %s failed", socket_dir);
359
0
        exit(1);
360
0
    }
361
362
0
    if (!opt_direct) {
363
0
        if ((unsigned)snprintf(qrexec_error_log_name, sizeof(qrexec_error_log_name),
364
0
                               "/var/log/qubes/qrexec.%s.log", remote_domain_name) >=
365
0
                sizeof(qrexec_error_log_name))
366
0
            errx(1, "remote domain name too long");
367
0
        umask(0007);        // make the log readable by the "qubes" group
368
0
        logfd =
369
0
            open(qrexec_error_log_name, O_WRONLY | O_CREAT | O_TRUNC,
370
0
                 0660);
371
372
0
        if (logfd < 0) {
373
0
            PERROR("open");
374
0
            exit(1);
375
0
        }
376
377
0
        if (dup2(logfd, 1) != 1 || dup2(logfd, 2) != 2)
378
0
            err(1, "dup2()");
379
0
        if (logfd > 2)
380
0
            close(logfd);
381
382
0
        if (setsid() < 0) {
383
0
            PERROR("setsid()");
384
0
            exit(1);
385
0
        }
386
0
    }
387
388
0
    int wait_fd;
389
0
    if (have_timeout) {
390
0
        vchan = libvchan_client_init_async(xid, VCHAN_BASE_PORT, &wait_fd);
391
0
        if (vchan != NULL && qubes_wait_for_vchan_connection_with_timeout(
392
0
                    vchan, wait_fd, false, startup_timeout) < 0) {
393
0
            if (!opt_direct && write(pipes[1], "\1", 1)) {}
394
0
            LOG(ERROR, "qrexec connection timeout");
395
0
            exit(3);
396
0
        }
397
0
    } else {
398
        /* No timeout in this case */
399
0
        vchan = libvchan_client_init(xid, VCHAN_BASE_PORT);
400
0
    }
401
0
    if (!vchan) {
402
0
        LOG(ERROR, "Cannot create data vchan connection");
403
0
        exit(3);
404
0
    }
405
0
    protocol_version = handle_agent_hello(vchan, remote_domain_name);
406
0
    if (protocol_version < 0) {
407
0
        exit(1);
408
0
    }
409
410
0
    if (setgid(getgid()) < 0) {
411
0
        PERROR("setgid()");
412
0
        exit(1);
413
0
    }
414
0
    if (setuid(getuid()) < 0) {
415
0
        PERROR("setuid()");
416
0
        exit(1);
417
0
    }
418
419
    /* initialize clients state arrays */
420
0
    for (i = 0; i < MAX_CLIENTS; i++) {
421
0
        clients[i].state = CLIENT_INVALID;
422
0
        policy_pending[i].pid = 0;
423
0
        used_vchan_ports[i] = VCHAN_PORT_UNUSED;
424
0
        vchan_port_notify_client[i] = VCHAN_PORT_UNUSED;
425
0
    }
426
427
0
    atexit(unlink_qrexec_socket);
428
0
    qrexec_daemon_unix_socket_fd =
429
0
        create_qrexec_socket(xid, remote_domain_name, remote_domain_uuid);
430
431
0
    struct sigaction sigchld_action = {
432
0
        .sa_handler = signal_handler,
433
0
        .sa_flags = SA_NOCLDSTOP,
434
0
    };
435
0
    struct sigaction sigterm_action = {
436
0
        .sa_handler = signal_handler,
437
0
        .sa_flags = 0,
438
0
    };
439
0
    sigemptyset(&sigchld_action.sa_mask);
440
0
    sigemptyset(&sigterm_action.sa_mask);
441
0
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
442
0
        err(1, "signal");
443
0
    if (sigaction(SIGCHLD, &sigchld_action, NULL))
444
0
        err(1, "sigaction");
445
0
    if (sigaction(SIGTERM, &sigterm_action, NULL))
446
0
        err(1, "sigaction");
447
0
    if (have_timeout && !opt_direct) {
448
0
        if (write(pipes[1], "", 1) != 1)
449
0
            err(1, "write(pipe)");
450
0
        close(pipes[1]);
451
0
    }
452
0
}
453
454
static int send_client_hello(int fd)
455
0
{
456
0
    struct msg_header hdr;
457
0
    struct peer_info info;
458
459
0
    hdr.type = MSG_HELLO;
460
0
    hdr.len = sizeof(info);
461
0
    info.version = QREXEC_PROTOCOL_VERSION;
462
463
0
    if (!write_all(fd, &hdr, sizeof(hdr))) {
464
0
        LOG(ERROR, "Failed to send MSG_HELLO hdr to client %d", fd);
465
0
        return -1;
466
0
    }
467
0
    if (!write_all(fd, &info, sizeof(info))) {
468
0
        LOG(ERROR, "Failed to send MSG_HELLO to client %d", fd);
469
0
        return -1;
470
0
    }
471
0
    return 0;
472
0
}
473
474
static int allocate_vchan_port(int connect_domain)
475
0
{
476
    /*
477
      Make sure the allocated ports numbers are unique for a given {domX, domY}
478
      set.
479
480
      For domX-domY connections, both daemons can allocate ports. If they both
481
      allocate the same port number, this can cause trouble:
482
      - We might receive MSG_CONNECTION_TERMINATED for the wrong connection.
483
      - Although vchan connections in both directions can exist independently,
484
        the direction (client-server or server-client) is not always
485
        the same, so collision is still possible.
486
487
      To prevent that from happening, for X < Y allow the daemon for X to
488
      allocate only odd port numbers, and the daemon for Y to allocate only
489
      even port numbers.
490
491
      (This does not apply if we are connecting to/from dom0, as there is no
492
      separate daemon running for dom0).
493
     */
494
495
0
    int i, step;
496
0
    if (connect_domain == 0) {
497
0
        i = 0;
498
0
        step = 1;
499
0
    } else {
500
0
        i = connect_domain > remote_domain_id ? 1 : 0;
501
0
        step = 2;
502
0
    }
503
504
0
    for (; i < MAX_CLIENTS; i += step) {
505
0
        if (used_vchan_ports[i] == VCHAN_PORT_UNUSED) {
506
0
            used_vchan_ports[i] = connect_domain;
507
0
            return VCHAN_BASE_DATA_PORT+i;
508
0
        }
509
0
    }
510
0
    return 0;
511
0
}
512
513
static void handle_new_client(void)
514
0
{
515
0
    int fd = do_accept(qrexec_daemon_unix_socket_fd);
516
0
    if (fd >= MAX_CLIENTS) {
517
0
        LOG(ERROR, "too many clients ?");
518
0
        exit(1);
519
0
    }
520
521
0
    if (send_client_hello(fd) < 0) {
522
0
        close(fd);
523
0
        clients[fd].state = CLIENT_INVALID;
524
0
        return;
525
0
    }
526
527
0
    clients[fd].state = CLIENT_HELLO;
528
0
    if (fd > max_client_fd)
529
0
        max_client_fd = fd;
530
0
}
531
532
static void terminate_client(int fd)
533
1
{
534
1
    int port;
535
1
    clients[fd].state = CLIENT_INVALID;
536
1
    close(fd);
537
    /* if client requested vchan connection end notify, cancel it */
538
257
    for (port = 0; port < MAX_CLIENTS; port++) {
539
256
        if (vchan_port_notify_client[port] == fd)
540
256
            vchan_port_notify_client[port] = VCHAN_PORT_UNUSED;
541
256
    }
542
1
}
543
544
static void release_vchan_port(int port, int expected_remote_id)
545
50
{
546
    /* release only if was reserved for connection to given domain */
547
50
    if (used_vchan_ports[port-VCHAN_BASE_DATA_PORT] == expected_remote_id) {
548
2
        used_vchan_ports[port-VCHAN_BASE_DATA_PORT] = VCHAN_PORT_UNUSED;
549
        /* notify client if requested - it will clear notification request */
550
2
        if (vchan_port_notify_client[port-VCHAN_BASE_DATA_PORT] != VCHAN_PORT_UNUSED)
551
1
            terminate_client(vchan_port_notify_client[port-VCHAN_BASE_DATA_PORT]);
552
2
    }
553
50
}
554
555
static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr)
556
0
{
557
0
    struct exec_params *params = NULL;
558
0
    uint32_t len;
559
0
    char *buf;
560
0
    int use_default_user = 0;
561
0
    int i;
562
563
0
    if (hdr->len <= sizeof(*params)) {
564
0
        LOG(ERROR, "Too-short packet received from client %d: "
565
0
                   "type %" PRIu32 ", len %" PRIu32 "(min %zu)",
566
0
                   fd, hdr->type, hdr->len, sizeof(*params) + 1);
567
0
        goto terminate;
568
0
    }
569
0
    if (hdr->len > MAX_QREXEC_CMD_LEN) {
570
0
        LOG(ERROR, "Too-long packet received from client %d: "
571
0
                   "type %" PRIu32 ", len %" PRIu32 "(max %lu)",
572
0
                   fd, hdr->type, hdr->len, MAX_QREXEC_CMD_LEN);
573
0
        goto terminate;
574
0
    }
575
0
    len = hdr->len - sizeof(*params);
576
577
0
    params = malloc(hdr->len);
578
0
    if (params == NULL) {
579
0
        PERROR("malloc");
580
0
        goto terminate;
581
0
    }
582
583
0
    if (!read_all(fd, params, hdr->len)) {
584
0
        goto terminate;
585
0
    }
586
0
    buf = params->cmdline;
587
588
0
    if (buf[len - 1] != '\0') {
589
0
        LOG(ERROR, "Client sent buffer of length %" PRIu32 " that is not "
590
0
            "NUL-terminated", len);
591
0
        goto terminate;
592
0
    }
593
594
0
    if (hdr->type == MSG_SERVICE_CONNECT) {
595
        /* if the service was accepted, do not send spurious
596
         * MSG_SERVICE_REFUSED when service process itself exit with non-zero
597
         * code. Avoid also sending MSG_SERVICE_CONNECT twice. */
598
0
        for (i = 0; i <= policy_pending_max; i++) {
599
0
            if (policy_pending[i].pid &&
600
0
                    policy_pending[i].response_sent == RESPONSE_PENDING &&
601
0
                    strncmp(policy_pending[i].params.ident, buf, len) == 0) {
602
0
                break;
603
0
            }
604
0
        }
605
0
        if (i > policy_pending_max) {
606
0
            LOG(ERROR, "Connection with ident %s not requested or already handled",
607
0
                    policy_pending[i].params.ident);
608
0
            goto terminate;
609
0
        }
610
0
        policy_pending[i].response_sent = RESPONSE_ALLOW;
611
0
    } else {
612
0
        if (hdr->type != MSG_JUST_EXEC && hdr->type != MSG_EXEC_CMDLINE) {
613
            // Sending such a message would just cause the agent to terminate.
614
0
            LOG(ERROR, "Invalid message type %" PRIu32 " from client", hdr->type);
615
0
            goto terminate;
616
0
        }
617
0
        if (params->connect_port != 0) {
618
            // This is wrong, so log it, but allow it in case any code relies
619
            // on it.  I did not find any such code.
620
0
            LOG(ERROR, "Client provided port %" PRIu32
621
0
                    " in non-MSG_SERVICE_CONNECT request (type %" PRIu32 ")",
622
0
                    params->connect_port, hdr->type);
623
0
        }
624
0
    }
625
626
0
    if (!params->connect_port) {
627
0
        struct exec_params client_params;
628
        /* allocate port and send it to the client */
629
0
        params->connect_port = allocate_vchan_port(params->connect_domain);
630
0
        if (params->connect_port <= 0) {
631
0
            LOG(ERROR, "Failed to allocate new vchan port, too many clients?");
632
0
            goto terminate;
633
0
        }
634
        /* notify the client when this connection got terminated */
635
0
        vchan_port_notify_client[params->connect_port-VCHAN_BASE_DATA_PORT] = fd;
636
0
        client_params.connect_port = params->connect_port;
637
0
        client_params.connect_domain = remote_domain_id;
638
0
        hdr->len = sizeof(client_params);
639
0
        if (!write_all(fd, hdr, sizeof(*hdr)) ||
640
0
                !write_all(fd, &client_params, sizeof(client_params))) {
641
0
            terminate_client(fd);
642
0
            release_vchan_port(params->connect_port, params->connect_domain);
643
0
            free(params);
644
0
            return 0;
645
0
        }
646
        /* restore original len value */
647
0
        hdr->len = len+sizeof(*params);
648
0
    } else {
649
0
        if (!((params->connect_port >= VCHAN_BASE_DATA_PORT) &&
650
0
              (params->connect_port < VCHAN_BASE_DATA_PORT+MAX_CLIENTS))) {
651
0
            LOG(ERROR, "Invalid connect port %" PRIu32, params->connect_port);
652
0
            goto terminate;
653
0
        }
654
0
    }
655
656
0
    if ((hdr->type != MSG_SERVICE_CONNECT) &&
657
0
        (strncmp(buf, default_user_keyword, default_user_keyword_len_without_colon+1) == 0))
658
0
    {
659
0
        use_default_user = 1;
660
0
        hdr->len -= default_user_keyword_len_without_colon;
661
0
        hdr->len += strlen(default_user);
662
0
    }
663
0
    if (libvchan_send(vchan, hdr, sizeof(*hdr)) != sizeof(*hdr))
664
0
        handle_vchan_error("send");
665
0
    if (use_default_user) {
666
0
        int send_len = strlen(default_user);
667
0
        if (libvchan_send(vchan, params, sizeof(*params)) != sizeof(*params))
668
0
            handle_vchan_error("send params");
669
0
        if (libvchan_send(vchan, default_user, send_len) != send_len)
670
0
            handle_vchan_error("send default_user");
671
0
        send_len = len-default_user_keyword_len_without_colon;
672
0
        if (libvchan_send(vchan, buf+default_user_keyword_len_without_colon,
673
0
                    send_len) != send_len)
674
0
            handle_vchan_error("send buf");
675
0
    } else {
676
0
        if (libvchan_send(vchan, params, hdr->len) != (int)hdr->len)
677
0
            handle_vchan_error("send buf");
678
0
    }
679
0
    free(params);
680
0
    return 1;
681
0
terminate:
682
0
    terminate_client(fd);
683
0
    free(params);
684
0
    return 0;
685
0
}
686
687
static void handle_cmdline_message_from_client(int fd)
688
0
{
689
0
    struct msg_header hdr;
690
0
    if (!read_all(fd, &hdr, sizeof hdr)) {
691
0
        terminate_client(fd);
692
0
        return;
693
0
    }
694
0
    switch (hdr.type) {
695
0
        case MSG_EXEC_CMDLINE:
696
0
        case MSG_JUST_EXEC:
697
0
        case MSG_SERVICE_CONNECT:
698
0
            break;
699
0
        default:
700
0
            terminate_client(fd);
701
0
            return;
702
0
    }
703
704
0
    if (!handle_cmdline_body_from_client(fd, &hdr)) {
705
        // client disconnected while sending cmdline, above call already
706
        // cleaned up client info
707
0
        return;
708
0
    }
709
0
    clients[fd].state = CLIENT_RUNNING;
710
0
}
711
712
static void handle_client_hello(int fd)
713
0
{
714
0
    struct msg_header hdr;
715
0
    struct peer_info info;
716
717
0
    if (!read_all(fd, &hdr, sizeof hdr)) {
718
0
        terminate_client(fd);
719
0
        return;
720
0
    }
721
0
    if (hdr.type != MSG_HELLO || hdr.len != sizeof(info)) {
722
0
        LOG(ERROR, "Invalid HELLO packet received from client %d: "
723
0
                "type %d, len %d", fd, hdr.type, hdr.len);
724
0
        terminate_client(fd);
725
0
        return;
726
0
    }
727
0
    if (!read_all(fd, &info, sizeof info)) {
728
0
        terminate_client(fd);
729
0
        return;
730
0
    }
731
0
    if (info.version != QREXEC_PROTOCOL_VERSION) {
732
0
        LOG(ERROR, "Incompatible client protocol version (remote %d, local %d)", info.version, QREXEC_PROTOCOL_VERSION);
733
0
        terminate_client(fd);
734
0
        return;
735
0
    }
736
0
    clients[fd].state = CLIENT_CMDLINE;
737
0
}
738
739
/* handle data received from one of qrexec_client processes */
740
static void handle_message_from_client(int fd)
741
0
{
742
0
    char buf[1];
743
744
0
    switch (clients[fd].state) {
745
0
        case CLIENT_HELLO:
746
0
            handle_client_hello(fd);
747
0
            return;
748
0
        case CLIENT_CMDLINE:
749
0
            handle_cmdline_message_from_client(fd);
750
0
            return;
751
0
        case CLIENT_RUNNING:
752
            // expected EOF
753
0
            if (read(fd, buf, sizeof(buf)) != 0) {
754
0
                LOG(ERROR, "Unexpected data received from client %d", fd);
755
0
            }
756
0
            terminate_client(fd);
757
0
            return;
758
0
        case CLIENT_INVALID:
759
0
            return; /* nothing to do */
760
0
        default:
761
0
            LOG(ERROR, "Invalid client state %d", clients[fd].state);
762
0
            exit(1);
763
0
    }
764
0
}
765
766
767
/*
768
 * The signal handler executes asynchronously; therefore all it should do is
769
 * to set a flag "signal has arrived", and let the main even loop react to this
770
 * flag in appropriate moment.
771
 */
772
static void signal_handler(int sig)
773
0
{
774
0
    switch (sig) {
775
0
    case SIGCHLD:
776
0
        child_exited = 1;
777
0
        break;
778
0
    case SIGTERM:
779
0
        terminate_requested = 1;
780
0
        break;
781
0
    default:
782
        /* cannot happen */
783
0
        abort();
784
0
    }
785
0
}
786
787
295
static void send_service_refused(libvchan_t *vchan, const struct service_params *untrusted_params) {
788
295
    struct msg_header hdr;
789
790
295
    hdr.type = MSG_SERVICE_REFUSED;
791
295
    hdr.len = sizeof(*untrusted_params);
792
793
295
    if (libvchan_send(vchan, &hdr, sizeof(hdr)) != sizeof(hdr)) {
794
0
        LOG(ERROR, "Failed to send MSG_SERVICE_REFUSED hdr to agent");
795
0
        exit(1);
796
0
    }
797
798
295
    if (libvchan_send(vchan, untrusted_params, sizeof(*untrusted_params)) != sizeof(*untrusted_params)) {
799
0
        LOG(ERROR, "Failed to send MSG_SERVICE_REFUSED to agent");
800
0
        exit(1);
801
0
    }
802
295
}
803
804
/* clean zombies, check for denied service calls */
805
static void reap_children(void)
806
0
{
807
0
    int status;
808
0
    int i;
809
810
0
    pid_t pid;
811
0
    while ((pid=waitpid(-1, &status, WNOHANG)) > 0) {
812
0
        for (i = 0; i <= policy_pending_max; i++) {
813
0
            if (policy_pending[i].pid == pid) {
814
0
                if (!WIFEXITED(status))
815
0
                    continue;
816
0
                status = WEXITSTATUS(status);
817
0
                if (status != 0) {
818
0
                    if (policy_pending[i].response_sent != RESPONSE_PENDING) {
819
0
                        LOG(ERROR, "qrexec-policy-exec for connection %s exited with code %d, but the response (%s) was already sent",
820
0
                                policy_pending[i].params.ident, status,
821
0
                                policy_pending[i].response_sent == RESPONSE_ALLOW ? "allow" : "deny");
822
0
                    } else {
823
0
                        policy_pending[i].response_sent = RESPONSE_DENY;
824
0
                        send_service_refused(vchan, &policy_pending[i].params);
825
0
                    }
826
0
                } else {
827
0
                    policy_pending[i].response_sent = RESPONSE_ALLOW;
828
0
                }
829
                /* in case of allowed calls, we will do the rest in
830
                 * MSG_SERVICE_CONNECT from client handler */
831
0
                policy_pending[i].pid = 0;
832
0
                while (policy_pending_max > 0 &&
833
0
                        policy_pending[policy_pending_max].pid == 0)
834
0
                    policy_pending_max--;
835
0
                break;
836
0
            }
837
0
        }
838
0
    }
839
0
    child_exited = 0;
840
0
}
841
842
141
static int find_policy_pending_slot(void) {
843
10.0k
    for (int i = 0; i < MAX_CLIENTS; i++) {
844
10.0k
        if (policy_pending[i].pid == 0) {
845
141
            if (i > policy_pending_max)
846
141
                policy_pending_max = i;
847
141
            return i;
848
141
        }
849
10.0k
    }
850
0
    return -1;
851
141
}
852
853
static void sanitize_name(char * untrusted_s_signed, char *extra_allowed_chars)
854
648
{
855
648
    unsigned char * untrusted_s;
856
12.4k
    for (untrusted_s=(unsigned char*)untrusted_s_signed; *untrusted_s; untrusted_s++) {
857
11.8k
        if (*untrusted_s >= 'a' && *untrusted_s <= 'z')
858
1.73k
            continue;
859
10.1k
        if (*untrusted_s >= 'A' && *untrusted_s <= 'Z')
860
1.67k
            continue;
861
8.43k
        if (*untrusted_s >= '0' && *untrusted_s <= '9')
862
1.40k
            continue;
863
7.02k
        if (*untrusted_s == '_' ||
864
7.02k
               *untrusted_s == '-' ||
865
7.02k
               *untrusted_s == '.')
866
2.75k
            continue;
867
4.27k
        if (extra_allowed_chars && strchr(extra_allowed_chars, *untrusted_s))
868
635
            continue;
869
3.64k
        *untrusted_s = '_';
870
3.64k
    }
871
648
}
872
873
static int parse_policy_response(
874
    char *response,
875
    size_t result_bytes,
876
    bool daemon,
877
    char **user,
878
    char **target_uuid,
879
    char **target,
880
    char **requested_target,
881
    int *autostart
882
0
) {
883
0
    *user = *target_uuid = *target = *requested_target = NULL;
884
0
    int result = *autostart = -1;
885
0
    const char *const msg = daemon ? "qrexec-policy-daemon" : "qrexec-policy-exec";
886
    // At least one byte must be returned
887
0
    if (result_bytes < 1) {
888
0
        LOG(ERROR, "%s didn't return any data", msg);
889
0
        return RESPONSE_MALFORMED;
890
0
    }
891
    // Forbid NUL bytes in response.  qrexec_read_all_to_malloc() has added the
892
    // NUL terminator already.
893
0
    if (strlen(response) != result_bytes) {
894
0
        LOG(ERROR, "%s wrote a NUL byte", msg);
895
0
        return RESPONSE_MALFORMED;
896
0
    }
897
    // Strip any trailing newlines.
898
0
    if (response[result_bytes - 1] == '\n') {
899
0
        result_bytes--;
900
0
        response[result_bytes] = '\0';
901
0
    }
902
0
    char *resp = response, *current_response;
903
0
    while ((current_response = strsep(&resp, "\n"))) {
904
0
        if (!strncmp(current_response, "result=", sizeof("result=") - 1)) {
905
0
            current_response += sizeof("result=") - 1;
906
0
            if (result != -1) {
907
0
                goto bad_response;
908
0
            }
909
0
            if (!strcmp(current_response, "allow"))
910
0
                result = 0;
911
0
            else if (!strcmp(current_response, "deny")) {
912
0
                result = 1;
913
0
            } else {
914
0
                goto bad_response;
915
0
            }
916
0
        } else if (!strncmp(current_response, "user=", sizeof("user=") - 1)) {
917
0
            if (*user)
918
0
                goto bad_response;
919
0
            *user = strdup(current_response + (sizeof("user=") - 1));
920
0
            if (*user == NULL)
921
0
                abort();
922
0
        } else if (!strncmp(current_response, "target=", sizeof("target=") - 1)) {
923
0
            if (*target != NULL)
924
0
                goto bad_response;
925
0
            *target = strdup(current_response + (sizeof("target=") - 1));
926
0
            if (*target == NULL)
927
0
                abort();
928
0
        } else if (!strncmp(current_response, "target_uuid=", sizeof("target_uuid=") - 1)) {
929
0
            if (*target_uuid != NULL)
930
0
                goto bad_response;
931
0
            *target_uuid = strdup(current_response + 12);
932
0
            if (*target_uuid == NULL)
933
0
                abort();
934
0
        } else if (!strncmp(current_response, "autostart=", sizeof("autostart=") - 1)) {
935
0
            current_response += sizeof("autostart=") - 1;
936
0
            if (*autostart != -1)
937
0
                goto bad_response;
938
0
            if (!strcmp(current_response, "True"))
939
0
                *autostart = 1;
940
0
            else if (!strcmp(current_response, "False"))
941
0
                *autostart = 0;
942
0
            else
943
0
                goto bad_response;
944
0
        } else if (!strncmp(current_response, "requested_target=", sizeof("requested_target=") - 1)) {
945
0
            if (*requested_target != NULL)
946
0
                goto bad_response;
947
0
            *requested_target = strdup(current_response + (sizeof("requested_target=") - 1));
948
0
            if (*requested_target == NULL)
949
0
                abort();
950
0
        } else {
951
0
            char *p = strchr(current_response, '=');
952
0
            if (p == NULL)
953
0
                goto bad_response;
954
0
            *p = '\0';
955
0
            LOG(ERROR, "Unknown response key %s, ignoring", current_response);
956
0
        }
957
0
    }
958
959
0
    switch (result) {
960
0
    case 0:
961
0
        if (*user == NULL || *target == NULL || *requested_target == NULL || *autostart == -1)
962
0
            break;
963
0
        return RESPONSE_ALLOW;
964
0
    case 1:
965
0
        if (*user != NULL || *target != NULL || *requested_target != NULL || *autostart != -1)
966
0
            break;
967
0
        return RESPONSE_DENY;
968
0
    default:
969
0
        break;
970
0
    }
971
972
0
bad_response:
973
0
    LOG(ERROR, "%s sent invalid response", msg);
974
0
    return RESPONSE_MALFORMED;
975
0
}
976
977
struct QrexecPolicyRequest {
978
};
979
980
static void send_request_to_daemon(
981
        const int daemon_socket,
982
        const char *remote_domain_name,
983
        const char *target_domain,
984
        const char *service_name)
985
0
{
986
0
    char *command;
987
0
    ssize_t bytes_sent = 0;
988
0
    int command_size = asprintf(&command,
989
0
            "source=%s\n"
990
0
            "intended_target=%s\n"
991
0
            "service_and_arg=%s\n\n",
992
0
            remote_domain_name,
993
0
            target_domain,
994
0
            service_name);
995
0
    if (command_size < 0) {
996
0
        PERROR("failed to construct request");
997
0
        daemon__exit(QREXEC_EXIT_PROBLEM);
998
0
    }
999
1000
0
    for (int i = 0; i < command_size; i += bytes_sent) {
1001
0
        bytes_sent = send(daemon_socket, command + i, command_size - i, MSG_NOSIGNAL);
1002
0
        if (bytes_sent > command_size - i)
1003
0
            abort(); // kernel read beyond buffer bounds?
1004
0
        if (bytes_sent < 0) {
1005
0
            assert(bytes_sent == -1);
1006
0
            PERROR("send to socket failed");
1007
0
            daemon__exit(QREXEC_EXIT_PROBLEM);
1008
0
        }
1009
0
    }
1010
0
    free(command);
1011
0
}
1012
1013
static _Noreturn void null_exit(void)
1014
0
{
1015
#ifdef COVERAGE
1016
    __gcov_dump();
1017
#endif
1018
0
    _exit(QREXEC_EXIT_PROBLEM);
1019
0
}
1020
1021
/*
1022
 * Called when agent sends a message asking to execute a predefined command.
1023
 */
1024
1025
static enum policy_response connect_daemon_socket(
1026
        const char *remote_domain_name,
1027
        const char *target_domain,
1028
        const char *service_name,
1029
        char **user,
1030
        char **target_uuid,
1031
        char **target,
1032
        char **requested_target,
1033
        int *autostart
1034
0
) {
1035
0
    int pid = -1;
1036
0
    struct sockaddr_un daemon_socket_address = {
1037
0
        .sun_family = AF_UNIX,
1038
0
        .sun_path = QREXEC_SOCKET_PATH
1039
0
    };
1040
1041
0
    int daemon_socket = socket(AF_UNIX, SOCK_STREAM, 0);
1042
0
    if (daemon_socket < 0) {
1043
0
         PERROR("socket creation failed");
1044
0
         daemon__exit(QREXEC_EXIT_PROBLEM);
1045
0
    }
1046
1047
0
    int connect_result = connect(daemon_socket,
1048
0
            (struct sockaddr *) &daemon_socket_address,
1049
0
            sizeof(daemon_socket_address));
1050
0
    if (connect_result == 0) {
1051
0
        send_request_to_daemon(daemon_socket,
1052
0
                               remote_domain_name,
1053
0
                               target_domain,
1054
0
                               service_name);
1055
0
        size_t result_bytes;
1056
        // this closes the socket
1057
0
        char *result = qubes_read_all_to_malloc(daemon_socket, 64, 4096, &result_bytes);
1058
0
        int policy_result = parse_policy_response(result, result_bytes, true, user, target_uuid, target, requested_target, autostart);
1059
0
        if (policy_result != RESPONSE_MALFORMED) {
1060
            // This leaks 'result', but as the code execs later anyway this isn't a problem.
1061
            // 'result' cannot be freed as 'user', 'target', and 'requested_target' point into
1062
            // the same buffer.
1063
0
            return policy_result;
1064
0
        }
1065
0
        free(result);
1066
0
    } else {
1067
0
        PERROR("connection to socket failed");
1068
0
        assert(connect_result == -1);
1069
0
        if (close(daemon_socket))
1070
0
            abort();
1071
0
    }
1072
0
    int fds[2];
1073
0
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds)) {
1074
0
        PERROR("socketpair()");
1075
0
        daemon__exit(QREXEC_EXIT_PROBLEM);
1076
0
    }
1077
0
    daemon_socket = fds[0];
1078
1079
0
    pid = fork();
1080
0
    switch (pid) {
1081
0
        case -1:
1082
0
            LOGE(ERROR, "Could not fork!");
1083
0
            daemon__exit(QREXEC_EXIT_PROBLEM);
1084
0
        case 0:
1085
0
            if (close(fds[0]))
1086
0
                daemon__exit(QREXEC_EXIT_PROBLEM);
1087
0
            if (dup2(fds[1], 0) != 0 || dup2(fds[1], 1) != 1) {
1088
0
                PERROR("dup2()");
1089
0
                daemon__exit(QREXEC_EXIT_PROBLEM);
1090
0
            }
1091
0
            if (close(fds[1]))
1092
0
                abort();
1093
0
            char remote_domain_id_str[10];
1094
0
            int v = snprintf(remote_domain_id_str, sizeof(remote_domain_id_str), "%d",
1095
0
                    remote_domain_id);
1096
0
            if (v >= 1 && v < (int)sizeof(remote_domain_id_str)) {
1097
0
                execl(policy_program,
1098
0
                        "qrexec-policy-exec",
1099
0
                        "--",
1100
0
                        remote_domain_name,
1101
0
                        target_domain,
1102
0
                        service_name,
1103
0
                        NULL);
1104
0
                PERROR("execl");
1105
0
            } else {
1106
0
                PERROR("snprintf");
1107
0
            }
1108
0
            daemon__exit(QREXEC_EXIT_PROBLEM);
1109
0
        default:
1110
0
            if (close(fds[1]))
1111
0
                abort();
1112
0
            size_t result_bytes;
1113
0
            int status;
1114
            // this closes the socket
1115
0
            char *result = qubes_read_all_to_malloc(daemon_socket, 64, 4096, &result_bytes);
1116
0
            do {
1117
0
                if (waitpid(pid, &status, 0) != pid) {
1118
0
                    PERROR("waitpid");
1119
0
                    daemon__exit(QREXEC_EXIT_PROBLEM);
1120
0
                }
1121
0
            } while (!WIFEXITED(status));
1122
0
            if (WEXITSTATUS(status) != 0) {
1123
0
                LOG(ERROR, "qrexec-policy-exec failed: %d", WEXITSTATUS(status));
1124
0
                daemon__exit(QREXEC_EXIT_PROBLEM);
1125
0
            }
1126
            // This leaks 'result', but as the code execs later anyway this isn't a problem.
1127
            // 'result' cannot be freed as 'user', 'target', and 'requested_target' point into
1128
            // the same buffer.
1129
0
            return parse_policy_response(result, result_bytes, true, user, target_uuid, target, requested_target, autostart);
1130
0
    }
1131
0
}
1132
1133
/* called from do_fork_exec */
1134
static _Noreturn void do_exec(const char *prog, const char *username __attribute__((unused)))
1135
0
{
1136
    /* avoid calling qubes-rpc-multiplexer through shell */
1137
0
    exec_qubes_rpc_if_requested(prog, environ);
1138
1139
    /* if above haven't executed qubes-rpc-multiplexer, pass it to shell */
1140
0
    execl("/bin/bash", "bash", "-c", prog, NULL);
1141
0
    PERROR("exec bash");
1142
0
    _exit(QREXEC_EXIT_PROBLEM);
1143
0
}
1144
1145
_Noreturn static void handle_execute_service_child(
1146
        const int remote_domain_id,
1147
        const char *remote_domain_name,
1148
        const char *target_domain,
1149
        const char *service_name,
1150
0
        const struct service_params *request_id) {
1151
0
    int i;
1152
1153
#ifdef SYS_close_range
1154
    int close_range_res = syscall(SYS_close_range, 3, ~0U, 0);
1155
#else
1156
0
    int close_range_res = -1;
1157
0
#endif
1158
0
    if (close_range_res != 0)
1159
0
        for (i = 3; i < MAX_FDS; i++)
1160
0
            close(i);
1161
1162
0
    char *user = NULL, *target = NULL, *requested_target = NULL, *target_uuid = NULL;
1163
0
    int autostart = -1;
1164
0
    int policy_response =
1165
0
        connect_daemon_socket(remote_domain_name, target_domain, service_name,
1166
0
                              &user, &target_uuid, &target, &requested_target, &autostart);
1167
1168
0
    if (policy_response != RESPONSE_ALLOW)
1169
0
        daemon__exit(QREXEC_EXIT_REQUEST_REFUSED);
1170
1171
    /* Replace the target domain with the version normalized by the policy engine */
1172
0
    target_domain = requested_target;
1173
0
    char *cmd = NULL;
1174
1175
    /*
1176
     * If there was no service argument, pretend that an empty argument was
1177
     * provided by appending "+" to the service name.
1178
     */
1179
0
    const char *const trailer = strchr(service_name, '+') ? "" : "+";
1180
1181
    /* Check if the target is dom0, which requires special handling. */
1182
0
    bool target_is_dom0 = target_refers_to_dom0(target);
1183
0
    if (target_is_dom0) {
1184
0
        char *type;
1185
0
        bool target_is_keyword = target_domain[0] == '@';
1186
0
        if (target_is_keyword) {
1187
0
            target_domain++;
1188
0
            type = "keyword";
1189
0
        } else {
1190
0
            type = "name";
1191
0
        }
1192
0
        if (asprintf(&cmd, "QUBESRPC %s%s %s %s %s",
1193
0
                     service_name,
1194
0
                     trailer,
1195
0
                     remote_domain_name,
1196
0
                     type,
1197
0
                     target_domain) <= 0)
1198
0
            daemon__exit(QREXEC_EXIT_PROBLEM);
1199
0
        register_exec_func(&do_exec);
1200
0
        daemon__exit(run_qrexec_to_dom0(request_id,
1201
0
                           remote_domain_id,
1202
0
                           remote_domain_name,
1203
0
                           cmd,
1204
0
                           5 /* 5 second timeout */,
1205
0
                           false /* return 0 not remote status code */));
1206
0
    } else {
1207
0
        bool const use_uuid = target_uuid != NULL;
1208
0
        const char *const selected_target = use_uuid ? target_uuid : target;
1209
0
        int service_length = asprintf(&cmd, "%s:QUBESRPC %s%s %s",
1210
0
                                      user,
1211
0
                                      service_name,
1212
0
                                      trailer,
1213
0
                                      remote_domain_name);
1214
0
        if (service_length < 0)
1215
0
            daemon__exit(QREXEC_EXIT_PROBLEM);
1216
0
        daemon__exit(qrexec_execute_vm(selected_target, autostart,
1217
0
                                       remote_domain_id,
1218
0
                                       cmd,
1219
0
                                       (size_t)service_length + 1,
1220
0
                                       request_id->ident, false, false,
1221
0
                                       use_uuid)
1222
0
                ? 0 : QREXEC_EXIT_PROBLEM);
1223
0
    }
1224
0
}
1225
1226
static void handle_execute_service(
1227
        const int remote_domain_id,
1228
        const char *remote_domain_name,
1229
        const char *target_domain,
1230
        const char *service_name,
1231
        const struct service_params *request_id)
1232
141
{
1233
141
    int policy_pending_slot;
1234
141
    pid_t pid;
1235
141
    struct sigaction sa = { .sa_handler = SIG_DFL };
1236
1237
141
    policy_pending_slot = find_policy_pending_slot();
1238
141
    if (policy_pending_slot < 0) {
1239
0
        LOG(ERROR, "Service request denied, too many pending requests");
1240
0
        send_service_refused(vchan, request_id);
1241
0
        return;
1242
0
    }
1243
1244
141
    switch (pid=fork()) {
1245
0
        case -1:
1246
0
            PERROR("fork");
1247
0
            exit(1);
1248
0
        case 0:
1249
0
            if (atexit(null_exit))
1250
0
                _exit(QREXEC_EXIT_PROBLEM);
1251
0
            if (sigaction(SIGTERM, &sa, NULL))
1252
0
                LOG(WARNING, "Failed to restore SIGTERM handler: %d", errno);
1253
0
            handle_execute_service_child(remote_domain_id, remote_domain_name,
1254
0
                                         target_domain, service_name, request_id);
1255
0
            abort();
1256
141
        default:
1257
141
            policy_pending[policy_pending_slot].pid = pid;
1258
141
            policy_pending[policy_pending_slot].params = *request_id;
1259
141
            policy_pending[policy_pending_slot].response_sent = RESPONSE_PENDING;
1260
141
            return;
1261
141
    }
1262
141
}
1263
1264
1265
static void handle_connection_terminated(void)
1266
97
{
1267
97
    struct exec_params untrusted_params, params;
1268
1269
97
    if (libvchan_recv(vchan, &untrusted_params, sizeof(untrusted_params))
1270
97
            != sizeof(untrusted_params))
1271
5
        handle_vchan_error("recv params");
1272
    /* sanitize start */
1273
97
    if (untrusted_params.connect_port < VCHAN_BASE_DATA_PORT ||
1274
97
            untrusted_params.connect_port >= VCHAN_BASE_DATA_PORT+MAX_CLIENTS) {
1275
42
        LOG(ERROR, "Invalid port in MSG_CONNECTION_TERMINATED (%d)",
1276
42
                untrusted_params.connect_port);
1277
42
        exit(1);
1278
42
    }
1279
    /* untrusted_params.connect_domain even if invalid will not harm - in worst
1280
     * case the port will not be released */
1281
97
    params = untrusted_params;
1282
    /* sanitize end */
1283
97
    release_vchan_port(params.connect_port, params.connect_domain);
1284
97
}
1285
1286
static void sanitize_message_from_agent(struct msg_header *untrusted_header)
1287
794
{
1288
794
    switch (untrusted_header->type) {
1289
127
        case MSG_TRIGGER_SERVICE:
1290
127
            if (protocol_version >= QREXEC_PROTOCOL_V3) {
1291
1
                LOG(ERROR, "agent sent (old) MSG_TRIGGER_SERVICE "
1292
1
                    "although it uses protocol %d", protocol_version);
1293
1
                exit(1);
1294
1
            }
1295
127
            if (untrusted_header->len != sizeof(struct trigger_service_params)) {
1296
40
                LOG(ERROR, "agent sent invalid MSG_TRIGGER_SERVICE packet");
1297
40
                exit(1);
1298
40
            }
1299
127
            break;
1300
461
        case MSG_TRIGGER_SERVICE3:
1301
461
            if (protocol_version < QREXEC_PROTOCOL_V3) {
1302
1
                LOG(ERROR, "agent sent (new) MSG_TRIGGER_SERVICE3 "
1303
1
                    "although it uses protocol %d", protocol_version);
1304
1
                exit(1);
1305
1
            }
1306
461
            if (untrusted_header->len <= sizeof(struct trigger_service_params3)) {
1307
8
                LOG(ERROR, "agent sent invalid MSG_TRIGGER_SERVICE3 packet");
1308
8
                exit(1);
1309
8
            }
1310
461
            if (untrusted_header->len - sizeof(struct trigger_service_params3)
1311
461
                    > MAX_SERVICE_NAME_LEN) {
1312
48
                LOG(ERROR, "agent sent too large MSG_TRIGGER_SERVICE3 packet");
1313
48
                exit(1);
1314
48
            }
1315
461
            break;
1316
137
        case MSG_CONNECTION_TERMINATED:
1317
137
            if (untrusted_header->len != sizeof(struct exec_params)) {
1318
40
                LOG(ERROR, "agent sent invalid MSG_CONNECTION_TERMINATED packet");
1319
40
                exit(1);
1320
40
            }
1321
137
            break;
1322
69
        default:
1323
69
            LOG(ERROR, "unknown mesage type 0x%x from agent",
1324
69
                    untrusted_header->type);
1325
69
            exit(1);
1326
794
    }
1327
794
}
1328
1329
static bool validate_request_id(struct service_params *untrusted_params, const char *msg)
1330
436
{
1331
9.23k
    for (size_t i = 0; i < sizeof(untrusted_params->ident); ++i) {
1332
9.13k
        switch (untrusted_params->ident[i]) {
1333
1.46k
        case '0' ... '9':
1334
4.99k
        case 'A' ... 'Z':
1335
8.05k
        case 'a' ... 'z':
1336
8.17k
        case '_':
1337
8.39k
        case '-':
1338
8.70k
        case '.':
1339
8.79k
        case ' ':
1340
8.79k
            continue;
1341
224
        case '\0': {
1342
224
            size_t terminator_offset = i;
1343
            /* Ensure that nothing non-NUL follows the terminator */
1344
2.72k
            for (i++; i < sizeof(untrusted_params->ident); i++) {
1345
2.54k
                if (untrusted_params->ident[i]) {
1346
47
                    LOG(ERROR, "Non-NUL byte %u at offset %zu follows NUL terminator at offset %zu in message %s",
1347
47
                        untrusted_params->ident[i], i, terminator_offset, msg);
1348
47
                    return false;
1349
47
                }
1350
2.54k
            }
1351
177
            return true;
1352
224
        }
1353
113
        default:
1354
113
            LOG(ERROR, "Bad byte %u at offset %zu for message %s", untrusted_params->ident[i], i, msg);
1355
113
            return false;
1356
9.13k
        }
1357
9.13k
    }
1358
99
    LOG(ERROR, "No NUL terminator in message %s", msg);
1359
99
    return false; // no NUL terminator
1360
436
}
1361
1362
576
#define ENSURE_NULL_TERMINATED(x) x[sizeof(x)-1] = 0
1363
1364
static bool validate_service_name(char *untrusted_service_name)
1365
148
{
1366
148
    switch (untrusted_service_name[0]) {
1367
5
    case '\0':
1368
5
        LOG(ERROR, "Empty service name not allowed");
1369
5
        return false;
1370
2
    case '+':
1371
2
        LOG(ERROR, "Service name must not start with '+'");
1372
2
        return false;
1373
141
    default:
1374
141
        sanitize_name(untrusted_service_name, "+");
1375
141
        return true;
1376
148
    }
1377
148
}
1378
1379
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
1380
static
1381
#endif
1382
void handle_message_from_agent(void)
1383
802
{
1384
802
    struct msg_header hdr, untrusted_hdr;
1385
1386
802
    if (libvchan_recv(vchan, &untrusted_hdr, sizeof(untrusted_hdr))
1387
802
            != sizeof(untrusted_hdr))
1388
8
        handle_vchan_error("recv hdr");
1389
    /* sanitize start */
1390
802
    sanitize_message_from_agent(&untrusted_hdr);
1391
802
    hdr = untrusted_hdr;
1392
    /* sanitize end */
1393
1394
    //      fprintf(stderr, "got %x %x %x\n", hdr.type, hdr.client_id,
1395
    //              hdr.len);
1396
1397
802
    switch (hdr.type) {
1398
86
        case MSG_TRIGGER_SERVICE: {
1399
86
            struct trigger_service_params untrusted_params, params;
1400
86
            if (libvchan_recv(vchan, &untrusted_params, sizeof(untrusted_params))
1401
86
                    != sizeof(untrusted_params))
1402
15
                handle_vchan_error("recv params");
1403
1404
            /* sanitize start */
1405
86
            ENSURE_NULL_TERMINATED(untrusted_params.service_name);
1406
86
            ENSURE_NULL_TERMINATED(untrusted_params.target_domain);
1407
86
            sanitize_name(untrusted_params.service_name, "+");
1408
86
            sanitize_name(untrusted_params.target_domain, "@:");
1409
86
            if (!validate_request_id(&untrusted_params.request_id, "MSG_TRIGGER_SERVICE")) {
1410
31
                send_service_refused(vchan, &untrusted_params.request_id);
1411
31
                return;
1412
31
            }
1413
55
            if (!validate_service_name(untrusted_params.service_name)) {
1414
2
                send_service_refused(vchan, &untrusted_params.request_id);
1415
2
                return;
1416
2
            }
1417
53
            params = untrusted_params;
1418
            /* sanitize end */
1419
1420
53
            handle_execute_service(remote_domain_id, remote_domain_name,
1421
53
                    params.target_domain,
1422
53
                    params.service_name,
1423
53
                    &params.request_id);
1424
53
            return;
1425
55
        }
1426
404
        case MSG_TRIGGER_SERVICE3: {
1427
404
            struct trigger_service_params3 *untrusted_params3, *params3;
1428
1429
404
            untrusted_params3 = malloc(hdr.len);
1430
404
            if (!untrusted_params3)
1431
0
                handle_vchan_error("malloc(service_name)");
1432
1433
404
            if (libvchan_recv(vchan, untrusted_params3, hdr.len)
1434
404
                    != (int)hdr.len) {
1435
39
                free(untrusted_params3);
1436
39
                handle_vchan_error("recv params3(service_name)");
1437
39
            }
1438
404
            size_t const service_name_len = hdr.len - sizeof(*untrusted_params3) - 1;
1439
1440
            /* sanitize start */
1441
404
            ENSURE_NULL_TERMINATED(untrusted_params3->target_domain);
1442
404
            sanitize_name(untrusted_params3->target_domain, "@:");
1443
404
            if (!validate_request_id(&untrusted_params3->request_id, "MSG_TRIGGER_SERVICE3"))
1444
228
                goto fail3;
1445
176
            if (untrusted_params3->service_name[service_name_len] != 0) {
1446
11
                LOG(ERROR, "Service name not NUL-terminated");
1447
11
                goto fail3;
1448
11
            }
1449
165
            size_t const nul_offset = strlen(untrusted_params3->service_name);
1450
165
            if (nul_offset != service_name_len) {
1451
18
                LOG(ERROR, "Service name contains NUL byte at offset %zu", nul_offset);
1452
18
                goto fail3;
1453
18
            }
1454
147
            if (!validate_service_name(untrusted_params3->service_name))
1455
5
                goto fail3;
1456
142
            params3 = untrusted_params3;
1457
142
            untrusted_params3 = NULL;
1458
            /* sanitize end */
1459
1460
142
            handle_execute_service(remote_domain_id, remote_domain_name,
1461
142
                    params3->target_domain,
1462
142
                    params3->service_name,
1463
142
                    &params3->request_id);
1464
142
            free(params3);
1465
142
            return;
1466
262
fail3:
1467
262
            send_service_refused(vchan, &untrusted_params3->request_id);
1468
262
            free(untrusted_params3);
1469
262
            return;
1470
147
        }
1471
97
        case MSG_CONNECTION_TERMINATED:
1472
97
            handle_connection_terminated();
1473
97
            return;
1474
802
    }
1475
802
}
1476
1477
/* qrexec-agent has disconnected, cleanup local state and try to connect again.
1478
 * If remote domain dies, terminate qrexec-daemon.
1479
 */
1480
0
static int handle_agent_restart(int xid) {
1481
0
    size_t i;
1482
1483
    // Stop listening.
1484
0
    unlink_qrexec_socket();
1485
0
    close(qrexec_daemon_unix_socket_fd);
1486
1487
    /* Close old (dead) vchan connection. */
1488
0
    libvchan_close(vchan);
1489
0
    vchan = NULL;
1490
1491
    /* Disconnect all local clients. This will look like all the qrexec
1492
     * connections were terminated, which isn't necessary true (established
1493
     * qrexec connection may survive qrexec-agent and qrexec-daemon restart),
1494
     * but we won't be notified about its termination. This may kill DispVM
1495
     * prematurely (if anyone restarts qrexec-agent inside DispVM), but it's
1496
     * better than the alternative (leaking DispVMs).
1497
     *
1498
     * But, do not mark related vchan ports as unused. Since we won't get call
1499
     * end notification, we don't know when such ports will really be unused.
1500
     */
1501
0
    for (i = 0; i < MAX_CLIENTS; i++) {
1502
0
        if (clients[i].state != CLIENT_INVALID)
1503
0
            terminate_client(i);
1504
0
    }
1505
1506
    /* Abort pending qrexec requests */
1507
0
    for (i = 0; i < MAX_CLIENTS; i++) {
1508
0
        if (policy_pending[i].pid != 0)
1509
0
            policy_pending[i].pid = 0;
1510
0
    }
1511
0
    policy_pending_max = -1;
1512
1513
    /* Restore default SIGTERM handling: libvchan_client_init() might block
1514
     * indefinitely, so we want the program to be killable.
1515
     */
1516
0
    if (signal(SIGTERM, SIG_DFL) == SIG_ERR)
1517
0
        err(1, "signal()");
1518
0
    if (terminate_requested)
1519
0
        return -1;
1520
1521
#ifdef COVERAGE
1522
    /* Dump coverage in case we are killed here. */
1523
    __gcov_dump();
1524
    __gcov_reset();
1525
#endif
1526
1527
0
    vchan = libvchan_client_init(remote_domain_id, VCHAN_BASE_PORT);
1528
0
    if (!vchan) {
1529
0
        PERROR("cannot connect to qrexec agent");
1530
0
        return -1;
1531
0
    }
1532
0
    if (handle_agent_hello(vchan, remote_domain_name) < 0) {
1533
0
        libvchan_close(vchan);
1534
0
        vchan = NULL;
1535
0
        return -1;
1536
0
    }
1537
0
    LOG(INFO, "qrexec-agent has reconnected");
1538
1539
0
    struct sigaction action = {
1540
0
        .sa_handler = signal_handler,
1541
0
        .sa_flags = 0,
1542
0
    };
1543
0
    if (sigaction(SIGTERM, &action, NULL))
1544
0
        err(1, "sigaction");
1545
1546
0
    qrexec_daemon_unix_socket_fd =
1547
0
        create_qrexec_socket(xid, remote_domain_name, remote_domain_uuid);
1548
0
    return 0;
1549
0
}
1550
1551
static struct option longopts[] = {
1552
    { "help", no_argument, 0, 'h' },
1553
    { "quiet", no_argument, 0, 'q' },
1554
    { "socket-dir", required_argument, 0, 'd' + 128 },
1555
    { "policy-program", required_argument, 0, 'p' },
1556
    { "direct", no_argument, 0, 'D' },
1557
    { "uuid", required_argument, 0, 'u' },
1558
    { NULL, 0, 0, 0 },
1559
};
1560
1561
static _Noreturn void usage(const char *argv0)
1562
0
{
1563
0
    fprintf(stderr, "usage: %s [options] domainid domain-name [default user]\n", argv0);
1564
0
    fprintf(stderr, "Options:\n");
1565
0
    fprintf(stderr, "  -h, --help - display usage\n");
1566
0
    fprintf(stderr, "  -q, --quiet - quiet mode\n");
1567
0
    fprintf(stderr, "  --socket-dir=PATH - directory for qrexec socket, default: %s\n",
1568
0
            QREXEC_DAEMON_SOCKET_DIR);
1569
0
    fprintf(stderr, "  -p, --policy-program=PATH - program to execute to check policy, default: %s\n",
1570
0
            QREXEC_POLICY_PROGRAM);
1571
0
    fprintf(stderr, "  -D, --direct - run directly, don't daemonize, log to stderr\n");
1572
0
    fprintf(stderr, "  -u, --uuid=UUID - domain UUID, mandatory\n");
1573
0
    exit(1);
1574
0
}
1575
1576
int main(int argc, char **argv)
1577
0
{
1578
0
    int i, opt;
1579
0
    sigset_t selectmask;
1580
0
    bool opt_direct = false;
1581
1582
0
    {
1583
0
        int null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
1584
0
        if (null_fd < 0)
1585
0
            err(1, "open(%s)", "/dev/null");
1586
0
        if (dup2(null_fd, 0) != 0)
1587
0
            err(1, "dup2(%d, 0)", null_fd);
1588
0
        if (close(null_fd) != 0)
1589
0
            err(1, "close(%d)", null_fd);
1590
0
    }
1591
1592
0
    setup_logging("qrexec-daemon");
1593
1594
0
    while ((opt=getopt_long(argc, argv, "hqp:Du:", longopts, NULL)) != -1) {
1595
0
        switch (opt) {
1596
0
            case 'q':
1597
0
                opt_quiet = 1;
1598
0
                break;
1599
0
            case 'd' + 128:
1600
0
                if ((socket_dir = strdup(optarg)) == NULL)
1601
0
                    err(1, "strdup()");
1602
0
                break;
1603
0
            case 'p':
1604
0
                if ((policy_program = strdup(optarg)) == NULL)
1605
0
                    err(1, "strdup()");
1606
0
                break;
1607
0
            case 'D':
1608
0
                opt_direct = 1;
1609
0
                break;
1610
0
            case 'u':
1611
0
                remote_domain_uuid = optarg;
1612
0
                break;
1613
0
            case 'h':
1614
0
            default: /* '?' */
1615
0
                usage(argv[0]);
1616
0
        }
1617
0
    }
1618
0
    if (argc - optind < 2 || argc - optind > 3) {
1619
0
        usage(argv[0]);
1620
0
    }
1621
0
    if (!remote_domain_uuid) {
1622
0
        fprintf(stderr, "Domain UUID option missing!\n");
1623
0
        usage(argv[0]);
1624
0
    }
1625
0
    remote_domain_id = atoi(argv[optind]);
1626
0
    remote_domain_name = argv[optind+1];
1627
0
    if (argc - optind >= 3)
1628
0
        default_user = argv[optind+2];
1629
0
    init(remote_domain_id, opt_direct);
1630
1631
0
    sigemptyset(&selectmask);
1632
0
    sigaddset(&selectmask, SIGCHLD);
1633
0
    sigprocmask(SIG_BLOCK, &selectmask, NULL);
1634
0
    sigemptyset(&selectmask);
1635
1636
    /*
1637
     * The main event loop. Waits for one of the following events:
1638
     * - message from client
1639
     * - message from agent
1640
     * - new client
1641
     * - child exited
1642
     */
1643
0
    while (!terminate_requested) {
1644
0
        struct timespec timeout = { 1, 0 };
1645
0
        int ret;
1646
1647
0
        if (child_exited)
1648
0
            reap_children();
1649
1650
0
        size_t nfds = 0;
1651
0
        struct pollfd fds[MAX_CLIENTS + 2];
1652
0
        fds[nfds++] = (struct pollfd) { libvchan_fd_for_select(vchan), POLLIN | POLLHUP, 0 };
1653
0
        if (libvchan_buffer_space(vchan) > (int)sizeof(struct msg_header)) {
1654
0
            assert(max_client_fd < MAX_CLIENTS);
1655
            // vchan not full, read from clients
1656
0
            fds[nfds++] = (struct pollfd) { qrexec_daemon_unix_socket_fd, POLLIN | POLLHUP, 0 };
1657
0
            for (i = 0; i <= max_client_fd; i++) {
1658
0
                if (clients[i].state != CLIENT_INVALID)
1659
0
                    fds[nfds++] = (struct pollfd) { i, POLLIN | POLLHUP, 0 };
1660
0
            }
1661
0
        }
1662
1663
0
        ret = ppoll_vchan(vchan, fds, nfds, &timeout, &selectmask);
1664
0
        if (ret < 0) {
1665
0
            if (errno == EINTR)
1666
0
                continue;
1667
0
            PERROR("ppoll");
1668
0
            return 1;
1669
0
        }
1670
1671
0
        if (!libvchan_is_open(vchan)) {
1672
0
            LOG(WARNING, "qrexec-agent has disconnected");
1673
0
            if (handle_agent_restart(remote_domain_id) < 0) {
1674
0
                LOG(ERROR, "Failed to reconnect to qrexec-agent, terminating");
1675
0
                return 1;
1676
0
            }
1677
            /* rdset may be outdated at this point, calculate it again. */
1678
0
            continue;
1679
0
        }
1680
1681
0
        if (nfds > 1 && fds[1].revents)
1682
0
            handle_new_client();
1683
1684
0
        while (libvchan_data_ready(vchan))
1685
0
            handle_message_from_agent();
1686
1687
0
        for (size_t i = 2; i < nfds; i++) {
1688
0
            if (fds[i].revents)
1689
0
                handle_message_from_client(fds[i].fd);
1690
0
        }
1691
0
    }
1692
1693
0
    if (vchan)
1694
0
        libvchan_close(vchan);
1695
1696
0
    return 0;
1697
0
}
1698
1699
// vim:ts=4:sw=4:et: