This patch enables virNetSocket to be used as an ssh client when
properly configured.
Fucntion virNetSocketNewConnectLibSSH() is added, that takes all needed
parameters and creates a libssh2 session context and performs steps
needed to open the connection.
---
src/libvirt_private.syms | 1 +
src/rpc/virnetsocket.c | 178 +++++++++++++++++++++++++++++++++++++++++++++-
src/rpc/virnetsocket.h | 13 ++++
3 files changed, 191 insertions(+), 1 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index c023dbf..479613f 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1575,6 +1575,7 @@ virNetSocketListen;
virNetSocketLocalAddrString;
virNetSocketNewConnectCommand;
virNetSocketNewConnectExternal;
+virNetSocketNewConnectLibSSH2;
virNetSocketNewConnectSSH;
virNetSocketNewConnectTCP;
virNetSocketNewConnectUNIX;
diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c
index b6f156b..5bf4b47 100644
--- a/src/rpc/virnetsocket.c
+++ b/src/rpc/virnetsocket.c
@@ -47,6 +47,10 @@
#include "passfd.h"
+#if HAVE_LIBSSH2
+# include "virnetlibssh2session.h"
+#endif
+
#define VIR_FROM_THIS VIR_FROM_RPC
@@ -83,6 +87,9 @@ struct _virNetSocket {
size_t saslEncodedLength;
size_t saslEncodedOffset;
#endif
+#if HAVE_LIBSSH2
+ virNetLibSSH2SessionPtr sshSession;
+#endif
};
@@ -705,6 +712,139 @@ int virNetSocketNewConnectSSH(const char *nodename,
return virNetSocketNewConnectCommand(cmd, retsock);
}
+#if HAVE_LIBSSH2
+int
+virNetSocketNewConnectLibSSH2(const char *host,
+ const char *port,
+ const char *username,
+ const char *password,
+ const char *privkey,
+ const char *knownHosts,
+ const char *knownHostsVerify,
+ const char *authMethods,
+ const char *command,
+ virConnectAuthPtr auth,
+ virNetSocketPtr *retsock)
+{
+ virNetSocketPtr sock = NULL;
+ virNetLibSSH2SessionPtr sess = NULL;
+ unsigned int verify;
+ int ret = -1;
+ int portN;
+
+ char *authMethodNext = NULL;
+ char *authMethodsCopy = NULL;
+ char *authMethod;
+
+ /* port number will be verified while opening the socket */
+ if (virStrToLong_i(port, NULL, 10, &portN) < 0) {
+ virReportError(VIR_ERR_LIBSSH2_ERROR, "%s",
+ _("Failed to parse port number"));
+ goto error;
+ }
+
+ /* create ssh session context */
+ if (!(sess = virNetLibSSH2SessionNew()))
+ goto error;
+
+ /* set ssh session parameters */
+ if (virNetLibSSH2SessionAuthSetCallback(sess, auth) != 0)
+ goto error;
+
+ if (STRCASEEQ("auto", knownHostsVerify))
+ verify = VIR_NET_LIBSSH2_HOSTKEY_VERIFY_AUTO_ADD;
+ else if (STRCASEEQ("ignore", knownHostsVerify))
+ verify = VIR_NET_LIBSSH2_HOSTKEY_VERIFY_IGNORE;
+ else if (STRCASEEQ("normal", knownHostsVerify))
+ verify = VIR_NET_LIBSSH2_HOSTKEY_VERIFY_NORMAL;
+ else {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("Invalid host key verification method:
'%s'"),
+ knownHostsVerify);
+ goto error;
+ }
+
+ if (virNetLibSSH2SessionSetHostKeyVerification(sess,
+ host,
+ portN,
+ knownHosts,
+ false,
+ verify) != 0)
+ goto error;
+
+ if (virNetLibSSH2SessionSetChannelCommand(sess, command) != 0)
+ goto error;
+
+ if (!(authMethodNext = authMethodsCopy = strdup(authMethods))) {
+ virReportOOMError();
+ goto error;
+ }
+
+ while ((authMethod = strsep(&authMethodNext, ","))) {
+ if (STRCASEEQ(authMethod, "keyboard-interactive"))
+ ret = virNetLibSSH2SessionAuthAddKeyboardAuth(sess, username, -1);
+ else if (STRCASEEQ(authMethod, "password"))
+ ret = virNetLibSSH2SessionAuthAddPasswordAuth(sess,
+ username,
+ password);
+ else if (STRCASEEQ(authMethod, "privkey"))
+ ret = virNetLibSSH2SessionAuthAddPrivKeyAuth(sess,
+ username,
+ privkey,
+ NULL);
+ else if (STRCASEEQ(authMethod, "agent"))
+ ret = virNetLibSSH2SessionAuthAddAgentAuth(sess, username);
+ else {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("Invalid authentication method: '%s'"),
+ authMethod);
+ ret = -1;
+ goto error;
+ }
+
+ if (ret != 0)
+ goto error;
+ }
+
+ /* connect to remote server */
+ if ((ret = virNetSocketNewConnectTCP(host, port, &sock)) < 0)
+ goto error;
+
+ /* connect to the host using ssh */
+ if ((ret = virNetLibSSH2SessionConnect(sess, virNetSocketGetFD(sock))) != 0)
+ goto error;
+
+ sock->sshSession = sess;
+ *retsock = sock;
+
+ VIR_FREE(authMethodsCopy);
+ return 0;
+
+error:
+ virObjectUnref(sock);
+ virObjectUnref(sess);
+ VIR_FREE(authMethodsCopy);
+ return ret;
+}
+#else
+int
+virNetSocketNewConnectLibSSH2(const char *host ATTRIBUTE_UNUSED,
+ const char *port ATTRIBUTE_UNUSED,
+ const char *username ATTRIBUTE_UNUSED,
+ const char *password ATTRIBUTE_UNUSED,
+ const char *privkey ATTRIBUTE_UNUSED,
+ const char *knownHosts ATTRIBUTE_UNUSED,
+ const char *knownHostsVerify ATTRIBUTE_UNUSED,
+ const char *authMethods ATTRIBUTE_UNUSED,
+ const char *command ATTRIBUTE_UNUSED,
+ virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+ virNetSocketPtr *retsock ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s",
+ _("libssh2 transport support was not enabled"));
+ return -1;
+}
+#endif /* HAVE_LIBSSH2 */
int virNetSocketNewConnectExternal(const char **cmdargv,
virNetSocketPtr *retsock)
@@ -747,6 +887,10 @@ void virNetSocketDispose(void *obj)
virObjectUnref(sock->saslSession);
#endif
+#if HAVE_LIBSSH2
+ virObjectUnref(sock->sshSession);
+#endif
+
VIR_FORCE_CLOSE(sock->fd);
VIR_FORCE_CLOSE(sock->errfd);
@@ -926,6 +1070,12 @@ bool virNetSocketHasCachedData(virNetSocketPtr sock
ATTRIBUTE_UNUSED)
{
bool hasCached = false;
virMutexLock(&sock->lock);
+
+#if HAVE_LIBSSH2
+ if (virNetLibSSH2SessionHasCachedData(sock->sshSession))
+ hasCached = true;
+#endif
+
#if HAVE_SASL
if (sock->saslDecoded)
hasCached = true;
@@ -934,6 +1084,21 @@ bool virNetSocketHasCachedData(virNetSocketPtr sock
ATTRIBUTE_UNUSED)
return hasCached;
}
+#if HAVE_LIBSSH2
+static ssize_t virNetSocketLibSSH2Read(virNetSocketPtr sock,
+ char *buf,
+ size_t len)
+{
+ return virNetLibSSH2ChannelRead(sock->sshSession, buf, len);
+}
+
+static ssize_t virNetSocketLibSSH2Write(virNetSocketPtr sock,
+ const char *buf,
+ size_t len)
+{
+ return virNetLibSSH2ChannelWrite(sock->sshSession, buf, len);
+}
+#endif
bool virNetSocketHasPendingData(virNetSocketPtr sock ATTRIBUTE_UNUSED)
{
@@ -952,6 +1117,12 @@ static ssize_t virNetSocketReadWire(virNetSocketPtr sock, char *buf,
size_t len)
{
char *errout = NULL;
ssize_t ret;
+
+#if HAVE_LIBSSH2
+ if (sock->sshSession)
+ return virNetSocketLibSSH2Read(sock, buf, len);
+#endif
+
reread:
if (sock->tlsSession &&
virNetTLSSessionGetHandshakeStatus(sock->tlsSession) ==
@@ -1001,6 +1172,12 @@ reread:
static ssize_t virNetSocketWriteWire(virNetSocketPtr sock, const char *buf, size_t len)
{
ssize_t ret;
+
+#if HAVE_LIBSSH2
+ if (sock->sshSession)
+ return virNetSocketLibSSH2Write(sock, buf, len);
+#endif
+
rewrite:
if (sock->tlsSession &&
virNetTLSSessionGetHandshakeStatus(sock->tlsSession) ==
@@ -1129,7 +1306,6 @@ static ssize_t virNetSocketWriteSASL(virNetSocketPtr sock, const
char *buf, size
}
#endif
-
ssize_t virNetSocketRead(virNetSocketPtr sock, char *buf, size_t len)
{
ssize_t ret;
diff --git a/src/rpc/virnetsocket.h b/src/rpc/virnetsocket.h
index cc3f912..fc5e17f 100644
--- a/src/rpc/virnetsocket.h
+++ b/src/rpc/virnetsocket.h
@@ -75,6 +75,18 @@ int virNetSocketNewConnectSSH(const char *nodename,
const char *path,
virNetSocketPtr *addr);
+int virNetSocketNewConnectLibSSH2(const char *host,
+ const char *port,
+ const char *username,
+ const char *password,
+ const char *privkey,
+ const char *knownHosts,
+ const char *knownHostsVerify,
+ const char *authMethods,
+ const char *command,
+ virConnectAuthPtr auth,
+ virNetSocketPtr *retsock);
+
int virNetSocketNewConnectExternal(const char **cmdargv,
virNetSocketPtr *addr);
@@ -103,6 +115,7 @@ int virNetSocketRecvFD(virNetSocketPtr sock, int *fd);
void virNetSocketSetTLSSession(virNetSocketPtr sock,
virNetTLSSessionPtr sess);
+
# ifdef HAVE_SASL
void virNetSocketSetSASLSession(virNetSocketPtr sock,
virNetSASLSessionPtr sess);
--
1.7.8.6