summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.bin/ssh/readconf.c112
-rw-r--r--usr.bin/ssh/readconf.h3
-rw-r--r--usr.bin/ssh/ssh.15
-rw-r--r--usr.bin/ssh/ssh_config.523
4 files changed, 127 insertions, 16 deletions
diff --git a/usr.bin/ssh/readconf.c b/usr.bin/ssh/readconf.c
index 91a8e00a463..a407bab7780 100644
--- a/usr.bin/ssh/readconf.c
+++ b/usr.bin/ssh/readconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.251 2016/04/06 06:42:17 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.252 2016/04/15 00:30:19 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -24,6 +24,7 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#include <glob.h>
#include <netdb.h>
#include <paths.h>
#include <pwd.h>
@@ -114,11 +115,18 @@
*/
+static int read_config_file_depth(const char *filename, struct passwd *pw,
+ const char *host, const char *original_host, Options *options,
+ int flags, int *activep, int depth);
+static int process_config_line_depth(Options *options, struct passwd *pw,
+ const char *host, const char *original_host, char *line,
+ const char *filename, int linenum, int *activep, int flags, int depth);
+
/* Keyword tokens. */
typedef enum {
oBadOption,
- oHost, oMatch,
+ oHost, oMatch, oInclude,
oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout,
oGatewayPorts, oExitOnForwardFailure,
oPasswordAuthentication, oRSAAuthentication,
@@ -247,6 +255,7 @@ static struct {
{ "controlmaster", oControlMaster },
{ "controlpersist", oControlPersist },
{ "hashknownhosts", oHashKnownHosts },
+ { "include", oInclude },
{ "tunnel", oTunnel },
{ "tunneldevice", oTunnelDevice },
{ "localcommand", oLocalCommand },
@@ -772,22 +781,32 @@ static const struct multistate multistate_canonicalizehostname[] = {
* Processes a single option line as used in the configuration files. This
* only sets those values that have not already been set.
*/
-#define WHITESPACE " \t\r\n"
int
process_config_line(Options *options, struct passwd *pw, const char *host,
const char *original_host, char *line, const char *filename,
int linenum, int *activep, int flags)
{
+ return process_config_line_depth(options, pw, host, original_host,
+ line, filename, linenum, activep, flags, 0);
+}
+
+#define WHITESPACE " \t\r\n"
+static int
+process_config_line_depth(Options *options, struct passwd *pw, const char *host,
+ const char *original_host, char *line, const char *filename,
+ int linenum, int *activep, int flags, int depth)
+{
char *s, **charptr, *endofnumber, *keyword, *arg, *arg2;
char **cpptr, fwdarg[256];
u_int i, *uintptr, max_entries = 0;
- int negated, opcode, *intptr, value, value2, cmdline = 0;
+ int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0;
LogLevel *log_level_ptr;
long long val64;
size_t len;
struct Forward fwd;
const struct multistate *multistate_ptr;
struct allowed_cname *cname;
+ glob_t gl;
if (activep == NULL) { /* We are processing a command line directive */
cmdline = 1;
@@ -1247,6 +1266,8 @@ parse_keytypes:
*activep = 0;
arg2 = NULL;
while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
+ if ((flags & SSHCONF_NEVERMATCH) != 0)
+ break;
negated = *arg == '!';
if (negated)
arg++;
@@ -1279,7 +1300,7 @@ parse_keytypes:
if (value < 0)
fatal("%.200s line %d: Bad Match condition", filename,
linenum);
- *activep = value;
+ *activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value;
break;
case oEscapeChar:
@@ -1407,6 +1428,63 @@ parse_keytypes:
intptr = &options->visual_host_key;
goto parse_flag;
+ case oInclude:
+ if (cmdline)
+ fatal("Include directive not supported as a "
+ "command-line option");
+ value = 0;
+ while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
+ /*
+ * Ensure all paths are anchored. User configuration
+ * files may begin with '~/' but system configurations
+ * must not. If the path is relative, then treat it
+ * as living in ~/.ssh for user configurations or
+ * /etc/ssh for system ones.
+ */
+ if (*arg == '~' && (flags & SSHCONF_USERCONF) == 0)
+ fatal("%.200s line %d: bad include path %s.",
+ filename, linenum, arg);
+ if (*arg != '/' && *arg != '~') {
+ xasprintf(&arg2, "%s/%s",
+ (flags & SSHCONF_USERCONF) ?
+ "~/" _PATH_SSH_USER_DIR : SSHDIR, arg);
+ } else
+ arg2 = xstrdup(arg);
+ memset(&gl, 0, sizeof(gl));
+ r = glob(arg2, GLOB_TILDE, NULL, &gl);
+ if (r == GLOB_NOMATCH) {
+ debug("%.200s line %d: include %s matched no "
+ "files",filename, linenum, arg2);
+ continue;
+ } else if (r != 0 || gl.gl_pathc < 0)
+ fatal("%.200s line %d: glob failed for %s.",
+ filename, linenum, arg2);
+ free(arg2);
+ oactive = *activep;
+ for (i = 0; i < (u_int)gl.gl_pathc; i++) {
+ debug3("%.200s line %d: Including file %s "
+ "depth %d%s", filename, linenum,
+ gl.gl_pathv[i], depth,
+ oactive ? "" : " (parse only)");
+ r = read_config_file_depth(gl.gl_pathv[i],
+ pw, host, original_host, options,
+ flags | SSHCONF_CHECKPERM |
+ (oactive ? 0 : SSHCONF_NEVERMATCH),
+ activep, depth + 1);
+ /*
+ * don't let Match in includes clobber the
+ * containing file's Match state.
+ */
+ *activep = oactive;
+ if (r != 1)
+ value = -1;
+ }
+ globfree(&gl);
+ }
+ if (value != 0)
+ return value;
+ break;
+
case oIPQoS:
arg = strdelim(&s);
if ((value = parse_ipqos(arg)) == -1)
@@ -1565,22 +1643,35 @@ parse_keytypes:
return 0;
}
-
/*
* Reads the config file and modifies the options accordingly. Options
* should already be initialized before this call. This never returns if
* there is an error. If the file does not exist, this returns 0.
*/
-
int
read_config_file(const char *filename, struct passwd *pw, const char *host,
const char *original_host, Options *options, int flags)
{
+ int active = 1;
+
+ return read_config_file_depth(filename, pw, host, original_host,
+ options, flags, &active, 0);
+}
+
+#define READCONF_MAX_DEPTH 16
+static int
+read_config_file_depth(const char *filename, struct passwd *pw,
+ const char *host, const char *original_host, Options *options,
+ int flags, int *activep, int depth)
+{
FILE *f;
char line[1024];
- int active, linenum;
+ int linenum;
int bad_options = 0;
+ if (depth < 0 || depth > READCONF_MAX_DEPTH)
+ fatal("Too many recursive configuration includes");
+
if ((f = fopen(filename, "r")) == NULL)
return 0;
@@ -1600,13 +1691,12 @@ read_config_file(const char *filename, struct passwd *pw, const char *host,
* Mark that we are now processing the options. This flag is turned
* on/off by Host specifications.
*/
- active = 1;
linenum = 0;
while (fgets(line, sizeof(line), f)) {
/* Update line number counter. */
linenum++;
- if (process_config_line(options, pw, host, original_host,
- line, filename, linenum, &active, flags) != 0)
+ if (process_config_line_depth(options, pw, host, original_host,
+ line, filename, linenum, activep, flags, depth) != 0)
bad_options++;
}
fclose(f);
diff --git a/usr.bin/ssh/readconf.h b/usr.bin/ssh/readconf.h
index c84d068bd0c..5f44510665f 100644
--- a/usr.bin/ssh/readconf.h
+++ b/usr.bin/ssh/readconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.113 2016/01/14 16:17:40 markus Exp $ */
+/* $OpenBSD: readconf.h,v 1.114 2016/04/15 00:30:19 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -179,6 +179,7 @@ typedef struct {
#define SSHCONF_CHECKPERM 1 /* check permissions on config file */
#define SSHCONF_USERCONF 2 /* user provided config file not system */
#define SSHCONF_POSTCANON 4 /* After hostname canonicalisation */
+#define SSHCONF_NEVERMATCH 8 /* Match/Host never matches; internal only */
#define SSH_UPDATE_HOSTKEYS_NO 0
#define SSH_UPDATE_HOSTKEYS_YES 1
diff --git a/usr.bin/ssh/ssh.1 b/usr.bin/ssh/ssh.1
index cc5334338fd..85309ecc4f1 100644
--- a/usr.bin/ssh/ssh.1
+++ b/usr.bin/ssh/ssh.1
@@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $OpenBSD: ssh.1,v 1.369 2016/02/17 07:38:19 jmc Exp $
-.Dd $Mdocdate: February 17 2016 $
+.\" $OpenBSD: ssh.1,v 1.370 2016/04/15 00:30:19 djm Exp $
+.Dd $Mdocdate: April 15 2016 $
.Dt SSH 1
.Os
.Sh NAME
@@ -503,6 +503,7 @@ For full details of the options listed below, and their possible values, see
.It HostName
.It IdentityFile
.It IdentitiesOnly
+.It Include
.It IPQoS
.It KbdInteractiveAuthentication
.It KbdInteractiveDevices
diff --git a/usr.bin/ssh/ssh_config.5 b/usr.bin/ssh/ssh_config.5
index caf13a62d09..880f1104969 100644
--- a/usr.bin/ssh/ssh_config.5
+++ b/usr.bin/ssh/ssh_config.5
@@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $OpenBSD: ssh_config.5,v 1.228 2016/02/20 23:01:46 sobrado Exp $
-.Dd $Mdocdate: February 20 2016 $
+.\" $OpenBSD: ssh_config.5,v 1.229 2016/04/15 00:30:19 djm Exp $
+.Dd $Mdocdate: April 15 2016 $
.Dt SSH_CONFIG 5
.Os
.Sh NAME
@@ -1019,6 +1019,25 @@ It is recommended that
.Cm IgnoreUnknown
be listed early in the configuration file as it will not be applied
to unknown options that appear before it.
+.It Cm Include
+Include the specified configuration file(s).
+Multiple path names may be specified and each pathname may contain
+.Xr glob 3
+wildcards and, for user configurations, shell-like
+.Dq ~
+references to user home directories.
+Files without absolute paths are assumed to be in
+.Pa ~/.ssh
+if included in a user configurations file or
+.Pa /etc/ssh
+if included from the system configuration file.
+.Cm Include
+directive may appear inside a
+.Cm Match
+or
+.Cm Host
+block
+to perform conditional inclusion.
.It Cm IPQoS
Specifies the IPv4 type-of-service or DSCP class for connections.
Accepted values are