diff options
author | Gordon Ross <gwr@nexenta.com> | 2018-11-06 01:27:53 -0500 |
---|---|---|
committer | Gordon Ross <gwr@nexenta.com> | 2019-06-03 22:09:35 -0400 |
commit | 7d1ffc32e5e72873791b96934af035e0f051fc14 (patch) | |
tree | 1b712216a61d8178e5df14b9bd4e1bb4a4a99050 | |
parent | 07a6ae61f8958faa11352bf1b552d85d79e9cbbe (diff) | |
download | illumos-joyent-7d1ffc32e5e72873791b96934af035e0f051fc14.tar.gz |
7587 SMB should support enhanced Unicode
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
20 files changed, 2959 insertions, 429 deletions
diff --git a/exception_lists/check_rtime b/exception_lists/check_rtime index 0c9a2cbf57..fa34af4770 100644 --- a/exception_lists/check_rtime +++ b/exception_lists/check_rtime @@ -228,6 +228,7 @@ FORBIDDEN_DEP usr/lib/MACH(smbfs)/libfknsmb.so.1 FORBIDDEN_DEP usr/lib/MACH(smbfs)/libfksmbfs.so.1 FORBIDDEN_DEP usr/lib/MACH(smbsrv)/libfksmbsrv.so.1 FORBIDDEN_DEP usr/lib/smbsrv/fksmbd +FORBIDDEN_DEP usr/lib/smbsrv/test-msgbuf FORBIDDEN_DEP usr/sbin/amd64/zdb FORBIDDEN_DEP usr/sbin/i86/zdb FORBIDDEN_DEP usr/sbin/sparcv7/zdb diff --git a/exception_lists/packaging b/exception_lists/packaging index 7d99df560a..90caaa7a2f 100644 --- a/exception_lists/packaging +++ b/exception_lists/packaging @@ -603,6 +603,7 @@ usr/lib/smbsrv/libfksmbsrv.so.1 usr/lib/smbsrv/libmlsvc.so usr/lib/smbsrv/libsmb.so usr/lib/smbsrv/libsmbns.so +usr/lib/smbsrv/test-msgbuf # # # Private/Internal 64-bit libraries of smbsrv. Do not ship. diff --git a/usr/src/cmd/smbsrv/Makefile b/usr/src/cmd/smbsrv/Makefile index 641026e8e9..193ce84c88 100644 --- a/usr/src/cmd/smbsrv/Makefile +++ b/usr/src/cmd/smbsrv/Makefile @@ -25,7 +25,8 @@ # Copyright 2013 Nexenta Systems, Inc. All rights reserved. # -SUBDIRS = smbadm smbd smbstat dtrace fksmbd bind-helper +SUBDIRS = smbadm smbd smbstat dtrace fksmbd bind-helper \ + test-msgbuf MSGSUBDIRS = smbadm smbstat include ../Makefile.cmd diff --git a/usr/src/cmd/smbsrv/test-msgbuf/.dbxrc b/usr/src/cmd/smbsrv/test-msgbuf/.dbxrc new file mode 100644 index 0000000000..983ebcfb35 --- /dev/null +++ b/usr/src/cmd/smbsrv/test-msgbuf/.dbxrc @@ -0,0 +1,22 @@ + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Nexenta Systems, Inc. All rights reserved. +# + +set -o emacs + +setenv ROOT ${CODEMGR_WS}/proto/root_i386 +setenv LD_LIBRARY_PATH ${ROOT}/usr/lib/smbsrv:${ROOT}/usr/lib:${ROOT}/lib + +echo debug ${ROOT}/usr/lib/smbsrv/test-msgbuf diff --git a/usr/src/cmd/smbsrv/test-msgbuf/Makefile b/usr/src/cmd/smbsrv/test-msgbuf/Makefile new file mode 100644 index 0000000000..96ab0292d1 --- /dev/null +++ b/usr/src/cmd/smbsrv/test-msgbuf/Makefile @@ -0,0 +1,124 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2018 Nexenta Systems, Inc. All rights reserved. +# + + +PROG= test-msgbuf + +OBJS_LOCAL= test_main.o test_misc.o test_conv.o \ + test_mbmarshal.o test_msgbuf.o +OBJS_SMBSRV= smb_mbuf_marshaling.o smb_mbuf_util.o smb_alloc.o +OBJS_SMBCMN= smb_msgbuf.o smb_string.o smb_utf8.o + +OBJS= ${OBJS_LOCAL} ${OBJS_SMBSRV} ${OBJS_SMBCMN} + +SMBSRV_SRCDIR=../../../uts/common/fs/smbsrv +SMBSRV_CMNDIR=../../../common/smbsrv +SRCS= ${OBJS_LOCAL:.o=.c} \ + ${OBJS_SMBSRV:%.o=${SMBSRV_SRCDIR}/%.c} \ + ${OBJS_SMBCMN:%.o=${SMBSRV_CMNDIR}/%.c} + +include ../../Makefile.cmd +include ../../Makefile.ctf + +# Note: need our sys includes _before_ ENVCPPFLAGS, proto etc. +CPPFLAGS.first += -I. +CPPFLAGS.first += -I../../../lib/libfakekernel/common +CPPFLAGS.first += -I../../../lib/smbsrv/libfksmbsrv/common + +INCS += -I../../../uts/common +INCS += -I../../../uts/common/smbsrv +INCS += -I../../../common/smbsrv + +CSTD= $(CSTD_GNU99) +C99LMODE= -Xc99=%all + +CFLAGS += $(CCVERBOSE) +CFLAGS64 += $(CCVERBOSE) + +CERRWARN += -_gcc=-Wno-parentheses + +CPPFLAGS += -D_REENTRANT +CPPFLAGS += -DTESTJIG +CPPFLAGS += -Dsyslog=smb_syslog +CPPFLAGS += -D_LARGEFILE64_SOURCE=1 +# Always debug here +CPPFLAGS += -DDEBUG +CPPFLAGS += $(INCS) + +LDFLAGS += $(ZNOLAZYLOAD) +LDFLAGS += '-R$$ORIGIN/..' +LDLIBS += -lfakekernel -lcmdutils + +LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2 +LINTFLAGS += -xerroff=E_NAME_USED_NOT_DEF2 +LINTFLAGS += -xerroff=E_INCONS_ARG_DECL2 +LINTFLAGS += -xerroff=E_INCONS_VAL_TYPE_DECL2 + +ROOTSMBDDIR = $(ROOTLIB)/smbsrv +ROOTSMBDFILE = $(PROG:%=$(ROOTSMBDDIR)/%) + +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +clean: + -$(RM) $(OBJS) + +lint: # lint_SRCS + +include ../../Makefile.targ + +install: all $(ROOTSMBDFILE) + +test_main.o : test_main.c + $(CC) $(CFLAGS) $(CPPFLAGS) -D_KMEMUSER -c test_main.c + $(POST_PROCESS_O) + +test_misc.o : test_misc.c + $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \ + -I../../../uts/common/smbsrv \ + -I../../../common/smbsrv -c test_misc.c + $(POST_PROCESS_O) + +# OBJS_SMBSRV +%.o: ../../../uts/common/fs/smbsrv/%.c + $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \ + -I../../../uts/common/smbsrv \ + -I../../../common/smbsrv -c $< + $(POST_PROCESS_O) + +# OBJS_SMBCMN +%.o: ../../../common/smbsrv/%.c + $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \ + -I../../../uts/common/smbsrv \ + -I../../../common/smbsrv -c $< + $(POST_PROCESS_O) + +$(ROOTSMBDDIR)/%: % + $(INS.file) diff --git a/usr/src/cmd/smbsrv/test-msgbuf/Run.sh b/usr/src/cmd/smbsrv/test-msgbuf/Run.sh new file mode 100755 index 0000000000..9063607a95 --- /dev/null +++ b/usr/src/cmd/smbsrv/test-msgbuf/Run.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Nexenta Systems, Inc. All rights reserved. +# + +# Helper program to run test-msgbuf (unit test program) +# using binaries from the proto area. + +[ -n "$CODEMGR_WS" ] || { + echo "Need a buildenv to set CODEMGR_WS=..." + exit 1; +} + +ROOT=${CODEMGR_WS}/proto/root_i386 +LD_LIBRARY_PATH=$ROOT/usr/lib/smbsrv:$ROOT/usr/lib:$ROOT/lib +export LD_LIBRARY_PATH +export UMEM_DEBUG=default + +# run with the passed options +exec $ROOT/usr/lib/smbsrv/test-msgbuf "$@" diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_conv.c b/usr/src/cmd/smbsrv/test-msgbuf/test_conv.c new file mode 100644 index 0000000000..224d0373ec --- /dev/null +++ b/usr/src/cmd/smbsrv/test-msgbuf/test_conv.c @@ -0,0 +1,342 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Test conversion of strings UTF-8 to/from UTF-16 etc. + * + * This tests both 16-bit unicode symbols (UCS-2) and so called + * "enhanced" unicode symbols such as the "poop emoji" that are + * above 65535 and encode to four bytes as UTF-8. + */ + +#include <sys/types.h> +#include <sys/debug.h> +#include <sys/u8_textprep.h> +#include <smbsrv/string.h> +#include <stdio.h> +#include <string.h> + +#include "test_defs.h" + +#define U_FW_A 0xff21 // full-width A (A) +static const char fwA[4] = "\xef\xbc\xa1"; + +#define U_POOP 0x1f4a9 // poop emoji (💩) +static const char poop[5] = "\xf0\x9f\x92\xa9"; + +static char mbsa[] = "A\xef\xbc\xa1."; // A fwA . (5) +static char mbsp[] = "P\xf0\x9f\x92\xa9."; // P poop . (6) +static smb_wchar_t wcsa[] = { 'A', U_FW_A, '.', 0 }; // (3) +static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4) + + +static void +conv_wctomb() +{ + char mbs[8]; + int len; + + len = smb_wctomb(mbs, U_FW_A); + if (len != 3) { + printf("Fail: conv_wctomb fwA ret=%d\n", len); + return; + } + mbs[len] = '\0'; + if (strcmp(mbs, fwA)) { + printf("Fail: conv_wctomb fwA cmp:\n"); + hexdump((uchar_t *)mbs, len+1); + return; + } + + len = smb_wctomb(mbs, U_POOP); + if (len != 4) { + printf("Fail: conv_wctomb poop ret=%d\n", len); + return; + } + mbs[len] = '\0'; + if (strcmp(mbs, poop)) { + printf("Fail: conv_wctomb poop cmp:\n"); + hexdump((uchar_t *)mbs, len+1); + return; + } + + /* null wc to mbs should return 1 and put a null */ + len = smb_wctomb(mbs, 0); + if (len != 1) { + printf("Fail: conv_wctomb null ret=%d\n", len); + return; + } + if (mbs[0] != '\0') { + printf("Fail: conv_wctomb null cmp:\n"); + hexdump((uchar_t *)mbs, len+1); + return; + } + + printf("Pass: conv_wctomb\n"); +} + +static void +conv_mbtowc() +{ + uint32_t wch = 0; + int len; + + /* + * The (void *) cast here is to let this build both + * before and after an interface change in smb_mbtowc + * (uint16_t vs uint32_t) + */ + len = smb_mbtowc((void *)&wch, fwA, 4); + if (len != 3) { + printf("Fail: conv_mbtowc fwA ret=%d\n", len); + return; + } + if (wch != U_FW_A) { + printf("Fail: conv_mbtowc fwA cmp: 0x%x\n", wch); + return; + } + + len = smb_mbtowc((void *)&wch, poop, 4); // poop emoji + if (len != 4) { + printf("Fail: conv_mbtowc poop ret=%d\n", len); + return; + } + if (wch != U_POOP) { + printf("Fail: conv_mbtowc poop cmp: 0x%x\n", wch); + return; + } + + /* null mbs to wc should return 0 (and set wch=0) */ + len = smb_mbtowc((void *)&wch, "", 4); + if (len != 0) { + printf("Fail: conv_mbtowc null ret=%d\n", len); + return; + } + if (wch != 0) { + printf("Fail: conv_mbtowc null cmp: 0x%x\n", wch); + return; + } + + printf("Pass: conv_mbtowc\n"); +} + +static void +conv_wcstombs() +{ + char tmbs[16]; + int len; + + len = smb_wcstombs(tmbs, wcsa, sizeof (tmbs)); + if (len != 5) { + printf("Fail: conv_wcstombs A ret=%d\n", len); + return; + } + if (strcmp(tmbs, mbsa)) { + printf("Fail: conv_wcstombs A cmp:\n"); + hexdump((uchar_t *)tmbs, len+2); + return; + } + + len = smb_wcstombs(tmbs, wcsp, sizeof (tmbs)); + if (len != 6) { + printf("Fail: conv_wcstombs f ret=%d\n", len); + return; + } + if (strcmp(tmbs, mbsp)) { + printf("Fail: conv_wcstombs f cmp:\n"); + hexdump((uchar_t *)tmbs, len+2); + return; + } + + printf("Pass: conv_wcstombs\n"); +} + +static void +conv_mbstowcs() +{ + smb_wchar_t twcs[8]; + uint32_t wch = 0; + int len; + + len = smb_mbstowcs(twcs, mbsa, sizeof (twcs)); + if (len != 3) { + printf("Fail: conv_mbstowcs A ret=%d\n", len); + return; + } + if (memcmp(twcs, wcsa, len+2)) { + printf("Fail: conv_mbstowcs A cmp: 0x%x\n", wch); + hexdump((uchar_t *)twcs, len+2); + return; + } + + len = smb_mbstowcs(twcs, mbsp, sizeof (twcs)); + if (len != 4) { + printf("Fail: conv_mbstowcs P ret=%d\n", len); + return; + } + if (memcmp(twcs, wcsp, len+2)) { + printf("Fail: conv_mbstowcs P cmp: 0x%x\n", wch); + hexdump((uchar_t *)twcs, len+2); + return; + } + + printf("Pass: conv_mbstowcs\n"); +} + +/* + * An OEM string that will require iconv. + */ +static uchar_t fubar_oem[] = "F\201bar"; // CP850 x81 (ü) +static char fubar_mbs[] = "F\303\274bar"; // UTF8 xC3 xBC + + +static void +conv_oemtombs() +{ + char tmbs[16]; + int len; + + len = smb_oemtombs(tmbs, (uchar_t *)"foo", 4); + if (len != 3) { + printf("Fail: conv_wctomb foo ret=%d\n", len); + return; + } + if (strcmp(tmbs, "foo")) { + printf("Fail: conv_wctomb foo cmp:\n"); + hexdump((uchar_t *)tmbs, len+1); + return; + } + + len = smb_oemtombs(tmbs, fubar_oem, 7); + if (len != 6) { + printf("Fail: conv_oemtombs fubar ret=%d\n", len); + return; + } + if (strcmp(tmbs, fubar_mbs)) { + printf("Fail: conv_oemtombs fubar cmp:\n"); + hexdump((uchar_t *)tmbs, len+1); + return; + } + + printf("Pass: conv_oemtombs\n"); +} + +static void +conv_mbstooem() +{ + uint8_t oemcs[8]; + uint32_t wch = 0; + int len; + + len = smb_mbstooem(oemcs, "foo", 8); + if (len != 3) { + printf("Fail: conv_mbstooem foo ret=%d\n", len); + return; + } + if (memcmp(oemcs, "foo", len+1)) { + printf("Fail: conv_mbstooem P cmp: 0x%x\n", wch); + hexdump((uchar_t *)oemcs, len+1); + return; + } + + len = smb_mbstooem(oemcs, fubar_mbs, 8); + if (len != 5) { + printf("Fail: conv_mbstooem fubar ret=%d\n", len); + return; + } + if (memcmp(oemcs, (char *)fubar_oem, len+1)) { + printf("Fail: conv_mbstooem fubar cmp: 0x%x\n", wch); + hexdump((uchar_t *)oemcs, len+1); + return; + } + + len = smb_mbstooem(oemcs, mbsp, 8); + if (len != 3) { + printf("Fail: conv_mbstooem poop ret=%d\n", len); + return; + } + if (memcmp(oemcs, "P?.", len+1)) { + printf("Fail: conv_mbstooem poop cmp: 0x%x\n", wch); + hexdump((uchar_t *)oemcs, len+1); + return; + } + + printf("Pass: conv_mbstooem\n"); +} + +static void +conv_sbequiv_strlen() +{ + int len; + + len = (int)smb_sbequiv_strlen("a"); + if (len != 1) { + printf("Fail: conv_sbequiv_strlen (a) len=%d\n", len); + return; + } + + len = (int)smb_sbequiv_strlen(fubar_mbs); + if (len != strlen((char *)fubar_oem)) { + printf("Fail: conv_sbequiv_strlen (fubar) len=%d\n", len); + return; + } + + len = (int)smb_sbequiv_strlen(mbsp); + if (len != 3) { // "P?." + printf("Fail: conv_sbequiv_strlen (poop) len=%d\n", len); + return; + } + + printf("Pass: conv_sbequiv_strlen\n"); +} + +static void +conv_wcequiv_strlen() +{ + int len; + + len = (int)smb_wcequiv_strlen("a"); + if (len != 2) { + printf("Fail: conv_wcequiv_strlen (a) len=%d\n", len); + return; + } + + len = (int)smb_wcequiv_strlen(fwA); + if (len != 2) { + printf("Fail: conv_wcequiv_strlen (fwA) len=%d\n", len); + return; + } + + len = (int)smb_wcequiv_strlen(poop); + if (len != 4) { + printf("Fail: conv_wcequiv_strlen (poop) len=%d\n", len); + return; + } + + printf("Pass: conv_wcequiv_strlen\n"); +} + +void +test_conv() +{ + conv_wctomb(); + conv_mbtowc(); + conv_wcstombs(); + conv_mbstowcs(); + conv_oemtombs(); + conv_mbstooem(); + conv_sbequiv_strlen(); + conv_wcequiv_strlen(); +} diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_defs.h b/usr/src/cmd/smbsrv/test-msgbuf/test_defs.h new file mode 100644 index 0000000000..c7e6a4eeb1 --- /dev/null +++ b/usr/src/cmd/smbsrv/test-msgbuf/test_defs.h @@ -0,0 +1,36 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +#ifndef _TEST_DEFS_H +#define _TEST_DEFS_H + +/* + * Describe the purpose of the file here. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void hexdump(const uchar_t *buf, int len); +extern void test_conv(void); +extern void test_mbmarshal(void); +extern void test_msgbuf(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _TEST_DEFS_H */ diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_main.c b/usr/src/cmd/smbsrv/test-msgbuf/test_main.c new file mode 100644 index 0000000000..e79ccc08da --- /dev/null +++ b/usr/src/cmd/smbsrv/test-msgbuf/test_main.c @@ -0,0 +1,86 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Test & debug program for smb_msgbuf.c and smb_mbuf_marshaling.c + */ + +#include <sys/types.h> +#include <sys/debug.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> + +#include "test_defs.h" + + +int +main(int argc, char *argv[]) +{ + + test_conv(); + test_mbmarshal(); + test_msgbuf(); + + return (0); +} + +void +hexdump(const uchar_t *buf, int len) +{ + int idx; + char ascii[24]; + char *pa = ascii; + + memset(ascii, '\0', sizeof (ascii)); + + idx = 0; + while (len--) { + if ((idx & 15) == 0) { + printf("%04X: ", idx); + pa = ascii; + } + if (*buf > ' ' && *buf <= '~') + *pa++ = *buf; + else + *pa++ = '.'; + printf("%02x ", *buf++); + + idx++; + if ((idx & 3) == 0) { + *pa++ = ' '; + putchar(' '); + } + if ((idx & 15) == 0) { + *pa = '\0'; + printf("%s\n", ascii); + } + } + + if ((idx & 15) != 0) { + *pa = '\0'; + /* column align the last ascii row */ + while ((idx & 15) != 0) { + if ((idx & 3) == 0) + putchar(' '); + printf(" "); + idx++; + } + printf("%s\n", ascii); + } +} diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_mbmarshal.c b/usr/src/cmd/smbsrv/test-msgbuf/test_mbmarshal.c new file mode 100644 index 0000000000..967915688e --- /dev/null +++ b/usr/src/cmd/smbsrv/test-msgbuf/test_mbmarshal.c @@ -0,0 +1,680 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Test putting/getting unicode strings in mbchains. + */ + +#include <sys/types.h> +#include <sys/debug.h> +#include <sys/varargs.h> +#include <smbsrv/smb_kproto.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> + +#include "test_defs.h" + +static char mbsa[] = "A\xef\xbc\xa1."; // A fwA . (5) +static char mbsp[] = "P\xf0\x9f\x92\xa9."; // P poop . (6) +static smb_wchar_t wcsa[] = { 'A', 0xff21, '.', 0 }; // (3) +static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4) + +smb_session_t test_ssn; +smb_request_t test_sr; + +/* + * Put ASCII string with NULL + */ +static void +mbm_put_a0() +{ + uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; + mbuf_chain_t *mbc; + int rc; + + mbc = smb_mbc_alloc(100); + + rc = smb_mbc_encodef(mbc, "sw", "one", 42); + if (rc != 0) { + printf("Fail: mbm_put_a0 encode\n"); + goto out; + } + if (mbc->chain->m_len != 6) { + printf("Fail: mbm_put_a0 len=%d\n", + mbc->chain->m_len); + return; + } + + if (memcmp(mbc->chain->m_data, wire, 6)) { + printf("Fail: mbm_put_a0 cmp:\n"); + hexdump((uchar_t *)mbc->chain->m_data, 6); + return; + } + + printf("Pass: mbm_put_a0\n"); + +out: + smb_mbc_free(mbc); +} + +/* + * Put ASCII string, no NULL + */ +static void +mbm_put_a1() +{ + uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; + mbuf_chain_t *mbc; + int rc; + + mbc = smb_mbc_alloc(100); + + rc = smb_mbc_encodef(mbc, "4sw", "one.", 42); + if (rc != 0) { + printf("Fail: mbm_put_a1 encode\n"); + goto out; + } + if (mbc->chain->m_len != 6) { + printf("Fail: mbm_put_a1 len=%d\n", + mbc->chain->m_len); + return; + } + + if (memcmp(mbc->chain->m_data, wire, 6)) { + printf("Fail: mbm_put_a1 cmp:\n"); + hexdump((uchar_t *)mbc->chain->m_data, 6); + return; + } + + printf("Pass: mbm_put_a1\n"); + +out: + smb_mbc_free(mbc); +} + +static void +mbm_put_apad() +{ + uint8_t wire[] = { 'o', 'n', 'e', 0, 0 }; + mbuf_chain_t *mbc; + int rc; + + mbc = smb_mbc_alloc(100); + + /* Encode with wire length > strlen */ + rc = smb_mbc_encodef(mbc, "5s", "one"); + if (rc != 0) { + printf("Fail: mbm_put_apad encode\n"); + goto out; + } + if (mbc->chain->m_len != 5) { + printf("Fail: mbm_put_apad len=%d\n", + mbc->chain->m_len); + return; + } + + if (memcmp(mbc->chain->m_data, wire, 5)) { + printf("Fail: mbm_put_apad cmp:\n"); + hexdump((uchar_t *)mbc->chain->m_data, 5); + return; + } + + printf("Pass: mbm_put_apad\n"); + +out: + smb_mbc_free(mbc); +} + +static void +mbm_put_atrunc() +{ + uint8_t wire[] = { 'o', 'n', 'e', 't', }; + mbuf_chain_t *mbc; + int rc; + + mbc = smb_mbc_alloc(100); + + /* Encode with wire length < strlen */ + rc = smb_mbc_encodef(mbc, "4s", "onetwo"); + if (rc != 0) { + printf("Fail: mbm_put_atrunc encode\n"); + goto out; + } + /* Trunc should put exactly 4 */ + if (mbc->chain->m_len != 4) { + printf("Fail: mbm_put_atrunc len=%d\n", + mbc->chain->m_len); + return; + } + + if (memcmp(mbc->chain->m_data, wire, 4)) { + printf("Fail: mbm_put_atrunc cmp:\n"); + hexdump((uchar_t *)mbc->chain->m_data, 4); + return; + } + + printf("Pass: mbm_put_atrunc\n"); + +out: + smb_mbc_free(mbc); +} + +/* + * Put unicode string with NULL + */ +static void +mbm_put_u0() +{ + uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; + mbuf_chain_t *mbc; + int rc; + + mbc = smb_mbc_alloc(100); + + rc = smb_mbc_encodef(mbc, "Uw", "one", 42); + if (rc != 0) { + printf("Fail: mbm_put_u0 encode\n"); + goto out; + } + if (mbc->chain->m_len != 10) { + printf("Fail: mbm_put_u0 len=%d\n", + mbc->chain->m_len); + return; + } + + if (memcmp(mbc->chain->m_data, wire, 10)) { + printf("Fail: mbm_put_u0 cmp:\n"); + hexdump((uchar_t *)mbc->chain->m_data, 10); + return; + } + + printf("Pass: mbm_put_u0\n"); + +out: + smb_mbc_free(mbc); +} + +/* + * Put unicode string, no NULL + */ +static void +mbm_put_u1() +{ + uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; + mbuf_chain_t *mbc; + int rc; + + mbc = smb_mbc_alloc(100); + + rc = smb_mbc_encodef(mbc, "8Uw", "one.", 42); + if (rc != 0) { + printf("Fail: mbm_put_u1 encode\n"); + goto out; + } + if (mbc->chain->m_len != 10) { + printf("Fail: mbm_put_u1 len=%d\n", + mbc->chain->m_len); + return; + } + + if (memcmp(mbc->chain->m_data, wire, 10)) { + printf("Fail: mbm_put_u1 cmp:\n"); + hexdump((uchar_t *)mbc->chain->m_data, 10); + return; + } + + printf("Pass: mbm_put_u1\n"); + +out: + smb_mbc_free(mbc); +} + +static void +mbm_put_u3() +{ + mbuf_chain_t *mbc; + int rc; + + mbc = smb_mbc_alloc(100); + + rc = smb_mbc_encodef(mbc, "U", mbsa); + if (rc != 0) { + printf("Fail: mbm_put_u3 encode\n"); + goto out; + } + if (mbc->chain->m_len != 8) { + printf("Fail: mbm_put_u3 len=%d\n", + mbc->chain->m_len); + return; + } + + if (memcmp(mbc->chain->m_data, wcsa, 8)) { + printf("Fail: mbm_put_u3 cmp:\n"); + hexdump((uchar_t *)mbc->chain->m_data, 8); + return; + } + + printf("Pass: mbm_put_u3\n"); + +out: + smb_mbc_free(mbc); +} + +static void +mbm_put_u4() +{ + mbuf_chain_t *mbc; + int rc; + + mbc = smb_mbc_alloc(100); + + rc = smb_mbc_encodef(mbc, "U", mbsp); + if (rc != 0) { + printf("Fail: mbm_put_u4 encode\n"); + goto out; + } + if (mbc->chain->m_len != 10) { + printf("Fail: mbm_put_u4 len=%d\n", + mbc->chain->m_len); + return; + } + + if (memcmp(mbc->chain->m_data, wcsp, 10)) { + printf("Fail: mbm_put_u4 cmp:\n"); + hexdump((uchar_t *)mbc->chain->m_data, 10); + return; + } + + printf("Pass: mbm_put_u4\n"); + +out: + smb_mbc_free(mbc); +} + +static void +mbm_put_upad() +{ + uint16_t wire[] = { 'o', 'n', 'e', 0, 0 }; + mbuf_chain_t *mbc; + int rc; + + mbc = smb_mbc_alloc(100); + + /* Encode with wire length > strlen */ + rc = smb_mbc_encodef(mbc, "10U", "one"); + if (rc != 0) { + printf("Fail: mbm_put_upad encode\n"); + goto out; + } + if (mbc->chain->m_len != 10) { + printf("Fail: mbm_put_upad len=%d\n", + mbc->chain->m_len); + return; + } + + if (memcmp(mbc->chain->m_data, wire, 10)) { + printf("Fail: mbm_put_upad cmp:\n"); + hexdump((uchar_t *)mbc->chain->m_data, 10); + return; + } + + printf("Pass: mbm_put_upad\n"); + +out: + smb_mbc_free(mbc); +} + +static void +mbm_put_utrunc() +{ + uint16_t wire[] = { 'o', 'n', 'e', 't' }; + mbuf_chain_t *mbc; + int rc; + + mbc = smb_mbc_alloc(100); + + /* Encode with wire length < strlen */ + rc = smb_mbc_encodef(mbc, "8U", "onetwo"); + if (rc != 0) { + printf("Fail: mbm_put_utrunc encode\n"); + goto out; + } + /* Trunc should put exactly 8 */ + if (mbc->chain->m_len != 8) { + printf("Fail: mbm_put_utrunc len=%d\n", + mbc->chain->m_len); + return; + } + + if (memcmp(mbc->chain->m_data, wire, 8)) { + printf("Fail: mbm_put_utrunc cmp:\n"); + hexdump((uchar_t *)mbc->chain->m_data, 8); + return; + } + + printf("Pass: mbm_put_utrunc\n"); + +out: + smb_mbc_free(mbc); +} + +/* + * Parse an ascii string. + */ +static void +mbm_get_a0() +{ + uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; + mbuf_chain_t mbc; + char *s; + int rc; + uint16_t w; + + bzero(&mbc, sizeof (mbc)); + MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); + + rc = smb_mbc_decodef(&mbc, "%sw", &test_sr, &s, &w); + if (rc != 0) { + printf("Fail: mbm_get_a0 decode\n"); + goto out; + } + /* + * Decode a word after the string to make sure we + * end up positioned correctly after the string. + */ + if (w != 42) { + printf("Fail: mbm_get_a0 w=%d\n", w); + return; + } + if (strcmp(s, "one") != 0) { + printf("Fail: mbm_get_a0 cmp: <%s>\n", s); + return; + } + + printf("Pass: mbm_get_a0\n"); + +out: + MBC_FLUSH(&mbc); +} + +/* + * Parse an ascii string, no NULL + */ +static void +mbm_get_a1() +{ + uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; + mbuf_chain_t mbc; + char *s; + int rc; + uint16_t w; + + bzero(&mbc, sizeof (mbc)); + MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); + + rc = smb_mbc_decodef(&mbc, "%3s.w", &test_sr, &s, &w); + if (rc != 0) { + printf("Fail: mbm_get_a1 decode\n"); + goto out; + } + /* + * Decode a word after the string to make sure we + * end up positioned correctly after the string. + */ + if (w != 42) { + printf("Fail: mbm_get_a1 w=%d\n", w); + return; + } + if (strcmp(s, "one") != 0) { + printf("Fail: mbm_get_a1 cmp: <%s>\n", s); + return; + } + + printf("Pass: mbm_get_a1\n"); + +out: + MBC_FLUSH(&mbc); +} + +/* parse exactly to end of data */ +static void +mbm_get_a2() +{ + uint8_t wire[] = { 'o', 'n', 'e' }; + mbuf_chain_t mbc; + char *s; + int rc; + + bzero(&mbc, sizeof (mbc)); + MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); + + rc = smb_mbc_decodef(&mbc, "%3s", &test_sr, &s); + if (rc != 0) { + printf("Fail: mbm_get_a2 decode\n"); + goto out; + } + if (mbc.chain_offset != 3) { + printf("Fail: mbm_get_a2 wrong pos\n"); + return; + } + if (strcmp(s, "one") != 0) { + printf("Fail: mbm_get_a2 cmp: <%s>\n", s); + return; + } + + printf("Pass: mbm_get_a2\n"); + +out: + MBC_FLUSH(&mbc); +} + +/* + * Parse a unicode string. + */ +static void +mbm_get_u0() +{ + uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; + mbuf_chain_t mbc; + char *s; + int rc; + uint16_t w; + + bzero(&mbc, sizeof (mbc)); + MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); + + rc = smb_mbc_decodef(&mbc, "%Uw", &test_sr, &s, &w); + if (rc != 0) { + printf("Fail: mbm_get_u0 decode\n"); + goto out; + } + /* + * Decode a word after the string to make sure we + * end up positioned correctly after the string. + */ + if (w != 42) { + printf("Fail: mbm_get_u0 w=%d\n", w); + return; + } + if (strcmp(s, "one") != 0) { + printf("Fail: mbm_get_u0 cmp: <%s>\n", s); + return; + } + + printf("Pass: mbm_get_u0\n"); + +out: + MBC_FLUSH(&mbc); +} + +/* + * Parse a string that's NOT null terminated. + */ +static void +mbm_get_u1() +{ + uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; + mbuf_chain_t mbc; + char *s; + int rc; + uint16_t w; + + bzero(&mbc, sizeof (mbc)); + MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); + + rc = smb_mbc_decodef(&mbc, "%6U..w", &test_sr, &s, &w); + if (rc != 0) { + printf("Fail: mbm_get_u1 decode\n"); + goto out; + } + /* + * Decode a word after the string to make sure we + * end up positioned correctly after the string. + */ + if (w != 42) { + printf("Fail: mbm_get_u1 w=%d\n", w); + return; + } + if (strcmp(s, "one") != 0) { + printf("Fail: mbm_get_u1 cmp: <%s>\n", s); + return; + } + + printf("Pass: mbm_get_u1\n"); + +out: + MBC_FLUSH(&mbc); +} + +/* parse exactly to end of data */ +static void +mbm_get_u2() +{ + uint16_t wire[] = { 't', 'w', 'o' }; + mbuf_chain_t mbc; + char *s; + int rc; + + bzero(&mbc, sizeof (mbc)); + MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); + + rc = smb_mbc_decodef(&mbc, "%6U", &test_sr, &s); + if (rc != 0) { + printf("Fail: mbm_get_u2 decode\n"); + goto out; + } + if (mbc.chain_offset != 6) { + printf("Fail: mbm_get_u2 wrong pos\n"); + return; + } + if (strcmp(s, "two") != 0) { + printf("Fail: mbm_get_u2 cmp: <%s>\n", s); + return; + } + + printf("Pass: mbm_get_a2\n"); + +out: + MBC_FLUSH(&mbc); +} + +static void +mbm_get_u3() +{ + mbuf_chain_t mbc; + char *s; + int rc; + + bzero(&mbc, sizeof (mbc)); + MBC_ATTACH_BUF(&mbc, (uchar_t *)wcsa, sizeof (wcsa)); + + rc = smb_mbc_decodef(&mbc, "%#U", &test_sr, sizeof (wcsa), &s); + if (rc != 0) { + printf("Fail: mbm_get_u3 decode\n"); + goto out; + } + if (strcmp(s, mbsa) != 0) { + printf("Fail: mbm_get_u3 cmp: <%s>\n", s); + return; + } + + printf("Pass: mbm_get_u3\n"); + +out: + MBC_FLUSH(&mbc); +} + +static void +mbm_get_u4() +{ + mbuf_chain_t mbc; + char *s; + int rc; + + bzero(&mbc, sizeof (mbc)); + MBC_ATTACH_BUF(&mbc, (uchar_t *)wcsp, sizeof (wcsp)); + + rc = smb_mbc_decodef(&mbc, "%#U", &test_sr, sizeof (wcsp), &s); + if (rc != 0) { + printf("Fail: mbm_get_u4 decode\n"); + goto out; + } + if (strcmp(s, mbsp) != 0) { + printf("Fail: mbm_get_u4 cmp: <%s>\n", s); + return; + } + + printf("Pass: mbm_get_u4\n"); + +out: + MBC_FLUSH(&mbc); +} + +void +test_mbmarshal() +{ + + smb_mbc_init(); + + test_ssn.dialect = 0x210; // SMB 2.1 + test_sr.session = &test_ssn; + test_sr.sr_magic = SMB_REQ_MAGIC; + smb_srm_init(&test_sr); + + mbm_put_a0(); + mbm_put_a1(); + mbm_put_apad(); + mbm_put_atrunc(); + + mbm_put_u0(); + mbm_put_u1(); + mbm_put_u3(); + mbm_put_u4(); + mbm_put_upad(); + mbm_put_utrunc(); + + mbm_get_a0(); + mbm_get_a1(); + mbm_get_a2(); + mbm_get_u0(); + mbm_get_u1(); + mbm_get_u2(); + mbm_get_u3(); + mbm_get_u4(); + + smb_srm_fini(&test_sr); + smb_mbc_fini(); +} diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_misc.c b/usr/src/cmd/smbsrv/test-msgbuf/test_misc.c new file mode 100644 index 0000000000..c93634ba67 --- /dev/null +++ b/usr/src/cmd/smbsrv/test-msgbuf/test_misc.c @@ -0,0 +1,301 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * A few excerpts from smb_kutil.c + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/tzfile.h> +#include <sys/atomic.h> +#include <sys/debug.h> +#include <sys/time.h> +#include <smbsrv/smb_kproto.h> + +time_t tzh_leapcnt = 0; + +struct tm +*smb_gmtime_r(time_t *clock, struct tm *result); + +time_t +smb_timegm(struct tm *tm); + +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +static const int days_in_month[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +uint64_t +smb_time_unix_to_nt(timestruc_t *unix_time) +{ + uint64_t nt_time; + + if ((unix_time->tv_sec == 0) && (unix_time->tv_nsec == 0)) + return (0); + + nt_time = unix_time->tv_sec; + nt_time *= 10000000; /* seconds to 100ns */ + nt_time += unix_time->tv_nsec / 100; + return (nt_time + NT_TIME_BIAS); +} + +void +smb_time_nt_to_unix(uint64_t nt_time, timestruc_t *unix_time) +{ + uint32_t seconds; + + ASSERT(unix_time); + + if ((nt_time == 0) || (nt_time == -1)) { + unix_time->tv_sec = 0; + unix_time->tv_nsec = 0; + return; + } + + /* + * Can't represent times less than or equal NT_TIME_BIAS, + * so convert them to the oldest date we can store. + * Note that time zero is "special" being converted + * both directions as 0:0 (unix-to-nt, nt-to-unix). + */ + if (nt_time <= NT_TIME_BIAS) { + unix_time->tv_sec = 0; + unix_time->tv_nsec = 100; + return; + } + + nt_time -= NT_TIME_BIAS; + seconds = nt_time / 10000000; + unix_time->tv_sec = seconds; + unix_time->tv_nsec = (nt_time % 10000000) * 100; +} + + +/* + * smb_time_dos_to_unix + * + * Convert SMB_DATE & SMB_TIME values to a unix timestamp. + * + * A date/time field of 0 means that that server file system + * assigned value need not be changed. The behaviour when the + * date/time field is set to -1 is not documented but is + * generally treated like 0. + * If date or time is 0 or -1 the unix time is returned as 0 + * so that the caller can identify and handle this special case. + */ +int32_t +smb_time_dos_to_unix(int16_t date, int16_t time) +{ + struct tm atm; + + if (((date == 0) || (time == 0)) || + ((date == -1) || (time == -1))) { + return (0); + } + + atm.tm_year = ((date >> 9) & 0x3F) + 80; + atm.tm_mon = ((date >> 5) & 0x0F) - 1; + atm.tm_mday = ((date >> 0) & 0x1F); + atm.tm_hour = ((time >> 11) & 0x1F); + atm.tm_min = ((time >> 5) & 0x3F); + atm.tm_sec = ((time >> 0) & 0x1F) << 1; + + return (smb_timegm(&atm)); +} + +void +smb_time_unix_to_dos(int32_t ux_time, int16_t *date_p, int16_t *time_p) +{ + struct tm atm; + int i; + time_t tmp_time; + + if (ux_time == 0) { + *date_p = 0; + *time_p = 0; + return; + } + + tmp_time = (time_t)ux_time; + (void) smb_gmtime_r(&tmp_time, &atm); + + if (date_p) { + i = 0; + i += atm.tm_year - 80; + i <<= 4; + i += atm.tm_mon + 1; + i <<= 5; + i += atm.tm_mday; + + *date_p = (short)i; + } + if (time_p) { + i = 0; + i += atm.tm_hour; + i <<= 6; + i += atm.tm_min; + i <<= 5; + i += atm.tm_sec >> 1; + + *time_p = (short)i; + } +} + +/* + * smb_gmtime_r + * + * Thread-safe version of smb_gmtime. Returns a null pointer if either + * input parameter is a null pointer. Otherwise returns a pointer + * to result. + * + * Day of the week calculation: the Epoch was a thursday. + * + * There are no timezone corrections so tm_isdst and tm_gmtoff are + * always zero, and the zone is always WET. + */ +struct tm * +smb_gmtime_r(time_t *clock, struct tm *result) +{ + time_t tsec; + int year; + int month; + int sec_per_month; + + if (clock == 0 || result == 0) + return (0); + + bzero(result, sizeof (struct tm)); + tsec = *clock; + tsec -= tzh_leapcnt; + + result->tm_wday = tsec / SECSPERDAY; + result->tm_wday = (result->tm_wday + TM_THURSDAY) % DAYSPERWEEK; + + year = EPOCH_YEAR; + while (tsec >= (isleap(year) ? (SECSPERDAY * DAYSPERLYEAR) : + (SECSPERDAY * DAYSPERNYEAR))) { + if (isleap(year)) + tsec -= SECSPERDAY * DAYSPERLYEAR; + else + tsec -= SECSPERDAY * DAYSPERNYEAR; + + ++year; + } + + result->tm_year = year - TM_YEAR_BASE; + result->tm_yday = tsec / SECSPERDAY; + + for (month = TM_JANUARY; month <= TM_DECEMBER; ++month) { + sec_per_month = days_in_month[month] * SECSPERDAY; + + if (month == TM_FEBRUARY && isleap(year)) + sec_per_month += SECSPERDAY; + + if (tsec < sec_per_month) + break; + + tsec -= sec_per_month; + } + + result->tm_mon = month; + result->tm_mday = (tsec / SECSPERDAY) + 1; + tsec %= SECSPERDAY; + result->tm_sec = tsec % 60; + tsec /= 60; + result->tm_min = tsec % 60; + tsec /= 60; + result->tm_hour = (int)tsec; + + return (result); +} + + +/* + * smb_timegm + * + * Converts the broken-down time in tm to a time value, i.e. the number + * of seconds since the Epoch (00:00:00 UTC, January 1, 1970). This is + * not a POSIX or ANSI function. Per the man page, the input values of + * tm_wday and tm_yday are ignored and, as the input data is assumed to + * represent GMT, we force tm_isdst and tm_gmtoff to 0. + * + * Before returning the clock time, we use smb_gmtime_r to set up tm_wday + * and tm_yday, and bring the other fields within normal range. I don't + * think this is really how it should be done but it's convenient for + * now. + */ +time_t +smb_timegm(struct tm *tm) +{ + time_t tsec; + int dd; + int mm; + int yy; + int year; + + if (tm == 0) + return (-1); + + year = tm->tm_year + TM_YEAR_BASE; + tsec = tzh_leapcnt; + + for (yy = EPOCH_YEAR; yy < year; ++yy) { + if (isleap(yy)) + tsec += SECSPERDAY * DAYSPERLYEAR; + else + tsec += SECSPERDAY * DAYSPERNYEAR; + } + + for (mm = TM_JANUARY; mm < tm->tm_mon; ++mm) { + dd = days_in_month[mm] * SECSPERDAY; + + if (mm == TM_FEBRUARY && isleap(year)) + dd += SECSPERDAY; + + tsec += dd; + } + + tsec += (tm->tm_mday - 1) * SECSPERDAY; + tsec += tm->tm_sec; + tsec += tm->tm_min * SECSPERMIN; + tsec += tm->tm_hour * SECSPERHOUR; + + tm->tm_isdst = 0; + (void) smb_gmtime_r(&tsec, tm); + return (tsec); +} diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_msgbuf.c b/usr/src/cmd/smbsrv/test-msgbuf/test_msgbuf.c new file mode 100644 index 0000000000..b58bd54b83 --- /dev/null +++ b/usr/src/cmd/smbsrv/test-msgbuf/test_msgbuf.c @@ -0,0 +1,638 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Test putting/getting unicode strings in mbchains. + */ + +#include <sys/types.h> +#include <sys/debug.h> +#include <sys/varargs.h> +#include <smbsrv/smb_kproto.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> + +#include "test_defs.h" + +static char mbsa[] = "A\xef\xbc\xa1."; // A fwA . (5) +static char mbsp[] = "P\xf0\x9f\x92\xa9."; // P poop . (6) +static smb_wchar_t wcsa[] = { 'A', 0xff21, '.', 0 }; // (3) +static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4) + +/* + * Put ASCII string with NULL + */ +static void +msg_put_a0() +{ + uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; + uint8_t temp[32]; + smb_msgbuf_t mb; + int mbflags = 0; + int rc; + + smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); + + rc = smb_msgbuf_encode(&mb, "sw", "one", 42); + if (rc != 6) { + printf("Fail: msg_put_a0 encode\n"); + goto out; + } + + if (memcmp(temp, wire, 6)) { + printf("Fail: msg_put_a0 cmp:\n"); + hexdump((uchar_t *)temp, 6); + return; + } + + printf("Pass: msg_put_a0\n"); + +out: + smb_msgbuf_term(&mb); +} + +/* + * Put ASCII string, no NULL + */ +static void +msg_put_a1() +{ + uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; + uint8_t temp[32]; + smb_msgbuf_t mb; + int mbflags = 0; + int rc; + + smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); + + rc = smb_msgbuf_encode(&mb, "4sw", "one.", 42); + if (rc != 6) { + printf("Fail: msg_put_a1 encode\n"); + goto out; + } + + if (memcmp(temp, wire, 6)) { + printf("Fail: msg_put_a1 cmp:\n"); + hexdump((uchar_t *)temp, 6); + return; + } + + printf("Pass: msg_put_a1\n"); + +out: + smb_msgbuf_term(&mb); +} + +static void +msg_put_apad() +{ + uint8_t wire[] = { 'o', 'n', 'e', 0, 0 }; + uint8_t temp[32]; + smb_msgbuf_t mb; + int mbflags = 0; + int rc; + + smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); + + /* Encode with wire length > strlen */ + rc = smb_msgbuf_encode(&mb, "5s", "one"); + if (rc != 5) { + printf("Fail: msg_put_apad encode\n"); + goto out; + } + + if (memcmp(temp, wire, 5)) { + printf("Fail: msg_put_apad cmp:\n"); + hexdump((uchar_t *)temp, 5); + return; + } + + printf("Pass: msg_put_apad\n"); + +out: + smb_msgbuf_term(&mb); +} + +static void +msg_put_atrunc() +{ + uint8_t wire[] = { 'o', 'n', 'e', 't', }; + uint8_t temp[32]; + smb_msgbuf_t mb; + int mbflags = 0; + int rc; + + smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); + + /* Encode with wire length < strlen */ + rc = smb_msgbuf_encode(&mb, "4s", "onetwo"); + /* Trunc should put exactly 4 */ + if (rc != 4) { + printf("Fail: msg_put_atrunc encode\n"); + goto out; + } + + if (memcmp(temp, wire, 4)) { + printf("Fail: msg_put_atrunc cmp:\n"); + hexdump((uchar_t *)temp, 4); + return; + } + + printf("Pass: msg_put_atrunc\n"); + +out: + smb_msgbuf_term(&mb); +} + +/* + * Put unicode string with NULL + */ +static void +msg_put_u0() +{ + uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; + uint8_t temp[32]; + smb_msgbuf_t mb; + int mbflags = 0; + int rc; + + smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); + + rc = smb_msgbuf_encode(&mb, "Uw", "one", 42); + if (rc != 10) { + printf("Fail: msg_put_u0 encode\n"); + goto out; + } + + if (memcmp(temp, wire, 10)) { + printf("Fail: msg_put_u0 cmp:\n"); + hexdump((uchar_t *)temp, 10); + return; + } + + printf("Pass: msg_put_u0\n"); + +out: + smb_msgbuf_term(&mb); +} + +/* + * Put unicode string, no NULL + */ +static void +msg_put_u1() +{ + uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; + uint8_t temp[32]; + smb_msgbuf_t mb; + int mbflags = 0; + int rc; + + smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); + + rc = smb_msgbuf_encode(&mb, "8Uw", "one.", 42); + if (rc != 10) { + printf("Fail: msg_put_u1 encode\n"); + goto out; + } + + if (memcmp(temp, wire, 10)) { + printf("Fail: msg_put_u1 cmp:\n"); + hexdump((uchar_t *)temp, 10); + return; + } + + printf("Pass: msg_put_u1\n"); + +out: + smb_msgbuf_term(&mb); +} + +static void +msg_put_u3() +{ + uint8_t temp[32]; + smb_msgbuf_t mb; + int mbflags = 0; + int rc; + + smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); + + rc = smb_msgbuf_encode(&mb, "U", mbsa); + if (rc != 8) { + printf("Fail: msg_put_u3 encode\n"); + goto out; + } + + if (memcmp(temp, wcsa, 8)) { + printf("Fail: msg_put_u3 cmp:\n"); + hexdump((uchar_t *)temp, 8); + return; + } + + printf("Pass: msg_put_u3\n"); + +out: + smb_msgbuf_term(&mb); +} + +static void +msg_put_u4() +{ + uint8_t temp[32]; + smb_msgbuf_t mb; + int mbflags = 0; + int rc; + + smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); + + rc = smb_msgbuf_encode(&mb, "U", mbsp); + if (rc != 10) { + printf("Fail: msg_put_u4 encode\n"); + goto out; + } + + if (memcmp(temp, wcsp, 10)) { + printf("Fail: msg_put_u4 cmp:\n"); + hexdump((uchar_t *)temp, 10); + return; + } + + printf("Pass: msg_put_u4\n"); + +out: + smb_msgbuf_term(&mb); +} + +static void +msg_put_upad() +{ + uint16_t wire[] = { 'o', 'n', 'e', 0, 0 }; + uint8_t temp[32]; + smb_msgbuf_t mb; + int mbflags = 0; + int rc; + + smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); + + /* Encode with wire length > strlen */ + rc = smb_msgbuf_encode(&mb, "10U", "one"); + if (rc != 10) { + printf("Fail: msg_put_upad encode\n"); + goto out; + } + + if (memcmp(temp, wire, 10)) { + printf("Fail: msg_put_upad cmp:\n"); + hexdump((uchar_t *)temp, 10); + return; + } + + printf("Pass: msg_put_upad\n"); + +out: + smb_msgbuf_term(&mb); +} + +static void +msg_put_utrunc() +{ + uint16_t wire[] = { 'o', 'n', 'e', 't' }; + uint8_t temp[32]; + smb_msgbuf_t mb; + int mbflags = 0; + int rc; + + smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); + + /* Encode with wire length < strlen */ + rc = smb_msgbuf_encode(&mb, "8U", "onetwo"); + /* Trunc should put exactly 8 */ + if (rc != 8) { + printf("Fail: msg_put_utrunc encode\n"); + goto out; + } + + if (memcmp(temp, wire, 8)) { + printf("Fail: msg_put_utrunc cmp:\n"); + hexdump((uchar_t *)temp, 8); + return; + } + + printf("Pass: msg_put_utrunc\n"); + +out: + smb_msgbuf_term(&mb); +} + +/* + * Parse an ascii string. + */ +static void +msg_get_a0() +{ + uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; + smb_msgbuf_t mb; + int mbflags = 0; + char *s; + int rc; + uint16_t w; + + smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags); + + rc = smb_msgbuf_decode(&mb, "sw", &s, &w); + if (rc != 6) { + printf("Fail: msg_get_a0 decode\n"); + goto out; + } + /* + * Decode a word after the string to make sure we + * end up positioned correctly after the string. + */ + if (w != 42) { + printf("Fail: msg_get_a0 w=%d\n", w); + return; + } + if (strcmp(s, "one") != 0) { + printf("Fail: msg_get_a0 cmp: <%s>\n", s); + return; + } + + printf("Pass: msg_get_a0\n"); + +out: + smb_msgbuf_term(&mb); +} + +/* + * Parse an ascii string, no NULL + */ +static void +msg_get_a1() +{ + uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; + smb_msgbuf_t mb; + int mbflags = 0; + char *s; + int rc; + uint16_t w; + + smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags); + + rc = smb_msgbuf_decode(&mb, "3s.w", &s, &w); + if (rc != 6) { + printf("Fail: msg_get_a1 decode\n"); + goto out; + } + /* + * Decode a word after the string to make sure we + * end up positioned correctly after the string. + */ + if (w != 42) { + printf("Fail: msg_get_a1 w=%d\n", w); + return; + } + if (strcmp(s, "one") != 0) { + printf("Fail: msg_get_a1 cmp: <%s>\n", s); + return; + } + + printf("Pass: msg_get_a1\n"); + +out: + smb_msgbuf_term(&mb); +} + +/* parse exactly to end of data */ +static void +msg_get_a2() +{ + uint8_t wire[] = { 'o', 'n', 'e' }; + smb_msgbuf_t mb; + int mbflags = 0; + char *s; + int rc; + + smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags); + + rc = smb_msgbuf_decode(&mb, "3s", &s); + if (rc != 3) { + printf("Fail: msg_get_a2 decode\n"); + goto out; + } + if (mb.scan != mb.end) { + printf("Fail: msg_get_a2 wrong pos\n"); + return; + } + if (strcmp(s, "one") != 0) { + printf("Fail: msg_get_a2 cmp: <%s>\n", s); + return; + } + + printf("Pass: msg_get_a2\n"); + +out: + smb_msgbuf_term(&mb); +} + +/* + * Parse a unicode string. + */ +static void +msg_get_u0() +{ + uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; + smb_msgbuf_t mb; + int mbflags = 0; + char *s; + int rc; + uint16_t w; + + smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags); + + rc = smb_msgbuf_decode(&mb, "Uw", &s, &w); + if (rc != 10) { + printf("Fail: msg_get_u0 decode\n"); + goto out; + } + /* + * Decode a word after the string to make sure we + * end up positioned correctly after the string. + */ + if (w != 42) { + printf("Fail: msg_get_u0 w=%d\n", w); + return; + } + if (strcmp(s, "one") != 0) { + printf("Fail: msg_get_u0 cmp: <%s>\n", s); + return; + } + + printf("Pass: msg_get_u0\n"); + +out: + smb_msgbuf_term(&mb); +} + +/* + * Parse a string that's NOT null terminated. + */ +static void +msg_get_u1() +{ + uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; + smb_msgbuf_t mb; + int mbflags = 0; + char *s; + int rc; + uint16_t w; + + smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags); + + rc = smb_msgbuf_decode(&mb, "6U..w", &s, &w); + if (rc != 10) { + printf("Fail: msg_get_u1 decode\n"); + goto out; + } + /* + * Decode a word after the string to make sure we + * end up positioned correctly after the string. + */ + if (w != 42) { + printf("Fail: msg_get_u1 w=%d\n", w); + return; + } + if (strcmp(s, "one") != 0) { + printf("Fail: msg_get_u1 cmp: <%s>\n", s); + return; + } + + printf("Pass: msg_get_u1\n"); + +out: + smb_msgbuf_term(&mb); +} + +/* parse exactly to end of data */ +static void +msg_get_u2() +{ + uint16_t wire[] = { 'o', 'n', 'e' }; + smb_msgbuf_t mb; + int mbflags = 0; + char *s; + int rc; + + smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags); + + rc = smb_msgbuf_decode(&mb, "6U", &s); + if (rc != 6) { + printf("Fail: msg_get_u2 decode\n"); + goto out; + } + if (mb.scan != mb.end) { + printf("Fail: msg_get_u2 wrong pos\n"); + return; + } + if (strcmp(s, "one") != 0) { + printf("Fail: msg_get_u2 cmp: <%s>\n", s); + return; + } + + printf("Pass: msg_get_u2\n"); + +out: + smb_msgbuf_term(&mb); +} + +static void +msg_get_u3() +{ + smb_msgbuf_t mb; + int mbflags = 0; + char *s; + int rc; + + smb_msgbuf_init(&mb, (uint8_t *)wcsa, sizeof (wcsa), mbflags); + + rc = smb_msgbuf_decode(&mb, "#U", sizeof (wcsa), &s); + if (rc != 8) { + printf("Fail: msg_get_u3 decode\n"); + goto out; + } + if (strcmp(s, mbsa) != 0) { + printf("Fail: msg_get_u3 cmp: <%s>\n", s); + return; + } + + printf("Pass: msg_get_u3\n"); + +out: + smb_msgbuf_term(&mb); +} + +static void +msg_get_u4() +{ + smb_msgbuf_t mb; + int mbflags = 0; + char *s; + int rc; + + smb_msgbuf_init(&mb, (uint8_t *)wcsp, sizeof (wcsp), mbflags); + + rc = smb_msgbuf_decode(&mb, "#U", sizeof (wcsp), &s); + if (rc != 10) { + printf("Fail: msg_get_u4 decode\n"); + goto out; + } + if (strcmp(s, mbsp) != 0) { + printf("Fail: msg_get_u4 cmp: <%s>\n", s); + return; + } + + printf("Pass: msg_get_u4\n"); + +out: + smb_msgbuf_term(&mb); +} + +void +test_msgbuf() +{ + + msg_put_a0(); + msg_put_a1(); + msg_put_apad(); + msg_put_atrunc(); + + msg_put_u0(); + msg_put_u1(); + msg_put_u3(); + msg_put_u4(); + msg_put_upad(); + msg_put_utrunc(); + + msg_get_a0(); + msg_get_a1(); + msg_get_a2(); + msg_get_u0(); + msg_get_u1(); + msg_get_u2(); + msg_get_u3(); + msg_get_u4(); + +} diff --git a/usr/src/common/smbsrv/smb_match.c b/usr/src/common/smbsrv/smb_match.c index e687e3cc9f..b35833ff29 100644 --- a/usr/src/common/smbsrv/smb_match.c +++ b/usr/src/common/smbsrv/smb_match.c @@ -142,7 +142,7 @@ smb_match_private(const char *pat, const char *str, struct match_priv *priv) const char *limit; char pc; /* current pattern char */ int rc; - smb_wchar_t wcpat, wcstr; /* current wchar in pat, str */ + uint32_t wcpat, wcstr; /* current wchar in pat, str */ int nbpat, nbstr; /* multi-byte length of it */ if (priv->depth >= SMB_MATCH_DEPTH_MAX) diff --git a/usr/src/common/smbsrv/smb_msgbuf.c b/usr/src/common/smbsrv/smb_msgbuf.c index 54cb75e066..b11cd39a50 100644 --- a/usr/src/common/smbsrv/smb_msgbuf.c +++ b/usr/src/common/smbsrv/smb_msgbuf.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 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -53,6 +53,12 @@ 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. */ @@ -177,7 +183,7 @@ smb_msgbuf_term(smb_msgbuf_t *mb) * 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 + * On success, returns the number of bytes decoded. Otherwise * returns a -ve error code. */ int @@ -213,15 +219,12 @@ smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...) 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; @@ -324,75 +327,23 @@ buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap) 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); + case 's': /* get OEM string */ 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; + if (!repc_specified) + repc = 0; + rc = msgbuf_get_oem_string(mb, cvalpp, repc); + if (rc != 0) + return (rc); break; - case 'U': /* get unicode string */ + case 'U': /* get UTF-16 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; + if (!repc_specified) + repc = 0; + rc = msgbuf_get_unicode_string(mb, cvalpp, repc); + if (rc != 0) + return (rc); break; case 'M': @@ -416,6 +367,151 @@ unicode_translation: 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 @@ -466,8 +562,6 @@ buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) uint8_t *bvalp; char *cvalp; uint8_t c; - smb_wchar_t wchar; - int count; boolean_t repc_specified; int repc; int rc; @@ -571,80 +665,23 @@ buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) goto unicode_translation; /* FALLTHROUGH */ - case 's': /* put string */ + case 's': /* put OEM 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--; - } + if (!repc_specified) + repc = 0; + rc = msgbuf_put_oem_string(mb, cvalp, repc); + if (rc != 0) + return (rc); break; - case 'U': /* put unicode string */ + case 'U': /* put UTF-16 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--; - } + if (!repc_specified) + repc = 0; + rc = msgbuf_put_unicode_string(mb, cvalp, repc); + if (rc != 0) + return (rc); break; case 'M': @@ -665,6 +702,141 @@ unicode_translation: 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 diff --git a/usr/src/common/smbsrv/smb_string.c b/usr/src/common/smbsrv/smb_string.c index 3d2abc474b..7922d84916 100644 --- a/usr/src/common/smbsrv/smb_string.c +++ b/usr/src/common/smbsrv/smb_string.c @@ -174,8 +174,8 @@ smb_islower(int c) * If the specified character is lowercase, the uppercase value will * be returned. Otherwise the original value will be returned. */ -int -smb_toupper(int c) +uint32_t +smb_toupper(uint32_t c) { uint16_t mask = is_unicode ? 0xffff : 0xff; @@ -187,8 +187,8 @@ smb_toupper(int c) * If the specified character is uppercase, the lowercase value will * be returned. Otherwise the original value will be returned. */ -int -smb_tolower(int c) +uint32_t +smb_tolower(uint32_t c) { uint16_t mask = is_unicode ? 0xffff : 0xff; @@ -204,7 +204,7 @@ smb_tolower(int c) char * smb_strupr(char *s) { - smb_wchar_t c; + uint32_t c; char *p = s; while (*p) { @@ -235,7 +235,7 @@ smb_strupr(char *s) char * smb_strlwr(char *s) { - smb_wchar_t c; + uint32_t c; char *p = s; while (*p) { @@ -264,7 +264,7 @@ smb_strlwr(char *s) int smb_isstrlwr(const char *s) { - smb_wchar_t c; + uint32_t c; int n; const char *p = s; @@ -295,7 +295,7 @@ smb_isstrlwr(const char *s) int smb_isstrupr(const char *s) { - smb_wchar_t c; + uint32_t c; int n; const char *p = s; @@ -440,7 +440,7 @@ smb_unicode_init(void) * unc_server server or domain name with no leading/trailing '\' * unc_share share name with no leading/trailing '\' * unc_path relative path to the share with no leading/trailing '\' - * it is valid for unc_path to be NULL. + * it is valid for unc_path to be NULL. * * Upon successful return of this function, smb_unc_free() * MUST be called when returned 'unc' is no longer needed. diff --git a/usr/src/common/smbsrv/smb_utf8.c b/usr/src/common/smbsrv/smb_utf8.c index 3c3583471f..8446fb0b9e 100644 --- a/usr/src/common/smbsrv/smb_utf8.c +++ b/usr/src/common/smbsrv/smb_utf8.c @@ -22,32 +22,12 @@ * Copyright 2009 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. */ /* - * Multibyte/wide-char conversion routines. Wide-char encoding provides - * a fixed size character encoding that maps to the Unicode 16-bit - * (UCS-2) character set standard. Multibyte or UCS transformation - * format (UTF) encoding is a variable length character encoding scheme - * that s compatible with existing ASCII characters and guarantees that - * the resultant strings do not contain embedded null characters. Both - * types of encoding provide a null terminator: single byte for UTF-8 - * and a wide-char null for Unicode. See RFC 2044. - * - * The table below illustrates the UTF-8 encoding scheme. The letter x - * indicates bits available for encoding the character value. - * - * UCS-2 UTF-8 octet sequence (binary) - * 0x0000-0x007F 0xxxxxxx - * 0x0080-0x07FF 110xxxxx 10xxxxxx - * 0x0800-0xFFFF 1110xxxx 10xxxxxx 10xxxxxx - * - * RFC 2044 - * UTF-8,a transformation format of UNICODE and ISO 10646 - * F. Yergeau - * Alis Technologies - * October 1996 + * Multibyte/wide-char conversion routines. SMB uses UTF-16 on the wire + * (smb_wchar_t) and we use UTF-8 internally (our multi-byte, or mbs). */ #if defined(_KERNEL) || defined(_FAKE_KERNEL) @@ -60,6 +40,7 @@ #include <iconv.h> #include <assert.h> #endif /* _KERNEL || _FAKE_KERNEL */ +#include <sys/u8_textprep.h> #include <smbsrv/string.h> @@ -76,26 +57,37 @@ * multibyte character is encountered. */ size_t -smb_mbstowcs(smb_wchar_t *wcstring, const char *mbstring, size_t nwchars) +smb_mbstowcs(smb_wchar_t *wcs, const char *mbs, size_t nwchars) { - int len; - smb_wchar_t *start = wcstring; - - while (nwchars--) { - len = smb_mbtowc(wcstring, mbstring, MTS_MB_CHAR_MAX); - if (len < 0) { - *wcstring = 0; - return ((size_t)-1); - } + size_t mbslen, wcslen; + int err; - if (*mbstring == 0) - break; + /* NULL or empty input is allowed. */ + if (mbs == NULL || *mbs == '\0') { + if (wcs != NULL && nwchars > 0) + *wcs = 0; + return (0); + } - ++wcstring; - mbstring += len; + /* + * Traditional mbstowcs(3C) allows wcs==NULL to get the length. + * SMB never calls it that way, but let's future-proof. + */ + if (wcs == NULL) { + return ((size_t)-1); } - return (wcstring - start); + mbslen = strlen(mbs); + wcslen = nwchars; + err = uconv_u8tou16((const uchar_t *)mbs, &mbslen, + wcs, &wcslen, UCONV_OUT_LITTLE_ENDIAN); + if (err != 0) + return ((size_t)-1); + + if (wcslen < nwchars) + wcs[wcslen] = 0; + + return (wcslen); } @@ -114,49 +106,36 @@ smb_mbstowcs(smb_wchar_t *wcstring, const char *mbstring, size_t nwchars) * states. Otherwise it should be return 0. * * If mbchar is non-null, returns the number of bytes processed in - * mbchar. If mbchar is invalid, returns -1. + * mbchar. If mbchar is null, convert the null (wcharp=0) but + * return length zero. If mbchar is invalid, returns -1. */ int /*ARGSUSED*/ -smb_mbtowc(smb_wchar_t *wcharp, const char *mbchar, size_t nbytes) +smb_mbtowc(uint32_t *wcharp, const char *mbchar, size_t nbytes) { - unsigned char mbyte; - smb_wchar_t wide_char; - int count; - int bytes_left; + uint32_t wide_char; + int count, err; + size_t mblen; + size_t wclen; if (mbchar == NULL) return (0); /* no shift states */ - /* 0xxxxxxx -> 1 byte ASCII encoding */ - if (((mbyte = *mbchar++) & 0x80) == 0) { - if (wcharp) - *wcharp = (smb_wchar_t)mbyte; - - return (mbyte ? 1 : 0); - } - - /* 10xxxxxx -> invalid first byte */ - if ((mbyte & 0x40) == 0) + /* + * How many bytes in this symbol? + */ + count = u8_validate((char *)mbchar, nbytes, NULL, 0, &err); + if (count < 0) return (-1); - wide_char = mbyte; - if ((mbyte & 0x20) == 0) { - wide_char &= 0x1f; - bytes_left = 1; - } else if ((mbyte & 0x10) == 0) { - wide_char &= 0x0f; - bytes_left = 2; - } else { + mblen = count; + wclen = 1; + err = uconv_u8tou32((const uchar_t *)mbchar, &mblen, + &wide_char, &wclen, UCONV_OUT_SYSTEM_ENDIAN); + if (err != 0) return (-1); - } - - count = 1; - while (bytes_left--) { - if (((mbyte = *mbchar++) & 0xc0) != 0x80) - return (-1); - - count++; - wide_char = (wide_char << 6) | (mbyte & 0x3f); + if (wclen == 0) { + wide_char = 0; + count = 0; } if (wcharp) @@ -174,25 +153,27 @@ smb_mbtowc(smb_wchar_t *wcharp, const char *mbchar, size_t nbytes) * mbchar must be large enough to accommodate the multibyte character. * * Returns the numberof bytes written to mbchar. + * Note: handles null like any 1-byte char. */ int -smb_wctomb(char *mbchar, smb_wchar_t wchar) +smb_wctomb(char *mbchar, uint32_t wchar) { - if ((wchar & ~0x7f) == 0) { - *mbchar = (char)wchar; - return (1); - } + char junk[MTS_MB_CUR_MAX+1]; + size_t mblen; + size_t wclen; + int err; - if ((wchar & ~0x7ff) == 0) { - *mbchar++ = (wchar >> 6) | 0xc0; - *mbchar = (wchar & 0x3f) | 0x80; - return (2); - } + if (mbchar == NULL) + mbchar = junk; - *mbchar++ = (wchar >> 12) | 0xe0; - *mbchar++ = ((wchar >> 6) & 0x3f) | 0x80; - *mbchar = (wchar & 0x3f) | 0x80; - return (3); + mblen = MTS_MB_CUR_MAX; + wclen = 1; + err = uconv_u32tou8(&wchar, &wclen, (uchar_t *)mbchar, &mblen, + UCONV_IN_SYSTEM_ENDIAN | UCONV_IGNORE_NULL); + if (err != 0) + return (-1); + + return ((int)mblen); } @@ -206,46 +187,46 @@ smb_wctomb(char *mbchar, smb_wchar_t wchar) * terminated if there is room. * * Returns the number of bytes converted, not counting the terminating - * null byte. + * null byte. Returns -1 if an invalid WC sequence is encountered. */ size_t -smb_wcstombs(char *mbstring, const smb_wchar_t *wcstring, size_t nbytes) +smb_wcstombs(char *mbs, const smb_wchar_t *wcs, size_t nbytes) { - char *start = mbstring; - const smb_wchar_t *wcp = wcstring; - smb_wchar_t wide_char = 0; - char buf[4]; - size_t len; + size_t mbslen, wcslen; + int err; - if ((mbstring == NULL) || (wcstring == NULL)) + /* NULL or empty input is allowed. */ + if (wcs == NULL || *wcs == 0) { + if (mbs != NULL && nbytes > 0) + *mbs = '\0'; return (0); + } - while (nbytes > MTS_MB_CHAR_MAX) { - wide_char = *wcp++; - len = smb_wctomb(mbstring, wide_char); - - if (wide_char == 0) - /*LINTED E_PTRDIFF_OVERFLOW*/ - return (mbstring - start); - - mbstring += len; - nbytes -= len; + /* + * Traditional wcstombs(3C) allows mbs==NULL to get the length. + * SMB never calls it that way, but let's future-proof. + */ + if (mbs == NULL) { + return ((size_t)-1); } - while (wide_char && nbytes) { - wide_char = *wcp++; - if ((len = smb_wctomb(buf, wide_char)) > nbytes) { - *mbstring = 0; - break; - } + /* + * Compute wcslen + */ + wcslen = 0; + while (wcs[wcslen] != 0) + wcslen++; - bcopy(buf, mbstring, len); - mbstring += len; - nbytes -= len; - } + mbslen = nbytes; + err = uconv_u16tou8(wcs, &wcslen, + (uchar_t *)mbs, &mbslen, UCONV_IN_LITTLE_ENDIAN); + if (err != 0) + return ((size_t)-1); + + if (mbslen < nbytes) + mbs[mbslen] = '\0'; - /*LINTED E_PTRDIFF_OVERFLOW*/ - return (mbstring - start); + return (mbslen); } @@ -257,7 +238,7 @@ smb_wcstombs(char *mbstring, const smb_wchar_t *wcstring, size_t nbytes) size_t smb_wcequiv_strlen(const char *mbs) { - smb_wchar_t wide_char; + uint32_t wide_char; size_t bytes; size_t len = 0; @@ -265,9 +246,15 @@ smb_wcequiv_strlen(const char *mbs) bytes = smb_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX); if (bytes == ((size_t)-1)) return ((size_t)-1); + mbs += bytes; len += sizeof (smb_wchar_t); - mbs += bytes; + if (bytes > 3) { + /* + * Extended unicode, so TWO smb_wchar_t + */ + len += sizeof (smb_wchar_t); + } } return (len); @@ -277,27 +264,37 @@ smb_wcequiv_strlen(const char *mbs) /* * Returns the number of bytes that would be written if the multi- * byte string mbs was converted to an OEM character string, - * not counting the terminating null character. + * (smb_mbstooem) not counting the terminating null character. */ size_t smb_sbequiv_strlen(const char *mbs) { - smb_wchar_t wide_char; size_t nbytes; size_t len = 0; while (*mbs) { - nbytes = smb_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX); + nbytes = smb_mbtowc(NULL, mbs, MTS_MB_CHAR_MAX); if (nbytes == ((size_t)-1)) return ((size_t)-1); + if (nbytes == 0) + break; - /* - * 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; + if (nbytes == 1) { + /* ASCII */ + len++; + } else if (nbytes < 8) { + /* Compute OEM length */ + char mbsbuf[8]; + uint8_t oembuf[8]; + int oemlen; + (void) strlcpy(mbsbuf, mbs, nbytes+1); + oemlen = smb_mbstooem(oembuf, mbsbuf, 8); + if (oemlen < 0) + return ((size_t)-1); + len += oemlen; + } else { + return ((size_t)-1); + } mbs += nbytes; } diff --git a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers index 8f1ad4a552..37b8272be4 100644 --- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers +++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers @@ -270,6 +270,7 @@ SYMBOL_VERSION SUNWprivate { smb_lookup_name; smb_lookup_sid; smb_match_netlogon_seqnum; + smb_mbstooem; smb_mbstowcs; smb_mbtowc; smb_msgbuf_base; @@ -308,6 +309,7 @@ SYMBOL_VERSION SUNWprivate { smb_nic_getnum; smb_nic_init; smb_notify_dc_changed; + smb_oemtombs; smb_priv_getbyname; smb_priv_getbyvalue; smb_priv_presentable_ids; 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 e4cd2435db..f9b629b24f 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c +++ b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c @@ -22,7 +22,7 @@ * 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. */ /* @@ -45,8 +45,7 @@ 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_oem_string(smb_request_t *, 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); @@ -310,7 +309,7 @@ smb_mbc_vdecodef(mbuf_chain_t *mbc, const char *fmt, va_list ap) goto unicode_translation; /* FALLTHROUGH */ - case 's': /* OEM string */ + case 's': /* get OEM string */ oem_conversion: ASSERT(sr != NULL); charpp = va_arg(ap, char **); @@ -321,14 +320,12 @@ oem_conversion: return (-1); break; - case 'U': /* Convert from unicode */ + case 'U': /* get UTF-16 string */ unicode_translation: ASSERT(sr != 0); 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, charpp, repc) != 0) return (-1); @@ -519,7 +516,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; + char *charp; uint8_t *cvalp; timestruc_t *tvp; smb_vdb_t *vdp; @@ -680,20 +677,18 @@ smb_mbc_vencodef(mbuf_chain_t *mbc, const char *fmt, va_list ap) goto unicode_translation; /* FALLTHROUGH */ - case 's': /* OEM string */ + case 's': /* put OEM string */ oem_conversion: charp = va_arg(ap, char *); if (!repc_specified) repc = 0; - if (mbc_marshal_put_oem_string(sr, mbc, + if (mbc_marshal_put_oem_string(mbc, charp, repc) != 0) return (DECODE_NO_MORE_DATA); break; - case 'U': /* Convert to unicode, align to word boundary */ + case 'U': /* put UTF-16 string */ unicode_translation: - if (mbc->chain_offset & 1) - mbc->chain_offset++; charp = va_arg(ap, char *); if (!repc_specified) repc = 0; @@ -1071,91 +1066,147 @@ mbc_marshal_put_long_long(mbuf_chain_t *mbc, uint64_t data) /* * 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_oem_string( - smb_request_t *sr, - mbuf_chain_t *mbc, - char *str, - int repc) +mbc_marshal_put_oem_string(mbuf_chain_t *mbc, char *mbs, int repc) { - uint8_t *s, *oembuf; - int buflen; + uint8_t *oembuf = NULL; + uint8_t *s; int oemlen; - int putlen; + int rlen; + int rc; /* - * First convert to OEM string. The OEM string - * will be no longer than the UTF-8 string. + * Compute length of converted OEM string, + * NOT including null terminator */ - buflen = strlen(str) + 1; - oembuf = smb_srm_zalloc(sr, buflen); - oemlen = smb_mbstooem(oembuf, str, buflen); - if (oemlen == -1) + if ((oemlen = smb_sbequiv_strlen(mbs)) == -1) return (DECODE_NO_MORE_DATA); - /* null terminator */ - if (oemlen < buflen) - oembuf[oemlen++] = '\0'; - - /* If specified, repc limits the length. */ - putlen = oemlen; - if ((repc > 0) && (repc < putlen)) - putlen = repc; - - if (mbc_marshal_make_room(mbc, putlen)) + /* + * 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); + /* + * Convert into a temporary buffer + * Free oembuf before return. + */ + 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'; + + /* + * Copy the converted string into the message, + * truncated or paded as required. + */ s = oembuf; - while (putlen > 0) { + while (repc > 0) { mbc_marshal_store_byte(mbc, *s); - s++; - putlen--; + if (*s != '\0') + s++; + repc--; } + rc = 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 UTF-16 null (2 bytes) unless the repc count - * limits the length we put into the mbc. + * 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 *str, 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; + + /* 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); + } - if ((length = smb_wcequiv_strlen(str)) == -1) + /* + * 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); - /* null terminator */ - length += sizeof (smb_wchar_t); + /* + * 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); - /* If specified, repc limits the length. */ - if ((repc > 0) && (repc < length)) - length = repc; + /* + * Convert into a temporary buffer + * Free wcsbuf before return. + */ + 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; - if (mbc_marshal_make_room(mbc, length)) - return (DECODE_NO_MORE_DATA); - while (length > 0) { - 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 str will not be advanced beyond - * that point. Length will continue to be decremented so - * we won't get stuck here. - */ - str += consumed; + /* + * 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*/ @@ -1394,61 +1445,71 @@ mbc_marshal_get_long_long(mbuf_chain_t *mbc, uint64_t *data) * * 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. + * If max_bytes != 0, consume at most max_bytes of the mbc. + * See also: msgbuf_get_oem_string */ static int -mbc_marshal_get_oem_string( - smb_request_t *sr, - mbuf_chain_t *mbc, - char **strpp, - int repc) +mbc_marshal_get_oem_string(smb_request_t *sr, + mbuf_chain_t *mbc, char **strpp, int max_bytes) { - uint8_t *ch, *rcvbuf; - char *mbsbuf; - int mbslen, mbsmax; - int buflen; - int oemlen; - - buflen = MALLOC_QUANTUM; - rcvbuf = smb_srm_zalloc(sr, buflen); + char *mbs; + uint8_t *oembuf = NULL; + int oemlen, oemmax; + int mbsmax; + int rlen; + int rc; - if (repc == 0) - repc = 0xffff; + if (max_bytes == 0) + max_bytes = 0xffff; + /* + * Get the OtW data into a temporary buffer. + * Free oembuf before return. + */ oemlen = 0; - ch = rcvbuf; + oemmax = MALLOC_QUANTUM; + oembuf = smb_mem_alloc(oemmax); for (;;) { - while (oemlen < buflen) { - if (repc-- <= 0) { - *ch++ = 0; - goto multibyte_encode; - } - if (MBC_ROOM_FOR(mbc, sizeof (char)) == 0) { - /* Data will never be available */ - return (DECODE_NO_MORE_DATA); - } - if ((*ch++ = mbc_marshal_fetch_byte(mbc)) == 0) - goto multibyte_encode; - oemlen++; + uint8_t ch; + + if (oemlen >= max_bytes) + break; + if ((oemlen + 2) >= oemmax) { + oemmax += MALLOC_QUANTUM; + oembuf = smb_mem_realloc(oembuf, oemmax); } - buflen += MALLOC_QUANTUM; - rcvbuf = smb_srm_rezalloc(sr, rcvbuf, buflen); - ch = rcvbuf + oemlen; + if (mbc_marshal_get_char(mbc, &ch) != 0) { + rc = DECODE_NO_MORE_DATA; + goto out; + } + if (ch == 0) + break; + oembuf[oemlen++] = ch; } + oembuf[oemlen] = '\0'; -multibyte_encode: /* - * UTF-8 encode the return string for internal system use. - * Allocated size is worst-case: 3x larger than OEM. + * Get the buffer we'll return and convert to UTF-8. + * May take as much as double the space. */ - 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); + 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); } /* @@ -1456,46 +1517,83 @@ multibyte_encode: * * 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. + * 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, char **strpp, int repc) + 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 (repc == 0) - repc = 0xffff; + if (max_bytes == 0) + max_bytes = 0xffff; - max = MALLOC_QUANTUM; - *strpp = 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 = *strpp; + /* + * 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 (repc <= 0) - goto done; - repc -= 2; - - if (mbc_marshal_get_short(mbc, &wchar) != 0) - return (DECODE_NO_MORE_DATA); - - if (wchar == 0) goto done; + uint16_t wchar; - 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); + } + if (mbc_marshal_get_short(mbc, &wchar) != 0) { + rc = DECODE_NO_MORE_DATA; + goto out; } - max += MALLOC_QUANTUM; - *strpp = smb_srm_rezalloc(sr, *strpp, max); - ch = *strpp + length; + if (wchar == 0) + break; + /* Keep in little-endian form. */ + LE_OUT16(wcsbuf + wcslen, wchar); + wcslen++; } -done: *ch = 0; - return (0); + 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*/ diff --git a/usr/src/uts/common/fs/smbsrv/smb_vops.c b/usr/src/uts/common/fs/smbsrv/smb_vops.c index 23ba643089..7a3905d7bb 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_vops.c +++ b/usr/src/uts/common/fs/smbsrv/smb_vops.c @@ -1587,7 +1587,7 @@ smb_vop_catia_v5tov4(char *name, char *buf, int buflen) { int v4_idx, numbytes, inc; int space_left = buflen - 1; /* one byte reserved for null */ - smb_wchar_t wc; + uint32_t wc; char mbstring[MTS_MB_CHAR_MAX]; char *p, *src = name, *dst = buf; @@ -1644,7 +1644,7 @@ smb_vop_catia_v4tov5(char *name, char *buf, int buflen) { int v5_idx, numbytes; int space_left = buflen - 1; /* one byte reserved for null */ - smb_wchar_t wc; + uint32_t wc; char mbstring[MTS_MB_CHAR_MAX]; char *src = name, *dst = buf; diff --git a/usr/src/uts/common/smbsrv/string.h b/usr/src/uts/common/smbsrv/string.h index 510fe502c3..14b95f6efa 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 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2017 by Delphix. All rights reserved. */ @@ -85,7 +85,7 @@ typedef enum codepage_id { /* * Maximum number of bytes per multi-byte character. */ -#define MTS_MB_CUR_MAX 3 +#define MTS_MB_CUR_MAX 4 #define MTS_MB_CHAR_MAX MTS_MB_CUR_MAX typedef uint16_t smb_wchar_t; @@ -118,8 +118,8 @@ void smb_codepage_fini(void); int smb_isupper(int); int smb_islower(int); -int smb_toupper(int); -int smb_tolower(int); +uint32_t smb_toupper(uint32_t); +uint32_t smb_tolower(uint32_t); char *smb_strupr(char *); char *smb_strlwr(char *); int smb_isstrupr(const char *); @@ -130,8 +130,8 @@ boolean_t smb_match(const char *, const char *, boolean_t); size_t smb_mbstowcs(smb_wchar_t *, const char *, size_t); size_t smb_wcstombs(char *, const smb_wchar_t *, size_t); -int smb_mbtowc(smb_wchar_t *, const char *, size_t); -int smb_wctomb(char *, smb_wchar_t); +int smb_mbtowc(uint32_t *, const char *, size_t); +int smb_wctomb(char *, uint32_t); size_t smb_wcequiv_strlen(const char *); size_t smb_sbequiv_strlen(const char *); @@ -139,9 +139,6 @@ 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 *); - size_t ucstooem(char *, const smb_wchar_t *, size_t, uint32_t); size_t oemtoucs(smb_wchar_t *, const char *, size_t, uint32_t); |