summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/common/smbsrv/smb_utf8.c230
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/mapfile-vers2
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_kutil.c18
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c256
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_trans2_find.c101
-rw-r--r--usr/src/uts/common/smbsrv/string.h5
6 files changed, 369 insertions, 243 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);
}
diff --git a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
index aaa9d368b3..8f1ad4a552 100644
--- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
+++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
@@ -270,7 +270,6 @@ SYMBOL_VERSION SUNWprivate {
smb_lookup_name;
smb_lookup_sid;
smb_match_netlogon_seqnum;
- smb_mbstos;
smb_mbstowcs;
smb_mbtowc;
smb_msgbuf_base;
@@ -376,7 +375,6 @@ SYMBOL_VERSION SUNWprivate {
smb_smf_restart_service;
smb_smf_scf_fini;
smb_smf_scf_init;
- smb_stombs;
smb_strcasecmp;
smb_string_decode;
smb_string_encode;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_kutil.c b/usr/src/uts/common/fs/smbsrv/smb_kutil.c
index e5ac75cbbc..5c74e4f8cd 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_kutil.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_kutil.c
@@ -70,22 +70,36 @@ static const int days_in_month[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
+/*
+ * Given a UTF-8 string (our internal form everywhere)
+ * return either the Unicode (UTF-16) length in bytes,
+ * or the OEM length in bytes. Which we return is
+ * determined by whether the client supports Unicode.
+ * This length does NOT include the null.
+ */
int
smb_ascii_or_unicode_strlen(struct smb_request *sr, char *str)
{
if (sr->session->dialect >= SMB_VERS_2_BASE ||
(sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0)
return (smb_wcequiv_strlen(str));
- return (strlen(str));
+ return (smb_sbequiv_strlen(str));
}
+/*
+ * Given a UTF-8 string (our internal form everywhere)
+ * return either the Unicode (UTF-16) length in bytes,
+ * or the OEM length in bytes. Which we return is
+ * determined by whether the client supports Unicode.
+ * This length DOES include the null.
+ */
int
smb_ascii_or_unicode_strlen_null(struct smb_request *sr, char *str)
{
if (sr->session->dialect >= SMB_VERS_2_BASE ||
(sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0)
return (smb_wcequiv_strlen(str) + 2);
- return (strlen(str) + 1);
+ return (smb_sbequiv_strlen(str) + 1);
}
int
diff --git a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c
index a3fa7f364f..e4cd2435db 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c
@@ -39,14 +39,14 @@
#define DECODE_ALLOCATION_ERROR 2
#define DECODE_CONVERSION_ERROR 3
-static int mbc_marshal_cstou8(char *, char *, size_t, char *, size_t);
static int mbc_marshal_make_room(mbuf_chain_t *, int32_t);
static void mbc_marshal_store_byte(mbuf_chain_t *, uint8_t);
static int mbc_marshal_put_char(mbuf_chain_t *mbc, uint8_t);
static int mbc_marshal_put_short(mbuf_chain_t *mbc, uint16_t);
static int mbc_marshal_put_long(mbuf_chain_t *mbc, uint32_t);
static int mbc_marshal_put_long_long(mbuf_chain_t *mbc, uint64_t);
-static int mbc_marshal_put_ascii_string(mbuf_chain_t *, char *, int);
+static int mbc_marshal_put_oem_string(smb_request_t *, mbuf_chain_t *,
+ char *, int);
static int mbc_marshal_put_unicode_string(mbuf_chain_t *, char *, int);
static int mbc_marshal_put_uio(mbuf_chain_t *, struct uio *);
static int mbc_marshal_put_mbufs(mbuf_chain_t *mbc, mbuf_t *m);
@@ -58,10 +58,10 @@ static int mbc_marshal_get_long(mbuf_chain_t *mbc, uint32_t *data);
static uint64_t qswap(uint64_t ll);
static int mbc_marshal_get_odd_long_long(mbuf_chain_t *mbc, uint64_t *data);
static int mbc_marshal_get_long_long(mbuf_chain_t *mbc, uint64_t *data);
-static int mbc_marshal_get_ascii_string(smb_request_t *, mbuf_chain_t *,
- uint8_t **ascii, int);
+static int mbc_marshal_get_oem_string(smb_request_t *, mbuf_chain_t *,
+ char **, int);
static int mbc_marshal_get_unicode_string(smb_request_t *, mbuf_chain_t *,
- uint8_t **, int);
+ char **, int);
static int mbc_marshal_get_mbufs(mbuf_chain_t *, int32_t, mbuf_t **);
static int mbc_marshal_get_mbuf_chain(mbuf_chain_t *, int32_t, mbuf_chain_t *);
static int mbc_marshal_get_uio(mbuf_chain_t *, struct uio *);
@@ -152,7 +152,7 @@ smb_mbc_vdecodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
uint8_t c;
uint8_t cval;
uint8_t *cvalp;
- uint8_t **cvalpp;
+ char **charpp;
uint16_t wval;
uint16_t *wvalp;
uint32_t *lvalp;
@@ -294,7 +294,7 @@ smb_mbc_vdecodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
return (-1);
if (cval != 2)
return (-1);
- goto ascii_conversion;
+ goto oem_conversion;
case 'A':
case 'S':
@@ -310,27 +310,27 @@ smb_mbc_vdecodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
goto unicode_translation;
/* FALLTHROUGH */
- case 's':
-ascii_conversion:
+ case 's': /* OEM string */
+oem_conversion:
ASSERT(sr != NULL);
- cvalpp = va_arg(ap, uint8_t **);
+ charpp = va_arg(ap, char **);
if (!repc_specified)
repc = 0;
- if (mbc_marshal_get_ascii_string(sr,
- mbc, cvalpp, repc) != 0)
+ if (mbc_marshal_get_oem_string(sr,
+ mbc, charpp, repc) != 0)
return (-1);
break;
case 'U': /* Convert from unicode */
unicode_translation:
ASSERT(sr != 0);
- cvalpp = va_arg(ap, uint8_t **);
+ charpp = va_arg(ap, char **);
if (!repc_specified)
repc = 0;
if (mbc->chain_offset & 1)
mbc->chain_offset++;
if (mbc_marshal_get_unicode_string(sr,
- mbc, cvalpp, repc) != 0)
+ mbc, charpp, repc) != 0)
return (-1);
break;
@@ -519,6 +519,7 @@ smb_mbc_peek(mbuf_chain_t *mbc, int offset, const char *fmt, ...)
int
smb_mbc_vencodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
{
+ char *charp;
uint8_t *cvalp;
timestruc_t *tvp;
smb_vdb_t *vdp;
@@ -549,7 +550,6 @@ smb_mbc_vencodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
repc = va_arg(ap, int);
c = *fmt++;
repc_specified = B_TRUE;
-
}
switch (c) {
@@ -659,7 +659,7 @@ smb_mbc_vencodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
case 'L':
tag = 2;
- goto ascii_conversion;
+ goto oem_conversion;
case 'S':
case 'A':
@@ -680,12 +680,25 @@ smb_mbc_vencodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
goto unicode_translation;
/* FALLTHROUGH */
- case 's': /* ASCII/multibyte string */
-ascii_conversion: cvalp = va_arg(ap, uint8_t *);
+ case 's': /* OEM string */
+oem_conversion:
+ charp = va_arg(ap, char *);
if (!repc_specified)
repc = 0;
- if (mbc_marshal_put_ascii_string(mbc,
- (char *)cvalp, repc) != 0)
+ if (mbc_marshal_put_oem_string(sr, mbc,
+ charp, repc) != 0)
+ return (DECODE_NO_MORE_DATA);
+ break;
+
+ case 'U': /* Convert to unicode, align to word boundary */
+unicode_translation:
+ if (mbc->chain_offset & 1)
+ mbc->chain_offset++;
+ charp = va_arg(ap, char *);
+ if (!repc_specified)
+ repc = 0;
+ if (mbc_marshal_put_unicode_string(mbc,
+ charp, repc) != 0)
return (DECODE_NO_MORE_DATA);
break;
@@ -728,18 +741,6 @@ ascii_conversion: cvalp = va_arg(ap, uint8_t *);
return (DECODE_NO_MORE_DATA);
break;
- case 'U': /* Convert to unicode, align to word boundary */
-unicode_translation:
- if (mbc->chain_offset & 1)
- mbc->chain_offset++;
- cvalp = va_arg(ap, uint8_t *);
- if (!repc_specified)
- repc = 0;
- if (mbc_marshal_put_unicode_string(mbc,
- (char *)cvalp, repc) != 0)
- return (DECODE_NO_MORE_DATA);
- break;
-
default:
ASSERT(0);
return (-1);
@@ -1068,74 +1069,88 @@ mbc_marshal_put_long_long(mbuf_chain_t *mbc, uint64_t data)
}
/*
- * When need to convert from UTF-8 (internal format) to a single
- * byte string (external format ) when marshalling a string.
+ * Marshal a UTF-8 string (str) into mbc, converting to OEM codeset.
+ * Also write a null unless the repc count limits the length we put.
*/
static int
-mbc_marshal_put_ascii_string(mbuf_chain_t *mbc, char *mbs, int repc)
+mbc_marshal_put_oem_string(
+ smb_request_t *sr,
+ mbuf_chain_t *mbc,
+ char *str,
+ int repc)
{
- smb_wchar_t wide_char;
- int nbytes;
- int length;
-
- if ((length = smb_sbequiv_strlen(mbs)) == -1)
- return (DECODE_NO_MORE_DATA);
-
- length += sizeof (char);
+ uint8_t *s, *oembuf;
+ int buflen;
+ int oemlen;
+ int putlen;
- if ((repc > 0) && (repc < length))
- length = repc;
- if (mbc_marshal_make_room(mbc, length))
+ /*
+ * First convert to OEM string. The OEM string
+ * will be no longer than the UTF-8 string.
+ */
+ buflen = strlen(str) + 1;
+ oembuf = smb_srm_zalloc(sr, buflen);
+ oemlen = smb_mbstooem(oembuf, str, buflen);
+ if (oemlen == -1)
return (DECODE_NO_MORE_DATA);
- while (*mbs) {
- /*
- * We should restore oem chars here.
- */
- nbytes = smb_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX);
- if (nbytes == -1)
- return (DECODE_NO_MORE_DATA);
+ /* null terminator */
+ if (oemlen < buflen)
+ oembuf[oemlen++] = '\0';
- mbc_marshal_store_byte(mbc, (uint8_t)wide_char);
+ /* If specified, repc limits the length. */
+ putlen = oemlen;
+ if ((repc > 0) && (repc < putlen))
+ putlen = repc;
- if (wide_char & 0xFF00)
- mbc_marshal_store_byte(mbc, wide_char >> 8);
+ if (mbc_marshal_make_room(mbc, putlen))
+ return (DECODE_NO_MORE_DATA);
- mbs += nbytes;
+ s = oembuf;
+ while (putlen > 0) {
+ mbc_marshal_store_byte(mbc, *s);
+ s++;
+ putlen--;
}
- mbc_marshal_store_byte(mbc, 0);
return (0);
}
+/*
+ * Marshal a UTF-8 string (str) into mbc, converting to UTF-16.
+ * Also write a UTF-16 null (2 bytes) unless the repc count
+ * limits the length we put into the mbc.
+ */
static int
-mbc_marshal_put_unicode_string(mbuf_chain_t *mbc, char *ascii, int repc)
+mbc_marshal_put_unicode_string(mbuf_chain_t *mbc, char *str, int repc)
{
smb_wchar_t wchar;
int consumed;
int length;
- if ((length = smb_wcequiv_strlen(ascii)) == -1)
+ if ((length = smb_wcequiv_strlen(str)) == -1)
return (DECODE_NO_MORE_DATA);
+ /* null terminator */
length += sizeof (smb_wchar_t);
+ /* If specified, repc limits the length. */
if ((repc > 0) && (repc < length))
length = repc;
if (mbc_marshal_make_room(mbc, length))
return (DECODE_NO_MORE_DATA);
while (length > 0) {
- consumed = smb_mbtowc(&wchar, ascii, MTS_MB_CHAR_MAX);
+ consumed = smb_mbtowc(&wchar, str, MTS_MB_CHAR_MAX);
if (consumed == -1)
break; /* Invalid sequence */
/*
* Note that consumed will be 0 when the null terminator
- * is encountered and ascii will not be advanced beyond
+ * is encountered and str will not be advanced beyond
* that point. Length will continue to be decremented so
* we won't get stuck here.
*/
- ascii += consumed;
+ str += consumed;
mbc_marshal_store_byte(mbc, wchar);
mbc_marshal_store_byte(mbc, wchar >> 8);
length -= sizeof (smb_wchar_t);
@@ -1375,37 +1390,36 @@ mbc_marshal_get_long_long(mbuf_chain_t *mbc, uint64_t *data)
}
/*
- * mbc_marshal_get_ascii_string
- *
- * The ascii string in smb includes oem chars. Since the
- * system needs utf8 encodes unicode char, conversion is
- * required to convert the oem char to unicode and then
- * to encode the converted wchars to utf8 format.
- * Therefore, the **ascii returned will be in such format
- * instead of the real ASCII format.
+ * mbc_marshal_get_oem_string
+ *
+ * Decode an OEM string, returning its UTF-8 form in strpp,
+ * allocated using smb_srm_zalloc (automatically freed).
+ * If repc != 0, consume no more than repc bytes.
*/
static int
-mbc_marshal_get_ascii_string(
+mbc_marshal_get_oem_string(
smb_request_t *sr,
mbuf_chain_t *mbc,
- uint8_t **ascii,
- int max_ascii)
+ char **strpp,
+ int repc)
{
- char *rcvbuf;
- char *ch;
- int max;
- int length = 0;
+ uint8_t *ch, *rcvbuf;
+ char *mbsbuf;
+ int mbslen, mbsmax;
+ int buflen;
+ int oemlen;
- max = MALLOC_QUANTUM;
- rcvbuf = smb_srm_zalloc(sr, max);
+ buflen = MALLOC_QUANTUM;
+ rcvbuf = smb_srm_zalloc(sr, buflen);
- if (max_ascii == 0)
- max_ascii = 0xffff;
+ if (repc == 0)
+ repc = 0xffff;
+ oemlen = 0;
ch = rcvbuf;
for (;;) {
- while (length < max) {
- if (max_ascii-- <= 0) {
+ while (oemlen < buflen) {
+ if (repc-- <= 0) {
*ch++ = 0;
goto multibyte_encode;
}
@@ -1415,26 +1429,38 @@ mbc_marshal_get_ascii_string(
}
if ((*ch++ = mbc_marshal_fetch_byte(mbc)) == 0)
goto multibyte_encode;
- length++;
+ oemlen++;
}
- max += MALLOC_QUANTUM;
- rcvbuf = smb_srm_rezalloc(sr, rcvbuf, max);
- ch = rcvbuf + length;
+ buflen += MALLOC_QUANTUM;
+ rcvbuf = smb_srm_rezalloc(sr, rcvbuf, buflen);
+ ch = rcvbuf + oemlen;
}
multibyte_encode:
/*
- * UTF-8 encode the string for internal system use.
+ * UTF-8 encode the return string for internal system use.
+ * Allocated size is worst-case: 3x larger than OEM.
*/
- length = strlen(rcvbuf) + 1;
- *ascii = smb_srm_zalloc(sr, length * MTS_MB_CHAR_MAX);
- return (mbc_marshal_cstou8("CP850", (char *)*ascii,
- (size_t)length * MTS_MB_CHAR_MAX, rcvbuf, (size_t)length));
+ mbsmax = (oemlen + 1) * MTS_MB_CHAR_MAX;
+ mbsbuf = smb_srm_zalloc(sr, mbsmax);
+ mbslen = smb_oemtombs(mbsbuf, rcvbuf, mbsmax);
+ if (mbslen == -1)
+ return (DECODE_NO_MORE_DATA);
+
+ *strpp = mbsbuf;
+ return (0);
}
+/*
+ * mbc_marshal_get_unicode_string
+ *
+ * Decode a UTF-16 string, returning its UTF-8 form in strpp,
+ * allocated using smb_srm_zalloc (automatically freed).
+ * If repc != 0, consume no more than repc bytes.
+ */
static int
mbc_marshal_get_unicode_string(smb_request_t *sr,
- mbuf_chain_t *mbc, uint8_t **ascii, int max_unicode)
+ mbuf_chain_t *mbc, char **strpp, int repc)
{
int max;
uint16_t wchar;
@@ -1442,18 +1468,18 @@ mbc_marshal_get_unicode_string(smb_request_t *sr,
int emitted;
int length = 0;
- if (max_unicode == 0)
- max_unicode = 0xffff;
+ if (repc == 0)
+ repc = 0xffff;
max = MALLOC_QUANTUM;
- *ascii = smb_srm_zalloc(sr, max);
+ *strpp = smb_srm_zalloc(sr, max);
- ch = (char *)*ascii;
+ ch = *strpp;
for (;;) {
while ((length + MTS_MB_CHAR_MAX) < max) {
- if (max_unicode <= 0)
+ if (repc <= 0)
goto done;
- max_unicode -= 2;
+ repc -= 2;
if (mbc_marshal_get_short(mbc, &wchar) != 0)
return (DECODE_NO_MORE_DATA);
@@ -1465,8 +1491,8 @@ mbc_marshal_get_unicode_string(smb_request_t *sr,
ch += emitted;
}
max += MALLOC_QUANTUM;
- *ascii = smb_srm_rezalloc(sr, *ascii, max);
- ch = (char *)*ascii + length;
+ *strpp = smb_srm_rezalloc(sr, *strpp, max);
+ ch = *strpp + length;
}
done: *ch = 0;
return (0);
@@ -1571,29 +1597,3 @@ mbc_marshal_get_skip(mbuf_chain_t *mbc, uint_t skip)
mbc->chain_offset += skip;
return (0);
}
-
-/*
- * Converts oem string to UTF-8 string with an output string of max
- * maxconv bytes. The string may be truncated or not null-terminated if
- * there is not enough room.
- *
- * returns -1, cnt (partial conversion) or 0 (success)
- */
-
-static int
-mbc_marshal_cstou8(char *cs, char *outbuf, size_t maxconv,
- char *inbuf, size_t srcbytes)
-{
- kiconv_t t2u;
- size_t inlen = srcbytes;
- size_t outlen = maxconv;
- int err = 0;
- size_t rc;
-
- if ((t2u = kiconv_open("UTF-8", cs)) == (kiconv_t)-1)
- return (-1);
-
- rc = kiconv(t2u, &inbuf, &inlen, &outbuf, &outlen, &err);
- (void) kiconv_close(t2u);
- return ((int)rc);
-}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c
index cdfaf0c8ee..2592edfdad 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c
@@ -213,7 +213,7 @@
* various functions involved in FindFirst, FindNext.
*/
typedef struct smb_find_args {
- uint32_t fa_maxdata;
+ uint32_t fa_fixedsize;
uint16_t fa_infolev;
uint16_t fa_maxcount;
uint16_t fa_fflag;
@@ -225,7 +225,7 @@ typedef struct smb_find_args {
static int smb_trans2_find_entries(smb_request_t *, smb_xa_t *,
smb_odir_t *, smb_find_args_t *);
-static int smb_trans2_find_get_maxdata(smb_request_t *, uint16_t, uint16_t);
+static int smb_trans2_find_get_fixedsize(smb_request_t *, uint16_t, uint16_t);
static int smb_trans2_find_mbc_encode(smb_request_t *, smb_xa_t *,
smb_fileinfo_t *, smb_find_args_t *);
@@ -320,9 +320,9 @@ smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa)
odir_flags = SMB_ODIR_OPENF_BACKUP_INTENT;
}
- args.fa_maxdata =
- smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
- if (args.fa_maxdata == 0)
+ args.fa_fixedsize =
+ smb_trans2_find_get_fixedsize(sr, args.fa_infolev, args.fa_fflag);
+ if (args.fa_fixedsize == 0)
return (SDRC_ERROR);
status = smb_odir_openpath(sr, pn->pn_path, sattr, odir_flags, &od);
@@ -455,9 +455,9 @@ smb_com_trans2_find_next2(smb_request_t *sr, smb_xa_t *xa)
if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT)
sr->user_cr = smb_user_getprivcred(sr->uid_user);
- args.fa_maxdata =
- smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
- if (args.fa_maxdata == 0)
+ args.fa_fixedsize =
+ smb_trans2_find_get_fixedsize(sr, args.fa_infolev, args.fa_fflag);
+ if (args.fa_fixedsize == 0)
return (SDRC_ERROR);
od = smb_tree_lookup_odir(sr, odid);
@@ -536,6 +536,7 @@ smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
smb_odir_resume_t odir_resume;
uint16_t count, maxcount;
int rc = -1;
+ int LastEntryOffset = 0;
boolean_t need_rewind = B_FALSE;
/*
@@ -565,6 +566,7 @@ smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
if (rc != 0 || args->fa_eos != 0)
break;
+ LastEntryOffset = xa->rep_data_mb.chain_offset;
rc = smb_trans2_find_mbc_encode(sr, xa, &fileinfo, args);
if (rc == -1)
return (-1); /* fatal encoding error */
@@ -584,6 +586,15 @@ smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
if (args->fa_eos != 0 && rc == ENOENT)
rc = 0;
+ /*
+ * All but the ancient info levels start with NextEntryOffset.
+ * That's supposed to be zero in the last entry returned.
+ */
+ if (args->fa_infolev >= SMB_FIND_FILE_DIRECTORY_INFO) {
+ (void) smb_mbc_poke(&xa->rep_data_mb,
+ LastEntryOffset, "l", 0);
+ }
+
/* save the last cookie returned to client */
if (count != 0)
smb_odir_save_fname(od, args->fa_lastkey, args->fa_lastname);
@@ -621,20 +632,19 @@ smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
}
/*
- * smb_trans2_find_get_maxdata
+ * smb_trans2_find_get_fixedsize
*
- * Calculate the minimum response space required for the specified
- * information level.
+ * Calculate the sizeof the fixed part of the response for the
+ * specified information level.
*
- * A non-zero return value provides the minimum space required.
+ * A non-zero return value provides the fixed size.
* A return value of zero indicates an unknown information level.
*/
static int
-smb_trans2_find_get_maxdata(smb_request_t *sr, uint16_t infolev, uint16_t fflag)
+smb_trans2_find_get_fixedsize(smb_request_t *sr, uint16_t infolev,
+ uint16_t fflag)
{
- int maxdata;
-
- maxdata = smb_ascii_or_unicode_null_len(sr);
+ int maxdata = 0;
switch (infolev) {
case SMB_INFO_STANDARD :
@@ -736,27 +746,53 @@ smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa,
uint32_t resume_key;
char buf83[26];
smb_msgbuf_t mb;
+ int pad = 0;
namelen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_name);
if (namelen == -1)
return (-1);
- /*
- * If ascii the filename length returned to the client should
- * include the null terminator for levels except STANDARD and
- * EASIZE.
- */
- if (!(sr->smb_flg2 & SMB_FLAGS2_UNICODE)) {
- if ((args->fa_infolev != SMB_INFO_STANDARD) &&
- (args->fa_infolev != SMB_INFO_QUERY_EA_SIZE))
+ if (args->fa_infolev < SMB_FIND_FILE_DIRECTORY_INFO) {
+ /*
+ * Ancient info levels don't have a NextEntryOffset
+ * field, so there's no padding for alignment.
+ * The client expects a null after the file name,
+ * and then the next entry. The namelength field
+ * never includes the null for these old levels.
+ * Using the pad value to write the null because
+ * we don't want to add that to namelen.
+ * [MS-CIFS] sec. 2.8.1.{1-3}
+ */
+ if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0)
+ pad = 2; /* Unicode null */
+ else
+ pad = 1; /* ascii null */
+ next_entry_offset = args->fa_fixedsize + namelen + pad;
+ if (!MBC_ROOM_FOR(&xa->rep_data_mb, next_entry_offset))
+ return (1);
+ } else {
+ /*
+ * Later info levels: The file name is written WITH
+ * null termination, and the size of that null _is_
+ * included in the namelen field. There may also
+ * be padding, and we pad to align(4) like Windows.
+ * Don't include the padding in the "room for" test
+ * because we want to ignore any error writing the
+ * pad bytes after the last element.
+ */
+ if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0)
+ namelen += 2;
+ else
namelen += 1;
+ next_entry_offset = args->fa_fixedsize + namelen;
+ if (!MBC_ROOM_FOR(&xa->rep_data_mb, next_entry_offset))
+ return (1);
+ if ((next_entry_offset & 3) != 0) {
+ pad = 4 - (next_entry_offset & 3);
+ next_entry_offset += pad;
+ }
}
- next_entry_offset = args->fa_maxdata + namelen;
-
- if (MBC_ROOM_FOR(&xa->rep_data_mb, (args->fa_maxdata + namelen)) == 0)
- return (1);
-
mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE) ? SMB_MSGBUF_UNICODE : 0;
dsize32 = (fileinfo->fi_size > UINT_MAX) ?
UINT_MAX : (uint32_t)fileinfo->fi_size;
@@ -938,8 +974,11 @@ smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa,
(args->fa_lno & 1) != 0)
args->fa_lno++;
- (void) smb_mbc_encodef(&xa->rep_data_mb, "%u", sr,
- fileinfo->fi_name);
+ (void) smb_mbc_encodef(&xa->rep_data_mb, "%#u", sr,
+ namelen, fileinfo->fi_name);
+
+ if (pad)
+ (void) smb_mbc_encodef(&xa->rep_data_mb, "#.", pad);
return (0);
}
diff --git a/usr/src/uts/common/smbsrv/string.h b/usr/src/uts/common/smbsrv/string.h
index 92c4fc91b4..510fe502c3 100644
--- a/usr/src/uts/common/smbsrv/string.h
+++ b/usr/src/uts/common/smbsrv/string.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2017 by Delphix. All rights reserved.
*/
@@ -136,6 +136,9 @@ int smb_wctomb(char *, smb_wchar_t);
size_t smb_wcequiv_strlen(const char *);
size_t smb_sbequiv_strlen(const char *);
+int smb_oemtombs(char *, const uint8_t *, int);
+int smb_mbstooem(uint8_t *, const char *, int);
+
int smb_stombs(char *, char *, int);
int smb_mbstos(char *, const char *);