- (stevesk) initial work for OpenBSD "support supplementary group in
   {Allow,Deny}Groups" patch:
   - import getgrouplist.c from OpenBSD (bsd-getgrouplist.c)
   - add bsd-getgrouplist.h
   - new files groupaccess.[ch]
   - build but don't use yet (need to merge auth.c changes)
diff --git a/ChangeLog b/ChangeLog
index 9a92eb1..072cb4c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+20010114
+ - (stevesk) initial work for OpenBSD "support supplementary group in
+   {Allow,Deny}Groups" patch:
+   - import getgrouplist.c from OpenBSD (bsd-getgrouplist.c)
+   - add bsd-getgrouplist.h
+   - new files groupaccess.[ch]
+   - build but don't use yet (need to merge auth.c changes)
+	
 20010112
  - (bal) OpenBSD Sync
    - markus@cvs.openbsd.org 2001/01/10 22:56:22
diff --git a/Makefile.in b/Makefile.in
index c22d5ef..1080f26 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -39,11 +39,11 @@
 
 LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o cli.o compat.o compress.o crc32.o cygwin_util.o deattack.o dispatch.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o rijndael.o entropy.o readpass.o rsa.o ssh-dss.o ssh-rsa.o tildexpand.o ttymodes.o uidswap.o util.o uuencode.o xmalloc.o 
 
-LIBOPENBSD_COMPAT_OBJS=bsd-arc4random.o bsd-base64.o bsd-bindresvport.o bsd-daemon.o bsd-getcwd.o bsd-inet_aton.o bsd-inet_ntoa.o bsd-misc.o bsd-mktemp.o bsd-realpath.o bsd-rresvport.o bsd-setenv.o bsd-sigaction.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bsd-strsep.o bsd-strtok.o bsd-vis.o bsd-setproctitle.o bsd-waitpid.o fake-getaddrinfo.o fake-getnameinfo.o next-posix.o
+LIBOPENBSD_COMPAT_OBJS=bsd-arc4random.o bsd-base64.o bsd-bindresvport.o bsd-daemon.o bsd-getcwd.o bsd-getgrouplist.o bsd-inet_aton.o bsd-inet_ntoa.o bsd-misc.o bsd-mktemp.o bsd-realpath.o bsd-rresvport.o bsd-setenv.o bsd-sigaction.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bsd-strsep.o bsd-strtok.o bsd-vis.o bsd-setproctitle.o bsd-waitpid.o fake-getaddrinfo.o fake-getnameinfo.o next-posix.o
 
 SSHOBJS= ssh.o sshconnect.o sshconnect1.o sshconnect2.o log-client.o readconf.o clientloop.o
 
-SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-skey.o auth2-skey.o auth-rhosts.o auth-options.o auth-krb4.o auth-pam.o auth2-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o dh.o pty.o log-server.o login.o loginrec.o servconf.o serverloop.o md5crypt.o session.o
+SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-skey.o auth2-skey.o auth-rhosts.o auth-options.o auth-krb4.o auth-pam.o auth2-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o dh.o pty.o log-server.o login.o loginrec.o servconf.o serverloop.o md5crypt.o session.o groupaccess.o
 
 TROFFMAN	= scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8
 CATMAN		= scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh-keyscan.0 ssh.0 sshd.0 sftp-server.0
diff --git a/bsd-getgrouplist.c b/bsd-getgrouplist.c
new file mode 100644
index 0000000..f7a27c3
--- /dev/null
+++ b/bsd-getgrouplist.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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"
+
+#ifndef HAVE_GETGROUPLIST
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char rcsid[] = "$OpenBSD: getgrouplist.c,v 1.7 1997/08/19 19:13:27 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * get credential
+ */
+#include <sys/types.h>
+#include <string.h>
+#include <grp.h>
+
+int
+getgrouplist(uname, agroup, groups, grpcnt)
+	const char *uname;
+	gid_t agroup;
+	register gid_t *groups;
+	int *grpcnt;
+{
+	register struct group *grp;
+	register int i, ngroups;
+	int ret, maxgroups;
+	int bail;
+
+	ret = 0;
+	ngroups = 0;
+	maxgroups = *grpcnt;
+
+	/*
+	 * install primary group
+	 */
+	if (ngroups >= maxgroups) {
+		*grpcnt = ngroups;
+		return (-1);
+	}
+	groups[ngroups++] = agroup;
+
+	/*
+	 * Scan the group file to find additional groups.
+	 */
+	setgrent();
+	while ((grp = getgrent())) {
+		if (grp->gr_gid == agroup)
+			continue;
+		for (bail = 0, i = 0; bail == 0 && i < ngroups; i++)
+			if (groups[i] == grp->gr_gid)
+				bail = 1;
+		if (bail)
+			continue;
+		for (i = 0; grp->gr_mem[i]; i++) {
+			if (!strcmp(grp->gr_mem[i], uname)) {
+				if (ngroups >= maxgroups) {
+					ret = -1;
+					goto out;
+				}
+				groups[ngroups++] = grp->gr_gid;
+				break;
+			}
+		}
+	}
+out:
+	endgrent();
+	*grpcnt = ngroups;
+	return (ret);
+}
+
+#endif /* HAVE_GETGROUPLIST */
diff --git a/bsd-getgrouplist.h b/bsd-getgrouplist.h
new file mode 100644
index 0000000..ef9e601
--- /dev/null
+++ b/bsd-getgrouplist.h
@@ -0,0 +1,14 @@
+#ifndef _BSD_GETGROUPLIST_H
+#define _BSD_GETGROUPLIST_H
+
+#include "config.h"
+
+#ifndef HAVE_GETGROUPLIST
+
+#include <grp.h>
+
+int getgrouplist(const char *, gid_t, gid_t *, int *);
+
+#endif
+
+#endif
diff --git a/configure.in b/configure.in
index 7f1d2af..1ee1885 100644
--- a/configure.in
+++ b/configure.in
@@ -316,7 +316,7 @@
 AC_CHECK_HEADERS(bstring.h endian.h floatingpoint.h getopt.h lastlog.h limits.h login.h login_cap.h maillock.h netdb.h netgroup.h netinet/in_systm.h paths.h poll.h pty.h shadow.h security/pam_appl.h sys/bitypes.h sys/bsdtty.h sys/cdefs.h sys/poll.h sys/queue.h sys/select.h sys/stat.h sys/stropts.h sys/sysmacros.h sys/time.h sys/ttcompat.h sys/un.h stddef.h time.h ttyent.h usersec.h util.h utmp.h utmpx.h vis.h)
 
 dnl    Checks for library functions.
-AC_CHECK_FUNCS(arc4random atexit b64_ntop bcopy bindresvport_af clock fchmod freeaddrinfo futimes gai_strerror getcwd getaddrinfo getnameinfo getrlimit getrusage getttyent inet_aton inet_ntoa innetgr login_getcapbool md5_crypt memmove mkdtemp on_exit openpty realpath rresvport_af setdtablesize setenv seteuid setlogin setproctitle setreuid setrlimit setsid sigaction sigvec snprintf strerror strlcat strlcpy strsep strtok_r sysconf vsnprintf vhangup vis waitpid _getpty __b64_ntop)
+AC_CHECK_FUNCS(arc4random atexit b64_ntop bcopy bindresvport_af clock fchmod freeaddrinfo futimes gai_strerror getcwd getaddrinfo getgrouplist getnameinfo getrlimit getrusage getttyent inet_aton inet_ntoa innetgr login_getcapbool md5_crypt memmove mkdtemp on_exit openpty realpath rresvport_af setdtablesize setenv seteuid setlogin setproctitle setreuid setrlimit setsid sigaction sigvec snprintf strerror strlcat strlcpy strsep strtok_r sysconf vsnprintf vhangup vis waitpid _getpty __b64_ntop)
 dnl    Checks for time functions
 AC_CHECK_FUNCS(gettimeofday time)
 dnl    Checks for libutil functions
diff --git a/groupaccess.c b/groupaccess.c
new file mode 100644
index 0000000..bf6be99
--- /dev/null
+++ b/groupaccess.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2001 Kevin Steves.  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 "groupaccess.h"
+#include "ssh.h"
+#include "xmalloc.h"
+#include "match.h"
+
+static int ngroups;
+static char *groups_byname[NGROUPS_MAX + 1];	/* +1 for base/primary group */
+
+int
+ga_init(const char *user, gid_t base)
+{
+	gid_t groups_bygid[NGROUPS_MAX + 1];
+	int i, j;
+	struct group *gr;
+
+	if (ngroups > 0)
+		ga_free();
+
+	ngroups = sizeof(groups_bygid) / sizeof(gid_t);
+	if (getgrouplist(user, base, groups_bygid, &ngroups) == -1)
+		log("getgrouplist: groups list too small");
+	for (i = 0, j = 0; i < ngroups; i++)
+		if ((gr = getgrgid(groups_bygid[i])) != NULL)
+			groups_byname[j++] = xstrdup(gr->gr_name);
+	return (ngroups = j);
+}
+
+int
+ga_match(char * const *groups, int n)
+{
+	int i, j;
+
+	for (i = 0; i < ngroups; i++)
+		for (j = 0; j < n; j++)
+			if (match_pattern(groups_byname[i], groups[j]))
+				return 1;
+	return 0;
+}
+
+void
+ga_free(void)
+{
+	int i;
+
+	if (ngroups > 0) {
+		for (i = 0; i < ngroups; i++)
+			xfree(groups_byname[i]);
+		ngroups = 0;
+	}
+}
diff --git a/groupaccess.h b/groupaccess.h
new file mode 100644
index 0000000..7e8584b
--- /dev/null
+++ b/groupaccess.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2001 Kevin Steves.  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.
+ */
+
+#ifndef GROUPACCESS_H
+#define GROUPACCESS_H
+
+#include <grp.h>
+
+/*
+ * Initialize group access list for user with primary (base) and
+ * supplementary groups.  Return the number of groups in the list.
+ */
+int ga_init(const char *user, gid_t base);
+
+/*
+ * Return 1 if one of user's groups is contained in groups.
+ * Return 0 otherwise.  Use match_pattern() for string comparison.
+ */
+int ga_match(char * const *groups, int ngroups);
+
+/*
+ * Free memory allocated for group access list.
+ */
+void ga_free(void);
+
+#endif
diff --git a/openbsd-compat.h b/openbsd-compat.h
index 1be0ccd..9213bea 100644
--- a/openbsd-compat.h
+++ b/openbsd-compat.h
@@ -24,6 +24,7 @@
 #include "bsd-vis.h"
 #include "bsd-waitpid.h"
 #include "bsd-setproctitle.h"
+#include "bsd-getgrouplist.h"
 
 /* rfc2553 socket API replacements */
 #include "fake-getaddrinfo.h"