Skip to content

Commit

Permalink
upstream: Add a sshd_config "Include" directive to allow inclusion
Browse files Browse the repository at this point in the history
of files. This has sensible semantics wrt Match blocks and accepts glob(3)
patterns to specify the included files. Based on patch by Jakub Jelen in
bz2468; feedback and ok markus@

OpenBSD-Commit-ID: 36ed0e845b872e33f03355b936a4fff02d5794ff
  • Loading branch information
djmdjm committed Jan 31, 2020
1 parent ba261a1 commit c2bd7f7
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 44 deletions.
5 changes: 3 additions & 2 deletions auth.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $OpenBSD: auth.c,v 1.145 2020/01/23 07:10:22 dtucker Exp $ */
/* $OpenBSD: auth.c,v 1.146 2020/01/31 22:42:45 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
Expand Down Expand Up @@ -79,6 +79,7 @@

/* import */
extern ServerOptions options;
extern struct include_list includes;
extern int use_privsep;
extern struct sshbuf *loginmsg;
extern struct passwd *privsep_pw;
Expand Down Expand Up @@ -571,7 +572,7 @@ getpwnamallow(struct ssh *ssh, const char *user)

ci = get_connection_info(ssh, 1, options.use_dns);
ci->user = user;
parse_server_match_config(&options, ci);
parse_server_match_config(&options, &includes, ci);
log_change_level(options.log_level);
process_permitopen(ssh, &options);

Expand Down
167 changes: 148 additions & 19 deletions servconf.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

/* $OpenBSD: servconf.c,v 1.359 2020/01/23 10:24:29 dtucker Exp $ */
/* $OpenBSD: servconf.c,v 1.360 2020/01/31 22:42:45 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
Expand Down Expand Up @@ -40,6 +40,11 @@
#ifdef HAVE_UTIL_H
#include <util.h>
#endif
#ifdef USE_SYSTEM_GLOB
# include <glob.h>
#else
# include "openbsd-compat/glob.h"
#endif

#include "openbsd-compat/sys-queue.h"
#include "xmalloc.h"
Expand Down Expand Up @@ -69,6 +74,9 @@ static void add_listen_addr(ServerOptions *, const char *,
const char *, int);
static void add_one_listen_addr(ServerOptions *, const char *,
const char *, int);
void parse_server_config_depth(ServerOptions *options, const char *filename,
struct sshbuf *conf, struct include_list *includes,
struct connection_info *connectinfo, int flags, int *activep, int depth);

/* Use of privilege separation or not */
extern int use_privsep;
Expand Down Expand Up @@ -526,7 +534,7 @@ typedef enum {
sAcceptEnv, sSetEnv, sPermitTunnel,
sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
sUsePrivilegeSeparation, sAllowAgentForwarding,
sHostCertificate,
sHostCertificate, sInclude,
sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser,
sKexAlgorithms, sCASignatureAlgorithms, sIPQoS, sVersionAddendum,
Expand All @@ -538,9 +546,10 @@ typedef enum {
sDeprecated, sIgnore, sUnsupported
} ServerOpCodes;

#define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */
#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */
#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH)
#define SSHCFG_GLOBAL 0x01 /* allowed in main section of config */
#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */
#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH)
#define SSHCFG_NEVERMATCH 0x04 /* Match never matches; internal only */

/* Textual representation of the tokens. */
static struct {
Expand Down Expand Up @@ -669,6 +678,7 @@ static struct {
{ "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
{ "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL },
{ "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
{ "include", sInclude, SSHCFG_ALL },
{ "ipqos", sIPQoS, SSHCFG_ALL },
{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
{ "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
Expand Down Expand Up @@ -1240,13 +1250,14 @@ static const struct multistate multistate_tcpfwd[] = {
{ NULL, -1 }
};

int
process_server_config_line(ServerOptions *options, char *line,
static int
process_server_config_line_depth(ServerOptions *options, char *line,
const char *filename, int linenum, int *activep,
struct connection_info *connectinfo)
struct connection_info *connectinfo, int inc_flags, int depth,
struct include_list *includes)
{
char ch, *cp, ***chararrayptr, **charptr, *arg, *arg2, *p;
int cmdline = 0, *intptr, value, value2, n, port;
int cmdline = 0, *intptr, value, value2, n, port, oactive, r, found;
SyslogFacility *log_facility_ptr;
LogLevel *log_level_ptr;
ServerOpCodes opcode;
Expand All @@ -1255,6 +1266,8 @@ process_server_config_line(ServerOptions *options, char *line,
long long val64;
const struct multistate *multistate_ptr;
const char *errstr;
struct include_item *item;
glob_t gbuf;

/* Strip trailing whitespace. Allow \f (form feed) at EOL only */
if ((len = strlen(line)) == 0)
Expand All @@ -1281,7 +1294,7 @@ process_server_config_line(ServerOptions *options, char *line,
cmdline = 1;
activep = &cmdline;
}
if (*activep && opcode != sMatch)
if (*activep && opcode != sMatch && opcode != sInclude)
debug3("%s:%d setting %s %s", filename, linenum, arg, cp);
if (*activep == 0 && !(flags & SSHCFG_MATCH)) {
if (connectinfo == NULL) {
Expand Down Expand Up @@ -1954,6 +1967,96 @@ process_server_config_line(ServerOptions *options, char *line,
*intptr = value;
break;

case sInclude:
if (cmdline) {
fatal("Include directive not supported as a "
"command-line option");
}
value = 0;
while ((arg2 = strdelim(&cp)) != NULL && *arg2 != '\0') {
value++;
found = 0;
if (*arg2 != '/' && *arg2 != '~') {
xasprintf(&arg, "%s/%s", SSHDIR, arg);
} else
arg = xstrdup(arg2);

/*
* Don't let included files clobber the containing
* file's Match state.
*/
oactive = *activep;

/* consult cache of include files */
TAILQ_FOREACH(item, includes, entry) {
if (strcmp(item->selector, arg) != 0)
continue;
if (item->filename != NULL) {
parse_server_config_depth(options,
item->filename, item->contents,
includes, connectinfo,
(oactive ? 0 : SSHCFG_NEVERMATCH),
activep, depth + 1);
}
found = 1;
*activep = oactive;
}
if (found != 0) {
free(arg);
continue;
}

/* requested glob was not in cache */
debug2("%s line %d: new include %s",
filename, linenum, arg);
if ((r = glob(arg, 0, NULL, &gbuf)) != 0) {
if (r != GLOB_NOMATCH) {
fatal("%s line %d: include \"%s\" "
"glob failed", filename,
linenum, arg);
}
/*
* If no entry matched then record a
* placeholder to skip later glob calls.
*/
debug2("%s line %d: no match for %s",
filename, linenum, arg);
item = xcalloc(1, sizeof(*item));
item->selector = strdup(arg);
TAILQ_INSERT_TAIL(includes,
item, entry);
}
if (gbuf.gl_pathc > INT_MAX)
fatal("%s: too many glob results", __func__);
for (n = 0; n < (int)gbuf.gl_pathc; n++) {
debug2("%s line %d: including %s",
filename, linenum, gbuf.gl_pathv[n]);
item = xcalloc(1, sizeof(*item));
item->selector = strdup(arg);
item->filename = strdup(gbuf.gl_pathv[n]);
if ((item->contents = sshbuf_new()) == NULL) {
fatal("%s: sshbuf_new failed",
__func__);
}
load_server_config(item->filename,
item->contents);
parse_server_config_depth(options,
item->filename, item->contents,
includes, connectinfo,
(oactive ? 0 : SSHCFG_NEVERMATCH),
activep, depth + 1);
*activep = oactive;
TAILQ_INSERT_TAIL(includes, item, entry);
}
globfree(&gbuf);
free(arg);
}
if (value == 0) {
fatal("%s line %d: Include missing filename argument",
filename, linenum);
}
break;

case sMatch:
if (cmdline)
fatal("Match directive not supported as a command-line "
Expand All @@ -1962,7 +2065,7 @@ process_server_config_line(ServerOptions *options, char *line,
if (value < 0)
fatal("%s line %d: Bad Match condition", filename,
linenum);
*activep = value;
*activep = (inc_flags & SSHCFG_NEVERMATCH) ? 0 : value;
break;

case sPermitListen:
Expand Down Expand Up @@ -2256,6 +2359,16 @@ process_server_config_line(ServerOptions *options, char *line,
return 0;
}

int
process_server_config_line(ServerOptions *options, char *line,
const char *filename, int linenum, int *activep,
struct connection_info *connectinfo, struct include_list *includes)
{
return process_server_config_line_depth(options, line, filename,
linenum, activep, connectinfo, 0, 0, includes);
}


/* Reads the server configuration file. */

void
Expand Down Expand Up @@ -2294,12 +2407,13 @@ load_server_config(const char *filename, struct sshbuf *conf)

void
parse_server_match_config(ServerOptions *options,
struct connection_info *connectinfo)
struct include_list *includes, struct connection_info *connectinfo)
{
ServerOptions mo;

initialize_server_options(&mo);
parse_server_config(&mo, "reprocess config", cfg, connectinfo);
parse_server_config(&mo, "reprocess config", cfg, includes,
connectinfo);
copy_set_server_options(options, &mo, 0);
}

Expand Down Expand Up @@ -2443,22 +2557,27 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
#undef M_CP_STROPT
#undef M_CP_STRARRAYOPT

#define SERVCONF_MAX_DEPTH 16
void
parse_server_config(ServerOptions *options, const char *filename,
struct sshbuf *conf, struct connection_info *connectinfo)
parse_server_config_depth(ServerOptions *options, const char *filename,
struct sshbuf *conf, struct include_list *includes,
struct connection_info *connectinfo, int flags, int *activep, int depth)
{
int active, linenum, bad_options = 0;
int linenum, bad_options = 0;
char *cp, *obuf, *cbuf;

if (depth < 0 || depth > SERVCONF_MAX_DEPTH)
fatal("Too many recursive configuration includes");

debug2("%s: config %s len %zu", __func__, filename, sshbuf_len(conf));

if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL)
fatal("%s: sshbuf_dup_string failed", __func__);
active = connectinfo ? 0 : 1;
linenum = 1;
while ((cp = strsep(&cbuf, "\n")) != NULL) {
if (process_server_config_line(options, cp, filename,
linenum++, &active, connectinfo) != 0)
if (process_server_config_line_depth(options, cp,
filename, linenum++, activep, connectinfo, flags,
depth, includes) != 0)
bad_options++;
}
free(obuf);
Expand All @@ -2468,6 +2587,16 @@ parse_server_config(ServerOptions *options, const char *filename,
process_queued_listen_addrs(options);
}

void
parse_server_config(ServerOptions *options, const char *filename,
struct sshbuf *conf, struct include_list *includes,
struct connection_info *connectinfo)
{
int active = connectinfo ? 0 : 1;
parse_server_config_depth(options, filename, conf, includes,
connectinfo, 0, &active, 0);
}

static const char *
fmt_multistate_int(int val, const struct multistate *m)
{
Expand Down
20 changes: 16 additions & 4 deletions servconf.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $OpenBSD: servconf.h,v 1.142 2019/12/15 18:57:30 djm Exp $ */
/* $OpenBSD: servconf.h,v 1.143 2020/01/31 22:42:45 djm Exp $ */

/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
Expand All @@ -16,6 +16,8 @@
#ifndef SERVCONF_H
#define SERVCONF_H

#include <sys/queue.h>

#define MAX_PORTS 256 /* Max # ports. */

#define MAX_SUBSYSTEMS 256 /* Max # subsystems. */
Expand Down Expand Up @@ -230,6 +232,15 @@ struct connection_info {
* unspecified */
};

/* List of included files for re-exec from the parsed configuration */
struct include_item {
char *selector;
char *filename;
struct sshbuf *contents;
TAILQ_ENTRY(include_item) entry;
};
TAILQ_HEAD(include_list, include_item);


/*
* These are string config options that must be copied between the
Expand Down Expand Up @@ -269,12 +280,13 @@ struct connection_info *get_connection_info(struct ssh *, int, int);
void initialize_server_options(ServerOptions *);
void fill_default_server_options(ServerOptions *);
int process_server_config_line(ServerOptions *, char *, const char *, int,
int *, struct connection_info *);
int *, struct connection_info *, struct include_list *includes);
void process_permitopen(struct ssh *ssh, ServerOptions *options);
void load_server_config(const char *, struct sshbuf *);
void parse_server_config(ServerOptions *, const char *, struct sshbuf *,
struct connection_info *);
void parse_server_match_config(ServerOptions *, struct connection_info *);
struct include_list *includes, struct connection_info *);
void parse_server_match_config(ServerOptions *,
struct include_list *includes, struct connection_info *);
int parse_server_match_testspec(struct connection_info *, char *);
int server_match_spec_complete(struct connection_info *);
void copy_set_server_options(ServerOptions *, ServerOptions *, int);
Expand Down
Loading

0 comments on commit c2bd7f7

Please sign in to comment.