qrexec* tools, initial version
This commit is contained in:
parent
80826329a7
commit
b98dffc965
13
qrexec/Makefile
Normal file
13
qrexec/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
CC=gcc
|
||||
CFLAGS+=-g -Wall
|
||||
XENLIBS=-lvchan -lxenstore -lxenctrl
|
||||
|
||||
all: qrexec_daemon qrexec_agent qrexec_client
|
||||
qrexec_daemon: qrexec_daemon.o unix_server.o ioall.o txrx-vchan.o buffer.o write_stdin.o
|
||||
$(CC) -g -o qrexec_daemon qrexec_daemon.o unix_server.o ioall.o txrx-vchan.o write_stdin.o buffer.o $(XENLIBS)
|
||||
qrexec_agent: qrexec_agent.o ioall.o exec.o txrx-vchan.o write_stdin.o buffer.o
|
||||
$(CC) -g -o qrexec_agent qrexec_agent.o ioall.o exec.o txrx-vchan.o write_stdin.o buffer.o $(XENLIBS)
|
||||
qrexec_client: qrexec_client.o ioall.o exec.o
|
||||
$(CC) -g -o qrexec_client qrexec_client.o ioall.o exec.o
|
||||
clean:
|
||||
rm -f *.o *~ qrexec_daemon qrexec_agent qrexec_client
|
93
qrexec/buffer.c
Normal file
93
qrexec/buffer.c
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#define BUFFER_LIMIT 50000000
|
||||
static int total_mem;
|
||||
static char *limited_malloc(int len)
|
||||
{
|
||||
char *ret;
|
||||
total_mem += len;
|
||||
if (total_mem > BUFFER_LIMIT) {
|
||||
fprintf(stderr, "attempt to allocate >BUFFER_LIMIT\n");
|
||||
exit(1);
|
||||
}
|
||||
ret = malloc(len);
|
||||
if (!ret) {
|
||||
perror("malloc");
|
||||
exit(1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void limited_free(char *ptr, int len)
|
||||
{
|
||||
free(ptr);
|
||||
total_mem -= len;
|
||||
}
|
||||
|
||||
void buffer_init(struct buffer *b)
|
||||
{
|
||||
b->buflen = 0;
|
||||
b->data = NULL;
|
||||
}
|
||||
|
||||
void buffer_free(struct buffer *b)
|
||||
{
|
||||
if (b->buflen)
|
||||
limited_free(b->data, b->buflen);
|
||||
buffer_init(b);
|
||||
}
|
||||
|
||||
void buffer_append(struct buffer *b, char *data, int len)
|
||||
{
|
||||
int newsize = len + b->buflen;
|
||||
char *qdata = limited_malloc(len + b->buflen);
|
||||
memcpy(qdata, b->data, b->buflen);
|
||||
memcpy(qdata + b->buflen, data, len);
|
||||
buffer_free(b);
|
||||
b->buflen = newsize;
|
||||
b->data = qdata;
|
||||
}
|
||||
|
||||
void buffer_remove(struct buffer *b, int len)
|
||||
{
|
||||
int newsize = b->buflen - len;
|
||||
char *qdata = limited_malloc(newsize);
|
||||
memcpy(qdata, b->data + len, newsize);
|
||||
buffer_free(b);
|
||||
b->buflen = newsize;
|
||||
b->data = qdata;
|
||||
}
|
||||
|
||||
int buffer_len(struct buffer *b)
|
||||
{
|
||||
return b->buflen;
|
||||
}
|
||||
|
||||
void *buffer_data(struct buffer *b)
|
||||
{
|
||||
return b->data;
|
||||
}
|
32
qrexec/buffer.h
Normal file
32
qrexec/buffer.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
struct buffer {
|
||||
char *data;
|
||||
int buflen;
|
||||
};
|
||||
|
||||
void buffer_init(struct buffer *b);
|
||||
void buffer_free(struct buffer *b);
|
||||
void buffer_append(struct buffer *b, char *data, int len);
|
||||
void buffer_remove(struct buffer *b, int len);
|
||||
int buffer_len(struct buffer *b);
|
||||
void *buffer_data(struct buffer *b);
|
74
qrexec/exec.c
Normal file
74
qrexec/exec.c
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern void do_exec(char *);
|
||||
|
||||
void fix_fds(int fdin, int fdout, int fderr)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 256; i++)
|
||||
if (i != fdin && i != fdout && i != fderr)
|
||||
close(i);
|
||||
dup2(fdin, 0);
|
||||
dup2(fdout, 1);
|
||||
dup2(fderr, 2);
|
||||
close(fdin);
|
||||
close(fdout);
|
||||
if (fderr != 2)
|
||||
close(fderr);
|
||||
}
|
||||
|
||||
void do_fork_exec(char *cmdline, int *pid, int *stdin_fd, int *stdout_fd,
|
||||
int *stderr_fd)
|
||||
{
|
||||
int inpipe[2], outpipe[2], errpipe[2];
|
||||
|
||||
if (pipe(inpipe) || pipe(outpipe) || (stderr_fd && pipe(errpipe))) {
|
||||
perror("pipe");
|
||||
exit(1);
|
||||
}
|
||||
switch (*pid = fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(-1);
|
||||
case 0:
|
||||
if (stderr_fd) {
|
||||
fix_fds(inpipe[0], outpipe[1], errpipe[1]);
|
||||
} else
|
||||
fix_fds(inpipe[0], outpipe[1], 2);
|
||||
|
||||
do_exec(cmdline);
|
||||
exit(-1);
|
||||
default:;
|
||||
}
|
||||
close(inpipe[0]);
|
||||
close(outpipe[1]);
|
||||
*stdin_fd = inpipe[1];
|
||||
*stdout_fd = outpipe[0];
|
||||
if (stderr_fd) {
|
||||
close(errpipe[1]);
|
||||
*stderr_fd = errpipe[0];
|
||||
}
|
||||
}
|
49
qrexec/glue.h
Normal file
49
qrexec/glue.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/select.h>
|
||||
|
||||
void do_fork_exec(char *cmdline, int *pid, int *stdin_fd, int *stdout_fd,
|
||||
int *stderr_fd);
|
||||
int write_all(int fd, void *buf, int size);
|
||||
int read_all(int fd, void *buf, int size);
|
||||
int peer_server_init(int port);
|
||||
char *peer_client_init(int dom, int port);
|
||||
void wait_for_vchan_or_argfd(int max, fd_set * rdset, fd_set * wrset);
|
||||
int read_ready_vchan_ext();
|
||||
int read_all_vchan_ext(void *buf, int size);
|
||||
int write_all_vchan_ext(void *buf, int size);
|
||||
int buffer_space_vchan_ext();
|
||||
void fix_fds(int fdin, int fdout, int fderr);
|
||||
|
||||
int get_server_socket(int domid);
|
||||
int do_accept(int s);
|
||||
void set_nonblock(int fd);
|
||||
|
||||
enum {
|
||||
WRITE_STDIN_OK = 0x200,
|
||||
WRITE_STDIN_BUFFERED,
|
||||
WRITE_STDIN_ERROR
|
||||
};
|
||||
|
||||
int flush_client_data(int fd, int clid, struct buffer *buffer);
|
||||
int write_stdin(int fd, int clid, char *data, int len,
|
||||
struct buffer *buffer);
|
67
qrexec/ioall.c
Normal file
67
qrexec/ioall.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int write_all(int fd, void *buf, int size)
|
||||
{
|
||||
int written = 0;
|
||||
int ret;
|
||||
while (written < size) {
|
||||
ret = write(fd, (char *) buf + written, size - written);
|
||||
if (ret <= 0) {
|
||||
perror("write");
|
||||
return 0;
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
// fprintf(stderr, "sent %d bytes\n", size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_all(int fd, void *buf, int size)
|
||||
{
|
||||
int got_read = 0;
|
||||
int ret;
|
||||
while (got_read < size) {
|
||||
ret = read(fd, (char *) buf + got_read, size - got_read);
|
||||
if (ret == 0) {
|
||||
fprintf(stderr, "EOF\n");
|
||||
return 0;
|
||||
}
|
||||
if (ret < 0) {
|
||||
perror("read");
|
||||
return 0;
|
||||
}
|
||||
got_read += ret;
|
||||
}
|
||||
// fprintf(stderr, "read %d bytes\n", size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void set_nonblock(int fd)
|
||||
{
|
||||
int fl = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
||||
}
|
58
qrexec/qrexec.h
Normal file
58
qrexec/qrexec.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#define QREXEC_DAEMON_SOCKET_DIR "/var/run/qubes"
|
||||
#define MAX_FDS 256
|
||||
#define MAX_DATA_CHUNK 4096
|
||||
|
||||
#define REXEC_PORT 512
|
||||
|
||||
enum {
|
||||
MSG_CLIENT_TO_SERVER_EXEC_CMDLINE = 0x100,
|
||||
MSG_CLIENT_TO_SERVER_JUST_EXEC,
|
||||
|
||||
MSG_SERVER_TO_AGENT_EXEC_CMDLINE,
|
||||
MSG_SERVER_TO_AGENT_JUST_EXEC,
|
||||
MSG_SERVER_TO_AGENT_INPUT,
|
||||
MSG_SERVER_TO_AGENT_CLIENT_END,
|
||||
|
||||
MSG_XOFF,
|
||||
MSG_XON,
|
||||
|
||||
MSG_AGENT_TO_SERVER_STDOUT,
|
||||
MSG_AGENT_TO_SERVER_STDERR,
|
||||
MSG_AGENT_TO_SERVER_EXIT_CODE,
|
||||
|
||||
MSG_SERVER_TO_CLIENT_STDOUT,
|
||||
MSG_SERVER_TO_CLIENT_STDERR,
|
||||
MSG_SERVER_TO_CLIENT_EXIT_CODE
|
||||
};
|
||||
|
||||
struct server_header {
|
||||
unsigned int type;
|
||||
unsigned int clid;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
struct client_header {
|
||||
unsigned int type;
|
||||
unsigned int len;
|
||||
};
|
452
qrexec/qrexec_agent.c
Normal file
452
qrexec/qrexec_agent.c
Normal file
@ -0,0 +1,452 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include "qrexec.h"
|
||||
#include "buffer.h"
|
||||
#include "glue.h"
|
||||
|
||||
enum fdtype {
|
||||
FDTYPE_INVALID,
|
||||
FDTYPE_STDOUT,
|
||||
FDTYPE_STDERR
|
||||
};
|
||||
|
||||
struct _process_fd {
|
||||
int clid;
|
||||
int type;
|
||||
int is_blocked;
|
||||
};
|
||||
struct _client_info {
|
||||
int stdin_fd;
|
||||
int stdout_fd;
|
||||
int stderr_fd;
|
||||
|
||||
int pid;
|
||||
int is_blocked;
|
||||
struct buffer buffer;
|
||||
};
|
||||
|
||||
int max_process_fd = -1;
|
||||
|
||||
/* indexed by file descriptor */
|
||||
struct _process_fd process_fd[MAX_FDS];
|
||||
|
||||
/* indexed by client id, which is descriptor number of a client in daemon */
|
||||
struct _client_info client_info[MAX_FDS];
|
||||
|
||||
void init()
|
||||
{
|
||||
peer_server_init(REXEC_PORT);
|
||||
}
|
||||
|
||||
void do_exec(char *cmd)
|
||||
{
|
||||
char *sep = index(cmd, ':');
|
||||
if (!sep) {
|
||||
fprintf(stderr,
|
||||
"cmdline is supposed to be in user:command form\n");
|
||||
exit(1);
|
||||
}
|
||||
*sep = 0;
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
|
||||
execl("/bin/su", "su", "-", cmd, "-c", sep + 1, NULL);
|
||||
perror("execl");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void handle_just_exec(int clid, int len)
|
||||
{
|
||||
char buf[len];
|
||||
int fdn, pid;
|
||||
|
||||
read_all_vchan_ext(buf, len);
|
||||
switch (pid=fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(1);
|
||||
case 0:
|
||||
fdn = open("/dev/null", O_RDWR);
|
||||
fix_fds(fdn, fdn, fdn);
|
||||
do_exec(buf);
|
||||
perror("execl");
|
||||
exit(1);
|
||||
default:;
|
||||
}
|
||||
fprintf(stderr, "executed (nowait) %s pid %d\n", buf, pid);
|
||||
}
|
||||
|
||||
void handle_exec(int clid, int len)
|
||||
{
|
||||
char buf[len];
|
||||
int pid, stdin_fd, stdout_fd, stderr_fd;
|
||||
|
||||
read_all_vchan_ext(buf, len);
|
||||
|
||||
do_fork_exec(buf, &pid, &stdin_fd, &stdout_fd, &stderr_fd);
|
||||
|
||||
process_fd[stdout_fd].clid = clid;
|
||||
process_fd[stdout_fd].type = FDTYPE_STDOUT;
|
||||
process_fd[stdout_fd].is_blocked = 0;
|
||||
process_fd[stderr_fd].clid = clid;
|
||||
process_fd[stderr_fd].type = FDTYPE_STDERR;
|
||||
process_fd[stderr_fd].is_blocked = 0;
|
||||
|
||||
if (stderr_fd > max_process_fd)
|
||||
max_process_fd = stderr_fd;
|
||||
if (stdout_fd > max_process_fd)
|
||||
max_process_fd = stdout_fd;
|
||||
|
||||
set_nonblock(stdin_fd);
|
||||
|
||||
client_info[clid].stdin_fd = stdin_fd;
|
||||
client_info[clid].stdout_fd = stdout_fd;
|
||||
client_info[clid].stderr_fd = stderr_fd;
|
||||
client_info[clid].pid = pid;
|
||||
client_info[clid].is_blocked = 0;
|
||||
buffer_init(&client_info[clid].buffer);
|
||||
|
||||
fprintf(stderr, "executed %s pid %d\n", buf, pid);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void update_max_process_fd()
|
||||
{
|
||||
int i;
|
||||
for (i = max_process_fd;
|
||||
process_fd[i].type == FDTYPE_INVALID && i >= 0; i--);
|
||||
max_process_fd = i;
|
||||
}
|
||||
|
||||
void send_exit_code(int clid, int status)
|
||||
{
|
||||
struct server_header s_hdr;
|
||||
s_hdr.type = MSG_AGENT_TO_SERVER_EXIT_CODE;
|
||||
s_hdr.clid = clid;
|
||||
s_hdr.len = sizeof status;
|
||||
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
write_all_vchan_ext(&status, sizeof(status));
|
||||
fprintf(stderr, "send exit code for clid %d pid %d\n", clid,
|
||||
client_info[clid].pid);
|
||||
}
|
||||
|
||||
|
||||
// erase process data structures, possibly forced by remote
|
||||
void remove_process(int clid, int status)
|
||||
{
|
||||
int i;
|
||||
if (!client_info[clid].pid)
|
||||
return;
|
||||
kill(client_info[clid].pid, SIGKILL);
|
||||
|
||||
if (status != -1)
|
||||
send_exit_code(clid, status);
|
||||
|
||||
|
||||
close(client_info[clid].stdin_fd);
|
||||
client_info[clid].pid = 0;
|
||||
client_info[clid].stdin_fd = -1;
|
||||
client_info[clid].is_blocked = 0;
|
||||
buffer_free(&client_info[clid].buffer);
|
||||
|
||||
for (i = 0; i <= max_process_fd; i++)
|
||||
if (process_fd[i].type != FDTYPE_INVALID
|
||||
&& process_fd[i].clid == clid) {
|
||||
process_fd[i].type = FDTYPE_INVALID;
|
||||
process_fd[i].clid = -1;
|
||||
process_fd[i].is_blocked = 0;
|
||||
close(i);
|
||||
}
|
||||
update_max_process_fd();
|
||||
}
|
||||
|
||||
void handle_input(int clid, int len)
|
||||
{
|
||||
char buf[len];
|
||||
|
||||
read_all_vchan_ext(buf, len);
|
||||
if (!client_info[clid].pid)
|
||||
return;
|
||||
|
||||
if (len == 0) {
|
||||
close(client_info[clid].stdin_fd);
|
||||
client_info[clid].stdin_fd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (write_stdin
|
||||
(client_info[clid].stdin_fd, clid, buf, len,
|
||||
&client_info[clid].buffer)) {
|
||||
case WRITE_STDIN_OK:
|
||||
break;
|
||||
case WRITE_STDIN_BUFFERED:
|
||||
client_info[clid].is_blocked = 1;
|
||||
break;
|
||||
case WRITE_STDIN_ERROR:
|
||||
remove_process(clid, 128);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown write_stdin?\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void set_blocked_outerr(int clid, int val)
|
||||
{
|
||||
process_fd[client_info[clid].stdout_fd].is_blocked = val;
|
||||
process_fd[client_info[clid].stderr_fd].is_blocked = val;
|
||||
}
|
||||
|
||||
void handle_server_data()
|
||||
{
|
||||
struct server_header s_hdr;
|
||||
read_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
|
||||
// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.clid,
|
||||
// s_hdr.len);
|
||||
|
||||
switch (s_hdr.type) {
|
||||
case MSG_XON:
|
||||
set_blocked_outerr(s_hdr.clid, 0);
|
||||
break;
|
||||
case MSG_XOFF:
|
||||
set_blocked_outerr(s_hdr.clid, 1);
|
||||
break;
|
||||
case MSG_SERVER_TO_AGENT_EXEC_CMDLINE:
|
||||
handle_exec(s_hdr.clid, s_hdr.len);
|
||||
break;
|
||||
case MSG_SERVER_TO_AGENT_JUST_EXEC:
|
||||
handle_just_exec(s_hdr.clid, s_hdr.len);
|
||||
break;
|
||||
case MSG_SERVER_TO_AGENT_INPUT:
|
||||
handle_input(s_hdr.clid, s_hdr.len);
|
||||
break;
|
||||
case MSG_SERVER_TO_AGENT_CLIENT_END:
|
||||
remove_process(s_hdr.clid, -1);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "msg type from daemon is %d ?\n",
|
||||
s_hdr.type);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_process_data(int fd)
|
||||
{
|
||||
struct server_header s_hdr;
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
int ret;
|
||||
int len;
|
||||
|
||||
len = buffer_space_vchan_ext();
|
||||
if (len <= sizeof s_hdr)
|
||||
return;
|
||||
|
||||
ret = read(fd, buf, len - sizeof s_hdr);
|
||||
s_hdr.clid = process_fd[fd].clid;
|
||||
|
||||
if (process_fd[fd].type == FDTYPE_STDOUT)
|
||||
s_hdr.type = MSG_AGENT_TO_SERVER_STDOUT;
|
||||
else if (process_fd[fd].type == FDTYPE_STDERR)
|
||||
s_hdr.type = MSG_AGENT_TO_SERVER_STDERR;
|
||||
else {
|
||||
fprintf(stderr, "fd=%d, clid=%d, type=%d ?\n", fd,
|
||||
process_fd[fd].clid, process_fd[fd].type);
|
||||
exit(1);
|
||||
}
|
||||
s_hdr.len = ret;
|
||||
if (ret >= 0) {
|
||||
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
write_all_vchan_ext(buf, ret);
|
||||
}
|
||||
if (ret == 0) {
|
||||
process_fd[fd].type = FDTYPE_INVALID;
|
||||
process_fd[fd].clid = -1;
|
||||
process_fd[fd].is_blocked = 0;
|
||||
close(fd);
|
||||
update_max_process_fd();
|
||||
}
|
||||
if (ret < 0)
|
||||
remove_process(process_fd[fd].clid, 127);
|
||||
}
|
||||
|
||||
volatile int child_exited;
|
||||
|
||||
void sigchld_handler(int x)
|
||||
{
|
||||
child_exited = 1;
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
}
|
||||
|
||||
int find_info(int pid)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_FDS; i++)
|
||||
if (client_info[i].pid == pid)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void handle_process_data_all(fd_set * select_fds)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i <= max_process_fd; i++)
|
||||
if (process_fd[i].type != FDTYPE_INVALID
|
||||
&& FD_ISSET(i, select_fds))
|
||||
handle_process_data(i);
|
||||
}
|
||||
|
||||
|
||||
void flush_out_err(int clid)
|
||||
{
|
||||
fd_set select_set;
|
||||
int fd_max = -1;
|
||||
int i;
|
||||
int ret;
|
||||
struct timeval tv;
|
||||
for (;;) {
|
||||
FD_ZERO(&select_set);
|
||||
for (i = 0; i <= max_process_fd; i++) {
|
||||
if (process_fd[i].type != FDTYPE_INVALID
|
||||
&& !process_fd[i].is_blocked
|
||||
&& process_fd[i].clid == clid) {
|
||||
FD_SET(i, &select_set);
|
||||
fd_max = i;
|
||||
}
|
||||
}
|
||||
if (fd_max == -1)
|
||||
return;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
ret = select(fd_max + 1, &select_set, NULL, NULL, &tv);
|
||||
if (ret < 0 && errno != EINTR) {
|
||||
perror("select");
|
||||
exit(1);
|
||||
}
|
||||
if (!ret)
|
||||
return;
|
||||
handle_process_data_all(&select_set);
|
||||
}
|
||||
}
|
||||
|
||||
void reap_children()
|
||||
{
|
||||
int status;
|
||||
int pid;
|
||||
int clid;
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
||||
clid = find_info(pid);
|
||||
if (clid < 0)
|
||||
continue;
|
||||
flush_out_err(clid);
|
||||
remove_process(clid, status);
|
||||
}
|
||||
child_exited = 0;
|
||||
}
|
||||
|
||||
int fill_fds_for_select(fd_set * rdset, fd_set * wrset)
|
||||
{
|
||||
int max = -1;
|
||||
int fd, i;
|
||||
FD_ZERO(rdset);
|
||||
FD_ZERO(wrset);
|
||||
|
||||
for (i = 0; i <= max_process_fd; i++)
|
||||
if (process_fd[i].type != FDTYPE_INVALID
|
||||
&& !process_fd[i].is_blocked) {
|
||||
FD_SET(i, rdset);
|
||||
max = i;
|
||||
}
|
||||
for (i = 0; i < MAX_FDS; i++)
|
||||
if (client_info[i].pid > 0 && client_info[i].is_blocked) {
|
||||
fd = client_info[i].stdin_fd;
|
||||
FD_SET(fd, wrset);
|
||||
if (fd > max)
|
||||
max = fd;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
void flush_client_data_agent(int clid)
|
||||
{
|
||||
struct _client_info *info = &client_info[clid];
|
||||
switch (flush_client_data(info->stdin_fd, clid, &info->buffer)) {
|
||||
case WRITE_STDIN_OK:
|
||||
info->is_blocked = 0;
|
||||
break;
|
||||
case WRITE_STDIN_ERROR:
|
||||
remove_process(clid, 128);
|
||||
break;
|
||||
case WRITE_STDIN_BUFFERED:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown flush_client_data?\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
fd_set rdset, wrset;
|
||||
int max;
|
||||
int i;
|
||||
|
||||
init();
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
|
||||
for (;;) {
|
||||
max = fill_fds_for_select(&rdset, &wrset);
|
||||
if (buffer_space_vchan_ext() <=
|
||||
sizeof(struct server_header))
|
||||
FD_ZERO(&rdset);
|
||||
|
||||
wait_for_vchan_or_argfd(max, &rdset, &wrset);
|
||||
|
||||
while (read_ready_vchan_ext())
|
||||
handle_server_data();
|
||||
|
||||
handle_process_data_all(&rdset);
|
||||
for (i = 0; i <= MAX_FDS; i++)
|
||||
if (client_info[i].pid > 0
|
||||
&& client_info[i].is_blocked
|
||||
&& FD_ISSET(client_info[i].stdin_fd, &wrset))
|
||||
flush_client_data_agent(i);
|
||||
|
||||
if (child_exited)
|
||||
reap_children();
|
||||
}
|
||||
}
|
236
qrexec/qrexec_client.c
Normal file
236
qrexec/qrexec_client.c
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "qrexec.h"
|
||||
#include "buffer.h"
|
||||
#include "glue.h"
|
||||
|
||||
int connect_unix_socket(int domid)
|
||||
{
|
||||
int s, len;
|
||||
struct sockaddr_un remote;
|
||||
|
||||
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
remote.sun_family = AF_UNIX;
|
||||
snprintf(remote.sun_path, sizeof remote.sun_path,
|
||||
QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", domid);
|
||||
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
|
||||
if (connect(s, (struct sockaddr *) &remote, len) == -1) {
|
||||
perror("connect");
|
||||
exit(1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void do_exec(char *prog)
|
||||
{
|
||||
execl("/bin/bash", "bash", "-c", prog, NULL);
|
||||
}
|
||||
|
||||
int local_stdin_fd, local_stdout_fd;
|
||||
|
||||
void prepare_local_fds(char *cmdline)
|
||||
{
|
||||
int pid;
|
||||
if (!cmdline) {
|
||||
local_stdin_fd = 1;
|
||||
local_stdout_fd = 0;
|
||||
return;
|
||||
}
|
||||
do_fork_exec(cmdline, &pid, &local_stdin_fd, &local_stdout_fd,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
void send_cmdline(int s, int type, char *cmdline)
|
||||
{
|
||||
struct client_header hdr;
|
||||
hdr.type = type;
|
||||
hdr.len = strlen(cmdline) + 1;
|
||||
if (!write_all(s, &hdr, sizeof(hdr))
|
||||
|| !write_all(s, cmdline, hdr.len)) {
|
||||
perror("write daemon");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_input(int s)
|
||||
{
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
int ret;
|
||||
ret = read(local_stdout_fd, buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
if (ret == 0) {
|
||||
local_stdout_fd = -1;
|
||||
shutdown(s, SHUT_WR);
|
||||
}
|
||||
if (!write_all(s, buf, ret)) {
|
||||
perror("write daemon");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_daemon_data(int s)
|
||||
{
|
||||
int status;
|
||||
struct client_header hdr;
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
|
||||
if (!read_all(s, &hdr, sizeof hdr)) {
|
||||
perror("read daemon");
|
||||
exit(1);
|
||||
}
|
||||
if (hdr.len > MAX_DATA_CHUNK) {
|
||||
fprintf(stderr, "client_header.len=%d\n", hdr.len);
|
||||
exit(1);
|
||||
}
|
||||
if (!read_all(s, buf, hdr.len)) {
|
||||
perror("read daemon");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (hdr.type) {
|
||||
case MSG_SERVER_TO_CLIENT_STDOUT:
|
||||
if (hdr.len == 0)
|
||||
close(local_stdin_fd);
|
||||
else
|
||||
write_all(local_stdin_fd, buf, hdr.len);
|
||||
break;
|
||||
case MSG_SERVER_TO_CLIENT_STDERR:
|
||||
write_all(2, buf, hdr.len);
|
||||
break;
|
||||
case MSG_SERVER_TO_CLIENT_EXIT_CODE:
|
||||
status = *(unsigned int *) buf;
|
||||
if (WIFEXITED(status))
|
||||
exit(WEXITSTATUS(status));
|
||||
else
|
||||
exit(255);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown msg %d\n", hdr.type);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// perhaps we could save a syscall if we include both sides in both
|
||||
// rdset and wrset; to be investigated
|
||||
void handle_daemon_only_until_writable(s)
|
||||
{
|
||||
fd_set rdset, wrset;
|
||||
|
||||
do {
|
||||
FD_ZERO(&rdset);
|
||||
FD_ZERO(&wrset);
|
||||
FD_SET(s, &rdset);
|
||||
FD_SET(s, &wrset);
|
||||
|
||||
if (select(s + 1, &rdset, &wrset, NULL, NULL) < 0) {
|
||||
perror("select");
|
||||
exit(1);
|
||||
}
|
||||
if (FD_ISSET(s, &rdset))
|
||||
handle_daemon_data(s);
|
||||
} while (!FD_ISSET(s, &wrset));
|
||||
}
|
||||
|
||||
void select_loop(int s)
|
||||
{
|
||||
fd_set select_set;
|
||||
int max;
|
||||
for (;;) {
|
||||
handle_daemon_only_until_writable(s);
|
||||
FD_ZERO(&select_set);
|
||||
FD_SET(s, &select_set);
|
||||
max = s;
|
||||
if (local_stdout_fd != -1) {
|
||||
FD_SET(local_stdout_fd, &select_set);
|
||||
if (s < local_stdout_fd)
|
||||
max = local_stdout_fd;
|
||||
}
|
||||
if (select(max + 1, &select_set, NULL, NULL, NULL) < 0) {
|
||||
perror("select");
|
||||
exit(1);
|
||||
}
|
||||
if (FD_ISSET(s, &select_set))
|
||||
handle_daemon_data(s);
|
||||
if (local_stdout_fd != -1
|
||||
&& FD_ISSET(local_stdout_fd, &select_set))
|
||||
handle_input(s);
|
||||
}
|
||||
}
|
||||
|
||||
void usage(char *name)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s -d domain_num [-l local_prog] -e remote_cmdline\n"
|
||||
"-e means exit after sending cmd\n", name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
int domid = 0;
|
||||
int s;
|
||||
int just_exec = 0;
|
||||
char *local_cmdline = NULL;
|
||||
while ((opt = getopt(argc, argv, "d:l:e")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
domid = atoi(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
local_cmdline = strdup(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
just_exec = 1;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
if (optind >= argc || !domid)
|
||||
usage(argv[0]);
|
||||
s = connect_unix_socket(domid);
|
||||
prepare_local_fds(local_cmdline);
|
||||
if (just_exec)
|
||||
send_cmdline(s, MSG_CLIENT_TO_SERVER_JUST_EXEC,
|
||||
argv[optind]);
|
||||
else {
|
||||
send_cmdline(s, MSG_CLIENT_TO_SERVER_EXEC_CMDLINE,
|
||||
argv[optind]);
|
||||
select_loop(s);
|
||||
}
|
||||
return 0;
|
||||
}
|
319
qrexec/qrexec_daemon.c
Normal file
319
qrexec/qrexec_daemon.c
Normal file
@ -0,0 +1,319 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include "qrexec.h"
|
||||
#include "buffer.h"
|
||||
#include "glue.h"
|
||||
|
||||
enum client_flags {
|
||||
CLIENT_INVALID = 0,
|
||||
CLIENT_CMDLINE = 1,
|
||||
CLIENT_DATA = 2,
|
||||
CLIENT_DONT_READ = 4,
|
||||
CLIENT_OUTQ_FULL = 8
|
||||
};
|
||||
|
||||
struct _client {
|
||||
int state;
|
||||
struct buffer buffer;
|
||||
};
|
||||
|
||||
struct _client clients[MAX_FDS];
|
||||
|
||||
int max_client_fd = -1;
|
||||
int server_fd;
|
||||
|
||||
void init(int xid)
|
||||
{
|
||||
if (xid <= 0) {
|
||||
fprintf(stderr, "domain id=0?\n");
|
||||
exit(1);
|
||||
}
|
||||
server_fd = get_server_socket(xid);
|
||||
peer_client_init(xid, REXEC_PORT);
|
||||
setuid(getuid());
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
void handle_new_client()
|
||||
{
|
||||
int fd = do_accept(server_fd);
|
||||
if (fd >= MAX_FDS) {
|
||||
fprintf(stderr, "too many clients ?\n");
|
||||
exit(1);
|
||||
}
|
||||
clients[fd].state = CLIENT_CMDLINE;
|
||||
buffer_init(&clients[fd].buffer);
|
||||
if (fd > max_client_fd)
|
||||
max_client_fd = fd;
|
||||
}
|
||||
|
||||
void flush_client(int fd)
|
||||
{
|
||||
int i;
|
||||
struct server_header s_hdr;
|
||||
close(fd);
|
||||
clients[fd].state = CLIENT_INVALID;
|
||||
buffer_free(&clients[fd].buffer);
|
||||
if (max_client_fd == fd) {
|
||||
for (i = fd; clients[i].state == CLIENT_INVALID && i >= 0;
|
||||
i--);
|
||||
max_client_fd = i;
|
||||
}
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_CLIENT_END;
|
||||
s_hdr.clid = fd;
|
||||
s_hdr.len = 0;
|
||||
write_all_vchan_ext(&s_hdr, sizeof(s_hdr));
|
||||
}
|
||||
|
||||
void pass_to_agent(int fd, struct server_header *s_hdr)
|
||||
{
|
||||
int len = s_hdr->len;
|
||||
char buf[len];
|
||||
if (!read_all(fd, buf, len)) {
|
||||
flush_client(fd);
|
||||
return;
|
||||
}
|
||||
write_all_vchan_ext(s_hdr, sizeof(*s_hdr));
|
||||
write_all_vchan_ext(buf, len);
|
||||
}
|
||||
|
||||
void handle_client_cmdline(int fd)
|
||||
{
|
||||
struct client_header hdr;
|
||||
struct server_header s_hdr;
|
||||
if (!read_all(fd, &hdr, sizeof hdr)) {
|
||||
flush_client(fd);
|
||||
return;
|
||||
}
|
||||
switch (hdr.type) {
|
||||
case MSG_CLIENT_TO_SERVER_EXEC_CMDLINE:
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_EXEC_CMDLINE;
|
||||
break;
|
||||
case MSG_CLIENT_TO_SERVER_JUST_EXEC:
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_JUST_EXEC;
|
||||
break;
|
||||
default:
|
||||
flush_client(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
s_hdr.clid = fd;
|
||||
s_hdr.len = hdr.len;
|
||||
pass_to_agent(fd, &s_hdr);
|
||||
clients[fd].state = CLIENT_DATA;
|
||||
set_nonblock(fd);
|
||||
if (hdr.type == MSG_CLIENT_TO_SERVER_JUST_EXEC)
|
||||
flush_client(fd);
|
||||
|
||||
}
|
||||
|
||||
void handle_client_data(int fd)
|
||||
{
|
||||
struct server_header s_hdr;
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
int len, ret;
|
||||
|
||||
if (clients[fd].state == CLIENT_CMDLINE) {
|
||||
handle_client_cmdline(fd);
|
||||
return;
|
||||
}
|
||||
len = buffer_space_vchan_ext();
|
||||
if (len <= sizeof s_hdr)
|
||||
return;
|
||||
ret = read(fd, buf, len - sizeof(s_hdr));
|
||||
if (ret < 0) {
|
||||
perror("read client");
|
||||
flush_client(fd);
|
||||
return;
|
||||
}
|
||||
s_hdr.clid = fd;
|
||||
s_hdr.len = ret;
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_INPUT;
|
||||
|
||||
write_all_vchan_ext(&s_hdr, sizeof(s_hdr));
|
||||
write_all_vchan_ext(buf, ret);
|
||||
if (ret == 0)
|
||||
clients[fd].state |= CLIENT_DONT_READ;
|
||||
}
|
||||
|
||||
void flush_client_data_daemon(int clid)
|
||||
{
|
||||
switch (flush_client_data(clid, clid, &clients[clid].buffer)) {
|
||||
case WRITE_STDIN_OK:
|
||||
clients[clid].state &= ~CLIENT_OUTQ_FULL;
|
||||
break;
|
||||
case WRITE_STDIN_ERROR:
|
||||
flush_client(clid);
|
||||
break;
|
||||
case WRITE_STDIN_BUFFERED:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown flush_client_data?\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void pass_to_client(int clid, struct client_header *hdr)
|
||||
{
|
||||
int len = hdr->len;
|
||||
char buf[sizeof(*hdr) + len];
|
||||
|
||||
*(struct client_header *) buf = *hdr;
|
||||
read_all_vchan_ext(buf + sizeof(*hdr), len);
|
||||
|
||||
switch (write_stdin
|
||||
(clid, clid, buf, len + sizeof(*hdr),
|
||||
&clients[clid].buffer)) {
|
||||
case WRITE_STDIN_OK:
|
||||
break;
|
||||
case WRITE_STDIN_BUFFERED:
|
||||
clients[clid].state |= CLIENT_OUTQ_FULL;
|
||||
break;
|
||||
case WRITE_STDIN_ERROR:
|
||||
flush_client(clid);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown write_stdin?\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_agent_data()
|
||||
{
|
||||
struct client_header hdr;
|
||||
struct server_header s_hdr;
|
||||
read_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
|
||||
// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.clid,
|
||||
// s_hdr.len);
|
||||
|
||||
if (s_hdr.clid >= MAX_FDS || s_hdr.clid < 0) {
|
||||
fprintf(stderr, "from agent: clid=%d\n", s_hdr.clid);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (s_hdr.type == MSG_XOFF) {
|
||||
clients[s_hdr.clid].state |= CLIENT_DONT_READ;
|
||||
return;
|
||||
}
|
||||
if (s_hdr.type == MSG_XON) {
|
||||
clients[s_hdr.clid].state &= ~CLIENT_DONT_READ;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (s_hdr.type) {
|
||||
case MSG_AGENT_TO_SERVER_STDOUT:
|
||||
hdr.type = MSG_SERVER_TO_CLIENT_STDOUT;
|
||||
break;
|
||||
case MSG_AGENT_TO_SERVER_STDERR:
|
||||
hdr.type = MSG_SERVER_TO_CLIENT_STDERR;
|
||||
break;
|
||||
case MSG_AGENT_TO_SERVER_EXIT_CODE:
|
||||
hdr.type = MSG_SERVER_TO_CLIENT_EXIT_CODE;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "from agent: type=%d\n", s_hdr.type);
|
||||
exit(1);
|
||||
}
|
||||
hdr.len = s_hdr.len;
|
||||
if (hdr.len > MAX_DATA_CHUNK) {
|
||||
fprintf(stderr, "agent feeded %d of data bytes?\n",
|
||||
hdr.len);
|
||||
exit(1);
|
||||
}
|
||||
if (clients[s_hdr.clid].state == CLIENT_INVALID) {
|
||||
// benefit of doubt - maybe client exited earlier
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
read_all_vchan_ext(buf, s_hdr.len);
|
||||
return;
|
||||
}
|
||||
pass_to_client(s_hdr.clid, &hdr);
|
||||
if (s_hdr.type == MSG_AGENT_TO_SERVER_EXIT_CODE)
|
||||
flush_client(s_hdr.clid);
|
||||
}
|
||||
|
||||
int fill_fds_for_select(fd_set * rdset, fd_set * wrset)
|
||||
{
|
||||
int i;
|
||||
int max = -1;
|
||||
FD_ZERO(rdset);
|
||||
FD_ZERO(wrset);
|
||||
for (i = 0; i <= max_client_fd; i++) {
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& !(clients[i].state & CLIENT_DONT_READ)) {
|
||||
FD_SET(i, rdset);
|
||||
max = i;
|
||||
}
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& clients[i].state & CLIENT_OUTQ_FULL) {
|
||||
FD_SET(i, wrset);
|
||||
max = i;
|
||||
}
|
||||
}
|
||||
FD_SET(server_fd, rdset);
|
||||
if (server_fd > max)
|
||||
max = server_fd;
|
||||
return max;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
fd_set rdset, wrset;
|
||||
int i;
|
||||
int max;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s domainid\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
init(atoi(argv[1]));
|
||||
for (;;) {
|
||||
max = fill_fds_for_select(&rdset, &wrset);
|
||||
if (buffer_space_vchan_ext() <=
|
||||
sizeof(struct server_header))
|
||||
FD_ZERO(&rdset);
|
||||
|
||||
wait_for_vchan_or_argfd(max, &rdset, &wrset);
|
||||
|
||||
if (FD_ISSET(server_fd, &rdset))
|
||||
handle_new_client();
|
||||
|
||||
while (read_ready_vchan_ext())
|
||||
handle_agent_data();
|
||||
|
||||
for (i = 0; i <= max_client_fd; i++)
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& FD_ISSET(i, &rdset))
|
||||
handle_client_data(i);
|
||||
|
||||
for (i = 0; i <= max_client_fd; i++)
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& FD_ISSET(i, &wrset))
|
||||
flush_client_data_daemon(i);
|
||||
}
|
||||
}
|
207
qrexec/txrx-vchan.c
Normal file
207
qrexec/txrx-vchan.c
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <libvchan.h>
|
||||
#include <xs.h>
|
||||
#include <xenctrl.h>
|
||||
|
||||
static struct libvchan *ctrl;
|
||||
static int is_server;
|
||||
int write_all_vchan_ext(void *buf, int size)
|
||||
{
|
||||
int written = 0;
|
||||
int ret;
|
||||
|
||||
while (written < size) {
|
||||
ret =
|
||||
libvchan_write(ctrl, (char *) buf + written,
|
||||
size - written);
|
||||
if (ret <= 0) {
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
// fprintf(stderr, "sent %d bytes\n", size);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
int read_all_vchan_ext(void *buf, int size)
|
||||
{
|
||||
int written = 0;
|
||||
int ret;
|
||||
while (written < size) {
|
||||
ret =
|
||||
libvchan_read(ctrl, (char *) buf + written,
|
||||
size - written);
|
||||
if (ret == 0) {
|
||||
fprintf(stderr, "EOF\n");
|
||||
exit(1);
|
||||
}
|
||||
if (ret < 0) {
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
// fprintf(stderr, "read %d bytes\n", size);
|
||||
return size;
|
||||
}
|
||||
|
||||
int read_ready_vchan_ext()
|
||||
{
|
||||
return libvchan_data_ready(ctrl);
|
||||
}
|
||||
|
||||
int buffer_space_vchan_ext()
|
||||
{
|
||||
return libvchan_buffer_space(ctrl);
|
||||
}
|
||||
|
||||
// if the remote domain is destroyed, we get no notification
|
||||
// thus, we check for the status periodically
|
||||
|
||||
static int xc_handle = -1;
|
||||
void slow_check_for_libvchan_is_eof(struct libvchan *ctrl)
|
||||
{
|
||||
struct evtchn_status evst;
|
||||
evst.port = ctrl->evport;
|
||||
evst.dom = DOMID_SELF;
|
||||
if (xc_evtchn_status(xc_handle, &evst)) {
|
||||
perror("xc_evtchn_status");
|
||||
exit(1);
|
||||
}
|
||||
if (evst.status != EVTCHNSTAT_interdomain) {
|
||||
fprintf(stderr, "event channel disconnected\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int wait_for_vchan_or_argfd_once(int max, fd_set * rdset, fd_set * wrset)
|
||||
{
|
||||
int vfd, ret;
|
||||
struct timeval tv = { 1, 100000 };
|
||||
vfd = libvchan_fd_for_select(ctrl);
|
||||
FD_SET(vfd, rdset);
|
||||
if (vfd > max)
|
||||
max = vfd;
|
||||
max++;
|
||||
ret = select(max, rdset, wrset, NULL, &tv);
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR) {
|
||||
perror("select");
|
||||
exit(1);
|
||||
} else {
|
||||
FD_ZERO(rdset);
|
||||
FD_ZERO(wrset);
|
||||
fprintf(stderr, "eintr\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
if (libvchan_is_eof(ctrl)) {
|
||||
fprintf(stderr, "libvchan_is_eof\n");
|
||||
exit(0);
|
||||
}
|
||||
if (!is_server && ret == 0)
|
||||
slow_check_for_libvchan_is_eof(ctrl);
|
||||
if (FD_ISSET(vfd, rdset))
|
||||
// the following will never block; we need to do this to
|
||||
// clear libvchan_fd pending state
|
||||
libvchan_wait(ctrl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void wait_for_vchan_or_argfd(int max, fd_set * rdset, fd_set * wrset)
|
||||
{
|
||||
fd_set r = *rdset, w = *wrset;
|
||||
do {
|
||||
*rdset = r;
|
||||
*wrset = w;
|
||||
}
|
||||
while (wait_for_vchan_or_argfd_once(max, rdset, wrset) == 0);
|
||||
}
|
||||
|
||||
int peer_server_init(int port)
|
||||
{
|
||||
is_server = 1;
|
||||
ctrl = libvchan_server_init(port);
|
||||
if (!ctrl) {
|
||||
perror("libvchan_server_init");
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *peer_client_init(int dom, int port)
|
||||
{
|
||||
struct xs_handle *xs;
|
||||
char buf[64];
|
||||
char *name;
|
||||
char *dummy;
|
||||
unsigned int len = 0;
|
||||
char devbuf[128];
|
||||
unsigned int count;
|
||||
char **vec;
|
||||
|
||||
// double_buffered = 1; // writes to vchan are buffered, nonblocking
|
||||
// double_buffer_init();
|
||||
xs = xs_daemon_open();
|
||||
if (!xs) {
|
||||
perror("xs_daemon_open");
|
||||
exit(1);
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "/local/domain/%d/name", dom);
|
||||
name = xs_read(xs, 0, buf, &len);
|
||||
if (!name) {
|
||||
perror("xs_read domainname");
|
||||
exit(1);
|
||||
}
|
||||
snprintf(devbuf, sizeof(devbuf),
|
||||
"/local/domain/%d/device/vchan/%d/event-channel", dom,
|
||||
port);
|
||||
xs_watch(xs, devbuf, devbuf);
|
||||
do {
|
||||
vec = xs_read_watch(xs, &count);
|
||||
if (vec)
|
||||
free(vec);
|
||||
len = 0;
|
||||
dummy = xs_read(xs, 0, devbuf, &len);
|
||||
}
|
||||
while (!dummy || !len); // wait for the server to create xenstore entries
|
||||
free(dummy);
|
||||
xs_daemon_close(xs);
|
||||
|
||||
// now client init should succeed; "while" is redundant
|
||||
while (!(ctrl = libvchan_client_init(dom, port)));
|
||||
|
||||
xc_handle = xc_interface_open();
|
||||
if (xc_handle < 0) {
|
||||
perror("xc_interface_open");
|
||||
exit(1);
|
||||
}
|
||||
return name;
|
||||
}
|
70
qrexec/unix_server.c
Normal file
70
qrexec/unix_server.c
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "qrexec.h"
|
||||
|
||||
int get_server_socket(int domid)
|
||||
{
|
||||
struct sockaddr_un sockname;
|
||||
int s;
|
||||
char socket_address[40];
|
||||
|
||||
snprintf(socket_address, sizeof(socket_address),
|
||||
QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", domid);
|
||||
unlink(socket_address);
|
||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
memset(&sockname, 0, sizeof(sockname));
|
||||
sockname.sun_family = AF_UNIX;
|
||||
memcpy(sockname.sun_path, socket_address, strlen(socket_address));
|
||||
|
||||
if (bind(s, (struct sockaddr *) &sockname, sizeof(sockname)) == -1) {
|
||||
printf("bind() failed\n");
|
||||
close(s);
|
||||
exit(1);
|
||||
}
|
||||
// chmod(sockname.sun_path, 0666);
|
||||
if (listen(s, 5) == -1) {
|
||||
perror("listen() failed\n");
|
||||
close(s);
|
||||
exit(1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int do_accept(int s)
|
||||
{
|
||||
struct sockaddr_un peer;
|
||||
unsigned int addrlen;
|
||||
int fd;
|
||||
addrlen = sizeof(peer);
|
||||
fd = accept(s, (struct sockaddr *) &peer, &addrlen);
|
||||
if (fd == -1) {
|
||||
perror("unix accept");
|
||||
exit(1);
|
||||
}
|
||||
return fd;
|
||||
}
|
85
qrexec/write_stdin.c
Normal file
85
qrexec/write_stdin.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "qrexec.h"
|
||||
#include "buffer.h"
|
||||
#include "glue.h"
|
||||
|
||||
int flush_client_data(int fd, int clid, struct buffer *buffer)
|
||||
{
|
||||
int ret;
|
||||
int len;
|
||||
for (;;) {
|
||||
len = buffer_len(buffer);
|
||||
if (len > MAX_DATA_CHUNK)
|
||||
len = MAX_DATA_CHUNK;
|
||||
ret = write(fd, buffer_data(buffer), len);
|
||||
if (ret == -1) {
|
||||
if (errno != EAGAIN) {
|
||||
return WRITE_STDIN_ERROR;
|
||||
} else
|
||||
return WRITE_STDIN_BUFFERED;
|
||||
}
|
||||
buffer_remove(buffer, len);
|
||||
len = buffer_len(buffer);
|
||||
if (!len) {
|
||||
struct server_header s_hdr;
|
||||
s_hdr.type = MSG_XON;
|
||||
s_hdr.clid = clid;
|
||||
s_hdr.len = 0;
|
||||
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
return WRITE_STDIN_OK;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
int write_stdin(int fd, int clid, char *data, int len,
|
||||
struct buffer *buffer)
|
||||
{
|
||||
int ret;
|
||||
ret = write(fd, data, len);
|
||||
if (ret == len)
|
||||
return WRITE_STDIN_OK;
|
||||
if (ret == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
struct server_header s_hdr;
|
||||
buffer_append(buffer, data, len);
|
||||
|
||||
s_hdr.type = MSG_XOFF;
|
||||
s_hdr.clid = clid;
|
||||
s_hdr.len = 0;
|
||||
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
|
||||
return WRITE_STDIN_BUFFERED;
|
||||
} else
|
||||
return WRITE_STDIN_ERROR;
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"writes < PIPE_BUF were supposed to be atomic ?\n");
|
||||
return WRITE_STDIN_ERROR;
|
||||
}
|
||||
|
||||
}
|
@ -60,6 +60,7 @@ fi
|
||||
%build
|
||||
make clean all
|
||||
make -C ../common
|
||||
make -C ../qrexec
|
||||
|
||||
%install
|
||||
|
||||
@ -72,6 +73,7 @@ mkdir -p $RPM_BUILD_ROOT/usr/bin
|
||||
cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm $RPM_BUILD_ROOT/usr/bin
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer
|
||||
cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir}
|
||||
@ -199,6 +201,7 @@ rm -rf $RPM_BUILD_ROOT
|
||||
%{kde_service_dir}/qvm-dvm.desktop
|
||||
%attr(4755,root,root) /usr/lib/qubes/qubes_penctl
|
||||
/usr/lib/qubes/qubes_add_pendrive_script
|
||||
/usr/lib/qubes/qrexec_agent
|
||||
/etc/udev/rules.d/qubes.rules
|
||||
/etc/sysconfig/iptables
|
||||
/var/lib/qubes
|
||||
|
@ -49,6 +49,7 @@ python -m compileall qvm-core qmemman
|
||||
python -O -m compileall qvm-core qmemman
|
||||
make -C restore
|
||||
make -C ../common
|
||||
make -C ../qrexec
|
||||
|
||||
%install
|
||||
|
||||
@ -86,6 +87,8 @@ cp aux-tools/reset_vm_configs.py $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp pendrive_swapper/qubes_pencmd $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp qmemman/server.py $RPM_BUILD_ROOT/usr/lib/qubes/qmemman_daemon.py
|
||||
cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
cp ../qrexec/qrexec_daemon $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
cp ../qrexec/qrexec_client $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
|
||||
cp restore/xenstore-watch restore/qvm-create-default-dvm $RPM_BUILD_ROOT/usr/bin
|
||||
cp restore/qubes_restore restore/xenfreepages $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
@ -276,6 +279,8 @@ fi
|
||||
/usr/lib/qubes/qubes_prepare_saved_domain.sh
|
||||
/etc/xen/scripts/block.qubes
|
||||
/etc/xen/scripts/vif-route-qubes
|
||||
/usr/lib/qubes/qrexec_client
|
||||
%attr(4750,root,qubes) /usr/lib/qubes/qrexec_daemon
|
||||
%attr(4750,root,qubes) /usr/lib/qubes/xenfreepages
|
||||
%attr(2770,root,qubes) %dir /var/log/qubes
|
||||
%attr(770,root,qubes) %dir /var/run/qubes
|
||||
|
@ -50,6 +50,7 @@ fi
|
||||
|
||||
|
||||
%build
|
||||
make -C ../qrexec
|
||||
|
||||
%install
|
||||
|
||||
@ -61,6 +62,7 @@ mkdir -p $RPM_BUILD_ROOT/etc/init.d
|
||||
cp qubes_core $RPM_BUILD_ROOT/etc/init.d/
|
||||
mkdir -p $RPM_BUILD_ROOT/var/lib/qubes
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp ../common/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp ../common/qubes_fix_nm_conf.sh $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/dhclient.d
|
||||
@ -175,6 +177,7 @@ rm -rf $RPM_BUILD_ROOT
|
||||
/etc/sysconfig/iptables
|
||||
/etc/init.d/qubes_core
|
||||
/var/lib/qubes
|
||||
/usr/lib/qubes/qrexec_agent
|
||||
/usr/lib/qubes/qubes_setup_dnat_to_ns
|
||||
/usr/lib/qubes/qubes_fix_nm_conf.sh
|
||||
/etc/dhclient.d/qubes_setup_dnat_to_ns.sh
|
||||
|
Loading…
Reference in New Issue
Block a user