blob: 65da84dbe46ec7dc3409edba5b56c6d1e6487580 [file] [log] [blame]
/*****************************************************************************\
* x11_util.c - x11 forwarding support functions
* also see src/slurmd/slurmstepd/x11_forwarding.[ch]
*****************************************************************************
* 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 <regex.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "src/common/list.h"
#include "src/common/read_config.h"
#include "src/common/run_command.h"
#include "src/common/strlcpy.h"
#include "src/common/uid.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/common/x11_util.h"
/*
* This should likely be detected at build time, but I have not
* seen any common systems where this is not the correct path.
*/
#define XAUTH_PATH "/usr/bin/xauth"
/*
* Convert an --x11 argument into flags.
* Note that it is legal to specify multiple options. The slurmstepd
* on a node will decide whether to set the forward up once for the entire
* job if it matches any of these qualifiers.
*/
uint16_t x11_str2flags(const char *str)
{
uint16_t flags = 0;
if (!xstrcasecmp(str, "all"))
flags |= X11_FORWARD_ALL;
if (!xstrcasecmp(str, "batch"))
flags |= X11_FORWARD_BATCH;
if (!xstrcasecmp(str, "first"))
flags |= X11_FORWARD_FIRST;
if (!xstrcasecmp(str, "last"))
flags |= X11_FORWARD_LAST;
return flags;
}
const char *x11_flags2str(uint16_t flags)
{
if (flags & X11_FORWARD_ALL)
return "all";
if (flags & X11_FORWARD_BATCH)
return "batch";
if (flags & X11_FORWARD_FIRST)
return "first";
if (flags & X11_FORWARD_LAST)
return "last";
return "unset";
}
/*
* Get local TCP port for X11 from DISPLAY environment variable, alongside an
* xmalloc()'d hostname in *target. If the port returned is 0, *target returns
* an xmalloc()'d string pointing to the local UNIX socket.
*
* Warning - will call exit(-1) if not able to retrieve.
*/
extern void x11_get_display(uint16_t *port, char **target)
{
char *display, *port_split, *port_period;
*target = NULL;
display = xstrdup(getenv("DISPLAY"));
if (!display) {
error("No DISPLAY variable set, cannot setup x11 forwarding.");
exit(-1);
}
if (display[0] == ':') {
struct stat st;
char *screen_period;
*port = 0;
screen_period = strchr(display, '.');
if (screen_period)
*screen_period = '\0';
xstrfmtcat(*target, "/tmp/.X11-unix/X%s", display + 1);
xfree(display);
if (stat(*target, &st) != 0) {
error("Cannot stat() local X11 socket `%s`", *target);
exit(-1);
}
return;
}
/*
* Parse out port number
* Example: localhost/unix:89.0 or localhost/unix:89
*/
port_split = strchr(display, ':');
if (!port_split) {
error("Error parsing DISPLAY environment variable. "
"Cannot use X11 forwarding.");
exit(-1);
}
*port_split = '\0';
/*
* Handle the "screen" portion of the display port.
* Xorg does not require a screen to be specified, defaults to 0.
*/
port_split++;
port_period = strchr(port_split, '.');
if (port_period)
*port_period = '\0';
*port = atoi(port_split) + X11_TCP_PORT_OFFSET;
*target = display;
}
extern char *x11_get_xauth(void)
{
int status, matchlen;
char **xauth_argv;
regex_t reg;
regmatch_t regmatch[2];
char *result, *cookie;
run_command_args_t run_command_args = {
.max_wait = 10000,
.script_path = XAUTH_PATH,
.script_type = "xauth",
.status = &status,
};
/*
* Two real-world examples:
* "zoidberg/unix:10 MIT-MAGIC-COOKIE-1 abcdef0123456789"
* "zoidberg:10 MIT-MAGIC-COOKIE-1 abcdef0123456789"
*
* The "/unix" bit is optional, and captured in "[[:alnum:].-/]+:".
* '.' and '-' are also allowed in the hostname portion, so match them
* in addition to '/'.
*
* The display number is also optional, in this example the number 10
* after the colon, that is the reason why after the [[:digit:]] part
* there is the '*' indicating 0 or more repetitions.
*
* Warning: the '-' must be either first or last in the [] brackets,
* otherwise it will be interpreted as a range instead of the literal
* character.
*/
static char *cookie_pattern = "^[[:alnum:]./-]+:[[:digit:]]*"
"[[:space:]]+MIT-MAGIC-COOKIE-1"
"[[:space:]]+([[:xdigit:]]+)$";
/*
* In some cases the only xauth cookie available may be the wildcard
* cookie. It is not included in the initial regex to make sure that
* the wildcard is only used if nothing else is available.
*
* The format for this cookie is:
* "#ffff#abcdef0123456789#:12345 MIT-MAGIC-COOKIE-1 abcdef0123456789"
*
* Definition of FamilyWild can be found in <X11/Xauth.h>
* The output format can be interpreted from dump_entry() in the xauth
* source code (process.c).
*/
static char *wildcard_pattern = "^#ffff#[[:xdigit:]./-]+#:[[:digit:]]*"
"[[:space:]]+MIT-MAGIC-COOKIE-1"
"[[:space:]]+([[:xdigit:]]+)$";
xauth_argv = xmalloc(sizeof(char *) * 10);
xauth_argv[0] = xstrdup("xauth");
xauth_argv[1] = xstrdup("list");
xauth_argv[2] = xstrdup(getenv("DISPLAY"));
run_command_args.script_argv = xauth_argv;
result = run_command(&run_command_args);
xfree_array(xauth_argv);
if (status) {
error("Problem running xauth command. "
"Cannot use X11 forwarding.");
exit(-1);
}
regcomp(&reg, cookie_pattern, REG_EXTENDED|REG_NEWLINE);
if (regexec(&reg, result, 2, regmatch, 0) == REG_NOMATCH) {
debug2("%s: Could not retrieve magic cookie, checking for wildcard cookie.",
__func__);
regcomp(&reg, wildcard_pattern, REG_EXTENDED|REG_NEWLINE);
if (regexec(&reg, result, 2, regmatch, 0) == REG_NOMATCH) {
error("%s: Could not retrieve magic cookie. Cannot use X11 forwarding.",
__func__);
exit(-1);
}
}
matchlen = regmatch[1].rm_eo - regmatch[1].rm_so + 1;
cookie = xmalloc(matchlen);
strlcpy(cookie, result + regmatch[1].rm_so, matchlen);
xfree(result);
return cookie;
}
extern int x11_set_xauth(char *xauthority, char *cookie, uint16_t display)
{
int i=0, status;
char *result;
char **xauth_argv;
char template[] = "/tmp/xauth-source-XXXXXX";
char *contents = NULL;
char host[HOST_NAME_MAX];
int fd;
run_command_args_t run_command_args = {
.max_wait = 10000,
.script_path = XAUTH_PATH,
.script_type = "xauth",
.status = &status,
};
if (gethostname(host, sizeof(host)))
fatal("%s: gethostname() failed: %m", __func__);
/* protect against weak file permissions in old glibc */
umask(0077);
if ((fd = mkstemp(template)) < 0)
fatal("%s: could not create temp file", __func__);
xstrfmtcat(contents, "add %s/unix:%u MIT-MAGIC-COOKIE-1 %s\n",
host, display, cookie);
safe_write(fd, contents, strlen(contents));
xfree(contents);
close(fd);
xauth_argv = xmalloc(sizeof(char *) * 10);
xauth_argv[i++] = "xauth";
xauth_argv[i++] = "-v";
xauth_argv[i++] = "-f";
xauth_argv[i++] = xauthority;
xauth_argv[i++] = "source";
xauth_argv[i++] = template;
xauth_argv[i++] = NULL;
xassert(i < 10);
run_command_args.script_argv = xauth_argv;
result = run_command(&run_command_args);
(void) unlink(template);
xfree(xauth_argv);
debug2("%s: result from xauth: %s", __func__, result);
xfree(result);
return status;
rwfail:
fatal("%s: could not write temporary xauth file", __func__);
return SLURM_ERROR;
}
extern int x11_delete_xauth(char *xauthority, char *host, uint16_t display)
{
int i=0, status;
char *result;
char **xauth_argv;
run_command_args_t run_command_args = {
.max_wait = 10000,
.script_path = XAUTH_PATH,
.script_type = "xauth",
.status = &status,
};
xauth_argv = xmalloc(sizeof(char *) * 10);
xauth_argv[i++] = xstrdup("xauth");
xauth_argv[i++] = xstrdup("-v");
xauth_argv[i++] = xstrdup("-f");
xauth_argv[i++] = xstrdup(xauthority);
xauth_argv[i++] = xstrdup("remove");
xauth_argv[i++] = xstrdup_printf("%s/unix:%u", host, display);
xauth_argv[i++] = NULL;
xassert(i < 10);
run_command_args.script_argv = xauth_argv;
result = run_command(&run_command_args);
xfree_array(xauth_argv);
debug2("%s: result from xauth: %s", __func__, result);
xfree(result);
return status;
}