diff --git a/Makefile.am b/Makefile.am index 13051ecc..daa44558 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,3 +10,6 @@ pkgconfig_DATA = libsmb2.pc EXTRA_DIST = \ libsmb2.pc.in + +test: $(SUBDIRS) + cd tests; make test diff --git a/configure.ac b/configure.ac index 3d978f9f..f1bcd667 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ -AC_INIT([libsmb2], [4.0.0], [ronniesahlberg@gmail.com]) +AC_INIT([libsmb2],[4.0.0],[ronniesahlberg@gmail.com]) -AC_PREREQ([2.58]) +AC_PREREQ([2.71]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([-Wall foreign subdir-objects 1.11]) AC_CANONICAL_HOST @@ -11,7 +11,7 @@ m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) dnl Do not add default CFLAGS in AC_PROG_CC : ${CFLAGS=""} AC_PROG_CC -AC_PROG_LIBTOOL +LT_INIT AM_PROG_CC_C_O @@ -174,6 +174,8 @@ AC_CONFIG_FILES([ examples/Makefile include/Makefile lib/Makefile + tests/Makefile ]) -AC_OUTPUT([libsmb2.pc]) +AC_CONFIG_FILES([libsmb2.pc]) +AC_OUTPUT diff --git a/examples/smb2-dcerpc-coder-test.c b/examples/smb2-dcerpc-coder-test.c index 5d3c6ff6..2c0721b9 100644 --- a/examples/smb2-dcerpc-coder-test.c +++ b/examples/smb2-dcerpc-coder-test.c @@ -48,7 +48,7 @@ int usage(void) typedef int (*compare_func)(void *ptr1, void *ptr2); -static void test_dcerpc_codec(struct dcerpc_context *dce, char *method, +static void test_dcerpc_coder(struct dcerpc_context *dce, char *method, dcerpc_coder coder, compare_func cmp, void *req, int req_size, int expected_offset, uint8_t *expected_data, @@ -70,7 +70,8 @@ static void test_dcerpc_codec(struct dcerpc_context *dce, char *method, memset(iov.buf, 0, iov.len); offset = 0; dcerpc_set_endian(pdu1, endian); - if (coder(dce, pdu1, &iov, &offset, req)) { + if (dcerpc_ptr_coder(dce, pdu1, &iov, &offset, req, + PTR_REF, coder)) { printf("Encoding failed\n"); exit(20); } @@ -90,6 +91,20 @@ static void test_dcerpc_codec(struct dcerpc_context *dce, char *method, } printf("\n"); } + /* Fake unque pointers. different implementations implement them + differently and I don't want to have to modify buffers that + I take from genuine network traffic. + */ + { + int o; + for (o = 0; o < offset; o+= 8) { + if (!memcmp(&buf[o], "UptrrtpU", 8)) { + memcpy(&buf[o], &expected_data[o], 8); + } + } + } + //int expected_offset, uint8_t *expected_data, + if (memcmp(iov.buf, expected_data, expected_offset)) { printf("Encoding failed 1. Data Mismatch\n"); for (i = 0; i < expected_offset; i++) { @@ -99,14 +114,14 @@ static void test_dcerpc_codec(struct dcerpc_context *dce, char *method, } exit(20); } - /* Decode it again */ req2 = calloc(1, req_size); pdu2 = dcerpc_allocate_pdu(dce, DCERPC_DECODE, req_size); offset = 0; dcerpc_set_endian(pdu2, endian); - if (coder(dce, pdu2, &iov, &offset, req2)) { - printf("Decoding failed\n"); + if (dcerpc_ptr_coder(dce, pdu2, &iov, &offset, req2, + PTR_REF, coder)) { + printf("Encoding failed\n"); exit(20); } if (offset != expected_offset) { @@ -120,19 +135,6 @@ static void test_dcerpc_codec(struct dcerpc_context *dce, char *method, free(req2); } -/* - struct srvsvc_SHARE_INFO_1 { - struct dcerpc_utf16 netname; - uint32_t type; - struct dcerpc_utf16 remark; - }; - int - srvsvc_SHARE_INFO_1_coder(struct dcerpc_context *ctx, - struct dcerpc_pdu *pdu, - struct smb2_iovec *iov, int *offset, - void *ptr) -*/ - static int compare_utf16(void *ptr1, void *ptr2) { struct dcerpc_utf16 *s1 = ptr1; @@ -157,7 +159,7 @@ static void test_utf16_ndr32_le(struct dcerpc_context *dce) s1.utf8 = "\\\\win16-1"; dcerpc_set_tctx(dce, 0); /* NDR32 */ - test_dcerpc_codec(dce, "dcerpc_utf16 NDR32 LE", + test_dcerpc_coder(dce, "dcerpc_utf16 NDR32 LE", dcerpc_utf16z_coder, compare_utf16, &s1, sizeof(s1), sizeof(buf), buf, 0, 1); @@ -175,7 +177,7 @@ static void test_utf16_ndr32_be(struct dcerpc_context *dce) s1.utf8 = "\\\\win16-1"; dcerpc_set_tctx(dce, 0); /* NDR32 */ - test_dcerpc_codec(dce, "dcerpc_utf16 NDR32 BE", + test_dcerpc_coder(dce, "dcerpc_utf16 NDR32 BE", dcerpc_utf16z_coder, compare_utf16, &s1, sizeof(s1), sizeof(buf), buf, 0, 0); @@ -195,12 +197,404 @@ static void test_utf16_ndr64_le(struct dcerpc_context *dce) s1.utf8 = "\\\\win16-1"; dcerpc_set_tctx(dce, 1); /* NDR64 */ - test_dcerpc_codec(dce, "dcerpc_utf16 NDR64 LE", + test_dcerpc_coder(dce, "dcerpc_utf16 NDR64 LE", dcerpc_utf16z_coder, compare_utf16, &s1, sizeof(s1), sizeof(buf), buf, 0, 1); } +/* + struct srvsvc_SHARE_INFO_1 { + struct dcerpc_utf16 netname; + uint32_t type; + struct dcerpc_utf16 remark; + }; + int + srvsvc_SHARE_INFO_1_coder(struct dcerpc_context *ctx, + struct dcerpc_pdu *pdu, + struct smb2_iovec *iov, int *offset, + void *ptr) +*/ + +static int compare_SHARE_INFO_1(void *ptr1, void *ptr2) +{ + struct srvsvc_SHARE_INFO_1 *s1 = ptr1; + struct srvsvc_SHARE_INFO_1 *s2 = ptr2; + + if (strcmp(s1->netname.utf8, s2->netname.utf8)) { + printf("Compare ->netname failed %s != %s\n", s1->netname.utf8, s2->netname.utf8); + exit(20); + } + if (s1->type != s2->type) { + printf("Compare ->type failed 0x%08x != 0x%08x\n", s1->type, s2->type); + exit(20); + } + if (strcmp(s1->remark.utf8, s2->remark.utf8)) { + printf("Compare ->remark failed %s != %s\n", s1->remark.utf8, s2->remark.utf8); + exit(20); + } + return 0; +} + +static void test_SHARE_INFO_1_ndr32_le(struct dcerpc_context *dce) +{ + struct srvsvc_SHARE_INFO_1 s1; + unsigned char buf[] = { + 0x55, 0x70, 0x74, 0x72, 0x03, 0x00, 0x00, 0x80, + 0x55, 0x70, 0x74, 0x72, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x49, 0x00, 0x50, 0x00, 0x43, 0x00, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x49, 0x00, + 0x50, 0x00, 0x43, 0x00, 0x00, 0x00 + }; + + s1.netname.utf8 = "IPC$"; + s1.type = 0x80000003; + s1.remark.utf8 = "Remote IPC"; + dcerpc_set_tctx(dce, 0); /* NDR32 */ + test_dcerpc_coder(dce, "dcerpc_SHARE_INFO_1 NDR32 LE", + srvsvc_SHARE_INFO_1_coder, compare_SHARE_INFO_1, + &s1, sizeof(s1), + sizeof(buf), buf, 0, 1); +} + +static int compare_SHARE_INFO_1_CONTAINER(void *ptr1, void *ptr2) +{ + struct srvsvc_SHARE_INFO_1_CONTAINER *s1 = ptr1; + struct srvsvc_SHARE_INFO_1_CONTAINER *s2 = ptr2; + int i; + + if (s1->EntriesRead != s2->EntriesRead) { + printf("Compare EntriesRead failed %d != %d\n", s1->EntriesRead, s2->EntriesRead); + exit(20); + } + for (i = 0; i < 10; i++) { + if (strcmp(s1->Buffer->share_info_1[i].netname.utf8, s2->Buffer->share_info_1[i].netname.utf8)) { + printf("Compare ->netname failed %s != %s\n", s1->Buffer->share_info_1[i].netname.utf8, s2->Buffer->share_info_1[i].netname.utf8); + exit(20); + } + if (s1->Buffer->share_info_1[i].type != s2->Buffer->share_info_1[i].type) { + printf("Compare ->type failed %d != %d\n", s1->Buffer->share_info_1[i].type, s2->Buffer->share_info_1[i].type); + exit(20); + } + if (strcmp(s1->Buffer->share_info_1[i].remark.utf8, s2->Buffer->share_info_1[i].remark.utf8)) { + printf("Compare ->remark failed %s != %s\n", s1->Buffer->share_info_1[i].remark.utf8, s2->Buffer->share_info_1[i].remark.utf8); + exit(20); + } + } + return 0; +} + +static void test_SHARE_INFO_1_CONTAINER_ndr32_le(struct dcerpc_context *dce) +{ + struct srvsvc_SHARE_INFO_1_carray ca; + struct srvsvc_SHARE_INFO_1_CONTAINER s1; + struct srvsvc_SHARE_INFO_1 si[10]; + unsigned char buf[] = { + /*000*/ 0x0a, 0x00, 0x00, 0x00, 0x55, 0x70, 0x74, 0x72, + 0x0a, 0x00, 0x00, 0x00, 0x55, 0x70, 0x74, 0x72, + /*010*/ 0x00, 0x00, 0x00, 0x80, 0x55, 0x70, 0x74, 0x72, + 0x55, 0x70, 0x74, 0x72, 0x00, 0x00, 0x00, 0x00, + /*020*/ 0x55, 0x70, 0x74, 0x72, 0x55, 0x70, 0x74, 0x72, + 0x00, 0x00, 0x00, 0x80, 0x55, 0x70, 0x74, 0x72, + /*030*/ 0x55, 0x70, 0x74, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x70, 0x74, 0x72, 0x55, 0x70, 0x74, 0x72, + /*040*/ 0x00, 0x00, 0x00, 0x00, 0x55, 0x70, 0x74, 0x72, + 0x55, 0x70, 0x74, 0x72, 0x03, 0x00, 0x00, 0x80, + /*050*/ 0x55, 0x70, 0x74, 0x72, 0x55, 0x70, 0x74, 0x72, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x70, 0x74, 0x72, + /*060*/ 0x55, 0x70, 0x74, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x70, 0x74, 0x72, 0x55, 0x70, 0x74, 0x72, + /*070*/ 0x00, 0x00, 0x00, 0x00, 0x55, 0x70, 0x74, 0x72, + 0x55, 0x70, 0x74, 0x72, 0x00, 0x00, 0x00, 0x00, + /*080*/ 0x55, 0x70, 0x74, 0x72, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x44, 0x00, 0x4d, 0x00, 0x49, 0x00, + 0x4e, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x52, 0x00, 0x65, 0x00, + 0x6d, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x20, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x43, 0x00, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, + 0x75, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x20, 0x00, + 0x73, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x46, 0x00, 0x53, 0x00, 0x2d, 0x00, + 0x53, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e, 0x00, + 0x64, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6f, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x45, 0x00, 0x6e, 0x00, + 0x63, 0x00, 0x72, 0x00, 0x79, 0x00, 0x70, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, + 0x43, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x52, 0x00, 0x65, 0x00, + 0x6d, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x20, 0x00, 0x49, 0x00, 0x50, 0x00, 0x43, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x46, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x53, 0x00, 0x63, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x63, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00 + }; + + s1.EntriesRead = 10; + s1.Buffer = &ca; + s1.Buffer->max_count = 10; + s1.Buffer->share_info_1 = &si[0]; + si[0].netname.utf8 = "ADMIN$"; + si[0].type = 0x80000000; + si[0].remark.utf8 = "Remote Admin"; + si[1].netname.utf8 = "C"; + si[1].type = 0x00000000; + si[1].remark.utf8 = ""; + si[2].netname.utf8 = "C$"; + si[2].type = 0x80000000; + si[2].remark.utf8 = "Default share"; + si[3].netname.utf8 = "DFS-Standalone"; + si[3].type = 0x00000000; + si[3].remark.utf8 = ""; + si[4].netname.utf8 = "Encrypted"; + si[4].type = 0x00000000; + si[4].remark.utf8 = ""; + si[5].netname.utf8 = "IPC$"; + si[5].type = 0x80000003; + si[5].remark.utf8 = "Remote IPC"; + si[6].netname.utf8 = "NFS"; + si[6].type = 0x00000000; + si[6].remark.utf8 = ""; + si[7].netname.utf8 = "Scratch"; + si[7].type = 0x00000000; + si[7].remark.utf8 = ""; + si[8].netname.utf8 = "Share"; + si[8].type = 0x00000000; + si[8].remark.utf8 = ""; + si[9].netname.utf8 = "Users"; + si[9].type = 0x00000000; + si[9].remark.utf8 = ""; + dcerpc_set_tctx(dce, 0); /* NDR32 */ + test_dcerpc_coder(dce, "dcerpc_SHARE_INFO_1_CONTAINER NDR32 LE", + srvsvc_SHARE_INFO_1_CONTAINER_coder, compare_SHARE_INFO_1_CONTAINER, + &s1, sizeof(s1), + sizeof(buf), buf, 0, 1); +} + +static void test_SHARE_INFO_1_CONTAINER_ndr64_le(struct dcerpc_context *dce) +{ + struct srvsvc_SHARE_INFO_1_carray ca; + struct srvsvc_SHARE_INFO_1_CONTAINER s1; + struct srvsvc_SHARE_INFO_1 si[10]; + unsigned char buf[] = { + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x44, 0x00, 0x4d, 0x00, 0x49, 0x00, + 0x4e, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x41, 0x00, + 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, + 0x75, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x20, 0x00, + 0x73, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x46, 0x00, 0x53, 0x00, 0x2d, 0x00, + 0x53, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e, 0x00, + 0x64, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6f, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x72, 0x00, + 0x79, 0x00, 0x70, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x00, 0x50, 0x00, 0x43, 0x00, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x49, 0x00, + 0x50, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x46, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x63, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x63, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }; + + s1.EntriesRead = 10; + s1.Buffer = &ca; + s1.Buffer->max_count = 10; + s1.Buffer->share_info_1 = &si[0]; + si[0].netname.utf8 = "ADMIN$"; + si[0].type = 0x80000000; + si[0].remark.utf8 = "Remote Admin"; + si[1].netname.utf8 = "C"; + si[1].type = 0x00000000; + si[1].remark.utf8 = ""; + si[2].netname.utf8 = "C$"; + si[2].type = 0x80000000; + si[2].remark.utf8 = "Default share"; + si[3].netname.utf8 = "DFS-Standalone"; + si[3].type = 0x00000000; + si[3].remark.utf8 = ""; + si[4].netname.utf8 = "Encrypted"; + si[4].type = 0x00000000; + si[4].remark.utf8 = ""; + si[5].netname.utf8 = "IPC$"; + si[5].type = 0x80000003; + si[5].remark.utf8 = "Remote IPC"; + si[6].netname.utf8 = "NFS"; + si[6].type = 0x00000000; + si[6].remark.utf8 = ""; + si[7].netname.utf8 = "Scratch"; + si[7].type = 0x00000000; + si[7].remark.utf8 = ""; + si[8].netname.utf8 = "Share"; + si[8].type = 0x00000000; + si[8].remark.utf8 = ""; + si[9].netname.utf8 = "Users"; + si[9].type = 0x00000000; + si[9].remark.utf8 = ""; + dcerpc_set_tctx(dce, 1); /* NDR64 */ + test_dcerpc_coder(dce, "dcerpc_SHARE_INFO_1_CONTAINER NDR64 LE", + srvsvc_SHARE_INFO_1_CONTAINER_coder, compare_SHARE_INFO_1_CONTAINER, + &s1, sizeof(s1), + sizeof(buf), buf, 0, 1); +} + int main(int argc, char *argv[]) { struct smb2_context *smb2; @@ -230,7 +624,10 @@ int main(int argc, char *argv[]) test_utf16_ndr32_le(dce); test_utf16_ndr32_be(dce); test_utf16_ndr64_le(dce); - + test_SHARE_INFO_1_ndr32_le(dce); + test_SHARE_INFO_1_CONTAINER_ndr32_le(dce); + test_SHARE_INFO_1_CONTAINER_ndr64_le(dce); + dcerpc_destroy_context(dce); smb2_destroy_context(smb2); diff --git a/examples/smb2-server-sync.c b/examples/smb2-server-sync.c index af8a4552..dccf63ae 100644 --- a/examples/smb2-server-sync.c +++ b/examples/smb2-server-sync.c @@ -270,12 +270,10 @@ static int logoff_handler(struct smb2_server *srvr, struct smb2_context *smb2) static int tree_connect_handler(struct smb2_server *srvr, struct smb2_context *smb2, struct smb2_tree_connect_request *req, - struct smb2_tree_connect_reply *rep, - uint32_t *tree_id) + struct smb2_tree_connect_reply *rep) { rep->share_type = SMB2_SHARE_TYPE_DISK; rep->maximal_access = 0x101f01ff; - *tree_id = 0x50625678; if (req->path && req->path_length) { int ei = (req->path_length / 2) - 4; @@ -414,6 +412,7 @@ static int query_info_handler(struct smb2_server *srvr, struct smb2_context *smb } struct smb2_server_request_handlers test_handlers = { + NULL, authorize_handler, session_handler, logoff_handler, @@ -429,7 +428,9 @@ struct smb2_server_request_handlers test_handlers = { cancel_handler, echo_handler, query_directory_handler, + NULL, query_info_handler, + NULL }; struct smb2_server server; @@ -474,6 +475,9 @@ int main(int argc, char **argv) memset(&server, 0, sizeof(server)); server.handlers = &test_handlers; + server.signing_enabled = 1; + server.allow_anonymous = 1; + server.port = strtoul(argv[1], NULL, 0); err = smb2_serve_port(&server, 1, on_new_client, (void *)&smb2); diff --git a/examples/smb2-share-enum.c b/examples/smb2-share-enum.c index d178ea73..c25954b6 100644 --- a/examples/smb2-share-enum.c +++ b/examples/smb2-share-enum.c @@ -22,6 +22,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #include #include #include +#include #include "smb2.h" #include "libsmb2.h" @@ -38,11 +39,12 @@ int poll(struct pollfd *fds, unsigned int nfds, int timo); #endif int is_finished; +int level; int usage(void) { fprintf(stderr, "Usage:\n" - "smb2-share-enum \n\n" + "smb2-share-enum [-l level] \n\n" "URL format: " "smb://[@][:]/\n"); exit(1); @@ -61,29 +63,39 @@ void se_cb(struct smb2_context *smb2, int status, } /* We always only use Level1 for netshare enum */ - printf("Number of shares:%d\n", rep->ses.ShareInfo.Level1.EntriesRead); - for (i = 0; i < rep->ses.ShareInfo.Level1.EntriesRead; i++) { - printf("%-20s %-20s", rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].netname.utf8, - rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].remark.utf8); - if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_DISKTREE) { - printf(" DISKTREE"); + switch (level) { + case SHARE_INFO_0: + printf("Number of shares:%d\n", rep->ses.ShareInfo.Level0.EntriesRead); + for (i = 0; i < rep->ses.ShareInfo.Level0.EntriesRead; i++) { + printf("%-20s\n", rep->ses.ShareInfo.Level0.Buffer->share_info_0[i].netname.utf8); } - if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_PRINTQ) { - printf(" PRINTQ"); + break; + case SHARE_INFO_1: + printf("Number of shares:%d\n", rep->ses.ShareInfo.Level1.EntriesRead); + for (i = 0; i < rep->ses.ShareInfo.Level1.EntriesRead; i++) { + printf("%-20s %-20s", rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].netname.utf8, + rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].remark.utf8); + if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_DISKTREE) { + printf(" DISKTREE"); + } + if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_PRINTQ) { + printf(" PRINTQ"); + } + if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_DEVICE) { + printf(" DEVICE"); + } + if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_IPC) { + printf(" IPC"); + } + if (rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & SHARE_TYPE_TEMPORARY) { + printf(" TEMPORARY"); + } + if (rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & SHARE_TYPE_HIDDEN) { + printf(" HIDDEN"); + } + printf("\n"); } - if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_DEVICE) { - printf(" DEVICE"); - } - if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_IPC) { - printf(" IPC"); - } - if (rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & SHARE_TYPE_TEMPORARY) { - printf(" TEMPORARY"); - } - if (rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & SHARE_TYPE_HIDDEN) { - printf(" HIDDEN"); - } - printf("\n"); + break; } smb2_free_data(smb2, rep); @@ -96,8 +108,19 @@ int main(int argc, char *argv[]) struct smb2_context *smb2; struct smb2_url *url; struct pollfd pfd; + int opt; + + while ((opt = getopt(argc, argv, "l:")) != -1) { + switch (opt) { + case 'l': + level = atoi(optarg); + break; + default: /* '?' */ + usage(); + } + } - if (argc < 2) { + if (optind >= argc) { usage(); } @@ -107,7 +130,16 @@ int main(int argc, char *argv[]) exit(0); } - url = smb2_parse_url(smb2, argv[1]); + switch (level) { + case SHARE_INFO_0: + case SHARE_INFO_1: + break; + default: + fprintf(stderr, "level must be 0/1\n"); + exit(0); + } + + url = smb2_parse_url(smb2, argv[optind]); if (url == NULL) { fprintf(stderr, "Failed to parse url: %s\n", smb2_get_error(smb2)); @@ -125,7 +157,7 @@ int main(int argc, char *argv[]) exit(10); } - if (smb2_share_enum_async(smb2, se_cb, NULL) != 0) { + if (smb2_share_enum_async(smb2, level, se_cb, NULL) != 0) { printf("smb2_share_enum failed. %s\n", smb2_get_error(smb2)); exit(10); } diff --git a/include/apple/config.h b/include/apple/config.h index 9108896d..5dbee46e 100644 --- a/include/apple/config.h +++ b/include/apple/config.h @@ -32,7 +32,7 @@ /* #undef HAVE_LIBSOCKET */ /* Whether we have linger */ -/* #undef HAVE_LINGER */ +#define HAVE_LINGER 1 /* Define to 1 if you have the header file. */ #define HAVE_NETDB_H 1 diff --git a/include/libsmb2-private.h b/include/libsmb2-private.h index e4d80d32..8e8fddae 100644 --- a/include/libsmb2-private.h +++ b/include/libsmb2-private.h @@ -108,9 +108,20 @@ enum smb2_sec { SMB2_SEC_KRB5, }; +/* current tree id stack, note: index 0 in the stack is not used +*/ +#define SMB2_MAX_TREE_NESTING 32 +#define smb2_tree_id(smb2) (((smb2)->tree_id_cur >= 0)?smb2->tree_id[(smb2)->tree_id_cur]:0xdeadbeef) + #define MAX_CREDITS 1024 #define SMB2_SALT_SIZE 32 +struct sync_cb_data { + int is_finished; + int status; + void *ptr; +}; + struct smb2_context { t_socket fd; @@ -147,12 +158,15 @@ struct smb2_context { smb2_error_cb error_cb; smb2_command_cb connect_cb; void *connect_data; + struct sync_cb_data connect_cb_data; int credits; char client_guid[16]; - uint32_t tree_id; + uint32_t tree_id[SMB2_MAX_TREE_NESTING]; + int tree_id_top; + int tree_id_cur; uint64_t message_id; uint64_t session_id; uint8_t *session_key; @@ -202,6 +216,18 @@ struct smb2_context { * (if this context is a server) */ struct smb2_pdu *next_pdu; + + /* flag indicated command packers/unpackers can pass "extra" + * content without trying to decode or encode it. this is + * useful for proxies and applies only to the commands with + * complex data: query-info, query-directory, ioctl, and + * create (contexts). the command fixed part is always + * de/en-coded regardless of this setting + */ + int passthrough; + + /* last file_id in a create-reply, for "related requests" */ + smb2_file_id last_file_id; /* Server capabilities */ uint8_t supports_multi_credit; @@ -288,6 +314,9 @@ struct smb2_iovec *smb2_add_iovector(struct smb2_context *smb2, int smb2_pad_to_64bit(struct smb2_context *smb2, struct smb2_io_vectors *v); +int smb2_connect_tree_id(struct smb2_context *smb2, uint32_t tree_id); +int smb2_disconnect_tree_id(struct smb2_context *smb2, uint32_t tree_id); + struct smb2_pdu *smb2_allocate_pdu(struct smb2_context *smb2, enum smb2_command command, smb2_command_cb cb, void *cb_data); @@ -357,6 +386,12 @@ int smb2_process_query_directory_request_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu); int smb2_process_query_directory_request_variable(struct smb2_context *smb2, struct smb2_pdu *pdu); +int smb2_process_change_notify_fixed(struct smb2_context *smb2, + struct smb2_pdu *pdu); +int smb2_process_change_notify_variable(struct smb2_context *smb2, + struct smb2_pdu *pdu); +int smb2_process_change_notify_request_fixed(struct smb2_context *smb2, + struct smb2_pdu *pdu); int smb2_process_query_info_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu); int smb2_process_query_info_variable(struct smb2_context *smb2, @@ -399,6 +434,8 @@ int smb2_process_flush_request_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu); int smb2_process_read_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu); +int smb2_process_read_variable(struct smb2_context *smb2, + struct smb2_pdu *pdu); int smb2_process_read_request_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu); int smb2_process_read_request_variable(struct smb2_context *smb2, diff --git a/include/module.modulemap b/include/module.modulemap new file mode 100644 index 00000000..a7b9a186 --- /dev/null +++ b/include/module.modulemap @@ -0,0 +1,21 @@ +module SMB2 [system] [extern_c] { + module LibSMB2 { + header "apple/config.h" + header "smb2/smb2-errors.h" + header "smb2/smb2.h" + header "smb2/libsmb2.h" + header "smb2/libsmb2-dcerpc.h" + header "smb2/libsmb2-dcerpc-lsa.h" + header "smb2/libsmb2-dcerpc-srvsvc.h" + } + + module Internal { + header "libsmb2-private.h" + } + + explicit module Raw { + header "smb2/libsmb2-raw.h" + } + + export SMB2 +} diff --git a/include/smb2/libsmb2-dcerpc-srvsvc.h b/include/smb2/libsmb2-dcerpc-srvsvc.h index 594d34d6..8ddcb4d5 100644 --- a/include/smb2/libsmb2-dcerpc-srvsvc.h +++ b/include/smb2/libsmb2-dcerpc-srvsvc.h @@ -41,6 +41,29 @@ struct dcerpc_pdu; #define SHARE_TYPE_TEMPORARY 0x40000000 #define SHARE_TYPE_HIDDEN 0x80000000 +enum SHARE_INFO_enum { + SHARE_INFO_0 = 0, + SHARE_INFO_1 = 1, +}; + +struct srvsvc_SHARE_INFO_0 { + struct dcerpc_utf16 netname; +}; +int srvsvc_SHARE_INFO_0_coder(struct dcerpc_context *ctx, + struct dcerpc_pdu *pdu, + struct smb2_iovec *iov, int *offset, + void *ptr); + +struct srvsvc_SHARE_INFO_0_carray { + uint32_t max_count; + struct srvsvc_SHARE_INFO_0 *share_info_0; +}; + +struct srvsvc_SHARE_INFO_0_CONTAINER { + uint32_t EntriesRead; + struct srvsvc_SHARE_INFO_0_carray *Buffer; +}; + struct srvsvc_SHARE_INFO_1 { struct dcerpc_utf16 netname; uint32_t type; @@ -52,17 +75,24 @@ int srvsvc_SHARE_INFO_1_coder(struct dcerpc_context *ctx, void *ptr); struct srvsvc_SHARE_INFO_1_carray { - uint32_t max_count; /* filled in by caller before decode */ - struct srvsvc_SHARE_INFO_1 share_info_1[]; + uint32_t max_count; + struct srvsvc_SHARE_INFO_1 *share_info_1; }; + struct srvsvc_SHARE_INFO_1_CONTAINER { uint32_t EntriesRead; struct srvsvc_SHARE_INFO_1_carray *Buffer; }; + +int srvsvc_SHARE_INFO_1_CONTAINER_coder(struct dcerpc_context *dce, + struct dcerpc_pdu *pdu, + struct smb2_iovec *iov, int *offset, + void *ptr); struct srvsvc_SHARE_ENUM_UNION { - uint32_t level; + uint32_t Level; union { + struct srvsvc_SHARE_INFO_0_CONTAINER Level0; struct srvsvc_SHARE_INFO_1_CONTAINER Level1; }; }; @@ -124,7 +154,7 @@ struct srvsvc_rep { * This pointer must be freed using smb2_free_data(). * -errno : An error occurred. */ -int smb2_share_enum_async(struct smb2_context *smb2, +int smb2_share_enum_async(struct smb2_context *smb2, enum SHARE_INFO_enum level, smb2_command_cb cb, void *cb_data); int srvsvc_NetrShareEnum_rep_coder(struct dcerpc_context *dce, diff --git a/include/smb2/libsmb2-dcerpc.h b/include/smb2/libsmb2-dcerpc.h index 91feda0b..5eed72cb 100644 --- a/include/smb2/libsmb2-dcerpc.h +++ b/include/smb2/libsmb2-dcerpc.h @@ -78,6 +78,11 @@ struct dcerpc_utf16 { const char *utf8; }; +struct dcerpc_carray { + uint32_t max_count; + uint8_t *data; +}; + extern p_syntax_id_t lsa_interface; extern p_syntax_id_t srvsvc_interface; @@ -101,9 +106,17 @@ int dcerpc_call_async(struct dcerpc_context *dce, int opnum, dcerpc_coder rep_coder, int rep_size, dcerpc_cb cb, void *cb_data); +int dcerpc_do_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu, + struct smb2_iovec *iov, + int *offset, void *ptr, + dcerpc_coder coder); int dcerpc_ptr_coder(struct dcerpc_context *dce, struct dcerpc_pdu *pdu, struct smb2_iovec *iov, int *offset, void *ptr, enum ptr_type type, dcerpc_coder coder); +int dcerpc_carray_coder(struct dcerpc_context *ctx, + struct dcerpc_pdu *pdu, + struct smb2_iovec *iov, int *offset, + void *ptr, int elem_size, dcerpc_coder coder); int dcerpc_uint8_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu, struct smb2_iovec *iov, int *offset, void *ptr); int dcerpc_uint16_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu, diff --git a/include/smb2/libsmb2-raw.h b/include/smb2/libsmb2-raw.h index 4b2d728d..9e8c7514 100644 --- a/include/smb2/libsmb2-raw.h +++ b/include/smb2/libsmb2-raw.h @@ -106,6 +106,7 @@ struct smb2_pdu *smb2_cmd_tree_connect_async(struct smb2_context *smb2, struct smb2_pdu *smb2_cmd_tree_connect_reply_async(struct smb2_context *smb2, struct smb2_tree_connect_reply *rep, + uint32_t tree_id, smb2_command_cb cb, void *cb_data); /* * Asynchronous SMB2 Tree Disconnect @@ -244,12 +245,33 @@ struct smb2_pdu *smb2_cmd_query_directory_async(struct smb2_context *smb2, smb2_command_cb cb, void *cb_data); struct smb2_pdu *smb2_cmd_query_directory_reply_async(struct smb2_context *smb2, - uint8_t info_class, - uint8_t flags, - uint32_t room, + struct smb2_query_directory_request *req, struct smb2_query_directory_reply *rep, smb2_command_cb cb, void *cb_data); +/* + * Asynchronous SMB2 Change Notify + * + * Returns: + * pdu : If the call was initiated and a connection will be attempted. + * Result of the close will be reported through the callback function. + * NULL : If there was an error. The callback function will not be invoked. + * + * Callback parameters : + * status can be either of : + * 0 : Close was successful. + * Command_data is a struct smb2_close_reply. + * + * !0 : Status is NT status code. Command_data is NULL. + */ +struct smb2_pdu *smb2_cmd_change_notify_async(struct smb2_context *smb2, + struct smb2_change_notify_request *req, + smb2_command_cb cb, void *cb_data); + +struct smb2_pdu *smb2_cmd_change_notify_reply_async(struct smb2_context *smb2, + struct smb2_change_notify_reply *rep, + smb2_command_cb cb, void *cb_data); + /* * Asynchronous SMB2 Query Info * @@ -273,9 +295,7 @@ struct smb2_pdu *smb2_cmd_query_info_async(struct smb2_context *smb2, smb2_command_cb cb, void *cb_data); struct smb2_pdu *smb2_cmd_query_info_reply_async(struct smb2_context *smb2, - uint8_t info_type, - uint8_t file_info_class, - uint32_t room, + struct smb2_query_info_request *req, struct smb2_query_info_reply *rep, smb2_command_cb cb, void *cb_data); @@ -299,6 +319,7 @@ struct smb2_pdu *smb2_cmd_set_info_async(struct smb2_context *smb2, smb2_command_cb cb, void *cb_data); struct smb2_pdu *smb2_cmd_set_info_reply_async(struct smb2_context *smb2, + struct smb2_set_info_request *req, smb2_command_cb cb, void *cb_data); /* @@ -366,6 +387,7 @@ struct smb2_pdu *smb2_cmd_echo_reply_async(struct smb2_context *smb2, * command_data is always NULL. */ struct smb2_pdu *smb2_cmd_lock_async(struct smb2_context *smb2, + struct smb2_lock_request *req, smb2_command_cb cb, void *cb_data); struct smb2_pdu *smb2_cmd_lock_reply_async(struct smb2_context *smb2, diff --git a/include/smb2/libsmb2.h b/include/smb2/libsmb2.h index 9bd0ab2a..290502f1 100644 --- a/include/smb2/libsmb2.h +++ b/include/smb2/libsmb2.h @@ -19,10 +19,6 @@ #ifndef _LIBSMB2_H_ #define _LIBSMB2_H_ -#ifdef __APPLE__ /* Some platforms doesn´t support stdint.h */ -#include -#endif - #ifdef __cplusplus extern "C" { #endif @@ -123,6 +119,15 @@ typedef int t_socket; */ struct smb2_context *smb2_init_context(void); +/* + * Close an SMB2 context + * + * closes socket if open, and clears keys but leave + * context allocated. the context will be destroyed + * at a time later when it won't be in-use + */ +void smb2_close_context(struct smb2_context *smb2); + /* * Destroy an smb2 context. * @@ -139,6 +144,12 @@ void smb2_destroy_context(struct smb2_context *smb2); */ struct smb2_context *smb2_active_contexts(void); +/* + * Determine of the context is currently active. This lets + * code know if the context was destroyed in a callback for example + */ +int smb2_context_active(struct smb2_context *smb2); + /* * EVENT SYSTEM INTEGRATION * ======================== @@ -242,6 +253,27 @@ int smb2_service_fd(struct smb2_context *smb2, t_socket fd, int revents); */ void smb2_set_timeout(struct smb2_context *smb2, int seconds); +/* + * Set passthrough-enable. Passthrough allows command packers + * and unpackers to keep the extra data on complex commands + * in its on-the-wire format without any interpretation. this + * is useful for proxy use cases where there might be no need + * to fully parse things like query-info replies or ioctl + * requests. If passthrough is not set, any command that + * that is processed that can't interpret the data will fail + * Most client use case will not need this + * + * Default is 0: no-passthrough + */ +void smb2_set_passthrough(struct smb2_context *smb2, + int passthrough); + +/* + * Get the current passthrough setting + */ +void smb2_get_passthrough(struct smb2_context *smb2, + int *passthrough); + /* * Set which version of SMB to negotiate. * Default is to let the server pick the version. @@ -470,6 +502,28 @@ int smb2_disconnect_share_async(struct smb2_context *smb2, */ int smb2_disconnect_share(struct smb2_context *smb2); +/* + * Select a tree id that was previously connected. Sets the tree_id + * in the context to be used for subsequent requests + * + * Returns: + * 0 : OK + * -errno : tree wasn't connected + */ +int smb2_select_tree_id(struct smb2_context *smb2, uint32_t tree_id); + +struct smb2_pdu; + +/* + * Get/Set the tree id of a pdu + * + * Returns: + * 0 : OK + * -errno : tree wasn't connected | no pdu | no context + */ +int smb2_get_tree_id_for_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu, uint32_t *tree_id); +int smb2_set_tree_id_for_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu, uint32_t tree_id); + /* * This function returns a description of the last encountered error. */ @@ -507,7 +561,6 @@ int nterror_to_errno(uint32_t status); struct smb2_url *smb2_parse_url(struct smb2_context *smb2, const char *url); void smb2_destroy_url(struct smb2_url *url); -struct smb2_pdu; /* * The functions are used when creating compound low level commands. * The general pattern for compound chains is @@ -528,6 +581,7 @@ void smb2_add_compound_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu, struct smb2_pdu *next_pdu); void smb2_free_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu); void smb2_queue_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu); +int smb2_pdu_is_compound(struct smb2_context *smb2); /* * OPENDIR @@ -1128,6 +1182,7 @@ const char *smb2_utf16_to_utf8(const uint16_t *str, size_t len); struct smb2_server; struct smb2_server_request_handlers { + int (*destruction_event)(struct smb2_server *srvr, struct smb2_context *smb2); int (*authorize_user)(struct smb2_server *srvr, struct smb2_context *smb2, const char *user, const char *domain, @@ -1136,8 +1191,7 @@ struct smb2_server_request_handlers { int (*logoff_cmd)(struct smb2_server *srvr, struct smb2_context *smb2); int (*tree_connect_cmd)(struct smb2_server *srvr, struct smb2_context *smb2, struct smb2_tree_connect_request *req, - struct smb2_tree_connect_reply *rep, - uint32_t *tree_id); + struct smb2_tree_connect_reply *rep); int (*tree_disconnect_cmd)(struct smb2_server *srvr, struct smb2_context *smb2, const uint32_t tree_id); int (*create_cmd)(struct smb2_server *srvr, struct smb2_context *smb2, @@ -1164,9 +1218,17 @@ struct smb2_server_request_handlers { int (*query_directory_cmd)(struct smb2_server *srvr, struct smb2_context *smb2, struct smb2_query_directory_request *req, struct smb2_query_directory_reply *rep); + int (*change_notify_cmd)(struct smb2_server *srvr, struct smb2_context *smb2, + struct smb2_change_notify_request *req); int (*query_info_cmd)(struct smb2_server *srvr, struct smb2_context *smb2, struct smb2_query_info_request *req, struct smb2_query_info_reply *rep); + int (*set_info_cmd)(struct smb2_server *srvr, struct smb2_context *smb2, + struct smb2_set_info_request *req); + /* + int (*oplock_break cmd)(struct smb2_server *srvr, struct smb2_context *smb2, + struct smb2_oplock_break_request *req); + */ }; struct smb2_server { @@ -1175,10 +1237,13 @@ struct smb2_server { char domain[128]; int fd; uint16_t port; + uint64_t session_counter; struct smb2_server_request_handlers *handlers; uint32_t max_transact_size; uint32_t max_read_size; uint32_t max_write_size; + int signing_enabled; + int allow_anonymous; /* saved from negotiate to be used in validate negotiate info */ uint32_t capabilities; uint32_t security_mode; @@ -1202,11 +1267,7 @@ int smb2_serve_port(struct smb2_server *server, const int max_connections, smb2_ * separation between dcerpc and smb2, so we need to include this header * here to retain compatibility for apps that depend on those symbols. */ -#ifdef __APPLE__ -#include -#else #include -#endif #ifdef __cplusplus } diff --git a/include/smb2/smb2.h b/include/smb2/smb2.h index ba8f712f..a9f4b4f0 100644 --- a/include/smb2/smb2.h +++ b/include/smb2/smb2.h @@ -19,10 +19,14 @@ #ifndef _SMB2_H_ #define _SMB2_H_ -#ifdef __APPLE__ -#include -#else #include + +#ifdef HAVE_STDINT_H +#include +#endif + +#ifdef HAVE_TIME_H +#include #endif #ifdef __cplusplus @@ -31,7 +35,7 @@ extern "C" { struct smb2_timeval { time_t tv_sec; - long tv_usec; + long tv_usec; }; #define SMB2_ERROR_REPLY_SIZE 9 @@ -66,11 +70,11 @@ enum smb2_command { SMB2_CANCEL = 12, SMB2_ECHO = 13, SMB2_QUERY_DIRECTORY = 14, - /* SMB2_CHANGE_NOTIFY, */ + SMB2_CHANGE_NOTIFY = 15, SMB2_QUERY_INFO = 16, SMB2_SET_INFO = 17, - /* SMB2_OPLOCK_BREAK, */ - + SMB2_OPLOCK_BREAK = 18, + SMB1_NEGOTIATE = 114, }; @@ -394,6 +398,18 @@ struct smb2_flush_request { smb2_file_id file_id; }; +#define SMB2_LOGFF_REQUEST_SIZE 4 + +struct smb2_logoff_request { + uint16_t reserved; +}; + +#define SMB2_ECHO_REQUEST_SIZE 4 + +struct smb2_echo_request { + uint16_t reserved; +}; + #define SMB2_FLUSH_REPLY_SIZE 4 #define SMB2_QUERY_DIRECTORY_REQUEST_SIZE 33 @@ -459,7 +475,7 @@ int smb2_decode_fileidfulldirectoryinformation( struct smb2_context *smb2, struct smb2_fileidfulldirectoryinformation *fs, struct smb2_iovec *vec); - + struct smb2_query_directory_request { uint8_t file_information_class; uint8_t flags; @@ -560,7 +576,7 @@ struct smb2_read_reply { #define SMB2_FILE_FS_CONTROL_INFORMATION 6 #define SMB2_FILE_FS_FULL_SIZE_INFORMATION 7 #define SMB2_FILE_FS_SECTOR_SIZE_INFORMATION 11 - + /* additional info */ #define SMB2_OWNER_SECURITY_INFORMATION 0x00000001 #define SMB2_GROUP_SECURITY_INFORMATION 0x00000002 @@ -666,9 +682,11 @@ struct smb2_file_network_open_info { struct smb2_set_info_request { uint8_t info_type; uint8_t file_info_class; - void *input_data; + uint32_t buffer_length; + uint16_t buffer_offset; uint32_t additional_information; smb2_file_id file_id; + void *input_data; }; #define SMB2_SET_INFO_REPLY_SIZE 2 @@ -972,6 +990,55 @@ struct smb2_ioctl_validate_negotiate_info { uint16_t dialect; }; +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_SIZE 0x00000008 +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_CREATION 0x00000040 +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_EA 0x00000080 +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_SECURITY 0x00000100 +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 +#define SMB2_CHANGE_NOTIIFY_FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 + +#define SMB2_CHANGE_NOTIFY_WATCH_TREE 0x0001 + +#define SMB2_CHANGE_NOTIFY_REQUEST_SIZE 32 + +struct smb2_change_notify_request { + uint16_t flags; + uint32_t output_buffer_length; + smb2_file_id file_id; + uint32_t completion_filter; +}; + +#define SMB2_CHANGE_NOTIFY_REPLY_SIZE 9 + +struct smb2_change_notify_reply { + uint16_t output_buffer_offset; + uint32_t output_buffer_length; + uint8_t *output; +}; + +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 + +#define SMB2_OPLOCK_BREAK_REQUEST_SIZE 24 + +struct smb2_oplock_break_request { + uint8_t oplock_level; + smb2_file_id file_id; +}; + +#define SMB2_OPLOCK_BREAK_REPLY_SIZE 24 + +struct smb2_oplock_break_reply { + uint8_t oplock_level; + smb2_file_id file_id; +}; #define SMB2_WRITE_REQUEST_SIZE 49 @@ -1005,6 +1072,7 @@ struct smb2_lock_element { uint64_t offset; uint64_t length; uint32_t flags; + uint32_t reserved; }; #define SMB2_LOCK_REQUEST_SIZE 48 @@ -1014,7 +1082,7 @@ struct smb2_lock_request { uint8_t lock_sequence_number; uint32_t lock_sequence_index; smb2_file_id file_id; - uint8_t *locks; + struct smb2_lock_element *locks; }; #define SMB2_LOCK_REPLY_SIZE 4 diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index bdcb8fe9..1294ba99 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -25,6 +25,7 @@ if(ESP_PLATFORM) sha1.c sha224-256.c sha384-512.c + smb2-cmd-notify-change.c smb2-cmd-close.c smb2-cmd-create.c smb2-cmd-echo.c @@ -34,6 +35,7 @@ if(ESP_PLATFORM) smb2-cmd-lock.c smb2-cmd-logoff.c smb2-cmd-negotiate.c + smb2-cmd-notify.c smb2-cmd-query-directory.c smb2-cmd-query-info.c smb2-cmd-read.c @@ -62,7 +64,7 @@ if(ESP_PLATFORM) register_component() elseif(IOP AND BUILD_IRX) - set(SOURCES + set(SOURCES ps2/smb2_fio.c ps2/smb2man.c ps2/imports.c @@ -76,7 +78,7 @@ elseif(IOP AND BUILD_IRX) dcerpc-srvsvc.c errors.c hmac.c - hmac-md5.c + hmac-md5.c init.c ${KRB5_SOURCE} libsmb2.c @@ -84,9 +86,10 @@ elseif(IOP AND BUILD_IRX) md5.c ntlmssp.c pdu.c - sha1.c - sha224-256.c - sha384-512.c + sha1.c + sha224-256.c + sha384-512.c + smb2-cmd-notify-change.c smb2-cmd-close.c smb2-cmd-create.c smb2-cmd-echo.c @@ -108,15 +111,15 @@ elseif(IOP AND BUILD_IRX) smb2-data-filesystem-info.c smb2-data-security-descriptor.c smb2-data-reparse-point.c - smb2-share-enum.c - smb3-seal.c - smb2-signing.c + smb2-share-enum.c + smb3-seal.c + smb2-signing.c socket.c - spnego-wrapper.c + spnego-wrapper.c sync.c timestamps.c unicode.c - usha.c) + usha.c) BUILD_IOP_IMPORTS(${CMAKE_CURRENT_SOURCE_DIR}/ps2/imports.c ${CMAKE_CURRENT_SOURCE_DIR}/ps2/imports.lst) @@ -131,7 +134,7 @@ else() dcerpc-srvsvc.c errors.c hmac.c - hmac-md5.c + hmac-md5.c init.c ${KRB5_SOURCE} libsmb2.c @@ -139,9 +142,10 @@ else() md5.c ntlmssp.c pdu.c - sha1.c - sha224-256.c - sha384-512.c + sha1.c + sha224-256.c + sha384-512.c + smb2-cmd-notify-change.c smb2-cmd-close.c smb2-cmd-create.c smb2-cmd-echo.c @@ -163,15 +167,15 @@ else() smb2-data-filesystem-info.c smb2-data-security-descriptor.c smb2-data-reparse-point.c - smb2-share-enum.c - smb3-seal.c - smb2-signing.c + smb2-share-enum.c + smb3-seal.c + smb2-signing.c socket.c - spnego-wrapper.c + spnego-wrapper.c sync.c timestamps.c unicode.c - usha.c) + usha.c) endif() if(NOT ESP_PLATFORM) @@ -192,13 +196,13 @@ target_link_libraries(smb2_rpc PUBLIC ${core_DEPENDS} ${CORE_LIBRARIES}) target_include_directories(smb2_rpc PUBLIC ${INCLUDE_PATH}) set_target_properties(smb2_rpc PROPERTIES VERSION ${PROJECT_VERSION} - SOVERSION ${SOVERSION}) + SOVERSION ${SOVERSION}) elseif(IOP AND BUILD_IRX) add_executable(smb2man.irx ${SOURCES}) target_link_libraries(smb2man.irx - PRIVATE - gcc) + PRIVATE + gcc) add_custom_command(TARGET smb2man.irx POST_BUILD COMMAND md5sum ARGS smb2man.irx) elseif(PICO_BOARD) @@ -227,14 +231,14 @@ if(EE AND PS2RPC) install(TARGETS smb2_rpc EXPORT smb2_rpc RUNTIME DESTINATION bin ARCHIVE DESTINATION lib${LIB_SUFFIX} - LIBRARY DESTINATION lib${LIB_SUFFIX}) + LIBRARY DESTINATION lib${LIB_SUFFIX}) elseif(IOP AND BUILD_IRX) install(FILES ps2/ps2smb2.h DESTINATION ${PS2SDK}/common/include) install(TARGETS smb2man.irx EXPORT smb2man.irx RUNTIME DESTINATION ${PS2SDK}/ports_iop/irx ARCHIVE DESTINATION smb2man.irx - LIBRARY DESTINATION smb2man.irx) + LIBRARY DESTINATION smb2man.irx) else() install(TARGETS smb2 EXPORT smb2 RUNTIME DESTINATION bin diff --git a/lib/Makefile.am b/lib/Makefile.am index 3501f7a1..280a0585 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -47,6 +47,7 @@ libsmb2_la_SOURCES = \ smb2-cmd-lock.c \ smb2-cmd-logoff.c \ smb2-cmd-negotiate.c \ + smb2-cmd-notify-change.c \ smb2-cmd-query-directory.c \ smb2-cmd-query-info.c \ smb2-cmd-read.c \ diff --git a/lib/dcerpc-srvsvc.c b/lib/dcerpc-srvsvc.c index e5666ff1..46f057aa 100644 --- a/lib/dcerpc-srvsvc.c +++ b/lib/dcerpc-srvsvc.c @@ -69,6 +69,8 @@ #define SRVSVC_UUID 0x4b324fc8, 0x1670, 0x01d3, {0x12, 0x78, 0x5a, 0x47, 0xbf, 0x6e, 0xe1, 0x88} +int dcerpc_get_cr(struct dcerpc_pdu *pdu); + p_syntax_id_t srvsvc_interface = { {SRVSVC_UUID}, 3, 0 }; @@ -78,6 +80,74 @@ p_syntax_id_t srvsvc_interface = { * [MS-SRVS].pdf */ +/* + * typedef struct _SHARE_INFO_0 { + * [string] wchar_t * shi0_netname; + * } SHARE_INFO_0, *PSHARE_INFO_0, *LPSHARE_INFO_0; + */ +int +srvsvc_SHARE_INFO_0_coder(struct dcerpc_context *ctx, + struct dcerpc_pdu *pdu, + struct smb2_iovec *iov, int *offset, + void *ptr) +{ + struct srvsvc_SHARE_INFO_0 *nsi1 = ptr; + + if (dcerpc_ptr_coder(ctx, pdu, iov, offset, &nsi1->netname, + PTR_UNIQUE, dcerpc_utf16z_coder)) { + return -1; + } + return 0; +} + +/* + * [size_is(EntriesRead)] LPSHARE_INFO_0 Buffer; + */ +static int +srvsvc_SHARE_INFO_0_carray_coder(struct dcerpc_context *ctx, + struct dcerpc_pdu *pdu, + struct smb2_iovec *iov, int *offset, + void *ptr) +{ + return dcerpc_carray_coder(ctx, pdu, iov, offset, ptr, + sizeof(struct srvsvc_SHARE_INFO_0), + srvsvc_SHARE_INFO_0_coder); +} + +/* + * typedef struct _SHARE_INFO_0_CONTAINER { + * DWORD EntriesRead; + * [size_is(EntriesRead)] LPSHARE_INFO_0 Buffer; + * } SHARE_INFO_0_CONTAINER; +*/ +int +srvsvc_SHARE_INFO_0_CONTAINER_coder(struct dcerpc_context *dce, struct dcerpc_pdu *pdu, + struct smb2_iovec *iov, int *offset, + void *ptr) +{ + struct srvsvc_SHARE_INFO_0_CONTAINER *ctr = ptr; + + if (dcerpc_uint32_coder(dce, pdu, iov, offset, &ctr->EntriesRead)) { + return -1; + } + if (dcerpc_pdu_direction(pdu) == DCERPC_DECODE) { + if (ctr->Buffer == NULL) { + ctr->Buffer = smb2_alloc_data( + dcerpc_get_smb2_context(dce), + dcerpc_get_pdu_payload(pdu), + sizeof(struct srvsvc_SHARE_INFO_0_carray)); + } + } + if (dcerpc_ptr_coder(dce, pdu, iov, offset, + ctr->Buffer, + PTR_UNIQUE, + srvsvc_SHARE_INFO_0_carray_coder)) { + return -1; + } + + return 0; +} + /* * typedef struct _SHARE_INFO_1 { * [string] wchar_t *netname; @@ -111,33 +181,14 @@ srvsvc_SHARE_INFO_1_coder(struct dcerpc_context *ctx, * [size_is(EntriesRead)] LPSHARE_INFO_1 Buffer; */ static int -srvsvc_SHARE_INFO_1_array_coder(struct dcerpc_context *ctx, +srvsvc_SHARE_INFO_1_carray_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu, struct smb2_iovec *iov, int *offset, void *ptr) { - struct srvsvc_SHARE_INFO_1_carray *array = ptr; - int i; - uint64_t p; - - /* Conformance */ - p = array->max_count; - if (dcerpc_conformance_coder(ctx, pdu, iov, offset, &p)) { - return -1; - } - if (p != array->max_count) { - return -1; - } - - /* Data */ - for (i = 0; i < p; i++) { - if (srvsvc_SHARE_INFO_1_coder(ctx, pdu, iov, offset, - &array->share_info_1[i])) { - return -1; - } - } - - return 0; + return dcerpc_carray_coder(ctx, pdu, iov, offset, ptr, + sizeof(struct srvsvc_SHARE_INFO_1), + srvsvc_SHARE_INFO_1_coder); } /* @@ -146,34 +197,28 @@ srvsvc_SHARE_INFO_1_array_coder(struct dcerpc_context *ctx, * [size_is(EntriesRead)] LPSHARE_INFO_1 Buffer; * } SHARE_INFO_1_CONTAINER; */ -static int +int srvsvc_SHARE_INFO_1_CONTAINER_coder(struct dcerpc_context *dce, struct dcerpc_pdu *pdu, struct smb2_iovec *iov, int *offset, void *ptr) { - struct srvsvc_SHARE_INFO_1_CONTAINER *ctr1 = ptr; + struct srvsvc_SHARE_INFO_1_CONTAINER *ctr = ptr; - if (dcerpc_uint32_coder(dce, pdu, iov, offset, &ctr1->EntriesRead)) { + if (dcerpc_uint32_coder(dce, pdu, iov, offset, &ctr->EntriesRead)) { return -1; } if (dcerpc_pdu_direction(pdu) == DCERPC_DECODE) { - /* Need to allocate the buffer for the array */ - ctr1->Buffer = smb2_alloc_data(dcerpc_get_smb2_context(dce), - dcerpc_get_pdu_payload(pdu), - sizeof(struct srvsvc_SHARE_INFO_1_carray) + ctr1->EntriesRead * sizeof(struct srvsvc_SHARE_INFO_1)); - if (ctr1->Buffer == NULL) { - return -1; + if (ctr->Buffer == NULL) { + ctr->Buffer = smb2_alloc_data( + dcerpc_get_smb2_context(dce), + dcerpc_get_pdu_payload(pdu), + sizeof(struct srvsvc_SHARE_INFO_1_carray)); } - /* Need to set the max_count. When decoding we compare this - * with the maximum_count read from the pdu - */ - ctr1->Buffer->max_count = ctr1->EntriesRead; } - if (dcerpc_ptr_coder(dce, pdu, iov, offset, - ctr1->Buffer, + ctr->Buffer, PTR_UNIQUE, - srvsvc_SHARE_INFO_1_array_coder)) { + srvsvc_SHARE_INFO_1_carray_coder)) { return -1; } @@ -198,13 +243,20 @@ srvsvc_SHARE_ENUM_UNION_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu struct srvsvc_SHARE_ENUM_UNION *ctr = ptr; uint64_t p; - p = ctr->level; + p = ctr->Level; if (dcerpc_uint3264_coder(ctx, pdu, iov, offset, &p)) { return -1; } - ctr->level = (uint32_t)p; + ctr->Level = (uint32_t)p; - switch (ctr->level) { + switch (ctr->Level) { + case 0: + if (dcerpc_ptr_coder(ctx, pdu, iov, offset, &ctr->Level0, + PTR_UNIQUE, + srvsvc_SHARE_INFO_0_CONTAINER_coder)) { + return -1; + } + break; case 1: if (dcerpc_ptr_coder(ctx, pdu, iov, offset, &ctr->Level1, PTR_UNIQUE, @@ -233,7 +285,6 @@ srvsvc_SHARE_ENUM_STRUCT_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pd if (dcerpc_uint32_coder(ctx, pdu, iov, offset, &ses->Level)) { return -1; } - ses->ShareInfo.level = ses->Level; if (srvsvc_SHARE_ENUM_UNION_coder(ctx, pdu, iov, offset, &ses->ShareInfo)) { return -1; } diff --git a/lib/dcerpc.c b/lib/dcerpc.c index 2e304c12..fc409d60 100644 --- a/lib/dcerpc.c +++ b/lib/dcerpc.c @@ -529,6 +529,25 @@ dcerpc_add_deferred_pointer(struct dcerpc_context *ctx, pdu->max_ptr++; } +int +dcerpc_do_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu, + struct smb2_iovec *iov, + int *offset, void *ptr, + dcerpc_coder coder) +{ + pdu->max_alignment = 1; + pdu->is_conformance_run = 1; + if (coder(ctx, pdu, iov, offset, ptr)) { + return -1; + } + *offset = (*offset + (pdu->max_alignment - 1)) & ~(pdu->max_alignment - 1); + pdu->is_conformance_run = 0; + if (coder(ctx, pdu, iov, offset, ptr)) { + return -1; + } + return 0; +} + static int dcerpc_process_deferred_pointers(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu, @@ -541,14 +560,7 @@ dcerpc_process_deferred_pointers(struct dcerpc_context *ctx, while (pdu->cur_ptr != pdu->max_ptr) { idx = pdu->cur_ptr++; dp = &pdu->ptrs[idx]; - pdu->max_alignment = 1; - pdu->is_conformance_run = 1; - if (dp->coder(ctx, pdu, iov, offset, dp->ptr)) { - return -1; - } - *offset = (*offset + (pdu->max_alignment - 1)) & ~(pdu->max_alignment - 1); - pdu->is_conformance_run = 0; - if (dp->coder(ctx, pdu, iov, offset, dp->ptr)) { + if (dcerpc_do_coder(ctx, pdu, iov, offset, dp->ptr, dp->coder)) { return -1; } } @@ -718,14 +730,7 @@ dcerpc_encode_ptr(struct dcerpc_context *dce, struct dcerpc_pdu *pdu, case PTR_REF: if (pdu->top_level) { pdu->top_level = 0; - pdu->max_alignment = 1; - pdu->is_conformance_run = 1; - if (coder(dce, pdu, iov, offset, ptr)) { - return -1; - } - *offset = (*offset + (pdu->max_alignment - 1)) & ~(pdu->max_alignment - 1); - pdu->is_conformance_run = 0; - if (coder(dce, pdu, iov, offset, ptr)) { + if (dcerpc_do_coder(dce, pdu, iov, offset, ptr, coder)) { return -1; } pdu->top_level = top_level; @@ -754,14 +759,7 @@ dcerpc_encode_ptr(struct dcerpc_context *dce, struct dcerpc_pdu *pdu, } if (pdu->top_level) { pdu->top_level = 0; - pdu->max_alignment = 1; - pdu->is_conformance_run = 1; - if (coder(dce, pdu, iov, offset, ptr)) { - return -1; - } - *offset = (*offset + (pdu->max_alignment - 1)) & ~(pdu->max_alignment - 1); - pdu->is_conformance_run = 0; - if (coder(dce, pdu, iov, offset, ptr)) { + if (dcerpc_do_coder(dce, pdu, iov, offset, ptr, coder)) { return -1; } pdu->top_level = top_level; @@ -784,14 +782,7 @@ dcerpc_encode_ptr(struct dcerpc_context *dce, struct dcerpc_pdu *pdu, } if (pdu->top_level) { pdu->top_level = 0; - pdu->max_alignment = 1; - pdu->is_conformance_run = 1; - if (coder(dce, pdu, iov, offset, ptr)) { - return -1; - } - *offset = (*offset + (pdu->max_alignment - 1)) & ~(pdu->max_alignment - 1); - pdu->is_conformance_run = 0; - if (coder(dce, pdu, iov, offset, ptr)) { + if (dcerpc_do_coder(dce, pdu, iov, offset, ptr, coder)) { return -1; } pdu->top_level = top_level; @@ -843,14 +834,7 @@ dcerpc_decode_ptr(struct dcerpc_context *dce, struct dcerpc_pdu *pdu, case PTR_REF: if (pdu->top_level) { pdu->top_level = 0; - pdu->max_alignment = 1; - pdu->is_conformance_run = 1; - if (coder(dce, pdu, iov, offset, ptr)) { - return -1; - } - *offset = (*offset + (pdu->max_alignment - 1)) & ~(pdu->max_alignment - 1); - pdu->is_conformance_run = 0; - if (coder(dce, pdu, iov, offset, ptr)) { + if (dcerpc_do_coder(dce, pdu, iov, offset, ptr, coder)) { return -1; } pdu->top_level = top_level; @@ -872,14 +856,7 @@ dcerpc_decode_ptr(struct dcerpc_context *dce, struct dcerpc_pdu *pdu, if (pdu->top_level) { pdu->top_level = 0; - pdu->max_alignment = 1; - pdu->is_conformance_run = 1; - if (coder(dce, pdu, iov, offset, ptr)) { - return -1; - } - *offset = (*offset + (pdu->max_alignment - 1)) & ~(pdu->max_alignment - 1); - pdu->is_conformance_run = 0; - if (coder(dce, pdu, iov, offset, ptr)) { + if (dcerpc_do_coder(dce, pdu, iov, offset, ptr, coder)) { return -1; } pdu->top_level = top_level; @@ -903,6 +880,45 @@ dcerpc_decode_ptr(struct dcerpc_context *dce, struct dcerpc_pdu *pdu, return 0; } +int +dcerpc_carray_coder(struct dcerpc_context *ctx, + struct dcerpc_pdu *pdu, + struct smb2_iovec *iov, int *offset, + void *ptr, int elem_size, dcerpc_coder coder) +{ + struct dcerpc_carray *carray = ptr; + int i; + uint64_t p; + + /* Conformance */ + p = carray->max_count; + if (dcerpc_conformance_coder(ctx, pdu, iov, offset, &p)) { + return -1; + } + carray->max_count = p; + + if (dcerpc_pdu_direction(pdu) == DCERPC_DECODE) { + if (carray->max_count && carray->data == NULL) { + carray->data = smb2_alloc_data( + dcerpc_get_smb2_context(ctx), + dcerpc_get_pdu_payload(pdu), + carray->max_count * elem_size); + if (carray->data == NULL) { + return -1; + } + } + } + + /* Data */ + for (i = 0; i < p; i++) { + if (coder(ctx, pdu, iov, offset, &carray->data[i * elem_size])) { + return -1; + } + } + + return 0; +} + int dcerpc_ptr_coder(struct dcerpc_context *dce, struct dcerpc_pdu *pdu, struct smb2_iovec *iov, int *offset, void *ptr, @@ -929,6 +945,9 @@ dcerpc_encode_utf16(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu, /* Conformance part */ if (pdu->is_conformance_run) { + if (s->utf8 == NULL) { + s->utf8 = ""; + } s->utf16 = smb2_utf8_to_utf16(s->utf8); if (s->utf16 == NULL) { return -1; @@ -1958,3 +1977,9 @@ void dcerpc_set_endian(struct dcerpc_pdu *pdu, int little_endian) pdu->hdr.packed_drep[0] &= ~DCERPC_DR_LITTLE_ENDIAN; } } +int dcerpc_get_cr(struct dcerpc_pdu *pdu) +{ + return pdu->is_conformance_run; +} + + diff --git a/lib/init.c b/lib/init.c index 1f960f26..db3eb7db 100644 --- a/lib/init.c +++ b/lib/init.c @@ -333,6 +333,14 @@ void smb2_destroy_context(struct smb2_context *smb2) } smb2_free_pdu(smb2, pdu); } + if (smb2->pdu) { + struct smb2_pdu *pdu = smb2->pdu; + + if (pdu->cb) { + pdu->cb(smb2, SMB2_STATUS_CANCELLED, NULL, pdu->cb_data); + } + smb2_free_pdu(smb2, smb2->pdu); + } while (smb2->waitqueue) { struct smb2_pdu *pdu = smb2->waitqueue; @@ -340,13 +348,12 @@ void smb2_destroy_context(struct smb2_context *smb2) if (pdu->cb) { pdu->cb(smb2, SMB2_STATUS_CANCELLED, NULL, pdu->cb_data); } + if (pdu == smb2->pdu) { + smb2->pdu = NULL; + } smb2_free_pdu(smb2, pdu); } smb2_free_iovector(smb2, &smb2->in); - if (smb2->pdu) { - smb2_free_pdu(smb2, smb2->pdu); - smb2->pdu = NULL; - } if (smb2->fhs) { smb2_free_all_fhs(smb2); @@ -376,7 +383,6 @@ void smb2_destroy_context(struct smb2_context *smb2) } SMB2_LIST_REMOVE(&active_contexts, smb2); - free(smb2); } @@ -385,6 +391,19 @@ struct smb2_context *smb2_active_contexts(void) return active_contexts; } +int smb2_context_active(struct smb2_context *smb2) +{ + struct smb2_context *context = active_contexts; + + while (context) { + if (smb2 == context) { + return 1; + } + context = context->next; + } + return 0; +} + void smb2_free_iovector(struct smb2_context *smb2, struct smb2_io_vectors *v) { int i; @@ -666,3 +685,17 @@ void smb2_get_libsmb2Version(struct smb2_libversion *smb2_ver) smb2_ver->minor_version = LIBSMB2_MINOR_VERSION; smb2_ver->patch_version = LIBSMB2_MAJOR_VERSION; } + +void smb2_set_passthrough(struct smb2_context *smb2, + int passthrough) +{ + smb2->passthrough = passthrough; +} + +void smb2_get_passthrough(struct smb2_context *smb2, + int *passthrough) +{ + *passthrough = smb2->passthrough; +} + + diff --git a/lib/libsmb2.c b/lib/libsmb2.c index 62377274..29bfff35 100644 --- a/lib/libsmb2.c +++ b/lib/libsmb2.c @@ -183,7 +183,7 @@ struct smb2fh { int64_t end_of_file; }; -static void +void smb2_close_context(struct smb2_context *smb2) { if (smb2 == NULL) { @@ -200,7 +200,9 @@ smb2_close_context(struct smb2_context *smb2) smb2->message_id = 0; smb2->session_id = 0; - smb2->tree_id = 0; + smb2->tree_id_top = 0; + smb2->tree_id_cur = 0; + smb2->tree_id[0] = 0xdeadbeef; memset(smb2->signing_key, 0, SMB2_KEY_SIZE); if (smb2->session_key) { free(smb2->session_key); @@ -225,6 +227,7 @@ free_smb2dir(struct smb2_context *smb2, struct smb2dir *dir) free(dir->entries); dir->entries = e; } + free(dir->cb_data); free(dir); } @@ -536,19 +539,17 @@ free_c_data(struct smb2_context *smb2, struct connect_data *c_data) #endif } + if (smb2->connect_data == c_data) { + smb2->connect_data = NULL; /* to prevent double-free in smb2_destroy_context */ + } free(c_data->utf8_unc); free(c_data->utf16_unc); free(discard_const(c_data->server)); free(discard_const(c_data->share)); free(discard_const(c_data->user)); free(c_data); - - if (smb2->connect_data == c_data) { - smb2->connect_data = NULL; /* to prevent double-free in smb2_destroy_context */ - } } - static void tree_connect_cb(struct smb2_context *smb2, int status, void *command_data, void *private_data) @@ -770,7 +771,7 @@ session_setup_cb(struct smb2_context *smb2, int status, } smb2_create_signing_key(smb2); - + if (smb2->hdr.flags & SMB2_FLAGS_SIGNED) { uint8_t signature[16] _U_; @@ -797,14 +798,23 @@ session_setup_cb(struct smb2_context *smb2, int status, req.path_length = 2 * c_data->utf16_unc->len; req.path = c_data->utf16_unc->val; - pdu = smb2_cmd_tree_connect_async(smb2, &req, tree_connect_cb, c_data); - if (pdu == NULL) { - smb2_close_context(smb2); - c_data->cb(smb2, -ENOMEM, NULL, c_data->cb_data); + if (!smb2->passthrough) { + pdu = smb2_cmd_tree_connect_async(smb2, &req, tree_connect_cb, c_data); + if (pdu == NULL) { + smb2_close_context(smb2); + c_data->cb(smb2, -ENOMEM, NULL, c_data->cb_data); + free_c_data(smb2, c_data); + return; + } + smb2_queue_pdu(smb2, pdu); + } + else { + /* if user wants raw data she probably doesnt want us to + * do an implicit tree-connect, so just end here + */ + c_data->cb(smb2, 0, NULL, c_data->cb_data); free_c_data(smb2, c_data); - return; } - smb2_queue_pdu(smb2, pdu); } /* Returns 0 for success and -errno for failure */ @@ -866,10 +876,12 @@ negotiate_cb(struct smb2_context *smb2, int status, smb3_update_preauth_hash(smb2, smb2->in.niov - 1, &smb2->in.iov[1]); if (status != SMB2_STATUS_SUCCESS) { + //qqq smb2_close_context(smb2); smb2_set_nterror(smb2, status, "Negotiate failed with (0x%08x) %s. %s", status, nterror_to_str(status), smb2_get_error(smb2)); + // calls connect_cb c_data->cb(smb2, -nterror_to_errno(status), NULL, c_data->cb_data); free_c_data(smb2, c_data); @@ -1626,15 +1638,12 @@ create_cb_2(struct smb2_context *smb2, int status, void *command_data, void *private_data) { struct create_cb_data *create_data = private_data; - + if (status != SMB2_STATUS_SUCCESS) { - create_data->cb(smb2, -nterror_to_errno(status), - NULL, create_data->cb_data); - free(create_data); - return; + status = -nterror_to_errno(status); } - create_data->cb(smb2, 0, NULL, create_data->cb_data); + create_data->cb(smb2, status, NULL, create_data->cb_data); free(create_data); } @@ -1642,29 +1651,11 @@ static void create_cb_1(struct smb2_context *smb2, int status, void *command_data, void *private_data) { - struct create_cb_data *create_data = private_data; - struct smb2_create_reply *rep = command_data; - struct smb2_close_request req; - struct smb2_pdu *pdu; - if (status != SMB2_STATUS_SUCCESS) { - create_data->cb(smb2, -nterror_to_errno(status), - NULL, create_data->cb_data); - free(create_data); - return; - } - - memset(&req, 0, sizeof(struct smb2_close_request)); - req.flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; - memcpy(req.file_id, rep->file_id, SMB2_FD_SIZE); - - pdu = smb2_cmd_close_async(smb2, &req, create_cb_2, create_data); - if (pdu == NULL) { - create_data->cb(smb2, -ENOMEM, NULL, create_data->cb_data); - free(create_data); + smb2_set_error(smb2, "Create failed with status %d. %s", status, + smb2_get_error(smb2)); return; } - smb2_queue_pdu(smb2, pdu); } static int @@ -1673,8 +1664,9 @@ smb2_unlink_internal(struct smb2_context *smb2, const char *path, smb2_command_cb cb, void *cb_data) { struct create_cb_data *create_data; - struct smb2_create_request req; - struct smb2_pdu *pdu; + struct smb2_create_request cr_req; + struct smb2_close_request cl_req; + struct smb2_pdu *pdu, *next_pdu; if (smb2 == NULL) { return -EINVAL; @@ -1690,26 +1682,40 @@ smb2_unlink_internal(struct smb2_context *smb2, const char *path, create_data->cb_data = cb_data; - memset(&req, 0, sizeof(struct smb2_create_request)); - req.requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE; - req.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; - req.desired_access = SMB2_DELETE; + memset(&cr_req, 0, sizeof(struct smb2_create_request)); + cr_req.requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE; + cr_req.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + cr_req.desired_access = SMB2_DELETE; if (is_dir) { - req.file_attributes = SMB2_FILE_ATTRIBUTE_DIRECTORY; + cr_req.file_attributes = SMB2_FILE_ATTRIBUTE_DIRECTORY; } else { - req.file_attributes = SMB2_FILE_ATTRIBUTE_NORMAL; + cr_req.file_attributes = SMB2_FILE_ATTRIBUTE_NORMAL; } - req.share_access = SMB2_FILE_SHARE_READ | SMB2_FILE_SHARE_WRITE | + cr_req.share_access = SMB2_FILE_SHARE_READ | SMB2_FILE_SHARE_WRITE | SMB2_FILE_SHARE_DELETE; - req.create_disposition = SMB2_FILE_OPEN; - req.create_options = SMB2_FILE_DELETE_ON_CLOSE; - req.name = path; + cr_req.create_disposition = SMB2_FILE_OPEN; + cr_req.create_options = SMB2_FILE_DELETE_ON_CLOSE; + cr_req.name = path; - pdu = smb2_cmd_create_async(smb2, &req, create_cb_1, create_data); + pdu = smb2_cmd_create_async(smb2, &cr_req, create_cb_1, create_data); if (pdu == NULL) { smb2_set_error(smb2, "Failed to create create command"); return -ENOMEM; } + + memset(&cl_req, 0, sizeof(struct smb2_close_request)); + cl_req.flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; + memcpy(cl_req.file_id, compound_file_id, SMB2_FD_SIZE); + + next_pdu = smb2_cmd_close_async(smb2, &cl_req, create_cb_2, create_data); + if (next_pdu == NULL) { + smb2_set_error(smb2, "Failed to create close command"); + smb2_free_pdu(smb2, pdu); + free(create_data); + return -ENOMEM; + } + smb2_add_compound_pdu(smb2, pdu, next_pdu); + smb2_queue_pdu(smb2, pdu); return 0; @@ -1734,8 +1740,9 @@ smb2_mkdir_async(struct smb2_context *smb2, const char *path, smb2_command_cb cb, void *cb_data) { struct create_cb_data *create_data; - struct smb2_create_request req; - struct smb2_pdu *pdu; + struct smb2_create_request cr_req; + struct smb2_close_request cl_req; + struct smb2_pdu *pdu, *next_pdu; if (smb2 == NULL) { return -EINVAL; @@ -1750,21 +1757,35 @@ smb2_mkdir_async(struct smb2_context *smb2, const char *path, create_data->cb = cb; create_data->cb_data = cb_data; - memset(&req, 0, sizeof(struct smb2_create_request)); - req.requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE; - req.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; - req.desired_access = SMB2_FILE_READ_ATTRIBUTES; - req.file_attributes = SMB2_FILE_ATTRIBUTE_DIRECTORY; - req.share_access = SMB2_FILE_SHARE_READ | SMB2_FILE_SHARE_WRITE; - req.create_disposition = SMB2_FILE_CREATE; - req.create_options = SMB2_FILE_DIRECTORY_FILE; - req.name = path; + memset(&cr_req, 0, sizeof(struct smb2_create_request)); + cr_req.requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE; + cr_req.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + cr_req.desired_access = SMB2_FILE_READ_ATTRIBUTES; + cr_req.file_attributes = SMB2_FILE_ATTRIBUTE_DIRECTORY; + cr_req.share_access = SMB2_FILE_SHARE_READ | SMB2_FILE_SHARE_WRITE; + cr_req.create_disposition = SMB2_FILE_CREATE; + cr_req.create_options = SMB2_FILE_DIRECTORY_FILE; + cr_req.name = path; - pdu = smb2_cmd_create_async(smb2, &req, create_cb_1, create_data); + pdu = smb2_cmd_create_async(smb2, &cr_req, create_cb_1, create_data); if (pdu == NULL) { smb2_set_error(smb2, "Failed to create create command"); return -ENOMEM; } + + memset(&cl_req, 0, sizeof(struct smb2_close_request)); + cl_req.flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; + memcpy(cl_req.file_id, compound_file_id, SMB2_FD_SIZE); + + next_pdu = smb2_cmd_close_async(smb2, &cl_req, create_cb_2, create_data); + if (next_pdu == NULL) { + smb2_set_error(smb2, "Failed to create close command"); + smb2_free_pdu(smb2, pdu); + free(create_data); + return -ENOMEM; + } + smb2_add_compound_pdu(smb2, pdu, next_pdu); + smb2_queue_pdu(smb2, pdu); return 0; @@ -2688,7 +2709,7 @@ smb2_fd_event_callbacks(struct smb2_context *smb2, static void smb2_logoff_request_cb(struct smb2_server *server, struct smb2_context *smb2, void *command_data, void *cb_data) { - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; struct smb2_error_reply err; int ret = -EINVAL; @@ -2698,7 +2719,7 @@ smb2_logoff_request_cb(struct smb2_server *server, struct smb2_context *smb2, vo if (!ret) { pdu = smb2_cmd_logoff_reply_async(smb2, NULL, cb_data); } - else { + else if (ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_LOGOFF, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2714,16 +2735,17 @@ smb2_tree_connect_request_cb(struct smb2_server *server, struct smb2_context *sm struct smb2_tree_connect_request *req = command_data; struct smb2_tree_connect_reply rep; struct smb2_error_reply err; - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; int ret = -1; + memset(&rep, 0, sizeof(rep)); if (server->handlers && server->handlers->tree_connect_cmd) { - ret = server->handlers->tree_connect_cmd(server, smb2, req, &rep, &smb2->tree_id); + ret = server->handlers->tree_connect_cmd(server, smb2, req, &rep); } if (!ret) { - pdu = smb2_cmd_tree_connect_reply_async(smb2, &rep, NULL, cb_data); + pdu = smb2_cmd_tree_connect_reply_async(smb2, &rep, 0, NULL, cb_data); } - else { + else if (ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_TREE_CONNECT, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2736,17 +2758,17 @@ smb2_tree_connect_request_cb(struct smb2_server *server, struct smb2_context *sm static void smb2_tree_disconnect_request_cb(struct smb2_server *server, struct smb2_context *smb2, void *command_data, void *cb_data) { - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; struct smb2_error_reply err; int ret = -1; if (server->handlers && server->handlers->tree_disconnect_cmd) { - ret = server->handlers->tree_disconnect_cmd(server, smb2, smb2->tree_id); + ret = server->handlers->tree_disconnect_cmd(server, smb2, smb2_tree_id(smb2)); } if (!ret) { pdu = smb2_cmd_tree_disconnect_reply_async(smb2, NULL, cb_data); } - else { + else if (ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_TREE_DISCONNECT, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2762,16 +2784,17 @@ smb2_create_request_cb(struct smb2_server *server, struct smb2_context *smb2, vo struct smb2_create_request *req = command_data; struct smb2_create_reply rep; struct smb2_error_reply err; - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; int ret = -1; + memset(&rep, 0, sizeof(rep)); if (server->handlers && server->handlers->create_cmd) { ret = server->handlers->create_cmd(server, smb2, req, &rep); } if (!ret) { pdu = smb2_cmd_create_reply_async(smb2, &rep, NULL, cb_data); } - else { + else if (ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_CREATE, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2787,16 +2810,17 @@ smb2_close_request_cb(struct smb2_server *server, struct smb2_context *smb2, voi struct smb2_close_request *req = command_data; struct smb2_close_reply rep; struct smb2_error_reply err; - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; int ret = -1; + memset(&rep, 0, sizeof(rep)); if (server->handlers && server->handlers->close_cmd) { ret = server->handlers->close_cmd(server, smb2, req, &rep); } if (!ret) { pdu = smb2_cmd_close_reply_async(smb2, &rep, NULL, cb_data); } - else { + else if (ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_CLOSE, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2811,7 +2835,7 @@ smb2_flush_request_cb(struct smb2_server *server, struct smb2_context *smb2, voi { struct smb2_flush_request *req = command_data; struct smb2_error_reply err; - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; int ret = -1; if (server->handlers && server->handlers->flush_cmd) { @@ -2820,7 +2844,7 @@ smb2_flush_request_cb(struct smb2_server *server, struct smb2_context *smb2, voi if (!ret) { pdu = smb2_cmd_flush_reply_async(smb2, NULL, cb_data); } - else { + else if (ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_FLUSH, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2836,16 +2860,17 @@ smb2_read_request_cb(struct smb2_server *server, struct smb2_context *smb2, void struct smb2_read_request *req = command_data; struct smb2_read_reply rep; struct smb2_error_reply err; - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; int ret = -1; + memset(&rep, 0, sizeof(rep)); if (server->handlers && server->handlers->read_cmd) { ret = server->handlers->read_cmd(server, smb2, req, &rep); } if (!ret) { pdu = smb2_cmd_read_reply_async(smb2, &rep, NULL, cb_data); } - else { + else if (ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_READ, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2861,16 +2886,17 @@ smb2_write_request_cb(struct smb2_server *server, struct smb2_context *smb2, voi struct smb2_write_request *req = command_data; struct smb2_write_reply rep; struct smb2_error_reply err; - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; int ret = -1; + memset(&rep, 0, sizeof(rep)); if (server->handlers && server->handlers->write_cmd) { ret = server->handlers->write_cmd(server, smb2, req, &rep); } if (!ret) { pdu = smb2_cmd_write_reply_async(smb2, &rep, NULL, cb_data); } - else { + else if (ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_WRITE, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2885,7 +2911,7 @@ smb2_lock_request_cb(struct smb2_server *server, struct smb2_context *smb2, void { struct smb2_lock_request *req = command_data; struct smb2_error_reply err; - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; int ret = -1; if (server->handlers && server->handlers->lock_cmd) { @@ -2894,7 +2920,7 @@ smb2_lock_request_cb(struct smb2_server *server, struct smb2_context *smb2, void if (!ret) { pdu = smb2_cmd_lock_reply_async(smb2, NULL, cb_data); } - else { + else if(ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_LOCK, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2910,7 +2936,7 @@ smb2_ioctl_request_cb(struct smb2_server *server, struct smb2_context *smb2, voi struct smb2_ioctl_request *req = command_data; struct smb2_ioctl_reply rep; struct smb2_error_reply err; - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; struct smb2_ioctl_validate_negotiate_info out_info; int ret = -1; @@ -2936,7 +2962,7 @@ smb2_ioctl_request_cb(struct smb2_server *server, struct smb2_context *smb2, voi if (!ret) { pdu = smb2_cmd_ioctl_reply_async(smb2, &rep, NULL, cb_data); } - else { + else if (ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_IOCTL, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2957,7 +2983,7 @@ smb2_cancel_request_cb(struct smb2_server *server, struct smb2_context *smb2, vo if (server->handlers && server->handlers->cancel_cmd) { ret = server->handlers->cancel_cmd(server, smb2); } - if (ret) { + if (ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_CANCEL, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2971,7 +2997,7 @@ static void smb2_echo_request_cb(struct smb2_server *server, struct smb2_context *smb2, void *command_data, void *cb_data) { struct smb2_error_reply err; - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; int ret = -1; if (server->handlers && server->handlers->echo_cmd) { @@ -2980,7 +3006,7 @@ smb2_echo_request_cb(struct smb2_server *server, struct smb2_context *smb2, void if (!ret) { pdu = smb2_cmd_echo_reply_async(smb2, NULL, cb_data); } - else { + else if (ret < 0) { memset(&err, 0, sizeof(err)); pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_ECHO, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); @@ -2996,19 +3022,20 @@ smb2_query_directory_request_cb(struct smb2_server *server, struct smb2_context struct smb2_query_directory_request *req = command_data; struct smb2_query_directory_reply rep; struct smb2_error_reply err; - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; int ret = -1; + memset(&rep, 0, sizeof(rep)); memset(&err, 0, sizeof(err)); if (server->handlers && server->handlers->query_directory_cmd) { ret = server->handlers->query_directory_cmd(server, smb2, req, &rep); } - if (ret) { + if (ret < 0) { pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_QUERY_DIRECTORY, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); } - else { + else if (!ret) { if (rep.output_buffer_length == 0) { pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_QUERY_DIRECTORY, SMB2_STATUS_NO_MORE_FILES, NULL, cb_data); @@ -3018,8 +3045,7 @@ smb2_query_directory_request_cb(struct smb2_server *server, struct smb2_context &err, SMB2_QUERY_DIRECTORY, SMB2_STATUS_NOT_SUPPORTED, NULL, cb_data); } else { - pdu = smb2_cmd_query_directory_reply_async(smb2, req->file_information_class, - req->flags, req->output_buffer_length, &rep, NULL, cb_data); + pdu = smb2_cmd_query_directory_reply_async(smb2, req, &rep, NULL, cb_data); } } if (pdu != NULL) { @@ -3027,25 +3053,53 @@ smb2_query_directory_request_cb(struct smb2_server *server, struct smb2_context } } +static void +smb2_change_notify_request_cb(struct smb2_server *server, struct smb2_context *smb2, void *command_data, void *cb_data) +{ + struct smb2_change_notify_request *req = command_data; + struct smb2_change_notify_reply rep; + struct smb2_error_reply err; + struct smb2_pdu *pdu = NULL; + int ret = -1; + + memset(&rep, 0, sizeof(rep)); + memset(&err, 0, sizeof(err)); + + if (server->handlers && server->handlers->change_notify_cmd) { + ret = server->handlers->change_notify_cmd(server, smb2, req); + } + if (ret < 0) { + pdu = smb2_cmd_error_reply_async(smb2, + &err, SMB2_CHANGE_NOTIFY, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); + } + else if (!ret) { + pdu = smb2_cmd_change_notify_reply_async(smb2, &rep, NULL, cb_data); + } + if (pdu != NULL) { + smb2_queue_pdu(smb2, pdu); + } +} + static void smb2_query_info_request_cb(struct smb2_server *server, struct smb2_context *smb2, void *command_data, void *cb_data) { struct smb2_query_info_request *req = command_data; struct smb2_query_info_reply rep; struct smb2_error_reply err; - struct smb2_pdu *pdu; + struct smb2_pdu *pdu = NULL; int ret = -1; + memset(&rep, 0, sizeof(rep)); memset(&err, 0, sizeof(err)); if (server->handlers && server->handlers->query_info_cmd) { ret = server->handlers->query_info_cmd(server, smb2, req, &rep); } - if (ret) { + if (ret < 0) { pdu = smb2_cmd_error_reply_async(smb2, - &err, SMB2_IOCTL, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); + &err, SMB2_QUERY_INFO, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); } - else { + else if (!ret) { if (rep.output_buffer_length == 0) { pdu = smb2_cmd_error_reply_async(smb2, &err, SMB2_QUERY_INFO, SMB2_STATUS_NOT_SUPPORTED, NULL, cb_data); @@ -3055,8 +3109,7 @@ smb2_query_info_request_cb(struct smb2_server *server, struct smb2_context *smb2 &err, SMB2_QUERY_INFO, SMB2_STATUS_INVALID_INFO_CLASS, NULL, cb_data); } else { - pdu = smb2_cmd_query_info_reply_async(smb2, req->info_type, req->file_info_class, - req->output_buffer_length, &rep, NULL, cb_data); + pdu = smb2_cmd_query_info_reply_async(smb2, req, &rep, NULL, cb_data); } } if (pdu != NULL) { @@ -3064,6 +3117,34 @@ smb2_query_info_request_cb(struct smb2_server *server, struct smb2_context *smb2 } } +static void +smb2_set_info_request_cb(struct smb2_server *server, struct smb2_context *smb2, void *command_data, void *cb_data) +{ + struct smb2_set_info_request *req = command_data; + struct smb2_error_reply err; + struct smb2_pdu *pdu = NULL; + int ret = -1; + + memset(&err, 0, sizeof(err)); + + if (server->handlers && server->handlers->set_info_cmd) { + ret = server->handlers->set_info_cmd(server, smb2, req); + } + if (ret < 0) { + pdu = smb2_cmd_error_reply_async(smb2, + &err, SMB2_SET_INFO, SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); + } + else if (!ret) { + pdu = smb2_cmd_set_info_reply_async(smb2, req, NULL, cb_data); + } + if (pdu != NULL) { + smb2_queue_pdu(smb2, pdu); + } +} + +static void +smb2_session_setup_request_cb(struct smb2_context *smb2, int status, void *command_data, void *cb_data); + static void smb2_general_client_request_cb(struct smb2_context *smb2, int status, void *command_data, void *cb_data) { @@ -3075,10 +3156,21 @@ smb2_general_client_request_cb(struct smb2_context *smb2, int status, void *comm smb2_close_context(smb2); return; } + if (status == SMB2_STATUS_CANCELLED) { + return; + } + switch (smb2->pdu->header.command) { case SMB2_LOGOFF: smb2_logoff_request_cb(server, smb2, command_data, cb_data); - break; + /* alloc a pdu for next session setup request */ + smb2->next_pdu = smb2_allocate_pdu(smb2, SMB2_TREE_CONNECT, smb2_session_setup_request_cb, cb_data); + if (!smb2->next_pdu) { + smb2_set_error(smb2, "can not alloc pdu for authorization session setup request"); + smb2_close_context(smb2); + } + /* note special case */ + return; case SMB2_TREE_CONNECT: smb2_tree_connect_request_cb(server, smb2, command_data, cb_data); break; @@ -3115,11 +3207,18 @@ smb2_general_client_request_cb(struct smb2_context *smb2, int status, void *comm case SMB2_QUERY_DIRECTORY: smb2_query_directory_request_cb(server, smb2, command_data, cb_data); break; + case SMB2_CHANGE_NOTIFY: + smb2_change_notify_request_cb(server, smb2, command_data, cb_data); + break; case SMB2_QUERY_INFO: smb2_query_info_request_cb(server, smb2, command_data, cb_data); break; + case SMB2_SET_INFO: + smb2_set_info_request_cb(server, smb2, command_data, cb_data); + break; default: - smb2_set_error(smb2, "Client request not implemented %s", smb2_get_error(smb2)); + smb2_set_error(smb2, "Client request %d not implemented %s", + smb2->pdu->header.command, smb2_get_error(smb2)); break; } @@ -3150,6 +3249,10 @@ smb2_session_setup_request_cb(struct smb2_context *smb2, int status, void *comma int have_valid_session_key = 1; int ret; + if (status) { + return; + } + rep.security_buffer_length = 0; rep.security_buffer_offset = 0; @@ -3158,6 +3261,8 @@ smb2_session_setup_request_cb(struct smb2_context *smb2, int status, void *comma smb3_update_preauth_hash(smb2, smb2->in.niov - 1, &smb2->in.iov[1]); memset(&err, 0, sizeof(err)); + pdu = NULL; + if (smb2->sec == SMB2_SEC_NTLMSSP) { if (ntlmssp_get_message_type(smb2, req->security_buffer, req->security_buffer_length, @@ -3184,7 +3289,7 @@ smb2_session_setup_request_cb(struct smb2_context *smb2, int status, void *comma smb2->next_pdu = smb2_allocate_pdu(smb2, SMB2_SESSION_SETUP, smb2_session_setup_request_cb, cb_data); more_processing_needed = 1; - smb2->session_id = 0xdeadbeef; + smb2->session_id = server->session_counter++; } else if (message_type == AUTHENTICATION_MESSAGE) { /* alloc a pdu for next request (not really required to get tree connect) */ @@ -3206,8 +3311,18 @@ smb2_session_setup_request_cb(struct smb2_context *smb2, int status, void *comma if (message_type == AUTHENTICATION_MESSAGE) { if (!ntlmssp_get_authenticated(c_data->auth_data)) { smb2_set_error(smb2, "Authentication failed: %s", smb2_get_error(smb2)); + #if 0 smb2_close_context(smb2); return; + #else + pdu = smb2_cmd_error_reply_async(smb2, + &err, SMB2_SESSION_SETUP, + SMB2_STATUS_LOGON_FAILURE, NULL, cb_data); + smb2_free_pdu(smb2, smb2->next_pdu); + smb2->next_pdu = smb2_allocate_pdu(smb2, SMB2_SESSION_SETUP, + smb2_session_setup_request_cb, cb_data); + more_processing_needed = 0; + #endif } if (ntlmssp_get_session_key(c_data->auth_data, &smb2->session_key, @@ -3236,28 +3351,36 @@ smb2_session_setup_request_cb(struct smb2_context *smb2, int status, void *comma */ smb2_create_signing_key(smb2); } - pdu = smb2_cmd_session_setup_reply_async(smb2, &rep, NULL, cb_data); - if (pdu == NULL) { - return; - } - - if (more_processing_needed) { - pdu->header.status = SMB2_STATUS_MORE_PROCESSING_REQUIRED; + if (server->allow_anonymous && + ((smb2->user == NULL || smb2->user[0] == '\0')|| + (smb2->password == NULL || smb2->password[0] == '\0'))) { + rep.session_flags |= SMB2_SESSION_FLAG_IS_GUEST; } - else { - if (server->handlers && server->handlers->session_established) { - ret = server->handlers->session_established(server, smb2); - if (ret) { - smb2_set_error(smb2, "server session start handler failed"); - smb2_close_context(smb2); - return; - } + + if (!pdu) { + pdu = smb2_cmd_session_setup_reply_async(smb2, &rep, NULL, cb_data); + if (pdu == NULL) { + return; + } + + if (more_processing_needed) { + pdu->header.status = SMB2_STATUS_MORE_PROCESSING_REQUIRED; } else { - pdu = smb2_cmd_error_reply_async(smb2, - &err, SMB2_SESSION_SETUP, - SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); + if (server->handlers && server->handlers->session_established) { + ret = server->handlers->session_established(server, smb2); + if (ret) { + smb2_set_error(smb2, "server session start handler failed"); + smb2_close_context(smb2); + return; + } + } + else { + pdu = smb2_cmd_error_reply_async(smb2, + &err, SMB2_SESSION_SETUP, + SMB2_STATUS_NOT_IMPLEMENTED, NULL, cb_data); + } } } if (!smb2->next_pdu) { @@ -3276,6 +3399,7 @@ smb2_negotiate_request_cb(struct smb2_context *smb2, int status, void *command_d struct smb2_server *server = c_data->server_context; struct smb2_negotiate_request *req = command_data; struct smb2_negotiate_reply rep; + struct smb2_error_reply err; struct smb2_pdu *pdu; uint16_t dialects[SMB2_NEGOTIATE_MAX_DIALECTS]; int dialect_count; @@ -3284,6 +3408,8 @@ smb2_negotiate_request_cb(struct smb2_context *smb2, int status, void *command_d /*void *auth_data;*/ memset(&rep, 0, sizeof(rep)); + memset(&err, 0, sizeof(err)); + smb2_set_error(smb2, ""); /* negotiate highest version in request dialects */ switch (smb2->version) { @@ -3318,8 +3444,23 @@ smb2_negotiate_request_cb(struct smb2_context *smb2, int status, void *command_d } if (req && smb2->pdu->header.command != SMB1_NEGOTIATE) { - int d; - smb2->dialect = 0; + if (req->dialect_count == 0) { + /* windows does this crap */ + /* alloc a pdu for another negotiate request */ + smb2->next_pdu = smb2_allocate_pdu(smb2, SMB2_NEGOTIATE, smb2_negotiate_request_cb, cb_data); + if (!smb2->next_pdu) { + smb2_set_error(smb2, "can not alloc pdu for second negotiate request"); + smb2_close_context(smb2); + } + pdu = smb2_cmd_error_reply_async(smb2, + &err, SMB2_NEGOTIATE, SMB2_STATUS_INVALID_PARAMETER, NULL, cb_data); + if (pdu == NULL) { + return; + } + smb2_queue_pdu(smb2, pdu); + return; + } + smb2->dialect = 0; for (dialect_index = req->dialect_count - 1; dialect_index >= 0; dialect_index--) { for (d = dialect_count - 1; d >= 0; d--) { @@ -3388,26 +3529,30 @@ smb2_negotiate_request_cb(struct smb2_context *smb2, int status, void *command_d smb2->sign = 1; } - if (req->security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED && - smb2->dialect == SMB2_VERSION_0210) { - /* smb2.1 requires signing if enabled on both sides - * regardless of what the flags say */ - smb2->sign = 1; - } - if (req->security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED && - smb2->dialect == SMB2_VERSION_0311) { - /* smb3.1.1 requires signing if enabled on both sides - * regardless of what the flags say */ - smb2->sign = 1; + if (!server->allow_anonymous || + (smb2->password && smb2->password[0])) { + if (server->signing_enabled) { + if (req->security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED && + smb2->dialect == SMB2_VERSION_0210) { + /* smb2.1 requires signing if enabled on both sides + * regardless of what the flags say */ + smb2->sign = 1; + } + if (req->security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED && + smb2->dialect >= SMB2_VERSION_0311) { + /* smb3.1.1 requires signing if enabled on both sides + * regardless of what the flags say */ + smb2->sign = 1; + } + } } - if (smb2->seal) { smb2->sign = 0; } } - rep.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED | - (smb2->sign ? SMB2_NEGOTIATE_SIGNING_REQUIRED : 0); + rep.security_mode = (server->signing_enabled ? SMB2_NEGOTIATE_SIGNING_ENABLED : 0)| + (smb2->sign ? SMB2_NEGOTIATE_SIGNING_REQUIRED : 0); memcpy(rep.server_guid, server->guid, 16); /* TODO */ rep.max_transact_size = smb2->max_transact_size;; rep.max_read_size = smb2->max_read_size; @@ -3527,12 +3672,13 @@ int smb2_serve_port(struct smb2_server *server, const int max_connections, smb2_ strncpy(server->domain, default_domain, MIN(sizeof(server->domain),strlen(default_domain) + 1)); } - err = smb2_bind_and_listen(server->port, max_connections, &server->fd); if (err != 0) { return err; } + server->session_counter = 0x1234; + do { /* select on the file descriptors of all active client connections and our server socket for the first readable event @@ -3635,10 +3781,16 @@ int smb2_serve_port(struct smb2_server *server, const int max_connections, smb2_ /* cull connection-less clients here, one per iteration (since active list changes on destroy)*/ for (smb2 = smb2_active_contexts(); smb2; smb2 = smb2->next) { - if (!SMB2_VALID_SOCKET(smb2_get_fd(smb2))) { - smb2_destroy_context(smb2); - break; + if (smb2_is_server(smb2)) { + if (!SMB2_VALID_SOCKET(smb2_get_fd(smb2))) { + if (server->handlers && server->handlers->destruction_event) { + server->handlers->destruction_event(server, smb2); + } + smb2_destroy_context(smb2); + break; + } } + /* client connections are destroyed when they timeout or get disconnected */ } } } diff --git a/lib/libsmb2.syms b/lib/libsmb2.syms index 624ba2db..01e43b9d 100644 --- a/lib/libsmb2.syms +++ b/lib/libsmb2.syms @@ -5,11 +5,14 @@ dcerpc_connect_context_async dcerpc_context_handle_coder dcerpc_create_context dcerpc_destroy_context +dcerpc_do_coder dcerpc_free_data dcerpc_free_pdu dcerpc_get_error dcerpc_set_endian dcerpc_set_tctx +dcerpc_get_cr +dcerpc_ptr_coder dcerpc_utf16_coder dcerpc_utf16z_coder nterror_to_str @@ -18,6 +21,7 @@ smb2_add_compound_pdu smb2_close smb2_close_async smb2_closedir +smb2_close_context smb2_cmd_close_async smb2_cmd_create_async smb2_cmd_echo_async @@ -32,11 +36,14 @@ smb2_cmd_tree_disconnect_async smb2_connect_async smb2_connect_share smb2_connect_share_async +smb2_connect_tree_id +smb2_context_active smb2_decode_fileidfulldirectoryinformation smb2_destroy_context smb2_destroy_url smb2_disconnect_share smb2_disconnect_share_async +smb2_disconnect_tree_id smb2_fd_event_callbacks smb2_fh_from_file_id smb2_free_data @@ -53,9 +60,11 @@ smb2_get_error smb2_get_fd smb2_get_fds smb2_get_file_id +smb2_get_tree_id_for_pdu smb2_get_max_read_size smb2_get_max_write_size smb2_get_opaque +smb2_get_passthrough smb2_init_context smb2_mkdir smb2_mkdir_async @@ -65,6 +74,7 @@ smb2_open_async smb2_opendir smb2_opendir_async smb2_parse_url +smb2_pdu_is_compound smb2_pread smb2_pread_async smb2_pwrite @@ -81,6 +91,7 @@ smb2_rmdir smb2_rmdir_async smb2_lseek smb2_seekdir +smb2_select_tree_id smb2_serve_port smb2_service smb2_service_fd @@ -88,10 +99,12 @@ smb2_set_authentication smb2_set_security_mode smb2_set_version smb2_set_user +smb2_set_passthrough smb2_set_password smb2_set_password_from_file smb2_set_domain smb2_set_error +smb2_set_tree_id_for_pdu smb2_set_workstation smb2_set_opaque smb2_set_seal @@ -122,6 +135,8 @@ srvsvc_NetrShareEnum_rep_coder srvsvc_NetrShareEnum_req_coder srvsvc_NetrShareGetInfo_rep_coder srvsvc_NetrShareGetInfo_req_coder +srvsvc_SHARE_INFO_1_coder +srvsvc_SHARE_INFO_1_CONTAINER_coder NT_SID_AUTHORITY lsa_interface lsa_Close_rep_coder diff --git a/lib/ntlmssp.c b/lib/ntlmssp.c index 83624e01..d4e4fafd 100644 --- a/lib/ntlmssp.c +++ b/lib/ntlmssp.c @@ -169,13 +169,13 @@ ntlmssp_init_context(const char *user, } if (user) { - auth_data->user = strdup(user); + auth_data->user = strdup(user); } if (password) { - auth_data->password = strdup(password); + auth_data->password = strdup(password); } if (domain) { - auth_data->domain = strdup(domain); + auth_data->domain = strdup(domain); } if (workstation) { auth_data->workstation = strdup(workstation); @@ -725,6 +725,7 @@ encode_ntlm_challenge(struct smb2_context *smb2, struct auth_data *auth_data) for (i = 0; i < namelen; i++) { upper[i] = toupper(auth_data->workstation[i]); } + upper[namelen] = 0; utf16_workstation = smb2_utf8_to_utf16(auth_data->workstation); if (utf16_workstation == NULL) { goto finished; @@ -865,7 +866,7 @@ ntlmssp_generate_blob(struct smb2_server *server, struct smb2_context *smb2, tim } } else if (cmd == AUTHENTICATION_MESSAGE) { - auth_data->is_authenticated = !ntlmssp_authenticate_blob(server, + auth_data->is_authenticated = !ntlmssp_authenticate_blob(server, smb2, auth_data, ntlmssp, ntlmssp_len); if (auth_data->spnego_wrap) { @@ -973,15 +974,19 @@ ntlmssp_authenticate_blob(struct smb2_server *server, struct smb2_context *smb2, auth_data->user); return -1; } - if (!smb2->password) { + if (!smb2->password && !server->allow_anonymous) { smb2_set_error(smb2, "server has no passwd for %s", auth_data->user); return -1; } } - /* if no user/pw, an anonymous allowed, do anonymous */ - if (!auth_data->user || (auth_data->user[0] == '\0')) { - return 0; + /* if no user/pw, and anonymous allowed, do anonymous */ + if (!auth_data->user || (auth_data->user[0] == '\0') || + !smb2->password || (smb2->password[0] == '\0')) { + if (server->allow_anonymous) { + return 0; + } + return -1; } /* negotiate_flags = le32toh(u32); */ diff --git a/lib/pdu.c b/lib/pdu.c index 25c3b06b..c67d50c4 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -78,6 +78,8 @@ smb2_pad_to_64bit(struct smb2_context *smb2, struct smb2_io_vectors *v) return 0; } +#include + struct smb2_pdu * smb2_allocate_pdu(struct smb2_context *smb2, enum smb2_command command, smb2_command_cb cb, void *cb_data) @@ -129,9 +131,15 @@ smb2_allocate_pdu(struct smb2_context *smb2, enum smb2_command command, case SMB2_LOGOFF: case SMB2_ECHO: /* case SMB2_CANCEL: */ + hdr->sync.tree_id = 0; + break; + case SMB2_TREE_CONNECT: + /* [MS-SMB2] 2.2.1.2 */ + hdr->sync.tree_id = 0; break; default: - hdr->sync.tree_id = smb2->tree_id; + hdr->sync.tree_id = smb2_tree_id(smb2); + break; } switch (command) { @@ -164,12 +172,169 @@ smb2_allocate_pdu(struct smb2_context *smb2, enum smb2_command command, return pdu; } +int +smb2_select_tree_id(struct smb2_context *smb2, uint32_t tree_id) +{ + int i; + + for ( + i = 1; + i <= smb2->tree_id_top && i <= SMB2_MAX_TREE_NESTING; + i++ + ) { + if (smb2->tree_id[i] == tree_id) { + break; + } + } + if (smb2->tree_id_top < (SMB2_MAX_TREE_NESTING - 1)) { + smb2->tree_id_cur = i; + } + else { + smb2_set_error(smb2, "No connected tree-id %08X to select", tree_id); + return -1; + } + return 0; +} + +int +smb2_get_tree_id_for_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu, uint32_t *tree_id) +{ + if (pdu) { + switch (pdu->header.command) { + case SMB2_NEGOTIATE: + case SMB2_SESSION_SETUP: + case SMB2_LOGOFF: + case SMB2_ECHO: + /* case SMB2_CANCEL: */ + case SMB2_TREE_CONNECT: + *tree_id = 0; + return 0; + default: + break; + /* + *tree_id = pdu->header.sync.tree_id; + printf("%p PDU tid for %d is %08X\n", smb2, pdu->header.command, *tree_id); + return 0; + */ + } + } + if (smb2->tree_id_top > 0) { + *tree_id = smb2->tree_id[smb2->tree_id_cur]; + } + else { + smb2_set_error(smb2, "No tree-id connected"); + *tree_id = 0xdeadbeef; + return -1; + } + return 0; +} + +int +smb2_set_tree_id_for_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu, uint32_t tree_id) +{ + if (pdu) { + if (pdu->header.flags & SMB2_FLAGS_ASYNC_COMMAND) { + smb2_set_error(smb2, "no tree id for asyn pdu"); + return 0; + } + switch (pdu->header.command) { + case SMB2_NEGOTIATE: + case SMB2_SESSION_SETUP: + case SMB2_LOGOFF: + case SMB2_ECHO: + /* case SMB2_CANCEL: */ + break; + case SMB2_TREE_CONNECT: + break; + default: + pdu->header.sync.tree_id = tree_id; + } + return 0; + } + return -1; +} + +int +smb2_connect_tree_id(struct smb2_context *smb2, uint32_t tree_id) +{ + if (smb2->tree_id_top < (SMB2_MAX_TREE_NESTING - 1)) { + smb2->tree_id[++smb2->tree_id_top] = tree_id; + smb2->tree_id_cur = smb2->tree_id_top; + } + else { + smb2_set_error(smb2, "Tree nesting too deep"); + return -1; + } + return 0; +} + +int +smb2_disconnect_tree_id(struct smb2_context *smb2, uint32_t tree_id) +{ + int i, j, k; + + if (smb2->tree_id_top > 0) { + for ( + i = 1; + i <= smb2->tree_id_top && i <= SMB2_MAX_TREE_NESTING; + i++ + ) { + if (smb2->tree_id[i] == tree_id) { + break; + } + } + if (i <= smb2->tree_id_top) { + if (i == smb2->tree_id_top) { + smb2->tree_id_top--; + smb2->tree_id_cur = smb2->tree_id_top; + return 0; + } + else { + for (j = smb2->tree_id_top - 1; j > 0; j--) { + if (smb2->tree_id[j] == tree_id) { + break; + } + } + if (j > 0) { + if (j == smb2->tree_id_cur) { + /* note updating cur is a convenience for clients + * but isnt required and maybe get rid of this */ + if (j > 1) { + smb2->tree_id_cur = + j - 1; + } + else { + smb2->tree_id_cur = + smb2->tree_id_top - 1;; + } + } + for (k = j; k < smb2->tree_id_top; k++) { + smb2->tree_id[k] = smb2->tree_id[k + 1]; + } + smb2->tree_id_top--; + return 0; + } + } + } + } + + smb2_set_error(smb2, "No tree-id %08X to remove", tree_id); + return -1; +} +int +smb2_pdu_is_compound(struct smb2_context *smb2) +{ + return (smb2) ? + (smb2->hdr.next_command != 0) : 0; +} + void smb2_add_compound_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu, struct smb2_pdu *next_pdu) { int i, offset; + /* find the last pdu in the chain */ while (pdu->next_compound) { pdu = pdu->next_compound; @@ -298,9 +463,11 @@ static void smb2_encode_header(struct smb2_context *smb2, struct smb2_iovec *iov, struct smb2_header *hdr) { - hdr->message_id = smb2->message_id++; - if (hdr->credit_charge > 1) { - smb2->message_id += (hdr->credit_charge - 1); + if (!smb2_is_server(smb2)) { + hdr->message_id = smb2->message_id++; + if (hdr->credit_charge > 1) { + smb2->message_id += (hdr->credit_charge - 1); + } } memcpy(iov->buf, hdr->protocol_id, 4); @@ -316,6 +483,11 @@ smb2_encode_header(struct smb2_context *smb2, struct smb2_iovec *iov, if (hdr->flags & SMB2_FLAGS_ASYNC_COMMAND) { smb2_set_uint64(iov, 32, hdr->async.async_id); } else { + /* + printf(">>>>>>>>>> %p %s %d treeid=%08x %08X\n", smb2, + (hdr->flags & SMB2_FLAGS_SERVER_TO_REDIR) ? "rep" : "cmd", + hdr->command, hdr->sync.tree_id, smb2_tree_id(smb2)); + */ smb2_set_uint32(iov, 32, hdr->sync.process_id); smb2_set_uint32(iov, 36, hdr->sync.tree_id); } @@ -336,8 +508,8 @@ smb2_decode_header(struct smb2_context *smb2, struct smb2_iovec *iov, return -1; } if (!memcmp(iov->buf, smb1sign, 4)) { - /* and SMBv1 request. if it is a negotiate request - * fake an smb2 negotiate request */ + /* an SMBv1 request. if it is a negotiate request + * allow it through, else ingore */ if (iov->buf[4] == SMB1_NEGOTIATE) { /*printf("Handling SMBv1 Negotiate\n");*/ memset(hdr, 0, sizeof *hdr); @@ -366,8 +538,35 @@ smb2_decode_header(struct smb2_context *smb2, struct smb2_iovec *iov, } else { smb2_get_uint32(iov, 32, &hdr->sync.process_id); smb2_get_uint32(iov, 36, &hdr->sync.tree_id); - } + /* + printf("<<<<<<<<<<< %p %s %d treeid=%08x %08X\n", smb2, + (hdr->flags & SMB2_FLAGS_SERVER_TO_REDIR) ? "rep" : "cmd", + hdr->command, hdr->sync.tree_id, smb2_tree_id(smb2)); + */ + /* for requests, set the context tree id to the header value + */ + if (!(hdr->flags & SMB2_FLAGS_SERVER_TO_REDIR)) { + switch (hdr->command) { + case SMB2_NEGOTIATE: + case SMB2_SESSION_SETUP: + case SMB2_LOGOFF: + case SMB2_ECHO: + /* case SMB2_CANCEL: */ + break; + case SMB2_TREE_CONNECT: + break; + default: + /// TODO - care about not having this already connected + smb2_select_tree_id(smb2, hdr->sync.tree_id); + break; + } + if (smb2_is_server(smb2)) { + /* remember message id to format reply */ + smb2->message_id = hdr->message_id; + } + } + } smb2_get_uint64(iov, 40, &hdr->session_id); memcpy(&hdr->signature, iov->buf + 48, 16); @@ -381,6 +580,55 @@ smb2_add_to_outqueue(struct smb2_context *smb2, struct smb2_pdu *pdu) smb2_change_events(smb2, smb2->fd, smb2_which_events(smb2)); } +struct smb2_pdu * +smb2_find_pdu_by_command(struct smb2_context *smb2, + uint32_t command) { + struct smb2_pdu *pdu; + + for (pdu = smb2->waitqueue; pdu; pdu = pdu->next) { + if (pdu->header.command == command) { + break; + } + } + return pdu; +} + +static int +smb2_correlate_reply(struct smb2_context *smb2, struct smb2_pdu *pdu) +{ + struct smb2_pdu *req_pdu; + int ret = 0; + + /* set reply flag */ + pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; + + req_pdu = smb2_find_pdu_by_command(smb2, pdu->header.command); + if (req_pdu == NULL) { + smb2_set_error(smb2, "no matching request PDU " + "found for reply to cmd %d", + pdu->header.command); + return -1; + } + + SMB2_LIST_REMOVE(&smb2->waitqueue, req_pdu); + + pdu->header.credit_request_response = + 64 + req_pdu->header.credit_charge; + + /* replies always have to have the same message-id and tree-id as + * the request we sent, so use the request from the wait queue + * to make sure that is the case. (exception is tree-connect where + * the reply has the new tree-id and request was 0 ) + */ + + pdu->header.message_id = req_pdu->header.message_id; + if (pdu->header.command != SMB2_TREE_CONNECT) { + pdu->header.sync.tree_id = req_pdu->header.sync.tree_id; + } + smb2_free_pdu(smb2, req_pdu); + return ret; +} + void smb2_queue_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu) { @@ -388,6 +636,12 @@ smb2_queue_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu) /* Update all the PDU headers in this chain */ for (p = pdu; p; p = p->next_compound) { + if (smb2_is_server(smb2)) { + if (!(pdu->header.flags & SMB2_FLAGS_ASYNC_COMMAND)) { + smb2_correlate_reply(smb2, p); + /* TODO - care about check reply failures? */ + } + } smb2_encode_header(smb2, &p->out.iov[0], &p->header); if (smb2->sign || (p->header.command == SMB2_TREE_CONNECT && smb2->dialect == SMB2_VERSION_0311 && !smb2->seal)) { @@ -472,12 +726,16 @@ smb2_get_fixed_reply_size(struct smb2_context *smb2, struct smb2_pdu *pdu) return SMB2_ECHO_REPLY_SIZE; case SMB2_QUERY_DIRECTORY: return SMB2_QUERY_DIRECTORY_REPLY_SIZE; + case SMB2_CHANGE_NOTIFY: + return SMB2_CHANGE_NOTIFY_REPLY_SIZE; case SMB2_QUERY_INFO: return SMB2_QUERY_INFO_REPLY_SIZE; case SMB2_SET_INFO: return SMB2_SET_INFO_REPLY_SIZE; case SMB2_IOCTL: return SMB2_IOCTL_REPLY_SIZE; + case SMB2_OPLOCK_BREAK: + return SMB2_OPLOCK_BREAK_REPLY_SIZE; } return -1; } @@ -514,12 +772,16 @@ smb2_get_fixed_request_size(struct smb2_context *smb2, struct smb2_pdu *pdu) return SMB2_ECHO_REQUEST_SIZE; case SMB2_QUERY_DIRECTORY: return SMB2_QUERY_DIRECTORY_REQUEST_SIZE; + case SMB2_CHANGE_NOTIFY: + return SMB2_CHANGE_NOTIFY_REQUEST_SIZE; case SMB2_QUERY_INFO: return SMB2_QUERY_INFO_REQUEST_SIZE; case SMB2_SET_INFO: return SMB2_SET_INFO_REQUEST_SIZE; case SMB2_IOCTL: return SMB2_IOCTL_REQUEST_SIZE; + case SMB2_OPLOCK_BREAK: + return SMB2_OPLOCK_BREAK_REQUEST_SIZE; } return -1; } @@ -569,12 +831,16 @@ smb2_process_reply_payload_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu return smb2_process_lock_fixed(smb2, pdu); case SMB2_QUERY_DIRECTORY: return smb2_process_query_directory_fixed(smb2, pdu); + case SMB2_CHANGE_NOTIFY: + return smb2_process_change_notify_fixed(smb2, pdu); case SMB2_QUERY_INFO: return smb2_process_query_info_fixed(smb2, pdu); case SMB2_SET_INFO: return smb2_process_set_info_fixed(smb2, pdu); case SMB2_IOCTL: return smb2_process_ioctl_fixed(smb2, pdu); + case SMB2_OPLOCK_BREAK: + return -1; } return 0; } @@ -604,7 +870,7 @@ smb2_process_reply_payload_variable(struct smb2_context *smb2, struct smb2_pdu * case SMB2_FLUSH: return 0; case SMB2_READ: - return 0; + return smb2_process_read_variable(smb2, pdu); case SMB2_WRITE: return 0; case SMB2_ECHO: @@ -615,12 +881,16 @@ smb2_process_reply_payload_variable(struct smb2_context *smb2, struct smb2_pdu * return 0; case SMB2_QUERY_DIRECTORY: return smb2_process_query_directory_variable(smb2, pdu); + case SMB2_CHANGE_NOTIFY: + return smb2_process_change_notify_variable(smb2, pdu); case SMB2_QUERY_INFO: return smb2_process_query_info_variable(smb2, pdu); case SMB2_SET_INFO: return 0; case SMB2_IOCTL: return smb2_process_ioctl_variable(smb2, pdu); + case SMB2_OPLOCK_BREAK: + return -1; } return 0; } @@ -657,6 +927,8 @@ smb2_process_request_payload_fixed(struct smb2_context *smb2, struct smb2_pdu *p return 0; case SMB2_QUERY_DIRECTORY: return smb2_process_query_directory_request_fixed(smb2, pdu); + case SMB2_CHANGE_NOTIFY: + return smb2_process_change_notify_request_fixed(smb2, pdu); case SMB2_QUERY_INFO: return smb2_process_query_info_request_fixed(smb2, pdu); case SMB2_SET_INFO: @@ -702,6 +974,8 @@ smb2_process_request_payload_variable(struct smb2_context *smb2, struct smb2_pdu return 0; case SMB2_QUERY_DIRECTORY: return smb2_process_query_directory_request_variable(smb2, pdu); + case SMB2_CHANGE_NOTIFY: + return 0; case SMB2_QUERY_INFO: return smb2_process_query_info_request_variable(smb2, pdu); case SMB2_SET_INFO: diff --git a/lib/smb2-cmd-close.c b/lib/smb2-cmd-close.c index a050e370..87bd466d 100644 --- a/lib/smb2-cmd-close.c +++ b/lib/smb2-cmd-close.c @@ -114,9 +114,6 @@ smb2_encode_close_reply(struct smb2_context *smb2, uint8_t *buf; struct smb2_iovec *iov; - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - len = SMB2_CLOSE_REPLY_SIZE & 0xfffffffe; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { @@ -227,7 +224,7 @@ smb2_process_close_request_fixed(struct smb2_context *smb2, } smb2_get_uint16(iov, 2, &req->flags); - memcpy(req->file_id, iov->buf + 4, SMB2_FD_SIZE); + memcpy(req->file_id, iov->buf + 8, SMB2_FD_SIZE); return 0; } diff --git a/lib/smb2-cmd-create.c b/lib/smb2-cmd-create.c index 3db240ba..7fab2a36 100644 --- a/lib/smb2-cmd-create.c +++ b/lib/smb2-cmd-create.c @@ -47,14 +47,16 @@ #include #endif -#include - #include "compat.h" #include "smb2.h" #include "libsmb2.h" #include "libsmb2-private.h" +#define CCX_OFFSET() \ + PAD_TO_64BIT(SMB2_HEADER_SIZE + (SMB2_CREATE_REQUEST_SIZE & 0xfffe) \ + + (req->name_length ? req->name_length : 1)); + static int smb2_encode_create_request(struct smb2_context *smb2, struct smb2_pdu *pdu, @@ -64,9 +66,10 @@ smb2_encode_create_request(struct smb2_context *smb2, uint8_t *buf; uint16_t ch; struct smb2_utf16 *name = NULL; + uint32_t name_byte_len = 0; struct smb2_iovec *iov; - - len = SMB2_CREATE_REQUEST_SIZE & 0xfffffffe; + + len = SMB2_CREATE_REQUEST_SIZE & 0xfffe; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { smb2_set_error(smb2, "Failed to allocate create buffer"); @@ -82,8 +85,10 @@ smb2_encode_create_request(struct smb2_context *smb2, smb2_set_error(smb2, "Could not convert name into UTF-16"); return -1; } + name_byte_len = 2 * name->len; /* name length */ - smb2_set_uint16(iov, 46, 2 * name->len); + req->name_length = name_byte_len; + smb2_set_uint16(iov, 46, req->name_length); } smb2_set_uint16(iov, 0, SMB2_CREATE_REQUEST_SIZE); @@ -97,21 +102,37 @@ smb2_encode_create_request(struct smb2_context *smb2, smb2_set_uint32(iov, 36, req->create_disposition); smb2_set_uint32(iov, 40, req->create_options); /* name offset */ - smb2_set_uint16(iov, 44, SMB2_HEADER_SIZE + 56); + req->name_offset = PAD_TO_32BIT(SMB2_HEADER_SIZE + (SMB2_CREATE_REQUEST_SIZE & 0xfffe)); + smb2_set_uint16(iov, 44, req->name_offset); + /* context offset */ + if (req->create_context_length) { + if (req->name_length == 0) { + req->create_context_offset = PAD_TO_64BIT(4 + req->name_offset); + } + else { + req->create_context_offset = PAD_TO_64BIT(req->name_length + req->name_offset); + } + } + else { + req->create_context_offset = 0; + } + smb2_set_uint32(iov, 48, req->create_context_offset); smb2_set_uint32(iov, 52, req->create_context_length); - + /* Name */ if (name) { - buf = malloc(2 * name->len); + len = PAD_TO_64BIT(name_byte_len); + buf = malloc(len); if (buf == NULL) { smb2_set_error(smb2, "Failed to allocate create name"); free(name); return -1; } - memcpy(buf, &name->val[0], 2 * name->len); + memcpy(buf, &name->val[0], name_byte_len); + memset(buf + name_byte_len, 0, len - name_byte_len); iov = smb2_add_iovector(smb2, &pdu->out, buf, - 2 * name->len, + len, free); /* Convert '/' to '\' */ for (i = 0; i < name->len; i++) { @@ -120,23 +141,30 @@ smb2_encode_create_request(struct smb2_context *smb2, smb2_set_uint16(iov, i * 2, 0x005c); } } + free(name); } - free(name); - - /* Create Context */ - if (req->create_context_length) { - smb2_set_error(smb2, "Create context not implemented, yet"); - return -1; + else { + /* have to have at least one byte for name even if len is 0 + * this code pads out to a 64 bit boundary to place the + * create contexts on which they require */ + static uint8_t zero[8]; + iov = smb2_add_iovector(smb2, &pdu->out, + zero, 8, NULL); } - - /* The buffer must contain at least one byte, even if name is "" - * and there is no create context. - */ - if (name == NULL && !req->create_context_length) { - static uint8_t zero; - + /* Create Context: note there is encoding, we just pass along */ + if (req->create_context_length) { + len = PAD_TO_64BIT(req->create_context_length); + buf = malloc(len); + if (buf == NULL) { + smb2_set_error(smb2, "Failed to allocate create context"); + return -1; + } + memcpy(buf, req->create_context, req->create_context_length); + memset(buf + req->create_context_length, 0, len - req->create_context_length); iov = smb2_add_iovector(smb2, &pdu->out, - &zero, 1, NULL); + buf, + len, + free); } return 0; @@ -176,10 +204,7 @@ smb2_encode_create_reply(struct smb2_context *smb2, uint8_t *buf; struct smb2_iovec *iov; - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - - len = SMB2_CREATE_REPLY_SIZE; + len = SMB2_CREATE_REPLY_SIZE & 0xfffe; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { smb2_set_error(smb2, "Failed to allocate create buffer"); @@ -189,14 +214,37 @@ smb2_encode_create_reply(struct smb2_context *smb2, iov = smb2_add_iovector(smb2, &pdu->out, buf, len, free); smb2_set_uint16(iov, 0, SMB2_CREATE_REPLY_SIZE); + smb2_set_uint8(iov, 2, rep->oplock_level); + smb2_set_uint8(iov, 3, rep->flags); + smb2_set_uint32(iov, 4, rep->create_action); + smb2_set_uint64(iov, 8, rep->creation_time); + smb2_set_uint64(iov, 16, rep->last_access_time); + smb2_set_uint64(iov, 24, rep->last_write_time); + smb2_set_uint64(iov, 32, rep->change_time); + smb2_set_uint64(iov, 40, rep->allocation_size); + smb2_set_uint64(iov, 48, rep->end_of_file); + smb2_set_uint32(iov, 56, rep->file_attributes); + memcpy(&iov->buf[64], rep->file_id, SMB2_FD_SIZE); + rep->create_context_offset = PAD_TO_64BIT((SMB2_CREATE_REPLY_SIZE & 0xfffe) + SMB2_HEADER_SIZE); + smb2_set_uint32(iov, 80, rep->create_context_offset); + smb2_set_uint32(iov, 84, rep->create_context_length); /* Create Context */ - /* if (rep->create_context_length) { - smb2_set_error(smb2, "Create context not implemented, yet"); - return -1; + len = PAD_TO_64BIT(rep->create_context_length); + buf = malloc(len); + if (buf == NULL) { + smb2_set_error(smb2, "Failed to allocate create context"); + return -1; + } + memcpy(buf, rep->create_context, rep->create_context_length); + memset(buf + rep->create_context_length, 0, len - rep->create_context_length); + iov = smb2_add_iovector(smb2, &pdu->out, + buf, + len, + free); } - */ + return 0; } @@ -289,12 +337,15 @@ smb2_process_create_variable(struct smb2_context *smb2, struct smb2_pdu *pdu) { struct smb2_create_reply *rep = pdu->payload; + struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; + struct smb2_iovec vec; - /* No support for createcontext yet*/ - /* Create Context */ + vec.buf = iov->buf + IOV_OFFSET; + vec.len = iov->len - IOV_OFFSET; + + rep->create_context = NULL; if (rep->create_context_length) { - smb2_set_error(smb2, "Create context not implemented, yet"); - return -1; + rep->create_context = vec.buf; } return 0; @@ -310,7 +361,8 @@ smb2_process_create_request_fixed(struct smb2_context *smb2, struct smb2_create_request *req; struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; uint16_t struct_size; - + int remaining; + req = malloc(sizeof(*req)); if (req== NULL) { smb2_set_error(smb2, "Failed to allocate create request"); @@ -367,19 +419,41 @@ smb2_process_create_request_fixed(struct smb2_context *smb2, } /* Return the amount of data that the name will take up. - * Including any padding before the name itself. + * Including any padding before the name itself, and between name and create contexts */ - return IOVREQ_OFFSET + ((req->name_length + 3) & ~3) + req->create_context_length; + remaining = IOVREQ_OFFSET; + if (req->create_context_offset > req->name_offset) { + remaining += PAD_TO_64BIT(req->create_context_offset - req->name_offset); + } + else { + remaining += req->name_length; + } + remaining += req->create_context_length; + return remaining; } int smb2_process_create_request_variable(struct smb2_context *smb2, struct smb2_pdu *pdu) { - struct smb2_create_request *req = (struct smb2_create_request*)pdu->payload; - struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; - - req->name = (const char *)iov->buf; + struct smb2_create_request *req = (struct smb2_create_request*)pdu->payload;; + struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; + uint32_t offset; + + req->name = NULL; + if (req->name_length > 0) { + req->name = smb2_utf16_to_utf8((const uint16_t*)iov->buf, req->name_length / 2); + } + + /* we dont parse the create contexts but we tack them on in case the + * the caller wants to pass them along + */ + req->create_context = NULL; + if (req->create_context_length && req->create_context_offset) { + offset = req->create_context_offset - SMB2_HEADER_SIZE - + (SMB2_CREATE_REQUEST_SIZE & 0xfffe); + req->create_context = iov->buf + offset; + } return 0; } diff --git a/lib/smb2-cmd-echo.c b/lib/smb2-cmd-echo.c index 6e3fc97d..85815dc3 100644 --- a/lib/smb2-cmd-echo.c +++ b/lib/smb2-cmd-echo.c @@ -60,7 +60,7 @@ smb2_encode_echo_request(struct smb2_context *smb2, uint8_t *buf; int len; struct smb2_iovec *iov; - + len = SMB2_ECHO_REQUEST_SIZE; buf = calloc(len, sizeof(uint8_t)); @@ -68,7 +68,7 @@ smb2_encode_echo_request(struct smb2_context *smb2, smb2_set_error(smb2, "Failed to allocate echo buffer"); return -1; } - + iov = smb2_add_iovector(smb2, &pdu->out, buf, len, free); smb2_set_uint16(iov, 0, SMB2_ECHO_REQUEST_SIZE); @@ -81,7 +81,7 @@ smb2_cmd_echo_async(struct smb2_context *smb2, smb2_command_cb cb, void *cb_data) { struct smb2_pdu *pdu; - + pdu = smb2_allocate_pdu(smb2, SMB2_ECHO, cb, cb_data); if (pdu == NULL) { return NULL; @@ -91,7 +91,7 @@ smb2_cmd_echo_async(struct smb2_context *smb2, smb2_free_pdu(smb2, pdu); return NULL; } - + if (smb2_pad_to_64bit(smb2, &pdu->out) != 0) { smb2_free_pdu(smb2, pdu); return NULL; @@ -108,9 +108,6 @@ smb2_encode_echo_reply(struct smb2_context *smb2, int len; struct smb2_iovec *iov; - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - len = SMB2_ECHO_REPLY_SIZE; buf = calloc(len, sizeof(uint8_t)); @@ -118,7 +115,7 @@ smb2_encode_echo_reply(struct smb2_context *smb2, smb2_set_error(smb2, "Failed to allocate echo buffer"); return -1; } - + iov = smb2_add_iovector(smb2, &pdu->out, buf, len, free); smb2_set_uint16(iov, 0, SMB2_ECHO_REPLY_SIZE); @@ -130,7 +127,7 @@ smb2_cmd_echo_reply_async(struct smb2_context *smb2, smb2_command_cb cb, void *cb_data) { struct smb2_pdu *pdu; - + pdu = smb2_allocate_pdu(smb2, SMB2_ECHO, cb, cb_data); if (pdu == NULL) { return NULL; @@ -140,7 +137,7 @@ smb2_cmd_echo_reply_async(struct smb2_context *smb2, smb2_free_pdu(smb2, pdu); return NULL; } - + if (smb2_pad_to_64bit(smb2, &pdu->out) != 0) { smb2_free_pdu(smb2, pdu); return NULL; @@ -173,9 +170,17 @@ int smb2_process_echo_request_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu) { + struct smb2_echo_request *req; struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; uint16_t struct_size; + req = malloc(sizeof(*req)); + if (req == NULL) { + smb2_set_error(smb2, "Failed to allocate echo request"); + return -1; + } + pdu->payload = req; + smb2_get_uint16(iov, 0, &struct_size); if (struct_size != SMB2_ECHO_REQUEST_SIZE || (struct_size & 0xfffe) != iov->len) { diff --git a/lib/smb2-cmd-error.c b/lib/smb2-cmd-error.c index 6366749b..07d387d2 100644 --- a/lib/smb2-cmd-error.c +++ b/lib/smb2-cmd-error.c @@ -64,9 +64,6 @@ smb2_encode_error_reply(struct smb2_context *smb2, uint8_t *buf; struct smb2_iovec *iov; - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - len = SMB2_ERROR_REPLY_SIZE; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { @@ -104,12 +101,10 @@ smb2_cmd_error_reply_async(struct smb2_context *smb2, smb2_free_pdu(smb2, pdu); return NULL; } - if (smb2_pad_to_64bit(smb2, &pdu->out) != 0) { smb2_free_pdu(smb2, pdu); return NULL; } - return pdu; } diff --git a/lib/smb2-cmd-flush.c b/lib/smb2-cmd-flush.c index 76bba464..c9e27a85 100644 --- a/lib/smb2-cmd-flush.c +++ b/lib/smb2-cmd-flush.c @@ -95,7 +95,7 @@ smb2_cmd_flush_async(struct smb2_context *smb2, smb2_free_pdu(smb2, pdu); return NULL; } - + if (smb2_pad_to_64bit(smb2, &pdu->out) != 0) { smb2_free_pdu(smb2, pdu); return NULL; @@ -112,9 +112,6 @@ smb2_encode_flush_reply(struct smb2_context *smb2, uint8_t *buf; struct smb2_iovec *iov; - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - len = SMB2_FLUSH_REPLY_SIZE & 0xfffffffe; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { @@ -144,7 +141,7 @@ smb2_cmd_flush_reply_async(struct smb2_context *smb2, smb2_free_pdu(smb2, pdu); return NULL; } - + if (smb2_pad_to_64bit(smb2, &pdu->out) != 0) { smb2_free_pdu(smb2, pdu); return NULL; @@ -176,10 +173,19 @@ int smb2_process_flush_request_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu) { + struct smb2_flush_request *req; struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; uint16_t struct_size; + req = malloc(sizeof(*req)); + if (req == NULL) { + smb2_set_error(smb2, "Failed to allocate flush request"); + return -1; + } + pdu->payload = req; + smb2_get_uint16(iov, 0, &struct_size); + if (struct_size != SMB2_FLUSH_REQUEST_SIZE || (struct_size & 0xfffe) != iov->len) { smb2_set_error(smb2, "Unexpected size of flush " diff --git a/lib/smb2-cmd-ioctl.c b/lib/smb2-cmd-ioctl.c index a0b8bec0..83fb1c71 100644 --- a/lib/smb2-cmd-ioctl.c +++ b/lib/smb2-cmd-ioctl.c @@ -125,9 +125,6 @@ smb2_encode_ioctl_reply(struct smb2_context *smb2, uint8_t *buf; struct smb2_iovec *iov, *ioctlv; - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - len = SMB2_IOCTL_REPLY_SIZE & 0xfffffffe; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { @@ -141,18 +138,23 @@ smb2_encode_ioctl_reply(struct smb2_context *smb2, if (rep->output_count) { switch (rep->ctl_code) { case SMB2_FSCTL_VALIDATE_NEGOTIATE_INFO: - { + /* even when passthrough is set we transcode this one + */ len = SMB2_IOCTL_VALIDIATE_NEGOTIATE_INFO_SIZE; break; - } default: - smb2_set_error(smb2, "No handling of code %d", rep->ctl_code); - len = 0; - return -1; + if (smb2->passthrough) { + /* assume the replys output is already coded */ + len = rep->output_count; + } + else { + smb2_set_error(smb2, "No handling of code %d", rep->ctl_code); + len = 0; + return -1; + } break; } - len = PAD_TO_64BIT(len); - buf = malloc(len); + buf = malloc(PAD_TO_64BIT(len)); if (buf == NULL) { smb2_set_error(smb2, "Failed to allocate ioctl output"); return -1; @@ -160,22 +162,6 @@ smb2_encode_ioctl_reply(struct smb2_context *smb2, memset(buf, 0, rep->output_count); ioctlv = smb2_add_iovector(smb2, &pdu->out, buf, len, free); - rep->output_count = len; - } - - smb2_set_uint16(iov, 0, SMB2_IOCTL_REPLY_SIZE); - smb2_set_uint32(iov, 4, rep->ctl_code); - memcpy(iov->buf + 8, rep->file_id, SMB2_FD_SIZE); - smb2_set_uint32(iov, 24, SMB2_HEADER_SIZE + - (SMB2_IOCTL_REPLY_SIZE & 0xfffffffe)); - smb2_set_uint32(iov, 28, rep->input_count); - smb2_set_uint32(iov, 32, SMB2_HEADER_SIZE + - (SMB2_IOCTL_REPLY_SIZE & 0xfffffffe) + - PAD_TO_64BIT(rep->input_count)); - smb2_set_uint32(iov, 36, len); - smb2_set_uint32(iov, 40, rep->flags); - - if (rep->output_count && ioctlv) { switch (rep->ctl_code) { case SMB2_FSCTL_VALIDATE_NEGOTIATE_INFO: { @@ -185,18 +171,36 @@ smb2_encode_ioctl_reply(struct smb2_context *smb2, smb2_set_uint32(ioctlv, 0, info->capabilities); memcpy(&ioctlv->buf[4], info->guid, 16); - ioctlv->len += 16; smb2_set_uint16(ioctlv, 20, info->security_mode); smb2_set_uint16(ioctlv, 22, info->dialect); break; } default: - smb2_set_error(smb2, "No handling of code %d", rep->ctl_code); - len = 0; - return -1; + if (smb2->passthrough) { + memcpy(buf, rep->output, rep->output_count); + ioctlv->len = rep->output_count; + } + else + { + /* this is already checked above */ + return -1; + } break; } } + + smb2_set_uint16(iov, 0, SMB2_IOCTL_REPLY_SIZE); + smb2_set_uint32(iov, 4, rep->ctl_code); + memcpy(iov->buf + 8, rep->file_id, SMB2_FD_SIZE); + smb2_set_uint32(iov, 24, SMB2_HEADER_SIZE + + (SMB2_IOCTL_REPLY_SIZE & 0xfffffffe)); + smb2_set_uint32(iov, 28, rep->input_count); + smb2_set_uint32(iov, 32, SMB2_HEADER_SIZE + + (SMB2_IOCTL_REPLY_SIZE & 0xfffffffe) + + PAD_TO_64BIT(rep->input_count)); + smb2_set_uint32(iov, 36, len); + smb2_set_uint32(iov, 40, rep->flags); + return 0; } @@ -378,7 +382,7 @@ smb2_process_ioctl_request_variable(struct smb2_context *smb2, struct smb2_ioctl_request *req = pdu->payload; struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; struct smb2_iovec vec; - void *ptr; + void *ptr = NULL; if (req->input_count > iov->len - IOVREQ_OFFSET) { return -EINVAL; @@ -399,12 +403,20 @@ smb2_process_ioctl_request_variable(struct smb2_context *smb2, memcpy(info->guid, &vec.buf[4], 16); smb2_get_uint16(&vec, 20, &info->security_mode); smb2_get_uint16(&vec, 22, &info->dialect); + req->input_count = sizeof(struct smb2_ioctl_validate_negotiate_info); break; - } default: - smb2_set_error(smb2, "No handling of code %d", req->ctl_code); - req->input = NULL; - return -1; + if (smb2->passthrough) { + /* dont know how to handle this, let user decode it */ + ptr = vec.buf; + req->input_count = vec.len; + } + else { + smb2_set_error(smb2, + "No handling for ioctl req %x", + req->ctl_code); + } + break; } req->input = ptr; return 0; diff --git a/lib/smb2-cmd-lock.c b/lib/smb2-cmd-lock.c index 0be258cb..e767ff9c 100644 --- a/lib/smb2-cmd-lock.c +++ b/lib/smb2-cmd-lock.c @@ -64,7 +64,10 @@ smb2_encode_lock_request(struct smb2_context *smb2, int len; uint8_t *buf; struct smb2_iovec *iov; + struct smb2_lock_element *elements; uint32_t u32; + int i; + uint32_t offset; len = SMB2_LOCK_REQUEST_SIZE & 0xfffffffe; buf = calloc(len, sizeof(uint8_t)); @@ -82,14 +85,22 @@ smb2_encode_lock_request(struct smb2_context *smb2, memcpy(iov->buf + 8, req->file_id, SMB2_FD_SIZE); if (req->lock_count && req->locks) { - len = PAD_TO_32BIT(SMB2_LOCK_ELEMENT_SIZE * req->lock_count); + len = PAD_TO_64BIT(SMB2_LOCK_ELEMENT_SIZE * req->lock_count); buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { smb2_set_error(smb2, "Failed to allocate locks buffer"); return -1; } - memcpy(buf, req->locks, SMB2_LOCK_ELEMENT_SIZE * req->lock_count); - iov = smb2_add_iovector(smb2, &pdu->out, req->locks, len, free); + iov = smb2_add_iovector(smb2, &pdu->out, buf, len, free); + + elements = req->locks; + for (i = 0, offset = 0; i < req->lock_count; i++) { + smb2_set_uint64(iov, offset, elements->offset); + smb2_set_uint64(iov, offset + 8, elements->length); + smb2_set_uint32(iov, offset + 16, elements->flags); + offset += SMB2_LOCK_ELEMENT_SIZE; + elements++; + } } return 0; @@ -127,10 +138,7 @@ smb2_encode_lock_reply(struct smb2_context *smb2, int len; uint8_t *buf; struct smb2_iovec *iov; - - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - + len = SMB2_LOCK_REPLY_SIZE & 0xfffffffe; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { @@ -248,7 +256,7 @@ smb2_process_lock_request_fixed(struct smb2_context *smb2, } ptr = smb2_alloc_init(smb2, - req->lock_count * sizeof(struct smb2_lock_element)); + req->lock_count * SMB2_LOCK_ELEMENT_SIZE); if (!ptr) { smb2_set_error(smb2, "can not alloc lock buffer."); return -1; @@ -263,6 +271,7 @@ smb2_process_lock_request_fixed(struct smb2_context *smb2, vec.buf = iov->buf + 24; vec.len = iov->len - 24; + smb2_parse_locks(smb2, &vec, 1, ptr); } return SMB2_LOCK_ELEMENT_SIZE * (req->lock_count - 1); @@ -277,6 +286,6 @@ smb2_process_lock_request_variable(struct smb2_context *smb2, /* parse remaining locks, there is already one parsed */ return smb2_parse_locks(smb2, iov, req->lock_count - 1, - (uint8_t*)(req->locks) + sizeof(struct smb2_lock_element)); + (uint8_t*)(req->locks + 1)); } diff --git a/lib/smb2-cmd-logoff.c b/lib/smb2-cmd-logoff.c index 88b7bf09..23b16c6b 100644 --- a/lib/smb2-cmd-logoff.c +++ b/lib/smb2-cmd-logoff.c @@ -60,7 +60,7 @@ smb2_encode_logoff_request(struct smb2_context *smb2, uint8_t *buf; int len; struct smb2_iovec *iov; - + len = SMB2_LOGOFF_REQUEST_SIZE; buf = calloc(len, sizeof(uint8_t)); @@ -68,7 +68,7 @@ smb2_encode_logoff_request(struct smb2_context *smb2, smb2_set_error(smb2, "Failed to allocate logoff buffer"); return -1; } - + iov = smb2_add_iovector(smb2, &pdu->out, buf, len, free); smb2_set_uint16(iov, 0, SMB2_LOGOFF_REQUEST_SIZE); @@ -81,7 +81,7 @@ smb2_cmd_logoff_async(struct smb2_context *smb2, smb2_command_cb cb, void *cb_data) { struct smb2_pdu *pdu; - + pdu = smb2_allocate_pdu(smb2, SMB2_LOGOFF, cb, cb_data); if (pdu == NULL) { return NULL; @@ -91,7 +91,7 @@ smb2_cmd_logoff_async(struct smb2_context *smb2, smb2_free_pdu(smb2, pdu); return NULL; } - + if (smb2_pad_to_64bit(smb2, &pdu->out) != 0) { smb2_free_pdu(smb2, pdu); return NULL; @@ -107,9 +107,6 @@ smb2_encode_logoff_reply(struct smb2_context *smb2, uint8_t *buf; int len; struct smb2_iovec *iov; - - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; len = SMB2_LOGOFF_REPLY_SIZE; @@ -118,7 +115,7 @@ smb2_encode_logoff_reply(struct smb2_context *smb2, smb2_set_error(smb2, "Failed to allocate logoff reply buffer"); return -1; } - + iov = smb2_add_iovector(smb2, &pdu->out, buf, len, free); smb2_set_uint16(iov, 0, SMB2_LOGOFF_REPLY_SIZE); @@ -131,7 +128,7 @@ smb2_cmd_logoff_reply_async(struct smb2_context *smb2, smb2_command_cb cb, void *cb_data) { struct smb2_pdu *pdu; - + pdu = smb2_allocate_pdu(smb2, SMB2_LOGOFF, cb, cb_data); if (pdu == NULL) { return NULL; @@ -141,7 +138,7 @@ smb2_cmd_logoff_reply_async(struct smb2_context *smb2, smb2_free_pdu(smb2, pdu); return NULL; } - + if (smb2_pad_to_64bit(smb2, &pdu->out) != 0) { smb2_free_pdu(smb2, pdu); return NULL; @@ -161,5 +158,25 @@ int smb2_process_logoff_request_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu) { + struct smb2_logoff_request *req; + struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; + uint16_t struct_size; + + req = malloc(sizeof(*req)); + if (req == NULL) { + smb2_set_error(smb2, "Failed to allocate echo request"); + return -1; + } + pdu->payload = req; + + smb2_get_uint16(iov, 0, &struct_size); + if (struct_size != SMB2_ECHO_REQUEST_SIZE || + (struct_size & 0xfffe) != iov->len) { + smb2_set_error(smb2, "Unexpected size of logoff " + "request. Expected %d, got %d", + SMB2_ECHO_REQUEST_SIZE, + (int)iov->len); + return -1; + } return 0; } diff --git a/lib/smb2-cmd-negotiate.c b/lib/smb2-cmd-negotiate.c index 8746a46d..224cad8c 100644 --- a/lib/smb2-cmd-negotiate.c +++ b/lib/smb2-cmd-negotiate.c @@ -204,9 +204,6 @@ smb2_encode_negotiate_reply(struct smb2_context *smb2, int len, seclen; struct smb2_iovec *iov; - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - len = SMB2_NEGOTIATE_REPLY_SIZE & 0xfffe; len = PAD_TO_32BIT(len); if (smb2->dialect == SMB2_VERSION_ANY || diff --git a/lib/smb2-cmd-notify-change.c b/lib/smb2-cmd-notify-change.c new file mode 100644 index 00000000..461e5f0d --- /dev/null +++ b/lib/smb2-cmd-notify-change.c @@ -0,0 +1,261 @@ +/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ +/* + Copyright (C) 2016 by Brian Dodge + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifdef HAVE_STDINT_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef STDC_HEADERS +#include +#endif + +#ifdef HAVE_TIME_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include + +#include "compat.h" + +#include "smb2.h" +#include "libsmb2.h" +#include "libsmb2-private.h" + +static int +smb2_encode_change_notify_request(struct smb2_context *smb2, + struct smb2_pdu *pdu, + struct smb2_change_notify_request *req) +{ + int len; + uint8_t *buf; + struct smb2_iovec *iov; + + len = SMB2_CHANGE_NOTIFY_REQUEST_SIZE & 0xfffffffe; + buf = calloc(len, sizeof(uint8_t)); + if (buf == NULL) { + smb2_set_error(smb2, "Failed to allocate NOTIFY buffer"); + return -1; + } + + iov = smb2_add_iovector(smb2, &pdu->out, buf, len, free); + + smb2_set_uint16(iov, 0, SMB2_CHANGE_NOTIFY_REQUEST_SIZE); + smb2_set_uint16(iov, 2, req->flags); + smb2_set_uint32(iov, 4, req->output_buffer_length); + memcpy(iov->buf + 8, req->file_id, SMB2_FD_SIZE); + smb2_set_uint32(iov, 24, req->completion_filter); + + return 0; +} + +struct smb2_pdu * +smb2_cmd_change_notify_async(struct smb2_context *smb2, + struct smb2_change_notify_request *req, + smb2_command_cb cb, void *cb_data) +{ + struct smb2_pdu *pdu; + + pdu = smb2_allocate_pdu(smb2, SMB2_CHANGE_NOTIFY, cb, cb_data); + if (pdu == NULL) { + return NULL; + } + + if (smb2_encode_change_notify_request(smb2, pdu, req)) { + smb2_free_pdu(smb2, pdu); + return NULL; + } + + if (smb2_pad_to_64bit(smb2, &pdu->out) != 0) { + smb2_free_pdu(smb2, pdu); + return NULL; + } + + return pdu; +} + +static int +smb2_encode_change_notify_reply(struct smb2_context *smb2, + struct smb2_pdu *pdu, + struct smb2_change_notify_reply *rep) +{ + int len; + uint8_t *buf; + struct smb2_iovec *iov; + len = SMB2_CHANGE_NOTIFY_REQUEST_SIZE & 0xfffffffe; + buf = calloc(len, sizeof(uint8_t)); + if (buf == NULL) { + smb2_set_error(smb2, "Failed to allocate NOTIFY reply buffer"); + return -1; + } + + iov = smb2_add_iovector(smb2, &pdu->out, buf, len, free); + + smb2_set_uint16(iov, 0, SMB2_CHANGE_NOTIFY_REPLY_SIZE); + rep->output_buffer_offset = SMB2_HEADER_SIZE + SMB2_CHANGE_NOTIFY_REQUEST_SIZE; + smb2_set_uint16(iov, 2, rep->output_buffer_offset); + smb2_set_uint32(iov, 4, rep->output_buffer_length); + + if (rep->output_buffer_length == 0) { + return 0; + } + + len = rep->output_buffer_length; + len = PAD_TO_32BIT(len); + buf = malloc(len); + if (buf == NULL) { + smb2_set_error(smb2, "Failed to allocate output buf"); + return -1; + } + + iov = smb2_add_iovector(smb2, &pdu->out, + buf, + len, + free); + + if (smb2->passthrough) { + memcpy(buf, rep->output, rep->output_buffer_length); + memset(buf + rep->output_buffer_length, 0, len - rep->output_buffer_length); + iov->len = rep->output_buffer_length; + } + else { + smb2_set_error(smb2, "Change-notify buffer packing not implemented"); + return -1; + } + + return 0; +} + +struct smb2_pdu * +smb2_cmd_change_notify_reply_async(struct smb2_context *smb2, + struct smb2_change_notify_reply *rep, + smb2_command_cb cb, void *cb_data) +{ + struct smb2_pdu *pdu; + + pdu = smb2_allocate_pdu(smb2, SMB2_CHANGE_NOTIFY, cb, cb_data); + if (pdu == NULL) { + return NULL; + } + + if (smb2_encode_change_notify_reply(smb2, pdu, rep)) { + smb2_free_pdu(smb2, pdu); + return NULL; + } + + if (smb2_pad_to_64bit(smb2, &pdu->out) != 0) { + smb2_free_pdu(smb2, pdu); + return NULL; + } + + return pdu; +} + +int +smb2_process_change_notify_fixed(struct smb2_context *smb2, + struct smb2_pdu *pdu) +{ + struct smb2_change_notify_reply *rep; + struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; + uint16_t struct_size; + + rep = malloc(sizeof(*rep)); + if (rep == NULL) { + smb2_set_error(smb2, "Failed to allocate NOTIFY reply"); + return -1; + } + pdu->payload = rep; + + smb2_get_uint16(iov, 0, &struct_size); + if (struct_size != SMB2_CHANGE_NOTIFY_REQUEST_SIZE || + (struct_size & 0xfffe) != iov->len) { + smb2_set_error(smb2, "Unexpected size of change " + "notify reply. Expected %d, got %d", + SMB2_CHANGE_NOTIFY_REPLY_SIZE, + (int)iov->len); + return -1; + } + + smb2_get_uint16(iov, 2, &rep->output_buffer_offset); + smb2_get_uint32(iov, 8, &rep->output_buffer_length); + + return rep->output_buffer_length; +} + +int +smb2_process_change_notify_variable(struct smb2_context *smb2, + struct smb2_pdu *pdu) +{ + struct smb2_change_notify_reply *rep = pdu->payload; + struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; + + rep->output = (uint8_t *)iov->buf; + return 0; +} + +int +smb2_process_change_notify_request_fixed(struct smb2_context *smb2, + struct smb2_pdu *pdu) +{ + struct smb2_change_notify_request *req; + struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; + uint16_t struct_size; + + req = malloc(sizeof(*req)); + if (req == NULL) { + smb2_set_error(smb2, "Failed to allocate NOTIFY request"); + return -1; + } + pdu->payload = req; + + smb2_get_uint16(iov, 0, &struct_size); + if (struct_size != SMB2_CHANGE_NOTIFY_REQUEST_SIZE || + (struct_size & 0xfffe) != iov->len) { + smb2_set_error(smb2, "Unexpected size of change " + "notify request. Expected %d, got %d", + SMB2_CHANGE_NOTIFY_REQUEST_SIZE, + (int)iov->len); + return -1; + } + + smb2_get_uint16(iov, 2, &req->flags); + memcpy(req->file_id, iov->buf + 8, SMB2_FD_SIZE); + smb2_get_uint32(iov, 24, &req->completion_filter); + + return 0; +} + + diff --git a/lib/smb2-cmd-query-directory.c b/lib/smb2-cmd-query-directory.c index 55e589ae..4bff1a62 100644 --- a/lib/smb2-cmd-query-directory.c +++ b/lib/smb2-cmd-query-directory.c @@ -31,10 +31,6 @@ #include #endif -#ifdef HAVE_STDIO_H -#include -#endif - #ifdef HAVE_STRING_H #include #endif @@ -51,8 +47,6 @@ #include #endif -#include - #include "compat.h" #include "smb2.h" @@ -212,9 +206,6 @@ smb2_encode_query_directory_reply(struct smb2_context *smb2, int in_offset; int in_remain; - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - len = SMB2_QUERY_DIRECTORY_REPLY_SIZE & 0xfffe; len = PAD_TO_32BIT(len); @@ -228,63 +219,76 @@ smb2_encode_query_directory_reply(struct smb2_context *smb2, fslen = rep->output_buffer_length; rep->output_buffer_offset = len + SMB2_HEADER_SIZE; - rep->output_buffer_length = 0; if (rep->output_buffer) { - in_offset = 0; - in_remain = fslen; - do { - fs = (struct smb2_fileidbothdirectoryinformation*)(rep->output_buffer + in_offset); - fname_len = 0; - if (fs->name && fs->name[0]) { - name = smb2_utf8_to_utf16(fs->name); - if (name == NULL) { - smb2_set_error(smb2, "Could not convert name into UTF-16"); - return -1; + if (!smb2->passthrough) { + rep->output_buffer_length = 0; + in_offset = 0; + in_remain = fslen; + do { + fs = (struct smb2_fileidbothdirectoryinformation*)(rep->output_buffer + in_offset); + fname_len = 0; + if (fs->name && fs->name[0]) { + name = smb2_utf8_to_utf16(fs->name); + if (name == NULL) { + smb2_set_error(smb2, "Could not convert name into UTF-16"); + return -1; + } + fname_len = 2 * name->len; + free(name); } - fname_len = 2 * name->len; - free(name); - } - switch (info_class) - { - case SMB2_FILE_ID_FULL_DIRECTORY_INFORMATION: - fs_size = PAD_TO_32BIT(SMB2_FILEID_FULL_DIRECTORY_INFORMATION_SIZE + fname_len); - break; - case SMB2_FILE_ID_BOTH_DIRECTORY_INFORMATION: - fs_size = PAD_TO_32BIT(SMB2_FILEID_BOTH_DIRECTORY_INFORMATION_SIZE + fname_len); - break; - default: - fs_size = 0; - break; + switch (info_class) + { + case SMB2_FILE_ID_FULL_DIRECTORY_INFORMATION: + fs_size = PAD_TO_32BIT(SMB2_FILEID_FULL_DIRECTORY_INFORMATION_SIZE + fname_len); + break; + case SMB2_FILE_ID_BOTH_DIRECTORY_INFORMATION: + fs_size = PAD_TO_32BIT(SMB2_FILEID_BOTH_DIRECTORY_INFORMATION_SIZE + fname_len); + break; + default: + fs_size = 0; + break; + } + rep->output_buffer_length += fs_size; + in_offset += PAD_TO_64BIT(sizeof(struct smb2_fileidbothdirectoryinformation)); + in_remain -= PAD_TO_64BIT(sizeof(struct smb2_fileidbothdirectoryinformation)); } - rep->output_buffer_length += fs_size; - in_offset += PAD_TO_64BIT(sizeof(struct smb2_fileidbothdirectoryinformation)); - in_remain -= PAD_TO_64BIT(sizeof(struct smb2_fileidbothdirectoryinformation)); + while (in_remain >= sizeof(struct smb2_fileidbothdirectoryinformation)); } - while (in_remain >= sizeof(struct smb2_fileidbothdirectoryinformation)); } smb2_set_uint16(iov, 0, SMB2_QUERY_DIRECTORY_REPLY_SIZE); smb2_set_uint16(iov, 2, rep->output_buffer_offset); smb2_set_uint32(iov, 4, rep->output_buffer_length); - if (rep->output_buffer_length > 0) { - len = rep->output_buffer_length; - len = PAD_TO_32BIT(len); - buf = malloc(len); - if (buf == NULL) { - smb2_set_error(smb2, "Failed to allocate output buf"); - return -1; - } - - iov = smb2_add_iovector(smb2, &pdu->out, + if (rep->output_buffer_length == 0) { + return 0; + } + + len = rep->output_buffer_length; + len = PAD_TO_32BIT(len); + buf = malloc(len); + if (buf == NULL) { + smb2_set_error(smb2, "Failed to allocate output buf"); + return -1; + } + + iov = smb2_add_iovector(smb2, &pdu->out, buf, len, free); - - in_offset = 0; - in_remain = fslen; - offset = 0; + + in_offset = 0; + in_remain = fslen; + offset = 0; + + if (smb2->passthrough) { + memcpy(buf, rep->output_buffer, rep->output_buffer_length); + memset(buf + rep->output_buffer_length, 0, len - rep->output_buffer_length); + iov->len = rep->output_buffer_length; + } + else { + do { fs = (struct smb2_fileidbothdirectoryinformation*)(rep->output_buffer + in_offset); fname_len = 0; @@ -375,9 +379,7 @@ smb2_encode_query_directory_reply(struct smb2_context *smb2, struct smb2_pdu * smb2_cmd_query_directory_reply_async(struct smb2_context *smb2, - uint8_t info_class, - uint8_t flags, - uint32_t room, + struct smb2_query_directory_request *req, struct smb2_query_directory_reply *rep, smb2_command_cb cb, void *cb_data) { @@ -387,7 +389,8 @@ smb2_cmd_query_directory_reply_async(struct smb2_context *smb2, return NULL; } - if (smb2_encode_query_directory_reply(smb2, info_class, flags, room, pdu, rep)) { + if (smb2_encode_query_directory_reply(smb2, req->file_information_class, + req->flags, req->output_buffer_length, pdu, rep)) { smb2_free_pdu(smb2, pdu); return NULL; } @@ -497,7 +500,7 @@ smb2_process_query_directory_request_fixed(struct smb2_context *smb2, smb2_get_uint8(iov, 2, &req->file_information_class); smb2_get_uint8(iov, 3, &req->flags); smb2_get_uint32(iov, 4, &req->file_index); - memcpy(req->file_id, iov + 8, SMB2_FD_SIZE); + memcpy(req->file_id, &iov->buf[8], SMB2_FD_SIZE); smb2_get_uint16(iov, 24, &req->file_name_offset); smb2_get_uint16(iov, 26, &req->file_name_length); smb2_get_uint32(iov, 28, &req->output_buffer_length); @@ -532,9 +535,29 @@ smb2_process_query_directory_request_variable(struct smb2_context *smb2, { struct smb2_query_directory_request *req = pdu->payload; struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; - - req->name = (const char*)&iov->buf[IOVREQ_OFFSET]; - + void *ptr; + int name_byte_len; + + if (req->file_name_length > 0) { + req->name = smb2_utf16_to_utf8((uint16_t*)&iov->buf[IOVREQ_OFFSET], req->file_name_length / 2); + if (req->name) { + name_byte_len = strlen(req->name) + 1; + ptr = smb2_alloc_init(smb2, name_byte_len); + if (ptr) { + memcpy(ptr, req->name, name_byte_len); + } + free(discard_const(req->name)); + req->name = ptr; + if (!ptr) { + smb2_set_error(smb2, "can not alloc name buffer"); + return -1; + } + } + else { + smb2_set_error(smb2, "can not convert name to utf8"); + return -1; + } + } return 0; } diff --git a/lib/smb2-cmd-query-info.c b/lib/smb2-cmd-query-info.c index 2a987e1c..9769c938 100644 --- a/lib/smb2-cmd-query-info.c +++ b/lib/smb2-cmd-query-info.c @@ -55,7 +55,7 @@ #include "libsmb2.h" #include "libsmb2-private.h" -static int +int smb2_encode_query_info_request(struct smb2_context *smb2, struct smb2_pdu *pdu, struct smb2_query_info_request *req) @@ -82,6 +82,8 @@ smb2_encode_query_info_request(struct smb2_context *smb2, smb2_set_uint8(iov, 2, req->info_type); smb2_set_uint8(iov, 3, req->file_info_class); smb2_set_uint32(iov,4, req->output_buffer_length); + req->input_buffer_offset = SMB2_HEADER_SIZE + (SMB2_QUERY_INFO_REQUEST_SIZE & 0xfffe); + smb2_set_uint16(iov,8, req->input_buffer_offset); smb2_set_uint32(iov,12, req->input_buffer_length); smb2_set_uint32(iov,16, req->additional_information); smb2_set_uint32(iov,20, req->flags); @@ -121,20 +123,14 @@ smb2_cmd_query_info_async(struct smb2_context *smb2, static int smb2_encode_query_info_reply(struct smb2_context *smb2, - uint8_t info_type, - uint8_t file_info_class, - uint32_t room, - struct smb2_pdu *pdu, - struct smb2_query_info_reply *rep) + struct smb2_query_info_request *req, + struct smb2_pdu *pdu, + struct smb2_query_info_reply *rep) { int len; uint8_t *buf; struct smb2_iovec *iov, *cmdiov; - - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - - len = SMB2_QUERY_INFO_REPLY_SIZE & 0xfffffffe; + len = SMB2_QUERY_INFO_REPLY_SIZE & 0xfffe; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { smb2_set_error(smb2, "Failed to allocate query reply buffer"); @@ -153,10 +149,11 @@ smb2_encode_query_info_reply(struct smb2_context *smb2, if (rep->output_buffer_length > 0 && rep->output_buffer) { len = rep->output_buffer_length; - len = PAD_TO_32BIT(len); + len = PAD_TO_64BIT(len); + /* not sure exactly how long the encoding will be, some of the, * include variable data so add a whole lot of space */ - buf = malloc(1024); + buf = malloc(len + 1024); if (buf == NULL) { smb2_set_error(smb2, "Failed to allocate output buffer"); return -1; @@ -165,77 +162,86 @@ smb2_encode_query_info_reply(struct smb2_context *smb2, buf, len, free); - rep->output_buffer_length = 0; - switch (info_type) { - case SMB2_0_INFO_FILE: - switch (file_info_class) { - case SMB2_FILE_BASIC_INFORMATION: - smb2_encode_file_basic_info(smb2, - (struct smb2_file_basic_info *)rep->output_buffer, iov); - rep->output_buffer_length = iov->len; - break; - case SMB2_FILE_STANDARD_INFORMATION: - smb2_encode_file_standard_info(smb2, - (struct smb2_file_standard_info *)rep->output_buffer, iov); - rep->output_buffer_length = iov->len; - break; - case SMB2_FILE_RENAME_INFORMATION: - break; - case SMB2_FILE_ALL_INFORMATION: - smb2_encode_file_all_info(smb2, - (struct smb2_file_all_info *)rep->output_buffer, iov); - rep->output_buffer_length = iov->len; - break; - case SMB2_FILE_NETWORK_OPEN_INFORMATION: - smb2_encode_file_network_open_info(smb2, - (struct smb2_file_network_open_info *)rep->output_buffer, iov); - rep->output_buffer_length = iov->len; - break; - case SMB2_FILE_END_OF_FILE_INFORMATION: - break; - default: - break; - } - break; - case SMB2_0_INFO_FILESYSTEM: - switch (file_info_class) { - case SMB2_FILE_FS_VOLUME_INFORMATION: - break; - case SMB2_FILE_FS_SIZE_INFORMATION: - smb2_encode_file_fs_size_info(smb2, - (struct smb2_file_fs_size_info *)rep->output_buffer, iov); - rep->output_buffer_length = iov->len; - break; - case SMB2_FILE_FS_DEVICE_INFORMATION: - smb2_encode_file_fs_device_info(smb2, - (struct smb2_file_fs_device_info *)rep->output_buffer, iov); - rep->output_buffer_length = iov->len; - break; - case SMB2_FILE_FS_ATTRIBUTE_INFORMATION: - smb2_encode_file_fs_attribute_info(smb2, - (struct smb2_file_fs_attribute_info *)rep->output_buffer, iov); - rep->output_buffer_length = iov->len; + + if (smb2->passthrough) { + memcpy(buf, rep->output_buffer, rep->output_buffer_length); + memset(buf + rep->output_buffer_length, 0, len - rep->output_buffer_length); + /* blob needs 8 byte alignment */ + iov->len = len; + } + else { + rep->output_buffer_length = 0; + switch (req->info_type) { + case SMB2_0_INFO_FILE: + switch (req->file_info_class) { + case SMB2_FILE_BASIC_INFORMATION: + smb2_encode_file_basic_info(smb2, + (struct smb2_file_basic_info *)rep->output_buffer, iov); + rep->output_buffer_length = iov->len; + break; + case SMB2_FILE_STANDARD_INFORMATION: + smb2_encode_file_standard_info(smb2, + (struct smb2_file_standard_info *)rep->output_buffer, iov); + rep->output_buffer_length = iov->len; + break; + case SMB2_FILE_RENAME_INFORMATION: + break; + case SMB2_FILE_ALL_INFORMATION: + smb2_encode_file_all_info(smb2, + (struct smb2_file_all_info *)rep->output_buffer, iov); + rep->output_buffer_length = iov->len; + break; + case SMB2_FILE_NETWORK_OPEN_INFORMATION: + smb2_encode_file_network_open_info(smb2, + (struct smb2_file_network_open_info *)rep->output_buffer, iov); + rep->output_buffer_length = iov->len; + break; + case SMB2_FILE_END_OF_FILE_INFORMATION: + break; + default: + break; + } break; - case SMB2_FILE_FS_CONTROL_INFORMATION: + case SMB2_0_INFO_FILESYSTEM: + switch (req->file_info_class) { + case SMB2_FILE_FS_VOLUME_INFORMATION: + break; + case SMB2_FILE_FS_SIZE_INFORMATION: + smb2_encode_file_fs_size_info(smb2, + (struct smb2_file_fs_size_info *)rep->output_buffer, iov); + rep->output_buffer_length = iov->len; + break; + case SMB2_FILE_FS_DEVICE_INFORMATION: + smb2_encode_file_fs_device_info(smb2, + (struct smb2_file_fs_device_info *)rep->output_buffer, iov); + rep->output_buffer_length = iov->len; + break; + case SMB2_FILE_FS_ATTRIBUTE_INFORMATION: + smb2_encode_file_fs_attribute_info(smb2, + (struct smb2_file_fs_attribute_info *)rep->output_buffer, iov); + rep->output_buffer_length = iov->len; + break; + case SMB2_FILE_FS_CONTROL_INFORMATION: + break; + case SMB2_FILE_FS_FULL_SIZE_INFORMATION: + break; + case SMB2_FILE_FS_SECTOR_SIZE_INFORMATION: + break; + default: + len = -1; + break; + } break; - case SMB2_FILE_FS_FULL_SIZE_INFORMATION: + case SMB2_0_INFO_SECURITY: break; - case SMB2_FILE_FS_SECTOR_SIZE_INFORMATION: + case SMB2_0_INFO_QUOTA: break; default: - len = -1; - break; + return 0; } - break; - case SMB2_0_INFO_SECURITY: - break; - case SMB2_0_INFO_QUOTA: - break; - default: - return 0; + free(rep->output_buffer); + rep->output_buffer = NULL; } - free(rep->output_buffer); - rep->output_buffer = NULL; } smb2_set_uint32(cmdiov, 4, rep->output_buffer_length); @@ -244,11 +250,9 @@ smb2_encode_query_info_reply(struct smb2_context *smb2, struct smb2_pdu * smb2_cmd_query_info_reply_async(struct smb2_context *smb2, - uint8_t info_type, - uint8_t file_info_class, - uint32_t room, - struct smb2_query_info_reply *rep, - smb2_command_cb cb, void *cb_data) + struct smb2_query_info_request *req, + struct smb2_query_info_reply *rep, + smb2_command_cb cb, void *cb_data) { struct smb2_pdu *pdu; @@ -257,7 +261,7 @@ smb2_cmd_query_info_reply_async(struct smb2_context *smb2, return NULL; } - if (smb2_encode_query_info_reply(smb2, info_type, file_info_class, room, pdu, rep)) { + if (smb2_encode_query_info_reply(smb2, req, pdu, rep)) { smb2_free_pdu(smb2, pdu); return NULL; } @@ -336,8 +340,7 @@ smb2_process_query_info_fixed(struct smb2_context *smb2, return IOV_OFFSET + rep->output_buffer_length; } -int -smb2_process_query_info_variable(struct smb2_context *smb2, +int smb2_process_query_info_variable(struct smb2_context *smb2, struct smb2_pdu *pdu) { struct smb2_query_info_reply *rep = pdu->payload; @@ -345,8 +348,16 @@ smb2_process_query_info_variable(struct smb2_context *smb2, struct smb2_iovec vec = {&iov->buf[IOV_OFFSET], iov->len - IOV_OFFSET, NULL}; - void *ptr; + void *ptr = NULL; + if (smb2->passthrough) { + ptr = smb2_alloc_init(smb2, + rep->output_buffer_length); + memcpy(ptr, vec.buf, vec.len); + rep->output_buffer = ptr; + return 0; + } + switch (pdu->info_type) { case SMB2_0_INFO_FILE: switch (pdu->file_info_class) { @@ -523,9 +534,9 @@ smb2_process_query_info_request_fixed(struct smb2_context *smb2, smb2_get_uint32(iov, 20, &req->flags); memcpy(req->file_id, iov->buf + 24, SMB2_FD_SIZE); - if (req->input_buffer_length == 0) { - return 0; - } + if (req->input_buffer_length == 0) { + return 0; + } if (req->input_buffer_offset < SMB2_HEADER_SIZE + (SMB2_QUERY_INFO_REQUEST_SIZE & 0xfffe)) { smb2_set_error(smb2, "Input buffer overlaps with " diff --git a/lib/smb2-cmd-read.c b/lib/smb2-cmd-read.c index eb81e3c9..4edcb3d4 100644 --- a/lib/smb2-cmd-read.c +++ b/lib/smb2-cmd-read.c @@ -87,16 +87,37 @@ smb2_encode_read_request(struct smb2_context *smb2, smb2_set_uint32(iov, 40, req->remaining_bytes); smb2_set_uint16(iov, 46, req->read_channel_info_length); - if (req->read_channel_info_length > 0 || + if (req->read_channel_info_length > 0 && req->read_channel_info != NULL) { - smb2_set_error(smb2, "ChannelInfo not yet implemented"); - return -1; + if (smb2->passthrough) { + req->read_channel_info_offset = + (SMB2_READ_REQUEST_SIZE & 0xfffffffe) + SMB2_HEADER_SIZE; + smb2_set_uint16(iov, 44, req->read_channel_info_offset); + + len = PAD_TO_64BIT(req->read_channel_info_length); + buf = malloc(len); + if (buf == NULL) { + smb2_set_error(smb2, "Failed to allocate read channel context"); + return -1; + } + memcpy(buf, req->read_channel_info, req->read_channel_info_length); + memset(buf + req->read_channel_info_length, 0, + len - req->read_channel_info_length); + iov = smb2_add_iovector(smb2, &pdu->out, + buf, + len, + free); + } + else { + smb2_set_error(smb2, "ChannelInfo not yet implemented"); + return -1; + } } /* The buffer must contain at least one byte, even if we do not * have any read channel info. */ - if (req->read_channel_info == NULL) { + if (req->read_channel_info_length == 0) { static uint8_t zero; smb2_add_iovector(smb2, &pdu->out, &zero, 1, NULL); @@ -148,9 +169,6 @@ smb2_encode_read_reply(struct smb2_context *smb2, uint8_t *buf; struct smb2_iovec *iov; - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - len = SMB2_READ_REPLY_SIZE & 0xfffffffe; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { @@ -162,7 +180,7 @@ smb2_encode_read_reply(struct smb2_context *smb2, rep->data_offset = 0; if (rep->data_length && rep->data) { - rep->data_offset = len + SMB2_HEADER_SIZE; + rep->data_offset = (SMB2_READ_REPLY_SIZE & 0xfffffffe) + SMB2_HEADER_SIZE; } smb2_set_uint16(iov, 0, SMB2_READ_REPLY_SIZE); smb2_set_uint8(iov, 2, rep->data_offset); @@ -236,17 +254,30 @@ smb2_process_read_fixed(struct smb2_context *smb2, if (rep->data_offset != SMB2_HEADER_SIZE + 16) { smb2_set_error(smb2, "Unexpected data offset in Read reply. " "Expected %d, got %d", - 16, rep->data_offset); + SMB2_HEADER_SIZE + 16, rep->data_offset); return -1; } return rep->data_length; } +int +smb2_process_read_variable(struct smb2_context *smb2, + struct smb2_pdu *pdu) +{ + struct smb2_read_reply *rep = (struct smb2_read_reply*)pdu->payload; + struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; -#define IOVREQ_OFFSET (req->read_channel_info_offset - SMB2_HEADER_SIZE - \ - (SMB2_READ_REQUEST_SIZE & 0xfffe)) + rep->data = malloc(rep->data_length); + if (rep->data) { + memcpy(rep->data, iov->buf, rep->data_length); + return 0; + } + return -1; +} +#define IOVREQ_OFFSET ((req->read_channel_info_offset)?(req->read_channel_info_offset - SMB2_HEADER_SIZE - \ + (SMB2_READ_REQUEST_SIZE & 0xfffe)):0) int smb2_process_read_request_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu) @@ -275,11 +306,27 @@ smb2_process_read_request_fixed(struct smb2_context *smb2, smb2_get_uint32(iov, 4, &req->length); smb2_get_uint64(iov, 8, &req->offset); memcpy(req->file_id, iov->buf + 16, SMB2_FD_SIZE); - smb2_get_uint32(iov, 24, &req->minimum_count); - smb2_get_uint32(iov, 28, &req->channel); - smb2_get_uint32(iov, 28, &req->remaining_bytes); - smb2_get_uint16(iov, 32, &req->read_channel_info_offset); - smb2_get_uint16(iov, 34, &req->read_channel_info_length); + smb2_get_uint32(iov, 32, &req->minimum_count); + smb2_get_uint32(iov, 36, &req->channel); + smb2_get_uint32(iov, 40, &req->remaining_bytes); + if (req->read_channel_info_length) { + req->read_channel_info_offset = (SMB2_READ_REQUEST_SIZE & 0xfffffffe) + SMB2_HEADER_SIZE; + smb2_get_uint16(iov, 44, &req->read_channel_info_offset); + } + smb2_get_uint16(iov, 46, &req->read_channel_info_length); + + if (req->length > smb2->max_read_size) { + smb2_set_error(smb2, "can not read more than %d bytes", smb2->max_read_size); + return -1; + } + + /* provide an iovec to read the data into */ + req->buf = malloc(req->length); + if (!req->buf) { + smb2_set_error(smb2, "can not alloc for read reply data"); + return -1; + } + smb2_add_iovector(smb2, &pdu->in, req->buf, req->length, free); if (req->read_channel_info_length == 0) { return 0; diff --git a/lib/smb2-cmd-session-setup.c b/lib/smb2-cmd-session-setup.c index a4e2757c..7b240d45 100644 --- a/lib/smb2-cmd-session-setup.c +++ b/lib/smb2-cmd-session-setup.c @@ -131,9 +131,6 @@ smb2_encode_session_setup_reply(struct smb2_context *smb2, uint8_t *buf; int len; struct smb2_iovec *iov; - - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; len = SMB2_SESSION_SETUP_REPLY_SIZE & 0xfffe; len = PAD_TO_32BIT(len); diff --git a/lib/smb2-cmd-set-info.c b/lib/smb2-cmd-set-info.c index d2e7e6d1..2d1a61ff 100644 --- a/lib/smb2-cmd-set-info.c +++ b/lib/smb2-cmd-set-info.c @@ -55,7 +55,6 @@ #include "libsmb2.h" #include "libsmb2-private.h" -#include static int smb2_encode_set_info_request(struct smb2_context *smb2, struct smb2_pdu *pdu, @@ -86,6 +85,22 @@ smb2_encode_set_info_request(struct smb2_context *smb2, smb2_set_uint32(iov,12, req->additional_information); memcpy(iov->buf + 16, req->file_id, SMB2_FD_SIZE); + if (smb2->passthrough) { + if (req->buffer_length) { + buf = malloc(PAD_TO_32BIT(req->buffer_length)); + if (buf == NULL) { + smb2_set_error(smb2, "Failed to allocate set " + "info data buffer"); + return -1; + } + memcpy(buf, req->input_data, req->buffer_length); + smb2_add_iovector(smb2, &pdu->out, buf, req->buffer_length, free); + } + smb2_set_uint32(iov, 4, req->buffer_length); + smb2_set_uint16(iov, 8, req->buffer_offset); + return 0; + } + switch (req->info_type) { case SMB2_0_INFO_FILE: switch (req->file_info_class) { @@ -214,6 +229,52 @@ smb2_cmd_set_info_async(struct smb2_context *smb2, return pdu; } +static int +smb2_encode_set_info_reply(struct smb2_context *smb2, + struct smb2_pdu *pdu, + struct smb2_set_info_request *req) +{ + int len; + uint8_t *buf; + struct smb2_iovec *iov; + + len = SMB2_SET_INFO_REPLY_SIZE & 0xfffffffe; + buf = calloc(len, sizeof(uint8_t)); + if (buf == NULL) { + smb2_set_error(smb2, "Failed to allocate set info buffer"); + return -1; + } + + iov = smb2_add_iovector(smb2, &pdu->out, buf, len, free); + smb2_set_uint16(iov, 0, SMB2_SET_INFO_REPLY_SIZE); + return 0; +} + +struct smb2_pdu * +smb2_cmd_set_info_reply_async(struct smb2_context *smb2, + struct smb2_set_info_request *req, + smb2_command_cb cb, void *cb_data) +{ + struct smb2_pdu *pdu; + + pdu = smb2_allocate_pdu(smb2, SMB2_SET_INFO, cb, cb_data); + if (pdu == NULL) { + return NULL; + } + + if (smb2_encode_set_info_reply(smb2, pdu, req)) { + smb2_free_pdu(smb2, pdu); + return NULL; + } + + if (smb2_pad_to_64bit(smb2, &pdu->out) != 0) { + smb2_free_pdu(smb2, pdu); + return NULL; + } + + return pdu; +} + int smb2_process_set_info_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu) @@ -225,13 +286,49 @@ int smb2_process_set_info_request_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu) { - return 0; + struct smb2_set_info_request *req; + struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; + uint16_t struct_size; + + req = malloc(sizeof(*req)); + if (req == NULL) { + smb2_set_error(smb2, "Failed to allocate set-info request"); + return -1; + } + pdu->payload = req; + + smb2_get_uint16(iov, 0, &struct_size); + if (struct_size != SMB2_SET_INFO_REQUEST_SIZE || + (struct_size & 0xfffe) != iov->len) { + smb2_set_error(smb2, "Unexpected size of set " + "info request. Expected %d, got %d", + SMB2_SET_INFO_REQUEST_SIZE, + (int)iov->len); + return -1; + } + + smb2_get_uint8(iov, 2, &req->info_type); + smb2_get_uint8(iov, 3, &req->file_info_class); + smb2_get_uint32(iov, 4, &req->buffer_length); + smb2_get_uint16(iov, 8, &req->buffer_offset); + smb2_get_uint32(iov, 12, &req->additional_information); + memcpy(req->file_id, iov->buf + 16, SMB2_FD_SIZE); + + return req->buffer_length; } int smb2_process_set_info_request_variable(struct smb2_context *smb2, struct smb2_pdu *pdu) { + struct smb2_set_info_request *req = (struct smb2_set_info_request*)pdu->payload; + struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; + + if (!smb2->passthrough) { + smb2_set_error(smb2, "can not interpret set-info buffers yet"); + return -1; + } + req->input_data = iov->buf; return 0; } diff --git a/lib/smb2-cmd-tree-connect.c b/lib/smb2-cmd-tree-connect.c index 269dd707..0fe34832 100644 --- a/lib/smb2-cmd-tree-connect.c +++ b/lib/smb2-cmd-tree-connect.c @@ -129,10 +129,7 @@ smb2_encode_tree_connect_reply(struct smb2_context *smb2, int len; uint8_t *buf; struct smb2_iovec *iov; - - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - + len = SMB2_TREE_CONNECT_REPLY_SIZE; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { @@ -152,19 +149,27 @@ smb2_encode_tree_connect_reply(struct smb2_context *smb2, return 0; } - struct smb2_pdu * smb2_cmd_tree_connect_reply_async(struct smb2_context *smb2, struct smb2_tree_connect_reply *rep, + uint32_t tree_id, smb2_command_cb cb, void *cb_data) { struct smb2_pdu *pdu; - + static uint32_t s_tree_id = 0xfeedface; + pdu = smb2_allocate_pdu(smb2, SMB2_TREE_CONNECT, cb, cb_data); if (pdu == NULL) { return NULL; } + if (!tree_id) { + /* invent a tree-id and use it while tree connected */ + tree_id = s_tree_id++; + } + smb2_connect_tree_id(smb2, tree_id); + pdu->header.sync.tree_id = smb2_tree_id(smb2); + if (smb2_encode_tree_connect_reply(smb2, pdu, rep)) { smb2_free_pdu(smb2, pdu); return NULL; @@ -193,6 +198,8 @@ smb2_process_tree_connect_fixed(struct smb2_context *smb2, } pdu->payload = rep; + smb2_connect_tree_id(smb2, smb2->hdr.sync.tree_id); + smb2_get_uint16(iov, 0, &struct_size); if (struct_size != SMB2_TREE_CONNECT_REPLY_SIZE || (struct_size & 0xfffe) != iov->len) { @@ -205,15 +212,12 @@ smb2_process_tree_connect_fixed(struct smb2_context *smb2, smb2_get_uint8(iov, 2, &rep->share_type); smb2_get_uint32(iov, 4, &rep->share_flags); - smb2_get_uint32(iov, 4, &rep->capabilities); - smb2_get_uint32(iov, 4, &rep->maximal_access); + smb2_get_uint32(iov, 8, &rep->capabilities); + smb2_get_uint32(iov, 12, &rep->maximal_access); if (!smb2->seal) smb2->seal = !!(rep->share_flags & SMB2_SHAREFLAG_ENCRYPT_DATA); - /* Update tree ID to use for future PDUs */ - smb2->tree_id = smb2->hdr.sync.tree_id; - return 0; } @@ -231,7 +235,7 @@ smb2_process_tree_connect_request_fixed(struct smb2_context *smb2, return -1; } pdu->payload = req; - + smb2_get_uint16(iov, 0, &struct_size); if (struct_size != SMB2_TREE_CONNECT_REQUEST_SIZE || (struct_size & 0xfffe) != iov->len) { diff --git a/lib/smb2-cmd-tree-disconnect.c b/lib/smb2-cmd-tree-disconnect.c index 75ad4449..84001f34 100644 --- a/lib/smb2-cmd-tree-disconnect.c +++ b/lib/smb2-cmd-tree-disconnect.c @@ -109,9 +109,6 @@ smb2_encode_tree_disconnect_reply(struct smb2_context *smb2, int len; struct smb2_iovec *iov; - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - len = SMB2_TREE_DISCONNECT_REPLY_SIZE; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { @@ -123,7 +120,7 @@ smb2_encode_tree_disconnect_reply(struct smb2_context *smb2, iov = smb2_add_iovector(smb2, &pdu->out, buf, len, free); smb2_set_uint16(iov, 0, SMB2_TREE_DISCONNECT_REPLY_SIZE); - + return 0; } @@ -142,12 +139,10 @@ smb2_cmd_tree_disconnect_reply_async(struct smb2_context *smb2, smb2_free_pdu(smb2, pdu); return NULL; } - if (smb2_pad_to_64bit(smb2, &pdu->out) != 0) { smb2_free_pdu(smb2, pdu); return NULL; } - return pdu; } @@ -155,6 +150,7 @@ int smb2_process_tree_disconnect_fixed(struct smb2_context *smb2, struct smb2_pdu *pdu) { + smb2_disconnect_tree_id(smb2, smb2->hdr.sync.tree_id); return 0; } @@ -164,3 +160,4 @@ smb2_process_tree_disconnect_request_fixed(struct smb2_context *smb2, { return 0; } + diff --git a/lib/smb2-cmd-write.c b/lib/smb2-cmd-write.c index b117f194..51618705 100644 --- a/lib/smb2-cmd-write.c +++ b/lib/smb2-cmd-write.c @@ -86,10 +86,31 @@ smb2_encode_write_request(struct smb2_context *smb2, smb2_set_uint16(iov, 42, req->write_channel_info_length); smb2_set_uint32(iov, 44, req->flags); - if (req->write_channel_info_length > 0 || + if (req->write_channel_info_length > 0 && req->write_channel_info != NULL) { - smb2_set_error(smb2, "ChannelInfo not yet implemented"); - return -1; + if (smb2->passthrough) { + req->write_channel_info_offset = + (SMB2_READ_REQUEST_SIZE & 0xfffffffe) + SMB2_HEADER_SIZE; + smb2_set_uint16(iov, 44, req->write_channel_info_offset); + + len = PAD_TO_64BIT(req->write_channel_info_length); + buf = malloc(len); + if (buf == NULL) { + smb2_set_error(smb2, "Failed to allocate write channel context"); + return -1; + } + memcpy(buf, req->write_channel_info, req->write_channel_info_length); + memset(buf + req->write_channel_info_length, 0, + len - req->write_channel_info_length); + iov = smb2_add_iovector(smb2, &pdu->out, + buf, + len, + free); + } + else { + smb2_set_error(smb2, "ChannelInfo not yet implemented"); + return -1; + } } return 0; @@ -137,9 +158,6 @@ smb2_encode_write_reply(struct smb2_context *smb2, uint8_t *buf; struct smb2_iovec *iov; - pdu->header.flags |= SMB2_FLAGS_SERVER_TO_REDIR; - pdu->header.credit_request_response = 1; - len = SMB2_WRITE_REPLY_SIZE; buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { @@ -212,8 +230,8 @@ smb2_process_write_fixed(struct smb2_context *smb2, return 0; } -#define IOVREQ_OFFSET (req->write_channel_info_offset - SMB2_HEADER_SIZE - \ - (SMB2_WRITE_REQUEST_SIZE & 0xfffe)) +#define IOVREQ_OFFSET (req->write_channel_info_length ? (req->write_channel_info_offset - SMB2_HEADER_SIZE - \ + (SMB2_WRITE_REQUEST_SIZE & 0xfffe)):0) int smb2_process_write_request_fixed(struct smb2_context *smb2, @@ -248,16 +266,22 @@ smb2_process_write_request_fixed(struct smb2_context *smb2, smb2_get_uint16(iov, 32, &req->write_channel_info_offset); smb2_get_uint16(iov, 34, &req->write_channel_info_length); - if (req->write_channel_info_length == 0) { - return 0; + if (req->write_channel_info_length) { + if (req->write_channel_info_offset < (SMB2_HEADER_SIZE + (SMB2_WRITE_REQUEST_SIZE & 0xfffe))) { + smb2_set_error(smb2, "channel info overlaps request"); + return -1; + } } - if (req->write_channel_info_offset < SMB2_HEADER_SIZE + (SMB2_WRITE_REQUEST_SIZE & 0xfffe)) { - smb2_set_error(smb2, "channel info overlaps request", ""); - return -1; + if (req->length) { + return IOVREQ_OFFSET + PAD_TO_64BIT(req->write_channel_info_length) + req->length; + } + else if (req->write_channel_info_length) { + return IOVREQ_OFFSET + req->write_channel_info_length; + } + else { + return 0; } - - return IOVREQ_OFFSET + req->write_channel_info_length; } int @@ -266,8 +290,17 @@ smb2_process_write_request_variable(struct smb2_context *smb2, { struct smb2_write_request *req = (struct smb2_write_request*)pdu->payload; struct smb2_iovec *iov = &smb2->in.iov[smb2->in.niov - 1]; - - req->buf = (uint8_t *)iov->buf; + struct smb2_iovec vec = { &iov->buf[IOVREQ_OFFSET], + iov->len, + NULL }; + + req->write_channel_info = (uint8_t *)vec.buf; + req->buf = malloc(PAD_TO_64BIT(req->length)); + if (!req->buf) { + smb2_set_error(smb2, "can not alloc buffer for write data"); + return -1; + } + memcpy(discard_const(req->buf), &vec.buf[PAD_TO_64BIT(req->write_channel_info_length)], req->length); return 0; } diff --git a/lib/smb2-share-enum.c b/lib/smb2-share-enum.c index cba08758..ff37b8ab 100644 --- a/lib/smb2-share-enum.c +++ b/lib/smb2-share-enum.c @@ -132,6 +132,7 @@ share_enum_bind_cb(struct dcerpc_context *dce, int status, int smb2_share_enum_async(struct smb2_context *smb2, + enum SHARE_INFO_enum level, smb2_command_cb cb, void *cb_data) { struct dcerpc_context *dce; @@ -164,9 +165,20 @@ smb2_share_enum_async(struct smb2_context *smb2, sprintf(server, "\\\\%s", smb2->server); nse->se_req.ServerName.utf8 = server; - nse->se_req.ses.Level = 1; - nse->se_req.ses.ShareInfo.Level1.EntriesRead = 0; - nse->se_req.ses.ShareInfo.Level1.Buffer = NULL; + switch (level) { + case SHARE_INFO_0: + nse->se_req.ses.Level = level; + nse->se_req.ses.ShareInfo.Level = level; + nse->se_req.ses.ShareInfo.Level0.EntriesRead = 0; + nse->se_req.ses.ShareInfo.Level0.Buffer = NULL; + break; + case SHARE_INFO_1: + nse->se_req.ses.Level = level; + nse->se_req.ses.ShareInfo.Level = level; + nse->se_req.ses.ShareInfo.Level1.EntriesRead = 0; + nse->se_req.ses.ShareInfo.Level1.Buffer = NULL; + break; + } nse->se_req.PreferedMaximumLength = 0xffffffff; nse->se_req.ResumeHandle = 0; diff --git a/lib/socket.c b/lib/socket.c index 838f4557..ce404b0e 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -213,7 +213,6 @@ smb2_write_to_socket(struct smb2_context *smb2) smb2_set_error(smb2, "trying to write but not connected"); return -1; } - while ((pdu = smb2->outqueue) != NULL) { struct iovec iov[SMB2_MAX_VECTORS] _U_; struct iovec *tmpiov; @@ -305,7 +304,13 @@ smb2_write_to_socket(struct smb2_context *smb2) pdu->next_compound = NULL; smb2->credits -= pdu->header.credit_charge; - SMB2_LIST_ADD_END(&smb2->waitqueue, pdu); + if (!smb2->is_server) { + /* queue requests to correlate replies with */ + SMB2_LIST_ADD_END(&smb2->waitqueue, pdu); + } + else { + smb2->credits += pdu->header.credit_request_response; + } pdu = tmp_pdu; } } @@ -436,9 +441,11 @@ static int smb2_read_data(struct smb2_context *smb2, read_func func, } while (count > 0); + /* put on wait queue to queue_pdu doesn't complain */ + SMB2_LIST_ADD_END(&smb2->waitqueue, pdu); + smb2->in.num_done = 0; pdu->cb(smb2, smb2->hdr.status, pdu->payload, pdu->cb_data); - smb2_free_pdu(smb2, pdu); smb2->pdu = NULL; smb2->pdu = smb2->next_pdu; smb2->next_pdu = NULL; @@ -447,7 +454,9 @@ static int smb2_read_data(struct smb2_context *smb2, read_func func, /* Record the offset for the start of payload data. */ smb2->payload_offset = smb2->in.num_done; - smb2->credits += smb2->hdr.credit_request_response; + if (!smb2_is_server(smb2)) { + smb2->credits += smb2->hdr.credit_request_response; + } if (!smb2_is_server(smb2) && !(smb2->hdr.flags & SMB2_FLAGS_SERVER_TO_REDIR)) { smb2_set_error(smb2, "received non-reply"); @@ -486,6 +495,13 @@ static int smb2_read_data(struct smb2_context *smb2, read_func func, smb2_set_error(smb2, "no pdu for request"); return -ENOMEM; } + /* set the pdu header's message id to the request's id and + * the tree id to the request's tree id + */ + pdu->header.message_id = smb2->hdr.message_id; + if (!(smb2->hdr.flags & SMB2_FLAGS_ASYNC_COMMAND)) { + pdu->header.sync.tree_id = smb2->hdr.sync.tree_id; + } /* if the session is properly opened then we could get * any request from the client, so use the headers command * not the pdus command for the rest of input @@ -637,6 +653,7 @@ static int smb2_read_data(struct smb2_context *smb2, read_func func, */ smb2->in.num_done = 0; if (smb3_decrypt_pdu(smb2)) { + smb2_set_error(smb2, "Failed to decrypyt pdu"); return -1; } /* We are all done now with this PDU. Reset num_done to 0 @@ -670,6 +687,7 @@ static int smb2_read_data(struct smb2_context *smb2, read_func func, if (smb2_calc_signature(smb2, &smb2->in.iov[1 + iov_offset].buf[48], &smb2->in.iov[1 + iov_offset], smb2->in.niov - 1 - iov_offset) < 0) { + smb2_set_error(smb2, "Signature calc failed."); return -1; } if (memcmp(&signature[0], &smb2->in.iov[1 + iov_offset].buf[48], 16)) { @@ -681,8 +699,15 @@ static int smb2_read_data(struct smb2_context *smb2, read_func func, is_chained = smb2->hdr.next_command; - pdu->cb(smb2, smb2->hdr.status, pdu->payload, pdu->cb_data); - smb2_free_pdu(smb2, pdu); + if (smb2->is_server) { + /* queue requests to correlate our replies we send back later */ + SMB2_LIST_ADD_END(&smb2->waitqueue, pdu); + pdu->cb(smb2, smb2->hdr.status, pdu->payload, pdu->cb_data); + } + else { + pdu->cb(smb2, smb2->hdr.status, pdu->payload, pdu->cb_data); + smb2_free_pdu(smb2, pdu); + } smb2->pdu = NULL; if (smb2_is_server(smb2)) { @@ -707,12 +732,10 @@ static int smb2_read_data(struct smb2_context *smb2, read_func func, return 0; } -#include static ssize_t smb2_readv_from_socket(struct smb2_context *smb2, const struct iovec *iov, int iovcnt) { - ssize_t rc = readv(smb2->fd, (struct iovec*) iov, iovcnt); - + ssize_t rc = readv(smb2->fd, (struct iovec*) iov, iovcnt); return rc; } diff --git a/lib/spnego-wrapper.c b/lib/spnego-wrapper.c index ea47e440..f1426c13 100644 --- a/lib/spnego-wrapper.c +++ b/lib/spnego-wrapper.c @@ -40,10 +40,6 @@ #include #endif -#ifdef STDC_HEADERS -#include -#endif - #ifdef HAVE_SYS_TYPES_H #include #endif diff --git a/lib/sync.c b/lib/sync.c index 6ca00041..5920f95e 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -60,12 +60,6 @@ #include "libsmb2-raw.h" #include "libsmb2-private.h" -struct sync_cb_data { - int is_finished; - int status; - void *ptr; -}; - static int wait_for_reply(struct smb2_context *smb2, struct sync_cb_data *cb_data) { @@ -108,7 +102,9 @@ static void connect_cb(struct smb2_context *smb2, int status, struct sync_cb_data *cb_data = private_data; if (cb_data->status == SMB2_STATUS_CANCELLED) { - free(cb_data); + if (cb_data != &smb2->connect_cb_data) { + free(cb_data); + } return; } @@ -127,14 +123,8 @@ int smb2_connect_share(struct smb2_context *smb2, struct sync_cb_data *cb_data; int rc = 0; - cb_data = calloc(1, sizeof(struct sync_cb_data)); - if (cb_data == NULL) { - smb2_set_error(smb2, "Failed to allocate sync_cb_data"); - return -ENOMEM; - } - + cb_data = &smb2->connect_cb_data; rc = smb2_connect_share_async(smb2, server, share, user, connect_cb, cb_data); - if (rc < 0) { goto out; } @@ -147,7 +137,6 @@ int smb2_connect_share(struct smb2_context *smb2, rc = cb_data->status; out: - free(cb_data); return rc; } @@ -160,11 +149,7 @@ int smb2_disconnect_share(struct smb2_context *smb2) struct sync_cb_data *cb_data; int rc = 0; - cb_data = calloc(1, sizeof(struct sync_cb_data)); - if (cb_data == NULL) { - smb2_set_error(smb2, "Failed to allocate sync_cb_data"); - return -ENOMEM; - } + cb_data = &smb2->connect_cb_data; rc = smb2_disconnect_share_async(smb2, connect_cb, cb_data); if (rc < 0) { @@ -179,7 +164,6 @@ int smb2_disconnect_share(struct smb2_context *smb2) rc = cb_data->status; out: - free(cb_data); return rc; } @@ -193,7 +177,6 @@ static void opendir_cb(struct smb2_context *smb2, int status, struct sync_cb_data *cb_data = private_data; if (cb_data->status == SMB2_STATUS_CANCELLED) { - free(cb_data); return; } @@ -212,6 +195,7 @@ struct smb2dir *smb2_opendir(struct smb2_context *smb2, const char *path) return NULL; } + /* smb2dir takes wnership of cb_data on success */ if (smb2_opendir_async(smb2, path, opendir_cb, cb_data) != 0) { smb2_set_error(smb2, "smb2_opendir_async failed"); @@ -225,7 +209,6 @@ struct smb2dir *smb2_opendir(struct smb2_context *smb2, const char *path) } ptr = cb_data->ptr; - free(cb_data); return ptr; } diff --git a/module.modulemap b/module.modulemap deleted file mode 100644 index 8607d579..00000000 --- a/module.modulemap +++ /dev/null @@ -1,21 +0,0 @@ -module SMB2 [extern_c] { - module LibSMB2 { - header "include/apple/config.h" - header "include/smb2/libsmb2.h" - header "include/smb2/libsmb2-dcerpc.h" - header "include/smb2/libsmb2-dcerpc-lsa.h" - header "include/smb2/libsmb2-dcerpc-srvsvc.h" - header "include/smb2/smb2.h" - header "include/smb2/smb2-errors.h" - } - - module Internal { - header "include/libsmb2-private.h" - } - - explicit module Raw { - header "include/smb2/libsmb2-raw.h" - } - - export SMB2 -} diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 00000000..96396cba --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,32 @@ +AM_CPPFLAGS = -I${srcdir}/../include -I${srcdir}/../include/smb2 \ + "-D_U_=__attribute__((unused))" \ + "-D_R_(A,B)=__attribute__((format(printf,A,B)))" +AM_CFLAGS = $(WARN_CFLAGS) +LDADD = ../lib/libsmb2.la + +noinst_PROGRAMS = prog_ls prog_mkdir prog_rmdir + +EXTRA_PROGRAMS = ld_sockerr +CLEANFILES = ld_sockerr.o ld_sockerr.so + +ld_sockerr_SOURCES = ld_sockerr.c +ld_sockerr_CFLAGS = $(AM_CFLAGS) -fPIC + +bin_SCRIPTS = ld_sockerr.so + +ld_sockerr.o: ld_sockerr-ld_sockerr.o + $(LIBTOOL) --mode=link $(CC) -o $@ $^ + +ld_sockerr.so: ld_sockerr.o + $(CC) -shared -o ld_sockerr.so ld_sockerr.o -ldl + +T = `ls test_*.sh` + +test: $(noinst_PROGRAMS) $(bin_SCRIPTS) + for TEST in $(T); do \ + echo "Running $$TEST"; \ + echo "--------------"; \ + sh $$TEST || exit 1; \ + echo "--------------"; \ + echo; \ + done diff --git a/tests/README b/tests/README new file mode 100644 index 00000000..ea0ea08e --- /dev/null +++ b/tests/README @@ -0,0 +1,28 @@ +To run the test you need an SMB server that exports a share and +two configurations files: + +1, ./tests/NTLM +=============== +This file contains the username to password mapping you need to authenticate +to the server with NTLM. + +Example: +$ cat tests/NTLM +win16-1:Administrator:mypassword +$ + +2, tests/setup.local +==================== +This file sets two environment variables the tests need, NTLM_USER_FILE +and TESTURL. + +Example: +$ cat tests/setup.local +TESTURL=smb://Administrator@win16-1/Share +NTLM_USER_FILE=`pwd`/NTLM +$ + +A good idea is to use a special share/subdirectory on the server +so that the tests do not overwrite/corrupt/delete important files. +Create a dedicated share on the server that is only used for testing +and be prepared that any data in this share can be randomly deleted. diff --git a/tests/functions.sh b/tests/functions.sh new file mode 100755 index 00000000..d8be8f14 --- /dev/null +++ b/tests/functions.sh @@ -0,0 +1,11 @@ +. ./setup.local + +success() { + echo "[OK]" +} + +failure() { + echo "[FAILED]" + exit 1 +} + diff --git a/tests/ld_sockerr.c b/tests/ld_sockerr.c new file mode 100644 index 00000000..4142f503 --- /dev/null +++ b/tests/ld_sockerr.c @@ -0,0 +1,61 @@ +/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ +/* + Copyright (C) 2024 by Ronnie Sahlberg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +int readv_close = -1; + +int (*real_readv)(int fd, const struct iovec *iov, int iovcnt); + +ssize_t readv(int fd, const struct iovec *iov, int iovcnt) +{ + static int call_idx = 0; + + call_idx++; + + if (call_idx == readv_close) { + /* write some garbage */ + write(fd, &call_idx, sizeof(call_idx)); + errno = EBADF; + return -1; + } + return real_readv(fd, iov, iovcnt); +} + + +static void __attribute__((constructor)) +_init(void) +{ + /* Close the socket at this call to readv */ + if (getenv("READV_CLOSE") != NULL) { + readv_close = atoi(getenv("READV_CLOSE")); + } + + real_readv = dlsym(RTLD_NEXT, "readv"); +} diff --git a/tests/prog_ls.c b/tests/prog_ls.c new file mode 100644 index 00000000..3d973346 --- /dev/null +++ b/tests/prog_ls.c @@ -0,0 +1,126 @@ +/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ +/* + Copyright (C) 2016 by Ronnie Sahlberg + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define _GNU_SOURCE + +#include +#if !defined(__amigaos4__) && !defined(__AMIGA__) && !defined(__AROS__) +#include +#endif +#include +#include +#include +#include +#include + +#include "smb2.h" +#include "libsmb2.h" +#include "libsmb2-raw.h" + +#ifdef __AROS__ +#include "asprintf.h" +#endif + +int usage(void) +{ + fprintf(stderr, "Usage:\n" + "smb2-ls-sync \n\n" + "URL format: " + "smb://[@][:]//\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct smb2_context *smb2; + struct smb2_url *url; + struct smb2dir *dir; + struct smb2dirent *ent; + char *link; + int rc = 0; + + if (argc < 2) { + usage(); + } + + smb2 = smb2_init_context(); + if (smb2 == NULL) { + fprintf(stderr, "Failed to init context\n"); + exit(0); + } + + url = smb2_parse_url(smb2, argv[1]); + if (url == NULL) { + fprintf(stderr, "Failed to parse url: %s\n", + smb2_get_error(smb2)); + exit(0); + } + + smb2_set_security_mode(smb2, SMB2_NEGOTIATE_SIGNING_ENABLED); + if (smb2_connect_share(smb2, url->server, url->share, url->user) < 0) { + printf("smb2_connect_share failed. %s\n", smb2_get_error(smb2)); + goto out_context; + } + + dir = smb2_opendir(smb2, url->path); + if (dir == NULL) { + printf("smb2_opendir failed. %s\n", smb2_get_error(smb2)); + goto out_disconnect; + } + + while ((ent = smb2_readdir(smb2, dir))) { + char *type; + time_t t; + + t = (time_t)ent->st.smb2_mtime; + switch (ent->st.smb2_type) { + case SMB2_TYPE_LINK: + type = "LINK"; + break; + case SMB2_TYPE_FILE: + type = "FILE"; + break; + case SMB2_TYPE_DIRECTORY: + type = "DIRECTORY"; + break; + default: + type = "unknown"; + break; + } + printf("%-20s %-9s %15"PRIu64" %s", ent->name, type, ent->st.smb2_size, asctime(localtime(&t))); + if (ent->st.smb2_type == SMB2_TYPE_LINK) { + char buf[256]; + + if (url->path && url->path[0]) { + asprintf(&link, "%s/%s", url->path, ent->name); + } else { + asprintf(&link, "%s", ent->name); + } + if (smb2_readlink(smb2, link, buf, 256) == 0) { + printf(" -> [%s]\n", buf); + } else { + printf(" readlink failed\n"); + } + free(link); + } + } + + smb2_closedir(smb2, dir); + out_disconnect: + smb2_disconnect_share(smb2); + out_context: + smb2_destroy_url(url); + smb2_destroy_context(smb2); + + return rc; +} diff --git a/tests/prog_mkdir.c b/tests/prog_mkdir.c new file mode 100644 index 00000000..ebf2585c --- /dev/null +++ b/tests/prog_mkdir.c @@ -0,0 +1,85 @@ +/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ +/* + Copyright (C) 2016 by Ronnie Sahlberg + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define _GNU_SOURCE + +#include +#if !defined(__amigaos4__) && !defined(__AMIGA__) && !defined(__AROS__) +#include +#endif +#include +#include +#include +#include +#include + +#include "smb2.h" +#include "libsmb2.h" +#include "libsmb2-raw.h" + +#ifdef __AROS__ +#include "asprintf.h" +#endif + +int usage(void) +{ + fprintf(stderr, "Usage:\n" + "prog_mkdir \n\n" + "URL format: " + "smb://[@][:]//\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct smb2_context *smb2; + struct smb2_url *url; + int rc = 0; + + if (argc < 2) { + usage(); + } + + smb2 = smb2_init_context(); + if (smb2 == NULL) { + fprintf(stderr, "Failed to init context\n"); + exit(0); + } + + url = smb2_parse_url(smb2, argv[1]); + if (url == NULL) { + fprintf(stderr, "Failed to parse url: %s\n", + smb2_get_error(smb2)); + exit(0); + } + + smb2_set_security_mode(smb2, SMB2_NEGOTIATE_SIGNING_ENABLED); + if (smb2_connect_share(smb2, url->server, url->share, url->user) < 0) { + printf("smb2_connect_share failed. %s\n", smb2_get_error(smb2)); + goto out_context; + } + + smb2_rmdir(smb2, url->path); + if (smb2_mkdir(smb2, url->path)) { + printf("smb2_mkdir failed. %s\n", smb2_get_error(smb2)); + goto out_disconnect; + } + + out_disconnect: + smb2_disconnect_share(smb2); + out_context: + smb2_destroy_url(url); + smb2_destroy_context(smb2); + + return rc; +} diff --git a/tests/prog_rmdir.c b/tests/prog_rmdir.c new file mode 100644 index 00000000..9e76f126 --- /dev/null +++ b/tests/prog_rmdir.c @@ -0,0 +1,84 @@ +/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ +/* + Copyright (C) 2016 by Ronnie Sahlberg + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define _GNU_SOURCE + +#include +#if !defined(__amigaos4__) && !defined(__AMIGA__) && !defined(__AROS__) +#include +#endif +#include +#include +#include +#include +#include + +#include "smb2.h" +#include "libsmb2.h" +#include "libsmb2-raw.h" + +#ifdef __AROS__ +#include "asprintf.h" +#endif + +int usage(void) +{ + fprintf(stderr, "Usage:\n" + "prog_rmdir \n\n" + "URL format: " + "smb://[@][:]//\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct smb2_context *smb2; + struct smb2_url *url; + int rc = 0; + + if (argc < 2) { + usage(); + } + + smb2 = smb2_init_context(); + if (smb2 == NULL) { + fprintf(stderr, "Failed to init context\n"); + exit(0); + } + + url = smb2_parse_url(smb2, argv[1]); + if (url == NULL) { + fprintf(stderr, "Failed to parse url: %s\n", + smb2_get_error(smb2)); + exit(0); + } + + smb2_set_security_mode(smb2, SMB2_NEGOTIATE_SIGNING_ENABLED); + if (smb2_connect_share(smb2, url->server, url->share, url->user) < 0) { + printf("smb2_connect_share failed. %s\n", smb2_get_error(smb2)); + goto out_context; + } + + if (smb2_rmdir(smb2, url->path)) { + printf("smb2_rmdir failed. %s\n", smb2_get_error(smb2)); + goto out_disconnect; + } + + out_disconnect: + smb2_disconnect_share(smb2); + out_context: + smb2_destroy_url(url); + smb2_destroy_context(smb2); + + return rc; +} diff --git a/tests/test_0100_ls_basic.sh b/tests/test_0100_ls_basic.sh new file mode 100755 index 00000000..19c3c2b9 --- /dev/null +++ b/tests/test_0100_ls_basic.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +. ./functions.sh + +echo "basic ls test" + +echo -n "Testing prog_ls on root of share ... " +./prog_ls "${TESTURL}/" > /dev/null || failure +success + +exit 0 diff --git a/tests/test_0101_ls_basic_valgrind.sh b/tests/test_0101_ls_basic_valgrind.sh new file mode 100755 index 00000000..2e470ebe --- /dev/null +++ b/tests/test_0101_ls_basic_valgrind.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +. ./functions.sh + +echo "basic ls test with valgrind" + +echo -n "Testing prog_ls on root of share ... " +libtool --mode=execute valgrind --leak-check=full --error-exitcode=1 ./prog_ls "${TESTURL}/" >/dev/null 2>&1 || failure +success + +exit 0 diff --git a/tests/test_0102_ls_basic_socket_error.sh b/tests/test_0102_ls_basic_socket_error.sh new file mode 100755 index 00000000..e83449c2 --- /dev/null +++ b/tests/test_0102_ls_basic_socket_error.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +. ./functions.sh + +echo "basic ls test with valgrind and session errors" + +echo "Testing prog_ls on root of share with socket failures " +for IDX in `seq 1 43`; do + echo -n "Testing prog_ls on root of share with socket failure at #${IDX} ..." + READV_CLOSE=${IDX} LD_PRELOAD=./ld_sockerr.so libtool --mode=execute valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 ./prog_ls "${TESTURL}/" >/dev/null 2>valgrind.out || failure + success +done + +exit 0 diff --git a/tests/test_0200_mkdir.sh b/tests/test_0200_mkdir.sh new file mode 100755 index 00000000..2f2137af --- /dev/null +++ b/tests/test_0200_mkdir.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +. ./functions.sh + +echo "basic mkdir test" + +echo -n "Testing prog_mkdir on root of share ... " +./prog_mkdir "${TESTURL}/testdir" > /dev/null || failure +success + +echo -n "Testing prog_rmdir on root of share ... " +./prog_rmdir "${TESTURL}/testdir" > /dev/null || failure +success + +exit 0