version 1.225, 2012/04/12 02:42:32 |
version 1.226, 2012/05/13 01:42:32 |
|
|
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* |
/* |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
|
|
#include "match.h" |
#include "match.h" |
#include "channels.h" |
#include "channels.h" |
#include "groupaccess.h" |
#include "groupaccess.h" |
|
#include "canohost.h" |
|
#include "packet.h" |
|
|
static void add_listen_addr(ServerOptions *, char *, int); |
static void add_listen_addr(ServerOptions *, char *, int); |
static void add_one_listen_addr(ServerOptions *, char *, int); |
static void add_one_listen_addr(ServerOptions *, char *, int); |
|
|
options->listen_addrs = aitop; |
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. |
* The strategy for the Match blocks is that the config file is parsed twice. |
* |
* |
|
|
return result; |
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 |
static int |
match_cfg_line(char **condition, int line, const char *user, const char *host, |
match_cfg_line(char **condition, int line, struct connection_info *ci) |
const char *address) |
|
{ |
{ |
int result = 1; |
int result = 1, port; |
char *arg, *attrib, *cp = *condition; |
char *arg, *attrib, *cp = *condition; |
size_t len; |
size_t len; |
|
|
if (user == NULL) |
if (ci == NULL) |
debug3("checking syntax for 'Match %s'", cp); |
debug3("checking syntax for 'Match %s'", cp); |
else |
else |
debug3("checking match for '%s' user %s host %s addr %s", cp, |
debug3("checking match for '%s' user %s host %s addr %s " |
user ? user : "(null)", host ? host : "(null)", |
"laddr %s lport %d", cp, ci->user ? ci->user : "(null)", |
address ? address : "(null)"); |
ci->host ? ci->host : "(null)", |
|
ci->address ? ci->address : "(null)", |
|
ci->laddress ? ci->laddress : "(null)", ci->lport); |
|
|
while ((attrib = strdelim(&cp)) && *attrib != '\0') { |
while ((attrib = strdelim(&cp)) && *attrib != '\0') { |
if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { |
if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { |
|
|
} |
} |
len = strlen(arg); |
len = strlen(arg); |
if (strcasecmp(attrib, "user") == 0) { |
if (strcasecmp(attrib, "user") == 0) { |
if (!user) { |
if (ci == NULL || ci->user == NULL) { |
result = 0; |
result = 0; |
continue; |
continue; |
} |
} |
if (match_pattern_list(user, arg, len, 0) != 1) |
if (match_pattern_list(ci->user, arg, len, 0) != 1) |
result = 0; |
result = 0; |
else |
else |
debug("user %.100s matched 'User %.100s' at " |
debug("user %.100s matched 'User %.100s' at " |
"line %d", user, arg, line); |
"line %d", ci->user, arg, line); |
} else if (strcasecmp(attrib, "group") == 0) { |
} 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: |
case -1: |
return -1; |
return -1; |
case 0: |
case 0: |
result = 0; |
result = 0; |
} |
} |
} else if (strcasecmp(attrib, "host") == 0) { |
} else if (strcasecmp(attrib, "host") == 0) { |
if (!host) { |
if (ci == NULL || ci->host == NULL) { |
result = 0; |
result = 0; |
continue; |
continue; |
} |
} |
if (match_hostname(host, arg, len) != 1) |
if (match_hostname(ci->host, arg, len) != 1) |
result = 0; |
result = 0; |
else |
else |
debug("connection from %.100s matched 'Host " |
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) { |
} 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: |
case 1: |
debug("connection from %.100s matched 'Address " |
debug("connection from %.100s matched 'Address " |
"%.100s' at line %d", address, arg, line); |
"%.100s' at line %d", ci->address, arg, line); |
break; |
break; |
case 0: |
case 0: |
case -1: |
case -1: |
|
|
case -2: |
case -2: |
return -1; |
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 { |
} else { |
error("Unsupported Match attribute %s", attrib); |
error("Unsupported Match attribute %s", attrib); |
return -1; |
return -1; |
} |
} |
} |
} |
if (user != NULL) |
if (ci != NULL) |
debug3("match %sfound", result ? "" : "not "); |
debug3("match %sfound", result ? "" : "not "); |
*condition = cp; |
*condition = cp; |
return result; |
return result; |
|
|
|
|
int |
int |
process_server_config_line(ServerOptions *options, char *line, |
process_server_config_line(ServerOptions *options, char *line, |
const char *filename, int linenum, int *activep, const char *user, |
const char *filename, int linenum, int *activep, |
const char *host, const char *address) |
struct connection_info *connectinfo) |
{ |
{ |
char *cp, **charptr, *arg, *p; |
char *cp, **charptr, *arg, *p; |
int cmdline = 0, *intptr, value, value2, n; |
int cmdline = 0, *intptr, value, value2, n; |
|
|
if (*activep && opcode != sMatch) |
if (*activep && opcode != sMatch) |
debug3("%s:%d setting %s %s", filename, linenum, arg, cp); |
debug3("%s:%d setting %s %s", filename, linenum, arg, cp); |
if (*activep == 0 && !(flags & SSHCFG_MATCH)) { |
if (*activep == 0 && !(flags & SSHCFG_MATCH)) { |
if (user == NULL) { |
if (connectinfo == NULL) { |
fatal("%s line %d: Directive '%s' is not allowed " |
fatal("%s line %d: Directive '%s' is not allowed " |
"within a Match block", filename, linenum, arg); |
"within a Match block", filename, linenum, arg); |
} else { /* this is a directive we have already processed */ |
} else { /* this is a directive we have already processed */ |
|
|
if (cmdline) |
if (cmdline) |
fatal("Match directive not supported as a command-line " |
fatal("Match directive not supported as a command-line " |
"option"); |
"option"); |
value = match_cfg_line(&cp, linenum, user, host, address); |
value = match_cfg_line(&cp, linenum, connectinfo); |
if (value < 0) |
if (value < 0) |
fatal("%s line %d: Bad Match condition", filename, |
fatal("%s line %d: Bad Match condition", filename, |
linenum); |
linenum); |
|
|
} |
} |
|
|
void |
void |
parse_server_match_config(ServerOptions *options, const char *user, |
parse_server_match_config(ServerOptions *options, |
const char *host, const char *address) |
struct connection_info *connectinfo) |
{ |
{ |
ServerOptions mo; |
ServerOptions mo; |
|
|
initialize_server_options(&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); |
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 */ |
/* Helper macros */ |
#define M_CP_INTOPT(n) do {\ |
#define M_CP_INTOPT(n) do {\ |
if (src->n != -1) \ |
if (src->n != -1) \ |
|
|
|
|
void |
void |
parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, |
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; |
int active, linenum, bad_options = 0; |
char *cp, *obuf, *cbuf; |
char *cp, *obuf, *cbuf; |
|
|
debug2("%s: config %s len %d", __func__, filename, buffer_len(conf)); |
debug2("%s: config %s len %d", __func__, filename, buffer_len(conf)); |
|
|
obuf = cbuf = xstrdup(buffer_ptr(conf)); |
obuf = cbuf = xstrdup(buffer_ptr(conf)); |
active = user ? 0 : 1; |
active = connectinfo ? 0 : 1; |
linenum = 1; |
linenum = 1; |
while ((cp = strsep(&cbuf, "\n")) != NULL) { |
while ((cp = strsep(&cbuf, "\n")) != NULL) { |
if (process_server_config_line(options, cp, filename, |
if (process_server_config_line(options, cp, filename, |
linenum++, &active, user, host, address) != 0) |
linenum++, &active, connectinfo) != 0) |
bad_options++; |
bad_options++; |
} |
} |
xfree(obuf); |
xfree(obuf); |