summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--regress/usr.bin/ssh/addrmatch.sh50
-rw-r--r--usr.bin/ssh/auth.c7
-rw-r--r--usr.bin/ssh/servconf.c163
-rw-r--r--usr.bin/ssh/servconf.h22
-rw-r--r--usr.bin/ssh/sshd.86
-rw-r--r--usr.bin/ssh/sshd.c39
-rw-r--r--usr.bin/ssh/sshd_config.56
7 files changed, 206 insertions, 87 deletions
diff --git a/regress/usr.bin/ssh/addrmatch.sh b/regress/usr.bin/ssh/addrmatch.sh
index f9d613baf9a..239ac0a9971 100644
--- a/regress/usr.bin/ssh/addrmatch.sh
+++ b/regress/usr.bin/ssh/addrmatch.sh
@@ -1,4 +1,4 @@
-# $OpenBSD: addrmatch.sh,v 1.3 2010/02/09 04:57:36 djm Exp $
+# $OpenBSD: addrmatch.sh,v 1.4 2012/05/13 01:42:32 dtucker Exp $
# Placed in the Public Domain.
tid="address match"
@@ -7,38 +7,48 @@ mv $OBJ/sshd_proxy $OBJ/sshd_proxy_bak
run_trial()
{
- user="$1"; addr="$2"; host="$3"; expected="$4"; descr="$5"
+ user="$1"; addr="$2"; host="$3"; laddr="$4"; lport="$5"
+ expected="$6"; descr="$7"
verbose "test $descr for $user $addr $host"
result=`${SSHD} -f $OBJ/sshd_proxy -T \
- -C user=${user},addr=${addr},host=${host} | \
- awk '/^passwordauthentication/ {print $2}'`
+ -C user=${user},addr=${addr},host=${host},laddr=${laddr},lport=${lport} | \
+ awk '/^forcecommand/ {print $2}'`
if [ "$result" != "$expected" ]; then
- fail "failed for $user $addr $host: expected $expected, got $result"
+ fail "failed '$descr' expected $expected got $result"
fi
}
cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy
cat >>$OBJ/sshd_proxy <<EOD
-PasswordAuthentication no
+ForceCommand nomatch
Match Address 192.168.0.0/16,!192.168.30.0/24,10.0.0.0/8,host.example.com
- PasswordAuthentication yes
+ ForceCommand match1
Match Address 1.1.1.1,::1,!::3,2000::/16
- PasswordAuthentication yes
+ ForceCommand match2
+Match LocalAddress 127.0.0.1,::1
+ ForceCommand match3
+Match LocalPort 5678
+ ForceCommand match4
EOD
-run_trial user 192.168.0.1 somehost yes "permit, first entry"
-run_trial user 192.168.30.1 somehost no "deny, negative match"
-run_trial user 19.0.0.1 somehost no "deny, no match"
-run_trial user 10.255.255.254 somehost yes "permit, list middle"
-run_trial user 192.168.30.1 192.168.0.1 no "deny, faked IP in hostname"
-run_trial user 1.1.1.1 somehost.example.com yes "permit, bare IP4 address"
-run_trial user ::1 somehost.example.com yes "permit, bare IP6 address"
-run_trial user ::2 somehost.exaple.com no "deny IPv6"
-run_trial user ::3 somehost no "deny IP6 negated"
-run_trial user ::4 somehost no "deny, IP6 no match"
-run_trial user 2000::1 somehost yes "permit, IP6 network"
-run_trial user 2001::1 somehost no "deny, IP6 network"
+run_trial user 192.168.0.1 somehost 1.2.3.4 1234 match1 "first entry"
+run_trial user 192.168.30.1 somehost 1.2.3.4 1234 nomatch "negative match"
+run_trial user 19.0.0.1 somehost 1.2.3.4 1234 nomatch "no match"
+run_trial user 10.255.255.254 somehost 1.2.3.4 1234 match1 "list middle"
+run_trial user 192.168.30.1 192.168.0.1 1.2.3.4 1234 nomatch "faked IP in hostname"
+run_trial user 1.1.1.1 somehost.example.com 1.2.3.4 1234 match2 "bare IP4 address"
+run_trial user 19.0.0.1 somehost 127.0.0.1 1234 match3 "localaddress"
+run_trial user 19.0.0.1 somehost 1.2.3.4 5678 match4 "localport"
+
+run_trial user ::1 somehost.example.com ::2 1234 match2 "bare IP6 address"
+run_trial user ::2 somehost.exaple.com ::2 1234 nomatch "deny IPv6"
+run_trial user ::3 somehost ::2 1234 nomatch "IP6 negated"
+run_trial user ::4 somehost ::2 1234 nomatch "IP6 no match"
+run_trial user 2000::1 somehost ::2 1234 match2 "IP6 network"
+run_trial user 2001::1 somehost ::2 1234 nomatch "IP6 network"
+run_trial user ::5 somehost ::1 1234 match3 "IP6 localaddress"
+run_trial user ::5 somehost ::2 5678 match4 "IP6 localport"
cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy
rm $OBJ/sshd_proxy_bak
diff --git a/usr.bin/ssh/auth.c b/usr.bin/ssh/auth.c
index a3920fab111..01c6a5dcf12 100644
--- a/usr.bin/ssh/auth.c
+++ b/usr.bin/ssh/auth.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.c,v 1.95 2012/04/11 13:17:54 djm Exp $ */
+/* $OpenBSD: auth.c,v 1.96 2012/05/13 01:42:32 dtucker Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@@ -451,9 +451,10 @@ getpwnamallow(const char *user)
extern login_cap_t *lc;
auth_session_t *as;
struct passwd *pw;
+ struct connection_info *ci = get_connection_info(1, options.use_dns);
- parse_server_match_config(&options, user,
- get_canonical_hostname(options.use_dns), get_remote_ipaddr());
+ ci->user = user;
+ parse_server_match_config(&options, ci);
pw = getpwnam(user);
if (pw == NULL) {
diff --git a/usr.bin/ssh/servconf.c b/usr.bin/ssh/servconf.c
index 2deda3eb659..3eac3ee8451 100644
--- a/usr.bin/ssh/servconf.c
+++ b/usr.bin/ssh/servconf.c
@@ -1,4 +1,5 @@
-/* $OpenBSD: servconf.c,v 1.225 2012/04/12 02:42:32 djm Exp $ */
+
+/* $OpenBSD: servconf.c,v 1.226 2012/05/13 01:42:32 dtucker Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -43,6 +44,8 @@
#include "match.h"
#include "channels.h"
#include "groupaccess.h"
+#include "canohost.h"
+#include "packet.h"
static void add_listen_addr(ServerOptions *, char *, int);
static void add_one_listen_addr(ServerOptions *, char *, int);
@@ -500,6 +503,20 @@ add_one_listen_addr(ServerOptions *options, char *addr, int port)
options->listen_addrs = aitop;
}
+struct connection_info *
+get_connection_info(int populate, int use_dns)
+{
+ static struct connection_info ci;
+
+ if (!populate)
+ return &ci;
+ ci.host = get_canonical_hostname(use_dns);
+ ci.address = get_remote_ipaddr();
+ ci.laddress = get_local_ipaddr(packet_get_connection_in());
+ ci.lport = get_local_port();
+ return &ci;
+}
+
/*
* The strategy for the Match blocks is that the config file is parsed twice.
*
@@ -561,20 +578,25 @@ out:
return result;
}
+/*
+ * All of the attributes on a single Match line are ANDed together, so we need to check every
+ * attribute and set the result to zero if any attribute does not match.
+ */
static int
-match_cfg_line(char **condition, int line, const char *user, const char *host,
- const char *address)
+match_cfg_line(char **condition, int line, struct connection_info *ci)
{
- int result = 1;
+ int result = 1, port;
char *arg, *attrib, *cp = *condition;
size_t len;
- if (user == NULL)
+ if (ci == NULL)
debug3("checking syntax for 'Match %s'", cp);
else
- debug3("checking match for '%s' user %s host %s addr %s", cp,
- user ? user : "(null)", host ? host : "(null)",
- address ? address : "(null)");
+ debug3("checking match for '%s' user %s host %s addr %s "
+ "laddr %s lport %d", cp, ci->user ? ci->user : "(null)",
+ ci->host ? ci->host : "(null)",
+ ci->address ? ci->address : "(null)",
+ ci->laddress ? ci->laddress : "(null)", ci->lport);
while ((attrib = strdelim(&cp)) && *attrib != '\0') {
if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
@@ -583,37 +605,45 @@ match_cfg_line(char **condition, int line, const char *user, const char *host,
}
len = strlen(arg);
if (strcasecmp(attrib, "user") == 0) {
- if (!user) {
+ if (ci == NULL || ci->user == NULL) {
result = 0;
continue;
}
- if (match_pattern_list(user, arg, len, 0) != 1)
+ if (match_pattern_list(ci->user, arg, len, 0) != 1)
result = 0;
else
debug("user %.100s matched 'User %.100s' at "
- "line %d", user, arg, line);
+ "line %d", ci->user, arg, line);
} else if (strcasecmp(attrib, "group") == 0) {
- switch (match_cfg_line_group(arg, line, user)) {
+ if (ci == NULL || ci->user == NULL) {
+ result = 0;
+ continue;
+ }
+ switch (match_cfg_line_group(arg, line, ci->user)) {
case -1:
return -1;
case 0:
result = 0;
}
} else if (strcasecmp(attrib, "host") == 0) {
- if (!host) {
+ if (ci == NULL || ci->host == NULL) {
result = 0;
continue;
}
- if (match_hostname(host, arg, len) != 1)
+ if (match_hostname(ci->host, arg, len) != 1)
result = 0;
else
debug("connection from %.100s matched 'Host "
- "%.100s' at line %d", host, arg, line);
+ "%.100s' at line %d", ci->host, arg, line);
} else if (strcasecmp(attrib, "address") == 0) {
- switch (addr_match_list(address, arg)) {
+ if (ci == NULL || ci->address == NULL) {
+ result = 0;
+ continue;
+ }
+ switch (addr_match_list(ci->address, arg)) {
case 1:
debug("connection from %.100s matched 'Address "
- "%.100s' at line %d", address, arg, line);
+ "%.100s' at line %d", ci->address, arg, line);
break;
case 0:
case -1:
@@ -622,12 +652,47 @@ match_cfg_line(char **condition, int line, const char *user, const char *host,
case -2:
return -1;
}
+ } else if (strcasecmp(attrib, "localaddress") == 0){
+ if (ci == NULL || ci->laddress == NULL) {
+ result = 0;
+ continue;
+ }
+ switch (addr_match_list(ci->laddress, arg)) {
+ case 1:
+ debug("connection from %.100s matched "
+ "'LocalAddress %.100s' at line %d",
+ ci->laddress, arg, line);
+ break;
+ case 0:
+ case -1:
+ result = 0;
+ break;
+ case -2:
+ return -1;
+ }
+ } else if (strcasecmp(attrib, "localport") == 0) {
+ if ((port = a2port(arg)) == -1) {
+ error("Invalid LocalPort '%s' on Match line",
+ arg);
+ return -1;
+ }
+ if (ci == NULL || ci->lport == 0) {
+ result = 0;
+ continue;
+ }
+ /* TODO support port lists */
+ if (port == ci->lport)
+ debug("connection from %.100s matched "
+ "'LocalPort %d' at line %d",
+ ci->laddress, port, line);
+ else
+ result = 0;
} else {
error("Unsupported Match attribute %s", attrib);
return -1;
}
}
- if (user != NULL)
+ if (ci != NULL)
debug3("match %sfound", result ? "" : "not ");
*condition = cp;
return result;
@@ -674,8 +739,8 @@ static const struct multistate multistate_privsep[] = {
int
process_server_config_line(ServerOptions *options, char *line,
- const char *filename, int linenum, int *activep, const char *user,
- const char *host, const char *address)
+ const char *filename, int linenum, int *activep,
+ struct connection_info *connectinfo)
{
char *cp, **charptr, *arg, *p;
int cmdline = 0, *intptr, value, value2, n;
@@ -706,7 +771,7 @@ process_server_config_line(ServerOptions *options, char *line,
if (*activep && opcode != sMatch)
debug3("%s:%d setting %s %s", filename, linenum, arg, cp);
if (*activep == 0 && !(flags & SSHCFG_MATCH)) {
- if (user == NULL) {
+ if (connectinfo == NULL) {
fatal("%s line %d: Directive '%s' is not allowed "
"within a Match block", filename, linenum, arg);
} else { /* this is a directive we have already processed */
@@ -1271,7 +1336,7 @@ process_server_config_line(ServerOptions *options, char *line,
if (cmdline)
fatal("Match directive not supported as a command-line "
"option");
- value = match_cfg_line(&cp, linenum, user, host, address);
+ value = match_cfg_line(&cp, linenum, connectinfo);
if (value < 0)
fatal("%s line %d: Bad Match condition", filename,
linenum);
@@ -1433,16 +1498,58 @@ load_server_config(const char *filename, Buffer *conf)
}
void
-parse_server_match_config(ServerOptions *options, const char *user,
- const char *host, const char *address)
+parse_server_match_config(ServerOptions *options,
+ struct connection_info *connectinfo)
{
ServerOptions mo;
initialize_server_options(&mo);
- parse_server_config(&mo, "reprocess config", &cfg, user, host, address);
+ parse_server_config(&mo, "reprocess config", &cfg, connectinfo);
copy_set_server_options(options, &mo, 0);
}
+int parse_server_match_testspec(struct connection_info *ci, char *spec)
+{
+ char *p;
+
+ while ((p = strsep(&spec, ",")) && *p != '\0') {
+ if (strncmp(p, "addr=", 5) == 0) {
+ ci->address = xstrdup(p + 5);
+ } else if (strncmp(p, "host=", 5) == 0) {
+ ci->host = xstrdup(p + 5);
+ } else if (strncmp(p, "user=", 5) == 0) {
+ ci->user = xstrdup(p + 5);
+ } else if (strncmp(p, "laddr=", 6) == 0) {
+ ci->laddress = xstrdup(p + 6);
+ } else if (strncmp(p, "lport=", 6) == 0) {
+ ci->lport = a2port(p + 6);
+ if (ci->lport == -1) {
+ fprintf(stderr, "Invalid port '%s' in test mode"
+ " specification %s\n", p+6, p);
+ return -1;
+ }
+ } else {
+ fprintf(stderr, "Invalid test mode specification %s\n",
+ p);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * returns 1 for a complete spec, 0 for partial spec and -1 for an
+ * empty spec.
+ */
+int server_match_spec_complete(struct connection_info *ci)
+{
+ if (ci->user && ci->host && ci->address)
+ return 1; /* complete */
+ if (!ci->user && !ci->host && !ci->address)
+ return -1; /* empty */
+ return 0; /* partial */
+}
+
/* Helper macros */
#define M_CP_INTOPT(n) do {\
if (src->n != -1) \
@@ -1516,7 +1623,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
void
parse_server_config(ServerOptions *options, const char *filename, Buffer *conf,
- const char *user, const char *host, const char *address)
+ struct connection_info *connectinfo)
{
int active, linenum, bad_options = 0;
char *cp, *obuf, *cbuf;
@@ -1524,11 +1631,11 @@ parse_server_config(ServerOptions *options, const char *filename, Buffer *conf,
debug2("%s: config %s len %d", __func__, filename, buffer_len(conf));
obuf = cbuf = xstrdup(buffer_ptr(conf));
- active = user ? 0 : 1;
+ active = connectinfo ? 0 : 1;
linenum = 1;
while ((cp = strsep(&cbuf, "\n")) != NULL) {
if (process_server_config_line(options, cp, filename,
- linenum++, &active, user, host, address) != 0)
+ linenum++, &active, connectinfo) != 0)
bad_options++;
}
xfree(obuf);
diff --git a/usr.bin/ssh/servconf.h b/usr.bin/ssh/servconf.h
index 7b394b8d6e8..a1c8a01180b 100644
--- a/usr.bin/ssh/servconf.h
+++ b/usr.bin/ssh/servconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.h,v 1.100 2012/04/12 02:42:32 djm Exp $ */
+/* $OpenBSD: servconf.h,v 1.101 2012/05/13 01:42:32 dtucker Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -168,6 +168,16 @@ typedef struct {
char *version_addendum; /* Appended to SSH banner */
} ServerOptions;
+/* Information about the incoming connection as used by Match */
+struct connection_info {
+ const char *user;
+ const char *host; /* possibly resolved hostname */
+ const char *address; /* remote address */
+ const char *laddress; /* local address */
+ int lport; /* local port */
+};
+
+
/*
* These are string config options that must be copied between the
* Match sub-config and the main config, and must be sent from the
@@ -182,15 +192,17 @@ typedef struct {
M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \
} while (0)
+struct connection_info *get_connection_info(int, int);
void initialize_server_options(ServerOptions *);
void fill_default_server_options(ServerOptions *);
int process_server_config_line(ServerOptions *, char *, const char *, int,
- int *, const char *, const char *, const char *);
+ int *, struct connection_info *);
void load_server_config(const char *, Buffer *);
void parse_server_config(ServerOptions *, const char *, Buffer *,
- const char *, const char *, const char *);
-void parse_server_match_config(ServerOptions *, const char *, const char *,
- const char *);
+ struct connection_info *);
+void parse_server_match_config(ServerOptions *, 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);
void dump_config(ServerOptions *);
char *derelativise_path(const char *);
diff --git a/usr.bin/ssh/sshd.8 b/usr.bin/ssh/sshd.8
index 569b9c6fefa..99c8c4a62ed 100644
--- a/usr.bin/ssh/sshd.8
+++ b/usr.bin/ssh/sshd.8
@@ -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: sshd.8,v 1.264 2011/09/23 00:22:04 dtucker Exp $
-.Dd $Mdocdate: September 23 2011 $
+.\" $OpenBSD: sshd.8,v 1.265 2012/05/13 01:42:32 dtucker Exp $
+.Dd $Mdocdate: May 13 2012 $
.Dt SSHD 8
.Os
.Sh NAME
@@ -114,6 +114,8 @@ The connection parameters are supplied as keyword=value pairs.
The keywords are
.Dq user ,
.Dq host ,
+.Dq laddr ,
+.Dq lport ,
and
.Dq addr .
All are required and may be supplied in any order, either with multiple
diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c
index c72c88f400d..741b8513556 100644
--- a/usr.bin/ssh/sshd.c
+++ b/usr.bin/ssh/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.390 2012/04/12 02:42:32 djm Exp $ */
+/* $OpenBSD: sshd.c,v 1.391 2012/05/13 01:42:32 dtucker Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1290,14 +1290,14 @@ main(int ac, char **av)
int opt, i, j, on = 1;
int sock_in = -1, sock_out = -1, newsock = -1;
const char *remote_ip;
- char *test_user = NULL, *test_host = NULL, *test_addr = NULL;
int remote_port;
- char *line, *p, *cp;
+ char *line;
int config_s[2] = { -1 , -1 };
u_int64_t ibytes, obytes;
mode_t new_umask;
Key *key;
Authctxt *authctxt;
+ struct connection_info *connection_info = get_connection_info(0, 0);
/* Save argv. */
saved_argv = av;
@@ -1401,20 +1401,9 @@ main(int ac, char **av)
test_flag = 2;
break;
case 'C':
- cp = optarg;
- while ((p = strsep(&cp, ",")) && *p != '\0') {
- if (strncmp(p, "addr=", 5) == 0)
- test_addr = xstrdup(p + 5);
- else if (strncmp(p, "host=", 5) == 0)
- test_host = xstrdup(p + 5);
- else if (strncmp(p, "user=", 5) == 0)
- test_user = xstrdup(p + 5);
- else {
- fprintf(stderr, "Invalid test "
- "mode specification %s\n", p);
- exit(1);
- }
- }
+ if (parse_server_match_testspec(connection_info,
+ optarg) == -1)
+ exit(1);
break;
case 'u':
utmp_len = (u_int)strtonum(optarg, 0, MAXHOSTNAMELEN+1, NULL);
@@ -1426,7 +1415,7 @@ main(int ac, char **av)
case 'o':
line = xstrdup(optarg);
if (process_server_config_line(&options, line,
- "command-line", 0, NULL, NULL, NULL, NULL) != 0)
+ "command-line", 0, NULL, NULL) != 0)
exit(1);
xfree(line);
break;
@@ -1468,13 +1457,10 @@ main(int ac, char **av)
* the parameters we need. If we're not doing an extended test,
* do not silently ignore connection test params.
*/
- if (test_flag >= 2 &&
- (test_user != NULL || test_host != NULL || test_addr != NULL)
- && (test_user == NULL || test_host == NULL || test_addr == NULL))
+ if (test_flag >= 2 && server_match_spec_complete(connection_info) == 0)
fatal("user, host and addr are all required when testing "
"Match configs");
- if (test_flag < 2 && (test_user != NULL || test_host != NULL ||
- test_addr != NULL))
+ if (test_flag < 2 && server_match_spec_complete(connection_info) >= 0)
fatal("Config test connection parameter (-C) provided without "
"test mode (-T)");
@@ -1486,7 +1472,7 @@ main(int ac, char **av)
load_server_config(config_file_name, &cfg);
parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
- &cfg, NULL, NULL, NULL);
+ &cfg, NULL);
/* Fill in default values for those options not explicitly set. */
fill_default_server_options(&options);
@@ -1629,9 +1615,8 @@ main(int ac, char **av)
}
if (test_flag > 1) {
- if (test_user != NULL && test_addr != NULL && test_host != NULL)
- parse_server_match_config(&options, test_user,
- test_host, test_addr);
+ if (server_match_spec_complete(connection_info) == 1)
+ parse_server_match_config(&options, connection_info);
dump_config(&options);
}
diff --git a/usr.bin/ssh/sshd_config.5 b/usr.bin/ssh/sshd_config.5
index a5045d1e083..0c2fb1db1f1 100644
--- a/usr.bin/ssh/sshd_config.5
+++ b/usr.bin/ssh/sshd_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: sshd_config.5,v 1.138 2012/04/12 02:43:55 djm Exp $
-.Dd $Mdocdate: April 12 2012 $
+.\" $OpenBSD: sshd_config.5,v 1.139 2012/05/13 01:42:32 dtucker Exp $
+.Dd $Mdocdate: May 13 2012 $
.Dt SSHD_CONFIG 5
.Os
.Sh NAME
@@ -678,6 +678,8 @@ The available criteria are
.Cm User ,
.Cm Group ,
.Cm Host ,
+.Cm LocalAddress ,
+.Cm LocalPort ,
and
.Cm Address .
The match patterns may consist of single entries or comma-separated