Print this page
NEX-19025 CIFS gets confused with filenames containing enhanced Unicode
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
and: (fix build, check-rtime)
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-4458 Incorrect directory listing response for non-UNICODE clients
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-4029 Hang listing directory with fksmbd
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3738 Should support SMB2_CAP_LARGE_MTU
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-3611 CLONE NEX-3550 Replace smb2_enable with max_protocol
Reviewed by: Yuri Pankov <Yuri.Pankov@nexenta.com>
NEX-2353 Codenomicon: SMB2 TC # 448950 - PANIC in SMB2.Compounded-commands...
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
SMB-56 extended security NTLMSSP, inbound
SMB-48 Panic with smbtorture raw.scan-eamax

@@ -20,11 +20,11 @@
  */
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  * SMB mbuf marshaling encode/decode.
  */

@@ -37,18 +37,17 @@
 #define DECODE_NO_ERROR         0
 #define DECODE_NO_MORE_DATA     1
 #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(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);
 static int mbc_marshal_put_mbuf_chain(mbuf_chain_t *mbc, mbuf_chain_t *nmbc);
 static uint8_t mbc_marshal_fetch_byte(mbuf_chain_t *mbc);

@@ -56,14 +55,14 @@
 static int mbc_marshal_get_short(mbuf_chain_t *mbc, uint16_t *data);
 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 *);
 static int mbc_marshal_get_skip(mbuf_chain_t *, uint_t);
 

@@ -150,11 +149,11 @@
 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;
         uint64_t        *llvalp;
         smb_vdb_t       *vdp;

@@ -292,11 +291,11 @@
                 case 'L':
                         if (mbc_marshal_get_char(mbc, &cval) != 0)
                                 return (-1);
                         if (cval != 2)
                                 return (-1);
-                        goto ascii_conversion;
+                        goto oem_conversion;
 
                 case 'A':
                 case 'S':
                         if (mbc_marshal_get_char(mbc, &cval) != 0)
                                 return (-1);

@@ -308,31 +307,29 @@
                 case 'u': /* Convert from unicode if flags are set */
                         if (unicode)
                                 goto unicode_translation;
                         /* FALLTHROUGH */
 
-                case 's':
-ascii_conversion:
+                case 's':       /* get 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 */
+                case 'U':       /* get UTF-16 string */
 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;
 
                 case 'Y': /* dos time to unix time tt/dd */
                         lvalp = va_arg(ap, uint32_t *);

@@ -517,10 +514,11 @@
  *      U       Align the offset of the mbuf chain on a 16bit boundary.
  */
 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;
         smb_request_t   *sr = NULL;
         uint64_t        llval;

@@ -547,11 +545,10 @@
                         repc_specified = B_TRUE;
                 } else if (c == '#') {
                         repc = va_arg(ap, int);
                         c = *fmt++;
                         repc_specified = B_TRUE;
-
                 }
 
                 switch (c) {
                 case '%':
                         sr = va_arg(ap, struct smb_request *);

@@ -657,11 +654,11 @@
                         break;
 
 
                 case 'L':
                         tag = 2;
-                        goto ascii_conversion;
+                        goto oem_conversion;
 
                 case 'S':
                 case 'A':
                         tag = 4;
                         goto tagged_str;

@@ -678,19 +675,30 @@
                 case 'u':       /* Convert from unicode if flags are set */
                         if (unicode)
                                 goto unicode_translation;
                         /* FALLTHROUGH */
 
-                case 's':       /* ASCII/multibyte string */
-ascii_conversion:       cvalp = va_arg(ap, uint8_t *);
+                case 's':       /* put 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(mbc,
+                            charp, repc) != 0)
                                 return (DECODE_NO_MORE_DATA);
                         break;
 
+                case 'U':       /* put UTF-16 string */
+unicode_translation:
+                        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;
+
                 case 'Y':               /* int32_t, encode dos date/time */
                         while (repc-- > 0) {
                                 uint16_t        d, t;
 
                                 lval = va_arg(ap, uint32_t);

@@ -726,22 +734,10 @@
                         while (repc-- > 0)
                                 if (mbc_marshal_put_char(mbc, 0) != 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++;
-                        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);
                 }
         }

@@ -918,10 +914,27 @@
 
         return (0);
 }
 
 /*
+ * Put padding sufficient to align to A, where
+ * A is some power of 2 greater than zero.
+ */
+int
+smb_mbc_put_align(mbuf_chain_t *mbc, int align)
+{
+        int mask = align - 1;
+        int padsz;
+
+        ASSERT(align > 0 && (align & mask) == 0);
+        if ((mbc->chain_offset & mask) == 0)
+                return (0);
+        padsz = align - (mbc->chain_offset & mask);
+        return (smb_mbc_encodef(mbc, "#.", padsz));
+}
+
+/*
  * Put data into mbuf chain allocating as needed.
  * Adds room to end of mbuf chain if needed.
  */
 static int
 mbc_marshal_make_room(mbuf_chain_t *mbc, int32_t bytes_needed)

@@ -1066,83 +1079,153 @@
         mbc_marshal_store_byte(mbc, data >> 56);
         return (0);
 }
 
 /*
- * 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.
+ * When (repc > 0) the length we marshal must be exactly repc, and
+ * truncate or pad the mbc data as necessary.
+ * See also: msgbuf_put_oem_string
  */
 static int
-mbc_marshal_put_ascii_string(mbuf_chain_t *mbc, char *mbs, int repc)
+mbc_marshal_put_oem_string(mbuf_chain_t *mbc, char *mbs, int repc)
 {
-        smb_wchar_t     wide_char;
-        int             nbytes;
-        int             length;
+        uint8_t         *oembuf = NULL;
+        uint8_t         *s;
+        int             oemlen;
+        int             rlen;
+        int             rc;
 
-        if ((length = smb_sbequiv_strlen(mbs)) == -1)
+        /*
+         * Compute length of converted OEM string,
+         * NOT including null terminator
+         */
+        if ((oemlen = smb_sbequiv_strlen(mbs)) == -1)
                 return (DECODE_NO_MORE_DATA);
 
-        length += sizeof (char);
-
-        if ((repc > 0) && (repc < length))
-                length = repc;
-        if (mbc_marshal_make_room(mbc, length))
+        /*
+         * If repc not specified, put whole string + NULL,
+         * otherwise will truncate or pad as needed.
+         */
+        if (repc <= 0)
+                repc = oemlen + 1;
+        if (mbc_marshal_make_room(mbc, repc))
                 return (DECODE_NO_MORE_DATA);
 
-        while (*mbs) {
                 /*
-                 * We should restore oem chars here.
+         * Convert into a temporary buffer
+         * Free oembuf before return.
                  */
-                nbytes = smb_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX);
-                if (nbytes == -1)
-                        return (DECODE_NO_MORE_DATA);
+        oembuf = smb_mem_zalloc(oemlen + 1);
+        ASSERT(oembuf != NULL);
+        rlen = smb_mbstooem(oembuf, mbs, oemlen);
+        if (rlen < 0) {
+                rc = DECODE_NO_MORE_DATA;
+                goto out;
+        }
+        if (rlen > oemlen)
+                rlen = oemlen;
+        oembuf[rlen] = '\0';
 
-                mbc_marshal_store_byte(mbc, (uint8_t)wide_char);
-
-                if (wide_char & 0xFF00)
-                        mbc_marshal_store_byte(mbc, wide_char >> 8);
-
-                mbs += nbytes;
+        /*
+         * Copy the converted string into the message,
+         * truncated or paded as required.
+         */
+        s = oembuf;
+        while (repc > 0) {
+                mbc_marshal_store_byte(mbc, *s);
+                if (*s != '\0')
+                        s++;
+                repc--;
         }
+        rc = 0;
 
-        mbc_marshal_store_byte(mbc, 0);
-        return (0);
+out:
+        if (oembuf != NULL)
+                smb_mem_free(oembuf);
+        return (rc);
 }
 
+/*
+ * Marshal a UTF-8 string (str) into mbc, converting to UTF-16.
+ * Also write a null unless the repc count limits the length.
+ * When (repc > 0) the length we marshal must be exactly repc,
+ * and truncate or pad the mbc data as necessary.
+ * See also: msgbuf_put_unicode_string
+ */
 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 *mbs, int repc)
 {
-        smb_wchar_t     wchar;
-        int             consumed;
-        int             length;
+        smb_wchar_t     *wcsbuf = NULL;
+        smb_wchar_t     *wp;
+        size_t          wcslen, wcsbytes;
+        size_t          rlen;
+        int             rc;
 
-        if ((length = smb_wcequiv_strlen(ascii)) == -1)
+        /* align to word boundary */
+        if (mbc->chain_offset & 1) {
+                if (mbc_marshal_make_room(mbc, 1))
                 return (DECODE_NO_MORE_DATA);
+                mbc_marshal_store_byte(mbc, 0);
+        }
 
-        length += sizeof (smb_wchar_t);
+        /*
+         * Compute length of converted UTF-16 string,
+         * NOT including null terminator (in bytes).
+         */
+        wcsbytes = smb_wcequiv_strlen(mbs);
+        if (wcsbytes == (size_t)-1)
+                return (DECODE_NO_MORE_DATA);
 
-        if ((repc > 0) && (repc < length))
-                length = repc;
-
-        if (mbc_marshal_make_room(mbc, length))
+        /*
+         * If repc not specified, put whole string + NULL,
+         * otherwise will truncate or pad as needed.
+         */
+        if (repc <= 0)
+                repc = wcsbytes + 2;
+        if (mbc_marshal_make_room(mbc, repc))
                 return (DECODE_NO_MORE_DATA);
-        while (length > 0) {
-                consumed = smb_mbtowc(&wchar, ascii, 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
-                 * that point. Length will continue to be decremented so
-                 * we won't get stuck here.
+         * Convert into a temporary buffer
+         * Free wcsbuf before return.
                  */
-                ascii += consumed;
+        wcslen = wcsbytes / 2;
+        wcsbuf = smb_mem_zalloc(wcsbytes + 2);
+        ASSERT(wcsbuf != NULL);
+        rlen = smb_mbstowcs(wcsbuf, mbs, wcslen);
+        if (rlen == (size_t)-1) {
+                rc = DECODE_NO_MORE_DATA;
+                goto out;
+        }
+        if (rlen > wcslen)
+                rlen = wcslen;
+        wcsbuf[rlen] = 0;
+
+        /*
+         * Copy the converted string into the message,
+         * truncated or paded as required.  Preserve
+         * little-endian order while copying.
+         */
+        wp = wcsbuf;
+        while (repc > 1) {
+                smb_wchar_t wchar = LE_IN16(wp);
                 mbc_marshal_store_byte(mbc, wchar);
                 mbc_marshal_store_byte(mbc, wchar >> 8);
-                length -= sizeof (smb_wchar_t);
+                if (wchar != 0)
+                        wp++;
+                repc -= sizeof (smb_wchar_t);
         }
-        return (0);
+        if (repc > 0)
+                mbc_marshal_store_byte(mbc, 0);
+
+        rc = 0;
+out:
+        if (wcsbuf != NULL)
+                smb_mem_free(wcsbuf);
+        return (rc);
 }
 
 static int /*ARGSUSED*/
 uiorefnoop(caddr_t p, int size, int adj)
 {

@@ -1373,105 +1456,163 @@
         }
         return (0);
 }
 
 /*
- * mbc_marshal_get_ascii_string
+ * mbc_marshal_get_oem_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.
+ * Decode an OEM string, returning its UTF-8 form in strpp,
+ * allocated using smb_srm_zalloc (automatically freed).
+ * If max_bytes != 0, consume at most max_bytes of the mbc.
+ * See also: msgbuf_get_oem_string
  */
 static int
-mbc_marshal_get_ascii_string(
-    smb_request_t       *sr,
-    mbuf_chain_t        *mbc,
-    uint8_t             **ascii,
-    int                 max_ascii)
+mbc_marshal_get_oem_string(smb_request_t *sr,
+    mbuf_chain_t *mbc, char **strpp, int max_bytes)
 {
-        char            *rcvbuf;
-        char            *ch;
-        int             max;
-        int             length = 0;
+        char            *mbs;
+        uint8_t         *oembuf = NULL;
+        int             oemlen, oemmax;
+        int             mbsmax;
+        int             rlen;
+        int             rc;
 
-        max = MALLOC_QUANTUM;
-        rcvbuf = smb_srm_zalloc(sr, max);
+        if (max_bytes == 0)
+                max_bytes = 0xffff;
 
-        if (max_ascii == 0)
-                max_ascii = 0xffff;
-
-        ch = rcvbuf;
+        /*
+         * Get the OtW data into a temporary buffer.
+         * Free oembuf before return.
+         */
+        oemlen = 0;
+        oemmax = MALLOC_QUANTUM;
+        oembuf = smb_mem_alloc(oemmax);
         for (;;) {
-                while (length < max) {
-                        if (max_ascii-- <= 0) {
-                                *ch++ = 0;
-                                goto multibyte_encode;
+                uint8_t ch;
+
+                if (oemlen >= max_bytes)
+                        break;
+                if ((oemlen + 2) >= oemmax) {
+                        oemmax += MALLOC_QUANTUM;
+                        oembuf = smb_mem_realloc(oembuf, oemmax);
                         }
-                        if (MBC_ROOM_FOR(mbc, sizeof (char)) == 0) {
-                                /* Data will never be available */
-                                return (DECODE_NO_MORE_DATA);
+                if (mbc_marshal_get_char(mbc, &ch) != 0) {
+                        rc = DECODE_NO_MORE_DATA;
+                        goto out;
                         }
-                        if ((*ch++ = mbc_marshal_fetch_byte(mbc)) == 0)
-                                goto multibyte_encode;
-                        length++;
+                if (ch == 0)
+                        break;
+                oembuf[oemlen++] = ch;
                 }
-                max += MALLOC_QUANTUM;
-                rcvbuf = smb_srm_rezalloc(sr, rcvbuf, max);
-                ch = rcvbuf + length;
-        }
+        oembuf[oemlen] = '\0';
 
-multibyte_encode:
         /*
-         * UTF-8 encode the string for internal system use.
+         * Get the buffer we'll return and convert to UTF-8.
+         * May take as much as double the space.
          */
-        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 * 2;
+        mbs = smb_srm_zalloc(sr, mbsmax + 1);
+        ASSERT(mbs != NULL);
+        rlen = smb_oemtombs(mbs, oembuf, mbsmax);
+        if (rlen < 0) {
+                rc = DECODE_NO_MORE_DATA;
+                goto out;
+        }
+        if (rlen > mbsmax)
+                rlen = mbsmax;
+        mbs[rlen] = '\0';
+        *strpp = mbs;
+        rc = 0;
+
+out:
+        if (oembuf != NULL)
+                smb_mem_free(oembuf);
+        return (rc);
 }
 
+/*
+ * 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 max_bytes != 0, consume at most max_bytes of the mbc.
+ * See also: msgbuf_get_unicode_string
+ */
 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 max_bytes)
 {
-        int             max;
-        uint16_t        wchar;
-        char            *ch;
-        int             emitted;
-        int             length = 0;
+        char            *mbs;
+        uint16_t        *wcsbuf = NULL;
+        int             wcslen;         // wchar count
+        int             wcsmax;         // byte count
+        size_t          mbsmax;
+        size_t          rlen;
+        int             rc;
 
-        if (max_unicode == 0)
-                max_unicode = 0xffff;
+        if (max_bytes == 0)
+                max_bytes = 0xffff;
 
-        max = MALLOC_QUANTUM;
-        *ascii = smb_srm_zalloc(sr, max);
+        /*
+         * Unicode strings are always word aligned.
+         */
+        if (mbc->chain_offset & 1) {
+                if (MBC_ROOM_FOR(mbc, sizeof (char)) == 0)
+                        return (DECODE_NO_MORE_DATA);
+                mbc->chain_offset++;
+        }
 
-        ch = (char *)*ascii;
+        /*
+         * Get the OtW data into a temporary buffer.
+         * Free wcsbuf before return.
+         */
+        wcslen = 0;
+        wcsmax = MALLOC_QUANTUM;
+        wcsbuf = smb_mem_alloc(wcsmax);
         for (;;) {
-                while ((length + MTS_MB_CHAR_MAX) < max) {
-                        if (max_unicode <= 0)
-                                goto done;
-                        max_unicode -= 2;
+                uint16_t        wchar;
 
-                        if (mbc_marshal_get_short(mbc, &wchar) != 0)
-                                return (DECODE_NO_MORE_DATA);
-
-                        if (wchar == 0) goto done;
-
-                        emitted = smb_wctomb(ch, wchar);
-                        length += emitted;
-                        ch += emitted;
+                if ((wcslen * 2) >= max_bytes)
+                        break;
+                if (((wcslen * 2) + 4) >= wcsmax) {
+                        wcsmax += MALLOC_QUANTUM;
+                        wcsbuf = smb_mem_realloc(wcsbuf, wcsmax);
                 }
-                max += MALLOC_QUANTUM;
-                *ascii = smb_srm_rezalloc(sr, *ascii, max);
-                ch = (char *)*ascii + length;
+                if (mbc_marshal_get_short(mbc, &wchar) != 0) {
+                        rc = DECODE_NO_MORE_DATA;
+                        goto out;
         }
-done:   *ch = 0;
-        return (0);
+                if (wchar == 0)
+                        break;
+                /* Keep in little-endian form. */
+                LE_OUT16(wcsbuf + wcslen, wchar);
+                wcslen++;
+        }
+        wcsbuf[wcslen] = 0;
+
+        /*
+         * Get the buffer we'll return and convert to UTF-8.
+         * May take as much 4X number of wide chars.
+         */
+        mbsmax = wcslen * MTS_MB_CUR_MAX;
+        mbs = smb_srm_zalloc(sr, mbsmax + 1);
+        ASSERT(mbs != NULL);
+        rlen = smb_wcstombs(mbs, wcsbuf, mbsmax);
+        if (rlen == (size_t)-1) {
+                rc = DECODE_NO_MORE_DATA;
+                goto out;
+        }
+        if (rlen > mbsmax)
+                rlen = mbsmax;
+        mbs[rlen] = '\0';
+        *strpp = mbs;
+        rc = 0;
+
+out:
+        if (wcsbuf != NULL)
+                smb_mem_free(wcsbuf);
+        return (rc);
 }
 
 static int /*ARGSUSED*/
 mbc_marshal_get_mbufs(mbuf_chain_t *mbc, int32_t bytes, mbuf_t **m)
 {

@@ -1568,32 +1709,6 @@
 {
         if (MBC_ROOM_FOR(mbc, skip) == 0)
                 return (DECODE_NO_MORE_DATA);
         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);
 }