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-2460 libfksmbd should not link with libsmb
SMB-56 extended security NTLMSSP, inbound
*** 20,30 ****
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
! * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
* Msgbuf buffer management implementation. The smb_msgbuf interface is
* typically used to encode or decode SMB data using sprintf/scanf
--- 20,30 ----
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
! * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
* Msgbuf buffer management implementation. The smb_msgbuf interface is
* typically used to encode or decode SMB data using sprintf/scanf
*** 51,60 ****
--- 51,66 ----
static int buf_decode(smb_msgbuf_t *, char *, va_list ap);
static int buf_encode(smb_msgbuf_t *, char *, va_list ap);
static void *smb_msgbuf_malloc(smb_msgbuf_t *, size_t);
static int smb_msgbuf_chkerc(char *text, int erc);
+ static int msgbuf_get_oem_string(smb_msgbuf_t *, char **, int);
+ static int msgbuf_get_unicode_string(smb_msgbuf_t *, char **, int);
+ static int msgbuf_put_oem_string(smb_msgbuf_t *, char *, int);
+ static int msgbuf_put_unicode_string(smb_msgbuf_t *, char *, int);
+
+
/*
* Returns the offset or number of bytes used within the buffer.
*/
size_t
smb_msgbuf_used(smb_msgbuf_t *mb)
*** 175,185 ****
* smb_msgbuf_decode
*
* Decode a smb_msgbuf buffer as indicated by the format string into
* the variable arg list. This is similar to a scanf operation.
*
! * On success, returns the number of bytes encoded. Otherwise
* returns a -ve error code.
*/
int
smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...)
{
--- 181,191 ----
* smb_msgbuf_decode
*
* Decode a smb_msgbuf buffer as indicated by the format string into
* the variable arg list. This is similar to a scanf operation.
*
! * On success, returns the number of bytes decoded. Otherwise
* returns a -ve error code.
*/
int
smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...)
{
*** 211,229 ****
* ensure correct behaviour and error handling.
*/
static int
buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap)
{
- uint32_t ival;
uint8_t c;
uint8_t *bvalp;
uint16_t *wvalp;
uint32_t *lvalp;
uint64_t *llvalp;
- char *cvalp;
char **cvalpp;
- smb_wchar_t wchar;
boolean_t repc_specified;
int repc;
int rc;
while ((c = *fmt++) != 0) {
--- 217,232 ----
*** 322,401 ****
case 'u': /* Convert from unicode if flags are set */
if (mb->flags & SMB_MSGBUF_UNICODE)
goto unicode_translation;
/*FALLTHROUGH*/
! case 's': /* get string */
! if (!repc_specified)
! repc = strlen((const char *)mb->scan) + 1;
! if (smb_msgbuf_has_space(mb, repc) == 0)
! return (SMB_MSGBUF_UNDERFLOW);
! if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
! return (SMB_MSGBUF_UNDERFLOW);
cvalpp = va_arg(ap, char **);
! *cvalpp = cvalp;
! /* Translate OEM to mbs */
! while (repc > 0) {
! wchar = *mb->scan++;
! repc--;
! if (wchar == 0)
break;
- ival = smb_wctomb(cvalp, wchar);
- cvalp += ival;
- }
- *cvalp = '\0';
- if (repc > 0)
- mb->scan += repc;
- break;
! case 'U': /* get unicode string */
unicode_translation:
- /*
- * Unicode strings are always word aligned.
- * The malloc'd area is larger than the
- * original string because the UTF-8 chars
- * may be longer than the wide-chars.
- */
- smb_msgbuf_word_align(mb);
- if (!repc_specified) {
- /*
- * Count bytes, including the null.
- */
- uint8_t *tmp_scan = mb->scan;
- repc = 2; /* the null */
- while ((wchar = LE_IN16(tmp_scan)) != 0) {
- tmp_scan += 2;
- repc += 2;
- }
- }
- if (smb_msgbuf_has_space(mb, repc) == 0)
- return (SMB_MSGBUF_UNDERFLOW);
- /*
- * Get space for translated string
- * Allocates worst-case size.
- */
- if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
- return (SMB_MSGBUF_UNDERFLOW);
cvalpp = va_arg(ap, char **);
! *cvalpp = cvalp;
! /*
! * Translate unicode to mbs, stopping after
! * null or repc limit.
! */
! while (repc >= 2) {
! wchar = LE_IN16(mb->scan);
! mb->scan += 2;
! repc -= 2;
! if (wchar == 0)
break;
- ival = smb_wctomb(cvalp, wchar);
- cvalp += ival;
- }
- *cvalp = '\0';
- if (repc > 0)
- mb->scan += repc;
- break;
case 'M':
if (smb_msgbuf_has_space(mb, 4) == 0)
return (SMB_MSGBUF_UNDERFLOW);
--- 325,352 ----
case 'u': /* Convert from unicode if flags are set */
if (mb->flags & SMB_MSGBUF_UNICODE)
goto unicode_translation;
/*FALLTHROUGH*/
! case 's': /* get OEM string */
cvalpp = va_arg(ap, char **);
! if (!repc_specified)
! repc = 0;
! rc = msgbuf_get_oem_string(mb, cvalpp, repc);
! if (rc != 0)
! return (rc);
break;
! case 'U': /* get UTF-16 string */
unicode_translation:
cvalpp = va_arg(ap, char **);
! if (!repc_specified)
! repc = 0;
! rc = msgbuf_get_unicode_string(mb, cvalpp, repc);
! if (rc != 0)
! return (rc);
break;
case 'M':
if (smb_msgbuf_has_space(mb, 4) == 0)
return (SMB_MSGBUF_UNDERFLOW);
*** 414,425 ****
--- 365,521 ----
}
return (SMB_MSGBUF_SUCCESS);
}
+ /*
+ * msgbuf_get_oem_string
+ *
+ * Decode an OEM string, returning its UTF-8 form in strpp,
+ * allocated using smb_msgbuf_malloc (automatically freed).
+ * If max_bytes != 0, consume at most max_bytes of the mb.
+ * See also: mbc_marshal_get_oem_string
+ */
+ static int
+ msgbuf_get_oem_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
+ {
+ char *mbs;
+ uint8_t *oembuf = NULL;
+ int oemlen; // len of OEM string, w/o null
+ int datalen; // OtW data len
+ int mbsmax; // max len of ret str
+ int rlen;
+ if (max_bytes == 0)
+ max_bytes = 0xffff;
+
+ /*
+ * Determine the OtW data length and OEM string length
+ * Note: oemlen is the string length (w/o null) and
+ * datalen is how much we move mb->scan
+ */
+ datalen = 0;
+ oemlen = 0;
+ for (;;) {
+ if (datalen >= max_bytes)
+ break;
+ /* in-line smb_msgbuf_has_space */
+ if ((mb->scan + datalen) >= mb->end)
+ return (SMB_MSGBUF_UNDERFLOW);
+ datalen++;
+ if (mb->scan[datalen - 1] == 0)
+ break;
+ oemlen++;
+ }
+
+ /*
+ * Get datalen bytes into a temp buffer
+ * sized with room to add a null.
+ * Free oembuf in smb_msgbuf_term
+ */
+ oembuf = smb_msgbuf_malloc(mb, datalen + 1);
+ if (oembuf == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ bcopy(mb->scan, oembuf, datalen);
+ mb->scan += datalen;
+ oembuf[oemlen] = '\0';
+
+ /*
+ * Get the buffer we'll return and convert to UTF-8.
+ * May take as much as double the space.
+ */
+ mbsmax = oemlen * 2;
+ mbs = smb_msgbuf_malloc(mb, mbsmax + 1);
+ if (mbs == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ rlen = smb_oemtombs(mbs, oembuf, mbsmax);
+ if (rlen < 0)
+ return (SMB_MSGBUF_UNDERFLOW);
+ if (rlen > mbsmax)
+ rlen = mbsmax;
+ mbs[rlen] = '\0';
+ *strpp = mbs;
+ return (0);
+ }
+
/*
+ * msgbuf_get_unicode_string
+ *
+ * Decode a UTF-16 string, returning its UTF-8 form in strpp,
+ * allocated using smb_msgbuf_malloc (automatically freed).
+ * If max_bytes != 0, consume at most max_bytes of the mb.
+ * See also: mbc_marshal_get_unicode_string
+ */
+ static int
+ msgbuf_get_unicode_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
+ {
+ char *mbs;
+ uint16_t *wcsbuf = NULL;
+ int wcslen; // wchar count
+ int datalen; // OtW data len
+ size_t mbsmax; // max len of ret str
+ size_t rlen;
+
+ if (max_bytes == 0)
+ max_bytes = 0xffff;
+
+ /*
+ * Unicode strings are always word aligned.
+ */
+ smb_msgbuf_word_align(mb);
+
+ /*
+ * Determine the OtW data length and (WC) string length
+ * Note: wcslen counts 16-bit wide_chars (w/o null),
+ * and datalen is how much we move mb->scan
+ */
+ datalen = 0;
+ wcslen = 0;
+ for (;;) {
+ if (datalen >= max_bytes)
+ break;
+ /* in-line smb_msgbuf_has_space */
+ if ((mb->scan + datalen) >= mb->end)
+ return (SMB_MSGBUF_UNDERFLOW);
+ datalen += 2;
+ if (mb->scan[datalen - 2] == 0 &&
+ mb->scan[datalen - 1] == 0)
+ break;
+ wcslen++;
+ }
+
+ /*
+ * Get datalen bytes into a temp buffer
+ * sized with room to add a (WC) null.
+ * Note: wcsbuf has little-endian order
+ */
+ wcsbuf = smb_msgbuf_malloc(mb, datalen + 2);
+ if (wcsbuf == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ bcopy(mb->scan, wcsbuf, datalen);
+ mb->scan += datalen;
+ 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_msgbuf_malloc(mb, mbsmax + 1);
+ if (mbs == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ rlen = smb_wcstombs(mbs, wcsbuf, mbsmax);
+ if (rlen == (size_t)-1)
+ return (SMB_MSGBUF_UNDERFLOW);
+ if (rlen > mbsmax)
+ rlen = mbsmax;
+ mbs[rlen] = '\0';
+ *strpp = mbs;
+ return (0);
+ }
+
+ /*
* smb_msgbuf_encode
*
* Encode a smb_msgbuf buffer as indicated by the format string using
* the variable arg list. This is similar to a sprintf operation.
*
*** 464,475 ****
uint32_t lval;
uint64_t llval;
uint8_t *bvalp;
char *cvalp;
uint8_t c;
- smb_wchar_t wchar;
- int count;
boolean_t repc_specified;
int repc;
int rc;
while ((c = *fmt++) != 0) {
--- 560,569 ----
*** 569,654 ****
case 'u': /* conditional unicode */
if (mb->flags & SMB_MSGBUF_UNICODE)
goto unicode_translation;
/* FALLTHROUGH */
! case 's': /* put string */
cvalp = va_arg(ap, char *);
! if (!repc_specified) {
! repc = smb_sbequiv_strlen(cvalp);
! if (repc == -1)
! return (SMB_MSGBUF_OVERFLOW);
! if (!(mb->flags & SMB_MSGBUF_NOTERM))
! repc++;
! }
! if (smb_msgbuf_has_space(mb, repc) == 0)
! return (SMB_MSGBUF_OVERFLOW);
! while (repc > 0) {
! count = smb_mbtowc(&wchar, cvalp,
! MTS_MB_CHAR_MAX);
! if (count < 0)
! return (SMB_MSGBUF_DATA_ERROR);
! cvalp += count;
! if (wchar == 0)
break;
- *mb->scan++ = (uint8_t)wchar;
- repc--;
- if (wchar & 0xff00) {
- *mb->scan++ = wchar >> 8;
- repc--;
- }
- }
- if (*cvalp == '\0' && repc > 0 &&
- (mb->flags & SMB_MSGBUF_NOTERM) == 0) {
- *mb->scan++ = 0;
- repc--;
- }
- while (repc > 0) {
- *mb->scan++ = 0;
- repc--;
- }
- break;
! case 'U': /* put unicode string */
unicode_translation:
- /*
- * Unicode strings are always word aligned.
- */
- smb_msgbuf_word_align(mb);
cvalp = va_arg(ap, char *);
! if (!repc_specified) {
! repc = smb_wcequiv_strlen(cvalp);
! if (!(mb->flags & SMB_MSGBUF_NOTERM))
! repc += 2;
! }
! if (!smb_msgbuf_has_space(mb, repc))
! return (SMB_MSGBUF_OVERFLOW);
! while (repc >= 2) {
! count = smb_mbtowc(&wchar, cvalp,
! MTS_MB_CHAR_MAX);
! if (count < 0)
! return (SMB_MSGBUF_DATA_ERROR);
! cvalp += count;
! if (wchar == 0)
break;
- LE_OUT16(mb->scan, wchar);
- mb->scan += 2;
- repc -= 2;
- }
- if (*cvalp == '\0' && repc >= 2 &&
- (mb->flags & SMB_MSGBUF_NOTERM) == 0) {
- LE_OUT16(mb->scan, 0);
- mb->scan += 2;
- repc -= 2;
- }
- while (repc > 0) {
- *mb->scan++ = 0;
- repc--;
- }
- break;
-
case 'M':
if (smb_msgbuf_has_space(mb, 4) == 0)
return (SMB_MSGBUF_OVERFLOW);
*mb->scan++ = 0xFF;
--- 663,691 ----
case 'u': /* conditional unicode */
if (mb->flags & SMB_MSGBUF_UNICODE)
goto unicode_translation;
/* FALLTHROUGH */
! case 's': /* put OEM string */
cvalp = va_arg(ap, char *);
! if (!repc_specified)
! repc = 0;
! rc = msgbuf_put_oem_string(mb, cvalp, repc);
! if (rc != 0)
! return (rc);
break;
! case 'U': /* put UTF-16 string */
unicode_translation:
cvalp = va_arg(ap, char *);
! if (!repc_specified)
! repc = 0;
! rc = msgbuf_put_unicode_string(mb, cvalp, repc);
! if (rc != 0)
! return (rc);
break;
case 'M':
if (smb_msgbuf_has_space(mb, 4) == 0)
return (SMB_MSGBUF_OVERFLOW);
*mb->scan++ = 0xFF;
*** 663,674 ****
--- 700,846 ----
}
return (SMB_MSGBUF_SUCCESS);
}
+ /*
+ * 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 mb data as necessary.
+ * See also: mbc_marshal_put_oem_string
+ */
+ static int
+ msgbuf_put_oem_string(smb_msgbuf_t *mb, char *mbs, int repc)
+ {
+ uint8_t *oembuf = NULL;
+ uint8_t *s;
+ int oemlen;
+ int rlen;
+ /*
+ * Compute length of converted OEM string,
+ * NOT including null terminator
+ */
+ if ((oemlen = smb_sbequiv_strlen(mbs)) == -1)
+ return (SMB_MSGBUF_DATA_ERROR);
+
+ /*
+ * If repc not specified, put whole string + NULL,
+ * otherwise will truncate or pad as needed.
+ */
+ if (repc <= 0) {
+ repc = oemlen;
+ if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
+ repc += sizeof (char);
+ }
+ if (smb_msgbuf_has_space(mb, repc) == 0)
+ return (SMB_MSGBUF_OVERFLOW);
+
+ /*
+ * Convert into a temporary buffer
+ * Free oembuf in smb_msgbuf_term.
+ */
+ oembuf = smb_msgbuf_malloc(mb, oemlen + 1);
+ if (oembuf == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ rlen = smb_mbstooem(oembuf, mbs, oemlen);
+ if (rlen < 0)
+ return (SMB_MSGBUF_DATA_ERROR);
+ if (rlen > oemlen)
+ rlen = oemlen;
+ oembuf[rlen] = '\0';
+
+ /*
+ * Copy the converted string into the message,
+ * truncated or paded as required.
+ */
+ s = oembuf;
+ while (repc > 0) {
+ *mb->scan++ = *s;
+ if (*s != '\0')
+ s++;
+ repc--;
+ }
+
+ return (0);
+ }
+
/*
+ * 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 mb data as necessary.
+ * See also: mbc_marshal_put_unicode_string
+ */
+ static int
+ msgbuf_put_unicode_string(smb_msgbuf_t *mb, char *mbs, int repc)
+ {
+ smb_wchar_t *wcsbuf = NULL;
+ smb_wchar_t *wp;
+ size_t wcslen, wcsbytes;
+ size_t rlen;
+
+ /* align to word boundary */
+ smb_msgbuf_word_align(mb);
+
+ /*
+ * Compute length of converted UTF-16 string,
+ * NOT including null terminator (in bytes).
+ */
+ wcsbytes = smb_wcequiv_strlen(mbs);
+ if (wcsbytes == (size_t)-1)
+ return (SMB_MSGBUF_DATA_ERROR);
+
+ /*
+ * If repc not specified, put whole string + NULL,
+ * otherwise will truncate or pad as needed.
+ */
+ if (repc <= 0) {
+ repc = (int)wcsbytes;
+ if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
+ repc += sizeof (smb_wchar_t);
+ }
+ if (smb_msgbuf_has_space(mb, repc) == 0)
+ return (SMB_MSGBUF_OVERFLOW);
+
+ /*
+ * Convert into a temporary buffer
+ * Free wcsbuf in smb_msgbuf_term
+ */
+ wcslen = wcsbytes / 2;
+ wcsbuf = smb_msgbuf_malloc(mb, wcsbytes + 2);
+ if (wcsbuf == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ rlen = smb_mbstowcs(wcsbuf, mbs, wcslen);
+ if (rlen == (size_t)-1)
+ return (SMB_MSGBUF_DATA_ERROR);
+ 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);
+ LE_OUT16(mb->scan, wchar);
+ mb->scan += 2;
+ if (wchar != 0)
+ wp++;
+ repc -= sizeof (smb_wchar_t);
+ }
+ if (repc > 0)
+ *mb->scan++ = '\0';
+
+ return (0);
+ }
+
+ /*
* smb_msgbuf_malloc
*
* Allocate some memory for use with this smb_msgbuf. We increase the
* requested size to hold the list pointer and return a pointer
* to the area for use by the caller.