root/src/vfs/sftpfs/config_parser.c

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

DEFINITIONS

This source file includes following definitions.
  1. sftpfs_ssh_config_entity_free
  2. sftpfs_correct_file_name
  3. sftpfs_fill_config_entity_from_string
  4. sftpfs_fill_config_entity_from_config
  5. sftpfs_get_config_entity
  6. sftpfs_fill_connection_data_from_config
  7. sftpfs_init_config_variables_patterns
  8. sftpfs_deinit_config_variables_patterns

   1 /* Virtual File System: SFTP file system.
   2    The SSH config parser
   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 #include <stddef.h>
  30 #include <stdlib.h>             /* atoi() */
  31 
  32 #include "lib/global.h"
  33 
  34 #include "lib/search.h"
  35 #include "lib/util.h"           /* tilde_expand() */
  36 #include "lib/vfs/utilvfs.h"
  37 
  38 #include "internal.h"
  39 
  40 /*** global variables ****************************************************************************/
  41 
  42 /*** file scope macro definitions ****************************************************************/
  43 
  44 #define SFTP_DEFAULT_PORT 22
  45 
  46 #ifndef SFTPFS_SSH_CONFIG
  47 #define SFTPFS_SSH_CONFIG "~/.ssh/config"
  48 #endif
  49 
  50 /*** file scope type declarations ****************************************************************/
  51 
  52 typedef struct
  53 {
  54     char *real_host;            /* host DNS name or ip address */
  55     int port;                   /* port for connect to host */
  56     char *user;                 /* the user to log in as */
  57     gboolean password_auth;     /* FALSE - no passwords allowed (default TRUE) */
  58     gboolean identities_only;   /* TRUE - no ssh agent (default FALSE) */
  59     gboolean pubkey_auth;       /* FALSE - disable public key authentication (default TRUE) */
  60     char *identity_file;        /* A file from which the user's DSA, ECDSA or DSA authentication identity is read. */
  61 } sftpfs_ssh_config_entity_t;
  62 
  63 enum config_var_type
  64 {
  65     STRING,
  66     INTEGER,
  67     BOOLEAN,
  68     FILENAME
  69 };
  70 
  71 /*** file scope variables ************************************************************************/
  72 
  73 /* *INDENT-OFF* */
  74 static struct
  75 {
  76     const char *pattern;
  77     mc_search_t *pattern_regexp;
  78     enum config_var_type type;
  79     size_t offset;
  80 } config_variables[] =
  81 {
  82     {"^\\s*User\\s+(.*)$", NULL, STRING, offsetof (sftpfs_ssh_config_entity_t, user)},
  83     {"^\\s*HostName\\s+(.*)$", NULL, STRING, offsetof (sftpfs_ssh_config_entity_t, real_host)},
  84     {"^\\s*IdentitiesOnly\\s+(.*)$", NULL, BOOLEAN, offsetof (sftpfs_ssh_config_entity_t, identities_only)},
  85     {"^\\s*IdentityFile\\s+(.*)$", NULL, FILENAME, offsetof (sftpfs_ssh_config_entity_t, identity_file)},
  86     {"^\\s*Port\\s+(.*)$", NULL, INTEGER, offsetof (sftpfs_ssh_config_entity_t, port)},
  87     {"^\\s*PasswordAuthentication\\s+(.*)$", NULL, BOOLEAN, offsetof (sftpfs_ssh_config_entity_t, password_auth)},
  88     {"^\\s*PubkeyAuthentication\\s+(.*)$", NULL, STRING, offsetof (sftpfs_ssh_config_entity_t, pubkey_auth)},
  89     {NULL, NULL, 0, 0}
  90 };
  91 /* *INDENT-ON* */
  92 
  93 /* --------------------------------------------------------------------------------------------- */
  94 /*** file scope functions ************************************************************************/
  95 /* --------------------------------------------------------------------------------------------- */
  96 /**
  97  * Free one config entity.
  98  *
  99  * @param config_entity config entity structure
 100  */
 101 
 102 static void
 103 sftpfs_ssh_config_entity_free (sftpfs_ssh_config_entity_t * config_entity)
     /* [previous][next][first][last][top][bottom][index][help]  */
 104 {
 105     g_free (config_entity->real_host);
 106     g_free (config_entity->user);
 107     g_free (config_entity->identity_file);
 108     g_free (config_entity);
 109 }
 110 
 111 /* --------------------------------------------------------------------------------------------- */
 112 /**
 113  * Transform tilda (~) to full home dirname.
 114  *
 115  * @param filename file name with tilda
 116  * @return newly allocated file name with full home dirname
 117  */
 118 
 119 static char *
 120 sftpfs_correct_file_name (const char *filename)
     /* [previous][next][first][last][top][bottom][index][help]  */
 121 {
 122     vfs_path_t *vpath;
 123     char *fn;
 124 
 125     fn = tilde_expand (filename);
 126     vpath = vfs_path_from_str (fn);
 127     g_free (fn);
 128     return vfs_path_free (vpath, FALSE);
 129 }
 130 
 131 /* --------------------------------------------------------------------------------------------- */
 132 
 133 #define POINTER_TO_STRUCTURE_MEMBER(type)  \
 134     ((type) ((char *) config_entity + (size_t) config_variables[i].offset))
 135 
 136 /**
 137  * Parse string and filling one config entity by parsed data.
 138  *
 139  * @param config_entity config entity structure
 140  * @param buffer        string for parce
 141  */
 142 
 143 static void
 144 sftpfs_fill_config_entity_from_string (sftpfs_ssh_config_entity_t * config_entity, char *buffer)
     /* [previous][next][first][last][top][bottom][index][help]  */
 145 {
 146     int i;
 147 
 148     for (i = 0; config_variables[i].pattern != NULL; i++)
 149     {
 150         if (mc_search_run (config_variables[i].pattern_regexp, buffer, 0, strlen (buffer), NULL))
 151         {
 152             int value_offset;
 153             char *value;
 154 
 155             int *pointer_int;
 156             char **pointer_str;
 157             gboolean *pointer_bool;
 158 
 159             /* Calculate start of value in string */
 160             value_offset = mc_search_getstart_result_by_num (config_variables[i].pattern_regexp, 1);
 161             value = &buffer[value_offset];
 162 
 163             switch (config_variables[i].type)
 164             {
 165             case STRING:
 166                 pointer_str = POINTER_TO_STRUCTURE_MEMBER (char **);
 167                 *pointer_str = g_strdup (value);
 168                 break;
 169             case FILENAME:
 170                 pointer_str = POINTER_TO_STRUCTURE_MEMBER (char **);
 171                 *pointer_str = sftpfs_correct_file_name (value);
 172                 break;
 173             case INTEGER:
 174                 pointer_int = POINTER_TO_STRUCTURE_MEMBER (int *);
 175                 *pointer_int = atoi (value);
 176                 break;
 177             case BOOLEAN:
 178                 pointer_bool = POINTER_TO_STRUCTURE_MEMBER (gboolean *);
 179                 *pointer_bool = strcasecmp (value, "True") == 0;
 180                 break;
 181             default:
 182                 continue;
 183             }
 184             return;
 185         }
 186     }
 187 }
 188 
 189 #undef POINTER_TO_STRUCTURE_MEMBER
 190 
 191 /* --------------------------------------------------------------------------------------------- */
 192 /**
 193  * Fill one config entity from config file.
 194  *
 195  * @param ssh_config_handler file descriptor for the ssh config file
 196  * @param config_entity      config entity structure
 197  * @param vpath_element      path element with host data (hostname, port)
 198  * @param mcerror            pointer to the error handler
 199  * @return TRUE if config entity was filled successfully, FALSE otherwise
 200  */
 201 
 202 static gboolean
 203 sftpfs_fill_config_entity_from_config (FILE * ssh_config_handler,
     /* [previous][next][first][last][top][bottom][index][help]  */
 204                                        sftpfs_ssh_config_entity_t * config_entity,
 205                                        const vfs_path_element_t * vpath_element, GError ** mcerror)
 206 {
 207     char buffer[BUF_MEDIUM];
 208     gboolean host_block_hit = FALSE;
 209     gboolean pattern_block_hit = FALSE;
 210     mc_search_t *host_regexp;
 211     gboolean ok = TRUE;
 212 
 213     mc_return_val_if_error (mcerror, FALSE);
 214 
 215     host_regexp = mc_search_new ("^\\s*host\\s+(.*)$", DEFAULT_CHARSET);
 216     host_regexp->search_type = MC_SEARCH_T_REGEX;
 217     host_regexp->is_case_sensitive = FALSE;
 218 
 219     while (TRUE)
 220     {
 221         char *cr;
 222 
 223         if (fgets (buffer, sizeof (buffer), ssh_config_handler) == NULL)
 224         {
 225             int e;
 226 
 227             e = errno;
 228 
 229             if (!feof (ssh_config_handler))
 230             {
 231                 mc_propagate_error (mcerror, e,
 232                                     _("sftp: an error occurred while reading %s: %s"),
 233                                     SFTPFS_SSH_CONFIG, strerror (e));
 234                 ok = FALSE;
 235                 goto done;
 236             }
 237 
 238             break;
 239         }
 240 
 241         cr = strrchr (buffer, '\n');
 242         if (cr != NULL)
 243             *cr = '\0';
 244 
 245         if (mc_search_run (host_regexp, buffer, 0, strlen (buffer), NULL))
 246         {
 247             const char *host_pattern;
 248             int host_pattern_offset;
 249 
 250             /* if previous host block exactly describe our connection */
 251             if (host_block_hit)
 252                 goto done;
 253 
 254             host_pattern_offset = mc_search_getstart_result_by_num (host_regexp, 1);
 255             host_pattern = &buffer[host_pattern_offset];
 256             if (strcmp (host_pattern, vpath_element->host) == 0)
 257             {
 258                 /* current host block describe our connection */
 259                 host_block_hit = TRUE;
 260             }
 261             else
 262             {
 263                 mc_search_t *pattern_regexp;
 264 
 265                 pattern_regexp = mc_search_new (host_pattern, DEFAULT_CHARSET);
 266                 pattern_regexp->search_type = MC_SEARCH_T_GLOB;
 267                 pattern_regexp->is_case_sensitive = FALSE;
 268                 pattern_regexp->is_entire_line = TRUE;
 269                 pattern_block_hit =
 270                     mc_search_run (pattern_regexp, vpath_element->host, 0,
 271                                    strlen (vpath_element->host), NULL);
 272                 mc_search_free (pattern_regexp);
 273             }
 274         }
 275         else if (pattern_block_hit || host_block_hit)
 276         {
 277             sftpfs_fill_config_entity_from_string (config_entity, buffer);
 278         }
 279     }
 280 
 281   done:
 282     mc_search_free (host_regexp);
 283     return ok;
 284 }
 285 
 286 /* --------------------------------------------------------------------------------------------- */
 287 /**
 288  * Open the ssh config file and fill config entity.
 289  *
 290  * @param vpath_element path element with host data (hostname, port)
 291  * @param mcerror       pointer to the error handler
 292  * @return newly allocated config entity structure
 293  */
 294 
 295 static sftpfs_ssh_config_entity_t *
 296 sftpfs_get_config_entity (const vfs_path_element_t * vpath_element, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 297 {
 298     sftpfs_ssh_config_entity_t *config_entity;
 299     FILE *ssh_config_handler;
 300     char *config_filename;
 301 
 302     mc_return_val_if_error (mcerror, FALSE);
 303 
 304     config_entity = g_new0 (sftpfs_ssh_config_entity_t, 1);
 305     config_entity->password_auth = TRUE;
 306     config_entity->identities_only = FALSE;
 307     config_entity->pubkey_auth = TRUE;
 308     config_entity->port = SFTP_DEFAULT_PORT;
 309 
 310     config_filename = sftpfs_correct_file_name (SFTPFS_SSH_CONFIG);
 311     ssh_config_handler = fopen (config_filename, "r");
 312     g_free (config_filename);
 313 
 314     if (ssh_config_handler != NULL)
 315     {
 316         gboolean ok;
 317 
 318         ok = sftpfs_fill_config_entity_from_config
 319             (ssh_config_handler, config_entity, vpath_element, mcerror);
 320         fclose (ssh_config_handler);
 321 
 322         if (!ok)
 323         {
 324             sftpfs_ssh_config_entity_free (config_entity);
 325             return NULL;
 326         }
 327     }
 328 
 329     if (config_entity->user == NULL)
 330     {
 331         config_entity->user = vfs_get_local_username ();
 332         if (config_entity->user == NULL)
 333         {
 334             sftpfs_ssh_config_entity_free (config_entity);
 335             config_entity = NULL;
 336             mc_propagate_error (mcerror, EPERM, "%s", _("sftp: Unable to get current user name."));
 337         }
 338     }
 339     return config_entity;
 340 }
 341 
 342 /* --------------------------------------------------------------------------------------------- */
 343 /*** public functions ****************************************************************************/
 344 /* --------------------------------------------------------------------------------------------- */
 345 /**
 346  * Reads data from the ssh config file related to connection.
 347  *
 348  * @param super connection data
 349  * @param error pointer to the error handler
 350  */
 351 
 352 void
 353 sftpfs_fill_connection_data_from_config (struct vfs_s_super *super, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 354 {
 355     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 356     sftpfs_ssh_config_entity_t *config_entity;
 357 
 358     mc_return_if_error (mcerror);
 359 
 360     config_entity = sftpfs_get_config_entity (super->path_element, mcerror);
 361     if (config_entity == NULL)
 362         return;
 363 
 364     sftpfs_super->config_auth_type = (config_entity->pubkey_auth) ? PUBKEY : 0;
 365     sftpfs_super->config_auth_type |= (config_entity->identities_only) ? 0 : AGENT;
 366     sftpfs_super->config_auth_type |= (config_entity->password_auth) ? PASSWORD : 0;
 367 
 368     if (super->path_element->port == 0)
 369         super->path_element->port = config_entity->port;
 370 
 371     if (super->path_element->user == NULL)
 372         super->path_element->user = g_strdup (config_entity->user);
 373 
 374     if (config_entity->real_host != NULL)
 375     {
 376         g_free (super->path_element->host);
 377         super->path_element->host = g_strdup (config_entity->real_host);
 378     }
 379 
 380     if (config_entity->identity_file != NULL)
 381     {
 382         sftpfs_super->privkey = g_strdup (config_entity->identity_file);
 383         sftpfs_super->pubkey = g_strdup_printf ("%s.pub", config_entity->identity_file);
 384     }
 385 
 386     sftpfs_ssh_config_entity_free (config_entity);
 387 }
 388 
 389 /* --------------------------------------------------------------------------------------------- */
 390 /**
 391  * Initialize the SSH config parser.
 392  */
 393 
 394 void
 395 sftpfs_init_config_variables_patterns (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 396 {
 397     int i;
 398 
 399     for (i = 0; config_variables[i].pattern != NULL; i++)
 400     {
 401         config_variables[i].pattern_regexp =
 402             mc_search_new (config_variables[i].pattern, DEFAULT_CHARSET);
 403         config_variables[i].pattern_regexp->search_type = MC_SEARCH_T_REGEX;
 404         config_variables[i].pattern_regexp->is_case_sensitive = FALSE;
 405     }
 406 }
 407 
 408 /* --------------------------------------------------------------------------------------------- */
 409 /**
 410  * Deinitialize the SSH config parser.
 411  */
 412 
 413 void
 414 sftpfs_deinit_config_variables_patterns (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 415 {
 416     int i;
 417 
 418     for (i = 0; config_variables[i].pattern != NULL; i++)
 419     {
 420         mc_search_free (config_variables[i].pattern_regexp);
 421         config_variables[i].pattern_regexp = NULL;
 422     }
 423 }
 424 
 425 /* --------------------------------------------------------------------------------------------- */

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