Manual pages: mcmcdiffmceditmcview

root/src/vfs/sftpfs/connection.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. sftpfs_open_socket
  2. sftpfs_read_known_hosts
  3. sftpfs_update_known_hosts
  4. sftpfs_compute_fingerprint_hash
  5. sftpfs_process_known_host
  6. sftpfs_recognize_auth_types
  7. sftpfs_open_connection_ssh_agent
  8. sftpfs_open_connection_ssh_key
  9. LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC
  10. sftpfs_open_connection_ssh_password
  11. sftpfs_open_connection
  12. sftpfs_close_connection

   1 /* Virtual File System: SFTP file system.
   2    The internal functions: connections
   3 
   4    Copyright (C) 2011-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Ilia Maslakov <il.smind@gmail.com>, 2011
   9    Slava Zanko <slavazanko@gmail.com>, 2011, 2012, 2013
  10 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  25  */
  26 
  27 #include <config.h>
  28 #include <errno.h>
  29 
  30 #include <netdb.h>       // struct hostent
  31 #include <sys/socket.h>  // AF_INET
  32 #include <netinet/in.h>  // struct in_addr
  33 #ifdef HAVE_ARPA_INET_H
  34 #include <arpa/inet.h>
  35 #endif
  36 
  37 #include <libssh2.h>
  38 #include <libssh2_sftp.h>
  39 
  40 #include "lib/global.h"
  41 
  42 #include "lib/util.h"
  43 #include "lib/tty/tty.h"  // tty_enable_interrupt_key ()
  44 #include "lib/vfs/utilvfs.h"
  45 #include "lib/mcconfig.h"  // mc_config_get_home_dir ()
  46 #include "lib/widget.h"    // query_dialog ()
  47 
  48 #include "internal.h"
  49 
  50 /*** global variables ****************************************************************************/
  51 
  52 /*** file scope macro definitions ****************************************************************/
  53 
  54 #define SHA1_DIGEST_LENGTH 20
  55 
  56 /*** file scope type declarations ****************************************************************/
  57 
  58 /*** forward declarations (file scope functions) *************************************************/
  59 
  60 /*** file scope variables ************************************************************************/
  61 
  62 #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
  63 static const char *const hostkey_method_ssh_ed25519 = "ssh-ed25519";
  64 #endif
  65 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521
  66 static const char *const hostkey_method_ssh_ecdsa_521 = "ecdsa-sha2-nistp521";
  67 #endif
  68 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384
  69 static const char *const hostkey_method_ssh_ecdsa_384 = "ecdsa-sha2-nistp384";
  70 #endif
  71 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
  72 static const char *const hostkey_method_ssh_ecdsa_256 = "ecdsa-sha2-nistp256";
  73 #endif
  74 static const char *const hostkey_method_ssh_rsa = "ssh-rsa";
  75 static const char *const hostkey_method_ssh_dss = "ssh-dss";
  76 
  77 static const char *default_hostkey_methods =
  78 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
  79     "ecdsa-sha2-nistp256,"
  80 #endif
  81 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384
  82     "ecdsa-sha2-nistp384,"
  83 #endif
  84 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521
  85     "ecdsa-sha2-nistp521,"
  86 #endif
  87 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
  88     "ecdsa-sha2-nistp256-cert-v01@openssh.com,"
  89 #endif
  90 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384
  91     "ecdsa-sha2-nistp384-cert-v01@openssh.com,"
  92 #endif
  93 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521
  94     "ecdsa-sha2-nistp521-cert-v01@openssh.com,"
  95 #endif
  96 #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
  97     "ssh-ed25519,"
  98     "ssh-ed25519-cert-v01@openssh.com,"
  99 #endif
 100     "rsa-sha2-256,"
 101     "rsa-sha2-512,"
 102     "ssh-rsa,"
 103     "ssh-rsa-cert-v01@openssh.com,"
 104     "ssh-dss";
 105 
 106 /**
 107  *
 108  * The current implementation of know host key checking has following limitations:
 109  *
 110  *   - Only plain-text entries are supported (`HashKnownHosts no` OpenSSH option)
 111  *   - Only HEX-encoded SHA1 fingerprint display is supported (`FingerprintHash` OpenSSH option)
 112  *   - Resolved IP addresses are *not* saved/validated along with the hostnames
 113  *
 114  */
 115 
 116 static const char *kbi_passwd = NULL;
 117 static const struct vfs_s_super *kbi_super = NULL;
 118 
 119 /* --------------------------------------------------------------------------------------------- */
 120 /*** file scope functions ************************************************************************/
 121 /* --------------------------------------------------------------------------------------------- */
 122 /**
 123  * Create socket to host.
 124  *
 125  * @param super   connection data
 126  * @param mcerror pointer to the error handler
 127  * @return socket descriptor number, -1 if any error was occurred
 128  */
 129 
 130 static int
 131 sftpfs_open_socket (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 132 {
 133     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 134     struct addrinfo hints, *res = NULL, *curr_res;
 135     int my_socket = 0;
 136     char port[BUF_TINY];
 137     static char address_ipv4[INET_ADDRSTRLEN];
 138     static char address_ipv6[INET6_ADDRSTRLEN];
 139     int e;
 140 
 141     mc_return_val_if_error (mcerror, LIBSSH2_INVALID_SOCKET);
 142 
 143     if (super->path_element->host == NULL || *super->path_element->host == '\0')
 144     {
 145         mc_propagate_error (mcerror, 0, "%s", _ ("sftp: Invalid host name."));
 146         return LIBSSH2_INVALID_SOCKET;
 147     }
 148 
 149     sprintf (port, "%hu", (unsigned short) super->path_element->port);
 150 
 151     tty_enable_interrupt_key ();  // clear the interrupt flag
 152 
 153     memset (&hints, 0, sizeof (hints));
 154     hints.ai_family = AF_UNSPEC;
 155     hints.ai_socktype = SOCK_STREAM;
 156 
 157 #ifdef AI_ADDRCONFIG
 158     /* By default, only look up addresses using address types for
 159      * which a local interface is configured (i.e. no IPv6 if no IPv6
 160      * interfaces, likewise for IPv4 (see RFC 3493 for details). */
 161     hints.ai_flags = AI_ADDRCONFIG;
 162 #endif
 163 
 164     e = getaddrinfo (super->path_element->host, port, &hints, &res);
 165 
 166 #ifdef AI_ADDRCONFIG
 167     if (e == EAI_BADFLAGS)
 168     {
 169         // Retry with no flags if AI_ADDRCONFIG was rejected.
 170         hints.ai_flags = 0;
 171         e = getaddrinfo (super->path_element->host, port, &hints, &res);
 172     }
 173 #endif
 174 
 175     if (e != 0)
 176     {
 177         mc_propagate_error (mcerror, e, _ ("sftp: %s"), gai_strerror (e));
 178         my_socket = LIBSSH2_INVALID_SOCKET;
 179         goto ret;
 180     }
 181 
 182     for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
 183     {
 184         int save_errno;
 185 
 186         switch (curr_res->ai_addr->sa_family)
 187         {
 188         case AF_INET:
 189             sftpfs_super->ip_address =
 190                 inet_ntop (AF_INET, &((struct sockaddr_in *) curr_res->ai_addr)->sin_addr,
 191                            address_ipv4, INET_ADDRSTRLEN);
 192             break;
 193         case AF_INET6:
 194             sftpfs_super->ip_address =
 195                 inet_ntop (AF_INET6, &((struct sockaddr_in6 *) curr_res->ai_addr)->sin6_addr,
 196                            address_ipv6, INET6_ADDRSTRLEN);
 197             break;
 198         default:
 199             sftpfs_super->ip_address = NULL;
 200         }
 201 
 202         if (sftpfs_super->ip_address == NULL)
 203         {
 204             mc_propagate_error (
 205                 mcerror, 0, "%s",
 206                 _ ("sftp: failed to convert remote host IP address into text form"));
 207             my_socket = LIBSSH2_INVALID_SOCKET;
 208             goto ret;
 209         }
 210 
 211         my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
 212 
 213         if (my_socket < 0)
 214         {
 215             if (curr_res->ai_next != NULL)
 216                 continue;
 217 
 218             vfs_print_message (_ ("sftp: %s"), unix_error_string (errno));
 219             my_socket = LIBSSH2_INVALID_SOCKET;
 220             goto ret;
 221         }
 222 
 223         vfs_print_message (_ ("sftp: making connection to %s"), super->path_element->host);
 224 
 225         if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
 226             break;
 227 
 228         save_errno = errno;
 229 
 230         close (my_socket);
 231 
 232         if (save_errno == EINTR && tty_got_interrupt ())
 233             mc_propagate_error (mcerror, 0, "%s", _ ("sftp: connection interrupted by user"));
 234         else if (res->ai_next == NULL)
 235             mc_propagate_error (mcerror, save_errno, _ ("sftp: connection to server failed: %s"),
 236                                 unix_error_string (save_errno));
 237         else
 238             continue;
 239 
 240         my_socket = LIBSSH2_INVALID_SOCKET;
 241         break;
 242     }
 243 
 244 ret:
 245     if (res != NULL)
 246         freeaddrinfo (res);
 247     tty_disable_interrupt_key ();
 248     return my_socket;
 249 }
 250 
 251 /* --------------------------------------------------------------------------------------------- */
 252 
 253 /**
 254  * Read ~/.ssh/known_hosts file.
 255  *
 256  * @param super connection data
 257  * @param mcerror pointer to the error handler
 258  * @return TRUE on success, FALSE otherwise
 259  *
 260  * Thanks the Curl project for the code used in this function.
 261  */
 262 static gboolean
 263 sftpfs_read_known_hosts (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 264 {
 265     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 266     struct libssh2_knownhost *store = NULL;
 267     int rc;
 268     gboolean found = FALSE;
 269 
 270     sftpfs_super->known_hosts = libssh2_knownhost_init (sftpfs_super->session);
 271     if (sftpfs_super->known_hosts == NULL)
 272         goto err;
 273 
 274     sftpfs_super->known_hosts_file =
 275         mc_build_filename (mc_config_get_home_dir (), ".ssh", "known_hosts", (char *) NULL);
 276 
 277     if (!exist_file (sftpfs_super->known_hosts_file))
 278     {
 279         mc_propagate_error (mcerror, 0, _ ("sftp: cannot open %s:\n%s"),
 280                             sftpfs_super->known_hosts_file, unix_error_string (errno));
 281         return FALSE;
 282     }
 283 
 284     rc = libssh2_knownhost_readfile (sftpfs_super->known_hosts, sftpfs_super->known_hosts_file,
 285                                      LIBSSH2_KNOWNHOST_FILE_OPENSSH);
 286     if (rc > 0)
 287     {
 288         const char *kh_name_end = NULL;
 289 
 290         while (!found && libssh2_knownhost_get (sftpfs_super->known_hosts, &store, store) == 0)
 291         {
 292             /* For non-standard ports, the name will be enclosed in
 293              * square brackets, followed by a colon and the port */
 294             if (store == NULL)
 295                 continue;
 296 
 297             if (store->name == NULL)
 298                 // Ignore hashed hostnames. Currently, libssh2 offers no way for us to match it
 299                 continue;
 300 
 301             if (store->name[0] != '[')
 302                 found = strcmp (store->name, super->path_element->host) == 0;
 303             else
 304             {
 305                 int port;
 306 
 307                 kh_name_end = strstr (store->name, "]:");
 308                 if (kh_name_end == NULL)
 309                     // Invalid host pattern
 310                     continue;
 311 
 312                 port = (int) g_ascii_strtoll (kh_name_end + 2, NULL, 10);
 313                 if (port == super->path_element->port)
 314                 {
 315                     size_t kh_name_size;
 316 
 317                     kh_name_size = strlen (store->name) - 1 - strlen (kh_name_end);
 318                     found = strncmp (store->name + 1, super->path_element->host, kh_name_size) == 0;
 319                 }
 320             }
 321         }
 322     }
 323 
 324     if (found)
 325     {
 326         int mask;
 327         const char *hostkey_method = NULL;
 328         char *hostkey_methods;
 329 
 330         mask = store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK;
 331 
 332         switch (mask)
 333         {
 334 #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
 335         case LIBSSH2_KNOWNHOST_KEY_ED25519:
 336             hostkey_method = hostkey_method_ssh_ed25519;
 337             break;
 338 #endif
 339 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521
 340         case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
 341             hostkey_method = hostkey_method_ssh_ecdsa_521;
 342             break;
 343 #endif
 344 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384
 345         case LIBSSH2_KNOWNHOST_KEY_ECDSA_384:
 346             hostkey_method = hostkey_method_ssh_ecdsa_384;
 347             break;
 348 #endif
 349 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
 350         case LIBSSH2_KNOWNHOST_KEY_ECDSA_256:
 351             hostkey_method = hostkey_method_ssh_ecdsa_256;
 352             break;
 353 #endif
 354         case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
 355             hostkey_method = hostkey_method_ssh_rsa;
 356             break;
 357         case LIBSSH2_KNOWNHOST_KEY_SSHDSS:
 358             hostkey_method = hostkey_method_ssh_dss;
 359             break;
 360         case LIBSSH2_KNOWNHOST_KEY_RSA1:
 361             mc_propagate_error (mcerror, 0, "%s",
 362                                 _ ("sftp: found host key of unsupported type: RSA1"));
 363             return FALSE;
 364         default:
 365             mc_propagate_error (mcerror, 0, "%s 0x%x", _ ("sftp: unknown host key type:"),
 366                                 (unsigned int) mask);
 367             return FALSE;
 368         }
 369 
 370         /* Append the default hostkey methods (with lower priority).
 371          * Since we ignored hashed hostnames, the actual matching host
 372          * key might have different type than the one found in
 373          * known_hosts for non-hashed hostname. Methods not supported
 374          * by libssh2 it are ignored. */
 375         hostkey_methods = g_strdup_printf ("%s,%s", hostkey_method, default_hostkey_methods);
 376         rc = libssh2_session_method_pref (sftpfs_super->session, LIBSSH2_METHOD_HOSTKEY,
 377                                           hostkey_methods);
 378         g_free (hostkey_methods);
 379         if (rc < 0)
 380             goto err;
 381     }
 382 
 383     return TRUE;
 384 
 385 err:
 386 {
 387     int sftp_errno;
 388 
 389     sftp_errno = libssh2_session_last_errno (sftpfs_super->session);
 390     sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror);
 391 }
 392     return FALSE;
 393 }
 394 
 395 /* --------------------------------------------------------------------------------------------- */
 396 
 397 /**
 398  * Write new host + key pair to the ~/.ssh/known_hosts file.
 399  *
 400  * @param super connection data
 401  * @param remote_key he key for the remote host
 402  * @param remote_key_len length of @remote_key
 403  * @param type_mask info about format of host name, key and key type
 404  * @return 0 on success, regular libssh2 error code otherwise
 405  *
 406  * Thanks the Curl project for the code used in this function.
 407  */
 408 static int
 409 sftpfs_update_known_hosts (struct vfs_s_super *super, const char *remote_key, size_t remote_key_len,
     /* [previous][next][first][last][top][bottom][index][help]  */
 410                            int type_mask)
 411 {
 412     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 413     int rc;
 414 
 415     // add this host + key pair
 416     rc = libssh2_knownhost_addc (sftpfs_super->known_hosts, super->path_element->host, NULL,
 417                                  remote_key, remote_key_len, NULL, 0, type_mask, NULL);
 418     if (rc < 0)
 419         return rc;
 420 
 421     // write the entire in-memory list of known hosts to the known_hosts file
 422     rc = libssh2_knownhost_writefile (sftpfs_super->known_hosts, sftpfs_super->known_hosts_file,
 423                                       LIBSSH2_KNOWNHOST_FILE_OPENSSH);
 424 
 425     if (rc < 0)
 426         return rc;
 427 
 428     (void) message (D_NORMAL, _ ("Information"),
 429                     _ ("Permanently added\n%s (%s)\nto the list of known hosts."),
 430                     super->path_element->host, sftpfs_super->ip_address);
 431 
 432     return 0;
 433 }
 434 
 435 /* --------------------------------------------------------------------------------------------- */
 436 /**
 437  * Compute and return readable host key fingerprint hash.
 438  *
 439  * @param session libssh2 session handle
 440  * @return pointer to static buffer on success, NULL otherwise
 441  */
 442 static const char *
 443 sftpfs_compute_fingerprint_hash (LIBSSH2_SESSION *session)
     /* [previous][next][first][last][top][bottom][index][help]  */
 444 {
 445     static char result[SHA1_DIGEST_LENGTH * 3 + 1];  // "XX:" for each byte, and EOL
 446     const char *fingerprint;
 447     size_t i;
 448 
 449     // The fingerprint points to static storage (!), don't free() it.
 450     fingerprint = libssh2_hostkey_hash (session, LIBSSH2_HOSTKEY_HASH_SHA1);
 451     if (fingerprint == NULL)
 452         return NULL;
 453 
 454     for (i = 0; i < SHA1_DIGEST_LENGTH && i * 3 < sizeof (result) - 1; i++)
 455         g_snprintf ((gchar *) (result + i * 3), 4, "%02x:", (guint8) fingerprint[i]);
 456 
 457     // remove last ":"
 458     result[i * 3 - 1] = '\0';
 459 
 460     return result;
 461 }
 462 
 463 /* --------------------------------------------------------------------------------------------- */
 464 
 465 /**
 466  * Process host info found in ~/.ssh/known_hosts file.
 467  *
 468  * @param super connection data
 469  * @param mcerror pointer to the error handler
 470  * @return TRUE on success, FALSE otherwise
 471  *
 472  * Thanks the Curl project for the code used in this function.
 473  */
 474 static gboolean
 475 sftpfs_process_known_host (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 476 {
 477     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 478     const char *remote_key;
 479     const char *key_type;
 480     const char *fingerprint_hash;
 481     size_t remote_key_len = 0;
 482     int remote_key_type = LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
 483     int keybit = 0;
 484     struct libssh2_knownhost *host = NULL;
 485     int rc;
 486     char *msg = NULL;
 487     gboolean handle_query = FALSE;
 488 
 489     remote_key = libssh2_session_hostkey (sftpfs_super->session, &remote_key_len, &remote_key_type);
 490     if (remote_key == NULL || remote_key_len == 0
 491         || remote_key_type == LIBSSH2_HOSTKEY_TYPE_UNKNOWN)
 492     {
 493         mc_propagate_error (mcerror, 0, "%s", _ ("sftp: cannot get the remote host key"));
 494         return FALSE;
 495     }
 496 
 497     switch (remote_key_type)
 498     {
 499     case LIBSSH2_HOSTKEY_TYPE_RSA:
 500         keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
 501         key_type = "RSA";
 502         break;
 503     case LIBSSH2_HOSTKEY_TYPE_DSS:
 504         keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
 505         key_type = "DSS";
 506         break;
 507 #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
 508     case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
 509         keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
 510         key_type = "ECDSA";
 511         break;
 512 #endif
 513 #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384
 514     case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
 515         keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
 516         key_type = "ECDSA";
 517         break;
 518 #endif
 519 #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521
 520     case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
 521         keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
 522         key_type = "ECDSA";
 523         break;
 524 #endif
 525 #ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
 526     case LIBSSH2_HOSTKEY_TYPE_ED25519:
 527         keybit = LIBSSH2_KNOWNHOST_KEY_ED25519;
 528         key_type = "ED25519";
 529         break;
 530 #endif
 531     default:
 532         mc_propagate_error (mcerror, 0, "%s",
 533                             _ ("sftp: unsupported key type, can't check remote host key"));
 534         return FALSE;
 535     }
 536 
 537     fingerprint_hash = sftpfs_compute_fingerprint_hash (sftpfs_super->session);
 538     if (fingerprint_hash == NULL)
 539     {
 540         mc_propagate_error (mcerror, 0, "%s", _ ("sftp: can't compute host key fingerprint hash"));
 541         return FALSE;
 542     }
 543 
 544     rc = libssh2_knownhost_checkp (
 545         sftpfs_super->known_hosts, super->path_element->host, super->path_element->port, remote_key,
 546         remote_key_len, LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | keybit,
 547         &host);
 548 
 549     switch (rc)
 550     {
 551     default:
 552     case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
 553         // something prevented the check to be made
 554         goto err;
 555 
 556     case LIBSSH2_KNOWNHOST_CHECK_MATCH:
 557         // host + key pair matched -- OK
 558         break;
 559 
 560     case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
 561         // no host match was found -- add it to the known_hosts file
 562         msg = g_strdup_printf (
 563             _ ("The authenticity of host\n%s (%s)\ncan't be established!\n"
 564                "%s key fingerprint hash is\nSHA1:%s.\n"
 565                "Do you want to add it to the list of known hosts and continue connecting?"),
 566             super->path_element->host, sftpfs_super->ip_address, key_type, fingerprint_hash);
 567         // Select "No" initially
 568         query_set_sel (2);
 569         rc = query_dialog (_ ("Warning"), msg, D_NORMAL, 3, _ ("&Yes"), _ ("&Ignore"), _ ("&No"));
 570         g_free (msg);
 571         handle_query = TRUE;
 572         break;
 573 
 574     case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
 575         msg = g_strdup_printf (_ ("%s (%s)\nis found in the list of known hosts but\n"
 576                                   "KEYS DO NOT MATCH! THIS COULD BE A MITM ATTACK!\n"
 577                                   "Are you sure you want to add it to the list of known hosts and "
 578                                   "continue connecting?"),
 579                                super->path_element->host, sftpfs_super->ip_address);
 580         // Select "No" initially
 581         query_set_sel (2);
 582         rc = query_dialog (MSG_ERROR, msg, D_ERROR, 3, _ ("&Yes"), _ ("&Ignore"), _ ("&No"));
 583         g_free (msg);
 584         handle_query = TRUE;
 585         break;
 586     }
 587 
 588     if (handle_query)
 589         switch (rc)
 590         {
 591         case 0:
 592             // Yes: add this host + key pair, continue connecting
 593             if (sftpfs_update_known_hosts (super, remote_key, remote_key_len,
 594                                            LIBSSH2_KNOWNHOST_TYPE_PLAIN
 595                                                | LIBSSH2_KNOWNHOST_KEYENC_RAW | keybit)
 596                 < 0)
 597                 goto err;
 598             break;
 599         case 1:
 600             // Ignore: do not add this host + key pair, continue connecting anyway
 601             break;
 602         case 2:
 603         default:
 604             mc_propagate_error (mcerror, 0, "%s", _ ("sftp: host key verification failed"));
 605             // No: abort connection
 606             goto err;
 607         }
 608 
 609     return TRUE;
 610 
 611 err:
 612 {
 613     int sftp_errno;
 614 
 615     sftp_errno = libssh2_session_last_errno (sftpfs_super->session);
 616     sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror);
 617 }
 618 
 619     return FALSE;
 620 }
 621 
 622 /* --------------------------------------------------------------------------------------------- */
 623 /**
 624  * Recognize authentication types supported by remote side and filling internal 'super' structure by
 625  * proper enum's values.
 626  *
 627  * @param super connection data
 628  * @return TRUE if some of authentication methods is available, FALSE otherwise
 629  */
 630 static gboolean
 631 sftpfs_recognize_auth_types (struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 632 {
 633     char *userauthlist;
 634     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 635 
 636     // check what authentication methods are available
 637     // userauthlist is internally managed by libssh2 and freed by libssh2_session_free()
 638     userauthlist = libssh2_userauth_list (sftpfs_super->session, super->path_element->user,
 639                                           strlen (super->path_element->user));
 640 
 641     if (userauthlist == NULL)
 642         return FALSE;
 643 
 644     if ((strstr (userauthlist, "password") != NULL
 645          || strstr (userauthlist, "keyboard-interactive") != NULL)
 646         && (sftpfs_super->config_auth_type & PASSWORD) != 0)
 647         sftpfs_super->auth_type |= PASSWORD;
 648 
 649     if (strstr (userauthlist, "publickey") != NULL
 650         && (sftpfs_super->config_auth_type & PUBKEY) != 0)
 651         sftpfs_super->auth_type |= PUBKEY;
 652 
 653     if ((sftpfs_super->config_auth_type & AGENT) != 0)
 654         sftpfs_super->auth_type |= AGENT;
 655 
 656     return TRUE;
 657 }
 658 
 659 /* --------------------------------------------------------------------------------------------- */
 660 /**
 661  * Open connection to host using SSH-agent helper.
 662  *
 663  * @param super   connection data
 664  * @param mcerror pointer to the error handler
 665  * @return TRUE if connection was successfully opened, FALSE otherwise
 666  */
 667 
 668 static gboolean
 669 sftpfs_open_connection_ssh_agent (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 670 {
 671     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 672     struct libssh2_agent_publickey *identity, *prev_identity = NULL;
 673     int rc;
 674 
 675     mc_return_val_if_error (mcerror, FALSE);
 676 
 677     sftpfs_super->agent = NULL;
 678 
 679     if ((sftpfs_super->auth_type & AGENT) == 0)
 680         return FALSE;
 681 
 682     // Connect to the ssh-agent
 683     sftpfs_super->agent = libssh2_agent_init (sftpfs_super->session);
 684     if (sftpfs_super->agent == NULL)
 685         return FALSE;
 686 
 687     if (libssh2_agent_connect (sftpfs_super->agent) != 0)
 688         return FALSE;
 689 
 690     if (libssh2_agent_list_identities (sftpfs_super->agent) != 0)
 691         return FALSE;
 692 
 693     while (TRUE)
 694     {
 695         rc = libssh2_agent_get_identity (sftpfs_super->agent, &identity, prev_identity);
 696         if (rc == 1)
 697             break;
 698 
 699         if (rc < 0)
 700             return FALSE;
 701 
 702         if (libssh2_agent_userauth (sftpfs_super->agent, super->path_element->user, identity) == 0)
 703             break;
 704 
 705         prev_identity = identity;
 706     }
 707 
 708     return (rc == 0);
 709 }
 710 
 711 /* --------------------------------------------------------------------------------------------- */
 712 /**
 713  * Open connection to host using SSH-keypair.
 714  *
 715  * @param super   connection data
 716  * @param mcerror pointer to the error handler
 717  * @return TRUE if connection was successfully opened, FALSE otherwise
 718  */
 719 
 720 static gboolean
 721 sftpfs_open_connection_ssh_key (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 722 {
 723     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 724     char *p, *passwd;
 725     gboolean ret_value = FALSE;
 726 
 727     mc_return_val_if_error (mcerror, FALSE);
 728 
 729     if ((sftpfs_super->auth_type & PUBKEY) == 0)
 730         return FALSE;
 731 
 732     if (sftpfs_super->privkey == NULL)
 733         return FALSE;
 734 
 735     if (libssh2_userauth_publickey_fromfile (sftpfs_super->session, super->path_element->user,
 736                                              sftpfs_super->pubkey, sftpfs_super->privkey,
 737                                              super->path_element->password)
 738         == 0)
 739         return TRUE;
 740 
 741     p = g_strdup_printf (_ ("sftp: Enter passphrase for %s "), super->path_element->user);
 742     passwd = vfs_get_password (p);
 743     g_free (p);
 744 
 745     if (passwd == NULL)
 746         mc_propagate_error (mcerror, 0, "%s", _ ("sftp: Passphrase is empty."));
 747     else
 748     {
 749         ret_value = (libssh2_userauth_publickey_fromfile (
 750                          sftpfs_super->session, super->path_element->user, sftpfs_super->pubkey,
 751                          sftpfs_super->privkey, passwd)
 752                      == 0);
 753         g_free (passwd);
 754     }
 755 
 756     return ret_value;
 757 }
 758 
 759 /* --------------------------------------------------------------------------------------------- */
 760 
 761 /**
 762  * Keyboard-interactive password helper for opening connection to host by
 763  * sftpfs_open_connection_ssh_password
 764  *
 765  * Uses global kbi_super (data with existing connection) and kbi_passwd (password)
 766  *
 767  * @param name             username
 768  * @param name_len         length of @name
 769  * @param instruction      unused
 770  * @param instruction_len  unused
 771  * @param num_prompts      number of possible problems to process
 772  * @param prompts          array of prompts to process
 773  * @param responses        array of responses, one per prompt
 774  * @param abstract         unused
 775  */
 776 
 777 static LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC (sftpfs_keyboard_interactive_helper)
     /* [previous][next][first][last][top][bottom][index][help]  */
 778 {
 779     int i;
 780     size_t len;
 781 
 782     (void) instruction;
 783     (void) instruction_len;
 784     (void) abstract;
 785 
 786     if (kbi_super == NULL || kbi_passwd == NULL)
 787         return;
 788 
 789     if (strncmp (name, kbi_super->path_element->user, name_len) != 0)
 790         return;
 791 
 792     // assume these are password prompts
 793     len = strlen (kbi_passwd);
 794 
 795     for (i = 0; i < num_prompts; ++i)
 796         if (memcmp (prompts[i].text, "Password: ", prompts[i].length) == 0)
 797         {
 798             responses[i].text = strdup (kbi_passwd);
 799             responses[i].length = len;
 800         }
 801 }
 802 
 803 /* --------------------------------------------------------------------------------------------- */
 804 /**
 805  * Open connection to host using password.
 806  *
 807  * @param super   connection data
 808  * @param mcerror pointer to the error handler
 809  * @return TRUE if connection was successfully opened, FALSE otherwise
 810  */
 811 
 812 static gboolean
 813 sftpfs_open_connection_ssh_password (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 814 {
 815     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 816     char *p, *passwd;
 817     gboolean ret_value = FALSE;
 818     int rc;
 819 
 820     mc_return_val_if_error (mcerror, FALSE);
 821 
 822     if ((sftpfs_super->auth_type & PASSWORD) == 0)
 823         return FALSE;
 824 
 825     if (super->path_element->password != NULL)
 826     {
 827         while ((rc = libssh2_userauth_password (sftpfs_super->session, super->path_element->user,
 828                                                 super->path_element->password))
 829                == LIBSSH2_ERROR_EAGAIN)
 830             ;
 831         if (rc == 0)
 832             return TRUE;
 833 
 834         kbi_super = super;
 835         kbi_passwd = super->path_element->password;
 836 
 837         while ((rc = libssh2_userauth_keyboard_interactive (sftpfs_super->session,
 838                                                             super->path_element->user,
 839                                                             sftpfs_keyboard_interactive_helper))
 840                == LIBSSH2_ERROR_EAGAIN)
 841             ;
 842 
 843         kbi_super = NULL;
 844         kbi_passwd = NULL;
 845 
 846         if (rc == 0)
 847             return TRUE;
 848     }
 849 
 850     p = g_strdup_printf (_ ("sftp: Enter password for %s "), super->path_element->user);
 851     passwd = vfs_get_password (p);
 852     g_free (p);
 853 
 854     if (passwd == NULL)
 855         mc_propagate_error (mcerror, 0, "%s", _ ("sftp: Password is empty."));
 856     else
 857     {
 858         while ((rc = libssh2_userauth_password (sftpfs_super->session, super->path_element->user,
 859                                                 passwd))
 860                == LIBSSH2_ERROR_EAGAIN)
 861             ;
 862 
 863         if (rc != 0)
 864         {
 865             kbi_super = super;
 866             kbi_passwd = passwd;
 867 
 868             while ((rc = libssh2_userauth_keyboard_interactive (sftpfs_super->session,
 869                                                                 super->path_element->user,
 870                                                                 sftpfs_keyboard_interactive_helper))
 871                    == LIBSSH2_ERROR_EAGAIN)
 872                 ;
 873 
 874             kbi_super = NULL;
 875             kbi_passwd = NULL;
 876         }
 877 
 878         if (rc == 0)
 879         {
 880             ret_value = TRUE;
 881             g_free (super->path_element->password);
 882             super->path_element->password = passwd;
 883         }
 884         else
 885             g_free (passwd);
 886     }
 887 
 888     return ret_value;
 889 }
 890 
 891 /* --------------------------------------------------------------------------------------------- */
 892 /*** public functions ****************************************************************************/
 893 /* --------------------------------------------------------------------------------------------- */
 894 /**
 895  * Open new connection.
 896  *
 897  * @param super   connection data
 898  * @param mcerror pointer to the error handler
 899  * @return 0 if success, -1 otherwise
 900  */
 901 
 902 int
 903 sftpfs_open_connection (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 904 {
 905     int rc;
 906     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 907 
 908     mc_return_val_if_error (mcerror, -1);
 909 
 910     /*
 911      * The application code is responsible for creating the socket
 912      * and establishing the connection
 913      */
 914     sftpfs_super->socket_handle = sftpfs_open_socket (super, mcerror);
 915     if (sftpfs_super->socket_handle == LIBSSH2_INVALID_SOCKET)
 916         return (-1);
 917 
 918     // Create a session instance
 919     sftpfs_super->session = libssh2_session_init ();
 920     if (sftpfs_super->session == NULL)
 921         return (-1);
 922 
 923     if (!sftpfs_read_known_hosts (super, mcerror))
 924         return (-1);
 925 
 926     /* ... start it up. This will trade welcome banners, exchange keys,
 927      * and setup crypto, compression, and MAC layers
 928      */
 929     while ((rc = libssh2_session_handshake (sftpfs_super->session,
 930                                             (libssh2_socket_t) sftpfs_super->socket_handle))
 931            == LIBSSH2_ERROR_EAGAIN)
 932         ;
 933     if (rc != 0)
 934     {
 935         mc_propagate_error (mcerror, rc, "%s", _ ("sftp: failure establishing SSH session"));
 936         return (-1);
 937     }
 938 
 939     if (!sftpfs_process_known_host (super, mcerror))
 940         return (-1);
 941 
 942     if (!sftpfs_recognize_auth_types (super))
 943     {
 944         int sftp_errno;
 945 
 946         sftp_errno = libssh2_session_last_errno (sftpfs_super->session);
 947         sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror);
 948         return (-1);
 949     }
 950 
 951     if (!sftpfs_open_connection_ssh_agent (super, mcerror)
 952         && !sftpfs_open_connection_ssh_key (super, mcerror)
 953         && !sftpfs_open_connection_ssh_password (super, mcerror))
 954         return (-1);
 955 
 956     sftpfs_super->sftp_session = libssh2_sftp_init (sftpfs_super->session);
 957 
 958     if (sftpfs_super->sftp_session == NULL)
 959         return (-1);
 960 
 961     // Since we have not set non-blocking, tell libssh2 we are blocking
 962     libssh2_session_set_blocking (sftpfs_super->session, 1);
 963 
 964     return 0;
 965 }
 966 
 967 /* --------------------------------------------------------------------------------------------- */
 968 /**
 969  * Close connection.
 970  *
 971  * @param super            connection data
 972  * @param shutdown_message message for shutdown functions
 973  * @param mcerror          pointer to the error handler
 974  */
 975 
 976 void
 977 sftpfs_close_connection (struct vfs_s_super *super, const char *shutdown_message, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 978 {
 979     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 980 
 981     // no mc_return_*_if_error() here because of abort open_connection handling too
 982     (void) mcerror;
 983 
 984     if (sftpfs_super->sftp_session != NULL)
 985     {
 986         libssh2_sftp_shutdown (sftpfs_super->sftp_session);
 987         sftpfs_super->sftp_session = NULL;
 988     }
 989 
 990     if (sftpfs_super->agent != NULL)
 991     {
 992         libssh2_agent_disconnect (sftpfs_super->agent);
 993         libssh2_agent_free (sftpfs_super->agent);
 994         sftpfs_super->agent = NULL;
 995     }
 996 
 997     if (sftpfs_super->known_hosts != NULL)
 998     {
 999         libssh2_knownhost_free (sftpfs_super->known_hosts);
1000         sftpfs_super->known_hosts = NULL;
1001     }
1002 
1003     MC_PTR_FREE (sftpfs_super->known_hosts_file);
1004 
1005     if (sftpfs_super->session != NULL)
1006     {
1007         libssh2_session_disconnect (sftpfs_super->session, shutdown_message);
1008         libssh2_session_free (sftpfs_super->session);
1009         sftpfs_super->session = NULL;
1010     }
1011 
1012     if (sftpfs_super->socket_handle != LIBSSH2_INVALID_SOCKET)
1013     {
1014         close (sftpfs_super->socket_handle);
1015         sftpfs_super->socket_handle = LIBSSH2_INVALID_SOCKET;
1016     }
1017 }
1018 
1019 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */