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

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