From 637ea5427442d76c9499fb3922da3d81f9f0ad89 Mon Sep 17 00:00:00 2001 From: Peter Krempa Date: Mon, 14 Nov 2011 15:50:02 +0100 Subject: [PATCH] libssh2_transport: add ssh context support to virNetSocket This patch enables virNetSocket to be used as an ssh client when properly configured. This patch adds function virNetSocketNewConnectLibSSH2() that takes all needed parameters and creates a libssh2 session and performs steps needed to open the connection and then create a virNetSocket that seamlesly encapsulates the communication. --- src/libvirt_private.syms | 1 + src/rpc/virnetsocket.c | 178 ++++++++++++++++++++++++++++++++++++++- src/rpc/virnetsocket.h | 13 +++ 3 files changed, 191 insertions(+), 1 deletion(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a55fb73e72..86cc8167f7 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1595,6 +1595,7 @@ virNetSocketListen; virNetSocketLocalAddrString; virNetSocketNewConnectCommand; virNetSocketNewConnectExternal; +virNetSocketNewConnectLibSSH2; virNetSocketNewConnectSSH; virNetSocketNewConnectTCP; virNetSocketNewConnectUNIX; diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c index 69c25bd02f..530c081dbc 100644 --- a/src/rpc/virnetsocket.c +++ b/src/rpc/virnetsocket.c @@ -47,6 +47,10 @@ #include "passfd.h" +#if HAVE_LIBSSH2 +# include "virnetsshsession.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 + virNetSSHSessionPtr sshSession; +#endif }; @@ -725,6 +732,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; + virNetSSHSessionPtr 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_SSH, "%s", + _("Failed to parse port number")); + goto error; + } + + /* create ssh session context */ + if (!(sess = virNetSSHSessionNew())) + goto error; + + /* set ssh session parameters */ + if (virNetSSHSessionAuthSetCallback(sess, auth) != 0) + goto error; + + if (STRCASEEQ("auto", knownHostsVerify)) + verify = VIR_NET_SSH_HOSTKEY_VERIFY_AUTO_ADD; + else if (STRCASEEQ("ignore", knownHostsVerify)) + verify = VIR_NET_SSH_HOSTKEY_VERIFY_IGNORE; + else if (STRCASEEQ("normal", knownHostsVerify)) + verify = VIR_NET_SSH_HOSTKEY_VERIFY_NORMAL; + else { + virReportError(VIR_ERR_INVALID_ARG, + _("Invalid host key verification method: '%s'"), + knownHostsVerify); + goto error; + } + + if (virNetSSHSessionSetHostKeyVerification(sess, + host, + portN, + knownHosts, + false, + verify) != 0) + goto error; + + if (virNetSSHSessionSetChannelCommand(sess, command) != 0) + goto error; + + if (!(authMethodNext = authMethodsCopy = strdup(authMethods))) { + virReportOOMError(); + goto error; + } + + while ((authMethod = strsep(&authMethodNext, ","))) { + if (STRCASEEQ(authMethod, "keyboard-interactive")) + ret = virNetSSHSessionAuthAddKeyboardAuth(sess, username, -1); + else if (STRCASEEQ(authMethod, "password")) + ret = virNetSSHSessionAuthAddPasswordAuth(sess, + username, + password); + else if (STRCASEEQ(authMethod, "privkey")) + ret = virNetSSHSessionAuthAddPrivKeyAuth(sess, + username, + privkey, + NULL); + else if (STRCASEEQ(authMethod, "agent")) + ret = virNetSSHSessionAuthAddAgentAuth(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 = virNetSSHSessionConnect(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) @@ -767,6 +907,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); @@ -946,6 +1090,12 @@ bool virNetSocketHasCachedData(virNetSocketPtr sock ATTRIBUTE_UNUSED) { bool hasCached = false; virMutexLock(&sock->lock); + +#if HAVE_LIBSSH2 + if (virNetSSHSessionHasCachedData(sock->sshSession)) + hasCached = true; +#endif + #if HAVE_SASL if (sock->saslDecoded) hasCached = true; @@ -954,6 +1104,21 @@ bool virNetSocketHasCachedData(virNetSocketPtr sock ATTRIBUTE_UNUSED) return hasCached; } +#if HAVE_LIBSSH2 +static ssize_t virNetSocketLibSSH2Read(virNetSocketPtr sock, + char *buf, + size_t len) +{ + return virNetSSHChannelRead(sock->sshSession, buf, len); +} + +static ssize_t virNetSocketLibSSH2Write(virNetSocketPtr sock, + const char *buf, + size_t len) +{ + return virNetSSHChannelWrite(sock->sshSession, buf, len); +} +#endif bool virNetSocketHasPendingData(virNetSocketPtr sock ATTRIBUTE_UNUSED) { @@ -972,6 +1137,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) == @@ -1021,6 +1192,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) == @@ -1149,7 +1326,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 3750955ee4..75b66c59d5 100644 --- a/src/rpc/virnetsocket.h +++ b/src/rpc/virnetsocket.h @@ -78,6 +78,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); @@ -106,6 +118,7 @@ int virNetSocketRecvFD(virNetSocketPtr sock, int *fd); void virNetSocketSetTLSSession(virNetSocketPtr sock, virNetTLSSessionPtr sess); + # ifdef HAVE_SASL void virNetSocketSetSASLSession(virNetSocketPtr sock, virNetSASLSessionPtr sess); -- GitLab