зеркало из https://github.com/Azure/sonic-openssh.git
- djm@cvs.openbsd.org 2013/10/14 22:22:05
[readconf.c readconf.h ssh-keysign.c ssh.c ssh_config.5] add a "Match" keyword to ssh_config that allows matching on hostname, user and result of arbitrary commands. "nice work" markus@
This commit is contained in:
Родитель
71df752de2
Коммит
194fd904d8
|
@ -33,6 +33,10 @@
|
|||
[session.c session.h]
|
||||
Add logging of session starts in a useful format; ok markus@ feedback and
|
||||
ok dtucker@
|
||||
- djm@cvs.openbsd.org 2013/10/14 22:22:05
|
||||
[readconf.c readconf.h ssh-keysign.c ssh.c ssh_config.5]
|
||||
add a "Match" keyword to ssh_config that allows matching on hostname,
|
||||
user and result of arbitrary commands. "nice work" markus@
|
||||
|
||||
20131010
|
||||
- (dtucker) OpenBSD CVS Sync
|
||||
|
|
227
readconf.c
227
readconf.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: readconf.c,v 1.205 2013/08/20 00:11:37 djm Exp $ */
|
||||
/* $OpenBSD: readconf.c,v 1.206 2013/10/14 22:22:02 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -17,6 +17,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in_systm.h>
|
||||
|
@ -24,7 +25,10 @@
|
|||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <paths.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
@ -47,6 +51,7 @@
|
|||
#include "buffer.h"
|
||||
#include "kex.h"
|
||||
#include "mac.h"
|
||||
#include "uidswap.h"
|
||||
|
||||
/* Format of the configuration file:
|
||||
|
||||
|
@ -115,12 +120,13 @@
|
|||
|
||||
typedef enum {
|
||||
oBadOption,
|
||||
oHost, oMatch,
|
||||
oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout,
|
||||
oGatewayPorts, oExitOnForwardFailure,
|
||||
oPasswordAuthentication, oRSAAuthentication,
|
||||
oChallengeResponseAuthentication, oXAuthLocation,
|
||||
oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
|
||||
oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
|
||||
oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
|
||||
oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
|
||||
oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
|
||||
oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts,
|
||||
|
@ -194,6 +200,7 @@ static struct {
|
|||
{ "localforward", oLocalForward },
|
||||
{ "user", oUser },
|
||||
{ "host", oHost },
|
||||
{ "match", oMatch },
|
||||
{ "escapechar", oEscapeChar },
|
||||
{ "globalknownhostsfile", oGlobalKnownHostsFile },
|
||||
{ "globalknownhostsfile2", oDeprecated },
|
||||
|
@ -349,10 +356,188 @@ add_identity_file(Options *options, const char *dir, const char *filename,
|
|||
options->identity_files[options->num_identity_files++] = path;
|
||||
}
|
||||
|
||||
int
|
||||
default_ssh_port(void)
|
||||
{
|
||||
static int port;
|
||||
struct servent *sp;
|
||||
|
||||
if (port == 0) {
|
||||
sp = getservbyname(SSH_SERVICE_NAME, "tcp");
|
||||
port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute a command in a shell.
|
||||
* Return its exit status or -1 on abnormal exit.
|
||||
*/
|
||||
static int
|
||||
execute_in_shell(const char *cmd)
|
||||
{
|
||||
char *shell, *command_string;
|
||||
pid_t pid;
|
||||
int devnull, status;
|
||||
extern uid_t original_real_uid;
|
||||
|
||||
if ((shell = getenv("SHELL")) == NULL)
|
||||
shell = _PATH_BSHELL;
|
||||
|
||||
/*
|
||||
* Use "exec" to avoid "sh -c" processes on some platforms
|
||||
* (e.g. Solaris)
|
||||
*/
|
||||
xasprintf(&command_string, "exec %s", cmd);
|
||||
|
||||
/* Need this to redirect subprocess stdin/out */
|
||||
if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1)
|
||||
fatal("open(/dev/null): %s", strerror(errno));
|
||||
|
||||
debug("Executing command: '%.500s'", cmd);
|
||||
|
||||
/* Fork and execute the command. */
|
||||
if ((pid = fork()) == 0) {
|
||||
char *argv[4];
|
||||
|
||||
/* Child. Permanently give up superuser privileges. */
|
||||
permanently_drop_suid(original_real_uid);
|
||||
|
||||
/* Redirect child stdin and stdout. Leave stderr */
|
||||
if (dup2(devnull, STDIN_FILENO) == -1)
|
||||
fatal("dup2: %s", strerror(errno));
|
||||
if (dup2(devnull, STDOUT_FILENO) == -1)
|
||||
fatal("dup2: %s", strerror(errno));
|
||||
if (devnull > STDERR_FILENO)
|
||||
close(devnull);
|
||||
closefrom(STDERR_FILENO + 1);
|
||||
|
||||
argv[0] = shell;
|
||||
argv[1] = "-c";
|
||||
argv[2] = command_string;
|
||||
argv[3] = NULL;
|
||||
|
||||
execv(argv[0], argv);
|
||||
error("Unable to execute '%.100s': %s", cmd, strerror(errno));
|
||||
/* Die with signal to make this error apparent to parent. */
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
kill(getpid(), SIGTERM);
|
||||
_exit(1);
|
||||
}
|
||||
/* Parent. */
|
||||
if (pid < 0)
|
||||
fatal("%s: fork: %.100s", __func__, strerror(errno));
|
||||
|
||||
close(devnull);
|
||||
free(command_string);
|
||||
|
||||
while (waitpid(pid, &status, 0) == -1) {
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
fatal("%s: waitpid: %s", __func__, strerror(errno));
|
||||
}
|
||||
if (!WIFEXITED(status)) {
|
||||
error("command '%.100s' exited abnormally", cmd);
|
||||
return -1;
|
||||
}
|
||||
debug3("command returned status %d", WEXITSTATUS(status));
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse and execute a Match directive.
|
||||
*/
|
||||
static int
|
||||
match_cfg_line(Options *options, char **condition, struct passwd *pw,
|
||||
const char *host_arg, const char *filename, int linenum)
|
||||
{
|
||||
char *arg, *attrib, *cmd, *cp = *condition;
|
||||
const char *ruser, *host;
|
||||
int r, port, result = 1;
|
||||
size_t len;
|
||||
char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
|
||||
|
||||
/*
|
||||
* Configuration is likely to be incomplete at this point so we
|
||||
* must be prepared to use default values.
|
||||
*/
|
||||
port = options->port <= 0 ? default_ssh_port() : options->port;
|
||||
ruser = options->user == NULL ? pw->pw_name : options->user;
|
||||
host = options->hostname == NULL ? host_arg : options->hostname;
|
||||
|
||||
debug3("checking match for '%s' host %s", cp, host);
|
||||
while ((attrib = strdelim(&cp)) && *attrib != '\0') {
|
||||
if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
|
||||
error("Missing Match criteria for %s", attrib);
|
||||
return -1;
|
||||
}
|
||||
len = strlen(arg);
|
||||
if (strcasecmp(attrib, "host") == 0) {
|
||||
if (match_hostname(host, arg, len) != 1)
|
||||
result = 0;
|
||||
else
|
||||
debug("%.200s line %d: matched 'Host %.100s' ",
|
||||
filename, linenum, host);
|
||||
} else if (strcasecmp(attrib, "originalhost") == 0) {
|
||||
if (match_hostname(host_arg, arg, len) != 1)
|
||||
result = 0;
|
||||
else
|
||||
debug("%.200s line %d: matched "
|
||||
"'OriginalHost %.100s' ",
|
||||
filename, linenum, host_arg);
|
||||
} else if (strcasecmp(attrib, "user") == 0) {
|
||||
if (match_pattern_list(ruser, arg, len, 0) != 1)
|
||||
result = 0;
|
||||
else
|
||||
debug("%.200s line %d: matched 'User %.100s' ",
|
||||
filename, linenum, ruser);
|
||||
} else if (strcasecmp(attrib, "localuser") == 0) {
|
||||
if (match_pattern_list(pw->pw_name, arg, len, 0) != 1)
|
||||
result = 0;
|
||||
else
|
||||
debug("%.200s line %d: matched "
|
||||
"'LocalUser %.100s' ",
|
||||
filename, linenum, pw->pw_name);
|
||||
} else if (strcasecmp(attrib, "command") == 0) {
|
||||
if (gethostname(thishost, sizeof(thishost)) == -1)
|
||||
fatal("gethostname: %s", strerror(errno));
|
||||
strlcpy(shorthost, thishost, sizeof(shorthost));
|
||||
shorthost[strcspn(thishost, ".")] = '\0';
|
||||
snprintf(portstr, sizeof(portstr), "%d", port);
|
||||
|
||||
cmd = percent_expand(arg,
|
||||
"L", shorthost,
|
||||
"d", pw->pw_dir,
|
||||
"h", host,
|
||||
"l", thishost,
|
||||
"n", host_arg,
|
||||
"p", portstr,
|
||||
"r", ruser,
|
||||
"u", pw->pw_name,
|
||||
(char *)NULL);
|
||||
r = execute_in_shell(cmd);
|
||||
if (r == -1) {
|
||||
fatal("%.200s line %d: match command '%.100s' "
|
||||
"error", filename, linenum, cmd);
|
||||
} else if (r == 0) {
|
||||
debug("%.200s line %d: matched "
|
||||
"'Command \"%.100s\"' ",
|
||||
filename, linenum, cmd);
|
||||
} else
|
||||
result = 0;
|
||||
free(cmd);
|
||||
} else {
|
||||
error("Unsupported Match attribute %s", attrib);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
debug3("match %sfound", result ? "" : "not ");
|
||||
*condition = cp;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of the token pointed to by cp or oBadOption.
|
||||
*/
|
||||
|
||||
static OpCodes
|
||||
parse_token(const char *cp, const char *filename, int linenum,
|
||||
const char *ignored_unknown)
|
||||
|
@ -375,21 +560,24 @@ parse_token(const char *cp, const char *filename, int linenum,
|
|||
* only sets those values that have not already been set.
|
||||
*/
|
||||
#define WHITESPACE " \t\r\n"
|
||||
|
||||
int
|
||||
process_config_line(Options *options, const char *host,
|
||||
char *line, const char *filename, int linenum,
|
||||
int *activep, int userconfig)
|
||||
process_config_line(Options *options, struct passwd *pw, const char *host,
|
||||
char *line, const char *filename, int linenum, int *activep, int userconfig)
|
||||
{
|
||||
char *s, **charptr, *endofnumber, *keyword, *arg, *arg2;
|
||||
char **cpptr, fwdarg[256];
|
||||
u_int i, *uintptr, max_entries = 0;
|
||||
int negated, opcode, *intptr, value, value2;
|
||||
int negated, opcode, *intptr, value, value2, cmdline = 0;
|
||||
LogLevel *log_level_ptr;
|
||||
long long val64;
|
||||
size_t len;
|
||||
Forward fwd;
|
||||
|
||||
if (activep == NULL) { /* We are processing a command line directive */
|
||||
cmdline = 1;
|
||||
activep = &cmdline;
|
||||
}
|
||||
|
||||
/* Strip trailing whitespace */
|
||||
for (len = strlen(line) - 1; len > 0; len--) {
|
||||
if (strchr(WHITESPACE, line[len]) == NULL)
|
||||
|
@ -828,6 +1016,9 @@ parse_int:
|
|||
goto parse_flag;
|
||||
|
||||
case oHost:
|
||||
if (cmdline)
|
||||
fatal("Host directive not supported as a command-line "
|
||||
"option");
|
||||
*activep = 0;
|
||||
arg2 = NULL;
|
||||
while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
|
||||
|
@ -854,6 +1045,18 @@ parse_int:
|
|||
/* Avoid garbage check below, as strdelim is done. */
|
||||
return 0;
|
||||
|
||||
case oMatch:
|
||||
if (cmdline)
|
||||
fatal("Host directive not supported as a command-line "
|
||||
"option");
|
||||
value = match_cfg_line(options, &s, pw, host,
|
||||
filename, linenum);
|
||||
if (value < 0)
|
||||
fatal("%.200s line %d: Bad Match condition", filename,
|
||||
linenum);
|
||||
*activep = value;
|
||||
break;
|
||||
|
||||
case oEscapeChar:
|
||||
intptr = &options->escape_char;
|
||||
arg = strdelim(&s);
|
||||
|
@ -1107,8 +1310,8 @@ parse_int:
|
|||
*/
|
||||
|
||||
int
|
||||
read_config_file(const char *filename, const char *host, Options *options,
|
||||
int flags)
|
||||
read_config_file(const char *filename, struct passwd *pw, const char *host,
|
||||
Options *options, int flags)
|
||||
{
|
||||
FILE *f;
|
||||
char line[1024];
|
||||
|
@ -1139,8 +1342,8 @@ read_config_file(const char *filename, const char *host, Options *options,
|
|||
while (fgets(line, sizeof(line), f)) {
|
||||
/* Update line number counter. */
|
||||
linenum++;
|
||||
if (process_config_line(options, host, line, filename, linenum,
|
||||
&active, flags & SSHCONF_USERCONF) != 0)
|
||||
if (process_config_line(options, pw, host, line, filename,
|
||||
linenum, &active, flags & SSHCONF_USERCONF) != 0)
|
||||
bad_options++;
|
||||
}
|
||||
fclose(f);
|
||||
|
|
12
readconf.h
12
readconf.h
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: readconf.h,v 1.96 2013/08/20 00:11:38 djm Exp $ */
|
||||
/* $OpenBSD: readconf.h,v 1.97 2013/10/14 22:22:03 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
|
@ -159,12 +159,12 @@ typedef struct {
|
|||
|
||||
void initialize_options(Options *);
|
||||
void fill_default_options(Options *);
|
||||
int read_config_file(const char *, const char *, Options *, int);
|
||||
int process_config_line(Options *, struct passwd *, const char *, char *,
|
||||
const char *, int, int *, int);
|
||||
int read_config_file(const char *, struct passwd *, const char *,
|
||||
Options *, int);
|
||||
int parse_forward(Forward *, const char *, int, int);
|
||||
|
||||
int
|
||||
process_config_line(Options *, const char *, char *, const char *, int, int *,
|
||||
int);
|
||||
int default_ssh_port(void);
|
||||
|
||||
void add_local_forward(Options *, const Forward *);
|
||||
void add_remote_forward(Options *, const Forward *);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh-keysign.c,v 1.37 2013/05/17 00:13:14 djm Exp $ */
|
||||
/* $OpenBSD: ssh-keysign.c,v 1.38 2013/10/14 22:22:04 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2002 Markus Friedl. All rights reserved.
|
||||
*
|
||||
|
@ -187,7 +187,7 @@ main(int argc, char **argv)
|
|||
|
||||
/* verify that ssh-keysign is enabled by the admin */
|
||||
initialize_options(&options);
|
||||
(void)read_config_file(_PATH_HOST_CONFIG_FILE, "", &options, 0);
|
||||
(void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", &options, 0);
|
||||
fill_default_options(&options);
|
||||
if (options.enable_ssh_keysign != 1)
|
||||
fatal("ssh-keysign not enabled in %s",
|
||||
|
|
22
ssh.c
22
ssh.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh.c,v 1.381 2013/07/25 00:29:10 djm Exp $ */
|
||||
/* $OpenBSD: ssh.c,v 1.382 2013/10/14 22:22:04 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -242,7 +242,7 @@ main(int ac, char **av)
|
|||
char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
|
||||
struct stat st;
|
||||
struct passwd *pw;
|
||||
int dummy, timeout_ms;
|
||||
int timeout_ms;
|
||||
extern int optind, optreset;
|
||||
extern char *optarg;
|
||||
|
||||
|
@ -595,10 +595,9 @@ main(int ac, char **av)
|
|||
options.request_tty = REQUEST_TTY_NO;
|
||||
break;
|
||||
case 'o':
|
||||
dummy = 1;
|
||||
line = xstrdup(optarg);
|
||||
if (process_config_line(&options, host ? host : "",
|
||||
line, "command-line", 0, &dummy, SSHCONF_USERCONF)
|
||||
if (process_config_line(&options, pw, host ? host : "",
|
||||
line, "command-line", 0, NULL, SSHCONF_USERCONF)
|
||||
!= 0)
|
||||
exit(255);
|
||||
free(line);
|
||||
|
@ -703,18 +702,19 @@ main(int ac, char **av)
|
|||
*/
|
||||
if (config != NULL) {
|
||||
if (strcasecmp(config, "none") != 0 &&
|
||||
!read_config_file(config, host, &options, SSHCONF_USERCONF))
|
||||
!read_config_file(config, pw, host, &options,
|
||||
SSHCONF_USERCONF))
|
||||
fatal("Can't open user config file %.100s: "
|
||||
"%.100s", config, strerror(errno));
|
||||
} else {
|
||||
r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir,
|
||||
_PATH_SSH_USER_CONFFILE);
|
||||
if (r > 0 && (size_t)r < sizeof(buf))
|
||||
(void)read_config_file(buf, host, &options,
|
||||
(void)read_config_file(buf, pw, host, &options,
|
||||
SSHCONF_CHECKPERM|SSHCONF_USERCONF);
|
||||
|
||||
/* Read systemwide configuration file after user config. */
|
||||
(void)read_config_file(_PATH_HOST_CONFIG_FILE, host,
|
||||
(void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host,
|
||||
&options, 0);
|
||||
}
|
||||
|
||||
|
@ -752,10 +752,8 @@ main(int ac, char **av)
|
|||
options.user = xstrdup(pw->pw_name);
|
||||
|
||||
/* Get default port if port has not been set. */
|
||||
if (options.port == 0) {
|
||||
sp = getservbyname(SSH_SERVICE_NAME, "tcp");
|
||||
options.port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
|
||||
}
|
||||
if (options.port == 0)
|
||||
options.port = default_ssh_port();
|
||||
|
||||
/* preserve host name given on command line for %n expansion */
|
||||
host_arg = host;
|
||||
|
|
52
ssh_config.5
52
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.168 2013/08/20 06:56:07 jmc Exp $
|
||||
.Dd $Mdocdate: August 20 2013 $
|
||||
.\" $OpenBSD: ssh_config.5,v 1.169 2013/10/14 22:22:05 djm Exp $
|
||||
.Dd $Mdocdate: October 14 2013 $
|
||||
.Dt SSH_CONFIG 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -100,6 +100,8 @@ keywords are case-insensitive and arguments are case-sensitive):
|
|||
.It Cm Host
|
||||
Restricts the following declarations (up to the next
|
||||
.Cm Host
|
||||
or
|
||||
.Cm Match
|
||||
keyword) to be only for those hosts that match one of the patterns
|
||||
given after the keyword.
|
||||
If more than one pattern is provided, they should be separated by whitespace.
|
||||
|
@ -124,6 +126,52 @@ matches.
|
|||
See
|
||||
.Sx PATTERNS
|
||||
for more information on patterns.
|
||||
.It Cm Match
|
||||
Restricts the following declarations (up to the next
|
||||
.Cm Host
|
||||
or
|
||||
.Cm Match
|
||||
keyword) to be used only when the conditions following the
|
||||
.Cm Match
|
||||
keyword are satisfied.
|
||||
Match conditions are specified using one or more keyword/criteria pairs.
|
||||
The available keywords are:
|
||||
.Cm command ,
|
||||
.Cm host ,
|
||||
.Cm originalhost ,
|
||||
.Cm user ,
|
||||
and
|
||||
.Cm localuser .
|
||||
.Pp
|
||||
The criteria for the
|
||||
.Cm command
|
||||
keyword is a path to a command that is executed.
|
||||
If the command returns a zero exit status then the condition is considered true.
|
||||
Commands containing whitespace characters must be quoted.
|
||||
.Pp
|
||||
The other keywords' criteria must be single entries or comma-separated
|
||||
lists and may use the wildcard and negation operators described in the
|
||||
.Sx PATTERNS
|
||||
section.
|
||||
The criteria for the
|
||||
.Cm host
|
||||
keyword are matched against the target hostname, after any substitution
|
||||
by the
|
||||
.Cm Hostname
|
||||
option.
|
||||
The
|
||||
.Cm originalhost
|
||||
keyword matches against the hostname as it was specified on the command-line.
|
||||
The
|
||||
.Cm user
|
||||
keyword matches against the target username on the remote host.
|
||||
The
|
||||
.Cm localuser
|
||||
keyword matches against the name of the local user running
|
||||
.Xr ssh 1
|
||||
(this keyword may be useful in system-wide
|
||||
.Nm
|
||||
files).
|
||||
.It Cm AddressFamily
|
||||
Specifies which address family to use when connecting.
|
||||
Valid arguments are
|
||||
|
|
Загрузка…
Ссылка в новой задаче