blob: 44c0d9831b2fc5365c38f8ce7f0eefe78e4f0ec5 [file] [log] [blame]
/*****************************************************************************\
* x11_forwarding.c - setup x11 port forwarding
*****************************************************************************
* Copyright (C) SchedMD LLC.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm 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.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm 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 Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <grp.h>
#include <netinet/in.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include "src/common/eio.h"
#include "src/common/half_duplex.h"
#include "src/common/macros.h"
#include "src/common/net.h"
#include "src/common/read_config.h"
#include "src/common/uid.h"
#include "src/common/x11_util.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/interfaces/conn.h"
#include "src/slurmd/slurmstepd/slurmstepd.h"
#include "src/slurmd/slurmstepd/slurmstepd_job.h"
static uint32_t job_id = NO_VAL;
static uid_t job_uid;
static bool local_xauthority = false;
static char hostname[HOST_NAME_MAX] = {0};
static eio_handle_t *eio_handle = NULL;
/* Target salloc/srun host/port */
static slurm_addr_t alloc_node;
/* X11 display hostname on target, or UNIX socket. */
static char *x11_target = NULL;
/* X11 display port on target (if not a UNIX socket). */
static uint16_t x11_target_port = 0;
static uint16_t protocol_version = SLURM_PROTOCOL_VERSION;
static void *_eio_thread(void *arg)
{
eio_handle_mainloop(eio_handle);
return NULL;
}
static bool _x11_socket_readable(eio_obj_t *obj)
{
if (obj->shutdown) {
if (obj->fd != -1)
close(obj->fd);
obj->fd = -1;
return false;
}
return true;
}
static int _x11_socket_read(eio_obj_t *obj, list_t *objs)
{
void *tls_conn = NULL;
slurm_msg_t req, resp;
net_forward_msg_t rpc;
slurm_addr_t sin;
int *local, *remote;
char *srun_tls_cert = obj->arg;
int rc;
local = xmalloc(sizeof(*local));
remote = xmalloc(sizeof(*remote));
if ((*local = slurm_accept_conn(obj->fd, &sin)) == -1) {
error("accept call failure, shutting down");
goto shutdown;
}
if (!(tls_conn = slurm_open_msg_conn(&alloc_node, srun_tls_cert))) {
error("%s: slurm_open_msg_conn(%pA): %m",
__func__, &alloc_node);
goto shutdown;
}
*remote = conn_g_get_fd(tls_conn);
rpc.job_id = job_id;
rpc.flags = 0;
rpc.port = x11_target_port;
rpc.target = x11_target;
slurm_msg_t_init(&req);
slurm_msg_t_init(&resp);
req.msg_type = SRUN_NET_FORWARD;
req.protocol_version = protocol_version;
slurm_msg_set_r_uid(&req, job_uid);
req.data = &rpc;
slurm_send_recv_msg(tls_conn, &req, &resp, 0);
if (resp.msg_type != RESPONSE_SLURM_RC) {
error("Unexpected response on setup, forwarding failed.");
slurm_free_msg_members(&resp);
goto shutdown;
}
if ((rc = slurm_get_return_code(resp.msg_type, resp.data))) {
error("Error setting up X11 forwarding from remote: %s",
slurm_strerror(rc));
slurm_free_msg_members(&resp);
goto shutdown;
}
slurm_free_msg_members(&resp);
net_set_nodelay(*local, true, NULL);
net_set_nodelay(*remote, true, NULL);
if (half_duplex_add_objs_to_handle(eio_handle, local, remote,
tls_conn)) {
goto shutdown;
}
debug("%s: X11 forwarding setup successful", __func__);
return SLURM_SUCCESS;
shutdown:
debug2("%s: error, shutting down", __func__);
if (*local != -1)
close(*local);
xfree(local);
xfree(remote);
return SLURM_ERROR;
}
extern int shutdown_x11_forward(stepd_step_rec_t *step)
{
int rc = SLURM_SUCCESS;
debug("x11 forwarding shutdown in progress");
if (eio_handle)
eio_signal_shutdown(eio_handle);
if (step->x11_xauthority) {
if (local_xauthority) {
if (unlink(step->x11_xauthority)) {
error("%s: problem unlinking xauthority file %s: %m",
__func__, step->x11_xauthority);
rc = SLURM_ERROR;
}
} else
rc = x11_delete_xauth(step->x11_xauthority, hostname,
step->x11_display);
}
info("x11 forwarding shutdown complete");
return rc;
}
/*
* Bind to a local port for X11 connections. Each connection will setup a
* separate tunnel through the remote salloc/srun process.
*
* IN: job
* OUT: SLURM_SUCCESS or SLURM_ERROR
*/
extern int setup_x11_forward(stepd_step_rec_t *step)
{
int listen_socket = -1;
uint16_t port;
/*
* Range of ports we'll accept locally. This corresponds to X11
* displays of 20 through 99. Intentionally skipping [10 - 19]
* as 'ssh -X' will start at 10 and work up from there.
*/
uint16_t ports[2] = {6020, 6099};
/*
* EIO handles both the local listening socket, as well as the individual
* forwarded connections.
*/
eio_obj_t *obj;
static struct io_operations x11_socket_ops = {
.readable = _x11_socket_readable,
.handle_read = _x11_socket_read,
};
srun_info_t *srun = list_peek(step->sruns);
/* This should always be set to something else we have a bug. */
xassert(srun && srun->protocol_version);
protocol_version = srun->protocol_version;
job_id = step->step_id.job_id;
job_uid = step->uid;
x11_target = xstrdup(step->x11_target);
x11_target_port = step->x11_target_port;
slurm_set_addr(&alloc_node, step->x11_alloc_port, step->x11_alloc_host);
debug("X11Parameters: %s", slurm_conf.x11_params);
if (xstrcasestr(slurm_conf.x11_params, "home_xauthority")) {
char *home = xstrdup(step->pw_dir);
if (!home && !(home = uid_to_dir(step->uid))) {
error("Could not look up user home directory");
goto shutdown;
}
step->x11_xauthority = xstrdup_printf("%s/.Xauthority", home);
xfree(home);
} else {
/* use a node-local XAUTHORITY file instead of ~/.Xauthority */
local_xauthority = true;
step->x11_xauthority = slurm_get_tmp_fs(conf->node_name);
xstrcat(step->x11_xauthority, "/.Xauthority-XXXXXX");
}
/*
* Slurm uses the shortened hostname by default (and discards any
* domain component), which can cause problems for some sites.
* So retrieve the raw value from gethostname() again.
*/
if (gethostname(hostname, sizeof(hostname)))
fatal("%s: gethostname failed: %m", __func__);
if (net_stream_listen_ports(&listen_socket, &port, ports, true) == -1) {
error("failed to open local socket");
goto shutdown;
}
step->x11_display = port - X11_TCP_PORT_OFFSET;
info("X11 forwarding established on DISPLAY=%s:%d.0",
hostname, step->x11_display);
eio_handle = eio_handle_create(0);
obj = eio_obj_create(listen_socket, &x11_socket_ops, NULL);
obj->arg = xstrdup(srun->tls_cert);
eio_new_initial_obj(eio_handle, obj);
slurm_thread_create_detached(_eio_thread, NULL);
return SLURM_SUCCESS;
shutdown:
xfree(x11_target);
step->x11_display = 0;
xfree(step->x11_xauthority);
if (listen_socket != -1)
close(listen_socket);
return SLURM_ERROR;
}