return virNetClientNew(sock, nodename);
}
+
+/*
+ * The SSH Server uses shell to spawn the command we give
+ * it. Our command then invokes shell again. Thus we need
+ * to apply two levels of escaping, so that commands with
+ * whitespace in their path get correctly interpreted.
+ */
+static char *
+virNetClientDoubleEscapeShell(const char *str)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ g_autofree char *tmp = NULL;
+
+ virBufferEscapeShell(&buf, str);
+
+ tmp = virBufferContentAndReset(&buf);
+
+ virBufferEscapeShell(&buf, tmp);
+
+ return virBufferContentAndReset(&buf);
+}
+
+char *
+virNetClientSSHHelperCommand(const char *netcatPath,
+ const char *socketPath)
+{
+ g_autofree char *netcatPathSafe = virNetClientDoubleEscapeShell(netcatPath);
+
+ return g_strdup_printf(
+ "sh -c "
+ "'if '%s' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
+ "ARG=-q0;"
+ "else "
+ "ARG=;"
+ "fi;"
+ "'%s' $ARG -U %s'",
+ netcatPathSafe, netcatPathSafe, socketPath);
+}
+
+
+#define DEFAULT_VALUE(VAR, VAL) \
+ if (!VAR) \
+ VAR = VAL;
+
virNetClientPtr virNetClientNewSSH(const char *nodename,
const char *service,
const char *binary,
const char *username,
bool noTTY,
bool noVerify,
- const char *netcat,
+ const char *netcatPath,
const char *keyfile,
- const char *path)
+ const char *socketPath)
{
virNetSocketPtr sock;
+ g_autofree char *command = NULL;
+
+ DEFAULT_VALUE(netcatPath, "nc");
+
+ command = virNetClientSSHHelperCommand(netcatPath, socketPath);
if (virNetSocketNewConnectSSH(nodename, service, binary, username, noTTY,
- noVerify, netcat, keyfile, path, &sock) < 0)
+ noVerify, keyfile, command, &sock) < 0)
return NULL;
return virNetClientNew(sock, NULL);
}
-#define DEFAULT_VALUE(VAR, VAL) \
- if (!VAR) \
- VAR = VAL;
virNetClientPtr virNetClientNewLibSSH2(const char *host,
const char *port,
int family,
virURIPtr uri)
{
virNetSocketPtr sock = NULL;
-
- g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
- g_autofree char *nc = NULL;
g_autofree char *command = NULL;
-
g_autofree char *homedir = NULL;
g_autofree char *confdir = NULL;
g_autofree char *knownhosts = NULL;
knownhosts = g_strdup(knownHostsPath);
} else {
confdir = virGetUserConfigDirectory();
- virBufferAsprintf(&buf, "%s/known_hosts", confdir);
- if (!(knownhosts = virBufferContentAndReset(&buf)))
- return NULL;
+ knownhosts = g_strdup_printf("%s/known_hosts", confdir);
}
if (privkeyPath) {
DEFAULT_VALUE(netcatPath, "nc");
DEFAULT_VALUE(knownHostsVerify, "normal");
- virBufferEscapeShell(&buf, netcatPath);
- if (!(nc = virBufferContentAndReset(&buf)))
- return NULL;
- virBufferEscapeShell(&buf, nc);
- VIR_FREE(nc);
- if (!(nc = virBufferContentAndReset(&buf)))
- return NULL;
-
- virBufferAsprintf(&buf,
- "sh -c "
- "'if '%s' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
- "ARG=-q0;"
- "else "
- "ARG=;"
- "fi;"
- "'%s' $ARG -U %s'",
- nc, nc, socketPath);
-
- if (!(command = virBufferContentAndReset(&buf)))
- return NULL;
+ command = virNetClientSSHHelperCommand(netcatPath, socketPath);
if (virNetSocketNewConnectLibSSH2(host, port,
family,
return virNetClientNew(sock, NULL);
}
-#undef DEFAULT_VALUE
-#define DEFAULT_VALUE(VAR, VAL) \
- if (!VAR) \
- VAR = VAL;
virNetClientPtr virNetClientNewLibssh(const char *host,
const char *port,
int family,
virURIPtr uri)
{
virNetSocketPtr sock = NULL;
-
- g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
- g_autofree char *nc = NULL;
g_autofree char *command = NULL;
-
g_autofree char *homedir = NULL;
g_autofree char *confdir = NULL;
g_autofree char *knownhosts = NULL;
DEFAULT_VALUE(netcatPath, "nc");
DEFAULT_VALUE(knownHostsVerify, "normal");
- virBufferEscapeShell(&buf, netcatPath);
- if (!(nc = virBufferContentAndReset(&buf)))
- return NULL;
- virBufferEscapeShell(&buf, nc);
- VIR_FREE(nc);
- if (!(nc = virBufferContentAndReset(&buf)))
- return NULL;
-
- command = g_strdup_printf("sh -c "
- "'if '%s' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
- "ARG=-q0;" "else " "ARG=;" "fi;" "'%s' $ARG -U %s'", nc, nc,
- socketPath);
+ command = virNetClientSSHHelperCommand(netcatPath, socketPath);
if (virNetSocketNewConnectLibssh(host, port,
family,
const char *username,
bool noTTY,
bool noVerify,
- const char *netcat,
const char *keyfile,
- const char *path,
+ const char *command,
virNetSocketPtr *retsock)
{
- char *quoted;
virCommandPtr cmd;
- g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
*retsock = NULL;
if (noVerify)
virCommandAddArgList(cmd, "-o", "StrictHostKeyChecking=no", NULL);
- if (!netcat)
- netcat = "nc";
-
- virCommandAddArgList(cmd, "--", nodename, "sh", "-c", NULL);
-
- virBufferEscapeShell(&buf, netcat);
- quoted = virBufferContentAndReset(&buf);
+ virCommandAddArgList(cmd, "--", nodename, command, NULL);
- virBufferEscapeShell(&buf, quoted);
- VIR_FREE(quoted);
- quoted = virBufferContentAndReset(&buf);
-
- /*
- * This ugly thing is a shell script to detect availability of
- * the -q option for 'nc': debian and suse based distros need this
- * flag to ensure the remote nc will exit on EOF, so it will go away
- * when we close the connection tunnel. If it doesn't go away, subsequent
- * connection attempts will hang.
- *
- * Fedora's 'nc' doesn't have this option, and defaults to the desired
- * behavior.
- */
- virCommandAddArgFormat(cmd,
- "'if '%s' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
- "ARG=-q0;"
- "else "
- "ARG=;"
- "fi;"
- "'%s' $ARG -U %s'",
- quoted, quoted, path);
-
- VIR_FREE(quoted);
return virNetSocketNewConnectCommand(cmd, retsock);
}
#include "virstring.h"
#include "rpc/virnetsocket.h"
+#include "rpc/virnetclient.h"
#define VIR_FROM_THIS VIR_FROM_RPC
virNetSocketPtr csock = NULL; /* Client socket */
int ret = -1;
char buf[1024];
+ g_autofree char *command = virNetClientSSHHelperCommand(data->netcat,
+ data->path);
if (virNetSocketNewConnectSSH(data->nodename,
data->service,
data->username,
data->noTTY,
data->noVerify,
- data->netcat,
data->keyfile,
- data->path,
+ command,
&csock) < 0)
goto cleanup;
struct testSSHData sshData1 = {
.nodename = "somehost",
.path = "/tmp/socket",
+ .netcat = "nc",
.expectOut = "-T -e none -- somehost sh -c '"
"if 'nc' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
"ARG=-q0;"
struct testSSHData sshData5 = {
.nodename = "crashyhost",
.path = "/tmp/socket",
+ .netcat = "nc",
.expectOut = "-T -e none -- crashyhost sh -c "
"'if 'nc' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
"ARG=-q0;"
struct testSSHData sshData6 = {
.nodename = "example.com",
.path = "/tmp/socket",
+ .netcat = "nc",
.keyfile = "/root/.ssh/example_key",
.noVerify = true,
.expectOut = "-i /root/.ssh/example_key -T -e none -o StrictHostKeyChecking=no -- example.com sh -c '"