summaryrefslogtreecommitdiff
path: root/usr/src/common
diff options
context:
space:
mode:
authorGordon Ross <gwr@nexenta.com>2015-10-02 15:45:00 -0400
committerGordon Ross <gwr@nexenta.com>2019-06-03 22:09:35 -0400
commit07a6ae61f8958faa11352bf1b552d85d79e9cbbe (patch)
tree63d7732a32b6e1c946f37000cfcd70991bca93cb /usr/src/common
parentc2af9d806aba2e4f6d513af9b700509b9ed978f0 (diff)
downloadillumos-joyent-07a6ae61f8958faa11352bf1b552d85d79e9cbbe.tar.gz
10997 Incorrect directory listing response for non-UNICODE clients
Reviewed by: Matt Barden <Matt.Barden@nexenta.com> Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com> Approved by: Richard Lowe <richlowe@richlowe.net>
Diffstat (limited to 'usr/src/common')
-rw-r--r--usr/src/common/smbsrv/smb_utf8.c230
1 files changed, 151 insertions, 79 deletions
diff --git a/usr/src/common/smbsrv/smb_utf8.c b/usr/src/common/smbsrv/smb_utf8.c
index 3b84363dbd..3c3583471f 100644
--- a/usr/src/common/smbsrv/smb_utf8.c
+++ b/usr/src/common/smbsrv/smb_utf8.c
@@ -22,7 +22,7 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -53,12 +53,13 @@
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
#include <sys/types.h>
#include <sys/sunddi.h>
-#else
+#else /* _KERNEL || _FAKE_KERNEL */
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <strings.h>
-#endif
+#include <iconv.h>
+#include <assert.h>
+#endif /* _KERNEL || _FAKE_KERNEL */
#include <smbsrv/string.h>
@@ -275,7 +276,7 @@ smb_wcequiv_strlen(const char *mbs)
/*
* Returns the number of bytes that would be written if the multi-
- * byte string mbs was converted to a single byte character string,
+ * byte string mbs was converted to an OEM character string,
* not counting the terminating null character.
*/
size_t
@@ -290,10 +291,13 @@ smb_sbequiv_strlen(const char *mbs)
if (nbytes == ((size_t)-1))
return ((size_t)-1);
- if (wide_char & 0xFF00)
- len += sizeof (smb_wchar_t);
- else
- ++len;
+ /*
+ * Assume OEM characters are 1-byte (for now).
+ * That's true for cp850, which is the only
+ * codeset this currently supports. See:
+ * smb_oem.c : smb_oem_codeset
+ */
+ ++len;
mbs += nbytes;
}
@@ -301,106 +305,174 @@ smb_sbequiv_strlen(const char *mbs)
return (len);
}
+/*
+ * Convert OEM strings to/from internal (UTF-8) form.
+ *
+ * We rarely encounter these anymore because all modern
+ * SMB clients use Unicode (UTF-16). The few cases where
+ * this IS still called are normally using ASCII, i.e.
+ * tag names etc. so short-cut those cases. If we get
+ * something non-ASCII we have to call iconv.
+ *
+ * If we were to really support OEM code pages, we would
+ * need to have a way to set the OEM code page from some
+ * configuration value. For now it's always CP850.
+ * See also ./smb_oem.c
+ */
+static char smb_oem_codepage[32] = "CP850";
/*
- * stombs
+ * smb_oemtombs
*
- * Convert a regular null terminated string 'string' to a UTF-8 encoded
- * null terminated multi-byte string 'mbstring'. Only full converted
- * UTF-8 characters will be written 'mbstring'. If a character will not
- * fit within the remaining buffer space or 'mbstring' will overflow
- * max_mblen, the conversion process will be terminated and 'mbstring'
- * will be null terminated.
+ * Convert a null terminated OEM string 'string' to a UTF-8 string
+ * no longer than max_mblen (null terminated if space).
*
- * Returns the number of bytes written to 'mbstring', excluding the
- * terminating null character.
+ * If the input string contains invalid OEM characters, a value
+ * of -1 will be returned. Otherwise returns the length of 'mbs',
+ * excluding the terminating null character.
*
* If either mbstring or string is a null pointer, -1 is returned.
*/
int
-smb_stombs(char *mbstring, char *string, int max_mblen)
+smb_oemtombs(char *mbs, const uint8_t *oems, int max_mblen)
{
- char *start = mbstring;
- unsigned char *p = (unsigned char *)string;
- int space_left = max_mblen;
- int len;
- smb_wchar_t wide_char;
- char buf[4];
+ uchar_t *p;
+ int oemlen;
+ int rlen;
+ boolean_t need_iconv = B_FALSE;
- if (!mbstring || !string)
+ if (mbs == NULL || oems == NULL)
return (-1);
- while (*p && space_left > 2) {
- wide_char = *p++;
- len = smb_wctomb(mbstring, wide_char);
- mbstring += len;
- space_left -= len;
+ /*
+ * Check if the oems is all ASCII (and get the length
+ * while we're at it) so we know if we need to iconv.
+ * We usually can avoid the iconv calls.
+ */
+ oemlen = 0;
+ p = (uchar_t *)oems;
+ while (*p != '\0') {
+ oemlen++;
+ if (*p & 0x80)
+ need_iconv = B_TRUE;
+ p++;
}
- if (*p) {
- wide_char = *p;
- if ((len = smb_wctomb(buf, wide_char)) < 2) {
- *mbstring = *buf;
- mbstring += len;
- space_left -= len;
- }
+ if (need_iconv) {
+ int rc;
+ char *obuf = mbs;
+ size_t olen = max_mblen;
+ size_t ilen = oemlen;
+#if defined(_KERNEL) || defined(_FAKE_KERNEL)
+ char *ibuf = (char *)oems;
+ kiconv_t ic;
+ int err;
+
+ ic = kiconv_open("UTF-8", smb_oem_codepage);
+ if (ic == (kiconv_t)-1)
+ goto just_copy;
+ rc = kiconv(ic, &ibuf, &ilen, &obuf, &olen, &err);
+ (void) kiconv_close(ic);
+#else /* _KERNEL || _FAKE_KERNEL */
+ const char *ibuf = (char *)oems;
+ iconv_t ic;
+ ic = iconv_open("UTF-8", smb_oem_codepage);
+ if (ic == (iconv_t)-1)
+ goto just_copy;
+ rc = iconv(ic, &ibuf, &ilen, &obuf, &olen);
+ (void) iconv_close(ic);
+#endif /* _KERNEL || _FAKE_KERNEL */
+ if (rc < 0)
+ return (-1);
+ /* Return val. is output bytes. */
+ rlen = (max_mblen - olen);
+ } else {
+ just_copy:
+ rlen = oemlen;
+ if (rlen > max_mblen)
+ rlen = max_mblen;
+ bcopy(oems, mbs, rlen);
}
+ if (rlen < max_mblen)
+ mbs[rlen] = '\0';
- *mbstring = '\0';
-
- /*LINTED E_PTRDIFF_OVERFLOW*/
- return (mbstring - start);
+ return (rlen);
}
-
/*
- * mbstos
+ * smb_mbstooem
*
- * Convert a null terminated multi-byte string 'mbstring' to a regular
- * null terminated string 'string'. A 1-byte character in 'mbstring'
- * maps to a 1-byte character in 'string'. A 2-byte character in
- * 'mbstring' will be mapped to 2-bytes, if the upper byte is non-null.
- * Otherwise the upper byte null will be discarded to ensure that the
- * output stream does not contain embedded null characters.
+ * Convert a null terminated multi-byte string 'mbs' to an OEM string
+ * no longer than max_oemlen (null terminated if space).
*
- * If the input stream contains invalid multi-byte characters, a value
- * of -1 will be returned. Otherwise the length of 'string', excluding
- * the terminating null character, is returned.
+ * If the input string contains invalid multi-byte characters, a value
+ * of -1 will be returned. Otherwise returns the length of 'oems',
+ * excluding the terminating null character.
*
* If either mbstring or string is a null pointer, -1 is returned.
*/
int
-smb_mbstos(char *string, const char *mbstring)
+smb_mbstooem(uint8_t *oems, const char *mbs, int max_oemlen)
{
- smb_wchar_t wc;
- unsigned char *start = (unsigned char *)string;
- int len;
+ uchar_t *p;
+ int mbslen;
+ int rlen;
+ boolean_t need_iconv = B_FALSE;
- if (string == NULL || mbstring == NULL)
+ if (oems == NULL || mbs == NULL)
return (-1);
- while (*mbstring) {
- if ((len = smb_mbtowc(&wc, mbstring, MTS_MB_CHAR_MAX)) < 0) {
- *string = 0;
- return (-1);
- }
-
- if (wc & 0xFF00) {
- /*LINTED E_BAD_PTR_CAST_ALIGN*/
- *((smb_wchar_t *)string) = wc;
- string += sizeof (smb_wchar_t);
- }
- else
- {
- *string = (unsigned char)wc;
- string++;
- }
-
- mbstring += len;
+ /*
+ * Check if the mbs is all ASCII (and get the length
+ * while we're at it) so we know if we need to iconv.
+ * We usually can avoid the iconv calls.
+ */
+ mbslen = 0;
+ p = (uchar_t *)mbs;
+ while (*p != '\0') {
+ mbslen++;
+ if (*p & 0x80)
+ need_iconv = B_TRUE;
+ p++;
}
- *string = 0;
+ if (need_iconv) {
+ int rc;
+ char *obuf = (char *)oems;
+ size_t olen = max_oemlen;
+ size_t ilen = mbslen;
+#if defined(_KERNEL) || defined(_FAKE_KERNEL)
+ char *ibuf = (char *)mbs;
+ kiconv_t ic;
+ int err;
+
+ ic = kiconv_open(smb_oem_codepage, "UTF-8");
+ if (ic == (kiconv_t)-1)
+ goto just_copy;
+ rc = kiconv(ic, &ibuf, &ilen, &obuf, &olen, &err);
+ (void) kiconv_close(ic);
+#else /* _KERNEL || _FAKE_KERNEL */
+ const char *ibuf = mbs;
+ iconv_t ic;
+ ic = iconv_open(smb_oem_codepage, "UTF-8");
+ if (ic == (iconv_t)-1)
+ goto just_copy;
+ rc = iconv(ic, &ibuf, &ilen, &obuf, &olen);
+ (void) iconv_close(ic);
+#endif /* _KERNEL || _FAKE_KERNEL */
+ if (rc < 0)
+ return (-1);
+ /* Return val. is output bytes. */
+ rlen = (max_oemlen - olen);
+ } else {
+ just_copy:
+ rlen = mbslen;
+ if (rlen > max_oemlen)
+ rlen = max_oemlen;
+ bcopy(mbs, oems, rlen);
+ }
+ if (rlen < max_oemlen)
+ oems[rlen] = '\0';
- /*LINTED E_PTRDIFF_OVERFLOW*/
- return ((unsigned char *)string - start);
+ return (rlen);
}