sshd config parser
Darren Tucker
dtucker at zip.com.au
Mon Apr 3 23:22:41 EST 2006
Hi all.
New Match patch. Old bugs out, new bugs in :-) Feedback welcome.
--
Darren Tucker (dtucker at zip.com.au)
GPG key 8FF4FA69 / D9A3 86E9 7EEE AF4B B2D4 37C9 C982 80C7 8FF4 FA69
Good judgement comes with experience. Unfortunately, the experience
usually comes from bad judgement.
-------------- next part --------------
Index: Makefile.in
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/Makefile.in,v
retrieving revision 1.274
diff -u -p -r1.274 Makefile.in
--- Makefile.in 1 Jan 2006 08:47:05 -0000 1.274
+++ Makefile.in 2 Apr 2006 10:19:10 -0000
@@ -81,7 +81,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw
auth.o auth1.o auth2.o auth-options.o session.o \
auth-chall.o auth2-chall.o groupaccess.o \
auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
- auth2-none.o auth2-passwd.o auth2-pubkey.o \
+ auth2-none.o auth2-passwd.o auth2-pubkey.o cfgmatch.o \
monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \
auth-krb5.o \
auth2-gss.o gss-serv.o gss-serv-krb5.o \
Index: auth.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/auth.c,v
retrieving revision 1.101
diff -u -p -r1.101 auth.c
--- auth.c 31 Aug 2005 16:59:49 -0000 1.101
+++ auth.c 2 Apr 2006 02:23:02 -0000
@@ -492,6 +492,9 @@ getpwnamallow(const char *user)
#endif
struct passwd *pw;
+ parse_server_match_config(&options, user,
+ get_canonical_hostname(options.use_dns), get_remote_ipaddr());
+
pw = getpwnam(user);
if (pw == NULL) {
logit("Invalid user %.100s from %.100s",
Index: cfgmatch.c
===================================================================
RCS file: cfgmatch.c
diff -N cfgmatch.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ cfgmatch.c 3 Apr 2006 10:23:55 -0000
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2006 Darren Tucker. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#include "match.h"
+#include "log.h"
+#include "misc.h"
+#include "canohost.h"
+#include "groupaccess.h"
+
+int
+match_cfg_line(char **condition, int line, const char *user, const char *host,
+ const char *address)
+{
+ int result = 1;
+ char *arg, *attrib, *cp = *condition, *grplist[1];
+ size_t len;
+ struct passwd *pw;
+
+ if (user == NULL)
+ debug3("checking syntax for 'Match %s'", cp);
+ else
+ debug3("checking match for '%s'", cp);
+
+ 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, "user") == 0) {
+ if (!user) {
+ result = 0;
+ continue;
+ }
+ if (match_pattern_list(user, arg, len, 0) != 1)
+ /* XXX what about negative match? */
+ result = 0;
+ else
+ debug("user %.100s matched 'User %.100s' at "
+ "line %d", user, arg, line);
+ } else if (strcasecmp(attrib, "group") == 0) {
+ if (!user) {
+ result = 0;
+ continue;
+ }
+ grplist[0] = arg; /* XXX split on comma */
+ if ((pw = getpwnam(user)) == NULL) {
+ debug("Can't match group at line %d because "
+ "user %.100s does not exist", line, user);
+ result = 0;
+ } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
+ debug("Can't Match group because user %.100s "
+ "not in any group at line %d", user,
+ line);
+ result = 0;
+ } else if (ga_match(grplist, 1) != 1) {
+ debug("User %.100s does not match group "
+ "%.100s at line %d", user, arg,
+ line);
+ result = 0;
+ } else {
+ debug("User %.100s matched Group %.100s at "
+ "line %d", user, arg, line);
+ }
+ } else if (strcasecmp(attrib, "host") == 0) {
+ if (!host) {
+ result = 0;
+ continue;
+ }
+ if (match_hostname(host, arg, len) != 1)
+ result = 0;
+ else
+ debug("connection from %.100s matched 'Host "
+ "%.100s' at line %d", host, arg, line);
+ } else if (strcasecmp(attrib, "address") == 0) {
+ if (!address) {
+ result = 0;
+ continue;
+ }
+ if (match_hostname(address, arg, len) != 1)
+ result = 0;
+ else
+ debug("connection from %.100s matched 'Address "
+ "%.100s' at line %d", address, arg, line);
+ } else {
+ error("Unsupported Match attribute %s", attrib);
+ return -1;
+ }
+ }
+ *condition = cp;
+ debug3("match_cfg_line: returning %d", result);
+ return result;
+}
Index: match.h
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/match.h,v
retrieving revision 1.11
diff -u -p -r1.11 match.h
--- match.h 5 Mar 2002 01:42:43 -0000 1.11
+++ match.h 2 Apr 2006 01:27:11 -0000
@@ -20,5 +20,6 @@ int match_hostname(const char *, const
int match_host_and_ip(const char *, const char *, const char *);
int match_user(const char *, const char *, const char *, const char *);
char *match_list(const char *, const char *, u_int *);
+int match_cfg_line(char **, int, const char *, const char *, const char *);
#endif
Index: monitor.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/monitor.c,v
retrieving revision 1.88
diff -u -p -r1.88 monitor.c
--- monitor.c 5 Nov 2005 04:07:05 -0000 1.88
+++ monitor.c 2 Apr 2006 01:08:28 -0000
@@ -612,6 +612,7 @@ mm_answer_pwnamallow(int sock, Buffer *m
#endif
buffer_put_cstring(m, pwent->pw_dir);
buffer_put_cstring(m, pwent->pw_shell);
+ buffer_put_string(m, &options, sizeof(options));
out:
debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed);
Index: monitor_wrap.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/monitor_wrap.c,v
retrieving revision 1.54
diff -u -p -r1.54 monitor_wrap.c
--- monitor_wrap.c 29 Sep 2005 12:01:10 -0000 1.54
+++ monitor_wrap.c 3 Apr 2006 10:17:00 -0000
@@ -196,7 +196,8 @@ mm_getpwnamallow(const char *username)
{
Buffer m;
struct passwd *pw;
- u_int pwlen;
+ u_int len;
+ void *p;
debug3("%s entering", __func__);
@@ -212,8 +213,8 @@ mm_getpwnamallow(const char *username)
buffer_free(&m);
return (NULL);
}
- pw = buffer_get_string(&m, &pwlen);
- if (pwlen != sizeof(struct passwd))
+ pw = buffer_get_string(&m, &len);
+ if (len != sizeof(struct passwd))
fatal("%s: struct passwd size mismatch", __func__);
pw->pw_name = buffer_get_string(&m, NULL);
pw->pw_passwd = buffer_get_string(&m, NULL);
@@ -223,7 +224,28 @@ mm_getpwnamallow(const char *username)
#endif
pw->pw_dir = buffer_get_string(&m, NULL);
pw->pw_shell = buffer_get_string(&m, NULL);
- buffer_free(&m);
+ p = buffer_get_string(&m, &len);
+ if (len != sizeof(options))
+ fatal("%s: option block size mismatch", __func__);
+ memcpy(&options, p, len);
+ buffer_free(&m);
+
+ /* we don't use these options, so zero for safety */
+ options.num_ports = 0;
+ options.ports_from_cmdline = 0;
+ options.pid_file = NULL;
+ options.xauth_location = NULL;
+ options.ciphers = NULL;
+ options.num_allow_users = 0;
+ options.num_deny_users = 0;
+ options.num_allow_groups = 0;
+ options.num_deny_groups = 0;
+ options.macs = NULL;
+ options.num_subsystems = 0;
+ options.banner = NULL;
+ options.authorized_keys_file = NULL;
+ options.authorized_keys_file2 = NULL;
+ options.num_accept_env = 0;
return (pw);
}
Index: servconf.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/servconf.c,v
retrieving revision 1.136
diff -u -p -r1.136 servconf.c
--- servconf.c 13 Dec 2005 08:33:20 -0000 1.136
+++ servconf.c 3 Apr 2006 12:14:12 -0000
@@ -22,12 +22,14 @@ RCSID("$OpenBSD: servconf.c,v 1.146 2005
#include "cipher.h"
#include "kex.h"
#include "mac.h"
+#include "match.h"
static void add_listen_addr(ServerOptions *, char *, u_short);
static void add_one_listen_addr(ServerOptions *, char *, u_short);
/* Use of privilege separation or not */
extern int use_privsep;
+extern Buffer cfg;
/* Initializes the server options to their default values. */
@@ -102,9 +104,6 @@ initialize_server_options(ServerOptions
options->authorized_keys_file2 = NULL;
options->num_accept_env = 0;
options->permit_tun = -1;
-
- /* Needs to be accessable in many places */
- use_privsep = -1;
}
void
@@ -274,110 +273,117 @@ typedef enum {
sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
+ sMatch,
sUsePrivilegeSeparation,
sDeprecated, sUnsupported
} ServerOpCodes;
+#define SSHCFG_GLOBAL 0x01
+#define SSHCFG_MATCH 0x02
+#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH)
+
/* Textual representation of the tokens. */
static struct {
const char *name;
ServerOpCodes opcode;
+ u_int flags;
} keywords[] = {
/* Portable-specific options */
#ifdef USE_PAM
- { "usepam", sUsePAM },
+ { "usepam", sUsePAM, SSHCFG_ALL },
#else
- { "usepam", sUnsupported },
+ { "usepam", sUnsupported, SSHCFG_ALL },
#endif
- { "pamauthenticationviakbdint", sDeprecated },
+ { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL },
/* Standard Options */
- { "port", sPort },
- { "hostkey", sHostKeyFile },
- { "hostdsakey", sHostKeyFile }, /* alias */
- { "pidfile", sPidFile },
- { "serverkeybits", sServerKeyBits },
- { "logingracetime", sLoginGraceTime },
- { "keyregenerationinterval", sKeyRegenerationTime },
- { "permitrootlogin", sPermitRootLogin },
- { "syslogfacility", sLogFacility },
- { "loglevel", sLogLevel },
- { "rhostsauthentication", sDeprecated },
- { "rhostsrsaauthentication", sRhostsRSAAuthentication },
- { "hostbasedauthentication", sHostbasedAuthentication },
- { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly },
- { "rsaauthentication", sRSAAuthentication },
- { "pubkeyauthentication", sPubkeyAuthentication },
- { "dsaauthentication", sPubkeyAuthentication }, /* alias */
+ { "port", sPort, SSHCFG_GLOBAL },
+ { "hostkey", sHostKeyFile, SSHCFG_GLOBAL },
+ { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */
+ { "pidfile", sPidFile, SSHCFG_GLOBAL },
+ { "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL },
+ { "logingracetime", sLoginGraceTime, SSHCFG_ALL },
+ { "keyregenerationinterval", sKeyRegenerationTime, SSHCFG_GLOBAL },
+ { "permitrootlogin", sPermitRootLogin, SSHCFG_ALL },
+ { "syslogfacility", sLogFacility, SSHCFG_ALL },
+ { "loglevel", sLogLevel, SSHCFG_ALL },
+ { "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL },
+ { "rhostsrsaauthentication", sRhostsRSAAuthentication, SSHCFG_ALL },
+ { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL },
+ { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_ALL },
+ { "rsaauthentication", sRSAAuthentication, SSHCFG_ALL },
+ { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL },
+ { "dsaauthentication", sPubkeyAuthentication, SSHCFG_ALL }, /* alias */
#ifdef KRB5
- { "kerberosauthentication", sKerberosAuthentication },
- { "kerberosorlocalpasswd", sKerberosOrLocalPasswd },
- { "kerberosticketcleanup", sKerberosTicketCleanup },
+ { "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL },
+ { "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_ALL },
+ { "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_ALL },
#ifdef USE_AFS
- { "kerberosgetafstoken", sKerberosGetAFSToken },
+ { "kerberosgetafstoken", sKerberosGetAFSToken, SSHCFG_ALL },
#else
- { "kerberosgetafstoken", sUnsupported },
+ { "kerberosgetafstoken", sUnsupported, SSHCFG_ALL },
#endif
#else
- { "kerberosauthentication", sUnsupported },
- { "kerberosorlocalpasswd", sUnsupported },
- { "kerberosticketcleanup", sUnsupported },
- { "kerberosgetafstoken", sUnsupported },
+ { "kerberosauthentication", sUnsupported, SSHCFG_ALL },
+ { "kerberosorlocalpasswd", sUnsupported, SSHCFG_ALL },
+ { "kerberosticketcleanup", sUnsupported, SSHCFG_ALL },
+ { "kerberosgetafstoken", sUnsupported, SSHCFG_ALL },
#endif
- { "kerberostgtpassing", sUnsupported },
- { "afstokenpassing", sUnsupported },
+ { "kerberostgtpassing", sUnsupported, SSHCFG_ALL },
+ { "afstokenpassing", sUnsupported, SSHCFG_ALL },
#ifdef GSSAPI
- { "gssapiauthentication", sGssAuthentication },
- { "gssapicleanupcredentials", sGssCleanupCreds },
+ { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
+ { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_ALL },
#else
- { "gssapiauthentication", sUnsupported },
- { "gssapicleanupcredentials", sUnsupported },
+ { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
+ { "gssapicleanupcredentials", sUnsupported, SSHCFG_ALL },
#endif
- { "passwordauthentication", sPasswordAuthentication },
- { "kbdinteractiveauthentication", sKbdInteractiveAuthentication },
- { "challengeresponseauthentication", sChallengeResponseAuthentication },
- { "skeyauthentication", sChallengeResponseAuthentication }, /* alias */
- { "checkmail", sDeprecated },
- { "listenaddress", sListenAddress },
- { "addressfamily", sAddressFamily },
- { "printmotd", sPrintMotd },
- { "printlastlog", sPrintLastLog },
- { "ignorerhosts", sIgnoreRhosts },
- { "ignoreuserknownhosts", sIgnoreUserKnownHosts },
- { "x11forwarding", sX11Forwarding },
- { "x11displayoffset", sX11DisplayOffset },
- { "x11uselocalhost", sX11UseLocalhost },
- { "xauthlocation", sXAuthLocation },
- { "strictmodes", sStrictModes },
- { "permitemptypasswords", sEmptyPasswd },
- { "permituserenvironment", sPermitUserEnvironment },
- { "uselogin", sUseLogin },
- { "compression", sCompression },
- { "tcpkeepalive", sTCPKeepAlive },
- { "keepalive", sTCPKeepAlive }, /* obsolete alias */
- { "allowtcpforwarding", sAllowTcpForwarding },
- { "allowusers", sAllowUsers },
- { "denyusers", sDenyUsers },
- { "allowgroups", sAllowGroups },
- { "denygroups", sDenyGroups },
- { "ciphers", sCiphers },
- { "macs", sMacs },
- { "protocol", sProtocol },
- { "gatewayports", sGatewayPorts },
- { "subsystem", sSubsystem },
- { "maxstartups", sMaxStartups },
- { "maxauthtries", sMaxAuthTries },
- { "banner", sBanner },
- { "usedns", sUseDNS },
- { "verifyreversemapping", sDeprecated },
- { "reversemappingcheck", sDeprecated },
- { "clientaliveinterval", sClientAliveInterval },
- { "clientalivecountmax", sClientAliveCountMax },
- { "authorizedkeysfile", sAuthorizedKeysFile },
- { "authorizedkeysfile2", sAuthorizedKeysFile2 },
- { "useprivilegeseparation", sUsePrivilegeSeparation},
- { "acceptenv", sAcceptEnv },
- { "permittunnel", sPermitTunnel },
- { NULL, sBadOption }
+ { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
+ { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
+ { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_ALL },
+ { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_ALL }, /* alias */
+ { "checkmail", sDeprecated, SSHCFG_GLOBAL },
+ { "listenaddress", sListenAddress, SSHCFG_GLOBAL },
+ { "addressfamily", sAddressFamily, SSHCFG_GLOBAL },
+ { "printmotd", sPrintMotd, SSHCFG_ALL },
+ { "printlastlog", sPrintLastLog, SSHCFG_ALL },
+ { "ignorerhosts", sIgnoreRhosts, SSHCFG_ALL },
+ { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_ALL },
+ { "x11forwarding", sX11Forwarding, SSHCFG_ALL },
+ { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL },
+ { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL },
+ { "xauthlocation", sXAuthLocation, SSHCFG_ALL },
+ { "strictmodes", sStrictModes, SSHCFG_ALL },
+ { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL },
+ { "permituserenvironment", sPermitUserEnvironment, SSHCFG_ALL },
+ { "uselogin", sUseLogin, SSHCFG_ALL },
+ { "compression", sCompression, SSHCFG_GLOBAL },
+ { "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL },
+ { "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, /* obsolete alias */
+ { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL },
+ { "allowusers", sAllowUsers, SSHCFG_GLOBAL },
+ { "denyusers", sDenyUsers, SSHCFG_GLOBAL },
+ { "allowgroups", sAllowGroups, SSHCFG_GLOBAL },
+ { "denygroups", sDenyGroups, SSHCFG_GLOBAL },
+ { "ciphers", sCiphers, SSHCFG_GLOBAL },
+ { "macs", sMacs, SSHCFG_GLOBAL },
+ { "protocol", sProtocol, SSHCFG_GLOBAL },
+ { "gatewayports", sGatewayPorts, SSHCFG_ALL },
+ { "subsystem", sSubsystem, SSHCFG_ALL },
+ { "maxstartups", sMaxStartups, SSHCFG_GLOBAL },
+ { "maxauthtries", sMaxAuthTries, SSHCFG_ALL },
+ { "banner", sBanner, SSHCFG_ALL },
+ { "usedns", sUseDNS, SSHCFG_GLOBAL },
+ { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL },
+ { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL },
+ { "clientaliveinterval", sClientAliveInterval, SSHCFG_ALL },
+ { "clientalivecountmax", sClientAliveCountMax, SSHCFG_ALL },
+ { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL },
+ { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_ALL },
+ { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL},
+ { "acceptenv", sAcceptEnv, SSHCFG_ALL },
+ { "permittunnel", sPermitTunnel, SSHCFG_ALL },
+ { "match", sMatch, SSHCFG_ALL },
+ { NULL, sBadOption, 0 }
};
/*
@@ -386,13 +392,15 @@ static struct {
static ServerOpCodes
parse_token(const char *cp, const char *filename,
- int linenum)
+ int linenum, u_int *flags)
{
u_int i;
for (i = 0; keywords[i].name; i++)
- if (strcasecmp(cp, keywords[i].name) == 0)
+ if (strcasecmp(cp, keywords[i].name) == 0) {
+ *flags = keywords[i].flags;
return keywords[i].opcode;
+ }
error("%s: line %d: Bad configuration option: %s",
filename, linenum, cp);
@@ -437,15 +445,49 @@ add_one_listen_addr(ServerOptions *optio
options->listen_addrs = aitop;
}
+/*
+ * The strategy for the Match blocks is that the config file is parsed twice.
+ *
+ * The first time is at startup. activep is initialized to 1 and the
+ * directives in the global context are processed and acted on. Hitting a
+ * Match directive unsets activep and the directives inside the block are
+ * checked for syntax only.
+ *
+ * The second time is after a connection has been established but before
+ * authentication. activep is initialized to 2 and global config directives
+ * are ignored since they have already been processed. If the criteria in a
+ * Match block is met, activep is set and the subsequent directives
+ * processed and actioned until EOF or another Match block unsets it. Any
+ * options set are copied into the main server config.
+ *
+ * Potential additions/improvements:
+ * - Add Match support for pre-kex directives, eg Protocol, Ciphers.
+ *
+ * - Add a Tag directive (idea from David Leonard) ala pf, eg:
+ * Match Address 192.168.0.*
+ * Tag trusted
+ * Match Group wheel
+ * Tag trusted
+ * Match Tag trusted
+ * AllowTcpForwarding yes
+ * GatewayPorts clientspecified
+ * [...]
+ *
+ * - Add a PermittedChannelRequests directive
+ * Match Group shell
+ * PermittedChannelRequests session,forwarded-tcpip
+ */
+
int
process_server_config_line(ServerOptions *options, char *line,
- const char *filename, int linenum)
+ const char *filename, int linenum, int *activep, const char *user,
+ const char *host, const char *address)
{
char *cp, **charptr, *arg, *p;
- int *intptr, value, n;
+ int cmdline = 0, *intptr, value, n;
ServerOpCodes opcode;
u_short port;
- u_int i;
+ u_int i, flags = 0;
cp = line;
arg = strdelim(&cp);
@@ -456,7 +498,24 @@ process_server_config_line(ServerOptions
return 0;
intptr = NULL;
charptr = NULL;
- opcode = parse_token(arg, filename, linenum);
+ opcode = parse_token(arg, filename, linenum, &flags);
+
+ if (activep == NULL) { /* We are processing a command line directive */
+ cmdline = 1;
+ activep = &cmdline;
+ }
+ debug3("match: line %s active %d flags %d", line, *activep, flags);
+ if (*activep == 0 && !(flags & SSHCFG_MATCH)) {
+ if (user == 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 */
+ while (arg)
+ arg = strdelim(&cp);
+ return 0;
+ }
+ }
+
switch (opcode) {
/* Portable-specific options */
case sUsePAM:
@@ -494,7 +553,7 @@ parse_int:
fatal("%s line %d: missing integer value.",
filename, linenum);
value = atoi(arg);
- if (*intptr == -1)
+ if (*activep && *intptr == -1)
*intptr = value;
break;
@@ -574,7 +633,7 @@ parse_filename:
if (!arg || *arg == '\0')
fatal("%s line %d: missing file name.",
filename, linenum);
- if (*charptr == NULL) {
+ if (*activep && *charptr == NULL) {
*charptr = tilde_expand_filename(arg, getuid());
/* increase optional counter */
if (intptr != NULL)
@@ -625,7 +684,7 @@ parse_flag:
else
fatal("%s line %d: Bad yes/no argument: %s",
filename, linenum, arg);
- if (*intptr == -1)
+ if (*activep && *intptr == -1)
*intptr = value;
break;
@@ -890,6 +949,10 @@ parse_flag:
if (!arg || *arg == '\0')
fatal("%s line %d: Missing subsystem name.",
filename, linenum);
+ if (!*activep) {
+ arg = strdelim(&cp);
+ break;
+ }
for (i = 0; i < options->num_subsystems; i++)
if (strcmp(arg, options->subsystem_name[i]) == 0)
fatal("%s line %d: Subsystem '%s' already defined.",
@@ -961,6 +1024,8 @@ parse_flag:
if (options->num_accept_env >= MAX_ACCEPT_ENV)
fatal("%s line %d: too many allow env.",
filename, linenum);
+ if (!*activep)
+ break;
options->accept_env[options->num_accept_env++] =
xstrdup(arg);
}
@@ -988,6 +1053,17 @@ parse_flag:
*intptr = value;
break;
+ case sMatch:
+ if (cmdline)
+ fatal("Match directive not supported as a command-line "
+ "option");
+ value = match_cfg_line(&cp, linenum, user, host, address);
+ if (value < 0)
+ fatal("%s line %d: Bad Match condition", filename,
+ linenum);
+ *activep = value;
+ break;
+
case sDeprecated:
logit("%s line %d: Deprecated option %s",
filename, linenum, arg);
@@ -1044,18 +1120,140 @@ load_server_config(const char *filename,
}
void
-parse_server_config(ServerOptions *options, const char *filename, Buffer *conf)
+parse_server_match_config(ServerOptions *options, const char *user,
+ const char *host, const char *address)
+{
+ u_int i;
+ ServerOptions mo;
+
+ initialize_server_options(&mo);
+ parse_server_config(&mo, "reprocess config", &cfg, user, host, address);
+
+ /* now copy any (supported) values set */
+ if (mo.use_pam != -1)
+ options->use_pam = mo.use_pam;
+ if (mo.num_accept_env > 0) {
+ for (i = 0; i < options->num_accept_env; i++)
+ xfree(options->accept_env[i]);
+ options->num_accept_env = 0;
+ for (i = 0; i < mo.num_accept_env; i++) {
+ if (options->num_accept_env >= MAX_ACCEPT_ENV)
+ fatal("Too many allow env in Match block.");
+ options->accept_env[options->num_accept_env++] =
+ mo.accept_env[i];
+ }
+ }
+ if (mo.allow_tcp_forwarding != -1)
+ options->allow_tcp_forwarding = mo.allow_tcp_forwarding;
+ if (mo.authorized_keys_file != NULL) {
+ if (options->authorized_keys_file != NULL)
+ xfree(options->authorized_keys_file);
+ options->authorized_keys_file = mo.authorized_keys_file;
+ }
+ if (mo.authorized_keys_file2 != NULL) {
+ if (options->authorized_keys_file2 != NULL)
+ xfree(options->authorized_keys_file2);
+ options->authorized_keys_file2 = mo.authorized_keys_file2;
+ }
+ if (mo.banner != NULL) {
+ if (options->banner != NULL)
+ xfree(options->banner);
+ options->banner = mo.banner;
+ }
+ if (mo.password_authentication != -1)
+ options->password_authentication = mo.password_authentication;
+ if (mo.challenge_response_authentication != -1)
+ options->challenge_response_authentication =
+ mo.challenge_response_authentication;
+ if (mo.client_alive_count_max != -1)
+ options->client_alive_count_max = mo.client_alive_count_max;
+ if (mo.client_alive_interval != -1)
+ options->client_alive_interval = mo.client_alive_interval;
+ if (mo.gateway_ports != -1)
+ options->gateway_ports = mo.gateway_ports;
+ if (mo.gss_authentication != -1)
+ options->gss_authentication = mo.gss_authentication;
+ if (mo.hostbased_authentication != -1)
+ options->hostbased_authentication = mo.hostbased_authentication;
+ if (mo.hostbased_uses_name_from_packet_only != -1)
+ options->hostbased_uses_name_from_packet_only =
+ mo.hostbased_uses_name_from_packet_only;
+ if (mo.kerberos_authentication != -1)
+ options->kerberos_authentication = mo.kerberos_authentication;
+ if (mo.kerberos_or_local_passwd != -1)
+ options->kerberos_or_local_passwd = mo.kerberos_or_local_passwd;
+ if (mo.kerberos_ticket_cleanup != -1)
+ options->kerberos_ticket_cleanup = mo.kerberos_ticket_cleanup;
+ if (mo.kerberos_get_afs_token != -1)
+ options->kerberos_get_afs_token = mo.kerberos_get_afs_token;
+ if (mo.login_grace_time != -1)
+ options->login_grace_time = mo.login_grace_time;
+ if (mo.log_facility != -1)
+ options->log_facility = mo.log_facility;
+ if (mo.log_level != -1)
+ options->log_level = mo.log_level;
+ if (mo.permit_root_login != -1)
+ options->permit_root_login = mo.permit_root_login;
+ if (mo.max_authtries != -1)
+ options->max_authtries = mo.max_authtries;
+ if (mo.permit_empty_passwd != -1)
+ options->permit_empty_passwd = mo.permit_empty_passwd;
+ if (mo.permit_tun != -1)
+ options->permit_tun = mo.permit_tun;
+ if (mo.permit_user_env != -1)
+ options->permit_user_env = mo.permit_user_env;
+ if (mo.print_motd != -1)
+ options->print_motd = mo.print_motd;
+ if (mo.pubkey_authentication != -1)
+ options->pubkey_authentication = mo.pubkey_authentication;
+ if (mo.rsa_authentication != -1)
+ options->rsa_authentication = mo.rsa_authentication;
+ if (mo.strict_modes != -1)
+ options->strict_modes = mo.strict_modes;
+ if (mo.num_subsystems != 0) { /* Not currently used */
+ for (i = 0; i < options->num_subsystems; i++) {
+ xfree(options->subsystem_name[i]);
+ xfree(options->subsystem_command[i]);
+ }
+ options->num_subsystems = 0;
+ for (i = 0; i < mo.num_subsystems; i++) {
+ options->subsystem_name[options->num_subsystems] =
+ mo.subsystem_name[i];
+ options->subsystem_command[options->num_subsystems] =
+ mo.subsystem_command[i];
+ options->num_subsystems++;
+ }
+ }
+ if (mo.use_login != -1)
+ options->use_login = mo.use_login;
+ if (mo.xauth_location != NULL) {
+ if (options->xauth_location != NULL)
+ xfree(options->xauth_location);
+ options->xauth_location = mo.xauth_location;
+ }
+ if (mo.x11_display_offset != -1)
+ options->x11_display_offset = mo.x11_display_offset;
+ if (mo.x11_forwarding != -1)
+ options->x11_forwarding = mo.x11_forwarding;
+ if (mo.x11_use_localhost != -1)
+ options->x11_use_localhost = mo.x11_use_localhost;
+}
+
+void
+parse_server_config(ServerOptions *options, const char *filename, Buffer *conf,
+ const char *user, const char *host, const char *address)
{
- int linenum, bad_options = 0;
+ int active, linenum, bad_options = 0;
char *cp, *obuf, *cbuf;
debug2("%s: config %s len %d", __func__, filename, buffer_len(conf));
obuf = cbuf = xstrdup(buffer_ptr(conf));
+ active = user ? 0 : 1;
linenum = 1;
while ((cp = strsep(&cbuf, "\n")) != NULL) {
if (process_server_config_line(options, cp, filename,
- linenum++) != 0)
+ linenum++, &active, user, host, address) != 0)
bad_options++;
}
xfree(obuf);
Index: servconf.h
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/servconf.h,v
retrieving revision 1.64
diff -u -p -r1.64 servconf.h
--- servconf.h 13 Dec 2005 08:29:03 -0000 1.64
+++ servconf.h 3 Apr 2006 11:31:16 -0000
@@ -141,8 +141,12 @@ typedef struct {
void initialize_server_options(ServerOptions *);
void fill_default_server_options(ServerOptions *);
-int process_server_config_line(ServerOptions *, char *, const char *, int);
+int process_server_config_line(ServerOptions *, char *, const char *, int,
+ int *, const char *, const char *, const char *);
void load_server_config(const char *, Buffer *);
-void parse_server_config(ServerOptions *, 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 *);
#endif /* SERVCONF_H */
Index: sshd.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/sshd.c,v
retrieving revision 1.320
diff -u -p -r1.320 sshd.c
--- sshd.c 24 Dec 2005 03:59:12 -0000 1.320
+++ sshd.c 3 Apr 2006 11:30:11 -0000
@@ -201,12 +201,15 @@ int *startup_pipes = NULL;
int startup_pipe; /* in child */
/* variables used for privilege separation */
-int use_privsep;
+int use_privsep = -1; /* Needs to be accessable in many places */
struct monitor *pmonitor = NULL;
/* global authentication context */
Authctxt *the_authctxt = NULL;
+/* sshd_config buffer */
+Buffer cfg;
+
/* message to be displayed after login */
Buffer loginmsg;
@@ -892,7 +895,6 @@ main(int ac, char **av)
Key *key;
Authctxt *authctxt;
int ret, key_used = 0;
- Buffer cfg;
#ifdef HAVE_SECUREWARE
(void)set_auth_parameters(ac, av);
@@ -1011,7 +1013,7 @@ main(int ac, char **av)
case 'o':
line = xstrdup(optarg);
if (process_server_config_line(&options, line,
- "command-line", 0) != 0)
+ "command-line", 0, NULL, NULL, NULL, NULL) != 0)
exit(1);
xfree(line);
break;
@@ -1069,11 +1071,8 @@ main(int ac, char **av)
else
load_server_config(config_file_name, &cfg);
- parse_server_config(&options,
- rexeced_flag ? "rexec" : config_file_name, &cfg);
-
- if (!rexec_flag)
- buffer_free(&cfg);
+ parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
+ &cfg, NULL, NULL, NULL);
seed_rng();
Index: sshd_config.5
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/sshd_config.5,v
retrieving revision 1.53
diff -u -p -r1.53 sshd_config.5
--- sshd_config.5 3 Jan 2006 07:47:31 -0000 1.53
+++ sshd_config.5 3 Apr 2006 11:38:30 -0000
@@ -433,6 +433,70 @@ for data integrity protection.
Multiple algorithms must be comma-separated.
The default is
.Dq hmac-md5,hmac-sha1,hmac-ripemd160,hmac-sha1-96,hmac-md5-96 .
+.It Cm Match
+Introduces a conditional block. Keywords on lines following a
+.Cm Match
+block are only applied if the criteria on the
+.Cm Match
+are satisfied.
+The the arguments to
+.Cm Match
+block are one or more criteria-pattern pairs.
+The available criteria are
+.Cm User ,
+.Cm Group ,
+.Cm Host ,
+and
+.Cm Address .
+Only a subset of keywords may be used on the lines following a
+.Cm Match
+keyword.
+Available keywords are
+.Cm AcceptEnv ,
+.Cm AllowTcpForwarding ,
+.Cm AuthorizedKeysFile ,
+.Cm AuthorizedKeysFile2 ,
+.Cm Banner ,
+.Cm ChallengeResponseAuthentication ,
+.Cm ChallengeResponseAuthentication ,
+.Cm ClientAliveCountMax ,
+.Cm ClientAliveInterval ,
+.Cm GatewayPorts ,
+.Cm GssAuthentication ,
+.Cm GssCleanupCreds ,
+.Cm HostbasedAuthentication ,
+.Cm HostbasedUsesNameFromPacketOnly ,
+.Cm IgnoreRhosts ,
+.Cm IgnoreUserKnownHosts ,
+.Cm KbdInteractiveAuthentication ,
+.Cm KerberosAuthentication ,
+.Cm KerberosGetAFSToken ,
+.Cm KerberosOrLocalPasswd ,
+.Cm KerberosTicketCleanup ,
+.Cm LogFacility ,
+.Cm LogLevel ,
+.Cm LoginGraceTime ,
+.Cm MaxAuthTries ,
+.Cm PasswordAuthentication ,
+.Cm PermitEmptyPasswd ,
+.Cm PermitRootLogin ,
+.Cm PermitTunnel ,
+.Cm PermitUserEnvironment ,
+.Cm PrintLastLog ,
+.Cm PrintMotd ,
+.Cm PubkeyAuthentication ,
+.Cm PubkeyAuthentication ,
+.Cm RSAAuthentication ,
+.Cm RhostsRSAAuthentication ,
+.Cm StrictModes ,
+.Cm Subsystem ,
+.Cm UseLogin ,
+.Cm UsePAM ,
+.Cm X11DisplayOffset ,
+.Cm X11Forwarding ,
+.Cm X11UseLocalhost ,
+and
+.Cm XAuthLocation .
.It Cm MaxAuthTries
Specifies the maximum number of authentication attempts permitted per
connection.
@@ -827,6 +891,17 @@ Contains configuration data for
This file should be writable by root only, but it is recommended
(though not necessary) that it be world-readable.
.El
+.Sh EXAMPLES
+To allow
+.Cm PasswordAuthentication
+only from the local private network:
+.Bd -literal -offset indent
+PasswordAuthentication no
+Match Address 192.168.0.*
+ PasswordAuthentication yes
+.Ed
+.Bl -tag -width Ds
+.Bl -tag -width Ds
.Sh SEE ALSO
.Xr sshd 8
.Sh AUTHORS
More information about the openssh-unix-dev
mailing list