changeset 13824:17189d594419

3157 nfs: wrong utf8-encoded string should be cause of error Reviewed by: Eric Schrock <eric.schrock@delphix.com> Approved by: Richard Lowe <richlowe@richlowe.net>
author Daniil Lunev <d.lunev.mail@gmail.com>
date Fri, 14 Sep 2012 22:09:42 -0500
parents 3abbdbfdfaf3
children 63b4afd261aa
files usr/src/uts/common/fs/nfs/nfs4_srv.c usr/src/uts/common/fs/nfs/nfs4_subr.c usr/src/uts/common/nfs/nfs4.h
diffstat 3 files changed, 105 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/fs/nfs/nfs4_srv.c	Thu Aug 30 15:48:18 2012 -0500
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv.c	Fri Sep 14 22:09:42 2012 -0500
@@ -20,11 +20,10 @@
  */
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- */
-/*
  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  */
 
+
 /*
  *	Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
  *	All Rights Reserved
@@ -5805,6 +5804,13 @@
 	cs.statusp = &resp->status;
 	cs.req = req;
 
+	resp->status = utf8_name_verify(&(resp->tag));
+	if (resp->status != NFS4_OK) {
+		resp->array_len = 0;
+		resp->array = NULL;
+		return;
+	}
+
 	/*
 	 * XXX for now, minorversion should be zero
 	 */
--- a/usr/src/uts/common/fs/nfs/nfs4_subr.c	Thu Aug 30 15:48:18 2012 -0500
+++ b/usr/src/uts/common/fs/nfs/nfs4_subr.c	Fri Sep 14 22:09:42 2012 -0500
@@ -56,6 +56,33 @@
 #include <nfs/rnode4.h>
 #include <nfs/nfs4_clnt.h>
 
+/* utf8-checking variables */
+#define	UTF8_TAIL_MASK		0xc0
+#define	UTF8_TAIL_SIGNATURE	0x80
+#define	UTF8_TAIL_SHIFT		6
+#define	UTF16_SURROGATE_LOW	0xd800
+#define	UTF16_SURROGATE_HIGH	0xdfff
+#define	UNICODE_INVAL_1		0xfffe
+#define	UNICODE_INVAL_2		0xffff
+
+typedef struct {
+	unsigned char mask;
+	unsigned char signature;
+	unsigned int  min_val;
+	unsigned char tail_bytes;
+} utf8_encoding_table;
+
+static utf8_encoding_table utf8_table[] = {
+	{ 0x80,	0x00,	0x00000000,	0 }, // 1 byte
+	{ 0xe0,	0xc0,	0x00000080,	1 }, // 2 bytes
+	{ 0xf0,	0xe0,	0x00000800,	2 }, // 3 bytes
+	{ 0xf8,	0xf0,	0x00010000,	3 }, // 4 bytes
+	{ 0xfc,	0xf8,	0x00200000, 	4 }, // 5 bytes
+	{ 0xfe,	0xfc,	0x04000000,	5 }, // 6 bytes
+	{ 0,	0,	0,		0 },
+};
+
+
 /*
  * client side statistics
  */
@@ -714,6 +741,74 @@
 }
 
 /*
+ * utf8_name_verify - verify utf8-correctness of the passed string.
+ *
+ * Byte's checking is performed by applying and-mask to byte and checking
+ * result of this operation (signature).
+ * ~mask used to extract valuable bits from byte that will be put in 'symbol'
+ * that represents encoded unicode character.
+ *
+ * Symbols encoded with UTF8 have following format:
+ * 0xxxxxxx						  - 1 byte symbol
+ * 110xxxxx 10xxxxxx					  - 2 bytes
+ * 1110xxxx 10xxxxxx 10xxxxxx				  - 3 bytes
+ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx			  - 4 bytes
+ * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx	  - 5 bytes
+ * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - 6 bytes
+ */
+nfsstat4
+utf8_name_verify(utf8string *str)
+{
+	int len = str->utf8string_len;
+	unsigned char *u8p = (unsigned char *) str->utf8string_val;
+	int pos = 0;
+
+	while (pos < len) {
+		unsigned char c = u8p[pos++];
+		int  i;
+		unsigned int symbol;
+		utf8_encoding_table * encoding = utf8_table;
+
+		/* check leading byte */
+		while (encoding->mask != 0x00) {
+			if ((c & encoding->mask) == encoding->signature)
+				break;
+			++encoding;
+		}
+		if (encoding->mask == 0x00)
+			return (NFS4ERR_INVAL);
+
+		symbol = c & (~encoding->mask);
+
+		/* check tail bytes if leading byte describes so */
+		for (i = 0; i < encoding->tail_bytes; ++i) {
+			if (pos >= len)
+				return (NFS4ERR_INVAL);
+			c = u8p[pos++];
+			if ((c & UTF8_TAIL_MASK) != UTF8_TAIL_SIGNATURE)
+				return (NFS4ERR_INVAL);
+			symbol <<= UTF8_TAIL_SHIFT;
+			symbol |= (c & (~UTF8_TAIL_MASK));
+		}
+
+		/* check UTF-16 surrogate */
+		if ((symbol >= UTF16_SURROGATE_LOW) &&
+		    (symbol <= UTF16_SURROGATE_HIGH))
+			return (NFS4ERR_INVAL);
+
+		/* check wrong Unicode character case */
+		if ((symbol == UNICODE_INVAL_1) || (symbol == UNICODE_INVAL_2))
+			return (NFS4ERR_INVAL);
+
+		/* check overlonging */
+		if (symbol < encoding->min_val)
+			return (NFS4ERR_INVAL);
+	}
+
+	return (NFS4_OK);
+}
+
+/*
  * utf8_dir_verify - checks that the utf8 string is valid
  */
 nfsstat4
@@ -742,7 +837,7 @@
 	if (utf8_strchr(str, '\0') != NULL)
 		return (NFS4ERR_BADNAME);
 
-	return (NFS4_OK);
+	return (utf8_name_verify(str));
 }
 
 /*
--- a/usr/src/uts/common/nfs/nfs4.h	Thu Aug 30 15:48:18 2012 -0500
+++ b/usr/src/uts/common/nfs/nfs4.h	Fri Sep 14 22:09:42 2012 -0500
@@ -1310,6 +1310,7 @@
 extern utf8string *str_to_utf8(char *, utf8string *);
 extern utf8string *utf8_copy(utf8string *, utf8string *);
 extern int	utf8_compare(const utf8string *, const utf8string *);
+extern nfsstat4	utf8_name_verify(utf8string *);
 extern nfsstat4	utf8_dir_verify(utf8string *);
 extern char	*utf8_strchr(utf8string *, const char);
 extern int	ln_ace4_cmp(nfsace4 *, nfsace4 *, int);