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