void handle_trigger_io() { struct msg_header hdr; struct trigger_service_params params; int ret; int client_fd; client_fd = do_accept(trigger_fd); if (client_fd < 0) return; hdr.len = sizeof(params); ret = read(client_fd, ¶ms, sizeof(params)); if (ret == sizeof(params)) { hdr.type = MSG_TRIGGER_SERVICE; snprintf(params.request_id.ident, sizeof(params.request_id), "SOCKET%d", client_fd); if (libvchan_send(ctrl_vchan, &hdr, sizeof(hdr)) < 0) handle_vchan_error("write hdr"); if (libvchan_send(ctrl_vchan, ¶ms, sizeof(params)) < 0) handle_vchan_error("write params"); } if (ret <= 0) { close(client_fd); } /* do not close client_fd - we'll need it to send the connection details * later (when dom0 accepts the request) */ }
/* hdr parameter is received from dom0, so it is trusted */ void handle_server_exec_request(struct msg_header *hdr) { struct exec_params params; int buf_len = hdr->len-sizeof(params); char buf[buf_len]; pid_t child_agent; int client_fd; assert(hdr->len >= sizeof(params)); if (libvchan_recv(ctrl_vchan, ¶ms, sizeof(params)) < 0) handle_vchan_error("read exec params"); if (libvchan_recv(ctrl_vchan, buf, buf_len) < 0) handle_vchan_error("read exec cmd"); if ((hdr->type == MSG_EXEC_CMDLINE || hdr->type == MSG_JUST_EXEC) && !strstr(buf, ":nogui:")) { int child_socket = try_fork_server(hdr->type, params.connect_domain, params.connect_port, buf, buf_len); if (child_socket >= 0) { register_vchan_connection(-1, child_socket, params.connect_domain, params.connect_port); return; } } if (hdr->type == MSG_SERVICE_CONNECT && sscanf(buf, "SOCKET%d", &client_fd)) { /* FIXME: Maybe add some check if client_fd is really FD to some * qrexec-client-vm process; but this data comes from qrexec-daemon * (which sends back what it got from us earlier), so it isn't critical. */ if (write(client_fd, ¶ms, sizeof(params)) < 0) { /* ignore */ } /* No need to send request_id (buf) - the client don't need it, there * is only meaningless (for the client) socket FD */ /* Register connection even if there was an error sending params to * qrexec-client-vm. This way the mainloop will clean the things up * (close socket, send MSG_CONNECTION_TERMINATED) when qrexec-client-vm * will close the socket (terminate itself). */ register_vchan_connection(-1, client_fd, params.connect_domain, params.connect_port); return; } /* No fork server case */ child_agent = handle_new_process(hdr->type, params.connect_domain, params.connect_port, buf, buf_len); register_vchan_connection(child_agent, -1, params.connect_domain, params.connect_port); }
void release_connection(int id) { struct msg_header hdr; struct exec_params params; hdr.type = MSG_CONNECTION_TERMINATED; hdr.len = sizeof(struct exec_params); params.connect_domain = connection_info[id].connect_domain; params.connect_port = connection_info[id].connect_port; if (libvchan_send(ctrl_vchan, &hdr, sizeof(hdr)) < 0) handle_vchan_error("send"); if (libvchan_send(ctrl_vchan, ¶ms, sizeof(params)) < 0) handle_vchan_error("send"); connection_info[id].pid = 0; }
void handle_server_cmd() { struct msg_header s_hdr; if (libvchan_recv(ctrl_vchan, &s_hdr, sizeof(s_hdr)) < 0) handle_vchan_error("read s_hdr"); // fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.client_id, // s_hdr.len); switch (s_hdr.type) { case MSG_EXEC_CMDLINE: case MSG_JUST_EXEC: case MSG_SERVICE_CONNECT: wake_meminfo_writer(); handle_server_exec_request(&s_hdr); break; case MSG_SERVICE_REFUSED: handle_service_refused(&s_hdr); break; default: fprintf(stderr, "msg type from daemon is %d ?\n", s_hdr.type); exit(1); } }
int read_data(libvchan_t *vchan, char *buf, int size) { int written = 0; int ret; while (written < size) { ret = libvchan_read(vchan, buf + written, size - written); if (ret <= 0) handle_vchan_error(vchan, "read data"); written += ret; } // fprintf(stderr, "read %d bytes\n", size); return size; }
static void handle_execute_service(void) { int i; int policy_pending_slot; pid_t pid; struct trigger_service_params untrusted_params, params; char remote_domain_id_str[10]; if (libvchan_recv(vchan, &untrusted_params, sizeof(untrusted_params)) < 0) handle_vchan_error("recv params"); /* sanitize start */ ENSURE_NULL_TERMINATED(untrusted_params.service_name); ENSURE_NULL_TERMINATED(untrusted_params.target_domain); ENSURE_NULL_TERMINATED(untrusted_params.request_id.ident); sanitize_name(untrusted_params.service_name, "+"); sanitize_name(untrusted_params.target_domain, "@:"); sanitize_name(untrusted_params.request_id.ident, " "); params = untrusted_params; /* sanitize end */ policy_pending_slot = find_policy_pending_slot(); if (policy_pending_slot < 0) { fprintf(stderr, "Service request denied, too many pending requests\n"); send_service_refused(vchan, &untrusted_params.request_id); return; } switch (pid=fork()) { case -1: perror("fork"); exit(1); case 0: break; default: policy_pending[policy_pending_slot].pid = pid; policy_pending[policy_pending_slot].params = untrusted_params.request_id; return; } for (i = 3; i < MAX_FDS; i++) close(i); signal(SIGCHLD, SIG_DFL); signal(SIGPIPE, SIG_DFL); snprintf(remote_domain_id_str, sizeof(remote_domain_id_str), "%d", remote_domain_id); execl("/usr/bin/qrexec-policy", "qrexec-policy", "--", remote_domain_id_str, remote_domain_name, params.target_domain, params.service_name, params.request_id.ident, NULL); perror("execl"); _exit(1); }
int write_data_exact(libvchan_t *vchan, char *buf, int size) { int written = 0; int ret; while (written < size) { ret = libvchan_write(vchan, buf + written, size - written); if (ret <= 0) handle_vchan_error(vchan, "write data"); written += ret; } // fprintf(stderr, "sent %d bytes\n", size); return size; }
void handle_service_refused(struct msg_header *hdr) { struct service_params params; int socket_fd; if (hdr->len != sizeof(params)) { fprintf(stderr, "Invalid msg 0x%x length (%d)\n", MSG_SERVICE_REFUSED, hdr->len); exit(1); } if (libvchan_recv(ctrl_vchan, ¶ms, sizeof(params)) < 0) handle_vchan_error("read exec params"); if (sscanf(params.ident, "SOCKET%d", &socket_fd)) close(socket_fd); else fprintf(stderr, "Received REFUSED for unknown service request '%s'\n", params.ident); }
void init() { mode_t old_umask; /* FIXME: This 0 is remote domain ID */ ctrl_vchan = libvchan_server_init(0, VCHAN_BASE_PORT, 4096, 4096); if (!ctrl_vchan) handle_vchan_error("server_init"); if (handle_handshake(ctrl_vchan) < 0) exit(1); old_umask = umask(0); trigger_fd = get_server_socket(QREXEC_AGENT_TRIGGER_PATH); umask(old_umask); register_exec_func(do_exec); /* wait for qrexec daemon */ while (!libvchan_is_open(ctrl_vchan)) libvchan_wait(ctrl_vchan); }
static void handle_connection_terminated() { struct exec_params untrusted_params, params; if (libvchan_recv(vchan, &untrusted_params, sizeof(untrusted_params)) < 0) handle_vchan_error("recv params"); /* sanitize start */ if (untrusted_params.connect_port < VCHAN_BASE_DATA_PORT || untrusted_params.connect_port >= VCHAN_BASE_DATA_PORT+MAX_CLIENTS) { fprintf(stderr, "Invalid port in MSG_CONNECTION_TERMINATED (%d)\n", untrusted_params.connect_port); exit(1); } /* untrusted_params.connect_domain even if invalid will not harm - in worst * case the port will not be released */ params = untrusted_params; /* sanitize end */ release_vchan_port(params.connect_port, params.connect_domain); }
static void handle_message_from_agent(void) { struct msg_header hdr, untrusted_hdr; if (libvchan_recv(vchan, &untrusted_hdr, sizeof(untrusted_hdr)) < 0) handle_vchan_error("recv hdr"); /* sanitize start */ sanitize_message_from_agent(&untrusted_hdr); hdr = untrusted_hdr; /* sanitize end */ // fprintf(stderr, "got %x %x %x\n", hdr.type, hdr.client_id, // hdr.len); switch (hdr.type) { case MSG_TRIGGER_SERVICE: handle_execute_service(); return; case MSG_CONNECTION_TERMINATED: handle_connection_terminated(); return; } }
static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr) { struct exec_params params; int len = hdr->len-sizeof(params); char buf[len]; int use_default_user = 0; int i; if (!read_all(fd, ¶ms, sizeof(params))) { terminate_client(fd); return 0; } if (!read_all(fd, buf, len)) { terminate_client(fd); return 0; } if (hdr->type == MSG_SERVICE_CONNECT) { /* if the service was accepted, do not send spurious * MSG_SERVICE_REFUSED when service process itself exit with non-zero * code */ for (i = 0; i <= policy_pending_max; i++) { if (policy_pending[i].pid && strncmp(policy_pending[i].params.ident, buf, len) == 0) { policy_pending[i].pid = 0; while (policy_pending_max > 0 && policy_pending[policy_pending_max].pid == 0) policy_pending_max--; break; } } } if (!params.connect_port) { struct exec_params client_params; /* allocate port and send it to the client */ params.connect_port = allocate_vchan_port(params.connect_domain); if (params.connect_port <= 0) { fprintf(stderr, "Failed to allocate new vchan port, too many clients?\n"); terminate_client(fd); return 0; } /* notify the client when this connection got terminated */ vchan_port_notify_client[params.connect_port-VCHAN_BASE_DATA_PORT] = fd; client_params.connect_port = params.connect_port; client_params.connect_domain = remote_domain_id; hdr->len = sizeof(client_params); if (!write_all(fd, hdr, sizeof(*hdr))) { terminate_client(fd); release_vchan_port(params.connect_port, params.connect_domain); return 0; } if (!write_all(fd, &client_params, sizeof(client_params))) { terminate_client(fd); release_vchan_port(params.connect_port, params.connect_domain); return 0; } /* restore original len value */ hdr->len = len+sizeof(params); } else { assert(params.connect_port >= VCHAN_BASE_DATA_PORT); assert(params.connect_port < VCHAN_BASE_DATA_PORT+MAX_CLIENTS); } if (!strncmp(buf, default_user_keyword, default_user_keyword_len_without_colon+1)) { use_default_user = 1; hdr->len -= default_user_keyword_len_without_colon; hdr->len += strlen(default_user); } if (libvchan_send(vchan, hdr, sizeof(*hdr)) < 0) handle_vchan_error("send"); if (libvchan_send(vchan, ¶ms, sizeof(params)) < 0) handle_vchan_error("send params"); if (use_default_user) { if (libvchan_send(vchan, default_user, strlen(default_user)) < 0) handle_vchan_error("send default_user"); if (libvchan_send(vchan, buf+default_user_keyword_len_without_colon, len-default_user_keyword_len_without_colon) < 0) handle_vchan_error("send buf"); } else if (libvchan_send(vchan, buf, len) < 0) handle_vchan_error("send buf"); return 1; }