changeset 11044:eacbb90c6866

6655613 resync server's conditional Match block from OpenSSH
author Huie-Ying Lee <Huie-Ying.Lee@Sun.COM>
date Wed, 11 Nov 2009 16:01:18 -0800
parents 4db5a942fbd5
children 0db6e4248051
files usr/src/cmd/ssh/include/groupaccess.h usr/src/cmd/ssh/include/match.h usr/src/cmd/ssh/include/servconf.h usr/src/cmd/ssh/libssh/Makefile.com usr/src/cmd/ssh/libssh/common/addrmatch.c usr/src/cmd/ssh/libssh/common/uidswap.c usr/src/cmd/ssh/sshd/auth-pam.c usr/src/cmd/ssh/sshd/auth.c usr/src/cmd/ssh/sshd/auth2-gss.c usr/src/cmd/ssh/sshd/groupaccess.c usr/src/cmd/ssh/sshd/servconf.c usr/src/cmd/ssh/sshd/sshd.c usr/src/cmd/ssh/sshd/sshlogin.c
diffstat 13 files changed, 872 insertions(+), 129 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/ssh/include/groupaccess.h	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/include/groupaccess.h	Wed Nov 11 16:01:18 2009 -0800
@@ -3,8 +3,6 @@
 #ifndef	_GROUPACCESS_H
 #define	_GROUPACCESS_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -38,6 +36,7 @@
 
 int	 ga_init(const char *, gid_t);
 int	 ga_match(char * const *, int);
+int	 ga_match_pattern_list(const char *);
 void	 ga_free(void);
 
 #ifdef __cplusplus
--- a/usr/src/cmd/ssh/include/match.h	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/include/match.h	Wed Nov 11 16:01:18 2009 -0800
@@ -3,8 +3,6 @@
 #ifndef	_MATCH_H
 #define	_MATCH_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -29,6 +27,9 @@
 int	 match_user(const char *, const char *, const char *, const char *);
 char	*match_list(const char *, const char *, u_int *);
 
+/* addrmatch.c */
+int	 addr_match_list(const char *, const char *);
+
 #ifdef __cplusplus
 }
 #endif
--- a/usr/src/cmd/ssh/include/servconf.h	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/include/servconf.h	Wed Nov 11 16:01:18 2009 -0800
@@ -167,9 +167,15 @@
 }       ServerOptions;
 
 void	 initialize_server_options(ServerOptions *);
-void	 read_server_config(ServerOptions *, const char *);
 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 *,
+	     const char *, const char *, const char *);
+void	 parse_server_match_config(ServerOptions *, const char *, const char *,
+	     const char *);
+void	 copy_set_server_options(ServerOptions *, ServerOptions *, int);
 int	 chroot_requested(char *chroot_directory);
 
 #ifdef __cplusplus
--- a/usr/src/cmd/ssh/libssh/Makefile.com	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/libssh/Makefile.com	Wed Nov 11 16:01:18 2009 -0800
@@ -18,7 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -26,6 +26,7 @@
 VERS =		.1
 
 OBJECTS =	\
+		addrmatch.o \
 		atomicio.o \
 		authfd.o \
 		authfile.o \
@@ -42,10 +43,9 @@
 		dh.o \
 		dispatch.o \
 		engine.o \
+		entropy.o \
 		fatal.o \
 		g11n.o \
-		mac.o \
-		msg.o \
 		hostfile.o \
 		key.o \
 		kex.o \
@@ -58,16 +58,20 @@
 		kexgssc.o \
 		kexgsss.o \
 		log.o \
+		mac.o \
 		match.o \
 		misc.o \
 		mpaux.o \
+		msg.o \
 		nchan.o \
 		packet.o \
 		progressmeter.o \
+		proxy-io.o \
 		radix.o \
-		entropy.o \
+		readconf.o \
 		readpass.o \
 		rsa.o \
+		sftp-common.o \
 		ssh-dss.o \
 		ssh-gss.o \
 		ssh-rsa.o \
@@ -76,10 +80,7 @@
 		uidswap.o \
 		uuencode.o \
 		xlist.o \
-		xmalloc.o \
-		readconf.o \
-		sftp-common.o \
-		proxy-io.o
+		xmalloc.o
 
 include $(SRC)/lib/Makefile.lib
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/ssh/libssh/common/addrmatch.c	Wed Nov 11 16:01:18 2009 -0800
@@ -0,0 +1,424 @@
+/*	$OpenBSD: addrmatch.c,v 1.4 2008/12/10 03:55:20 stevesk Exp $ */
+
+/*
+ * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "match.h"
+#include "log.h"
+#include "xmalloc.h"
+
+struct xaddr {
+	sa_family_t	af;
+	union {
+		struct in_addr		v4;
+		struct in6_addr		v6;
+		u_int8_t		addr8[16];
+		u_int32_t		addr32[4];
+	} xa;		    /* 128-bit address */
+	u_int32_t	scope_id;	/* iface scope id for v6 */
+#define v4	xa.v4
+#define v6	xa.v6
+#define addr8	xa.addr8
+#define addr32	xa.addr32
+};
+
+static int
+addr_unicast_masklen(int af)
+{
+	switch (af) {
+	case AF_INET:
+		return 32;
+	case AF_INET6:
+		return 128;
+	default:
+		return -1;
+	}
+}
+
+static inline int
+masklen_valid(int af, u_int masklen)
+{
+	switch (af) {
+	case AF_INET:
+		return masklen <= 32 ? 0 : -1;
+	case AF_INET6:
+		return masklen <= 128 ? 0 : -1;
+	default:
+		return -1;
+	}
+}
+
+/*
+ * Convert struct sockaddr to struct xaddr
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa)
+{
+	struct sockaddr_in *in4 = (struct sockaddr_in *)sa;
+	struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
+
+	memset(xa, '\0', sizeof(*xa));
+
+	switch (sa->sa_family) {
+	case AF_INET:
+		if (slen < sizeof(*in4))
+			return -1;
+		xa->af = AF_INET;
+		memcpy(&xa->v4, &in4->sin_addr, sizeof(xa->v4));
+		break;
+	case AF_INET6:
+		if (slen < sizeof(*in6))
+			return -1;
+		xa->af = AF_INET6;
+		memcpy(&xa->v6, &in6->sin6_addr, sizeof(xa->v6));
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
+		xa->scope_id = in6->sin6_scope_id;
+#endif
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Calculate a netmask of length 'l' for address family 'af' and
+ * store it in 'n'.
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_netmask(int af, u_int l, struct xaddr *n)
+{
+	int i;
+
+	if (masklen_valid(af, l) != 0 || n == NULL)
+		return -1;
+
+	memset(n, '\0', sizeof(*n));
+	switch (af) {
+	case AF_INET:
+		n->af = AF_INET;
+		n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff);
+		return 0;
+	case AF_INET6:
+		n->af = AF_INET6;
+		for (i = 0; i < 4 && l >= 32; i++, l -= 32)
+			n->addr32[i] = 0xffffffffU;
+		if (i < 4 && l != 0)
+			n->addr32[i] = htonl((0xffffffff << (32 - l)) &
+			    0xffffffff);
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+/*
+ * Perform logical AND of addresses 'a' and 'b', storing result in 'dst'.
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b)
+{
+	int i;
+
+	if (dst == NULL || a == NULL || b == NULL || a->af != b->af)
+		return -1;
+
+	memcpy(dst, a, sizeof(*dst));
+	switch (a->af) {
+	case AF_INET:
+		dst->v4.s_addr &= b->v4.s_addr;
+		return 0;
+	case AF_INET6:
+		dst->scope_id = a->scope_id;
+		for (i = 0; i < 4; i++)
+			dst->addr32[i] &= b->addr32[i];
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+/*
+ * Compare addresses 'a' and 'b'
+ * Return 0 if addresses are identical, -1 if (a < b) or 1 if (a > b)
+ */
+static int
+addr_cmp(const struct xaddr *a, const struct xaddr *b)
+{
+	int i;
+
+	if (a->af != b->af)
+		return a->af == AF_INET6 ? 1 : -1;
+
+	switch (a->af) {
+	case AF_INET:
+		if (a->v4.s_addr == b->v4.s_addr)
+			return 0;
+		return ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr) ? 1 : -1;
+	case AF_INET6:
+		for (i = 0; i < 16; i++)
+			if (a->addr8[i] - b->addr8[i] != 0)
+				return a->addr8[i] > b->addr8[i] ? 1 : -1;
+		if (a->scope_id == b->scope_id)
+			return 0;
+		return a->scope_id > b->scope_id ? 1 : -1;
+	default:
+		return -1;
+	}
+}
+
+/*
+ * Parse string address 'p' into 'n'
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_pton(const char *p, struct xaddr *n)
+{
+	struct addrinfo hints, *ai;
+
+	memset(&hints, '\0', sizeof(hints));
+	hints.ai_flags = AI_NUMERICHOST;
+
+	if (p == NULL || getaddrinfo(p, NULL, &hints, &ai) != 0)
+		return -1;
+
+	if (ai == NULL || ai->ai_addr == NULL)
+		return -1;
+
+	if (n != NULL &&
+	    addr_sa_to_xaddr(ai->ai_addr, ai->ai_addrlen, n) == -1) {
+		freeaddrinfo(ai);
+		return -1;
+	}
+
+	freeaddrinfo(ai);
+	return 0;
+}
+
+/*
+ * Perform bitwise negation of address
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_invert(struct xaddr *n)
+{
+	int i;
+
+	if (n == NULL)
+		return (-1);
+
+	switch (n->af) {
+	case AF_INET:
+		n->v4.s_addr = ~n->v4.s_addr;
+		return (0);
+	case AF_INET6:
+		for (i = 0; i < 4; i++)
+			n->addr32[i] = ~n->addr32[i];
+		return (0);
+	default:
+		return (-1);
+	}
+}
+
+/*
+ * Calculate a netmask of length 'l' for address family 'af' and
+ * store it in 'n'.
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_hostmask(int af, u_int l, struct xaddr *n)
+{
+	if (addr_netmask(af, l, n) == -1 || addr_invert(n) == -1)
+		return (-1);
+	return (0);
+}
+
+/*
+ * Test whether address 'a' is all zeros (i.e. 0.0.0.0 or ::)
+ * Returns 0 on if address is all-zeros, -1 if not all zeros or on failure.
+ */
+static int
+addr_is_all0s(const struct xaddr *a)
+{
+	int i;
+
+	switch (a->af) {
+	case AF_INET:
+		return (a->v4.s_addr == 0 ? 0 : -1);
+	case AF_INET6:;
+		for (i = 0; i < 4; i++)
+			if (a->addr32[i] != 0)
+				return (-1);
+		return (0);
+	default:
+		return (-1);
+	}
+}
+
+/*
+ * Test whether host portion of address 'a', as determined by 'masklen'
+ * is all zeros.
+ * Returns 0 on if host portion of address is all-zeros,
+ * -1 if not all zeros or on failure.
+ */
+static int
+addr_host_is_all0s(const struct xaddr *a, u_int masklen)
+{
+	struct xaddr tmp_addr, tmp_mask, tmp_result;
+
+	memcpy(&tmp_addr, a, sizeof(tmp_addr));
+	if (addr_hostmask(a->af, masklen, &tmp_mask) == -1)
+		return (-1);
+	if (addr_and(&tmp_result, &tmp_addr, &tmp_mask) == -1)
+		return (-1);
+	return (addr_is_all0s(&tmp_result));
+}
+
+/*
+ * Parse a CIDR address (x.x.x.x/y or xxxx:yyyy::/z).
+ * Return -1 on parse error, -2 on inconsistency or 0 on success.
+ */
+static int
+addr_pton_cidr(const char *p, struct xaddr *n, u_int *l)
+{
+	struct xaddr tmp;
+	long unsigned int masklen = 999;
+	char addrbuf[64], *mp, *cp;
+
+	/* Don't modify argument */
+	if (p == NULL || strlcpy(addrbuf, p, sizeof(addrbuf)) > sizeof(addrbuf))
+		return -1;
+
+	if ((mp = strchr(addrbuf, '/')) != NULL) {
+		*mp = '\0';
+		mp++;
+		masklen = strtoul(mp, &cp, 10);
+		if (*mp == '\0' || *cp != '\0' || masklen > 128)
+			return -1;
+	}
+
+	if (addr_pton(addrbuf, &tmp) == -1)
+		return -1;
+
+	if (mp == NULL)
+		masklen = addr_unicast_masklen(tmp.af);
+	if (masklen_valid(tmp.af, masklen) == -1)
+		return -2;
+	if (addr_host_is_all0s(&tmp, masklen) != 0)
+		return -2;
+
+	if (n != NULL)
+		memcpy(n, &tmp, sizeof(*n));
+	if (l != NULL)
+		*l = masklen;
+
+	return 0;
+}
+
+static int
+addr_netmatch(const struct xaddr *host, const struct xaddr *net, u_int masklen)
+{
+	struct xaddr tmp_mask, tmp_result;
+
+	if (host->af != net->af)
+		return -1;
+
+	if (addr_netmask(host->af, masklen, &tmp_mask) == -1)
+		return -1;
+	if (addr_and(&tmp_result, host, &tmp_mask) == -1)
+		return -1;
+	return addr_cmp(&tmp_result, net);
+}
+
+/*
+ * Match "addr" against list pattern list "_list", which may contain a
+ * mix of CIDR addresses and old-school wildcards.
+ *
+ * If addr is NULL, then no matching is performed, but _list is parsed
+ * and checked for well-formedness.
+ *
+ * Returns 1 on match found (never returned when addr == NULL).
+ * Returns 0 on if no match found, or no errors found when addr == NULL.
+ * Returns -1 on negated match found (never returned when addr == NULL).
+ * Returns -2 on invalid list entry.
+ */
+int
+addr_match_list(const char *addr, const char *_list)
+{
+	char *list, *cp, *o;
+	struct xaddr try_addr, match_addr;
+	u_int masklen, neg;
+	int ret = 0, r;
+
+	if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
+		debug2("%s: couldn't parse address %.100s", __func__, addr);
+		return 0;
+	}
+	if ((o = list = strdup(_list)) == NULL)
+		return -1;
+	while ((cp = strsep(&list, ",")) != NULL) {
+		neg = *cp == '!';
+		if (neg)
+			cp++;
+		if (*cp == '\0') {
+			ret = -2;
+			break;
+		}
+		/* Prefer CIDR address matching */
+		r = addr_pton_cidr(cp, &match_addr, &masklen);
+		if (r == -2) {
+			error("Inconsistent mask length for "
+			    "network \"%.100s\"", cp);
+			ret = -2;
+			break;
+		} else if (r == 0) {
+			if (addr != NULL && addr_netmatch(&try_addr,
+                           &match_addr, masklen) == 0) {
+ foundit:
+				if (neg) {
+					ret = -1;
+					break;
+				}
+				ret = 1;
+			}
+			continue;
+		} else {
+			/* If CIDR parse failed, try wildcard string match */
+			if (addr != NULL && match_pattern(addr, cp) == 1)
+				goto foundit;
+		}
+	}
+	xfree(o);
+
+	return ret;
+}
--- a/usr/src/cmd/ssh/libssh/common/uidswap.c	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/libssh/common/uidswap.c	Wed Nov 11 16:01:18 2009 -0800
@@ -22,6 +22,7 @@
 
 #include "log.h"
 #include "uidswap.h"
+#include "buffer.h"
 #include "servconf.h"
 
 /*
--- a/usr/src/cmd/ssh/sshd/auth-pam.c	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/sshd/auth-pam.c	Wed Nov 11 16:01:18 2009 -0800
@@ -22,7 +22,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -34,6 +34,7 @@
 #include "auth.h"
 #include "auth-options.h"
 #include "auth-pam.h"
+#include "buffer.h"
 #include "servconf.h"
 #include "canohost.h"
 #include "compat.h"
--- a/usr/src/cmd/ssh/sshd/auth.c	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/sshd/auth.c	Wed Nov 11 16:01:18 2009 -0800
@@ -22,7 +22,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -44,11 +44,11 @@
 #include "match.h"
 #include "groupaccess.h"
 #include "log.h"
+#include "buffer.h"
 #include "servconf.h"
 #include "auth.h"
 #include "auth-options.h"
 #include "canohost.h"
-#include "buffer.h"
 #include "bufaux.h"
 #include "uidswap.h"
 #include "tildexpand.h"
@@ -582,6 +582,9 @@
 	if (user == NULL || *user == '\0')
 		return (NULL); /* implicit user, will be set later */
 
+	parse_server_match_config(&options, user,
+	    get_canonical_hostname(options.verify_reverse_mapping), get_remote_ipaddr());
+
 	pw = getpwnam(user);
 	if (pw == NULL) {
 		log("Illegal user %.100s from %.100s",
--- a/usr/src/cmd/ssh/sshd/auth2-gss.c	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/sshd/auth2-gss.c	Wed Nov 11 16:01:18 2009 -0800
@@ -22,23 +22,21 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #include "includes.h"
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef GSSAPI
 #include "auth.h"
 #include "ssh2.h"
 #include "xmalloc.h"
 #include "log.h"
 #include "dispatch.h"
+#include "buffer.h"
 #include "servconf.h"
 #include "compat.h"
-#include "buffer.h"
 #include "bufaux.h"
 #include "packet.h"
 
--- a/usr/src/cmd/ssh/sshd/groupaccess.c	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/sshd/groupaccess.c	Wed Nov 11 16:01:18 2009 -0800
@@ -25,8 +25,6 @@
 #include "includes.h"
 RCSID("$OpenBSD: groupaccess.c,v 1.5 2002/03/04 17:27:39 stevesk Exp $");
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include "groupaccess.h"
 #include "xmalloc.h"
 #include "match.h"
@@ -75,6 +73,30 @@
 }
 
 /*
+ * Return 1 if one of user's groups matches group_pattern list.
+ * Return 0 on negated or no match.
+ */
+int
+ga_match_pattern_list(const char *group_pattern)
+{
+	int i, found = 0;
+	size_t len = strlen(group_pattern);
+
+	for (i = 0; i < ngroups; i++) {
+		switch (match_pattern_list(groups_byname[i],
+		    group_pattern, len, 0)) {
+		case -1:
+			return 0;	/* Negated match wins */
+		case 0:
+			continue;
+		case 1:
+			found = 1;
+		}
+	}
+	return found;
+}
+
+/*
  * Free memory allocated for group access list.
  */
 void
--- a/usr/src/cmd/ssh/sshd/servconf.c	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/sshd/servconf.c	Wed Nov 11 16:01:18 2009 -0800
@@ -38,6 +38,7 @@
 
 #include "ssh.h"
 #include "log.h"
+#include "buffer.h"
 #include "servconf.h"
 #include "xmalloc.h"
 #include "compat.h"
@@ -48,10 +49,14 @@
 #include "kex.h"
 #include "mac.h"
 #include "auth.h"
+#include "match.h"
+#include "groupaccess.h"
 
 static void add_listen_addr(ServerOptions *, char *, u_short);
 static void add_one_listen_addr(ServerOptions *, char *, u_short);
 
+extern Buffer cfg;
+
 /* AF_UNSPEC or AF_INET or AF_INET6 */
 extern int IPv4or6;
 
@@ -415,103 +420,111 @@
 	sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
 	sMaxAuthTries, sMaxAuthTriesLog, sUsePrivilegeSeparation,
 	sLookupClientHostnames, sUseOpenSSLEngine, sChrootDirectory,
+	sMatch,
 	sDeprecated
 } 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)
+
 /* Textual representation of the tokens. */
 static struct {
 	const char *name;
 	ServerOpCodes opcode;
+	u_int flags;
 } keywords[] = {
 	/* Portable-specific options */
-	{ "PAMAuthenticationViaKbdInt", sPAMAuthenticationViaKbdInt },
+	{ "PAMAuthenticationViaKbdInt", sPAMAuthenticationViaKbdInt, 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", sRhostsAuthentication },
-	{ "rhostsrsaauthentication", sRhostsRSAAuthentication },
-	{ "hostbasedauthentication", sHostbasedAuthentication },
+	{ "port", sPort, SSHCFG_GLOBAL },
+	{ "hostkey", sHostKeyFile, SSHCFG_GLOBAL },
+	{ "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL },			/* alias */
+	{ "pidfile", sPidFile, SSHCFG_GLOBAL },
+	{ "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL },
+	{ "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL },
+	{ "keyregenerationinterval", sKeyRegenerationTime, SSHCFG_GLOBAL },
+	{ "permitrootlogin", sPermitRootLogin, SSHCFG_ALL },
+	{ "syslogfacility", sLogFacility, SSHCFG_GLOBAL },
+	{ "loglevel", sLogLevel, SSHCFG_GLOBAL },
+	{ "rhostsauthentication", sRhostsAuthentication, SSHCFG_GLOBAL },
+	{ "rhostsrsaauthentication", sRhostsRSAAuthentication, SSHCFG_ALL },
+	{ "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL },
 	{ "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly },
-	{ "rsaauthentication", sRSAAuthentication },
-	{ "pubkeyauthentication", sPubkeyAuthentication },
-	{ "dsaauthentication", sPubkeyAuthentication },			/* alias */
+	{ "rsaauthentication", sRSAAuthentication, SSHCFG_ALL },
+	{ "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL },
+	{ "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL },	/* alias */
 #ifdef GSSAPI
-	{ "gssapiauthentication", sGssAuthentication },
-	{ "gssapikeyexchange", sGssKeyEx },
-	{ "gssapistoredelegatedcredentials", sGssStoreDelegCreds },
-	{ "gssauthentication", sGssAuthentication },			/* alias */
-	{ "gsskeyex", sGssKeyEx },					/* alias */
-	{ "gssstoredelegcreds", sGssStoreDelegCreds },			/* alias */
+	{ "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
+	{ "gssapikeyexchange", sGssKeyEx,   SSHCFG_GLOBAL },
+	{ "gssapistoredelegatedcredentials", sGssStoreDelegCreds, SSHCFG_GLOBAL },
+	{ "gssauthentication", sGssAuthentication, SSHCFG_GLOBAL },	/* alias */
+	{ "gsskeyex", sGssKeyEx, SSHCFG_GLOBAL },	/* alias */
+	{ "gssstoredelegcreds", sGssStoreDelegCreds, SSHCFG_GLOBAL },	/* alias */
 #ifndef SUNW_GSSAPI
-	{ "gssusesessionccache", sGssUseSessionCredCache },
-	{ "gssusesessioncredcache", sGssUseSessionCredCache },
-	{ "gsscleanupcreds", sGssCleanupCreds },
+	{ "gssusesessionccache", sGssUseSessionCredCache, SSHCFG_GLOBAL },
+	{ "gssusesessioncredcache", sGssUseSessionCredCache, SSHCFG_GLOBAL },
+	{ "gsscleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
 #endif /* SUNW_GSSAPI */
 #endif
 #if defined(KRB4) || defined(KRB5)
-	{ "kerberosauthentication", sKerberosAuthentication },
-	{ "kerberosorlocalpasswd", sKerberosOrLocalPasswd },
-	{ "kerberosticketcleanup", sKerberosTicketCleanup },
+	{ "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL },
+	{ "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_GLOBAL },
+	{ "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_GLOBAL },
 #endif
 #if defined(AFS) || defined(KRB5)
-	{ "kerberostgtpassing", sKerberosTgtPassing },
+	{ "kerberostgtpassing", sKerberosTgtPassing, SSHCFG_GLOBAL },
 #endif
 #ifdef AFS
-	{ "afstokenpassing", sAFSTokenPassing },
+	{ "afstokenpassing", sAFSTokenPassing, SSHCFG_GLOBAL },
 #endif
-	{ "passwordauthentication", sPasswordAuthentication },
-	{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication },
-	{ "challengeresponseauthentication", sChallengeResponseAuthentication },
-	{ "skeyauthentication", sChallengeResponseAuthentication }, /* alias */
-	{ "checkmail", sDeprecated },
-	{ "listenaddress", sListenAddress },
-	{ "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 },
-	{ "keepalive", sKeepAlives },
-	{ "allowtcpforwarding", sAllowTcpForwarding },
-	{ "allowusers", sAllowUsers },
-	{ "denyusers", sDenyUsers },
-	{ "allowgroups", sAllowGroups },
-	{ "denygroups", sDenyGroups },
-	{ "ciphers", sCiphers },
-	{ "macs", sMacs },
-	{ "protocol", sProtocol },
-	{ "gatewayports", sGatewayPorts },
-	{ "subsystem", sSubsystem },
-	{ "maxstartups", sMaxStartups },
-	{ "banner", sBanner },
-	{ "verifyreversemapping", sVerifyReverseMapping },
-	{ "reversemappingcheck", sVerifyReverseMapping },
-	{ "clientaliveinterval", sClientAliveInterval },
-	{ "clientalivecountmax", sClientAliveCountMax },
-	{ "authorizedkeysfile", sAuthorizedKeysFile },
-	{ "authorizedkeysfile2", sAuthorizedKeysFile2 },
-	{ "maxauthtries", sMaxAuthTries },
-	{ "maxauthtrieslog", sMaxAuthTriesLog },
-	{ "useprivilegeseparation", sUsePrivilegeSeparation},
-	{ "lookupclienthostnames", sLookupClientHostnames},
-	{ "useopensslengine", sUseOpenSSLEngine},
-	{ "chrootdirectory", sChrootDirectory},
-	{ NULL, sBadOption }
+	{ "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
+	{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
+	{ "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
+	{ "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */
+	{ "checkmail", sDeprecated, SSHCFG_GLOBAL },
+	{ "listenaddress", sListenAddress, SSHCFG_GLOBAL },
+	{ "printmotd", sPrintMotd, SSHCFG_GLOBAL },
+	{ "printlastlog", sPrintLastLog, SSHCFG_GLOBAL },
+	{ "ignorerhosts", sIgnoreRhosts, SSHCFG_GLOBAL },
+	{ "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL },
+	{ "x11forwarding", sX11Forwarding, SSHCFG_ALL },
+	{ "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL },
+	{ "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL },
+	{ "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL },
+	{ "strictmodes", sStrictModes, SSHCFG_GLOBAL },
+	{ "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL },
+	{ "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL },
+	{ "uselogin", sUseLogin, SSHCFG_GLOBAL },
+	{ "compression", sCompression, SSHCFG_GLOBAL },
+	{ "keepalive", sKeepAlives, SSHCFG_GLOBAL },
+	{ "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_GLOBAL},
+	{ "maxstartups", sMaxStartups, SSHCFG_GLOBAL },
+	{ "banner", sBanner, SSHCFG_ALL },
+	{ "verifyreversemapping", sVerifyReverseMapping, SSHCFG_GLOBAL },
+	{ "reversemappingcheck", sVerifyReverseMapping,SSHCFG_GLOBAL },
+	{ "clientaliveinterval", sClientAliveInterval, SSHCFG_GLOBAL },
+	{ "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL },
+	{ "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_GLOBAL },
+	{ "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL },
+	{ "maxauthtries", sMaxAuthTries, SSHCFG_ALL },
+	{ "maxauthtrieslog", sMaxAuthTriesLog, SSHCFG_GLOBAL },
+	{ "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL },
+	{ "lookupclienthostnames", sLookupClientHostnames, SSHCFG_GLOBAL },
+	{ "useopensslengine", sUseOpenSSLEngine, SSHCFG_GLOBAL },
+	{ "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
+	{ "match", sMatch, SSHCFG_ALL },
+
+	{ NULL, sBadOption, 0 }
 };
 
 /*
@@ -520,13 +533,15 @@
 
 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);
@@ -569,13 +584,150 @@
 	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
+ */
+
+static int
+match_cfg_line_group(const char *grps, int line, const char *user)
+{
+	int result = 0;
+	struct passwd *pw;
+
+	if (user == NULL)
+		goto out;
+
+	if ((pw = getpwnam(user)) == NULL) {
+		debug("Can't match group at line %d because user %.100s does "
+		    "not exist", line, user);
+	} 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);
+	} else if (ga_match_pattern_list(grps) != 1) {
+		debug("user %.100s does not match group list %.100s at line %d",
+		    user, grps, line);
+	} else {
+		debug("user %.100s matched group list %.100s at line %d", user,
+		    grps, line);
+		result = 1;
+	}
+out:
+	ga_free();
+	return result;
+}
+
+static 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;
+	size_t len;
+
+	if (user == 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)");
+
+	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)
+				result = 0;
+			else
+				debug("user %.100s matched 'User %.100s' at "
+				    "line %d", user, arg, line);
+		} else if (strcasecmp(attrib, "group") == 0) {
+			switch (match_cfg_line_group(arg, line, user)) {
+			case -1:
+				return -1;
+			case 0:
+				result = 0;
+			}
+		} 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) {
+			switch (addr_match_list(address, arg)) {
+			case 1:
+				debug("connection from %.100s matched 'Address "
+				    "%.100s' at line %d", address, arg, line);
+				break;
+			case 0:
+			case -1:
+				result = 0;
+				break;
+			case -2:
+				return -1;
+			}
+		} else {
+			error("Unsupported Match attribute %s", attrib);
+			return -1;
+		}
+	}
+	if (user != NULL)
+		debug3("match %sfound", result ? "" : "not ");
+	*condition = cp;
+	return result;
+}
+
+#define WHITESPACE " \t\r\n"
+
 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, i, n;
+	int cmdline = 0, *intptr, value, n;
 	ServerOpCodes opcode;
+	u_int i, flags = 0;
 	size_t len;
 
 	cp = line;
@@ -587,7 +739,25 @@
 		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;
+	}
+	if (*activep && opcode != sMatch)
+		debug3("%s:%d setting %s %s", filename, linenum, arg, cp);
+	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 sPAMAuthenticationViaKbdInt:
@@ -625,7 +795,7 @@
 			fatal("%s line %d: missing integer value.",
 			    filename, linenum);
 		value = atoi(arg);
-		if (*intptr == -1)
+		if (*activep && *intptr == -1)
 			*intptr = value;
 		break;
 
@@ -695,7 +865,7 @@
 		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)
@@ -727,7 +897,7 @@
 			fatal("%s line %d: Bad yes/"
 			    "without-password/forced-commands-only/no "
 			    "argument: %s", filename, linenum, arg);
-		if (*intptr == -1)
+		if (*activep && *intptr == -1)
 			*intptr = value;
 		break;
 
@@ -746,7 +916,7 @@
 		else
 			fatal("%s line %d: Bad yes/no argument: %s",
 				filename, linenum, arg);
-		if (*intptr == -1)
+		if (*activep && *intptr == -1)
 			*intptr = value;
 		break;
 
@@ -884,16 +1054,23 @@
 		goto parse_flag;
 
 	case sGatewayPorts:
+		intptr = &options->gateway_ports;
 		arg = strdelim(&cp);
-		if (get_yes_no_flag(&options->gateway_ports, arg, filename,
-		    linenum, 1) == 1)
-			break;
-
+		if (!arg || *arg == '\0')
+			fatal("%s line %d: missing yes/no/clientspecified "
+			    "argument.", filename, linenum);
+		value = 0;	/* silence compiler */
 		if (strcmp(arg, "clientspecified") == 0)
-			options->gateway_ports = 2;
+			value = 2;
+		else if (strcmp(arg, "yes") == 0)
+			value = 1;
+		else if (strcmp(arg, "no") == 0)
+			value = 0;
 		else
-			fatal("%.200s line %d: Bad yes/no/clientspecified "
-			    "argument.", filename, linenum);
+			fatal("%s line %d: Bad yes/no/clientspecified "
+			    "argument: %s", filename, linenum, arg);
+		if (*activep && *intptr == -1)
+			*intptr = value;
 		break;
 
 	case sVerifyReverseMapping:
@@ -1016,6 +1193,10 @@
 		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.",
@@ -1113,10 +1294,21 @@
 		if (arg == NULL || *arg == '\0')
 			fatal("%s line %d: missing directory name for "
 			    "ChrootDirectory.", filename, linenum);
-		if (*charptr == NULL)
+		if (*activep && *charptr == NULL)
 			*charptr = xstrdup(arg);
 		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:
 		log("%s line %d: Deprecated option %s",
 		    filename, linenum, arg);
@@ -1134,33 +1326,122 @@
 	return 0;
 }
 
+
 /* Reads the server configuration file. */
 
 void
-read_server_config(ServerOptions *options, const char *filename)
+load_server_config(const char *filename, Buffer *conf)
 {
-	int linenum, bad_options = 0;
-	char line[1024];
+	char line[1024], *cp;
 	FILE *f;
 
-	f = fopen(filename, "r");
-	if (!f) {
+	debug2("%s: filename %s", __func__, filename);
+	if ((f = fopen(filename, "r")) == NULL) {
 		perror(filename);
 		exit(1);
 	}
-	linenum = 0;
+	buffer_clear(conf);
 	while (fgets(line, sizeof(line), f)) {
-		/* Update line number counter. */
-		linenum++;
-		if (process_server_config_line(options, line, filename, linenum) != 0)
+		/*
+		 * Trim out comments and strip whitespace
+		 * NB - preserve newlines, they are needed to reproduce
+		 * line numbers later for error messages
+		 */
+		if ((cp = strchr(line, '#')) != NULL)
+			memcpy(cp, "\n", 2);
+		cp = line + strspn(line, " \t\r");
+
+		buffer_append(conf, cp, strlen(cp));
+	}
+	buffer_append(conf, "\0", 1);
+	fclose(f);
+	debug2("%s: done config len = %d", __func__, buffer_len(conf));
+}
+
+void
+parse_server_match_config(ServerOptions *options, const char *user,
+    const char *host, const char *address)
+{
+	ServerOptions mo;
+
+	initialize_server_options(&mo);
+	parse_server_config(&mo, "reprocess config", &cfg, user, host, address);
+	copy_set_server_options(options, &mo, 0);
+}
+
+
+
+/* Helper macros */
+#define M_CP_INTOPT(n) do {\
+	if (src->n != -1) \
+		dst->n = src->n; \
+} while (0)
+#define M_CP_STROPT(n) do {\
+	if (src->n != NULL) { \
+		if (dst->n != NULL) \
+			xfree(dst->n); \
+		dst->n = src->n; \
+	} \
+} while(0)
+
+/*
+ * Copy any supported values that are set.
+ *
+ * If the preauth flag is set, we do not bother copying the the string or
+ * array values that are not used pre-authentication, because any that we
+ * do use must be explictly sent in mm_getpwnamallow().
+ */
+void
+copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
+{
+	M_CP_INTOPT(password_authentication);
+	M_CP_INTOPT(gss_authentication);
+	M_CP_INTOPT(rsa_authentication);
+	M_CP_INTOPT(pubkey_authentication);
+	M_CP_INTOPT(hostbased_authentication);
+	M_CP_INTOPT(kbd_interactive_authentication);
+	M_CP_INTOPT(permit_root_login);
+	M_CP_INTOPT(permit_empty_passwd);
+	M_CP_INTOPT(allow_tcp_forwarding);
+	M_CP_INTOPT(gateway_ports);
+	M_CP_INTOPT(x11_display_offset);
+	M_CP_INTOPT(x11_forwarding);
+	M_CP_INTOPT(x11_use_localhost);
+	M_CP_INTOPT(max_auth_tries);
+	M_CP_STROPT(banner);
+
+	if (preauth)
+		return;
+	M_CP_STROPT(chroot_directory);
+}
+
+#undef M_CP_INTOPT
+#undef M_CP_STROPT
+
+void
+parse_server_config(ServerOptions *options, const char *filename, Buffer *conf,
+    const char *user, const char *host, const char *address)
+{
+	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++, &active, user, host, address) != 0)
 			bad_options++;
 	}
-	(void) fclose(f);
+	xfree(obuf);
 	if (bad_options > 0)
 		fatal("%s: terminating, %d bad configuration options",
 		    filename, bad_options);
 }
 
+
 /*
  * Note that "none" is a special path having the same affect on sshd
  * configuration as not specifying ChrootDirectory at all.
--- a/usr/src/cmd/ssh/sshd/sshd.c	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/sshd/sshd.c	Wed Nov 11 16:01:18 2009 -0800
@@ -223,6 +223,9 @@
 static int *startup_pipes = NULL;
 static int startup_pipe = -1;	/* in child */
 
+/* sshd_config buffer */
+Buffer cfg;
+
 #ifdef GSSAPI
 static gss_OID_set mechs = GSS_C_NULL_OID_SET;
 #endif /* GSSAPI */
@@ -939,7 +942,7 @@
 			break;
 		case 'o':
 			if (process_server_config_line(&options, optarg,
-			    "command-line", 0) != 0)
+			    "command-line", 0, NULL, NULL, NULL, NULL) != 0)
 				exit(1);
 			break;
 		case '?':
@@ -974,8 +977,10 @@
 	drop_cray_privs();
 #endif
 
-	/* Read server configuration options from the configuration file. */
-	read_server_config(&options, config_file_name);
+	/* Fetch our configuration */
+	buffer_init(&cfg);
+	load_server_config(config_file_name, &cfg);
+	parse_server_config(&options, config_file_name, &cfg, NULL, NULL, NULL);
 
 	/* Fill in default values for those options not explicitly set. */
 	fill_default_server_options(&options);
--- a/usr/src/cmd/ssh/sshd/sshlogin.c	Wed Nov 11 14:54:55 2009 -0800
+++ b/usr/src/cmd/ssh/sshd/sshlogin.c	Wed Nov 11 16:01:18 2009 -0800
@@ -47,6 +47,7 @@
 
 #include "loginrec.h"
 #include "log.h"
+#include "buffer.h"
 #include "servconf.h"
 #include "canohost.h"
 #include "packet.h"