Skip to content

Commit

Permalink
Dropbear ssh: client config file
Browse files Browse the repository at this point in the history
Adding support for a per-client configuration file. Akin to the one
provided by the OpenSSH client. This initial implementation is
minimalistic with just basic support for a few options.
* Default config file location is ~/.ssh/dropbear_config. Can't be
customized. Not using `config` to avoid incompatibilities or worse -
silent changes of bahvior in the ssh clients.
* Added DROPBEAR_DEFAULT_USE_SSH_CONFIG complie directive. Currently
the default is to build the client without config file support. Override
in your localoptions.h
* Manpage update.
* .gitignore the .vscode folder - Visual Studio Code private files.

tjk :)
  • Loading branch information
tjkolev authored and mkj committed Apr 3, 2024
1 parent 2ea7450 commit 12ab8ff
Show file tree
Hide file tree
Showing 7 changed files with 319 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ tags
/test/venv/
/test/init/
/test/fakekey
.vscode/
.vscode/
2 changes: 1 addition & 1 deletion Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ SVROBJS = $(patsubst %,$(OBJ_DIR)/%,$(_SVROBJS))
_CLIOBJS=cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
cli-session.o cli-runopts.o cli-chansession.o \
cli-authpubkey.o cli-tcpfwd.o cli-channel.o cli-authinteract.o \
cli-agentfwd.o
cli-agentfwd.o cli-readconf.o
CLIOBJS = $(patsubst %,$(OBJ_DIR)/%,$(_CLIOBJS))

_CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
Expand Down
49 changes: 47 additions & 2 deletions manpages/dbclient.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH dbclient 1
.TH dbclient 1 2023-02-01
.SH NAME
dbclient \- lightweight SSH client
.SH SYNOPSIS
Expand All @@ -22,7 +22,6 @@ dbclient \- lightweight SSH client
is the client part of Dropbear SSH
.SH OPTIONS
.TP
.TP
.B command
A command to run on the remote host. This will normally be run by the remote host
using the user's shell. The command begins at the first hyphen argument after the
Expand Down Expand Up @@ -222,6 +221,52 @@ SSH_ASKPASS should be set to the path of a program that will return a password
on standard output. This program will only be used if either DISPLAY is set and
standard input is not a TTY, or the environment variable SSH_ASKPASS_ALWAYS is
set.

.SH FILES
.B ~/.ssh/dropbear_config

This is the per user configuration file. A very limited subset of the keywords for
ssh_config(5) is supported, and none of the advanced features. The file contains
key value pairs on a single line separated with space or '='. Empty lines are ignored.
Text starting with '#' is a comment, and also ignored.

The file is not considered if multi-hop connection is used. Values on the command line
override the respective values in the file.

The recognized keywords are as follows. Keywords are case insensitive and values are
case insensitive.

.TP
.B Host
Defines the options that would be applied if this value matches the host specified
on the command line. The next Host entry or EOF determine the list of applicable
options.

.TP
.B HostName
Specifies the actual host name to connect to. Can be DNS name or IP address.

.TP
.B Port
Specifies the port number to use to connect to the remote host.

.TP
.B
User
Specifies the user name to login in as.

.TP
.B
IdentityFile
Specifies the file with the private key used for public key authentication with the remote
host. The file must be in the Dropbear format. See dropbearkey(1) to generate one. A '~/' at
the start of the path will expanded to the executing user's home directory. A path that
does not start with '/' will be treated relative to this configuration file's directory. Otherwise
the path will be used as is.

Because this file contains a secret it must have strict permissions to prevent abuse
attempts - read/write for the executing user, and no access to anyone else.

.SH NOTES
If compiled with zlib support and if the server supports it, dbclient will
always use compression.
Expand Down
192 changes: 192 additions & 0 deletions src/cli-readconf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Dropbear - a SSH2 server
*
* Copyright (c) 2023 TJ Kolev
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. */

#include "dbutil.h"
#include "runopts.h"

#if DROPBEAR_DEFAULT_USE_SSH_CONFIG

#define TOKEN_CHARS " =\t\n"

#if DROPBEAR_CLI_PUBKEY_AUTH
extern void loadidentityfile(const char* filename, int warnfail);
#endif

typedef enum {
opInvalid = -1,
opHost,
opHostName,
opHostPort,
opLoginUser,
opIdentityFile,
} CfgOption;

static struct {
const char *name;
CfgOption option;
} ConfigOptions[] =
{
// start of config section
{ "host", opHost },

{ "hostname", opHostName },
{ "port", opHostPort },
{ "user", opLoginUser },
{ "identityfile", opIdentityFile },

// end loop condintion
{ NULL, opInvalid },
};

void read_config_file(char* filename, FILE* configFile, cli_runopts* options)
{
DEBUG1(("Reading configuration data '%.200s'", filename));

char *line = NULL;
size_t linesize = 0;
int linenum = 0;

char* cfgKey;
char* cfgVal;
char* saveptr;

int inHostSection = 0;
while(-1 != getline(&line, &linesize, configFile))
{
/* Update line number counter. */
linenum++;

char* commentStart = strchr(line, '#');
if(NULL != commentStart)
{
*commentStart = '\0'; // drop the comments
}

cfgKey = strtok_r(line, TOKEN_CHARS, &saveptr);
if(NULL == cfgKey)
{
continue;
}

CfgOption cfgOpt = opInvalid;
for (int i = 0; ConfigOptions[i].name; i++)
{
if (0 == strcasecmp(cfgKey, ConfigOptions[i].name))
{
cfgOpt = ConfigOptions[i].option;
break;
}
}

if(opInvalid == cfgOpt)
{
dropbear_exit("Unhandled key %s at '%s':%d.", cfgKey, filename, linenum);
}


cfgVal = strtok_r(NULL, TOKEN_CHARS, &saveptr);
if(NULL == cfgVal)
{
dropbear_exit("Missing value for key %s at '%s':%d.", cfgKey, filename, linenum);
}

if(inHostSection)
{
if(opHost == cfgOpt)
{
// Hit the next host section. Done reading config.
break;
}
switch(cfgOpt)
{
case opHostName:
{
// The host name is the alias given on the command line.
// Set the actual remote host specified in the config.
options->remotehost = strdup(cfgVal);
options->remotehostfixed = 1; // Subsequent command line parsing should leave it alone.
break;
}

case opHostPort:
{
options->remoteport = strdup(cfgVal);
break;
}

case opLoginUser:
{
options->username = strdup(cfgVal);
break;
}

case opIdentityFile:
{
#if DROPBEAR_CLI_PUBKEY_AUTH
char* keyFilePath;
if(strncmp(cfgVal, "~/", 2) == 0)
{
keyFilePath = expand_homedir_path(cfgVal);
}
else if(cfgVal[0] != '/')
{
char* configDir = dirname(filename);
int pathLen = strlen(configDir) + strlen(cfgVal) + 10;
char cbuff[pathLen];
snprintf(cbuff, pathLen, "%s/%s", configDir, cfgVal);
keyFilePath = strdup(cbuff);
}
else
{
keyFilePath = strdup(cfgVal);
}
loadidentityfile(keyFilePath, 1);
free(keyFilePath);
#else
dropbear_exit("This version of the code does not support identity file. %s at '%s':%d.", cfgKey, filename, linenum);
#endif
break;
}

default:
{
dropbear_exit("Unsupported configuration option %s at '%s':%d.", cfgKey, filename, linenum);
}
}
}
else
{
if(opHost != cfgOpt || 0 != strcmp(cfgVal, options->remotehost))
{
// Not our host section
continue;
}
inHostSection = 1;
}
}

free(line);
}

#endif // DROPBEAR_DEFAULT_USE_SSH_CONFIG
Loading

0 comments on commit 12ab8ff

Please sign in to comment.