Coverage Report

Created: 2024-02-25 06:27

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