summaryrefslogtreecommitdiff
path: root/libs/libssh2/src/userauth.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libssh2/src/userauth.c')
-rw-r--r--libs/libssh2/src/userauth.c1917
1 files changed, 1917 insertions, 0 deletions
diff --git a/libs/libssh2/src/userauth.c b/libs/libssh2/src/userauth.c
new file mode 100644
index 0000000000..cdfa25e663
--- /dev/null
+++ b/libs/libssh2/src/userauth.c
@@ -0,0 +1,1917 @@
+/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2005 Mikhail Gusarov <dottedmag@dottedmag.net>
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include <assert.h>
+
+/* Needed for struct iovec on some platforms */
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#include "transport.h"
+#include "session.h"
+#include "userauth.h"
+
+/* libssh2_userauth_list
+ *
+ * List authentication methods
+ * Will yield successful login if "none" happens to be allowable for this user
+ * Not a common configuration for any SSH server though
+ * username should be NULL, or a null terminated string
+ */
+static char *userauth_list(LIBSSH2_SESSION *session, const char *username,
+ unsigned int username_len)
+{
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 };
+ /* packet_type(1) + username_len(4) + service_len(4) +
+ service(14)"ssh-connection" + method_len(4) = 27 */
+ unsigned long methods_len;
+ unsigned char *s;
+ int rc;
+
+ if (session->userauth_list_state == libssh2_NB_state_idle) {
+ /* Zero the whole thing out */
+ memset(&session->userauth_list_packet_requirev_state, 0,
+ sizeof(session->userauth_list_packet_requirev_state));
+
+ session->userauth_list_data_len = username_len + 27;
+
+ s = session->userauth_list_data =
+ LIBSSH2_ALLOC(session, session->userauth_list_data_len);
+ if (!session->userauth_list_data) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for userauth_list");
+ return NULL;
+ }
+
+ *(s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection", 14);
+ _libssh2_store_u32(&s, 4); /* send "none" separately */
+
+ session->userauth_list_state = libssh2_NB_state_created;
+ }
+
+ if (session->userauth_list_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_list_data,
+ session->userauth_list_data_len,
+ (unsigned char *)"none", 4);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting userauth list");
+ return NULL;
+ }
+ /* now free the packet that was sent */
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ session->userauth_list_data = NULL;
+
+ if (rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-none request");
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ session->userauth_list_state = libssh2_NB_state_sent;
+ }
+
+ if (session->userauth_list_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_list_data,
+ &session->userauth_list_data_len, 0,
+ NULL, 0,
+ &session->userauth_list_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting userauth list");
+ return NULL;
+ } else if (rc) {
+ _libssh2_error(session, rc, "Failed getting response");
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ if (session->userauth_list_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ /* Wow, who'dve thought... */
+ _libssh2_error(session, LIBSSH2_ERROR_NONE, "No error");
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ session->userauth_list_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ methods_len = _libssh2_ntohu32(session->userauth_list_data + 1);
+
+ /* Do note that the memory areas overlap! */
+ memmove(session->userauth_list_data, session->userauth_list_data + 5,
+ methods_len);
+ session->userauth_list_data[methods_len] = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Permitted auth methods: %s",
+ session->userauth_list_data);
+ }
+
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return (char *) session->userauth_list_data;
+}
+
+/* libssh2_userauth_list
+ *
+ * List authentication methods
+ * Will yield successful login if "none" happens to be allowable for this user
+ * Not a common configuration for any SSH server though
+ * username should be NULL, or a null terminated string
+ */
+LIBSSH2_API char *
+libssh2_userauth_list(LIBSSH2_SESSION * session, const char *user,
+ unsigned int user_len)
+{
+ char *ptr;
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ userauth_list(session, user, user_len));
+ return ptr;
+}
+
+/*
+ * libssh2_userauth_authenticated
+ *
+ * Returns: 0 if not yet authenticated
+ * 1 if already authenticated
+ */
+LIBSSH2_API int
+libssh2_userauth_authenticated(LIBSSH2_SESSION * session)
+{
+ return (session->state & LIBSSH2_STATE_AUTHENTICATED)?1:0;
+}
+
+
+
+/* userauth_password
+ * Plain ol' login
+ */
+static int
+userauth_password(LIBSSH2_SESSION *session,
+ const char *username, unsigned int username_len,
+ const unsigned char *password, unsigned int password_len,
+ LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)))
+{
+ unsigned char *s;
+ static const unsigned char reply_codes[4] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE,
+ SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 0
+ };
+ int rc;
+
+ if (session->userauth_pswd_state == libssh2_NB_state_idle) {
+ /* Zero the whole thing out */
+ memset(&session->userauth_pswd_packet_requirev_state, 0,
+ sizeof(session->userauth_pswd_packet_requirev_state));
+
+ /*
+ * 40 = packet_type(1) + username_len(4) + service_len(4) +
+ * service(14)"ssh-connection" + method_len(4) + method(8)"password" +
+ * chgpwdbool(1) + password_len(4) */
+ session->userauth_pswd_data_len = username_len + 40;
+
+ session->userauth_pswd_data0 =
+ (unsigned char) ~SSH_MSG_USERAUTH_PASSWD_CHANGEREQ;
+
+ /* TODO: remove this alloc with a fixed buffer in the session
+ struct */
+ s = session->userauth_pswd_data =
+ LIBSSH2_ALLOC(session, session->userauth_pswd_data_len);
+ if (!session->userauth_pswd_data) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "userauth-password request");
+ }
+
+ *(s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection", sizeof("ssh-connection") - 1);
+ _libssh2_store_str(&s, "password", sizeof("password") - 1);
+ *s++ = '\0';
+ _libssh2_store_u32(&s, password_len);
+ /* 'password' is sent separately */
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting to login using password authentication");
+
+ session->userauth_pswd_state = libssh2_NB_state_created;
+ }
+
+ if (session->userauth_pswd_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_pswd_data,
+ session->userauth_pswd_data_len,
+ password, password_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block writing password request");
+ }
+
+ /* now free the sent packet */
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+
+ if (rc) {
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-password request");
+ }
+
+ session->userauth_pswd_state = libssh2_NB_state_sent;
+ }
+
+ password_response:
+
+ if ((session->userauth_pswd_state == libssh2_NB_state_sent)
+ || (session->userauth_pswd_state == libssh2_NB_state_sent1)
+ || (session->userauth_pswd_state == libssh2_NB_state_sent2)) {
+ if (session->userauth_pswd_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_pswd_data,
+ &session->userauth_pswd_data_len,
+ 0, NULL, 0,
+ &session->
+ userauth_pswd_packet_requirev_state);
+
+ if (rc) {
+ if (rc != LIBSSH2_ERROR_EAGAIN)
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+
+ return _libssh2_error(session, rc,
+ "Waiting for password response");
+ }
+
+ if (session->userauth_pswd_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Password authentication successful");
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return 0;
+ } else if (session->userauth_pswd_data[0] == SSH_MSG_USERAUTH_FAILURE) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Password authentication failed");
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Authentication failed "
+ "(username/password)");
+ }
+
+ session->userauth_pswd_newpw = NULL;
+ session->userauth_pswd_newpw_len = 0;
+
+ session->userauth_pswd_state = libssh2_NB_state_sent1;
+ }
+
+ if ((session->userauth_pswd_data[0] ==
+ SSH_MSG_USERAUTH_PASSWD_CHANGEREQ)
+ || (session->userauth_pswd_data0 ==
+ SSH_MSG_USERAUTH_PASSWD_CHANGEREQ)) {
+ session->userauth_pswd_data0 = SSH_MSG_USERAUTH_PASSWD_CHANGEREQ;
+
+ if ((session->userauth_pswd_state == libssh2_NB_state_sent1) ||
+ (session->userauth_pswd_state == libssh2_NB_state_sent2)) {
+ if (session->userauth_pswd_state == libssh2_NB_state_sent1) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Password change required");
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ }
+ if (passwd_change_cb) {
+ if (session->userauth_pswd_state == libssh2_NB_state_sent1) {
+ passwd_change_cb(session,
+ &session->userauth_pswd_newpw,
+ &session->userauth_pswd_newpw_len,
+ &session->abstract);
+ if (!session->userauth_pswd_newpw) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_PASSWORD_EXPIRED,
+ "Password expired, and "
+ "callback failed");
+ }
+
+ /* basic data_len + newpw_len(4) */
+ session->userauth_pswd_data_len =
+ username_len + password_len + 44;
+
+ s = session->userauth_pswd_data =
+ LIBSSH2_ALLOC(session,
+ session->userauth_pswd_data_len);
+ if (!session->userauth_pswd_data) {
+ LIBSSH2_FREE(session,
+ session->userauth_pswd_newpw);
+ session->userauth_pswd_newpw = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory "
+ "for userauth password "
+ "change request");
+ }
+
+ *(s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection",
+ sizeof("ssh-connection") - 1);
+ _libssh2_store_str(&s, "password",
+ sizeof("password") - 1);
+ *s++ = 0x01;
+ _libssh2_store_str(&s, (char *)password, password_len);
+ _libssh2_store_u32(&s,
+ session->userauth_pswd_newpw_len);
+ /* send session->userauth_pswd_newpw separately */
+
+ session->userauth_pswd_state = libssh2_NB_state_sent2;
+ }
+
+ if (session->userauth_pswd_state == libssh2_NB_state_sent2) {
+ rc = _libssh2_transport_send(session,
+ session->userauth_pswd_data,
+ session->userauth_pswd_data_len,
+ (unsigned char *)
+ session->userauth_pswd_newpw,
+ session->userauth_pswd_newpw_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting");
+ }
+
+ /* free the allocated packets again */
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ LIBSSH2_FREE(session, session->userauth_pswd_newpw);
+ session->userauth_pswd_newpw = NULL;
+
+ if (rc) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth "
+ "password-change request");
+ }
+
+ /*
+ * Ugliest use of goto ever. Blame it on the
+ * askN => requirev migration.
+ */
+ session->userauth_pswd_state = libssh2_NB_state_sent;
+ goto password_response;
+ }
+ }
+ } else {
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED,
+ "Password Expired, and no callback "
+ "specified");
+ }
+ }
+ }
+
+ /* FAILURE */
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Authentication failed");
+}
+
+/*
+ * libssh2_userauth_password_ex
+ *
+ * Plain ol' login
+ */
+
+LIBSSH2_API int
+libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username,
+ unsigned int username_len, const char *password,
+ unsigned int password_len,
+ LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)))
+{
+ int rc;
+ BLOCK_ADJUST(rc, session,
+ userauth_password(session, username, username_len,
+ (unsigned char *)password, password_len,
+ passwd_change_cb));
+ return rc;
+}
+
+static int
+memory_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *pubkeyfiledata,
+ size_t pubkeyfiledata_len)
+{
+ unsigned char *pubkey = NULL, *sp1, *sp2, *tmp;
+ size_t pubkey_len = pubkeyfiledata_len;
+ unsigned int tmp_len;
+
+ if (pubkeyfiledata_len <= 1) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid data in public key file");
+ }
+
+ pubkey = LIBSSH2_ALLOC(session, pubkeyfiledata_len);
+ if (!pubkey) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for public key data");
+ }
+
+ memcpy(pubkey, pubkeyfiledata, pubkeyfiledata_len);
+
+ /*
+ * Remove trailing whitespace
+ */
+ while (pubkey_len && isspace(pubkey[pubkey_len - 1]))
+ pubkey_len--;
+
+ if (!pubkey_len) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Missing public key data");
+ }
+
+ if ((sp1 = memchr(pubkey, ' ', pubkey_len)) == NULL) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid public key data");
+ }
+
+ sp1++;
+
+ if ((sp2 = memchr(sp1, ' ', pubkey_len - (sp1 - pubkey - 1))) == NULL) {
+ /* Assume that the id string is missing, but that it's okay */
+ sp2 = pubkey + pubkey_len;
+ }
+
+ if (libssh2_base64_decode(session, (char **) &tmp, &tmp_len,
+ (char *) sp1, sp2 - sp1)) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid key data, not base64 encoded");
+ }
+
+ /* Wasting some bytes here (okay, more than some), but since it's likely
+ * to be freed soon anyway, we'll just avoid the extra free/alloc and call
+ * it a wash
+ */
+ *method = pubkey;
+ *method_len = sp1 - pubkey - 1;
+
+ *pubkeydata = tmp;
+ *pubkeydata_len = tmp_len;
+
+ return 0;
+}
+
+/*
+ * file_read_publickey
+ *
+ * Read a public key from an id_???.pub style file
+ *
+ * Returns an allocated string containing the decoded key in *pubkeydata
+ * on success.
+ * Returns an allocated string containing the key method (e.g. "ssh-dss")
+ * in method on success.
+ */
+static int
+file_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *pubkeyfile)
+{
+ FILE *fd;
+ char c;
+ unsigned char *pubkey = NULL, *sp1, *sp2, *tmp;
+ size_t pubkey_len = 0, sp_len;
+ unsigned int tmp_len;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Loading public key file: %s",
+ pubkeyfile);
+ /* Read Public Key */
+ fd = fopen(pubkeyfile, "r");
+ if (!fd) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to open public key file");
+ }
+ while (!feof(fd) && 1 == fread(&c, 1, 1, fd) && c != '\r' && c != '\n') {
+ pubkey_len++;
+ }
+ rewind(fd);
+
+ if (pubkey_len <= 1) {
+ fclose(fd);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid data in public key file");
+ }
+
+ pubkey = LIBSSH2_ALLOC(session, pubkey_len);
+ if (!pubkey) {
+ fclose(fd);
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for public key data");
+ }
+ if (fread(pubkey, 1, pubkey_len, fd) != pubkey_len) {
+ LIBSSH2_FREE(session, pubkey);
+ fclose(fd);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to read public key from file");
+ }
+ fclose(fd);
+ /*
+ * Remove trailing whitespace
+ */
+ while (pubkey_len && isspace(pubkey[pubkey_len - 1])) {
+ pubkey_len--;
+ }
+
+ if (!pubkey_len) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Missing public key data");
+ }
+
+ if ((sp1 = memchr(pubkey, ' ', pubkey_len)) == NULL) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid public key data");
+ }
+
+ sp1++;
+
+ sp_len = sp1 > pubkey ? (sp1 - pubkey) - 1 : 0;
+ if ((sp2 = memchr(sp1, ' ', pubkey_len - sp_len)) == NULL) {
+ /* Assume that the id string is missing, but that it's okay */
+ sp2 = pubkey + pubkey_len;
+ }
+
+ if (libssh2_base64_decode(session, (char **) &tmp, &tmp_len,
+ (char *) sp1, sp2 - sp1)) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid key data, not base64 encoded");
+ }
+
+ /* Wasting some bytes here (okay, more than some), but since it's likely
+ * to be freed soon anyway, we'll just avoid the extra free/alloc and call
+ * it a wash */
+ *method = pubkey;
+ *method_len = sp1 - pubkey - 1;
+
+ *pubkeydata = tmp;
+ *pubkeydata_len = tmp_len;
+
+ return 0;
+}
+
+static int
+memory_read_privatekey(LIBSSH2_SESSION * session,
+ const LIBSSH2_HOSTKEY_METHOD ** hostkey_method,
+ void **hostkey_abstract,
+ const unsigned char *method, int method_len,
+ const char *privkeyfiledata, size_t privkeyfiledata_len,
+ const char *passphrase)
+{
+ const LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail =
+ libssh2_hostkey_methods();
+
+ *hostkey_method = NULL;
+ *hostkey_abstract = NULL;
+ while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) {
+ if ((*hostkey_methods_avail)->initPEMFromMemory
+ && strncmp((*hostkey_methods_avail)->name, (const char *) method,
+ method_len) == 0) {
+ *hostkey_method = *hostkey_methods_avail;
+ break;
+ }
+ hostkey_methods_avail++;
+ }
+ if (!*hostkey_method) {
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
+ "No handler for specified private key");
+ }
+
+ if ((*hostkey_method)->
+ initPEMFromMemory(session, privkeyfiledata, privkeyfiledata_len,
+ (unsigned char *) passphrase,
+ hostkey_abstract)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to initialize private key from file");
+ }
+
+ return 0;
+}
+
+/* libssh2_file_read_privatekey
+ * Read a PEM encoded private key from an id_??? style file
+ */
+static int
+file_read_privatekey(LIBSSH2_SESSION * session,
+ const LIBSSH2_HOSTKEY_METHOD ** hostkey_method,
+ void **hostkey_abstract,
+ const unsigned char *method, int method_len,
+ const char *privkeyfile, const char *passphrase)
+{
+ const LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail =
+ libssh2_hostkey_methods();
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Loading private key file: %s",
+ privkeyfile);
+ *hostkey_method = NULL;
+ *hostkey_abstract = NULL;
+ while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) {
+ if ((*hostkey_methods_avail)->initPEM
+ && strncmp((*hostkey_methods_avail)->name, (const char *) method,
+ method_len) == 0) {
+ *hostkey_method = *hostkey_methods_avail;
+ break;
+ }
+ hostkey_methods_avail++;
+ }
+ if (!*hostkey_method) {
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
+ "No handler for specified private key");
+ }
+
+ if ((*hostkey_method)->
+ initPEM(session, privkeyfile, (unsigned char *) passphrase,
+ hostkey_abstract)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to initialize private key from file");
+ }
+
+ return 0;
+}
+
+struct privkey_file {
+ const char *filename;
+ const char *passphrase;
+};
+
+static int
+sign_frommemory(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
+ const unsigned char *data, size_t data_len, void **abstract)
+{
+ struct privkey_file *pk_file = (struct privkey_file *) (*abstract);
+ const LIBSSH2_HOSTKEY_METHOD *privkeyobj;
+ void *hostkey_abstract;
+ struct iovec datavec;
+ int rc;
+
+ rc = memory_read_privatekey(session, &privkeyobj, &hostkey_abstract,
+ session->userauth_pblc_method,
+ session->userauth_pblc_method_len,
+ pk_file->filename,
+ strlen(pk_file->filename),
+ pk_file->passphrase);
+ if(rc)
+ return rc;
+
+ libssh2_prepare_iovec(&datavec, 1);
+ datavec.iov_base = (void *)data;
+ datavec.iov_len = data_len;
+
+ if (privkeyobj->signv(session, sig, sig_len, 1, &datavec,
+ &hostkey_abstract)) {
+ if (privkeyobj->dtor) {
+ privkeyobj->dtor(session, abstract);
+ }
+ return -1;
+ }
+
+ if (privkeyobj->dtor) {
+ privkeyobj->dtor(session, &hostkey_abstract);
+ }
+ return 0;
+}
+
+static int
+sign_fromfile(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
+ const unsigned char *data, size_t data_len, void **abstract)
+{
+ struct privkey_file *privkey_file = (struct privkey_file *) (*abstract);
+ const LIBSSH2_HOSTKEY_METHOD *privkeyobj;
+ void *hostkey_abstract;
+ struct iovec datavec;
+ int rc;
+
+ rc = file_read_privatekey(session, &privkeyobj, &hostkey_abstract,
+ session->userauth_pblc_method,
+ session->userauth_pblc_method_len,
+ privkey_file->filename,
+ privkey_file->passphrase);
+ if(rc)
+ return rc;
+
+ libssh2_prepare_iovec(&datavec, 1);
+ datavec.iov_base = (void *)data;
+ datavec.iov_len = data_len;
+
+ if (privkeyobj->signv(session, sig, sig_len, 1, &datavec,
+ &hostkey_abstract)) {
+ if (privkeyobj->dtor) {
+ privkeyobj->dtor(session, &hostkey_abstract);
+ }
+ return -1;
+ }
+
+ if (privkeyobj->dtor) {
+ privkeyobj->dtor(session, &hostkey_abstract);
+ }
+ return 0;
+}
+
+
+
+/* userauth_hostbased_fromfile
+ * Authenticate using a keypair found in the named files
+ */
+static int
+userauth_hostbased_fromfile(LIBSSH2_SESSION *session,
+ const char *username, size_t username_len,
+ const char *publickey, const char *privatekey,
+ const char *passphrase, const char *hostname,
+ size_t hostname_len,
+ const char *local_username,
+ size_t local_username_len)
+{
+ int rc;
+
+ if (session->userauth_host_state == libssh2_NB_state_idle) {
+ const LIBSSH2_HOSTKEY_METHOD *privkeyobj;
+ unsigned char *pubkeydata, *sig = NULL;
+ size_t pubkeydata_len = 0;
+ size_t sig_len = 0;
+ void *abstract;
+ unsigned char buf[5];
+ struct iovec datavec[4];
+
+ /* Zero the whole thing out */
+ memset(&session->userauth_host_packet_requirev_state, 0,
+ sizeof(session->userauth_host_packet_requirev_state));
+
+ if (publickey) {
+ rc = file_read_publickey(session, &session->userauth_host_method,
+ &session->userauth_host_method_len,
+ &pubkeydata, &pubkeydata_len, publickey);
+ if(rc)
+ /* Note: file_read_publickey() calls _libssh2_error() */
+ return rc;
+ }
+ else {
+ /* Compute public key from private key. */
+ rc = _libssh2_pub_priv_keyfile(session,
+ &session->userauth_host_method,
+ &session->userauth_host_method_len,
+ &pubkeydata, &pubkeydata_len,
+ privatekey, passphrase);
+ if (rc)
+ /* libssh2_pub_priv_keyfile calls _libssh2_error() */
+ return rc;
+ }
+
+ /*
+ * 52 = packet_type(1) + username_len(4) + servicename_len(4) +
+ * service_name(14)"ssh-connection" + authmethod_len(4) +
+ * authmethod(9)"hostbased" + method_len(4) + pubkeydata_len(4) +
+ * hostname_len(4) + local_username_len(4)
+ */
+ session->userauth_host_packet_len =
+ username_len + session->userauth_host_method_len + hostname_len +
+ local_username_len + pubkeydata_len + 52;
+
+ /*
+ * Preallocate space for an overall length, method name again,
+ * and the signature, which won't be any larger than the size of
+ * the publickeydata itself
+ */
+ session->userauth_host_s = session->userauth_host_packet =
+ LIBSSH2_ALLOC(session,
+ session->userauth_host_packet_len + 4 +
+ (4 + session->userauth_host_method_len) +
+ (4 + pubkeydata_len));
+ if (!session->userauth_host_packet) {
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ LIBSSH2_FREE(session, pubkeydata);
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Out of memory");
+ }
+
+ *(session->userauth_host_s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&session->userauth_host_s, username, username_len);
+ _libssh2_store_str(&session->userauth_host_s, "ssh-connection", 14);
+ _libssh2_store_str(&session->userauth_host_s, "hostbased", 9);
+ _libssh2_store_str(&session->userauth_host_s,
+ (const char *)session->userauth_host_method,
+ session->userauth_host_method_len);
+ _libssh2_store_str(&session->userauth_host_s, (const char *)pubkeydata,
+ pubkeydata_len);
+ LIBSSH2_FREE(session, pubkeydata);
+ _libssh2_store_str(&session->userauth_host_s, hostname, hostname_len);
+ _libssh2_store_str(&session->userauth_host_s, local_username,
+ local_username_len);
+
+ rc = file_read_privatekey(session, &privkeyobj, &abstract,
+ session->userauth_host_method,
+ session->userauth_host_method_len,
+ privatekey, passphrase);
+ if(rc) {
+ /* Note: file_read_privatekey() calls _libssh2_error() */
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ return rc;
+ }
+
+ _libssh2_htonu32(buf, session->session_id_len);
+ libssh2_prepare_iovec(datavec, 4);
+ datavec[0].iov_base = (void *)buf;
+ datavec[0].iov_len = 4;
+ datavec[1].iov_base = (void *)session->session_id;
+ datavec[1].iov_len = session->session_id_len;
+ datavec[2].iov_base = (void *)session->userauth_host_packet;
+ datavec[2].iov_len = session->userauth_host_packet_len;
+
+ if (privkeyobj && privkeyobj->signv &&
+ privkeyobj->signv(session, &sig, &sig_len, 3,
+ datavec, &abstract)) {
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ if (privkeyobj->dtor) {
+ privkeyobj->dtor(session, &abstract);
+ }
+ return -1;
+ }
+
+ if (privkeyobj && privkeyobj->dtor) {
+ privkeyobj->dtor(session, &abstract);
+ }
+
+ if (sig_len > pubkeydata_len) {
+ unsigned char *newpacket;
+ /* Should *NEVER* happen, but...well.. better safe than sorry */
+ newpacket = LIBSSH2_REALLOC(session, session->userauth_host_packet,
+ session->userauth_host_packet_len + 4 +
+ (4 + session->userauth_host_method_len)
+ + (4 + sig_len)); /* PK sigblob */
+ if (!newpacket) {
+ LIBSSH2_FREE(session, sig);
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed allocating additional space for "
+ "userauth-hostbased packet");
+ }
+ session->userauth_host_packet = newpacket;
+ }
+
+ session->userauth_host_s =
+ session->userauth_host_packet + session->userauth_host_packet_len;
+
+ _libssh2_store_u32(&session->userauth_host_s,
+ 4 + session->userauth_host_method_len + 4 + sig_len);
+ _libssh2_store_str(&session->userauth_host_s,
+ (const char *)session->userauth_host_method,
+ session->userauth_host_method_len);
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+
+ _libssh2_store_str(&session->userauth_host_s, (const char *)sig,
+ sig_len);
+ LIBSSH2_FREE(session, sig);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting hostbased authentication");
+
+ session->userauth_host_state = libssh2_NB_state_created;
+ }
+
+ if (session->userauth_host_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_host_packet,
+ session->userauth_host_s -
+ session->userauth_host_packet,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ }
+ else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ session->userauth_host_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-hostbased request");
+ }
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+
+ session->userauth_host_state = libssh2_NB_state_sent;
+ }
+
+ if (session->userauth_host_state == libssh2_NB_state_sent) {
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 };
+ size_t data_len;
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_host_data,
+ &data_len, 0, NULL, 0,
+ &session->
+ userauth_host_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ }
+
+ session->userauth_host_state = libssh2_NB_state_idle;
+ if (rc) {
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Auth failed");
+ }
+
+ if (session->userauth_host_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Hostbased authentication successful");
+ /* We are us and we've proved it. */
+ LIBSSH2_FREE(session, session->userauth_host_data);
+ session->userauth_host_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ return 0;
+ }
+ }
+
+ /* This public key is not allowed for this user on this server */
+ LIBSSH2_FREE(session, session->userauth_host_data);
+ session->userauth_host_data = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid signature for supplied public key, or bad "
+ "username/public key combination");
+}
+
+/* libssh2_userauth_hostbased_fromfile_ex
+ * Authenticate using a keypair found in the named files
+ */
+LIBSSH2_API int
+libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session,
+ const char *user,
+ unsigned int user_len,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase,
+ const char *host,
+ unsigned int host_len,
+ const char *localuser,
+ unsigned int localuser_len)
+{
+ int rc;
+ BLOCK_ADJUST(rc, session,
+ userauth_hostbased_fromfile(session, user, user_len,
+ publickey, privatekey,
+ passphrase, host, host_len,
+ localuser, localuser_len));
+ return rc;
+}
+
+
+
+int
+_libssh2_userauth_publickey(LIBSSH2_SESSION *session,
+ const char *username,
+ unsigned int username_len,
+ const unsigned char *pubkeydata,
+ unsigned long pubkeydata_len,
+ LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)),
+ void *abstract)
+{
+ unsigned char reply_codes[4] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE,
+ SSH_MSG_USERAUTH_PK_OK, 0
+ };
+ int rc;
+ unsigned char *s;
+
+ if (session->userauth_pblc_state == libssh2_NB_state_idle) {
+
+ /*
+ * The call to _libssh2_ntohu32 later relies on pubkeydata having at
+ * least 4 valid bytes containing the length of the method name.
+ */
+ if (pubkeydata_len < 4)
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid public key, too short");
+
+ /* Zero the whole thing out */
+ memset(&session->userauth_pblc_packet_requirev_state, 0,
+ sizeof(session->userauth_pblc_packet_requirev_state));
+
+ /*
+ * As an optimisation, userauth_publickey_fromfile reuses a
+ * previously allocated copy of the method name to avoid an extra
+ * allocation/free.
+ * For other uses, we allocate and populate it here.
+ */
+ if (!session->userauth_pblc_method) {
+ session->userauth_pblc_method_len = _libssh2_ntohu32(pubkeydata);
+
+ if(session->userauth_pblc_method_len > pubkeydata_len)
+ /* the method length simply cannot be longer than the entire
+ passed in data, so we use this to detect crazy input
+ data */
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid public key");
+
+ session->userauth_pblc_method =
+ LIBSSH2_ALLOC(session, session->userauth_pblc_method_len);
+ if (!session->userauth_pblc_method) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for public key "
+ "data");
+ }
+ memcpy(session->userauth_pblc_method, pubkeydata + 4,
+ session->userauth_pblc_method_len);
+ }
+ /*
+ * The length of the method name read from plaintext prefix in the
+ * file must match length embedded in the key.
+ * TODO: The data should match too but we don't check that. Should we?
+ */
+ else if (session->userauth_pblc_method_len !=
+ _libssh2_ntohu32(pubkeydata))
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid public key");
+
+ /*
+ * 45 = packet_type(1) + username_len(4) + servicename_len(4) +
+ * service_name(14)"ssh-connection" + authmethod_len(4) +
+ * authmethod(9)"publickey" + sig_included(1)'\0' + algmethod_len(4) +
+ * publickey_len(4)
+ */
+ session->userauth_pblc_packet_len =
+ username_len + session->userauth_pblc_method_len + pubkeydata_len +
+ 45;
+
+ /*
+ * Preallocate space for an overall length, method name again, and the
+ * signature, which won't be any larger than the size of the
+ * publickeydata itself.
+ *
+ * Note that the 'pubkeydata_len' extra bytes allocated here will not
+ * be used in this first send, but will be used in the later one where
+ * this same allocation is re-used.
+ */
+ s = session->userauth_pblc_packet =
+ LIBSSH2_ALLOC(session,
+ session->userauth_pblc_packet_len + 4 +
+ (4 + session->userauth_pblc_method_len)
+ + (4 + pubkeydata_len));
+ if (!session->userauth_pblc_packet) {
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Out of memory");
+ }
+
+ *s++ = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection", 14);
+ _libssh2_store_str(&s, "publickey", 9);
+
+ session->userauth_pblc_b = s;
+ /* Not sending signature with *this* packet */
+ *s++ = 0;
+
+ _libssh2_store_str(&s, (const char *)session->userauth_pblc_method,
+ session->userauth_pblc_method_len);
+ _libssh2_store_str(&s, (const char *)pubkeydata, pubkeydata_len);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting publickey authentication");
+
+ session->userauth_pblc_state = libssh2_NB_state_created;
+ }
+
+ if (session->userauth_pblc_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_pblc_packet,
+ session->userauth_pblc_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-publickey request");
+ }
+
+ session->userauth_pblc_state = libssh2_NB_state_sent;
+ }
+
+ if (session->userauth_pblc_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_pblc_data,
+ &session->userauth_pblc_data_len, 0,
+ NULL, 0,
+ &session->
+ userauth_pblc_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ }
+ else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Waiting for USERAUTH response");
+ }
+
+ if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Pubkey authentication prematurely successful");
+ /*
+ * God help any SSH server that allows an UNVERIFIED
+ * public key to validate the user
+ */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_FAILURE) {
+ /* This public key is not allowed for this user on this server */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Username/PublicKey combination invalid");
+ }
+
+ /* Semi-Success! */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+
+ *session->userauth_pblc_b = 0x01;
+ session->userauth_pblc_state = libssh2_NB_state_sent1;
+ }
+
+ if (session->userauth_pblc_state == libssh2_NB_state_sent1) {
+ unsigned char *buf;
+ unsigned char *sig;
+ size_t sig_len;
+
+ s = buf = LIBSSH2_ALLOC(session, 4 + session->session_id_len
+ + session->userauth_pblc_packet_len);
+ if (!buf) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "userauth-publickey signed data");
+ }
+
+ _libssh2_store_str(&s, (const char *)session->session_id,
+ session->session_id_len);
+
+ memcpy (s, session->userauth_pblc_packet,
+ session->userauth_pblc_packet_len);
+ s += session->userauth_pblc_packet_len;
+
+ rc = sign_callback(session, &sig, &sig_len, buf, s - buf, abstract);
+ LIBSSH2_FREE(session, buf);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ } else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Callback returned error");
+ }
+
+ /*
+ * If this function was restarted, pubkeydata_len might still be 0
+ * which will cause an unnecessary but harmless realloc here.
+ */
+ if (sig_len > pubkeydata_len) {
+ unsigned char *newpacket;
+ /* Should *NEVER* happen, but...well.. better safe than sorry */
+ newpacket = LIBSSH2_REALLOC(session,
+ session->userauth_pblc_packet,
+ session->userauth_pblc_packet_len + 4 +
+ (4 + session->userauth_pblc_method_len)
+ + (4 + sig_len)); /* PK sigblob */
+ if (!newpacket) {
+ LIBSSH2_FREE(session, sig);
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed allocating additional space for "
+ "userauth-publickey packet");
+ }
+ session->userauth_pblc_packet = newpacket;
+ }
+
+ s = session->userauth_pblc_packet + session->userauth_pblc_packet_len;
+ session->userauth_pblc_b = NULL;
+
+ _libssh2_store_u32(&s,
+ 4 + session->userauth_pblc_method_len + 4 + sig_len);
+ _libssh2_store_str(&s, (const char *)session->userauth_pblc_method,
+ session->userauth_pblc_method_len);
+
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+
+ _libssh2_store_str(&s, (const char *)sig, sig_len);
+ LIBSSH2_FREE(session, sig);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting publickey authentication -- phase 2");
+
+ session->userauth_pblc_s = s;
+ session->userauth_pblc_state = libssh2_NB_state_sent2;
+ }
+
+ if (session->userauth_pblc_state == libssh2_NB_state_sent2) {
+ rc = _libssh2_transport_send(session, session->userauth_pblc_packet,
+ session->userauth_pblc_s -
+ session->userauth_pblc_packet,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ } else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-publickey request");
+ }
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+
+ session->userauth_pblc_state = libssh2_NB_state_sent3;
+ }
+
+ /* PK_OK is no longer valid */
+ reply_codes[2] = 0;
+
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_pblc_data,
+ &session->userauth_pblc_data_len, 0, NULL, 0,
+ &session->userauth_pblc_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting userauth list");
+ } else if (rc) {
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Waiting for publickey USERAUTH response");
+ }
+
+ if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Publickey authentication successful");
+ /* We are us and we've proved it. */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ /* This public key is not allowed for this user on this server */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid signature for supplied public key, or bad "
+ "username/public key combination");
+}
+
+ /*
+ * userauth_publickey_frommemory
+ * Authenticate using a keypair from memory
+ */
+static int
+userauth_publickey_frommemory(LIBSSH2_SESSION *session,
+ const char *username,
+ size_t username_len,
+ const char *publickeydata,
+ size_t publickeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+ unsigned char *pubkeydata = NULL;
+ size_t pubkeydata_len = 0;
+ struct privkey_file privkey_file;
+ void *abstract = &privkey_file;
+ int rc;
+
+ privkey_file.filename = privatekeydata;
+ privkey_file.passphrase = passphrase;
+
+ if (session->userauth_pblc_state == libssh2_NB_state_idle) {
+ if (publickeydata_len && publickeydata) {
+ rc = memory_read_publickey(session, &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,
+ publickeydata, publickeydata_len);
+ if(rc)
+ return rc;
+ }
+ else if (privatekeydata_len && privatekeydata) {
+ /* Compute public key from private key. */
+ if (_libssh2_pub_priv_keyfilememory(session,
+ &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,
+ privatekeydata, privatekeydata_len,
+ passphrase))
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to extract public key "
+ "from private key.");
+ }
+ else {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid data in public and private key.");
+ }
+ }
+
+ rc = _libssh2_userauth_publickey(session, username, username_len,
+ pubkeydata, pubkeydata_len,
+ sign_frommemory, &abstract);
+ if(pubkeydata)
+ LIBSSH2_FREE(session, pubkeydata);
+
+ return rc;
+}
+
+/*
+ * userauth_publickey_fromfile
+ * Authenticate using a keypair found in the named files
+ */
+static int
+userauth_publickey_fromfile(LIBSSH2_SESSION *session,
+ const char *username,
+ size_t username_len,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+ unsigned char *pubkeydata = NULL;
+ size_t pubkeydata_len = 0;
+ struct privkey_file privkey_file;
+ void *abstract = &privkey_file;
+ int rc;
+
+ privkey_file.filename = privatekey;
+ privkey_file.passphrase = passphrase;
+
+ if (session->userauth_pblc_state == libssh2_NB_state_idle) {
+ if (publickey) {
+ rc = file_read_publickey(session, &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,publickey);
+ if (rc)
+ return rc;
+ }
+ else {
+ /* Compute public key from private key. */
+ rc = _libssh2_pub_priv_keyfile(session,
+ &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,
+ privatekey, passphrase);
+
+ /* _libssh2_pub_priv_keyfile calls _libssh2_error() */
+ if (rc)
+ return rc;
+ }
+ }
+
+ rc = _libssh2_userauth_publickey(session, username, username_len,
+ pubkeydata, pubkeydata_len,
+ sign_fromfile, &abstract);
+ if(pubkeydata)
+ LIBSSH2_FREE(session, pubkeydata);
+
+ return rc;
+}
+
+/* libssh2_userauth_publickey_frommemory
+ * Authenticate using a keypair from memory
+ */
+LIBSSH2_API int
+libssh2_userauth_publickey_frommemory(LIBSSH2_SESSION *session,
+ const char *user,
+ size_t user_len,
+ const char *publickeyfiledata,
+ size_t publickeyfiledata_len,
+ const char *privatekeyfiledata,
+ size_t privatekeyfiledata_len,
+ const char *passphrase)
+{
+ int rc;
+
+ if(NULL == passphrase)
+ /* if given a NULL pointer, make it point to a zero-length
+ string to save us from having to check this all over */
+ passphrase="";
+
+ BLOCK_ADJUST(rc, session,
+ userauth_publickey_frommemory(session, user, user_len,
+ publickeyfiledata,
+ publickeyfiledata_len,
+ privatekeyfiledata,
+ privatekeyfiledata_len,
+ passphrase));
+ return rc;
+}
+
+/* libssh2_userauth_publickey_fromfile_ex
+ * Authenticate using a keypair found in the named files
+ */
+LIBSSH2_API int
+libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
+ const char *user,
+ unsigned int user_len,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+ int rc;
+
+ if(NULL == passphrase)
+ /* if given a NULL pointer, make it point to a zero-length
+ string to save us from having to check this all over */
+ passphrase="";
+
+ BLOCK_ADJUST(rc, session,
+ userauth_publickey_fromfile(session, user, user_len,
+ publickey, privatekey,
+ passphrase));
+ return rc;
+}
+
+/* libssh2_userauth_publickey_ex
+ * Authenticate using an external callback function
+ */
+LIBSSH2_API int
+libssh2_userauth_publickey(LIBSSH2_SESSION *session,
+ const char *user,
+ const unsigned char *pubkeydata,
+ size_t pubkeydata_len,
+ LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)),
+ void **abstract)
+{
+ int rc;
+
+ if(!session)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, session,
+ _libssh2_userauth_publickey(session, user, strlen(user),
+ pubkeydata, pubkeydata_len,
+ sign_callback, abstract));
+ return rc;
+}
+
+
+
+/*
+ * userauth_keyboard_interactive
+ *
+ * Authenticate using a challenge-response authentication
+ */
+static int
+userauth_keyboard_interactive(LIBSSH2_SESSION * session,
+ const char *username,
+ unsigned int username_len,
+ LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback)))
+{
+ unsigned char *s;
+ int rc;
+
+ static const unsigned char reply_codes[4] = {
+ SSH_MSG_USERAUTH_SUCCESS,
+ SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_INFO_REQUEST, 0
+ };
+ unsigned int language_tag_len;
+ unsigned int i;
+
+ if (session->userauth_kybd_state == libssh2_NB_state_idle) {
+ session->userauth_kybd_auth_name = NULL;
+ session->userauth_kybd_auth_instruction = NULL;
+ session->userauth_kybd_num_prompts = 0;
+ session->userauth_kybd_auth_failure = 1;
+ session->userauth_kybd_prompts = NULL;
+ session->userauth_kybd_responses = NULL;
+
+ /* Zero the whole thing out */
+ memset(&session->userauth_kybd_packet_requirev_state, 0,
+ sizeof(session->userauth_kybd_packet_requirev_state));
+
+ session->userauth_kybd_packet_len =
+ 1 /* byte SSH_MSG_USERAUTH_REQUEST */
+ + 4 + username_len /* string user name (ISO-10646 UTF-8, as
+ defined in [RFC-3629]) */
+ + 4 + 14 /* string service name (US-ASCII) */
+ + 4 + 20 /* string "keyboard-interactive" (US-ASCII) */
+ + 4 + 0 /* string language tag (as defined in
+ [RFC-3066]) */
+ + 4 + 0 /* string submethods (ISO-10646 UTF-8) */
+ ;
+
+ session->userauth_kybd_data = s =
+ LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len);
+ if (!s) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive authentication");
+ }
+
+ *s++ = SSH_MSG_USERAUTH_REQUEST;
+
+ /* user name */
+ _libssh2_store_str(&s, username, username_len);
+
+ /* service name */
+ _libssh2_store_str(&s, "ssh-connection", sizeof("ssh-connection") - 1);
+
+ /* "keyboard-interactive" */
+ _libssh2_store_str(&s, "keyboard-interactive",
+ sizeof("keyboard-interactive") - 1);
+ /* language tag */
+ _libssh2_store_u32(&s, 0);
+
+ /* submethods */
+ _libssh2_store_u32(&s, 0);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting keyboard-interactive authentication");
+
+ session->userauth_kybd_state = libssh2_NB_state_created;
+ }
+
+ if (session->userauth_kybd_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_kybd_data,
+ session->userauth_kybd_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ } else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send keyboard-interactive request");
+ }
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+
+ session->userauth_kybd_state = libssh2_NB_state_sent;
+ }
+
+ for(;;) {
+ if (session->userauth_kybd_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_kybd_data,
+ &session->userauth_kybd_data_len,
+ 0, NULL, 0,
+ &session->
+ userauth_kybd_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ } else if (rc) {
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Waiting for keyboard USERAUTH response");
+ }
+
+ if (session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Keyboard-interactive authentication successful");
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ if (session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_FAILURE) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Keyboard-interactive authentication failed");
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Authentication failed "
+ "(keyboard-interactive)");
+ }
+
+ /* server requested PAM-like conversation */
+ s = session->userauth_kybd_data + 1;
+
+ /* string name (ISO-10646 UTF-8) */
+ session->userauth_kybd_auth_name_len = _libssh2_ntohu32(s);
+ s += 4;
+ if(session->userauth_kybd_auth_name_len) {
+ session->userauth_kybd_auth_name =
+ LIBSSH2_ALLOC(session,
+ session->userauth_kybd_auth_name_len);
+ if (!session->userauth_kybd_auth_name) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive 'name' "
+ "request field");
+ goto cleanup;
+ }
+ memcpy(session->userauth_kybd_auth_name, s,
+ session->userauth_kybd_auth_name_len);
+ s += session->userauth_kybd_auth_name_len;
+ }
+
+ /* string instruction (ISO-10646 UTF-8) */
+ session->userauth_kybd_auth_instruction_len = _libssh2_ntohu32(s);
+ s += 4;
+ if(session->userauth_kybd_auth_instruction_len) {
+ session->userauth_kybd_auth_instruction =
+ LIBSSH2_ALLOC(session,
+ session->userauth_kybd_auth_instruction_len);
+ if (!session->userauth_kybd_auth_instruction) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive 'instruction' "
+ "request field");
+ goto cleanup;
+ }
+ memcpy(session->userauth_kybd_auth_instruction, s,
+ session->userauth_kybd_auth_instruction_len);
+ s += session->userauth_kybd_auth_instruction_len;
+ }
+
+ /* string language tag (as defined in [RFC-3066]) */
+ language_tag_len = _libssh2_ntohu32(s);
+ s += 4;
+
+ /* ignoring this field as deprecated */
+ s += language_tag_len;
+
+ /* int num-prompts */
+ session->userauth_kybd_num_prompts = _libssh2_ntohu32(s);
+ s += 4;
+
+ if(session->userauth_kybd_num_prompts) {
+ session->userauth_kybd_prompts =
+ LIBSSH2_CALLOC(session,
+ sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) *
+ session->userauth_kybd_num_prompts);
+ if (!session->userauth_kybd_prompts) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive prompts array");
+ goto cleanup;
+ }
+
+ session->userauth_kybd_responses =
+ LIBSSH2_CALLOC(session,
+ sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) *
+ session->userauth_kybd_num_prompts);
+ if (!session->userauth_kybd_responses) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive responses array");
+ goto cleanup;
+ }
+
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ /* string prompt[1] (ISO-10646 UTF-8) */
+ session->userauth_kybd_prompts[i].length =
+ _libssh2_ntohu32(s);
+ s += 4;
+ session->userauth_kybd_prompts[i].text =
+ LIBSSH2_CALLOC(session,
+ session->userauth_kybd_prompts[i].length);
+ if (!session->userauth_kybd_prompts[i].text) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive prompt message");
+ goto cleanup;
+ }
+ memcpy(session->userauth_kybd_prompts[i].text, s,
+ session->userauth_kybd_prompts[i].length);
+ s += session->userauth_kybd_prompts[i].length;
+
+ /* boolean echo[1] */
+ session->userauth_kybd_prompts[i].echo = *s++;
+ }
+ }
+
+ response_callback(session->userauth_kybd_auth_name,
+ session->userauth_kybd_auth_name_len,
+ session->userauth_kybd_auth_instruction,
+ session->userauth_kybd_auth_instruction_len,
+ session->userauth_kybd_num_prompts,
+ session->userauth_kybd_prompts,
+ session->userauth_kybd_responses,
+ &session->abstract);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Keyboard-interactive response callback function"
+ " invoked");
+
+ session->userauth_kybd_packet_len =
+ 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */
+ + 4 /* int num-responses */
+ ;
+
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ /* string response[1] (ISO-10646 UTF-8) */
+ session->userauth_kybd_packet_len +=
+ 4 + session->userauth_kybd_responses[i].length;
+ }
+
+ /* A new userauth_kybd_data area is to be allocated, free the
+ former one. */
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+
+ session->userauth_kybd_data = s =
+ LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len);
+ if (!s) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for keyboard-"
+ "interactive response packet");
+ goto cleanup;
+ }
+
+ *s = SSH_MSG_USERAUTH_INFO_RESPONSE;
+ s++;
+ _libssh2_store_u32(&s, session->userauth_kybd_num_prompts);
+
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ _libssh2_store_str(&s,
+ session->userauth_kybd_responses[i].text,
+ session->userauth_kybd_responses[i].length);
+ }
+
+ session->userauth_kybd_state = libssh2_NB_state_sent1;
+ }
+
+ if (session->userauth_kybd_state == libssh2_NB_state_sent1) {
+ rc = _libssh2_transport_send(session, session->userauth_kybd_data,
+ session->userauth_kybd_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ if (rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-keyboard-interactive"
+ " request");
+ goto cleanup;
+ }
+
+ session->userauth_kybd_auth_failure = 0;
+ }
+
+ cleanup:
+ /*
+ * It's safe to clean all the data here, because unallocated pointers
+ * are filled by zeroes
+ */
+
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+
+ if (session->userauth_kybd_prompts) {
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ LIBSSH2_FREE(session, session->userauth_kybd_prompts[i].text);
+ session->userauth_kybd_prompts[i].text = NULL;
+ }
+ }
+
+ if (session->userauth_kybd_responses) {
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ LIBSSH2_FREE(session,
+ session->userauth_kybd_responses[i].text);
+ session->userauth_kybd_responses[i].text = NULL;
+ }
+ }
+
+ if(session->userauth_kybd_prompts) {
+ LIBSSH2_FREE(session, session->userauth_kybd_prompts);
+ session->userauth_kybd_prompts = NULL;
+ }
+ if(session->userauth_kybd_responses) {
+ LIBSSH2_FREE(session, session->userauth_kybd_responses);
+ session->userauth_kybd_responses = NULL;
+ }
+ if(session->userauth_kybd_auth_name) {
+ LIBSSH2_FREE(session, session->userauth_kybd_auth_name);
+ session->userauth_kybd_auth_name = NULL;
+ }
+ if(session->userauth_kybd_auth_instruction) {
+ LIBSSH2_FREE(session, session->userauth_kybd_auth_instruction);
+ session->userauth_kybd_auth_instruction = NULL;
+ }
+
+ if (session->userauth_kybd_auth_failure) {
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return -1;
+ }
+
+ session->userauth_kybd_state = libssh2_NB_state_sent;
+ }
+}
+
+/*
+ * libssh2_userauth_keyboard_interactive_ex
+ *
+ * Authenticate using a challenge-response authentication
+ */
+LIBSSH2_API int
+libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session,
+ const char *user,
+ unsigned int user_len,
+ LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback)))
+{
+ int rc;
+ BLOCK_ADJUST(rc, session,
+ userauth_keyboard_interactive(session, user, user_len,
+ response_callback));
+ return rc;
+}