qubes-core-agent-linux/qrexec/qrexec-client-vm.c
Marek Marczykowski-Górecki d2aa21625c
qrexec: exit with code 126 when service request was refused
Exit code 1 is very common in all kind of programs, including qrexec
services, so it is hard to distinguish remote failure from service call
refusal. Use something from top of the range here (but not 127, as it is
commonly used to report "Command not found")

QubesOS/qubes-issues#2861
2017-06-21 11:21:40 +02:00

175 lines
4.9 KiB
C

/*
* 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 _GNU_SOURCE
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include "libqrexec-utils.h"
#include "qrexec.h"
#include "qrexec-agent.h"
void handle_vchan_error(const char *op)
{
fprintf(stderr, "Error while vchan %s, exiting\n", op);
exit(1);
}
void do_exec(const char *cmd __attribute__((__unused__))) {
fprintf(stderr, "BUG: do_exec function shouldn't be called!\n");
exit(1);
}
int connect_unix_socket(char *path)
{
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;
strncpy(remote.sun_path, path,
sizeof(remote.sun_path));
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
if (connect(s, (struct sockaddr *) &remote, len) == -1) {
perror("connect");
exit(1);
}
return s;
}
char *get_program_name(char *prog)
{
char *basename = rindex(prog, '/');
if (basename)
return basename + 1;
else
return prog;
}
int main(int argc, char **argv)
{
int trigger_fd;
struct trigger_service_params params;
struct exec_params exec_params;
int ret, i;
int start_local_process = 0;
char *abs_exec_path;
pid_t child_pid = 0;
int inpipe[2], outpipe[2];
if (argc < 3) {
fprintf(stderr,
"usage: %s target_vmname program_ident [local_program [local program arguments]]\n",
argv[0]);
exit(1);
}
if (argc > 3) {
start_local_process = 1;
}
trigger_fd = connect_unix_socket(QREXEC_AGENT_TRIGGER_PATH);
memset(&params, 0, sizeof(params));
strncpy(params.service_name, argv[2], sizeof(params.service_name));
strncpy(params.target_domain, argv[1],
sizeof(params.target_domain));
snprintf(params.request_id.ident,
sizeof(params.request_id.ident), "SOCKET");
if (write(trigger_fd, &params, sizeof(params)) < 0) {
perror("write to agent");
exit(1);
}
ret = read(trigger_fd, &exec_params, sizeof(exec_params));
if (ret == 0) {
fprintf(stderr, "Request refused\n");
exit(126);
}
if (ret < 0 || ret != sizeof(exec_params)) {
perror("read");
exit(1);
}
if (start_local_process) {
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inpipe) ||
socketpair(AF_UNIX, SOCK_STREAM, 0, outpipe)) {
perror("socketpair");
exit(1);
}
prepare_child_env();
switch (child_pid = fork()) {
case -1:
perror("fork");
exit(-1);
case 0:
close(inpipe[1]);
close(outpipe[0]);
close(trigger_fd);
for (i = 0; i < 3; i++) {
if (i != 2 || getenv("PASS_LOCAL_STDERR")) {
char *env;
if (asprintf(&env, "SAVED_FD_%d=%d", i, dup(i)) < 0) {
perror("prepare SAVED_FD_");
exit(1);
}
putenv(env);
}
}
dup2(inpipe[0], 0);
dup2(outpipe[1], 1);
close(inpipe[0]);
close(outpipe[1]);
abs_exec_path = strdup(argv[3]);
argv[3] = get_program_name(argv[3]);
execv(abs_exec_path, argv + 3);
perror("execv");
exit(-1);
}
close(inpipe[0]);
close(outpipe[1]);
ret = handle_data_client(MSG_SERVICE_CONNECT,
exec_params.connect_domain, exec_params.connect_port,
inpipe[1], outpipe[0], -1);
} else {
ret = handle_data_client(MSG_SERVICE_CONNECT,
exec_params.connect_domain, exec_params.connect_port,
1, 0, -1);
}
close(trigger_fd);
if (start_local_process)
waitpid(child_pid, &i, 0);
return ret;
}