From 2c69fa0c388ccff1048749be06c7c522f23d4659 Mon Sep 17 00:00:00 2001
From: Magnus Hagander <magnus@hagander.net>
Date: Tue, 2 Dec 2008 10:39:31 +0000
Subject: [PATCH] Change wildcard cerfificate mapping to be much simler - we
 now only match the * character at the beginning of a pattern, and it does not
 match subdomains.

Since this means we no longer need fnmatch, remove the imported implementation
from port, along with the autoconf check for it.
---
 configure                        |  92 --------------
 configure.in                     |   7 +-
 src/include/fnmatchstub.h        |  27 -----
 src/include/pg_config.h.in       |   3 -
 src/interfaces/libpq/Makefile    |   8 +-
 src/interfaces/libpq/fe-secure.c |  60 ++++++++--
 src/port/fnmatch.c               | 198 -------------------------------
 src/tools/msvc/Mkvcbuild.pm      |   4 +-
 8 files changed, 55 insertions(+), 344 deletions(-)
 delete mode 100644 src/include/fnmatchstub.h
 delete mode 100644 src/port/fnmatch.c

diff --git a/configure b/configure
index 89de116af89..a91959c3270 100755
--- a/configure
+++ b/configure
@@ -24822,98 +24822,6 @@ esac
 
 
 
-# Check for fnmatch()
-{ echo "$as_me:$LINENO: checking for working POSIX fnmatch" >&5
-echo $ECHO_N "checking for working POSIX fnmatch... $ECHO_C" >&6; }
-if test "${ac_cv_func_fnmatch_works+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  # Some versions of Solaris, SCO, and the GNU C Library
-   # have a broken or incompatible fnmatch.
-   # So we run a test program.  If we are cross-compiling, take no chance.
-   # Thanks to John Oleynick, Franc,ois Pinard, and Paul Eggert for this test.
-   if test "$cross_compiling" = yes; then
-  ac_cv_func_fnmatch_works=cross
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <fnmatch.h>
-#	   define y(a, b, c) (fnmatch (a, b, c) == 0)
-#	   define n(a, b, c) (fnmatch (a, b, c) == FNM_NOMATCH)
-
-int
-main ()
-{
-return
-	   (!(y ("a*", "abc", 0)
-	      && n ("d*/*1", "d/s/1", FNM_PATHNAME)
-	      && y ("a\\\\bc", "abc", 0)
-	      && n ("a\\\\bc", "abc", FNM_NOESCAPE)
-	      && y ("*x", ".x", 0)
-	      && n ("*x", ".x", FNM_PERIOD)
-	      && 1));
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_link") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
-  { (case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_try") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_cv_func_fnmatch_works=yes
-else
-  echo "$as_me: program exited with status $ac_status" >&5
-echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-( exit $ac_status )
-ac_cv_func_fnmatch_works=no
-fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
-fi
-
-
-fi
-{ echo "$as_me:$LINENO: result: $ac_cv_func_fnmatch_works" >&5
-echo "${ECHO_T}$ac_cv_func_fnmatch_works" >&6; }
-if test $ac_cv_func_fnmatch_works = yes; then
-
-cat >>confdefs.h <<\_ACEOF
-#define HAVE_FNMATCH 1
-_ACEOF
-
-fi
-
-
-
-if test x"$ac_cv_func_fnmatch_works" != x"yes"; then
-   case " $LIBOBJS " in
-  *" fnmatch.$ac_objext "* ) ;;
-  *) LIBOBJS="$LIBOBJS fnmatch.$ac_objext"
- ;;
-esac
-
-fi
 
 # Select semaphore implementation type.
 if test "$PORTNAME" != "win32"; then
diff --git a/configure.in b/configure.in
index e006a97a5b4..9e4c2caf2ef 100644
--- a/configure.in
+++ b/configure.in
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-dnl $PostgreSQL: pgsql/configure.in,v 1.574 2008/11/26 11:26:54 petere Exp $
+dnl $PostgreSQL: pgsql/configure.in,v 1.575 2008/12/02 10:39:30 mha Exp $
 dnl
 dnl Developers, please strive to achieve this order:
 dnl
@@ -1625,11 +1625,6 @@ fi
 # SunOS doesn't handle negative byte comparisons properly with +/- return
 AC_FUNC_MEMCMP
 
-# Check for fnmatch()
-AC_FUNC_FNMATCH
-if test x"$ac_cv_func_fnmatch_works" != x"yes"; then
-   AC_LIBOBJ(fnmatch)
-fi
 
 # Select semaphore implementation type.
 if test "$PORTNAME" != "win32"; then
diff --git a/src/include/fnmatchstub.h b/src/include/fnmatchstub.h
deleted file mode 100644
index fb23d8f98ce..00000000000
--- a/src/include/fnmatchstub.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fnmatchstub.h
- *	  Stubs for fnmatch() in port/fnmatch.c
- *
- *
- * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $PostgreSQL: pgsql/src/include/fnmatchstub.h,v 1.1 2008/11/24 09:15:16 mha Exp $
- *
- *-------------------------------------------------------------------------
- */
-#ifndef FNMATCHSTUB_H
-#define FNMATCHSTUB_H
-
-extern int fnmatch(const char *, const char *, int);
-#define FNM_NOMATCH		1		/* Match failed. */
-#define FNM_NOSYS		2		/* Function not implemented. */
-#define FNM_NOESCAPE	0x01	/* Disable backslash escaping. */
-#define FNM_PATHNAME	0x02	/* Slash must be matched by slash. */
-#define FNM_PERIOD		0x04	/* Period must be matched by period. */
-#define FNM_CASEFOLD	0x08	/* Pattern is matched case-insensitive */
-#define FNM_LEADING_DIR	0x10	/* Ignore /<tail> after Imatch. */
-
-
-#endif
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 1064d155260..9f6f21bf817 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -143,9 +143,6 @@
 /* Define to 1 if you have the `fdatasync' function. */
 #undef HAVE_FDATASYNC
 
-/* Define to 1 if your system has a working POSIX `fnmatch' function. */
-#undef HAVE_FNMATCH
-
 /* Define to 1 if you have the `fpclass' function. */
 #undef HAVE_FPCLASS
 
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 82a7fc26c17..5a899d27894 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -5,7 +5,7 @@
 # Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
 # Portions Copyright (c) 1994, Regents of the University of California
 #
-# $PostgreSQL: pgsql/src/interfaces/libpq/Makefile,v 1.169 2008/11/24 09:15:16 mha Exp $
+# $PostgreSQL: pgsql/src/interfaces/libpq/Makefile,v 1.170 2008/12/02 10:39:30 mha Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -34,7 +34,7 @@ OBJS=	fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
 	fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
 	libpq-events.o \
 	md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o thread.o \
-	$(filter crypt.o fnmatch.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
+	$(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
 
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
@@ -80,7 +80,7 @@ backend_src = $(top_srcdir)/src/backend
 # For port modules, this only happens if configure decides the module
 # is needed (see filter hack in OBJS, above).
 
-crypt.c fnmatch.c getaddrinfo.c inet_aton.c noblock.c open.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c win32error.c pgsleep.c: % : $(top_srcdir)/src/port/%
+crypt.c getaddrinfo.c inet_aton.c noblock.c open.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c win32error.c pgsleep.c: % : $(top_srcdir)/src/port/%
 	rm -f $@ && $(LN_S) $< .
 
 md5.c ip.c: % : $(backend_src)/libpq/%
@@ -123,7 +123,7 @@ uninstall: uninstall-lib
 	rm -f '$(DESTDIR)$(datadir)/pg_service.conf.sample'
 
 clean distclean: clean-lib
-	rm -f $(OBJS) pg_config_paths.h crypt.c fnmatch.c getaddrinfo.c inet_aton.c noblock.c open.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c md5.c ip.c encnames.c wchar.c win32error.c pgsleep.c pthread.h libpq.rc
+	rm -f $(OBJS) pg_config_paths.h crypt.c getaddrinfo.c inet_aton.c noblock.c open.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c md5.c ip.c encnames.c wchar.c win32error.c pgsleep.c pthread.h libpq.rc
 # Might be left over from a Win32 client-only build
 	rm -f pg_config_paths.h
 
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 9f6781be476..5d1747821bf 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.109 2008/11/24 19:19:46 mha Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.110 2008/12/02 10:39:30 mha Exp $
  *
  * NOTES
  *
@@ -55,6 +55,7 @@
 #endif
 
 #ifdef USE_SSL
+
 #include <openssl/ssl.h>
 #include <openssl/bio.h>
 #if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
@@ -64,16 +65,6 @@
 #include <openssl/engine.h>
 #endif
 
-/* fnmatch() needed for client certificate checking */
-#ifdef HAVE_FNMATCH
-#include <fnmatch.h>
-#else
-#include "fnmatchstub.h"
-#endif
-#endif   /* USE_SSL */
-
-
-#ifdef USE_SSL
 
 #ifndef WIN32
 #define USER_CERT_FILE		".postgresql/postgresql.crt"
@@ -443,6 +434,51 @@ verify_cb(int ok, X509_STORE_CTX *ctx)
 	return ok;
 }
 
+
+/*
+ * Check if a wildcard certificate matches the server hostname.
+ *
+ * The rule for this is:
+ *  1. We only match the '*' character as wildcard
+ *  2. We match only wildcards at the start of the string
+ *  3. The '*' character does *not* match '.', meaning that we match only
+ *     a single pathname component.
+ *  4. We don't support more than one '*' in a single pattern.
+ *
+ * This is roughly in line with RFC2818, but contrary to what most browsers
+ * appear to be implementing (point 3 being the difference)
+ *
+ * Matching is always cone case-insensitive, since DNS is case insensitive.
+ */
+static int
+wildcard_certificate_match(const char *pattern, const char *string)
+{
+	int lenpat = strlen(pattern);
+	int lenstr = strlen(string);
+
+	/* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
+	if (lenpat < 3 ||
+		pattern[0] != '*' ||
+		pattern[1] != '.')
+		return 0;
+
+	if (lenpat > lenstr)
+		/* If pattern is longer than the string, we can never match */
+		return 0;
+
+	if (pg_strcasecmp(pattern+1, string+lenstr-lenpat+1) != 0)
+		/* If string does not end in pattern (minus the wildcard), we don't match */
+		return 0;
+
+	if (strchr(string, '.') < string+lenstr-lenpat)
+		/* If there is a dot left of where the pattern started to match, we don't match (rule 3) */
+		return 0;
+
+	/* String ended with pattern, and didn't have a dot before, so we match */
+	return 1;
+}
+
+
 /*
  *	Verify that common name resolves to peer.
  */
@@ -472,7 +508,7 @@ verify_peer_name_matches_certificate(PGconn *conn)
 		if (pg_strcasecmp(conn->peer_cn, conn->pghost) == 0)
 			/* Exact name match */
 			return true;
-		else if (fnmatch(conn->peer_cn, conn->pghost, FNM_NOESCAPE/* | FNM_CASEFOLD*/) == 0)
+		else if (wildcard_certificate_match(conn->peer_cn, conn->pghost))
 			/* Matched wildcard certificate */
 			return true;
 		else
diff --git a/src/port/fnmatch.c b/src/port/fnmatch.c
deleted file mode 100644
index c7fbc54a317..00000000000
--- a/src/port/fnmatch.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fnmatch.c
- *        fnmatch() - wildcard matching function
- *
- * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
- *
- *
- * IDENTIFICATION
- *        $PostgreSQL: pgsql/src/port/fnmatch.c,v 1.1 2008/11/24 09:15:16 mha Exp $
- *
- * This file was taken from NetBSD and is used on platforms that don't
- * provide fnmatch(). The NetBSD copyright terms follow.
- *-------------------------------------------------------------------------
- */
-
-/*	$NetBSD: fnmatch.c,v 1.21 2005/12/24 21:11:16 perry Exp $	*/
-
-/*
- * Copyright (c) 1989, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Guido van Rossum.
- *
- * 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. 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.
- */
-
-/*
- * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
- * Compares a filename or pathname to a pattern.
- */
-
-#include "c.h"
-#include "fnmatchstub.h"
-
-#define	EOS	'\0'
-
-static const char *rangematch (const char *, int, int);
-
-static inline int
-foldcase(int ch, int flags)
-{
-
-	if ((flags & FNM_CASEFOLD) != 0 && isupper(ch))
-		return (tolower(ch));
-	return (ch);
-}
-
-#define	FOLDCASE(ch, flags)	foldcase((unsigned char)(ch), (flags))
-
-int
-fnmatch(pattern, string, flags)
-	const char *pattern, *string;
-	int flags;
-{
-	const char *stringstart;
-	char c, test;
-
-	for (stringstart = string;;)
-		switch (c = FOLDCASE(*pattern++, flags)) {
-		case EOS:
-			if ((flags & FNM_LEADING_DIR) && *string == '/')
-				return (0);
-			return (*string == EOS ? 0 : FNM_NOMATCH);
-		case '?':
-			if (*string == EOS)
-				return (FNM_NOMATCH);
-			if (*string == '/' && (flags & FNM_PATHNAME))
-				return (FNM_NOMATCH);
-			if (*string == '.' && (flags & FNM_PERIOD) &&
-			    (string == stringstart ||
-			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
-				return (FNM_NOMATCH);
-			++string;
-			break;
-		case '*':
-			c = FOLDCASE(*pattern, flags);
-			/* Collapse multiple stars. */
-			while (c == '*')
-				c = FOLDCASE(*++pattern, flags);
-
-			if (*string == '.' && (flags & FNM_PERIOD) &&
-			    (string == stringstart ||
-			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
-				return (FNM_NOMATCH);
-
-			/* Optimize for pattern with * at end or before /. */
-			if (c == EOS) {
-				if (flags & FNM_PATHNAME)
-					return ((flags & FNM_LEADING_DIR) ||
-					    strchr(string, '/') == NULL ?
-					    0 : FNM_NOMATCH);
-				else
-					return (0);
-			} else if (c == '/' && flags & FNM_PATHNAME) {
-				if ((string = strchr(string, '/')) == NULL)
-					return (FNM_NOMATCH);
-				break;
-			}
-
-			/* General case, use recursion. */
-			while ((test = FOLDCASE(*string, flags)) != EOS) {
-				if (!fnmatch(pattern, string,
-					     flags & ~FNM_PERIOD))
-					return (0);
-				if (test == '/' && flags & FNM_PATHNAME)
-					break;
-				++string;
-			}
-			return (FNM_NOMATCH);
-		case '[':
-			if (*string == EOS)
-				return (FNM_NOMATCH);
-			if (*string == '/' && flags & FNM_PATHNAME)
-				return (FNM_NOMATCH);
-			if ((pattern =
-			    rangematch(pattern, FOLDCASE(*string, flags),
-				       flags)) == NULL)
-				return (FNM_NOMATCH);
-			++string;
-			break;
-		case '\\':
-			if (!(flags & FNM_NOESCAPE)) {
-				if ((c = FOLDCASE(*pattern++, flags)) == EOS) {
-					c = '\\';
-					--pattern;
-				}
-			}
-			/* FALLTHROUGH */
-		default:
-			if (c != FOLDCASE(*string++, flags))
-				return (FNM_NOMATCH);
-			break;
-		}
-	/* NOTREACHED */
-}
-
-static const char *
-rangematch(pattern, test, flags)
-	const char *pattern;
-	int test, flags;
-{
-	int negate, ok;
-	char c, c2;
-
-	/*
-	 * A bracket expression starting with an unquoted circumflex
-	 * character produces unspecified results (IEEE 1003.2-1992,
-	 * 3.13.2).  This implementation treats it like '!', for
-	 * consistency with the regular expression syntax.
-	 * J.T. Conklin (conklin@ngai.kaleida.com)
-	 */
-	if ((negate = (*pattern == '!' || *pattern == '^')) != 0)
-		++pattern;
-	
-	for (ok = 0; (c = FOLDCASE(*pattern++, flags)) != ']';) {
-		if (c == '\\' && !(flags & FNM_NOESCAPE))
-			c = FOLDCASE(*pattern++, flags);
-		if (c == EOS)
-			return (NULL);
-		if (*pattern == '-' 
-		    && (c2 = FOLDCASE(*(pattern+1), flags)) != EOS &&
-		        c2 != ']') {
-			pattern += 2;
-			if (c2 == '\\' && !(flags & FNM_NOESCAPE))
-				c2 = FOLDCASE(*pattern++, flags);
-			if (c2 == EOS)
-				return (NULL);
-			if (c <= test && test <= c2)
-				ok = 1;
-		} else if (c == test)
-			ok = 1;
-	}
-	return (ok == negate ? NULL : pattern);
-}
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 172ce6ef93a..c890bb53736 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -3,7 +3,7 @@ package Mkvcbuild;
 #
 # Package that generates build files for msvc build
 #
-# $PostgreSQL: pgsql/src/tools/msvc/Mkvcbuild.pm,v 1.33 2008/11/24 09:15:16 mha Exp $
+# $PostgreSQL: pgsql/src/tools/msvc/Mkvcbuild.pm,v 1.34 2008/12/02 10:39:31 mha Exp $
 #
 use Carp;
 use Win32;
@@ -43,7 +43,7 @@ sub mkvcbuild
     $solution = new Solution($config);
 
     our @pgportfiles = qw(
-      chklocale.c crypt.c fseeko.c fnmatch.c getrusage.c inet_aton.c random.c srandom.c
+      chklocale.c crypt.c fseeko.c getrusage.c inet_aton.c random.c srandom.c
       unsetenv.c getaddrinfo.c gettimeofday.c kill.c open.c rand.c
       snprintf.c strlcat.c strlcpy.c copydir.c dirmod.c exec.c noblock.c path.c pipe.c
       pgsleep.c pgstrcasecmp.c qsort.c qsort_arg.c sprompt.c thread.c
-- 
GitLab