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     rc = libssh2_knownhost_readfile (sftpfs_super->known_hosts, sftpfs_super->known_hosts_file,
 277                                      LIBSSH2_KNOWNHOST_FILE_OPENSSH);
 278     if (rc > 0)
 279     {
 280         const char *kh_name_end = NULL;
 281 
 282         while (!found && libssh2_knownhost_get (sftpfs_super->known_hosts, &store, store) == 0)
 283         {
 284             /* For non-standard ports, the name will be enclosed in
 285              * square brackets, followed by a colon and the port */
 286             if (store == NULL)
 287                 continue;
 288 
 289             if (store->name == NULL)
 290                 // Ignore hashed hostnames. Currently, libssh2 offers no way for us to match it
 291                 continue;
 292 
 293             if (store->name[0] != '[')
 294                 found = strcmp (store->name, super->path_element->host) == 0;
 295             else
 296             {
 297                 int port;
 298 
 299                 kh_name_end = strstr (store->name, "]:");
 300                 if (kh_name_end == NULL)
 301                     // Invalid host pattern
 302                     continue;
 303 
 304                 port = (int) g_ascii_strtoll (kh_name_end + 2, NULL, 10);
 305                 if (port == super->path_element->port)
 306                 {
 307                     size_t kh_name_size;
 308 
 309                     kh_name_size = strlen (store->name) - 1 - strlen (kh_name_end);
 310                     found = strncmp (store->name + 1, super->path_element->host, kh_name_size) == 0;
 311                 }
 312             }
 313         }
 314     }
 315 
 316     if (found)
 317     {
 318         int mask;
 319         const char *hostkey_method = NULL;
 320         char *hostkey_methods;
 321 
 322         mask = store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK;
 323 
 324         switch (mask)
 325         {
 326 #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
 327         case LIBSSH2_KNOWNHOST_KEY_ED25519:
 328             hostkey_method = hostkey_method_ssh_ed25519;
 329             break;
 330 #endif
 331 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521
 332         case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
 333             hostkey_method = hostkey_method_ssh_ecdsa_521;
 334             break;
 335 #endif
 336 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384
 337         case LIBSSH2_KNOWNHOST_KEY_ECDSA_384:
 338             hostkey_method = hostkey_method_ssh_ecdsa_384;
 339             break;
 340 #endif
 341 #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
 342         case LIBSSH2_KNOWNHOST_KEY_ECDSA_256:
 343             hostkey_method = hostkey_method_ssh_ecdsa_256;
 344             break;
 345 #endif
 346         case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
 347             hostkey_method = hostkey_method_ssh_rsa;
 348             break;
 349         case LIBSSH2_KNOWNHOST_KEY_SSHDSS:
 350             hostkey_method = hostkey_method_ssh_dss;
 351             break;
 352         case LIBSSH2_KNOWNHOST_KEY_RSA1:
 353             mc_propagate_error (mcerror, 0, "%s",
 354                                 _ ("sftp: found host key of unsupported type: RSA1"));
 355             return FALSE;
 356         default:
 357             mc_propagate_error (mcerror, 0, "%s 0x%x", _ ("sftp: unknown host key type:"),
 358                                 (unsigned int) mask);
 359             return FALSE;
 360         }
 361 
 362         /* Append the default hostkey methods (with lower priority).
 363          * Since we ignored hashed hostnames, the actual matching host
 364          * key might have different type than the one found in
 365          * known_hosts for non-hashed hostname. Methods not supported
 366          * by libssh2 it are ignored. */
 367         hostkey_methods = g_strdup_printf ("%s,%s", hostkey_method, default_hostkey_methods);
 368         rc = libssh2_session_method_pref (sftpfs_super->session, LIBSSH2_METHOD_HOSTKEY,
 369                                           hostkey_methods);
 370         g_free (hostkey_methods);
 371         if (rc < 0)
 372             goto err;
 373     }
 374 
 375     return TRUE;
 376 
 377 err:
 378 {
 379     int sftp_errno;
 380 
 381     sftp_errno = libssh2_session_last_errno (sftpfs_super->session);
 382     sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror);
 383 }
 384     return FALSE;
 385 }
 386 
 387 /* --------------------------------------------------------------------------------------------- */
 388 
 389 /**
 390  * Write new host + key pair to the ~/.ssh/known_hosts file.
 391  *
 392  * @param super connection data
 393  * @param remote_key he key for the remote host
 394  * @param remote_key_len length of @remote_key
 395  * @param type_mask info about format of host name, key and key type
 396  * @return 0 on success, regular libssh2 error code otherwise
 397  *
 398  * Thanks the Curl project for the code used in this function.
 399  */
 400 static int
 401 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]  */
 402                            int type_mask)
 403 {
 404     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 405     int rc;
 406 
 407     // add this host + key pair
 408     rc = libssh2_knownhost_addc (sftpfs_super->known_hosts, super->path_element->host, NULL,
 409                                  remote_key, remote_key_len, NULL, 0, type_mask, NULL);
 410     if (rc < 0)
 411         return rc;
 412 
 413     // write the entire in-memory list of known hosts to the known_hosts file
 414     rc = libssh2_knownhost_writefile (sftpfs_super->known_hosts, sftpfs_super->known_hosts_file,
 415                                       LIBSSH2_KNOWNHOST_FILE_OPENSSH);
 416 
 417     if (rc < 0)
 418         return rc;
 419 
 420     (void) message (D_NORMAL, _ ("Information"),
 421                     _ ("Permanently added\n%s (%s)\nto the list of known hosts."),
 422                     super->path_element->host, sftpfs_super->ip_address);
 423 
 424     return 0;
 425 }
 426 
 427 /* --------------------------------------------------------------------------------------------- */
 428 /**
 429  * Compute and return readable host key fingerprint hash.
 430  *
 431  * @param session libssh2 session handle
 432  * @return pointer to static buffer on success, NULL otherwise
 433  */
 434 static const char *
 435 sftpfs_compute_fingerprint_hash (LIBSSH2_SESSION *session)
     /* [previous][next][first][last][top][bottom][index][help]  */
 436 {
 437     static char result[SHA1_DIGEST_LENGTH * 3 + 1];  // "XX:" for each byte, and EOL
 438     const char *fingerprint;
 439     size_t i;
 440 
 441     // The fingerprint points to static storage (!), don't free() it.
 442     fingerprint = libssh2_hostkey_hash (session, LIBSSH2_HOSTKEY_HASH_SHA1);
 443     if (fingerprint == NULL)
 444         return NULL;
 445 
 446     for (i = 0; i < SHA1_DIGEST_LENGTH && i * 3 < sizeof (result) - 1; i++)
 447         g_snprintf ((gchar *) (result + i * 3), 4, "%02x:", (guint8) fingerprint[i]);
 448 
 449     // remove last ":"
 450     result[i * 3 - 1] = '\0';
 451 
 452     return result;
 453 }
 454 
 455 /* --------------------------------------------------------------------------------------------- */
 456 
 457 /**
 458  * Process host info found in ~/.ssh/known_hosts file.
 459  *
 460  * @param super connection data
 461  * @param mcerror pointer to the error handler
 462  * @return TRUE on success, FALSE otherwise
 463  *
 464  * Thanks the Curl project for the code used in this function.
 465  */
 466 static gboolean
 467 sftpfs_process_known_host (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 468 {
 469     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 470     const char *remote_key;
 471     const char *key_type;
 472     const char *fingerprint_hash;
 473     size_t remote_key_len = 0;
 474     int remote_key_type = LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
 475     int keybit = 0;
 476     struct libssh2_knownhost *host = NULL;
 477     int rc;
 478     char *msg = NULL;
 479     gboolean handle_query = FALSE;
 480 
 481     remote_key = libssh2_session_hostkey (sftpfs_super->session, &remote_key_len, &remote_key_type);
 482     if (remote_key == NULL || remote_key_len == 0
 483         || remote_key_type == LIBSSH2_HOSTKEY_TYPE_UNKNOWN)
 484     {
 485         mc_propagate_error (mcerror, 0, "%s", _ ("sftp: cannot get the remote host key"));
 486         return FALSE;
 487     }
 488 
 489     switch (remote_key_type)
 490     {
 491     case LIBSSH2_HOSTKEY_TYPE_RSA:
 492         keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
 493         key_type = "RSA";
 494         break;
 495     case LIBSSH2_HOSTKEY_TYPE_DSS:
 496         keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
 497         key_type = "DSS";
 498         break;
 499 #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
 500     case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
 501         keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
 502         key_type = "ECDSA";
 503         break;
 504 #endif
 505 #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384
 506     case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
 507         keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
 508         key_type = "ECDSA";
 509         break;
 510 #endif
 511 #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521
 512     case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
 513         keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
 514         key_type = "ECDSA";
 515         break;
 516 #endif
 517 #ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
 518     case LIBSSH2_HOSTKEY_TYPE_ED25519:
 519         keybit = LIBSSH2_KNOWNHOST_KEY_ED25519;
 520         key_type = "ED25519";
 521         break;
 522 #endif
 523     default:
 524         mc_propagate_error (mcerror, 0, "%s",
 525                             _ ("sftp: unsupported key type, can't check remote host key"));
 526         return FALSE;
 527     }
 528 
 529     fingerprint_hash = sftpfs_compute_fingerprint_hash (sftpfs_super->session);
 530     if (fingerprint_hash == NULL)
 531     {
 532         mc_propagate_error (mcerror, 0, "%s", _ ("sftp: can't compute host key fingerprint hash"));
 533         return FALSE;
 534     }
 535 
 536     rc = libssh2_knownhost_checkp (
 537         sftpfs_super->known_hosts, super->path_element->host, super->path_element->port, remote_key,
 538         remote_key_len, LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | keybit,
 539         &host);
 540 
 541     switch (rc)
 542     {
 543     default:
 544     case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
 545         // something prevented the check to be made
 546         goto err;
 547 
 548     case LIBSSH2_KNOWNHOST_CHECK_MATCH:
 549         // host + key pair matched -- OK
 550         break;
 551 
 552     case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
 553         // no host match was found -- add it to the known_hosts file
 554         msg = g_strdup_printf (
 555             _ ("The authenticity of host\n%s (%s)\ncan't be established!\n"
 556                "%s key fingerprint hash is\nSHA1:%s.\n"
 557                "Do you want to add it to the list of known hosts and continue connecting?"),
 558             super->path_element->host, sftpfs_super->ip_address, key_type, fingerprint_hash);
 559         // Select "No" initially
 560         query_set_sel (2);
 561         rc = query_dialog (_ ("Warning"), msg, D_NORMAL, 3, _ ("&Yes"), _ ("&Ignore"), _ ("&No"));
 562         g_free (msg);
 563         handle_query = TRUE;
 564         break;
 565 
 566     case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
 567         msg = g_strdup_printf (_ ("%s (%s)\nis found in the list of known hosts but\n"
 568                                   "KEYS DO NOT MATCH! THIS COULD BE A MITM ATTACK!\n"
 569                                   "Are you sure you want to add it to the list of known hosts and "
 570                                   "continue connecting?"),
 571                                super->path_element->host, sftpfs_super->ip_address);
 572         // Select "No" initially
 573         query_set_sel (2);
 574         rc = query_dialog (MSG_ERROR, msg, D_ERROR, 3, _ ("&Yes"), _ ("&Ignore"), _ ("&No"));
 575         g_free (msg);
 576         handle_query = TRUE;
 577         break;
 578     }
 579 
 580     if (handle_query)
 581         switch (rc)
 582         {
 583         case 0:
 584             // Yes: add this host + key pair, continue connecting
 585             if (sftpfs_update_known_hosts (super, remote_key, remote_key_len,
 586                                            LIBSSH2_KNOWNHOST_TYPE_PLAIN
 587                                                | LIBSSH2_KNOWNHOST_KEYENC_RAW | keybit)
 588                 < 0)
 589                 goto err;
 590             break;
 591         case 1:
 592             // Ignore: do not add this host + key pair, continue connecting anyway
 593             break;
 594         case 2:
 595         default:
 596             mc_propagate_error (mcerror, 0, "%s", _ ("sftp: host key verification failed"));
 597             // No: abort connection
 598             goto err;
 599         }
 600 
 601     return TRUE;
 602 
 603 err:
 604 {
 605     int sftp_errno;
 606 
 607     sftp_errno = libssh2_session_last_errno (sftpfs_super->session);
 608     sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror);
 609 }
 610 
 611     return FALSE;
 612 }
 613 
 614 /* --------------------------------------------------------------------------------------------- */
 615 /**
 616  * Recognize authentication types supported by remote side and filling internal 'super' structure by
 617  * proper enum's values.
 618  *
 619  * @param super connection data
 620  * @return TRUE if some of authentication methods is available, FALSE otherwise
 621  */
 622 static gboolean
 623 sftpfs_recognize_auth_types (struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 624 {
 625     char *userauthlist;
 626     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 627 
 628     // check what authentication methods are available
 629     // userauthlist is internally managed by libssh2 and freed by libssh2_session_free()
 630     userauthlist = libssh2_userauth_list (sftpfs_super->session, super->path_element->user,
 631                                           strlen (super->path_element->user));
 632 
 633     if (userauthlist == NULL)
 634         return FALSE;
 635 
 636     if ((strstr (userauthlist, "password") != NULL
 637          || strstr (userauthlist, "keyboard-interactive") != NULL)
 638         && (sftpfs_super->config_auth_type & PASSWORD) != 0)
 639         sftpfs_super->auth_type |= PASSWORD;
 640 
 641     if (strstr (userauthlist, "publickey") != NULL
 642         && (sftpfs_super->config_auth_type & PUBKEY) != 0)
 643         sftpfs_super->auth_type |= PUBKEY;
 644 
 645     if ((sftpfs_super->config_auth_type & AGENT) != 0)
 646         sftpfs_super->auth_type |= AGENT;
 647 
 648     return TRUE;
 649 }
 650 
 651 /* --------------------------------------------------------------------------------------------- */
 652 /**
 653  * Open connection to host using SSH-agent helper.
 654  *
 655  * @param super   connection data
 656  * @param mcerror pointer to the error handler
 657  * @return TRUE if connection was successfully opened, FALSE otherwise
 658  */
 659 
 660 static gboolean
 661 sftpfs_open_connection_ssh_agent (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 662 {
 663     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 664     struct libssh2_agent_publickey *identity, *prev_identity = NULL;
 665     int rc;
 666 
 667     mc_return_val_if_error (mcerror, FALSE);
 668 
 669     sftpfs_super->agent = NULL;
 670 
 671     if ((sftpfs_super->auth_type & AGENT) == 0)
 672         return FALSE;
 673 
 674     // Connect to the ssh-agent
 675     sftpfs_super->agent = libssh2_agent_init (sftpfs_super->session);
 676     if (sftpfs_super->agent == NULL)
 677         return FALSE;
 678 
 679     if (libssh2_agent_connect (sftpfs_super->agent) != 0)
 680         return FALSE;
 681 
 682     if (libssh2_agent_list_identities (sftpfs_super->agent) != 0)
 683         return FALSE;
 684 
 685     while (TRUE)
 686     {
 687         rc = libssh2_agent_get_identity (sftpfs_super->agent, &identity, prev_identity);
 688         if (rc == 1)
 689             break;
 690 
 691         if (rc < 0)
 692             return FALSE;
 693 
 694         if (libssh2_agent_userauth (sftpfs_super->agent, super->path_element->user, identity) == 0)
 695             break;
 696 
 697         prev_identity = identity;
 698     }
 699 
 700     return (rc == 0);
 701 }
 702 
 703 /* --------------------------------------------------------------------------------------------- */
 704 /**
 705  * Open connection to host using SSH-keypair.
 706  *
 707  * @param super   connection data
 708  * @param mcerror pointer to the error handler
 709  * @return TRUE if connection was successfully opened, FALSE otherwise
 710  */
 711 
 712 static gboolean
 713 sftpfs_open_connection_ssh_key (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 714 {
 715     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 716     char *p, *passwd;
 717     gboolean ret_value = FALSE;
 718 
 719     mc_return_val_if_error (mcerror, FALSE);
 720 
 721     if ((sftpfs_super->auth_type & PUBKEY) == 0)
 722         return FALSE;
 723 
 724     if (sftpfs_super->privkey == NULL)
 725         return FALSE;
 726 
 727     if (libssh2_userauth_publickey_fromfile (sftpfs_super->session, super->path_element->user,
 728                                              sftpfs_super->pubkey, sftpfs_super->privkey,
 729                                              super->path_element->password)
 730         == 0)
 731         return TRUE;
 732 
 733     p = g_strdup_printf (_ ("sftp: Enter passphrase for %s "), super->path_element->user);
 734     passwd = vfs_get_password (p);
 735     g_free (p);
 736 
 737     if (passwd == NULL)
 738         mc_propagate_error (mcerror, 0, "%s", _ ("sftp: Passphrase is empty."));
 739     else
 740     {
 741         ret_value = (libssh2_userauth_publickey_fromfile (
 742                          sftpfs_super->session, super->path_element->user, sftpfs_super->pubkey,
 743                          sftpfs_super->privkey, passwd)
 744                      == 0);
 745         g_free (passwd);
 746     }
 747 
 748     return ret_value;
 749 }
 750 
 751 /* --------------------------------------------------------------------------------------------- */
 752 
 753 /**
 754  * Keyboard-interactive password helper for opening connection to host by
 755  * sftpfs_open_connection_ssh_password
 756  *
 757  * Uses global kbi_super (data with existing connection) and kbi_passwd (password)
 758  *
 759  * @param name             username
 760  * @param name_len         length of @name
 761  * @param instruction      unused
 762  * @param instruction_len  unused
 763  * @param num_prompts      number of possible problems to process
 764  * @param prompts          array of prompts to process
 765  * @param responses        array of responses, one per prompt
 766  * @param abstract         unused
 767  */
 768 
 769 static LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC (sftpfs_keyboard_interactive_helper)
     /* [previous][next][first][last][top][bottom][index][help]  */
 770 {
 771     int i;
 772     size_t len;
 773 
 774     (void) instruction;
 775     (void) instruction_len;
 776     (void) abstract;
 777 
 778     if (kbi_super == NULL || kbi_passwd == NULL)
 779         return;
 780 
 781     if (strncmp (name, kbi_super->path_element->user, name_len) != 0)
 782         return;
 783 
 784     // assume these are password prompts
 785     len = strlen (kbi_passwd);
 786 
 787     for (i = 0; i < num_prompts; ++i)
 788         if (memcmp (prompts[i].text, "Password: ", prompts[i].length) == 0)
 789         {
 790             responses[i].text = strdup (kbi_passwd);
 791             responses[i].length = len;
 792         }
 793 }
 794 
 795 /* --------------------------------------------------------------------------------------------- */
 796 /**
 797  * Open connection to host using password.
 798  *
 799  * @param super   connection data
 800  * @param mcerror pointer to the error handler
 801  * @return TRUE if connection was successfully opened, FALSE otherwise
 802  */
 803 
 804 static gboolean
 805 sftpfs_open_connection_ssh_password (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 806 {
 807     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 808     char *p, *passwd;
 809     gboolean ret_value = FALSE;
 810     int rc;
 811 
 812     mc_return_val_if_error (mcerror, FALSE);
 813 
 814     if ((sftpfs_super->auth_type & PASSWORD) == 0)
 815         return FALSE;
 816 
 817     if (super->path_element->password != NULL)
 818     {
 819         while ((rc = libssh2_userauth_password (sftpfs_super->session, super->path_element->user,
 820                                                 super->path_element->password))
 821                == LIBSSH2_ERROR_EAGAIN)
 822             ;
 823         if (rc == 0)
 824             return TRUE;
 825 
 826         kbi_super = super;
 827         kbi_passwd = super->path_element->password;
 828 
 829         while ((rc = libssh2_userauth_keyboard_interactive (sftpfs_super->session,
 830                                                             super->path_element->user,
 831                                                             sftpfs_keyboard_interactive_helper))
 832                == LIBSSH2_ERROR_EAGAIN)
 833             ;
 834 
 835         kbi_super = NULL;
 836         kbi_passwd = NULL;
 837 
 838         if (rc == 0)
 839             return TRUE;
 840     }
 841 
 842     p = g_strdup_printf (_ ("sftp: Enter password for %s "), super->path_element->user);
 843     passwd = vfs_get_password (p);
 844     g_free (p);
 845 
 846     if (passwd == NULL)
 847         mc_propagate_error (mcerror, 0, "%s", _ ("sftp: Password is empty."));
 848     else
 849     {
 850         while ((rc = libssh2_userauth_password (sftpfs_super->session, super->path_element->user,
 851                                                 passwd))
 852                == LIBSSH2_ERROR_EAGAIN)
 853             ;
 854 
 855         if (rc != 0)
 856         {
 857             kbi_super = super;
 858             kbi_passwd = passwd;
 859 
 860             while ((rc = libssh2_userauth_keyboard_interactive (sftpfs_super->session,
 861                                                                 super->path_element->user,
 862                                                                 sftpfs_keyboard_interactive_helper))
 863                    == LIBSSH2_ERROR_EAGAIN)
 864                 ;
 865 
 866             kbi_super = NULL;
 867             kbi_passwd = NULL;
 868         }
 869 
 870         if (rc == 0)
 871         {
 872             ret_value = TRUE;
 873             g_free (super->path_element->password);
 874             super->path_element->password = passwd;
 875         }
 876         else
 877             g_free (passwd);
 878     }
 879 
 880     return ret_value;
 881 }
 882 
 883 /* --------------------------------------------------------------------------------------------- */
 884 /*** public functions ****************************************************************************/
 885 /* --------------------------------------------------------------------------------------------- */
 886 /**
 887  * Open new connection.
 888  *
 889  * @param super   connection data
 890  * @param mcerror pointer to the error handler
 891  * @return 0 if success, -1 otherwise
 892  */
 893 
 894 int
 895 sftpfs_open_connection (struct vfs_s_super *super, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 896 {
 897     int rc;
 898     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 899 
 900     mc_return_val_if_error (mcerror, -1);
 901 
 902     /*
 903      * The application code is responsible for creating the socket
 904      * and establishing the connection
 905      */
 906     sftpfs_super->socket_handle = sftpfs_open_socket (super, mcerror);
 907     if (sftpfs_super->socket_handle == LIBSSH2_INVALID_SOCKET)
 908         return (-1);
 909 
 910     // Create a session instance
 911     sftpfs_super->session = libssh2_session_init ();
 912     if (sftpfs_super->session == NULL)
 913         return (-1);
 914 
 915     if (!sftpfs_read_known_hosts (super, mcerror))
 916         return (-1);
 917 
 918     /* ... start it up. This will trade welcome banners, exchange keys,
 919      * and setup crypto, compression, and MAC layers
 920      */
 921     while ((rc = libssh2_session_handshake (sftpfs_super->session,
 922                                             (libssh2_socket_t) sftpfs_super->socket_handle))
 923            == LIBSSH2_ERROR_EAGAIN)
 924         ;
 925     if (rc != 0)
 926     {
 927         mc_propagate_error (mcerror, rc, "%s", _ ("sftp: failure establishing SSH session"));
 928         return (-1);
 929     }
 930 
 931     if (!sftpfs_process_known_host (super, mcerror))
 932         return (-1);
 933 
 934     if (!sftpfs_recognize_auth_types (super))
 935     {
 936         int sftp_errno;
 937 
 938         sftp_errno = libssh2_session_last_errno (sftpfs_super->session);
 939         sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror);
 940         return (-1);
 941     }
 942 
 943     if (!sftpfs_open_connection_ssh_agent (super, mcerror)
 944         && !sftpfs_open_connection_ssh_key (super, mcerror)
 945         && !sftpfs_open_connection_ssh_password (super, mcerror))
 946         return (-1);
 947 
 948     sftpfs_super->sftp_session = libssh2_sftp_init (sftpfs_super->session);
 949 
 950     if (sftpfs_super->sftp_session == NULL)
 951         return (-1);
 952 
 953     // Since we have not set non-blocking, tell libssh2 we are blocking
 954     libssh2_session_set_blocking (sftpfs_super->session, 1);
 955 
 956     return 0;
 957 }
 958 
 959 /* --------------------------------------------------------------------------------------------- */
 960 /**
 961  * Close connection.
 962  *
 963  * @param super            connection data
 964  * @param shutdown_message message for shutdown functions
 965  * @param mcerror          pointer to the error handler
 966  */
 967 
 968 void
 969 sftpfs_close_connection (struct vfs_s_super *super, const char *shutdown_message, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 970 {
 971     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 972 
 973     // no mc_return_*_if_error() here because of abort open_connection handling too
 974     (void) mcerror;
 975 
 976     if (sftpfs_super->sftp_session != NULL)
 977     {
 978         libssh2_sftp_shutdown (sftpfs_super->sftp_session);
 979         sftpfs_super->sftp_session = NULL;
 980     }
 981 
 982     if (sftpfs_super->agent != NULL)
 983     {
 984         libssh2_agent_disconnect (sftpfs_super->agent);
 985         libssh2_agent_free (sftpfs_super->agent);
 986         sftpfs_super->agent = NULL;
 987     }
 988 
 989     if (sftpfs_super->known_hosts != NULL)
 990     {
 991         libssh2_knownhost_free (sftpfs_super->known_hosts);
 992         sftpfs_super->known_hosts = NULL;
 993     }
 994 
 995     MC_PTR_FREE (sftpfs_super->known_hosts_file);
 996 
 997     if (sftpfs_super->session != NULL)
 998     {
 999         libssh2_session_disconnect (sftpfs_super->session, shutdown_message);
1000         libssh2_session_free (sftpfs_super->session);
1001         sftpfs_super->session = NULL;
1002     }
1003 
1004     if (sftpfs_super->socket_handle != LIBSSH2_INVALID_SOCKET)
1005     {
1006         close (sftpfs_super->socket_handle);
1007         sftpfs_super->socket_handle = LIBSSH2_INVALID_SOCKET;
1008     }
1009 }
1010 
1011 /* --------------------------------------------------------------------------------------------- */

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