diff options
-rw-r--r-- | usr/src/common/smbsrv/smb_utf8.c | 230 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libsmb/common/mapfile-vers | 2 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb_kutil.c | 18 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c | 256 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb_trans2_find.c | 101 | ||||
-rw-r--r-- | usr/src/uts/common/smbsrv/string.h | 5 |
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 *); |