summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorDave Davenport <qball@gmpclient.org>2019-03-04 22:05:08 +0100
committerDave Davenport <qball@gmpclient.org>2019-03-04 22:05:08 +0100
commit906a48f566d8c1c3366daefb3c454a17466e8e21 (patch)
tree0c60c1a15896a90ffe637bb08d75002c53615d91 /source
parentdf10eb27351fdafe6698ac8b33d4e4c18d167501 (diff)
[SSH] Add support for UserKnownHostsFile
Issue: #928
Diffstat (limited to 'source')
-rw-r--r--source/dialogs/ssh.c83
1 files changed, 52 insertions, 31 deletions
diff --git a/source/dialogs/ssh.c b/source/dialogs/ssh.c
index 23a9d163..2d998034 100644
--- a/source/dialogs/ssh.c
+++ b/source/dialogs/ssh.c
@@ -56,6 +56,23 @@
#include "history.h"
#include "dialogs/ssh.h"
+
+typedef struct _SshEntry {
+ char *hostname;
+ int port;
+} SshEntry;
+/**
+ * The internal data structure holding the private data of the SSH Mode.
+ */
+typedef struct
+{
+ GList *user_known_hosts;
+ /** List if available ssh hosts.*/
+ SshEntry *hosts_list;
+ /** Length of the #hosts_list.*/
+ unsigned int hosts_list_length;
+} SSHModePrivateData;
+
/**
* Name of the history file where previously chosen hosts are stored.
*/
@@ -68,11 +85,6 @@
#define SSH_TOKEN_DELIM "= \t\r\n"
-typedef struct _SshEntry {
- char *hostname;
- int port;
-} SshEntry;
-
/**
* @param entry The host to connect too
*
@@ -127,7 +139,7 @@ static void exec_ssh ( const SshEntry *entry )
char *path = g_build_filename ( cache_dir, SSH_CACHE_FILE, NULL );
// TODO update.
if ( entry->port > 0 ) {
- char *store = g_strdup_printf("%s:%d", entry->hostname, entry->port );
+ char *store = g_strdup_printf("%s:%d", entry->hostname, entry->port );
history_set ( path, store );
g_free ( store );
} else {
@@ -159,9 +171,8 @@ static void delete_ssh ( const char *host )
*
* @returns updated list of hosts.
*/
-static SshEntry *read_known_hosts_file ( SshEntry * retv, unsigned int *length )
+static SshEntry *read_known_hosts_file ( const char *path, SshEntry * retv, unsigned int *length )
{
- char *path = g_build_filename ( g_get_home_dir (), ".ssh", "known_hosts", NULL );
FILE *fd = fopen ( path, "r" );
if ( fd != NULL ) {
char *buffer = NULL;
@@ -173,7 +184,6 @@ static SshEntry *read_known_hosts_file ( SshEntry * retv, unsigned int *length )
// Find start.
if ( *start == '#' || *start == '@' ){
// skip comments or cert-authority or revoked items.
- printf("Comment.\n");
continue;
}
if ( *start == '|' ) {
@@ -214,7 +224,7 @@ static SshEntry *read_known_hosts_file ( SshEntry * retv, unsigned int *length )
// Add this host name to the list.
retv = g_realloc ( retv, ( ( *length ) + 2 ) * sizeof ( SshEntry ) );
retv[( *length )].hostname = g_strdup ( start );
- retv[( *length )].port = port;
+ retv[( *length )].port = port;
retv[( *length ) + 1].hostname = NULL;
retv[( *length ) + 1].port = 0;
( *length )++;
@@ -228,9 +238,10 @@ static SshEntry *read_known_hosts_file ( SshEntry * retv, unsigned int *length )
if ( fclose ( fd ) != 0 ) {
g_warning ( "Failed to close hosts file: '%s'", g_strerror ( errno ) );
}
+ } else {
+ g_debug ( "Failed to open KnownHostFile: '%s'", path );
}
- g_free ( path );
return retv;
}
@@ -308,7 +319,15 @@ static SshEntry *read_hosts_file ( SshEntry * retv, unsigned int *length )
return retv;
}
-static void parse_ssh_config_file ( const char *filename, SshEntry **retv, unsigned int *length, unsigned int num_favorites )
+static void add_known_hosts_file ( SSHModePrivateData *pd, const char *token )
+{
+ GList *item = g_list_find_custom ( pd->user_known_hosts, token, g_str_equal );
+ if ( item == NULL ) {
+ pd->user_known_hosts = g_list_append ( pd->user_known_hosts, g_strdup ( token ) );
+ }
+}
+
+static void parse_ssh_config_file ( SSHModePrivateData *pd, const char *filename, SshEntry **retv, unsigned int *length, unsigned int num_favorites )
{
FILE *fd = fopen ( filename, "r" );
@@ -324,7 +343,6 @@ static void parse_ssh_config_file ( const char *filename, SshEntry **retv, unsig
// The keyword is separated from its arguments by whitespace OR by
// optional whitespace and a '=' character.
char *token = strtok_r ( buffer, SSH_TOKEN_DELIM, &strtok_pointer );
-
// Skip empty lines and comment lines. Also skip lines where the
// keyword is not "Host".
if ( !token || *token == '#' ) {
@@ -348,7 +366,7 @@ static void parse_ssh_config_file ( const char *filename, SshEntry **retv, unsig
if ( glob ( full_path, 0, NULL, &globbuf ) == 0 ) {
for ( size_t iter = 0; iter < globbuf.gl_pathc; iter++ ) {
- parse_ssh_config_file ( globbuf.gl_pathv[iter], retv, length, num_favorites );
+ parse_ssh_config_file ( pd, globbuf.gl_pathv[iter], retv, length, num_favorites );
}
}
globfree ( &globbuf );
@@ -356,6 +374,12 @@ static void parse_ssh_config_file ( const char *filename, SshEntry **retv, unsig
g_free ( full_path );
g_free ( path );
}
+ else if ( g_strcmp0 ( token, "UserKnownHostsFile" ) == 0 ) {
+ while ( ( token = strtok_r ( NULL, SSH_TOKEN_DELIM, &strtok_pointer ) ) ) {
+ g_debug("Found extra UserKnownHostsFile: %s", token);
+ add_known_hosts_file ( pd, token );
+ }
+ }
else if ( g_strcmp0 ( token, "Host" ) == 0 ) {
// Now we know that this is a "Host" line.
// The "Host" keyword is followed by one more host names separated
@@ -414,7 +438,7 @@ static void parse_ssh_config_file ( const char *filename, SshEntry **retv, unsig
*
* @return an array of strings containing all the hosts.
*/
-static SshEntry * get_ssh ( unsigned int *length )
+static SshEntry * get_ssh ( SSHModePrivateData *pd, unsigned int *length )
{
SshEntry *retv = NULL;
unsigned int num_favorites = 0;
@@ -433,7 +457,7 @@ static SshEntry * get_ssh ( unsigned int *length )
char *portstr = strchr ( h[i], ':' );
if ( portstr != NULL ) {
*portstr = '\0';
- port = atoi ( &(portstr[1]) );
+ port = atoi ( &(portstr[1]) );
}
retv[i].hostname = h[i];
retv[i].port = port;
@@ -443,32 +467,28 @@ static SshEntry * get_ssh ( unsigned int *length )
g_free ( path );
num_favorites = ( *length );
+ const char *hd = g_get_home_dir ();
+ path = g_build_filename ( hd, ".ssh", "config", NULL );
+ parse_ssh_config_file ( pd, path, &retv, length, num_favorites );
+
if ( config.parse_known_hosts == TRUE ) {
- retv = read_known_hosts_file ( retv, length );
+ char *path = g_build_filename ( g_get_home_dir (), ".ssh", "known_hosts", NULL );
+ retv = read_known_hosts_file ( path, retv, length );
+ g_free ( path );
+ for ( GList *iter = g_list_first ( pd->user_known_hosts); iter; iter = g_list_next ( iter ) ) {
+ retv = read_known_hosts_file ( (const char*)iter->data, retv, length );
+ }
}
if ( config.parse_hosts == TRUE ) {
retv = read_hosts_file ( retv, length );
}
- const char *hd = g_get_home_dir ();
- path = g_build_filename ( hd, ".ssh", "config", NULL );
- parse_ssh_config_file ( path, &retv, length, num_favorites );
g_free ( path );
return retv;
}
-/**
- * The internal data structure holding the private data of the SSH Mode.
- */
-typedef struct
-{
- /** List if available ssh hosts.*/
- SshEntry *hosts_list;
- /** Length of the #hosts_list.*/
- unsigned int hosts_list_length;
-} SSHModePrivateData;
/**
* @param sw Object handle to the SSH Mode object
@@ -481,7 +501,7 @@ static int ssh_mode_init ( Mode *sw )
if ( mode_get_private_data ( sw ) == NULL ) {
SSHModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) );
mode_set_private_data ( sw, (void *) pd );
- pd->hosts_list = get_ssh ( &( pd->hosts_list_length ) );
+ pd->hosts_list = get_ssh ( pd, &( pd->hosts_list_length ) );
}
return TRUE;
}
@@ -510,6 +530,7 @@ static void ssh_mode_destroy ( Mode *sw )
for ( unsigned int i = 0; i < rmpd->hosts_list_length; i++ ){
g_free( rmpd->hosts_list[i].hostname );
}
+ g_list_free_full ( rmpd->user_known_hosts, g_free );
g_free ( rmpd->hosts_list );
g_free ( rmpd );
mode_set_private_data ( sw, NULL );