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.c963
1 files changed, 639 insertions, 324 deletions
diff --git a/libs/libssh2/src/userauth.c b/libs/libssh2/src/userauth.c
index 949dc1c660..e7578759f3 100644
--- a/libs/libssh2/src/userauth.c
+++ b/libs/libssh2/src/userauth.c
@@ -1,6 +1,6 @@
-/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
- * Copyright (c) 2005 Mikhail Gusarov <dottedmag@dottedmag.net>
- * Copyright (c) 2009-2014 by Daniel Stenberg
+/* Copyright (C) Sara Golemon <sarag@libssh2.org>
+ * Copyright (C) Mikhail Gusarov <dottedmag@dottedmag.net>
+ * Copyright (C) Daniel Stenberg
* All rights reserved.
*
* Redistribution and use in source and binary forms,
@@ -35,14 +35,13 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
*/
#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
@@ -52,8 +51,9 @@
#include "transport.h"
#include "session.h"
#include "userauth.h"
+#include "userauth_kbd_packet.h"
-/* libssh2_userauth_list
+/* userauth_list
*
* List authentication methods
* Will yield successful login if "none" happens to be allowable for this user
@@ -63,11 +63,13 @@
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 };
+ unsigned char reply_codes[4] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE,
+ SSH_MSG_USERAUTH_BANNER, 0 };
/* packet_type(1) + username_len(4) + service_len(4) +
service(14)"ssh-connection" + method_len(4) = 27 */
unsigned long methods_len;
+ unsigned int banner_len;
unsigned char *s;
int rc;
@@ -134,6 +136,57 @@ static char *userauth_list(LIBSSH2_SESSION *session, const char *username,
return NULL;
}
+ if(session->userauth_list_data[0] == SSH_MSG_USERAUTH_BANNER) {
+ if(session->userauth_list_data_len < 5) {
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ session->userauth_list_data = NULL;
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected packet size");
+ return NULL;
+ }
+ banner_len = _libssh2_ntohu32(session->userauth_list_data + 1);
+ if(banner_len > session->userauth_list_data_len - 5) {
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ session->userauth_list_data = NULL;
+ _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Unexpected userauth banner size");
+ return NULL;
+ }
+ session->userauth_banner = LIBSSH2_ALLOC(session, banner_len + 1);
+ if(!session->userauth_banner) {
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ session->userauth_list_data = NULL;
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for userauth_banner");
+ return NULL;
+ }
+ memcpy(session->userauth_banner, session->userauth_list_data + 5,
+ banner_len);
+ session->userauth_banner[banner_len] = '\0';
+ _libssh2_debug((session, LIBSSH2_TRACE_AUTH,
+ "Banner: %s",
+ session->userauth_banner));
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ session->userauth_list_data = NULL;
+ /* SSH_MSG_USERAUTH_BANNER has been handled */
+ reply_codes[2] = 0;
+ 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 || (session->userauth_list_data_len < 1)) {
+ _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");
@@ -163,13 +216,13 @@ static char *userauth_list(LIBSSH2_SESSION *session, const char *username,
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,
+ _libssh2_debug((session, LIBSSH2_TRACE_AUTH,
"Permitted auth methods: %s",
- session->userauth_list_data);
+ session->userauth_list_data));
}
session->userauth_list_state = libssh2_NB_state_idle;
- return (char *) session->userauth_list_data;
+ return (char *)session->userauth_list_data;
}
/* libssh2_userauth_list
@@ -189,6 +242,30 @@ libssh2_userauth_list(LIBSSH2_SESSION * session, const char *user,
return ptr;
}
+/* libssh2_userauth_banner
+ *
+ * Retrieve banner message from server, if available.
+ * When no such message is sent by server or if no authentication attempt has
+ * been made, this function returns LIBSSH2_ERROR_MISSING_AUTH_BANNER.
+ */
+LIBSSH2_API int
+libssh2_userauth_banner(LIBSSH2_SESSION *session, char **banner)
+{
+ if(!session)
+ return LIBSSH2_ERROR_MISSING_USERAUTH_BANNER;
+
+ if(!session->userauth_banner) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_MISSING_USERAUTH_BANNER,
+ "Missing userauth banner");
+ }
+
+ if(banner)
+ *banner = session->userauth_banner;
+
+ return LIBSSH2_ERROR_NONE;
+}
+
/*
* libssh2_userauth_authenticated
*
@@ -198,7 +275,7 @@ libssh2_userauth_list(LIBSSH2_SESSION * session, const char *user,
LIBSSH2_API int
libssh2_userauth_authenticated(LIBSSH2_SESSION * session)
{
- return (session->state & LIBSSH2_STATE_AUTHENTICATED)?1:0;
+ return (session->state & LIBSSH2_STATE_AUTHENTICATED) ? 1 : 0;
}
@@ -251,8 +328,8 @@ userauth_password(LIBSSH2_SESSION *session,
_libssh2_store_u32(&s, password_len);
/* 'password' is sent separately */
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
- "Attempting to login using password authentication");
+ _libssh2_debug((session, LIBSSH2_TRACE_AUTH,
+ "Attempting to login using password authentication"));
session->userauth_pswd_state = libssh2_NB_state_created;
}
@@ -279,7 +356,7 @@ userauth_password(LIBSSH2_SESSION *session,
session->userauth_pswd_state = libssh2_NB_state_sent;
}
- password_response:
+password_response:
if((session->userauth_pswd_state == libssh2_NB_state_sent)
|| (session->userauth_pswd_state == libssh2_NB_state_sent1)
@@ -306,8 +383,8 @@ userauth_password(LIBSSH2_SESSION *session,
}
if(session->userauth_pswd_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
- "Password authentication successful");
+ _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;
@@ -316,8 +393,8 @@ userauth_password(LIBSSH2_SESSION *session,
}
else if(session->userauth_pswd_data[0] ==
SSH_MSG_USERAUTH_FAILURE) {
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
- "Password authentication failed");
+ _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;
@@ -348,8 +425,8 @@ userauth_password(LIBSSH2_SESSION *session,
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_debug((session, LIBSSH2_TRACE_AUTH,
+ "Password change required"));
LIBSSH2_FREE(session, session->userauth_pswd_data);
session->userauth_pswd_data = NULL;
}
@@ -490,7 +567,7 @@ memory_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
{
unsigned char *pubkey = NULL, *sp1, *sp2, *tmp;
size_t pubkey_len = pubkeyfiledata_len;
- unsigned int tmp_len;
+ size_t tmp_len;
if(pubkeyfiledata_len <= 1) {
return _libssh2_error(session, LIBSSH2_ERROR_FILE,
@@ -518,7 +595,7 @@ memory_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
}
sp1 = memchr(pubkey, ' ', pubkey_len);
- if(sp1 == NULL) {
+ if(!sp1) {
LIBSSH2_FREE(session, pubkey);
return _libssh2_error(session, LIBSSH2_ERROR_FILE,
"Invalid public key data");
@@ -527,16 +604,17 @@ memory_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
sp1++;
sp2 = memchr(sp1, ' ', pubkey_len - (sp1 - pubkey));
- if(sp2 == NULL) {
+ if(!sp2) {
/* 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)) {
+ if(_libssh2_base64_decode(session, (char **)&tmp, &tmp_len,
+ (const char *)sp1,
+ sp2 - sp1)) {
LIBSSH2_FREE(session, pubkey);
return _libssh2_error(session, LIBSSH2_ERROR_FILE,
- "Invalid key data, not base64 encoded");
+ "Invalid key data, not base64 encoded");
}
/* Wasting some bytes here (okay, more than some), but since it's likely
@@ -573,10 +651,10 @@ file_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
char c;
unsigned char *pubkey = NULL, *sp1, *sp2, *tmp;
size_t pubkey_len = 0, sp_len;
- unsigned int tmp_len;
+ size_t tmp_len;
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Loading public key file: %s",
- pubkeyfile);
+ _libssh2_debug((session, LIBSSH2_TRACE_AUTH, "Loading public key file: %s",
+ pubkeyfile));
/* Read Public Key */
fd = fopen(pubkeyfile, FOPEN_READTEXT);
if(!fd) {
@@ -621,7 +699,7 @@ file_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
}
sp1 = memchr(pubkey, ' ', pubkey_len);
- if(sp1 == NULL) {
+ if(!sp1) {
LIBSSH2_FREE(session, pubkey);
return _libssh2_error(session, LIBSSH2_ERROR_FILE,
"Invalid public key data");
@@ -629,15 +707,16 @@ file_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
sp1++;
- sp_len = sp1 > pubkey ? (sp1 - pubkey) - 1 : 0;
+ sp_len = sp1 > pubkey ? (sp1 - pubkey) : 0;
sp2 = memchr(sp1, ' ', pubkey_len - sp_len);
- if(sp2 == NULL) {
+ if(!sp2) {
/* 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)) {
+ if(_libssh2_base64_decode(session, (char **)&tmp, &tmp_len,
+ (const char *)sp1,
+ sp2 - sp1)) {
LIBSSH2_FREE(session, pubkey);
return _libssh2_error(session, LIBSSH2_ERROR_FILE,
"Invalid key data, not base64 encoded");
@@ -659,7 +738,7 @@ 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 unsigned char *method, size_t method_len,
const char *privkeyfiledata, size_t privkeyfiledata_len,
const char *passphrase)
{
@@ -687,7 +766,7 @@ memory_read_privatekey(LIBSSH2_SESSION * session,
(unsigned char *) passphrase,
hostkey_abstract)) {
return _libssh2_error(session, LIBSSH2_ERROR_FILE,
- "Unable to initialize private key from file");
+ "Unable to initialize private key from memory");
}
return 0;
@@ -700,14 +779,14 @@ 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 unsigned char *method, size_t 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);
+ _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) {
@@ -763,7 +842,7 @@ sign_frommemory(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
datavec.iov_len = data_len;
if(privkeyobj->signv(session, sig, sig_len, 1, &datavec,
- &hostkey_abstract)) {
+ &hostkey_abstract)) {
if(privkeyobj->dtor) {
privkeyobj->dtor(session, &hostkey_abstract);
}
@@ -799,7 +878,7 @@ sign_fromfile(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
datavec.iov_len = data_len;
if(privkeyobj->signv(session, sig, sig_len, 1, &datavec,
- &hostkey_abstract)) {
+ &hostkey_abstract)) {
if(privkeyobj->dtor) {
privkeyobj->dtor(session, &hostkey_abstract);
}
@@ -812,7 +891,106 @@ sign_fromfile(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
return 0;
}
+int
+libssh2_sign_sk(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
+ const unsigned char *data, size_t data_len, void **abstract)
+{
+ int rc = LIBSSH2_ERROR_DECRYPT;
+ LIBSSH2_PRIVKEY_SK *sk_info = (LIBSSH2_PRIVKEY_SK *) (*abstract);
+ LIBSSH2_SK_SIG_INFO sig_info = { 0 };
+ if(sk_info->handle_len <= 0) {
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+
+ rc = sk_info->sign_callback(session,
+ &sig_info,
+ data,
+ data_len,
+ sk_info->algorithm,
+ sk_info->flags,
+ sk_info->application,
+ sk_info->key_handle,
+ sk_info->handle_len,
+ sk_info->orig_abstract);
+
+ if(rc == 0 && sig_info.sig_r_len > 0 && sig_info.sig_r) {
+ unsigned char *p = NULL;
+
+ if(sig_info.sig_s_len > 0 && sig_info.sig_s) {
+ /* sig length, sig_r, sig_s, flags, counter, plus 4 bytes for each
+ component's length, and up to 1 extra byte for each component */
+ *sig_len = 4 + 5 + sig_info.sig_r_len + 5 + sig_info.sig_s_len + 5;
+ *sig = LIBSSH2_ALLOC(session, *sig_len);
+
+ if(*sig) {
+ unsigned char *x = *sig;
+ p = *sig;
+
+ _libssh2_store_u32(&p, 0);
+
+ _libssh2_store_bignum2_bytes(&p,
+ sig_info.sig_r,
+ sig_info.sig_r_len);
+
+ _libssh2_store_bignum2_bytes(&p,
+ sig_info.sig_s,
+ sig_info.sig_s_len);
+
+ *sig_len = p - *sig;
+
+ _libssh2_store_u32(&x, (uint32_t)(*sig_len - 4));
+ }
+ else {
+ _libssh2_debug((session,
+ LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate ecdsa-sk signature."));
+ rc = LIBSSH2_ERROR_ALLOC;
+ }
+ }
+ else {
+ /* sig, flags, counter, plus 4 bytes for sig length. */
+ *sig_len = 4 + sig_info.sig_r_len + 1 + 4;
+ *sig = LIBSSH2_ALLOC(session, *sig_len);
+
+ if(*sig) {
+ p = *sig;
+
+ _libssh2_store_str(&p,
+ (const char *)sig_info.sig_r,
+ sig_info.sig_r_len);
+ }
+ else {
+ _libssh2_debug((session,
+ LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate ed25519-sk signature."));
+ rc = LIBSSH2_ERROR_ALLOC;
+ }
+ }
+
+ if(p) {
+ *p = sig_info.flags;
+ ++p;
+ _libssh2_store_u32(&p, sig_info.counter);
+
+ *sig_len = p - *sig;
+ }
+
+ LIBSSH2_FREE(session, sig_info.sig_r);
+
+ if(sig_info.sig_s) {
+ LIBSSH2_FREE(session, sig_info.sig_s);
+ }
+ }
+ else {
+ _libssh2_debug((session,
+ LIBSSH2_ERROR_DECRYPT,
+ "sign_callback failed or returned invalid signature."));
+ *sig_len = 0;
+ }
+
+ return rc;
+}
/* userauth_hostbased_fromfile
* Authenticate using a keypair found in the named files
@@ -828,11 +1006,6 @@ userauth_hostbased_fromfile(LIBSSH2_SESSION *session,
{
int rc;
-#if !LIBSSH2_RSA
- return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
- "RSA is not supported by crypto backend");
-#endif
-
if(session->userauth_host_state == libssh2_NB_state_idle) {
const LIBSSH2_HOSTKEY_METHOD *privkeyobj;
unsigned char *pubkeydata = NULL;
@@ -863,7 +1036,7 @@ userauth_hostbased_fromfile(LIBSSH2_SESSION *session,
&pubkeydata, &pubkeydata_len,
privatekey, passphrase);
if(rc)
- /* libssh2_pub_priv_keyfile calls _libssh2_error() */
+ /* libssh2_pub_priv_keyfile() calls _libssh2_error() */
return rc;
}
@@ -932,8 +1105,8 @@ userauth_hostbased_fromfile(LIBSSH2_SESSION *session,
datavec[2].iov_len = session->userauth_host_packet_len;
if(privkeyobj && privkeyobj->signv &&
- privkeyobj->signv(session, &sig, &sig_len, 3,
- datavec, &abstract)) {
+ 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);
@@ -972,8 +1145,8 @@ userauth_hostbased_fromfile(LIBSSH2_SESSION *session,
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);
+ (uint32_t)(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);
@@ -984,8 +1157,8 @@ userauth_hostbased_fromfile(LIBSSH2_SESSION *session,
sig_len);
LIBSSH2_FREE(session, sig);
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
- "Attempting hostbased authentication");
+ _libssh2_debug((session, LIBSSH2_TRACE_AUTH,
+ "Attempting hostbased authentication"));
session->userauth_host_state = libssh2_NB_state_created;
}
@@ -1033,8 +1206,8 @@ userauth_hostbased_fromfile(LIBSSH2_SESSION *session,
}
if(session->userauth_host_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
- "Hostbased authentication successful");
+ _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;
@@ -1075,14 +1248,196 @@ libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session,
return rc;
}
+size_t plain_method(char *method, size_t method_len)
+{
+ if(!strncmp("ssh-rsa-cert-v01@openssh.com",
+ method,
+ method_len)) {
+ return 7;
+ }
+
+ if(!strncmp("ecdsa-sha2-nistp256-cert-v01@openssh.com",
+ method,
+ method_len) ||
+ !strncmp("ecdsa-sha2-nistp384-cert-v01@openssh.com",
+ method,
+ method_len) ||
+ !strncmp("ecdsa-sha2-nistp521-cert-v01@openssh.com",
+ method,
+ method_len)) {
+ return 19;
+ }
+
+ if(!strncmp("ssh-ed25519-cert-v01@openssh.com",
+ method,
+ method_len)) {
+ return 11;
+ }
+
+ if(!strncmp("sk-ecdsa-sha2-nistp256-cert-v01@openssh.com",
+ method,
+ method_len)) {
+ const char *new_method = "sk-ecdsa-sha2-nistp256@openssh.com";
+ memcpy(method, new_method, strlen(new_method));
+ return strlen(new_method);
+ }
+
+ if(!strncmp("sk-ssh-ed25519-cert-v01@openssh.com",
+ method,
+ method_len)) {
+ const char *new_method = "sk-ssh-ed25519@openssh.com";
+ memcpy(method, new_method, strlen(new_method));
+ return strlen(new_method);
+ }
+
+ return method_len;
+}
+
+/**
+ * @function _libssh2_key_sign_algorithm
+ * @abstract Upgrades the algorithm used for public key signing RFC 8332
+ * @discussion Based on the incoming key_method value, this function
+ * will upgrade the key method input based on user preferences,
+ * server support algos and crypto backend support
+ * @related _libssh2_supported_key_sign_algorithms()
+ * @param key_method current key method, usually the default key sig method
+ * @param key_method_len length of the key method buffer
+ * @result error code or zero on success
+ */
+
+static int
+_libssh2_key_sign_algorithm(LIBSSH2_SESSION *session,
+ unsigned char **key_method,
+ size_t *key_method_len)
+{
+ const char *s = NULL;
+ const char *a = NULL;
+ const char *match = NULL;
+ const char *p = NULL;
+ const char *f = NULL;
+ char *i = NULL;
+ size_t p_len = 0;
+ size_t f_len = 0;
+ int rc = 0;
+ size_t match_len = 0;
+ char *filtered_algs = NULL;
+
+ const char *supported_algs =
+ _libssh2_supported_key_sign_algorithms(session,
+ *key_method,
+ *key_method_len);
+
+ if(!supported_algs || !session->server_sign_algorithms) {
+ /* no upgrading key algorithm supported, do nothing */
+ return LIBSSH2_ERROR_NONE;
+ }
+
+ filtered_algs = LIBSSH2_ALLOC(session, strlen(supported_algs) + 1);
+ if(!filtered_algs) {
+ rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate filtered algs");
+ return rc;
+ }
+
+ s = session->server_sign_algorithms;
+ i = filtered_algs;
+
+ /* this walks the server algo list and the supported algo list and creates
+ a filtered list that includes matches */
+
+ while(s && *s) {
+ p = strchr(s, ',');
+ p_len = (p ? (size_t)(p - s) : strlen(s));
+ a = supported_algs;
+
+ while(a && *a) {
+ f = strchr(a, ',');
+ f_len = (f ? (size_t)(f - a) : strlen(a));
+
+ if(f_len == p_len && memcmp(a, s, p_len) == 0) {
+
+ if(i != filtered_algs) {
+ memcpy(i, ",", 1);
+ i += 1;
+ }
+
+ memcpy(i, s, p_len);
+ i += p_len;
+ }
+
+ a = f ? (f + 1) : NULL;
+ }
+
+ s = p ? (p + 1) : NULL;
+ }
+
+ filtered_algs[i - filtered_algs] = '\0';
+ if(session->sign_algo_prefs) {
+ s = session->sign_algo_prefs;
+ }
+ else {
+ s = supported_algs;
+ }
+
+ /* now that we have the possible supported algos, match based on the prefs
+ or what is supported by the crypto backend, look for a match */
+
+ while(s && *s && !match) {
+ p = strchr(s, ',');
+ p_len = (p ? (size_t)(p - s) : strlen(s));
+ a = filtered_algs;
+
+ while(a && *a && !match) {
+ f = strchr(a, ',');
+ f_len = (f ? (size_t)(f - a) : strlen(a));
+
+ if(f_len == p_len && memcmp(a, s, p_len) == 0) {
+ /* found a match, upgrade key method */
+ match = s;
+ match_len = p_len;
+ }
+ else {
+ a = f ? (f + 1) : NULL;
+ }
+ }
+
+ s = p ? (p + 1) : NULL;
+ }
+
+ if(match) {
+ if(*key_method)
+ LIBSSH2_FREE(session, *key_method);
+
+ *key_method = LIBSSH2_ALLOC(session, match_len);
+ if(key_method) {
+ memcpy(*key_method, match, match_len);
+ *key_method_len = match_len;
+ }
+ else {
+ *key_method_len = 0;
+ rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate key method upgrade");
+ }
+ }
+ else {
+ /* no match was found */
+ rc = _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
+ "No signing signature matched");
+ }
+
+ if(filtered_algs)
+ LIBSSH2_FREE(session, filtered_algs);
+
+ return rc;
+}
int
_libssh2_userauth_publickey(LIBSSH2_SESSION *session,
const char *username,
- unsigned int username_len,
+ size_t username_len,
const unsigned char *pubkeydata,
- unsigned long pubkeydata_len,
+ size_t pubkeydata_len,
LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC
((*sign_callback)),
void *abstract)
@@ -1093,6 +1448,10 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session,
};
int rc;
unsigned char *s;
+ int auth_attempts = 0;
+
+retry_auth:
+ auth_attempts++;
if(session->userauth_pblc_state == libssh2_NB_state_idle) {
@@ -1118,9 +1477,8 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session,
session->userauth_pblc_method_len = _libssh2_ntohu32(pubkeydata);
if(session->userauth_pblc_method_len > pubkeydata_len - 4)
- /* the method length simply cannot be longer than the entire
- passed in data, so we use this to detect crazy input
- data */
+ /* the method length 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");
@@ -1135,15 +1493,27 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session,
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");
+
+ /* upgrade key signing algo if it is supported and
+ * it is our first auth attempt, otherwise fallback to
+ * the key default algo */
+ if(auth_attempts == 1) {
+ rc = _libssh2_key_sign_algorithm(session,
+ &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len);
+
+ if(rc)
+ return rc;
+ }
+
+ if(session->userauth_pblc_method_len &&
+ session->userauth_pblc_method) {
+ _libssh2_debug((session,
+ LIBSSH2_TRACE_KEX,
+ "Signing using %.*s",
+ session->userauth_pblc_method_len,
+ session->userauth_pblc_method));
+ }
/*
* 45 = packet_type(1) + username_len(4) + servicename_len(4) +
@@ -1189,8 +1559,8 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session,
session->userauth_pblc_method_len);
_libssh2_store_str(&s, (const char *)pubkeydata, pubkeydata_len);
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
- "Attempting publickey authentication");
+ _libssh2_debug((session, LIBSSH2_TRACE_AUTH,
+ "Attempting publickey authentication"));
session->userauth_pblc_state = libssh2_NB_state_created;
}
@@ -1237,8 +1607,8 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session,
}
if(session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
- "Pubkey authentication prematurely successful");
+ _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
@@ -1301,6 +1671,17 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session,
return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
"Would block");
}
+ else if(rc == LIBSSH2_ERROR_ALGO_UNSUPPORTED && auth_attempts == 1) {
+ /* try again with the default key algo */
+ 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;
+
+ rc = LIBSSH2_ERROR_NONE;
+ goto retry_auth;
+ }
else if(rc) {
LIBSSH2_FREE(session, session->userauth_pblc_method);
session->userauth_pblc_method = NULL;
@@ -1340,20 +1721,40 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session,
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);
+ session->userauth_pblc_method_len =
+ plain_method((char *)session->userauth_pblc_method,
+ session->userauth_pblc_method_len);
+
+ if(strncmp((const char *)session->userauth_pblc_method,
+ "sk-ecdsa-sha2-nistp256@openssh.com",
+ session->userauth_pblc_method_len) == 0 ||
+ strncmp((const char *)session->userauth_pblc_method,
+ "sk-ssh-ed25519@openssh.com",
+ session->userauth_pblc_method_len) == 0) {
+ _libssh2_store_u32(&s,
+ (uint32_t)(4 + session->userauth_pblc_method_len +
+ sig_len));
+ _libssh2_store_str(&s, (const char *)session->userauth_pblc_method,
+ session->userauth_pblc_method_len);
+ memcpy(s, sig, sig_len);
+ s += sig_len;
+ }
+ else {
+ _libssh2_store_u32(&s,
+ (uint32_t)(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_store_str(&s, (const char *)sig, sig_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");
+ _libssh2_debug((session, LIBSSH2_TRACE_AUTH,
+ "Attempting publickey authentication -- phase 2"));
session->userauth_pblc_s = s;
session->userauth_pblc_state = libssh2_NB_state_sent2;
@@ -1399,8 +1800,8 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session,
}
if(session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
- "Publickey authentication successful");
+ _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;
@@ -1418,10 +1819,10 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session,
"username/public key combination");
}
- /*
- * userauth_publickey_frommemory
- * Authenticate using a keypair from memory
- */
+/*
+ * userauth_publickey_frommemory
+ * Authenticate using a keypair from memory
+ */
static int
userauth_publickey_frommemory(LIBSSH2_SESSION *session,
const char *username,
@@ -1438,11 +1839,6 @@ userauth_publickey_frommemory(LIBSSH2_SESSION *session,
void *abstract = &privkey_file;
int rc;
-#if !LIBSSH2_RSA
- return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
- "RSA is not supported by crypto backend");
-#endif
-
privkey_file.filename = privatekeydata;
privkey_file.passphrase = passphrase;
@@ -1457,15 +1853,14 @@ userauth_publickey_frommemory(LIBSSH2_SESSION *session,
}
else if(privatekeydata_len && privatekeydata) {
/* Compute public key from private key. */
- if(_libssh2_pub_priv_keyfilememory(session,
+ rc = _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.");
+ passphrase);
+ if(rc)
+ return rc;
}
else {
return _libssh2_error(session, LIBSSH2_ERROR_FILE,
@@ -1500,11 +1895,6 @@ userauth_publickey_fromfile(LIBSSH2_SESSION *session,
void *abstract = &privkey_file;
int rc;
-#if !LIBSSH2_RSA
- return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
- "RSA is not supported by crypto backend");
-#endif
-
privkey_file.filename = privatekey;
privkey_file.passphrase = passphrase;
@@ -1554,7 +1944,7 @@ libssh2_userauth_publickey_frommemory(LIBSSH2_SESSION *session,
{
int rc;
- if(NULL == passphrase)
+ if(!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 = "";
@@ -1582,7 +1972,7 @@ libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
{
int rc;
- if(NULL == passphrase)
+ if(!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 = "";
@@ -1603,7 +1993,7 @@ libssh2_userauth_publickey(LIBSSH2_SESSION *session,
const unsigned char *pubkeydata,
size_t pubkeydata_len,
LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC
- ((*sign_callback)),
+ ((*sign_callback)),
void **abstract)
{
int rc;
@@ -1630,16 +2020,16 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session,
const char *username,
unsigned int username_len,
LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC
- ((*response_callback)))
+ ((*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) {
@@ -1690,8 +2080,8 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session,
/* submethods */
_libssh2_store_u32(&s, 0);
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
- "Attempting keyboard-interactive authentication");
+ _libssh2_debug((session, LIBSSH2_TRACE_AUTH,
+ "Attempting keyboard-interactive authentication"));
session->userauth_kybd_state = libssh2_NB_state_created;
}
@@ -1739,9 +2129,9 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session,
}
if(session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ _libssh2_debug((session, LIBSSH2_TRACE_AUTH,
"Keyboard-interactive "
- "authentication successful");
+ "authentication successful"));
LIBSSH2_FREE(session, session->userauth_kybd_data);
session->userauth_kybd_data = NULL;
session->state |= LIBSSH2_STATE_AUTHENTICATED;
@@ -1750,8 +2140,8 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session,
}
if(session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_FAILURE) {
- _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
- "Keyboard-interactive authentication failed");
+ _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;
@@ -1762,230 +2152,34 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session,
}
/* server requested PAM-like conversation */
- s = session->userauth_kybd_data + 1;
-
- if(session->userauth_kybd_data_len >= 5) {
- /* string name (ISO-10646 UTF-8) */
- session->userauth_kybd_auth_name_len = _libssh2_ntohu32(s);
- s += 4;
- }
- else {
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "userauth keyboard data buffer too small"
- "to get length");
+ if(userauth_keyboard_interactive_decode_info_request(session)
+ < 0) {
goto cleanup;
}
- 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;
- }
- if(s + session->userauth_list_data_len <=
- session->userauth_kybd_data +
- session->userauth_kybd_data_len) {
- memcpy(session->userauth_kybd_auth_name, s,
- session->userauth_kybd_auth_name_len);
- s += session->userauth_kybd_auth_name_len;
- }
- else {
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "userauth keyboard data buffer too small"
- "for auth name");
- goto cleanup;
- }
- }
-
- if(s + 4 <= session->userauth_kybd_data +
- session->userauth_kybd_data_len) {
- /* string instruction (ISO-10646 UTF-8) */
- session->userauth_kybd_auth_instruction_len =
- _libssh2_ntohu32(s);
- s += 4;
- }
- else {
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "userauth keyboard data buffer too small"
- "for auth instruction length");
- goto cleanup;
- }
-
- 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;
- }
- if(s + session->userauth_kybd_auth_instruction_len <=
- session->userauth_kybd_data +
- session->userauth_kybd_data_len) {
- memcpy(session->userauth_kybd_auth_instruction, s,
- session->userauth_kybd_auth_instruction_len);
- s += session->userauth_kybd_auth_instruction_len;
- }
- else {
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "userauth keyboard data buffer too small"
- "for auth instruction");
- goto cleanup;
- }
- }
-
- if(s + 4 <= session->userauth_kybd_data +
- session->userauth_kybd_data_len) {
- /* string language tag (as defined in [RFC-3066]) */
- language_tag_len = _libssh2_ntohu32(s);
- s += 4;
- }
- else {
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "userauth keyboard data buffer too small"
- "for auth language tag length");
- goto cleanup;
- }
-
- if(s + language_tag_len <= session->userauth_kybd_data +
- session->userauth_kybd_data_len) {
- /* ignoring this field as deprecated */
- s += language_tag_len;
- }
- else {
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "userauth keyboard data buffer too small"
- "for auth language tag");
- goto cleanup;
- }
-
- if(s + 4 <= session->userauth_kybd_data +
- session->userauth_kybd_data_len) {
- /* int num-prompts */
- session->userauth_kybd_num_prompts = _libssh2_ntohu32(s);
- s += 4;
- }
- else {
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "userauth keyboard data buffer too small"
- "for auth num keyboard prompts");
- goto cleanup;
- }
-
- if(session->userauth_kybd_num_prompts > 100) {
- _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
- "Too many replies for "
- "keyboard-interactive prompts");
- goto cleanup;
- }
-
- 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++) {
- if(s + 4 <= session->userauth_kybd_data +
- session->userauth_kybd_data_len) {
- /* string prompt[1] (ISO-10646 UTF-8) */
- session->userauth_kybd_prompts[i].length =
- _libssh2_ntohu32(s);
- s += 4;
- }
- else {
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "userauth keyboard data buffer too "
- "small for auth keyboard "
- "prompt length");
- goto cleanup;
- }
-
- 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;
- }
-
- if(s + session->userauth_kybd_prompts[i].length <=
- session->userauth_kybd_data +
- session->userauth_kybd_data_len) {
- memcpy(session->userauth_kybd_prompts[i].text, s,
- session->userauth_kybd_prompts[i].length);
- s += session->userauth_kybd_prompts[i].length;
- }
- else {
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "userauth keyboard data buffer too "
- "small for auth keyboard prompt");
- goto cleanup;
- }
- if(s < session->userauth_kybd_data +
- session->userauth_kybd_data_len) {
- /* boolean echo[1] */
- session->userauth_kybd_prompts[i].echo = *s++;
- }
- else {
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "userauth keyboard data buffer too "
- "small for auth keyboard prompt echo");
- goto cleanup;
- }
- }
- }
-
- response_callback(session->userauth_kybd_auth_name,
- session->userauth_kybd_auth_name_len,
+ response_callback((const char *)session->userauth_kybd_auth_name,
+ (int)session->userauth_kybd_auth_name_len,
+ (const char *)
session->userauth_kybd_auth_instruction,
- session->userauth_kybd_auth_instruction_len,
+ (int)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,
+ _libssh2_debug((session, LIBSSH2_TRACE_AUTH,
"Keyboard-interactive response callback function"
- " invoked");
+ " invoked"));
session->userauth_kybd_packet_len =
- 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */
- + 4 /* int num-responses */
+ 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) */
if(session->userauth_kybd_responses[i].length <=
- (SIZE_MAX - 4 - session->userauth_kybd_packet_len) ) {
+ (SIZE_MAX - 4 - session->userauth_kybd_packet_len)) {
session->userauth_kybd_packet_len +=
4 + session->userauth_kybd_responses[i].length;
}
@@ -2040,7 +2234,7 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session,
session->userauth_kybd_auth_failure = 0;
}
- cleanup:
+cleanup:
/*
* It's safe to clean all the data here, because unallocated pointers
* are filled by zeroes
@@ -2100,7 +2294,7 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session,
const char *user,
unsigned int user_len,
LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC
- ((*response_callback)))
+ ((*response_callback)))
{
int rc;
BLOCK_ADJUST(rc, session,
@@ -2108,3 +2302,124 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session,
response_callback));
return rc;
}
+
+/*
+ * libssh2_userauth_publickey_sk
+ *
+ * Authenticate using an external callback function
+ */
+LIBSSH2_API int
+libssh2_userauth_publickey_sk(LIBSSH2_SESSION *session,
+ const char *username,
+ size_t username_len,
+ const unsigned char *publickeydata,
+ size_t publickeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase,
+ LIBSSH2_USERAUTH_SK_SIGN_FUNC
+ ((*sign_callback)),
+ void **abstract)
+{
+ int rc = LIBSSH2_ERROR_NONE;
+
+ unsigned char *tmp_method = NULL;
+ size_t tmp_method_len = 0;
+
+ unsigned char *tmp_publickeydata = NULL;
+ size_t tmp_publickeydata_len = 0;
+
+ unsigned char *pubkeydata = NULL;
+ size_t pubkeydata_len = 0;
+
+ LIBSSH2_PRIVKEY_SK sk_info = { 0 };
+ void *sign_abstract = &sk_info;
+
+ sk_info.sign_callback = sign_callback;
+ sk_info.orig_abstract = abstract;
+
+ if(privatekeydata_len && privatekeydata) {
+
+ if(_libssh2_sk_pub_keyfilememory(session,
+ &tmp_method,
+ &tmp_method_len,
+ &tmp_publickeydata,
+ &tmp_publickeydata_len,
+ &(sk_info.algorithm),
+ &(sk_info.flags),
+ &(sk_info.application),
+ &(sk_info.key_handle),
+ &(sk_info.handle_len),
+ privatekeydata, privatekeydata_len,
+ passphrase)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to extract public key "
+ "from private key.");
+ }
+ else if(publickeydata_len == 0 || !publickeydata) {
+ session->userauth_pblc_method = tmp_method;
+ session->userauth_pblc_method_len = tmp_method_len;
+
+ pubkeydata_len = tmp_publickeydata_len;
+ pubkeydata = tmp_publickeydata;
+ }
+ else {
+ const char *ecdsa = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com";
+ const char *ed25519 = "sk-ssh-ed25519-cert-v01@openssh.com";
+
+ if(tmp_method) {
+ LIBSSH2_FREE(session, tmp_method);
+ }
+
+ if(!strncmp((char *)publickeydata, ecdsa, strlen(ecdsa))) {
+ session->userauth_pblc_method_len = strlen(ecdsa);
+ session->userauth_pblc_method =
+ LIBSSH2_ALLOC(session, session->userauth_pblc_method_len);
+
+ memcpy(session->userauth_pblc_method, ecdsa,
+ session->userauth_pblc_method_len);
+ }
+ else if(!strncmp((char *)publickeydata, ed25519,
+ strlen(ed25519))) {
+ session->userauth_pblc_method_len = strlen(ed25519);
+ session->userauth_pblc_method =
+ LIBSSH2_ALLOC(session, session->userauth_pblc_method_len);
+
+ memcpy(session->userauth_pblc_method, ed25519,
+ session->userauth_pblc_method_len);
+ }
+
+ rc = memory_read_publickey(session,
+ &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,
+ (char *)publickeydata,
+ publickeydata_len);
+ }
+ }
+ else {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid data in public and private key.");
+ }
+
+ if(rc == LIBSSH2_ERROR_NONE) {
+ rc = _libssh2_userauth_publickey(session, username, username_len,
+ pubkeydata, pubkeydata_len,
+ libssh2_sign_sk, &sign_abstract);
+
+ while(rc == LIBSSH2_ERROR_EAGAIN) {
+ rc = _libssh2_userauth_publickey(session, username, username_len,
+ pubkeydata, pubkeydata_len,
+ libssh2_sign_sk, &sign_abstract);
+ }
+ }
+
+ if(tmp_publickeydata)
+ LIBSSH2_FREE(session, tmp_publickeydata);
+
+ if(sk_info.application) {
+ LIBSSH2_FREE(session, (void *)sk_info.application);
+ }
+
+ return rc;
+}