#if defined(ESP32) /* Copyright (c) 2004-2007, Sara Golemon * Copyright (c) 2005 Mikhail Gusarov * 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 #include #include /* Needed for struct iovec on some platforms */ #ifdef HAVE_SYS_UIO_H #include #endif #include "transport.h" #include "session.h" #include "userauth.h" #include "userauth_kbd_packet.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) { 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; 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 || (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_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"); 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; } 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; } methods_len = _libssh2_ntohu32(session->userauth_list_data + 1); if(methods_len >= session->userauth_list_data_len - 5) { _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY, "Unexpected userauth list size"); return NULL; } /* 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_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(NULL == 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 != NULL) *banner = session->userauth_banner; return LIBSSH2_ERROR_NONE; } /* * 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"); } else if(session->userauth_pswd_data_len < 1) { session->userauth_pswd_state = libssh2_NB_state_idle; return _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Unexpected packet size"); } 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_len < 1) { session->userauth_pswd_state = libssh2_NB_state_idle; return _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Unexpected packet size"); } 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) */ if(username_len + password_len + 44 <= UINT_MAX) { session->userauth_pswd_data_len = username_len + password_len + 44; s = session->userauth_pswd_data = LIBSSH2_ALLOC(session, session->userauth_pswd_data_len); } else { s = session->userauth_pswd_data = NULL; session->userauth_pswd_data_len = 0; } 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"); } sp1 = memchr(pubkey, ' ', pubkey_len); if(sp1 == NULL) { LIBSSH2_FREE(session, pubkey); return _libssh2_error(session, LIBSSH2_ERROR_FILE, "Invalid public key data"); } sp1++; sp2 = memchr(sp1, ' ', pubkey_len - (sp1 - pubkey)); if(sp2 == 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, FOPEN_READTEXT); 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"); } sp1 = memchr(pubkey, ' ', pubkey_len); if(sp1 == NULL) { LIBSSH2_FREE(session, pubkey); return _libssh2_error(session, LIBSSH2_ERROR_FILE, "Invalid public key data"); } sp1++; sp_len = sp1 > pubkey ? (sp1 - pubkey) : 0; sp2 = memchr(sp1, ' ', pubkey_len - sp_len); if(sp2 == 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 memory"); } 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, &hostkey_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; } 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, *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 != NULL) { 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 */ 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 = NULL; unsigned char *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 || data_len < 1) { 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; } static int plain_method_len(const 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; } 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; int p_len = 0; int f_len = 0; int rc = 0; int 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 == NULL || session->server_sign_algorithms == NULL) { /* 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 ? (p - s) : (int) strlen(s); a = supported_algs; while(a && *a) { f = strchr(a, ','); f_len = f ? (f - a) : (int) 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 ? (p - s) : (int) strlen(s); a = filtered_algs; while(a && *a && !match) { f = strchr(a, ','); f_len = f ? (f - a) : (int) 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 != NULL) { 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, 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; int auth_attempts = 0; retry_auth: auth_attempts++; 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 - 4) /* 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); } /* 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) + * 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 || (session->userauth_pblc_data_len < 1)) { 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_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; 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; session->userauth_pblc_method_len = plain_method_len((const 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, 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, 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_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_data_len < 1) { 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. */ rc = _libssh2_pub_priv_keyfilememory(session, &session->userauth_pblc_method, &session->userauth_pblc_method_len, &pubkeydata, &pubkeydata_len, privatekeydata, privatekeydata_len, passphrase); if(rc) return rc; } 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 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_data_len < 1) { 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 */ if(userauth_keyboard_interactive_decode_info_request(session) < 0) { goto cleanup; } response_callback((const char *)session->userauth_kybd_auth_name, session->userauth_kybd_auth_name_len, (const char *) 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) */ if(session->userauth_kybd_responses[i].length <= (SIZE_MAX - 4 - session->userauth_kybd_packet_len) ) { session->userauth_kybd_packet_len += 4 + session->userauth_kybd_responses[i].length; } else { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-" "interactive response packet"); goto cleanup; } } /* 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; } /* 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 char *privatekeydata, size_t privatekeydata_len, const char *passphrase, LIBSSH2_USERAUTH_SK_SIGN_FUNC ((*sign_callback)), void **abstract) { unsigned char *pubkeydata = NULL; size_t pubkeydata_len = 0; LIBSSH2_PRIVKEY_SK sk_info = { 0 }; void *sign_abstract = &sk_info; int rc; sk_info.sign_callback = sign_callback; sk_info.orig_abstract = abstract; if(privatekeydata_len && privatekeydata) { if(_libssh2_sk_pub_keyfilememory(session, &session->userauth_pblc_method, &session->userauth_pblc_method_len, &pubkeydata, &pubkeydata_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 { 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, 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(pubkeydata) LIBSSH2_FREE(session, pubkeydata); if(sk_info.application) { LIBSSH2_FREE(session, (void *)sk_info.application); } return rc; } #endif