[PATCH 2/2] Cygwin: implement case-insensitive Unicode user and group name matching

Corinna Vinschen vinschen at redhat.com
Wed Feb 20 23:41:25 AEDT 2019


The previous revert enabled case-insensitive user names again.  This
patch implements the case-insensitive user and group name matching.
To allow Unicode chars, implement the matcher using wchar_t chars in
Cygwin-specific code.  Keep the generic code changes as small as possible.
Cygwin: implement case-insensitive Unicode user and group name matching

Signed-off-by: Corinna Vinschen <vinschen at redhat.com>
---
 groupaccess.c                    |   4 +
 match.c                          |   4 +
 openbsd-compat/bsd-cygwin_util.c | 146 +++++++++++++++++++++++++++++++
 servconf.c                       |   4 +
 4 files changed, 158 insertions(+)

diff --git a/groupaccess.c b/groupaccess.c
index 9e4d25521647..43367990d8c3 100644
--- a/groupaccess.c
+++ b/groupaccess.c
@@ -103,7 +103,11 @@ ga_match_pattern_list(const char *group_pattern)
 	int i, found = 0;
 
 	for (i = 0; i < ngroups; i++) {
+#ifndef HAVE_CYGWIN
 		switch (match_pattern_list(groups_byname[i], group_pattern, 0)) {
+#else
+		switch (match_pattern_list(groups_byname[i], group_pattern, 1)) {
+#endif
 		case -1:
 			return 0;	/* Negated match wins */
 		case 0:
diff --git a/match.c b/match.c
index bb3e95f678ca..b50ae4057391 100644
--- a/match.c
+++ b/match.c
@@ -111,6 +111,8 @@ match_pattern(const char *s, const char *pattern)
 	/* NOTREACHED */
 }
 
+#ifndef HAVE_CYGWIN /* Cygwin version in openbsd-compat/bsd-cygwin_util.c */
+
 /*
  * Tries to match the string against the
  * comma-separated sequence of subpatterns (each possibly preceded by ! to
@@ -170,6 +172,8 @@ match_pattern_list(const char *string, const char *pattern, int dolower)
 	return got_positive;
 }
 
+#endif
+
 /*
  * Tries to match the host name (which must be in all lowercase) against the
  * comma-separated sequence of subpatterns (each possibly preceded by ! to
diff --git a/openbsd-compat/bsd-cygwin_util.c b/openbsd-compat/bsd-cygwin_util.c
index fb49e30f5981..f721fca9d998 100644
--- a/openbsd-compat/bsd-cygwin_util.c
+++ b/openbsd-compat/bsd-cygwin_util.c
@@ -37,6 +37,8 @@
 #include <string.h>
 #include <unistd.h>
 #include <stdarg.h>
+#include <wchar.h>
+#include <wctype.h>
 
 #include "xmalloc.h"
 
@@ -117,4 +119,148 @@ free_windows_environment(char **p)
 	free(p);
 }
 
+/*
+ * Returns true if the given string matches the pattern (which may contain ?
+ * and * as wildcards), and zero if it does not match.
+ *
+ * The Cygwin version of this function must be case-insensitive and take
+ * Unicode characters into account.
+ */
+
+static int
+__match_pattern (const wchar_t *s, const wchar_t *pattern, int caseinsensitive)
+{
+	for (;;) {
+		/* If at end of pattern, accept if also at end of string. */
+		if (!*pattern)
+			return !*s;
+
+		if (*pattern == '*') {
+			/* Skip the asterisk. */
+			pattern++;
+
+			/* If at end of pattern, accept immediately. */
+			if (!*pattern)
+				return 1;
+
+			/* If next character in pattern is known, optimize. */
+			if (*pattern != '?' && *pattern != '*') {
+				/*
+				 * Look instances of the next character in
+				 * pattern, and try to match starting from
+				 * those.
+				 */
+				for (; *s; s++)
+					if (*s == *pattern &&
+					    __match_pattern(s + 1, pattern + 1,
+							    caseinsensitive))
+						return 1;
+				/* Failed. */
+				return 0;
+			}
+			/*
+			 * Move ahead one character at a time and try to
+			 * match at each position.
+			 */
+			for (; *s; s++)
+				if (__match_pattern(s, pattern, caseinsensitive))
+					return 1;
+			/* Failed. */
+			return 0;
+		}
+		/*
+		 * There must be at least one more character in the string.
+		 * If we are at the end, fail.
+		 */
+		if (!*s)
+			return 0;
+
+		/* Check if the next character of the string is acceptable. */
+		if (*pattern != '?' && (*pattern != *s &&
+		     (!caseinsensitive || towlower(*pattern) != towlower(*s))))
+			return 0;
+
+		/* Move to the next character, both in string and in pattern. */
+		s++;
+		pattern++;
+	}
+	/* NOTREACHED */
+}
+
+static int
+_match_pattern(const char *s, const char *pattern, int caseinsensitive)
+{
+	wchar_t *ws;
+	wchar_t *wpattern;
+	size_t len;
+
+	if ((len = mbstowcs(NULL, s, 0)) < 0)
+		return 0;
+	ws = (wchar_t *) alloca((len + 1) * sizeof (wchar_t));
+	mbstowcs(ws, s, len + 1);
+	if ((len = mbstowcs(NULL, pattern, 0)) < 0)
+		return 0;
+	wpattern = (wchar_t *) alloca((len + 1) * sizeof (wchar_t));
+	mbstowcs(wpattern, pattern, len + 1);
+	return __match_pattern (ws, wpattern, caseinsensitive);
+}
+
+/*
+ * Tries to match the string against the
+ * comma-separated sequence of subpatterns (each possibly preceded by ! to
+ * indicate negation).  Returns -1 if negation matches, 1 if there is
+ * a positive match, 0 if there is no match at all.
+ */
+int
+match_pattern_list(const char *string, const char *pattern, int caseinsensitive)
+{
+	char sub[1024];
+	int negated;
+	int got_positive;
+	u_int i, subi, len = strlen(pattern);
+
+	got_positive = 0;
+	for (i = 0; i < len;) {
+		/* Check if the subpattern is negated. */
+		if (pattern[i] == '!') {
+			negated = 1;
+			i++;
+		} else
+			negated = 0;
+
+		/*
+		 * Extract the subpattern up to a comma or end.  Convert the
+		 * subpattern to lowercase.
+		 */
+		for (subi = 0;
+		    i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
+		    subi++, i++)
+			sub[subi] = pattern[i];
+		/* If subpattern too long, return failure (no match). */
+		if (subi >= sizeof(sub) - 1)
+			return 0;
+
+		/* If the subpattern was terminated by a comma, then skip it. */
+		if (i < len && pattern[i] == ',')
+			i++;
+
+		/* Null-terminate the subpattern. */
+		sub[subi] = '\0';
+
+		/* Try to match the subpattern against the string. */
+		if (_match_pattern(string, sub, caseinsensitive)) {
+			if (negated)
+				return -1;		/* Negative */
+			else
+				got_positive = 1;	/* Positive */
+		}
+	}
+
+	/*
+	 * Return success if got a positive match.  If there was a negative
+	 * match, we have already returned -1 and never get here.
+	 */
+	return got_positive;
+}
+
 #endif /* HAVE_CYGWIN */
diff --git a/servconf.c b/servconf.c
index d9680aba11ca..4fa896fd4576 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1049,7 +1049,11 @@ match_cfg_line(char **condition, int line, struct connection_info *ci)
 			}
 			if (ci->user == NULL)
 				match_test_missing_fatal("User", "user");
+#ifndef HAVE_CYGWIN
 			if (match_pattern_list(ci->user, arg, 0) != 1)
+#else
+			if (match_pattern_list(ci->user, arg, 1) != 1)
+#endif
 				result = 0;
 			else
 				debug("user %.100s matched 'User %.100s' at "
-- 
2.20.1



More information about the openssh-unix-dev mailing list