diff options
-rw-r--r-- | usr.bin/ssh/readconf.c | 112 | ||||
-rw-r--r-- | usr.bin/ssh/readconf.h | 3 | ||||
-rw-r--r-- | usr.bin/ssh/ssh.1 | 5 | ||||
-rw-r--r-- | usr.bin/ssh/ssh_config.5 | 23 |
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 |