
On Tuesday, 1 November 2016 13:20:28 CET Peter Krempa wrote:
On Wed, Oct 19, 2016 at 14:40:36 +0200, Pino Toscano wrote:
Implement a new libssh transport, which uses libssh to communicate with remote hosts, and add all the build system stuff (search of libssh, private symbols, etc) to built it.
This new transport supports all the common ssh authentication methods, making use of libvirt's auth callbacks for interaction with the user. --- config-post.h | 2 + configure.ac | 3 + m4/virt-libssh.m4 | 26 + po/POTFILES.in | 1 + src/Makefile.am | 21 +- src/libvirt_libssh.syms | 22 + src/rpc/virnetlibsshsession.c | 1458 +++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetlibsshsession.h | 80 +++ 8 files changed, 1611 insertions(+), 2 deletions(-) create mode 100644 m4/virt-libssh.m4 create mode 100644 src/libvirt_libssh.syms create mode 100644 src/rpc/virnetlibsshsession.c create mode 100644 src/rpc/virnetlibsshsession.h
diff --git a/src/rpc/virnetlibsshsession.c b/src/rpc/virnetlibsshsession.c new file mode 100644 index 0000000..13dd52c --- /dev/null +++ b/src/rpc/virnetlibsshsession.c @@ -0,0 +1,1458 @@
[...]
+static void +virNetLibsshSessionAuthMethodsFree(virNetLibsshSessionPtr sess) +{ + size_t i; + + for (i = 0; i < sess->nauths; i++) { + VIR_FREE(sess->auths[i]->password);
This should be turned into VIR_DISPOSE_STR.
Right. (Note the same fix should be done in the libssh2 transport too.)
+ +/* string representation of public key of remote server */ +static char * +virSshServerKeyAsString(virNetLibsshSessionPtr sess) +{ + int ret; + ssh_key key; + unsigned char *keyhash; + size_t keyhashlen; + char *str; + + if (ssh_get_publickey(sess->session, &key) != SSH_OK) { + virReportError(VIR_ERR_LIBSSH, "%s", + _("failed to get the key of the current " + "session")); + return NULL; + } + + /* calculate remote key hash, using MD5 algorithm that is + * usual in OpenSSH. The returned value must be freed */ + ret = ssh_get_publickey_hash(key, SSH_PUBLICKEY_HASH_MD5, + &keyhash, &keyhashlen);
Openssh currently is using SHA256 with base64 encoding. I think we should at least for libssh switch to this newer approach.
Unfortunately the libssh API only allows SHA1 and MD5: enum ssh_publickey_hash_type { SSH_PUBLICKEY_HASH_SHA1, SSH_PUBLICKEY_HASH_MD5 }; libssh2 uses MD5, which is why I chose to do the same in this case. I will change it to SHA1, should be slightly better.
+void +virNetLibsshSessionAuthReset(virNetLibsshSessionPtr sess)
This function is not used anywhere in this series.
Neither is virNetSSHSessionAuthReset -- removed.
+/* perform keyboard interactive authentication + * + * returns SSH_AUTH_* values + */ +static int +virNetLibsshAuthenticateKeyboardInteractive(virNetLibsshSessionPtr sess, + virNetLibsshAuthMethodPtr priv) +{ + int ret; + const char *errmsg; + int try = 0; + + /* request user's key password */ + if (!sess->cred || !sess->cred->cb) { + virReportError(VIR_ERR_LIBSSH, "%s", + _("No user interaction callback provided: " + "Can't get input from keyboard interactive " + "authentication")); + return SSH_AUTH_ERROR; + } + + again:
Please use a while loop for this purpose.
+ ret = ssh_userauth_kbdint(sess->session, NULL, NULL); + while (ret == SSH_AUTH_INFO) { + const char *name, *instruction; + int nprompts, iprompt; + virBuffer buff = VIR_BUFFER_INITIALIZER; + + name = ssh_userauth_kbdint_getname(sess->session); + instruction = ssh_userauth_kbdint_getinstruction(sess->session); + nprompts = ssh_userauth_kbdint_getnprompts(sess->session); + + /* compose the main buffer with name and instruction, if present */ + if (name && name[0]) + virBufferAddStr(&buff, name); + if (instruction && instruction[0]) { + if (virBufferUse(&buff) > 0) + virBufferAddChar(&buff, '\n'); + virBufferAddStr(&buff, instruction); + } + + if (virBufferCheckError(&buff) < 0) + return -1; + + for (iprompt = 0; iprompt < nprompts; ++iprompt) { + virConnectCredential retr_passphrase; + const char *promptStr; + char echo; + virBuffer prompt_buff = VIR_BUFFER_INITIALIZER; + char *prompt = NULL; + int cred_type; + + /* get the prompt, stripping the trailing newlines and spaces */ + promptStr = ssh_userauth_kbdint_getprompt(sess->session, iprompt, + &echo); + + cred_type = virCredTypeForPrompt(sess->cred, echo); + if (cred_type == -1) { + virReportError(VIR_ERR_LIBSSH, "%s", + _("no suitable callback for input of keyboard " + "response")); + goto prompt_error; + } + + /* compose the instruction buffer with this prompt */ + if (virBufferUse(&buff) > 0) { + virBufferAddBuffer(&prompt_buff, &buff); + virBufferAddChar(&prompt_buff, '\n'); + } + virBufferAdd(&prompt_buff, promptStr, + virLengthForPromptString(promptStr)); + + if (virBufferCheckError(&prompt_buff) < 0) + goto prompt_error; + + prompt = virBufferContentAndReset(&prompt_buff); + + memset(&retr_passphrase, 0, sizeof(virConnectCredential)); + retr_passphrase.type = cred_type; + retr_passphrase.prompt = prompt; + + if (retr_passphrase.type == -1) { + virReportError(VIR_ERR_LIBSSH, "%s", + _("no suitable callback for input of key " + "passphrase")); + goto prompt_error; + } + + if (sess->cred->cb(&retr_passphrase, 1, sess->cred->cbdata)) { + virReportError(VIR_ERR_LIBSSH, "%s", + _("failed to retrieve keyboard interactive " + "result: callback has failed")); + goto prompt_error; + } + + VIR_FREE(prompt); + + ret = ssh_userauth_kbdint_setanswer(sess->session, iprompt, + retr_passphrase.result); + VIR_DISPOSE_STRING(retr_passphrase.result); + if (ret < 0) { + errmsg = ssh_get_error(sess->session); + virReportError(VIR_ERR_AUTH_FAILED, + _("authentication failed: %s"), errmsg); + goto prompt_error; + }
Maybe it would be better to extract this into a function since this looks rather complex.
How "granular" should this be split? Thanks, -- Pino Toscano