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 75997fe..e5d8a5e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1600,6 +1600,7 @@ virNetSocketListen; virNetSocketLocalAddrString; virNetSocketNewConnectCommand; virNetSocketNewConnectExternal; +virNetSocketNewConnectLibSSH; virNetSocketNewConnectSSH; virNetSocketNewConnectTCP; virNetSocketNewConnectUNIX; diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c index 88e5525..b841513 100644 --- a/src/rpc/virnetsocket.c +++ b/src/rpc/virnetsocket.c @@ -47,6 +47,10 @@ #include "passfd.h" +#if HAVE_LIBSSH2 +# include "virnetlibsshcontext.h" +#endif + #define VIR_FROM_THIS VIR_FROM_RPC @@ -82,6 +86,9 @@ struct _virNetSocket { size_t saslEncodedLength; size_t saslEncodedOffset; #endif +#if HAVE_LIBSSH2 + virNetLibSSHSessionPtr sshSession; +#endif }; @@ -688,6 +695,139 @@ int virNetSocketNewConnectSSH(const char *nodename, return virNetSocketNewConnectCommand(cmd, retsock); } +#if HAVE_LIBSSH2 +int +virNetSocketNewConnectLibSSH(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; + virNetLibSSHSessionPtr 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_LIBSSH_ERROR, "%s", + _("Failed to parse port number")); + goto error; + } + + /* create ssh session context */ + if (!(sess = virNetLibSSHSessionNew())) + goto error; + + /* set ssh session parameters */ + if (virNetLibSSHSessionAuthSetCallback(sess, auth) != 0) + goto error; + + if (STRCASEEQ("auto", knownHostsVerify)) + verify = VIR_NET_LIBSSH_HOSTKEY_VERIFY_AUTO_ADD; + else if (STRCASEEQ("ignore", knownHostsVerify)) + verify = VIR_NET_LIBSSH_HOSTKEY_VERIFY_IGNORE; + else if (STRCASEEQ("normal", knownHostsVerify)) + verify = VIR_NET_LIBSSH_HOSTKEY_VERIFY_NORMAL; + else { + virReportError(VIR_ERR_INVALID_ARG, + _("Invalid host key verification method: '%s'"), + knownHostsVerify); + goto error; + } + + if (virNetLibSSHSessionSetHostKeyVerification(sess, + host, + portN, + knownHosts, + false, + verify) != 0) + goto error; + + if (virNetLibSSHSessionSetChannelCommand(sess, command) != 0) + goto error; + + if (!(authMethodNext = authMethodsCopy = strdup(authMethods))) { + virReportOOMError(); + goto error; + } + + while ((authMethod = strsep(&authMethodNext, ","))) { + if (STRCASEEQ(authMethod, "keyboard-interactive")) + ret = virNetLibSSHSessionAuthAddKeyboardAuth(sess, username, -1); + else if (STRCASEEQ(authMethod, "password")) + ret = virNetLibSSHSessionAuthAddPasswordAuth(sess, + username, + password); + else if (STRCASEEQ(authMethod, "privkey")) + ret = virNetLibSSHSessionAuthAddPrivKeyAuth(sess, + username, + privkey, + NULL); + else if (STRCASEEQ(authMethod, "agent")) + ret = virNetLibSSHSessionAuthAddAgentAuth(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 = virNetLibSSHSessionConnect(sess, virNetSocketGetFD(sock))) != 0) + goto error; + + sock->sshSession = sess; + *retsock = sock; + + VIR_FREE(authMethodsCopy); + return 0; + +error: + virNetSocketFree(sock); + virNetLibSSHSessionFree(sess); + VIR_FREE(authMethodsCopy); + return ret; +} +#else +int +virNetSocketNewConnectLibSSH(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) @@ -753,6 +893,10 @@ void virNetSocketFree(virNetSocketPtr sock) virNetSASLSessionFree(sock->saslSession); #endif +#if HAVE_LIBSSH2 + virNetLibSSHSessionFree(sock->sshSession); +#endif + VIR_FORCE_CLOSE(sock->fd); VIR_FORCE_CLOSE(sock->errfd); @@ -937,6 +1081,12 @@ bool virNetSocketHasCachedData(virNetSocketPtr sock ATTRIBUTE_UNUSED) { bool hasCached = false; virMutexLock(&sock->lock); + +#if HAVE_LIBSSH2 + if (virNetLibSSHHasCachedData(sock->sshSession)) + hasCached = true; +#endif + #if HAVE_SASL if (sock->saslDecoded) hasCached = true; @@ -945,6 +1095,21 @@ bool virNetSocketHasCachedData(virNetSocketPtr sock ATTRIBUTE_UNUSED) return hasCached; } +#if HAVE_LIBSSH2 +static ssize_t virNetSocketLibSSHRead(virNetSocketPtr sock, + char *buf, + size_t len) +{ + return virNetLibSSHChannelRead(sock->sshSession, buf, len); +} + +static ssize_t virNetSocketLibSSHWrite(virNetSocketPtr sock, + const char *buf, + size_t len) +{ + return virNetLibSSHChannelWrite(sock->sshSession, buf, len); +} +#endif bool virNetSocketHasPendingData(virNetSocketPtr sock ATTRIBUTE_UNUSED) { @@ -963,6 +1128,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 virNetSocketLibSSHRead(sock, buf, len); +#endif + reread: if (sock->tlsSession && virNetTLSSessionGetHandshakeStatus(sock->tlsSession) == @@ -1012,6 +1183,12 @@ reread: static ssize_t virNetSocketWriteWire(virNetSocketPtr sock, const char *buf, size_t len) { ssize_t ret; + +#if HAVE_LIBSSH2 + if (sock->sshSession) + return virNetSocketLibSSHWrite(sock, buf, len); +#endif + rewrite: if (sock->tlsSession && virNetTLSSessionGetHandshakeStatus(sock->tlsSession) == @@ -1140,7 +1317,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 6c8e77c..adbdacf 100644 --- a/src/rpc/virnetsocket.h +++ b/src/rpc/virnetsocket.h @@ -74,6 +74,18 @@ int virNetSocketNewConnectSSH(const char *nodename, const char *path, virNetSocketPtr *addr); +int virNetSocketNewConnectLibSSH(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); @@ -102,6 +114,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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list