/* Virtual File System: SFTP file system. The internal functions: connections Copyright (C) 2011-2022 Free Software Foundation, Inc. Written by: Ilia Maslakov , 2011 Slava Zanko , 2011, 2012, 2013 This file is part of the Midnight Commander. The Midnight Commander is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The Midnight Commander is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include /* struct hostent */ #include /* AF_INET */ #include /* struct in_addr */ #ifdef HAVE_ARPA_INET_H #include #endif #include #include #include "lib/global.h" #include "lib/util.h" #include "lib/tty/tty.h" /* tty_enable_interrupt_key () */ #include "lib/vfs/utilvfs.h" #include "lib/mcconfig.h" /* mc_config_get_home_dir () */ #include "lib/widget.h" /* query_dialog () */ #include "internal.h" /*** global variables ****************************************************************************/ /*** file scope macro definitions ****************************************************************/ #define SHA1_DIGEST_LENGTH 20 /*** file scope type declarations ****************************************************************/ /*** file scope variables ************************************************************************/ #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 static const char *const hostkey_method_ssh_ed25519 = "ssh-ed25519"; #endif #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 static const char *const hostkey_method_ssh_ecdsa_521 = "ecdsa-sha2-nistp521"; #endif #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 static const char *const hostkey_method_ssh_ecdsa_384 = "ecdsa-sha2-nistp384"; #endif #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 static const char *const hostkey_method_ssh_ecdsa_256 = "ecdsa-sha2-nistp256"; #endif static const char *const hostkey_method_ssh_rsa = "ssh-rsa"; static const char *const hostkey_method_ssh_dss = "ssh-dss"; /** * * The current implementation of know host key checking has following limitations: * * - Only plain-text entries are supported (`HashKnownHosts no` OpenSSH option) * - Only HEX-encoded SHA1 fingerprint display is supported (`FingerprintHash` OpenSSH option) * - Resolved IP addresses are *not* saved/validated along with the hostnames * */ static const char *kbi_passwd = NULL; static const struct vfs_s_super *kbi_super = NULL; /* --------------------------------------------------------------------------------------------- */ /*** file scope functions ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ /** * Create socket to host. * * @param super connection data * @param mcerror pointer to the error handler * @return socket descriptor number, -1 if any error was occurred */ static int sftpfs_open_socket (struct vfs_s_super *super, GError ** mcerror) { sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); struct addrinfo hints, *res = NULL, *curr_res; int my_socket = 0; char port[BUF_TINY]; static char address_ipv4[INET_ADDRSTRLEN]; static char address_ipv6[INET6_ADDRSTRLEN]; int e; mc_return_val_if_error (mcerror, LIBSSH2_INVALID_SOCKET); if (super->path_element->host == NULL || *super->path_element->host == '\0') { mc_propagate_error (mcerror, 0, "%s", _("sftp: Invalid host name.")); return LIBSSH2_INVALID_SOCKET; } sprintf (port, "%hu", (unsigned short) super->path_element->port); tty_enable_interrupt_key (); /* clear the interrupt flag */ memset (&hints, 0, sizeof (hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; #ifdef AI_ADDRCONFIG /* By default, only look up addresses using address types for * which a local interface is configured (i.e. no IPv6 if no IPv6 * interfaces, likewise for IPv4 (see RFC 3493 for details). */ hints.ai_flags = AI_ADDRCONFIG; #endif e = getaddrinfo (super->path_element->host, port, &hints, &res); #ifdef AI_ADDRCONFIG if (e == EAI_BADFLAGS) { /* Retry with no flags if AI_ADDRCONFIG was rejected. */ hints.ai_flags = 0; e = getaddrinfo (super->path_element->host, port, &hints, &res); } #endif if (e != 0) { mc_propagate_error (mcerror, e, _("sftp: %s"), gai_strerror (e)); my_socket = LIBSSH2_INVALID_SOCKET; goto ret; } for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next) { int save_errno; switch (curr_res->ai_addr->sa_family) { case AF_INET: sftpfs_super->ip_address = inet_ntop (AF_INET, &((struct sockaddr_in *) curr_res->ai_addr)->sin_addr, address_ipv4, INET_ADDRSTRLEN); break; case AF_INET6: sftpfs_super->ip_address = inet_ntop (AF_INET6, &((struct sockaddr_in6 *) curr_res->ai_addr)->sin6_addr, address_ipv6, INET6_ADDRSTRLEN); break; default: sftpfs_super->ip_address = NULL; } if (sftpfs_super->ip_address == NULL) { mc_propagate_error (mcerror, 0, "%s", _("sftp: failed to convert remote host IP address into text form")); my_socket = LIBSSH2_INVALID_SOCKET; goto ret; } my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol); if (my_socket < 0) { if (curr_res->ai_next != NULL) continue; vfs_print_message (_("sftp: %s"), unix_error_string (errno)); my_socket = LIBSSH2_INVALID_SOCKET; goto ret; } vfs_print_message (_("sftp: making connection to %s"), super->path_element->host); if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0) break; save_errno = errno; close (my_socket); if (save_errno == EINTR && tty_got_interrupt ()) mc_propagate_error (mcerror, 0, "%s", _("sftp: connection interrupted by user")); else if (res->ai_next == NULL) mc_propagate_error (mcerror, save_errno, _("sftp: connection to server failed: %s"), unix_error_string (save_errno)); else continue; my_socket = LIBSSH2_INVALID_SOCKET; break; } ret: if (res != NULL) freeaddrinfo (res); tty_disable_interrupt_key (); return my_socket; } /* --------------------------------------------------------------------------------------------- */ /** * Read ~/.ssh/known_hosts file. * * @param super connection data * @param mcerror pointer to the error handler * @return TRUE on success, FALSE otherwise * * Thanks the Curl project for the code used in this function. */ static gboolean sftpfs_read_known_hosts (struct vfs_s_super *super, GError ** mcerror) { sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); struct libssh2_knownhost *store = NULL; int rc; gboolean found = FALSE; sftpfs_super->known_hosts = libssh2_knownhost_init (sftpfs_super->session); if (sftpfs_super->known_hosts == NULL) goto err; sftpfs_super->known_hosts_file = mc_build_filename (mc_config_get_home_dir (), ".ssh", "known_hosts", (char *) NULL); rc = libssh2_knownhost_readfile (sftpfs_super->known_hosts, sftpfs_super->known_hosts_file, LIBSSH2_KNOWNHOST_FILE_OPENSSH); if (rc > 0) { const char *kh_name_end = NULL; while (!found && libssh2_knownhost_get (sftpfs_super->known_hosts, &store, store) == 0) { /* For non-standard ports, the name will be enclosed in * square brackets, followed by a colon and the port */ if (store == NULL) continue; if (store->name == NULL) found = TRUE; else if (store->name[0] != '[') found = strcmp (store->name, super->path_element->host) == 0; else { int port; kh_name_end = strstr (store->name, "]:"); if (kh_name_end == NULL) /* Invalid host pattern */ continue; port = (int) g_ascii_strtoll (kh_name_end + 2, NULL, 10); if (port == super->path_element->port) { size_t kh_name_size; kh_name_size = strlen (store->name) - 1 - strlen (kh_name_end); found = strncmp (store->name + 1, super->path_element->host, kh_name_size) == 0; } } } } if (found) { int mask; const char *hostkey_method = NULL; mask = store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK; switch (mask) { #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 case LIBSSH2_KNOWNHOST_KEY_ED25519: hostkey_method = hostkey_method_ssh_ed25519; break; #endif #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 case LIBSSH2_KNOWNHOST_KEY_ECDSA_521: hostkey_method = hostkey_method_ssh_ecdsa_521; break; #endif #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 case LIBSSH2_KNOWNHOST_KEY_ECDSA_384: hostkey_method = hostkey_method_ssh_ecdsa_384; break; #endif #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 case LIBSSH2_KNOWNHOST_KEY_ECDSA_256: hostkey_method = hostkey_method_ssh_ecdsa_256; break; #endif case LIBSSH2_KNOWNHOST_KEY_SSHRSA: hostkey_method = hostkey_method_ssh_rsa; break; case LIBSSH2_KNOWNHOST_KEY_SSHDSS: hostkey_method = hostkey_method_ssh_dss; break; case LIBSSH2_KNOWNHOST_KEY_RSA1: mc_propagate_error (mcerror, 0, "%s", _("sftp: found host key of unsupported type: RSA1")); return FALSE; default: mc_propagate_error (mcerror, 0, "%s %u", _("sftp: unknown host key type:"), (unsigned int) mask); return FALSE; } rc = libssh2_session_method_pref (sftpfs_super->session, LIBSSH2_METHOD_HOSTKEY, hostkey_method); if (rc < 0) goto err; } return TRUE; err: { int sftp_errno; sftp_errno = libssh2_session_last_errno (sftpfs_super->session); sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror); } return FALSE; } /* --------------------------------------------------------------------------------------------- */ /** * Write new host + key pair to the ~/.ssh/known_hosts file. * * @param super connection data * @param remote_key he key for the remote host * @param remote_key_len length of @remote_key * @param type_mask info about format of host name, key and key type * @return 0 on success, regular libssh2 error code otherwise * * Thanks the Curl project for the code used in this function. */ static int sftpfs_update_known_hosts (struct vfs_s_super *super, const char *remote_key, size_t remote_key_len, int type_mask) { sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); int rc; /* add this host + key pair */ rc = libssh2_knownhost_addc (sftpfs_super->known_hosts, super->path_element->host, NULL, remote_key, remote_key_len, NULL, 0, type_mask, NULL); if (rc < 0) return rc; /* write the entire in-memory list of known hosts to the known_hosts file */ rc = libssh2_knownhost_writefile (sftpfs_super->known_hosts, sftpfs_super->known_hosts_file, LIBSSH2_KNOWNHOST_FILE_OPENSSH); if (rc < 0) return rc; (void) message (D_NORMAL, _("Information"), _("Permanently added\n%s (%s)\nto the list of known hosts."), super->path_element->host, sftpfs_super->ip_address); return 0; } /* --------------------------------------------------------------------------------------------- */ /** * Compute and return readable host key fingerprint hash. * * @param session libssh2 session handle * @return pointer to static buffer on success, NULL otherwise */ static const char * sftpfs_compute_fingerprint_hash (LIBSSH2_SESSION * session) { static char result[SHA1_DIGEST_LENGTH * 3 + 1]; /* "XX:" for each byte, and EOL */ const char *fingerprint; size_t i; /* The fingerprint points to static storage (!), don't free() it. */ fingerprint = libssh2_hostkey_hash (session, LIBSSH2_HOSTKEY_HASH_SHA1); if (fingerprint == NULL) return NULL; for (i = 0; i < SHA1_DIGEST_LENGTH && i * 3 < sizeof (result) - 1; i++) g_snprintf ((gchar *) (result + i * 3), 4, "%02x:", (guint8) fingerprint[i]); /* remove last ":" */ result[i * 3 - 1] = '\0'; return result; } /* --------------------------------------------------------------------------------------------- */ /** * Process host info found in ~/.ssh/known_hosts file. * * @param super connection data * @param mcerror pointer to the error handler * @return TRUE on success, FALSE otherwise * * Thanks the Curl project for the code used in this function. */ static gboolean sftpfs_process_known_host (struct vfs_s_super *super, GError ** mcerror) { sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); const char *remote_key; const char *key_type; const char *fingerprint_hash; size_t remote_key_len = 0; int remote_key_type = LIBSSH2_HOSTKEY_TYPE_UNKNOWN; int keybit = 0; struct libssh2_knownhost *host = NULL; int rc; char *msg = NULL; gboolean handle_query = FALSE; remote_key = libssh2_session_hostkey (sftpfs_super->session, &remote_key_len, &remote_key_type); if (remote_key == NULL || remote_key_len == 0 || remote_key_type == LIBSSH2_HOSTKEY_TYPE_UNKNOWN) { mc_propagate_error (mcerror, 0, "%s", _("sftp: cannot get the remote host key")); return FALSE; } switch (remote_key_type) { case LIBSSH2_HOSTKEY_TYPE_RSA: keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; key_type = "RSA"; break; case LIBSSH2_HOSTKEY_TYPE_DSS: keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; key_type = "DSS"; break; #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; key_type = "ECDSA"; break; #endif #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384 case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; key_type = "ECDSA"; break; #endif #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521 case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; key_type = "ECDSA"; break; #endif #ifdef LIBSSH2_HOSTKEY_TYPE_ED25519 case LIBSSH2_HOSTKEY_TYPE_ED25519: keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; key_type = "ED25519"; break; #endif default: mc_propagate_error (mcerror, 0, "%s", _("sftp: unsupported key type, can't check remote host key")); return FALSE; } fingerprint_hash = sftpfs_compute_fingerprint_hash (sftpfs_super->session); if (fingerprint_hash == NULL) { mc_propagate_error (mcerror, 0, "%s", _("sftp: can't compute host key fingerprint hash")); return FALSE; } rc = libssh2_knownhost_checkp (sftpfs_super->known_hosts, super->path_element->host, super->path_element->port, remote_key, remote_key_len, LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | keybit, &host); switch (rc) { default: case LIBSSH2_KNOWNHOST_CHECK_FAILURE: /* something prevented the check to be made */ goto err; case LIBSSH2_KNOWNHOST_CHECK_MATCH: /* host + key pair matched -- OK */ break; case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: /* no host match was found -- add it to the known_hosts file */ msg = g_strdup_printf (_("The authenticity of host\n%s (%s)\ncan't be established!\n" "%s key fingerprint hash is\nSHA1:%s.\n" "Do you want to add it to the list of known hosts and continue connecting?"), super->path_element->host, sftpfs_super->ip_address, key_type, fingerprint_hash); /* Select "No" initially */ query_set_sel (2); rc = query_dialog (_("Warning"), msg, D_NORMAL, 3, _("&Yes"), _("&Ignore"), _("&No")); g_free (msg); handle_query = TRUE; break; case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: msg = g_strdup_printf (_("%s (%s)\nis found in the list of known hosts but\n" "KEYS DO NOT MATCH! THIS COULD BE A MITM ATTACK!\n" "Are you sure you want to add it to the list of known hosts and continue connecting?"), super->path_element->host, sftpfs_super->ip_address); /* Select "No" initially */ query_set_sel (2); rc = query_dialog (MSG_ERROR, msg, D_ERROR, 3, _("&Yes"), _("&Ignore"), _("&No")); g_free (msg); handle_query = TRUE; break; } if (handle_query) switch (rc) { case 0: /* Yes: add this host + key pair, continue connecting */ if (sftpfs_update_known_hosts (super, remote_key, remote_key_len, LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | keybit) < 0) goto err; break; case 1: /* Ignore: do not add this host + key pair, continue connecting anyway */ break; case 2: default: mc_propagate_error (mcerror, 0, "%s", _("sftp: host key verification failed")); /* No: abort connection */ goto err; } return TRUE; err: { int sftp_errno; sftp_errno = libssh2_session_last_errno (sftpfs_super->session); sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror); } return FALSE; } /* --------------------------------------------------------------------------------------------- */ /** * Recognize authentication types supported by remote side and filling internal 'super' structure by * proper enum's values. * * @param super connection data * @return TRUE if some of authentication methods is available, FALSE otherwise */ static gboolean sftpfs_recognize_auth_types (struct vfs_s_super *super) { char *userauthlist; sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); /* check what authentication methods are available */ /* userauthlist is internally managed by libssh2 and freed by libssh2_session_free() */ userauthlist = libssh2_userauth_list (sftpfs_super->session, super->path_element->user, strlen (super->path_element->user)); if (userauthlist == NULL) return FALSE; if ((strstr (userauthlist, "password") != NULL || strstr (userauthlist, "keyboard-interactive") != NULL) && (sftpfs_super->config_auth_type & PASSWORD) != 0) sftpfs_super->auth_type |= PASSWORD; if (strstr (userauthlist, "publickey") != NULL && (sftpfs_super->config_auth_type & PUBKEY) != 0) sftpfs_super->auth_type |= PUBKEY; if ((sftpfs_super->config_auth_type & AGENT) != 0) sftpfs_super->auth_type |= AGENT; return TRUE; } /* --------------------------------------------------------------------------------------------- */ /** * Open connection to host using SSH-agent helper. * * @param super connection data * @param mcerror pointer to the error handler * @return TRUE if connection was successfully opened, FALSE otherwise */ static gboolean sftpfs_open_connection_ssh_agent (struct vfs_s_super *super, GError ** mcerror) { sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); struct libssh2_agent_publickey *identity, *prev_identity = NULL; int rc; mc_return_val_if_error (mcerror, FALSE); sftpfs_super->agent = NULL; if ((sftpfs_super->auth_type & AGENT) == 0) return FALSE; /* Connect to the ssh-agent */ sftpfs_super->agent = libssh2_agent_init (sftpfs_super->session); if (sftpfs_super->agent == NULL) return FALSE; if (libssh2_agent_connect (sftpfs_super->agent) != 0) return FALSE; if (libssh2_agent_list_identities (sftpfs_super->agent) != 0) return FALSE; while (TRUE) { rc = libssh2_agent_get_identity (sftpfs_super->agent, &identity, prev_identity); if (rc == 1) break; if (rc < 0) return FALSE; if (libssh2_agent_userauth (sftpfs_super->agent, super->path_element->user, identity) == 0) break; prev_identity = identity; } return (rc == 0); } /* --------------------------------------------------------------------------------------------- */ /** * Open connection to host using SSH-keypair. * * @param super connection data * @param mcerror pointer to the error handler * @return TRUE if connection was successfully opened, FALSE otherwise */ static gboolean sftpfs_open_connection_ssh_key (struct vfs_s_super *super, GError ** mcerror) { sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); char *p, *passwd; gboolean ret_value = FALSE; mc_return_val_if_error (mcerror, FALSE); if ((sftpfs_super->auth_type & PUBKEY) == 0) return FALSE; if (sftpfs_super->privkey == NULL) return FALSE; if (libssh2_userauth_publickey_fromfile (sftpfs_super->session, super->path_element->user, sftpfs_super->pubkey, sftpfs_super->privkey, super->path_element->password) == 0) return TRUE; p = g_strdup_printf (_("sftp: Enter passphrase for %s "), super->path_element->user); passwd = vfs_get_password (p); g_free (p); if (passwd == NULL) mc_propagate_error (mcerror, 0, "%s", _("sftp: Passphrase is empty.")); else { ret_value = (libssh2_userauth_publickey_fromfile (sftpfs_super->session, super->path_element->user, sftpfs_super->pubkey, sftpfs_super->privkey, passwd) == 0); g_free (passwd); } return ret_value; } /* --------------------------------------------------------------------------------------------- */ /** * Keyboard-interactive password helper for opening connection to host by * sftpfs_open_connection_ssh_password * * Uses global kbi_super (data with existing connection) and kbi_passwd (password) * * @param name username * @param name_len length of @name * @param instruction unused * @param instruction_len unused * @param num_prompts number of possible problems to process * @param prompts array of prompts to process * @param responses array of responses, one per prompt * @param abstract unused */ static LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC (sftpfs_keyboard_interactive_helper) { int i; size_t len; (void) instruction; (void) instruction_len; (void) abstract; if (kbi_super == NULL || kbi_passwd == NULL) return; if (strncmp (name, kbi_super->path_element->user, name_len) != 0) return; /* assume these are password prompts */ len = strlen (kbi_passwd); for (i = 0; i < num_prompts; ++i) if (strncmp (prompts[i].text, "Password: ", prompts[i].length) == 0) { responses[i].text = strdup (kbi_passwd); responses[i].length = len; } } /* --------------------------------------------------------------------------------------------- */ /** * Open connection to host using password. * * @param super connection data * @param mcerror pointer to the error handler * @return TRUE if connection was successfully opened, FALSE otherwise */ static gboolean sftpfs_open_connection_ssh_password (struct vfs_s_super *super, GError ** mcerror) { sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); char *p, *passwd; gboolean ret_value = FALSE; int rc; mc_return_val_if_error (mcerror, FALSE); if ((sftpfs_super->auth_type & PASSWORD) == 0) return FALSE; if (super->path_element->password != NULL) { while ((rc = libssh2_userauth_password (sftpfs_super->session, super->path_element->user, super->path_element->password)) == LIBSSH2_ERROR_EAGAIN); if (rc == 0) return TRUE; kbi_super = super; kbi_passwd = super->path_element->password; while ((rc = libssh2_userauth_keyboard_interactive (sftpfs_super->session, super->path_element->user, sftpfs_keyboard_interactive_helper)) == LIBSSH2_ERROR_EAGAIN) ; kbi_super = NULL; kbi_passwd = NULL; if (rc == 0) return TRUE; } p = g_strdup_printf (_("sftp: Enter password for %s "), super->path_element->user); passwd = vfs_get_password (p); g_free (p); if (passwd == NULL) mc_propagate_error (mcerror, 0, "%s", _("sftp: Password is empty.")); else { while ((rc = libssh2_userauth_password (sftpfs_super->session, super->path_element->user, passwd)) == LIBSSH2_ERROR_EAGAIN) ; if (rc != 0) { kbi_super = super; kbi_passwd = passwd; while ((rc = libssh2_userauth_keyboard_interactive (sftpfs_super->session, super->path_element->user, sftpfs_keyboard_interactive_helper)) == LIBSSH2_ERROR_EAGAIN) ; kbi_super = NULL; kbi_passwd = NULL; } if (rc == 0) { ret_value = TRUE; g_free (super->path_element->password); super->path_element->password = passwd; } else g_free (passwd); } return ret_value; } /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ /** * Open new connection. * * @param super connection data * @param mcerror pointer to the error handler * @return 0 if success, -1 otherwise */ int sftpfs_open_connection (struct vfs_s_super *super, GError ** mcerror) { int rc; sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); mc_return_val_if_error (mcerror, -1); /* * The application code is responsible for creating the socket * and establishing the connection */ sftpfs_super->socket_handle = sftpfs_open_socket (super, mcerror); if (sftpfs_super->socket_handle == LIBSSH2_INVALID_SOCKET) return (-1); /* Create a session instance */ sftpfs_super->session = libssh2_session_init (); if (sftpfs_super->session == NULL) return (-1); if (!sftpfs_read_known_hosts (super, mcerror)) return (-1); /* ... start it up. This will trade welcome banners, exchange keys, * and setup crypto, compression, and MAC layers */ while ((rc = libssh2_session_handshake (sftpfs_super->session, (libssh2_socket_t) sftpfs_super->socket_handle)) == LIBSSH2_ERROR_EAGAIN) ; if (rc != 0) { mc_propagate_error (mcerror, rc, "%s", _("sftp: failure establishing SSH session")); return (-1); } if (!sftpfs_process_known_host (super, mcerror)) return (-1); if (!sftpfs_recognize_auth_types (super)) { int sftp_errno; sftp_errno = libssh2_session_last_errno (sftpfs_super->session); sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror); return (-1); } if (!sftpfs_open_connection_ssh_agent (super, mcerror) && !sftpfs_open_connection_ssh_key (super, mcerror) && !sftpfs_open_connection_ssh_password (super, mcerror)) return (-1); sftpfs_super->sftp_session = libssh2_sftp_init (sftpfs_super->session); if (sftpfs_super->sftp_session == NULL) return (-1); /* Since we have not set non-blocking, tell libssh2 we are blocking */ libssh2_session_set_blocking (sftpfs_super->session, 1); return 0; } /* --------------------------------------------------------------------------------------------- */ /** * Close connection. * * @param super connection data * @param shutdown_message message for shutdown functions * @param mcerror pointer to the error handler */ void sftpfs_close_connection (struct vfs_s_super *super, const char *shutdown_message, GError ** mcerror) { sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); /* no mc_return_*_if_error() here because of abort open_connection handling too */ (void) mcerror; if (sftpfs_super->sftp_session != NULL) { libssh2_sftp_shutdown (sftpfs_super->sftp_session); sftpfs_super->sftp_session = NULL; } if (sftpfs_super->agent != NULL) { libssh2_agent_disconnect (sftpfs_super->agent); libssh2_agent_free (sftpfs_super->agent); sftpfs_super->agent = NULL; } if (sftpfs_super->known_hosts != NULL) { libssh2_knownhost_free (sftpfs_super->known_hosts); sftpfs_super->known_hosts = NULL; } MC_PTR_FREE (sftpfs_super->known_hosts_file); if (sftpfs_super->session != NULL) { libssh2_session_disconnect (sftpfs_super->session, shutdown_message); libssh2_session_free (sftpfs_super->session); sftpfs_super->session = NULL; } if (sftpfs_super->socket_handle != LIBSSH2_INVALID_SOCKET) { close (sftpfs_super->socket_handle); sftpfs_super->socket_handle = LIBSSH2_INVALID_SOCKET; } } /* --------------------------------------------------------------------------------------------- */