wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,747 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
Copyright (C) Stefan Metzmacher 2006
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "librpc/gen_ndr/ndr_misc.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
|
||||
#define BLOB_CHECK(cmd) do { \
|
||||
NTSTATUS _status; \
|
||||
_status = cmd; \
|
||||
NT_STATUS_NOT_OK_RETURN(_status); \
|
||||
} while (0)
|
||||
|
||||
#define BLOB_CHECK_MIN_SIZE(blob, size) do { \
|
||||
if ((blob)->length < (size)) { \
|
||||
return NT_STATUS_INFO_LENGTH_MISMATCH; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* align the end of the blob on an 8 byte boundary */
|
||||
#define BLOB_ALIGN(blob, alignment) do { \
|
||||
if ((blob)->length & ((alignment)-1)) { \
|
||||
uint8_t _pad = (alignment) - ((blob)->length & ((alignment)-1)); \
|
||||
BLOB_CHECK(smbsrv_blob_fill_data(blob, blob, (blob)->length+_pad)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* grow the data size of a trans2 reply */
|
||||
NTSTATUS smbsrv_blob_grow_data(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *blob,
|
||||
uint32_t new_size)
|
||||
{
|
||||
if (new_size > blob->length) {
|
||||
uint8_t *p;
|
||||
p = talloc_realloc(mem_ctx, blob->data, uint8_t, new_size);
|
||||
NT_STATUS_HAVE_NO_MEMORY(p);
|
||||
blob->data = p;
|
||||
}
|
||||
blob->length = new_size;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* grow the data, zero filling any new bytes */
|
||||
NTSTATUS smbsrv_blob_fill_data(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *blob,
|
||||
uint32_t new_size)
|
||||
{
|
||||
uint32_t old_size = blob->length;
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, new_size));
|
||||
if (new_size > old_size) {
|
||||
memset(blob->data + old_size, 0, new_size - old_size);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
pull a string from a blob in a trans2 request
|
||||
*/
|
||||
size_t smbsrv_blob_pull_string(struct smbsrv_request *req,
|
||||
const DATA_BLOB *blob,
|
||||
uint16_t offset,
|
||||
const char **str,
|
||||
int flags)
|
||||
{
|
||||
*str = NULL;
|
||||
/* we use STR_NO_RANGE_CHECK because the params are allocated
|
||||
separately in a DATA_BLOB, so we need to do our own range
|
||||
checking */
|
||||
if (offset >= blob->length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return req_pull_string(req, str,
|
||||
blob->data + offset,
|
||||
blob->length - offset,
|
||||
STR_NO_RANGE_CHECK | flags);
|
||||
}
|
||||
|
||||
/*
|
||||
push a string into the data section of a trans2 request
|
||||
return the number of bytes consumed in the output
|
||||
*/
|
||||
size_t smbsrv_blob_push_string(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *blob,
|
||||
uint32_t len_offset,
|
||||
uint32_t offset,
|
||||
const char *str,
|
||||
int dest_len,
|
||||
int default_flags,
|
||||
int flags)
|
||||
{
|
||||
int alignment = 0, ret = 0, pkt_len;
|
||||
|
||||
/* we use STR_NO_RANGE_CHECK because the params are allocated
|
||||
separately in a DATA_BLOB, so we need to do our own range
|
||||
checking */
|
||||
if (!str || offset >= blob->length) {
|
||||
if (flags & STR_LEN8BIT) {
|
||||
SCVAL(blob->data, len_offset, 0);
|
||||
} else {
|
||||
SIVAL(blob->data, len_offset, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
flags |= STR_NO_RANGE_CHECK;
|
||||
|
||||
if (dest_len == -1 || (dest_len > blob->length - offset)) {
|
||||
dest_len = blob->length - offset;
|
||||
}
|
||||
|
||||
if (!(flags & (STR_ASCII|STR_UNICODE))) {
|
||||
flags |= default_flags;
|
||||
}
|
||||
|
||||
if ((offset&1) && (flags & STR_UNICODE) && !(flags & STR_NOALIGN)) {
|
||||
alignment = 1;
|
||||
if (dest_len > 0) {
|
||||
SCVAL(blob->data + offset, 0, 0);
|
||||
ret = push_string(blob->data + offset + 1, str, dest_len-1, flags);
|
||||
}
|
||||
} else {
|
||||
ret = push_string(blob->data + offset, str, dest_len, flags);
|
||||
}
|
||||
|
||||
/* sometimes the string needs to be terminated, but the length
|
||||
on the wire must not include the termination! */
|
||||
pkt_len = ret;
|
||||
|
||||
if ((flags & STR_LEN_NOTERM) && (flags & STR_TERMINATE)) {
|
||||
if ((flags & STR_UNICODE) && ret >= 2) {
|
||||
pkt_len = ret-2;
|
||||
}
|
||||
if ((flags & STR_ASCII) && ret >= 1) {
|
||||
pkt_len = ret-1;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & STR_LEN8BIT) {
|
||||
SCVAL(blob->data, len_offset, pkt_len);
|
||||
} else {
|
||||
SIVAL(blob->data, len_offset, pkt_len);
|
||||
}
|
||||
|
||||
return ret + alignment;
|
||||
}
|
||||
|
||||
/*
|
||||
append a string to the data section of a trans2 reply
|
||||
len_offset points to the place in the packet where the length field
|
||||
should go
|
||||
*/
|
||||
NTSTATUS smbsrv_blob_append_string(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *blob,
|
||||
const char *str,
|
||||
uint_t len_offset,
|
||||
int default_flags,
|
||||
int flags)
|
||||
{
|
||||
size_t ret;
|
||||
uint32_t offset;
|
||||
const int max_bytes_per_char = 3;
|
||||
|
||||
offset = blob->length;
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, offset + (2+strlen_m(str))*max_bytes_per_char));
|
||||
ret = smbsrv_blob_push_string(mem_ctx, blob, len_offset, offset, str, -1, default_flags, flags);
|
||||
if (ret < 0) {
|
||||
return NT_STATUS_FOOBAR;
|
||||
}
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, offset + ret));
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS smbsrv_push_passthru_fsinfo(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *blob,
|
||||
enum smb_fsinfo_level level,
|
||||
union smb_fsinfo *fsinfo,
|
||||
int default_str_flags)
|
||||
{
|
||||
uint_t i;
|
||||
DATA_BLOB guid_blob;
|
||||
|
||||
switch (level) {
|
||||
case RAW_QFS_VOLUME_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 18));
|
||||
|
||||
push_nttime(blob->data, 0, fsinfo->volume_info.out.create_time);
|
||||
SIVAL(blob->data, 8, fsinfo->volume_info.out.serial_number);
|
||||
SSVAL(blob->data, 16, 0); /* padding */
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
|
||||
fsinfo->volume_info.out.volume_name.s,
|
||||
12, default_str_flags,
|
||||
STR_UNICODE));
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_SIZE_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 24));
|
||||
|
||||
SBVAL(blob->data, 0, fsinfo->size_info.out.total_alloc_units);
|
||||
SBVAL(blob->data, 8, fsinfo->size_info.out.avail_alloc_units);
|
||||
SIVAL(blob->data, 16, fsinfo->size_info.out.sectors_per_unit);
|
||||
SIVAL(blob->data, 20, fsinfo->size_info.out.bytes_per_sector);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_DEVICE_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 8));
|
||||
|
||||
SIVAL(blob->data, 0, fsinfo->device_info.out.device_type);
|
||||
SIVAL(blob->data, 4, fsinfo->device_info.out.characteristics);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_ATTRIBUTE_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 12));
|
||||
|
||||
SIVAL(blob->data, 0, fsinfo->attribute_info.out.fs_attr);
|
||||
SIVAL(blob->data, 4, fsinfo->attribute_info.out.max_file_component_length);
|
||||
/* this must not be null terminated or win98 gets
|
||||
confused! also note that w2k3 returns this as
|
||||
unicode even when ascii is negotiated */
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
|
||||
fsinfo->attribute_info.out.fs_type.s,
|
||||
8, default_str_flags,
|
||||
STR_UNICODE));
|
||||
return NT_STATUS_OK;
|
||||
|
||||
|
||||
case RAW_QFS_QUOTA_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 48));
|
||||
|
||||
SBVAL(blob->data, 0, fsinfo->quota_information.out.unknown[0]);
|
||||
SBVAL(blob->data, 8, fsinfo->quota_information.out.unknown[1]);
|
||||
SBVAL(blob->data, 16, fsinfo->quota_information.out.unknown[2]);
|
||||
SBVAL(blob->data, 24, fsinfo->quota_information.out.quota_soft);
|
||||
SBVAL(blob->data, 32, fsinfo->quota_information.out.quota_hard);
|
||||
SBVAL(blob->data, 40, fsinfo->quota_information.out.quota_flags);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
|
||||
case RAW_QFS_FULL_SIZE_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 32));
|
||||
|
||||
SBVAL(blob->data, 0, fsinfo->full_size_information.out.total_alloc_units);
|
||||
SBVAL(blob->data, 8, fsinfo->full_size_information.out.call_avail_alloc_units);
|
||||
SBVAL(blob->data, 16, fsinfo->full_size_information.out.actual_avail_alloc_units);
|
||||
SIVAL(blob->data, 24, fsinfo->full_size_information.out.sectors_per_unit);
|
||||
SIVAL(blob->data, 28, fsinfo->full_size_information.out.bytes_per_sector);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_OBJECTID_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 64));
|
||||
|
||||
BLOB_CHECK(ndr_push_struct_blob(&guid_blob, mem_ctx,
|
||||
&fsinfo->objectid_information.out.guid,
|
||||
(ndr_push_flags_fn_t)ndr_push_GUID));
|
||||
memcpy(blob->data, guid_blob.data, guid_blob.length);
|
||||
|
||||
for (i=0;i<6;i++) {
|
||||
SBVAL(blob->data, 16 + 8*i, fsinfo->objectid_information.out.unknown[i]);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
default:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
NTSTATUS smbsrv_push_passthru_fileinfo(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *blob,
|
||||
enum smb_fileinfo_level level,
|
||||
union smb_fileinfo *st,
|
||||
int default_str_flags)
|
||||
{
|
||||
uint_t i;
|
||||
size_t list_size;
|
||||
|
||||
switch (level) {
|
||||
case RAW_FILEINFO_BASIC_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 40));
|
||||
|
||||
push_nttime(blob->data, 0, st->basic_info.out.create_time);
|
||||
push_nttime(blob->data, 8, st->basic_info.out.access_time);
|
||||
push_nttime(blob->data, 16, st->basic_info.out.write_time);
|
||||
push_nttime(blob->data, 24, st->basic_info.out.change_time);
|
||||
SIVAL(blob->data, 32, st->basic_info.out.attrib);
|
||||
SIVAL(blob->data, 36, 0); /* padding */
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 56));
|
||||
|
||||
push_nttime(blob->data, 0, st->network_open_information.out.create_time);
|
||||
push_nttime(blob->data, 8, st->network_open_information.out.access_time);
|
||||
push_nttime(blob->data, 16, st->network_open_information.out.write_time);
|
||||
push_nttime(blob->data, 24, st->network_open_information.out.change_time);
|
||||
SBVAL(blob->data, 32, st->network_open_information.out.alloc_size);
|
||||
SBVAL(blob->data, 40, st->network_open_information.out.size);
|
||||
SIVAL(blob->data, 48, st->network_open_information.out.attrib);
|
||||
SIVAL(blob->data, 52, 0); /* padding */
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_STANDARD_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 24));
|
||||
|
||||
SBVAL(blob->data, 0, st->standard_info.out.alloc_size);
|
||||
SBVAL(blob->data, 8, st->standard_info.out.size);
|
||||
SIVAL(blob->data, 16, st->standard_info.out.nlink);
|
||||
SCVAL(blob->data, 20, st->standard_info.out.delete_pending);
|
||||
SCVAL(blob->data, 21, st->standard_info.out.directory);
|
||||
SSVAL(blob->data, 22, 0); /* padding */
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 8));
|
||||
|
||||
SIVAL(blob->data, 0, st->attribute_tag_information.out.attrib);
|
||||
SIVAL(blob->data, 4, st->attribute_tag_information.out.reparse_tag);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_EA_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
|
||||
|
||||
SIVAL(blob->data, 0, st->ea_info.out.ea_size);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_MODE_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
|
||||
|
||||
SIVAL(blob->data, 0, st->mode_information.out.mode);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_ALIGNMENT_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
|
||||
|
||||
SIVAL(blob->data, 0,
|
||||
st->alignment_information.out.alignment_requirement);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_ACCESS_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
|
||||
|
||||
SIVAL(blob->data, 0, st->access_information.out.access_flags);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_POSITION_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 8));
|
||||
|
||||
SBVAL(blob->data, 0, st->position_information.out.position);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_COMPRESSION_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 16));
|
||||
|
||||
SBVAL(blob->data, 0, st->compression_info.out.compressed_size);
|
||||
SSVAL(blob->data, 8, st->compression_info.out.format);
|
||||
SCVAL(blob->data, 10, st->compression_info.out.unit_shift);
|
||||
SCVAL(blob->data, 11, st->compression_info.out.chunk_shift);
|
||||
SCVAL(blob->data, 12, st->compression_info.out.cluster_shift);
|
||||
SSVAL(blob->data, 13, 0); /* 3 bytes padding */
|
||||
SCVAL(blob->data, 15, 0);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_INTERNAL_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 8));
|
||||
|
||||
SBVAL(blob->data, 0, st->internal_information.out.file_id);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_ALL_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 72));
|
||||
|
||||
push_nttime(blob->data, 0, st->all_info.out.create_time);
|
||||
push_nttime(blob->data, 8, st->all_info.out.access_time);
|
||||
push_nttime(blob->data, 16, st->all_info.out.write_time);
|
||||
push_nttime(blob->data, 24, st->all_info.out.change_time);
|
||||
SIVAL(blob->data, 32, st->all_info.out.attrib);
|
||||
SIVAL(blob->data, 36, 0); /* padding */
|
||||
SBVAL(blob->data, 40, st->all_info.out.alloc_size);
|
||||
SBVAL(blob->data, 48, st->all_info.out.size);
|
||||
SIVAL(blob->data, 56, st->all_info.out.nlink);
|
||||
SCVAL(blob->data, 60, st->all_info.out.delete_pending);
|
||||
SCVAL(blob->data, 61, st->all_info.out.directory);
|
||||
SSVAL(blob->data, 62, 0); /* padding */
|
||||
SIVAL(blob->data, 64, st->all_info.out.ea_size);
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
|
||||
st->all_info.out.fname.s,
|
||||
68, default_str_flags,
|
||||
STR_UNICODE));
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_NAME_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
|
||||
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
|
||||
st->name_info.out.fname.s,
|
||||
0, default_str_flags,
|
||||
STR_UNICODE));
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_ALT_NAME_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
|
||||
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
|
||||
st->alt_name_info.out.fname.s,
|
||||
0, default_str_flags,
|
||||
STR_UNICODE));
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_STREAM_INFORMATION:
|
||||
for (i=0;i<st->stream_info.out.num_streams;i++) {
|
||||
uint32_t data_size = blob->length;
|
||||
uint8_t *data;
|
||||
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, data_size + 24));
|
||||
data = blob->data + data_size;
|
||||
SBVAL(data, 8, st->stream_info.out.streams[i].size);
|
||||
SBVAL(data, 16, st->stream_info.out.streams[i].alloc_size);
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
|
||||
st->stream_info.out.streams[i].stream_name.s,
|
||||
data_size + 4, default_str_flags,
|
||||
STR_UNICODE));
|
||||
if (i == st->stream_info.out.num_streams - 1) {
|
||||
SIVAL(blob->data, data_size, 0);
|
||||
} else {
|
||||
BLOB_CHECK(smbsrv_blob_fill_data(mem_ctx, blob, (blob->length+7)&~7));
|
||||
SIVAL(blob->data, data_size,
|
||||
blob->length - data_size);
|
||||
}
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_SMB2_ALL_EAS:
|
||||
/* if no eas are returned the backend should
|
||||
* have returned NO_EAS_ON_FILE or NO_MORE_EAS
|
||||
*
|
||||
* so it's a programmer error if num_eas == 0
|
||||
*/
|
||||
if (st->all_eas.out.num_eas == 0) {
|
||||
smb_panic("0 eas for SMB2_ALL_EAS - programmer error in ntvfs backend");
|
||||
}
|
||||
|
||||
list_size = ea_list_size_chained(st->all_eas.out.num_eas,
|
||||
st->all_eas.out.eas);
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, list_size));
|
||||
|
||||
ea_put_list_chained(blob->data,
|
||||
st->all_eas.out.num_eas,
|
||||
st->all_eas.out.eas);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_SMB2_ALL_INFORMATION:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 0x64));
|
||||
|
||||
push_nttime(blob->data, 0x00, st->all_info2.out.create_time);
|
||||
push_nttime(blob->data, 0x08, st->all_info2.out.access_time);
|
||||
push_nttime(blob->data, 0x10, st->all_info2.out.write_time);
|
||||
push_nttime(blob->data, 0x18, st->all_info2.out.change_time);
|
||||
SIVAL(blob->data, 0x20, st->all_info2.out.attrib);
|
||||
SIVAL(blob->data, 0x24, st->all_info2.out.unknown1);
|
||||
SBVAL(blob->data, 0x28, st->all_info2.out.alloc_size);
|
||||
SBVAL(blob->data, 0x30, st->all_info2.out.size);
|
||||
SIVAL(blob->data, 0x38, st->all_info2.out.nlink);
|
||||
SCVAL(blob->data, 0x3C, st->all_info2.out.delete_pending);
|
||||
SCVAL(blob->data, 0x3D, st->all_info2.out.directory);
|
||||
SSVAL(blob->data, 0x3E, 0); /* padding */
|
||||
SBVAL(blob->data, 0x40, st->all_info2.out.file_id);
|
||||
SIVAL(blob->data, 0x48, st->all_info2.out.ea_size);
|
||||
SIVAL(blob->data, 0x4C, st->all_info2.out.access_mask);
|
||||
SBVAL(blob->data, 0x50, st->all_info2.out.position);
|
||||
SBVAL(blob->data, 0x58, st->all_info2.out.mode);
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
|
||||
st->all_info2.out.fname.s,
|
||||
0x60, default_str_flags,
|
||||
STR_UNICODE));
|
||||
return NT_STATUS_OK;
|
||||
|
||||
default:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
NTSTATUS smbsrv_pull_passthru_sfileinfo(TALLOC_CTX *mem_ctx,
|
||||
enum smb_setfileinfo_level level,
|
||||
union smb_setfileinfo *st,
|
||||
const DATA_BLOB *blob,
|
||||
int default_str_flags,
|
||||
struct smbsrv_request *req)
|
||||
{
|
||||
uint32_t len;
|
||||
DATA_BLOB str_blob;
|
||||
|
||||
switch (level) {
|
||||
case SMB_SFILEINFO_BASIC_INFORMATION:
|
||||
BLOB_CHECK_MIN_SIZE(blob, 36);
|
||||
|
||||
st->basic_info.in.create_time = pull_nttime(blob->data, 0);
|
||||
st->basic_info.in.access_time = pull_nttime(blob->data, 8);
|
||||
st->basic_info.in.write_time = pull_nttime(blob->data, 16);
|
||||
st->basic_info.in.change_time = pull_nttime(blob->data, 24);
|
||||
st->basic_info.in.attrib = IVAL(blob->data, 32);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case SMB_SFILEINFO_DISPOSITION_INFORMATION:
|
||||
BLOB_CHECK_MIN_SIZE(blob, 1);
|
||||
|
||||
st->disposition_info.in.delete_on_close = CVAL(blob->data, 0);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case SMB_SFILEINFO_ALLOCATION_INFORMATION:
|
||||
BLOB_CHECK_MIN_SIZE(blob, 8);
|
||||
|
||||
st->allocation_info.in.alloc_size = BVAL(blob->data, 0);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
|
||||
BLOB_CHECK_MIN_SIZE(blob, 8);
|
||||
|
||||
st->end_of_file_info.in.size = BVAL(blob->data, 0);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SFILEINFO_RENAME_INFORMATION:
|
||||
if (!req) {
|
||||
/*
|
||||
* TODO: get rid of smbsrv_request argument of
|
||||
* smbsrv_blob_pull_string()
|
||||
*/
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
BLOB_CHECK_MIN_SIZE(blob, 12);
|
||||
|
||||
st->rename_information.in.overwrite = CVAL(blob->data, 0);
|
||||
st->rename_information.in.root_fid = IVAL(blob->data, 4);
|
||||
len = IVAL(blob->data, 8);
|
||||
str_blob.data = blob->data+12;
|
||||
str_blob.length = MIN(blob->length, len);
|
||||
smbsrv_blob_pull_string(req, &str_blob, 0,
|
||||
&st->rename_information.in.new_name,
|
||||
STR_UNICODE);
|
||||
if (st->rename_information.in.new_name == NULL) {
|
||||
return NT_STATUS_FOOBAR;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SFILEINFO_POSITION_INFORMATION:
|
||||
BLOB_CHECK_MIN_SIZE(blob, 8);
|
||||
|
||||
st->position_information.in.position = BVAL(blob->data, 0);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SFILEINFO_MODE_INFORMATION:
|
||||
BLOB_CHECK_MIN_SIZE(blob, 4);
|
||||
|
||||
st->mode_information.in.mode = IVAL(blob->data, 0);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
default:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
/*
|
||||
fill a single entry in a trans2 find reply
|
||||
*/
|
||||
NTSTATUS smbsrv_push_passthru_search(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *blob,
|
||||
enum smb_search_data_level level,
|
||||
union smb_search_data *file,
|
||||
int default_str_flags)
|
||||
{
|
||||
uint8_t *data;
|
||||
uint_t ofs = blob->length;
|
||||
|
||||
switch (level) {
|
||||
case RAW_SEARCH_DATA_DIRECTORY_INFO:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 64));
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 4, file->directory_info.file_index);
|
||||
push_nttime(data, 8, file->directory_info.create_time);
|
||||
push_nttime(data, 16, file->directory_info.access_time);
|
||||
push_nttime(data, 24, file->directory_info.write_time);
|
||||
push_nttime(data, 32, file->directory_info.change_time);
|
||||
SBVAL(data, 40, file->directory_info.size);
|
||||
SBVAL(data, 48, file->directory_info.alloc_size);
|
||||
SIVAL(data, 56, file->directory_info.attrib);
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->directory_info.name.s,
|
||||
ofs + 60, default_str_flags,
|
||||
STR_TERMINATE_ASCII));
|
||||
BLOB_ALIGN(blob, 8);
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 0, blob->length - ofs);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 68));
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 4, file->full_directory_info.file_index);
|
||||
push_nttime(data, 8, file->full_directory_info.create_time);
|
||||
push_nttime(data, 16, file->full_directory_info.access_time);
|
||||
push_nttime(data, 24, file->full_directory_info.write_time);
|
||||
push_nttime(data, 32, file->full_directory_info.change_time);
|
||||
SBVAL(data, 40, file->full_directory_info.size);
|
||||
SBVAL(data, 48, file->full_directory_info.alloc_size);
|
||||
SIVAL(data, 56, file->full_directory_info.attrib);
|
||||
SIVAL(data, 64, file->full_directory_info.ea_size);
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->full_directory_info.name.s,
|
||||
ofs + 60, default_str_flags,
|
||||
STR_TERMINATE_ASCII));
|
||||
BLOB_ALIGN(blob, 8);
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 0, blob->length - ofs);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_NAME_INFO:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 12));
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 4, file->name_info.file_index);
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->name_info.name.s,
|
||||
ofs + 8, default_str_flags,
|
||||
STR_TERMINATE_ASCII));
|
||||
BLOB_ALIGN(blob, 8);
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 0, blob->length - ofs);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 94));
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 4, file->both_directory_info.file_index);
|
||||
push_nttime(data, 8, file->both_directory_info.create_time);
|
||||
push_nttime(data, 16, file->both_directory_info.access_time);
|
||||
push_nttime(data, 24, file->both_directory_info.write_time);
|
||||
push_nttime(data, 32, file->both_directory_info.change_time);
|
||||
SBVAL(data, 40, file->both_directory_info.size);
|
||||
SBVAL(data, 48, file->both_directory_info.alloc_size);
|
||||
SIVAL(data, 56, file->both_directory_info.attrib);
|
||||
SIVAL(data, 64, file->both_directory_info.ea_size);
|
||||
SCVAL(data, 69, 0); /* reserved */
|
||||
memset(data+70,0,24);
|
||||
smbsrv_blob_push_string(mem_ctx, blob,
|
||||
68 + ofs, 70 + ofs,
|
||||
file->both_directory_info.short_name.s,
|
||||
24, default_str_flags,
|
||||
STR_UNICODE | STR_LEN8BIT);
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->both_directory_info.name.s,
|
||||
ofs + 60, default_str_flags,
|
||||
STR_TERMINATE_ASCII));
|
||||
BLOB_ALIGN(blob, 8);
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 0, blob->length - ofs);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 80));
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 4, file->id_full_directory_info.file_index);
|
||||
push_nttime(data, 8, file->id_full_directory_info.create_time);
|
||||
push_nttime(data, 16, file->id_full_directory_info.access_time);
|
||||
push_nttime(data, 24, file->id_full_directory_info.write_time);
|
||||
push_nttime(data, 32, file->id_full_directory_info.change_time);
|
||||
SBVAL(data, 40, file->id_full_directory_info.size);
|
||||
SBVAL(data, 48, file->id_full_directory_info.alloc_size);
|
||||
SIVAL(data, 56, file->id_full_directory_info.attrib);
|
||||
SIVAL(data, 64, file->id_full_directory_info.ea_size);
|
||||
SIVAL(data, 68, 0); /* padding */
|
||||
SBVAL(data, 72, file->id_full_directory_info.file_id);
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->id_full_directory_info.name.s,
|
||||
ofs + 60, default_str_flags,
|
||||
STR_TERMINATE_ASCII));
|
||||
BLOB_ALIGN(blob, 8);
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 0, blob->length - ofs);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
|
||||
BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 104));
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 4, file->id_both_directory_info.file_index);
|
||||
push_nttime(data, 8, file->id_both_directory_info.create_time);
|
||||
push_nttime(data, 16, file->id_both_directory_info.access_time);
|
||||
push_nttime(data, 24, file->id_both_directory_info.write_time);
|
||||
push_nttime(data, 32, file->id_both_directory_info.change_time);
|
||||
SBVAL(data, 40, file->id_both_directory_info.size);
|
||||
SBVAL(data, 48, file->id_both_directory_info.alloc_size);
|
||||
SIVAL(data, 56, file->id_both_directory_info.attrib);
|
||||
SIVAL(data, 64, file->id_both_directory_info.ea_size);
|
||||
SCVAL(data, 69, 0); /* reserved */
|
||||
memset(data+70,0,26);
|
||||
smbsrv_blob_push_string(mem_ctx, blob,
|
||||
68 + ofs, 70 + ofs,
|
||||
file->id_both_directory_info.short_name.s,
|
||||
24, default_str_flags,
|
||||
STR_UNICODE | STR_LEN8BIT);
|
||||
SBVAL(data, 96, file->id_both_directory_info.file_id);
|
||||
BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->id_both_directory_info.name.s,
|
||||
ofs + 60, default_str_flags,
|
||||
STR_TERMINATE_ASCII));
|
||||
BLOB_ALIGN(blob, 8);
|
||||
data = blob->data + ofs;
|
||||
SIVAL(data, 0, blob->length - ofs);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
default:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# SMB server subsystem
|
||||
#
|
||||
[MODULE::SERVICE_SMB]
|
||||
INIT_FUNCTION = server_service_smb_init
|
||||
SUBSYSTEM = service
|
||||
OBJ_FILES = smb_server.o
|
||||
PRIVATE_PROTO_HEADER = service_smb_proto.h
|
||||
PRIVATE_DEPENDENCIES = SMB_SERVER
|
||||
|
||||
#######################
|
||||
# Start SUBSYSTEM SMB
|
||||
[SUBSYSTEM::SMB_SERVER]
|
||||
OBJ_FILES = \
|
||||
handle.o \
|
||||
tcon.o \
|
||||
session.o \
|
||||
blob.o \
|
||||
management.o
|
||||
PRIVATE_PROTO_HEADER = smb_server_proto.h
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
share \
|
||||
LIBPACKET \
|
||||
SMB_PROTOCOL \
|
||||
SMB2_PROTOCOL
|
||||
# End SUBSYSTEM SMB
|
||||
#######################
|
||||
|
||||
include smb/config.mk
|
||||
include smb2/config.mk
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Manage smbsrv_handle structures
|
||||
Copyright (C) Stefan Metzmacher 2006
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
init the handle structures
|
||||
****************************************************************************/
|
||||
NTSTATUS smbsrv_init_handles(struct smbsrv_tcon *tcon, uint64_t limit)
|
||||
{
|
||||
/*
|
||||
* the idr_* functions take 'int' as limit,
|
||||
* and only work with a max limit 0x00FFFFFF
|
||||
*/
|
||||
limit &= 0x00FFFFFF;
|
||||
|
||||
tcon->handles.idtree_hid = idr_init(tcon);
|
||||
NT_STATUS_HAVE_NO_MEMORY(tcon->handles.idtree_hid);
|
||||
tcon->handles.idtree_limit = limit;
|
||||
tcon->handles.list = NULL;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
find a handle given a handle id
|
||||
****************************************************************************/
|
||||
static struct smbsrv_handle *smbsrv_handle_find(struct smbsrv_handles_context *handles_ctx,
|
||||
uint64_t hid, struct timeval request_time)
|
||||
{
|
||||
void *p;
|
||||
struct smbsrv_handle *handle;
|
||||
|
||||
if (hid == 0) return NULL;
|
||||
|
||||
if (hid > handles_ctx->idtree_limit) return NULL;
|
||||
|
||||
p = idr_find(handles_ctx->idtree_hid, hid);
|
||||
if (!p) return NULL;
|
||||
|
||||
handle = talloc_get_type(p, struct smbsrv_handle);
|
||||
if (!handle) return NULL;
|
||||
|
||||
/* only give it away when the ntvfs subsystem has made the handle valid */
|
||||
if (!handle->ntvfs) return NULL;
|
||||
|
||||
handle->statistics.last_use_time = request_time;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
struct smbsrv_handle *smbsrv_smb_handle_find(struct smbsrv_tcon *smb_tcon,
|
||||
uint16_t fnum, struct timeval request_time)
|
||||
{
|
||||
return smbsrv_handle_find(&smb_tcon->handles, fnum, request_time);
|
||||
}
|
||||
|
||||
struct smbsrv_handle *smbsrv_smb2_handle_find(struct smbsrv_tcon *smb_tcon,
|
||||
uint64_t hid, struct timeval request_time)
|
||||
{
|
||||
return smbsrv_handle_find(&smb_tcon->handles, hid, request_time);
|
||||
}
|
||||
|
||||
/*
|
||||
destroy a connection structure
|
||||
*/
|
||||
static int smbsrv_handle_destructor(struct smbsrv_handle *handle)
|
||||
{
|
||||
struct smbsrv_handles_context *handles_ctx;
|
||||
|
||||
handles_ctx = &handle->tcon->handles;
|
||||
|
||||
idr_remove(handles_ctx->idtree_hid, handle->hid);
|
||||
DLIST_REMOVE(handles_ctx->list, handle);
|
||||
DLIST_REMOVE(handle->session->handles, &handle->session_item);
|
||||
|
||||
/* tell the ntvfs backend that we are disconnecting */
|
||||
if (handle->ntvfs) {
|
||||
talloc_free(handle->ntvfs);
|
||||
handle->ntvfs = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
find first available handle slot
|
||||
*/
|
||||
struct smbsrv_handle *smbsrv_handle_new(struct smbsrv_session *session,
|
||||
struct smbsrv_tcon *tcon,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct timeval request_time)
|
||||
{
|
||||
struct smbsrv_handles_context *handles_ctx = &tcon->handles;
|
||||
struct smbsrv_handle *handle;
|
||||
int i;
|
||||
|
||||
handle = talloc_zero(mem_ctx, struct smbsrv_handle);
|
||||
if (!handle) return NULL;
|
||||
handle->tcon = tcon;
|
||||
handle->session = session;
|
||||
|
||||
i = idr_get_new_above(handles_ctx->idtree_hid, handle, 1, handles_ctx->idtree_limit);
|
||||
if (i == -1) {
|
||||
DEBUG(1,("ERROR! Out of handle structures\n"));
|
||||
goto failed;
|
||||
}
|
||||
handle->hid = i;
|
||||
handle->session_item.handle = handle;
|
||||
|
||||
DLIST_ADD(handles_ctx->list, handle);
|
||||
DLIST_ADD(session->handles, &handle->session_item);
|
||||
talloc_set_destructor(handle, smbsrv_handle_destructor);
|
||||
|
||||
/* now fill in some statistics */
|
||||
handle->statistics.open_time = request_time;
|
||||
handle->statistics.last_use_time = request_time;
|
||||
|
||||
return handle;
|
||||
|
||||
failed:
|
||||
talloc_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
management calls for smb server
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "librpc/gen_ndr/ndr_irpc.h"
|
||||
#include "auth/auth.h"
|
||||
|
||||
/*
|
||||
return a list of open sessions
|
||||
*/
|
||||
static NTSTATUS smbsrv_session_information(struct irpc_message *msg,
|
||||
struct smbsrv_information *r)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = talloc_get_type(msg->private, struct smbsrv_connection);
|
||||
int i=0, count=0;
|
||||
struct smbsrv_session *sess;
|
||||
|
||||
/* count the number of sessions */
|
||||
for (sess=smb_conn->sessions.list; sess; sess=sess->next) {
|
||||
count++;
|
||||
}
|
||||
|
||||
r->out.info.sessions.num_sessions = count;
|
||||
r->out.info.sessions.sessions = talloc_array(r, struct smbsrv_session_info, count);
|
||||
NT_STATUS_HAVE_NO_MEMORY(r->out.info.sessions.sessions);
|
||||
|
||||
for (sess=smb_conn->sessions.list; sess; sess=sess->next) {
|
||||
struct smbsrv_session_info *info = &r->out.info.sessions.sessions[i];
|
||||
struct socket_address *client_addr;
|
||||
client_addr = socket_get_peer_addr(smb_conn->connection->socket, r);
|
||||
|
||||
if (client_addr) {
|
||||
info->client_ip = client_addr->addr;
|
||||
} else {
|
||||
info->client_ip = NULL;
|
||||
}
|
||||
|
||||
info->vuid = sess->vuid;
|
||||
info->account_name = sess->session_info->server_info->account_name;
|
||||
info->domain_name = sess->session_info->server_info->domain_name;
|
||||
|
||||
info->connect_time = timeval_to_nttime(&sess->statistics.connect_time);
|
||||
info->auth_time = timeval_to_nttime(&sess->statistics.auth_time);
|
||||
info->last_use_time= timeval_to_nttime(&sess->statistics.last_request_time);
|
||||
i++;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
return a list of tree connects
|
||||
*/
|
||||
static NTSTATUS smbsrv_tcon_information(struct irpc_message *msg,
|
||||
struct smbsrv_information *r)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = talloc_get_type(msg->private, struct smbsrv_connection);
|
||||
int i=0, count=0;
|
||||
struct smbsrv_tcon *tcon;
|
||||
|
||||
/* count the number of tcons */
|
||||
for (tcon=smb_conn->smb_tcons.list; tcon; tcon=tcon->next) {
|
||||
count++;
|
||||
}
|
||||
|
||||
r->out.info.tcons.num_tcons = count;
|
||||
r->out.info.tcons.tcons = talloc_array(r, struct smbsrv_tcon_info, count);
|
||||
NT_STATUS_HAVE_NO_MEMORY(r->out.info.tcons.tcons);
|
||||
|
||||
for (tcon=smb_conn->smb_tcons.list; tcon; tcon=tcon->next) {
|
||||
struct smbsrv_tcon_info *info = &r->out.info.tcons.tcons[i];
|
||||
struct socket_address *client_addr;
|
||||
client_addr = socket_get_peer_addr(smb_conn->connection->socket, r);
|
||||
|
||||
if (client_addr) {
|
||||
info->client_ip = client_addr->addr;
|
||||
} else {
|
||||
info->client_ip = NULL;
|
||||
}
|
||||
|
||||
info->tid = tcon->tid;
|
||||
info->share_name = tcon->share_name;
|
||||
info->connect_time = timeval_to_nttime(&tcon->statistics.connect_time);
|
||||
info->last_use_time= timeval_to_nttime(&tcon->statistics.last_request_time);
|
||||
i++;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
serve smbserver information via irpc
|
||||
*/
|
||||
static NTSTATUS smbsrv_information(struct irpc_message *msg,
|
||||
struct smbsrv_information *r)
|
||||
{
|
||||
switch (r->in.level) {
|
||||
case SMBSRV_INFO_SESSIONS:
|
||||
return smbsrv_session_information(msg, r);
|
||||
case SMBSRV_INFO_TCONS:
|
||||
return smbsrv_tcon_information(msg, r);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
initialise irpc management calls on a connection
|
||||
*/
|
||||
void smbsrv_management_init(struct smbsrv_connection *smb_conn)
|
||||
{
|
||||
IRPC_REGISTER(smb_conn->connection->msg_ctx, irpc, SMBSRV_INFORMATION,
|
||||
smbsrv_information, smb_conn);
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Password and authentication handling
|
||||
Copyright (C) Andrew Tridgell 1992-2005
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
Copyright (C) Stefan Metzmacher 2005-2006
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
|
||||
|
||||
/*
|
||||
* init the sessions structures
|
||||
*/
|
||||
NTSTATUS smbsrv_init_sessions(struct smbsrv_connection *smb_conn, uint64_t limit)
|
||||
{
|
||||
/*
|
||||
* the idr_* functions take 'int' as limit,
|
||||
* and only work with a max limit 0x00FFFFFF
|
||||
*/
|
||||
limit &= 0x00FFFFFF;
|
||||
|
||||
smb_conn->sessions.idtree_vuid = idr_init(smb_conn);
|
||||
NT_STATUS_HAVE_NO_MEMORY(smb_conn->sessions.idtree_vuid);
|
||||
smb_conn->sessions.idtree_limit = limit;
|
||||
smb_conn->sessions.list = NULL;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the session structure assoicated with a VUID
|
||||
* (not one from an in-progress session setup)
|
||||
*/
|
||||
struct smbsrv_session *smbsrv_session_find(struct smbsrv_connection *smb_conn,
|
||||
uint64_t vuid, struct timeval request_time)
|
||||
{
|
||||
void *p;
|
||||
struct smbsrv_session *sess;
|
||||
|
||||
if (vuid == 0) return NULL;
|
||||
|
||||
if (vuid > smb_conn->sessions.idtree_limit) return NULL;
|
||||
|
||||
p = idr_find(smb_conn->sessions.idtree_vuid, vuid);
|
||||
if (!p) return NULL;
|
||||
|
||||
/* only return a finished session */
|
||||
sess = talloc_get_type(p, struct smbsrv_session);
|
||||
if (sess && sess->session_info) {
|
||||
sess->statistics.last_request_time = request_time;
|
||||
return sess;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the session structure assoicated with a VUID
|
||||
* (assoicated with an in-progress session setup)
|
||||
*/
|
||||
struct smbsrv_session *smbsrv_session_find_sesssetup(struct smbsrv_connection *smb_conn, uint64_t vuid)
|
||||
{
|
||||
void *p;
|
||||
struct smbsrv_session *sess;
|
||||
|
||||
if (vuid == 0) return NULL;
|
||||
|
||||
if (vuid > smb_conn->sessions.idtree_limit) return NULL;
|
||||
|
||||
p = idr_find(smb_conn->sessions.idtree_vuid, vuid);
|
||||
if (!p) return NULL;
|
||||
|
||||
/* only return an unfinished session */
|
||||
sess = talloc_get_type(p, struct smbsrv_session);
|
||||
if (sess && !sess->session_info) {
|
||||
return sess;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* the session will be marked as valid for usage
|
||||
* by attaching a auth_session_info to the session.
|
||||
*
|
||||
* session_info will be talloc_stealed
|
||||
*/
|
||||
NTSTATUS smbsrv_session_sesssetup_finished(struct smbsrv_session *sess,
|
||||
struct auth_session_info *session_info)
|
||||
{
|
||||
/* this check is to catch programmer errors */
|
||||
if (!session_info) {
|
||||
talloc_free(sess);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* mark the session as successful authenticated */
|
||||
sess->session_info = talloc_steal(sess, session_info);
|
||||
|
||||
/* now fill in some statistics */
|
||||
sess->statistics.auth_time = timeval_current();
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
destroy a session structure
|
||||
****************************************************************************/
|
||||
static int smbsrv_session_destructor(struct smbsrv_session *sess)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = sess->smb_conn;
|
||||
|
||||
idr_remove(smb_conn->sessions.idtree_vuid, sess->vuid);
|
||||
DLIST_REMOVE(smb_conn->sessions.list, sess);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a new session structure with a VUID.
|
||||
* gensec_ctx is optional, but talloc_steal'ed when present
|
||||
*/
|
||||
struct smbsrv_session *smbsrv_session_new(struct smbsrv_connection *smb_conn,
|
||||
struct gensec_security *gensec_ctx)
|
||||
{
|
||||
struct smbsrv_session *sess = NULL;
|
||||
int i;
|
||||
|
||||
/* Ensure no vuid gets registered in share level security. */
|
||||
if (smb_conn->config.security == SEC_SHARE) return NULL;
|
||||
|
||||
sess = talloc_zero(smb_conn, struct smbsrv_session);
|
||||
if (!sess) return NULL;
|
||||
sess->smb_conn = smb_conn;
|
||||
|
||||
i = idr_get_new_random(smb_conn->sessions.idtree_vuid, sess, smb_conn->sessions.idtree_limit);
|
||||
if (i == -1) {
|
||||
DEBUG(1,("ERROR! Out of connection structures\n"));
|
||||
talloc_free(sess);
|
||||
return NULL;
|
||||
}
|
||||
sess->vuid = i;
|
||||
|
||||
/* use this to keep tabs on all our info from the authentication */
|
||||
sess->gensec_ctx = talloc_steal(sess, gensec_ctx);
|
||||
|
||||
DLIST_ADD(smb_conn->sessions.list, sess);
|
||||
talloc_set_destructor(sess, smbsrv_session_destructor);
|
||||
|
||||
/* now fill in some statistics */
|
||||
sess->statistics.connect_time = timeval_current();
|
||||
|
||||
return sess;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#######################
|
||||
# Start SUBSYSTEM SMB_PROTOCOL
|
||||
[SUBSYSTEM::SMB_PROTOCOL]
|
||||
PRIVATE_PROTO_HEADER = smb_proto.h
|
||||
OBJ_FILES = \
|
||||
receive.o \
|
||||
negprot.o \
|
||||
nttrans.o \
|
||||
reply.o \
|
||||
request.o \
|
||||
search.o \
|
||||
service.o \
|
||||
sesssetup.o \
|
||||
srvtime.o \
|
||||
trans2.o \
|
||||
signing.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
ntvfs LIBPACKET
|
||||
LDFLAGS = $(SUBSYSTEM_SMB_SERVER_OUTPUT)
|
||||
# End SUBSYSTEM SMB_PROTOCOL
|
||||
#######################
|
||||
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
negprot reply code
|
||||
Copyright (C) Andrew Tridgell 1992-1998
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "auth/auth.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "lib/stream/packet.h"
|
||||
|
||||
|
||||
/* initialise the auth_context for this server and return the cryptkey */
|
||||
static NTSTATUS get_challenge(struct smbsrv_connection *smb_conn, uint8_t buff[8])
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
const uint8_t *challenge;
|
||||
|
||||
/* muliple negprots are not premitted */
|
||||
if (smb_conn->negotiate.auth_context) {
|
||||
DEBUG(3,("get challenge: is this a secondary negprot? auth_context is non-NULL!\n"));
|
||||
return NT_STATUS_FOOBAR;
|
||||
}
|
||||
|
||||
DEBUG(10, ("get challenge: creating negprot_global_auth_context\n"));
|
||||
|
||||
nt_status = auth_context_create(smb_conn, lp_auth_methods(),
|
||||
smb_conn->connection->event.ctx,
|
||||
smb_conn->connection->msg_ctx,
|
||||
&smb_conn->negotiate.auth_context);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(0, ("auth_context_create() returned %s", nt_errstr(nt_status)));
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = auth_get_challenge(smb_conn->negotiate.auth_context, &challenge);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(0, ("auth_get_challenge() returned %s", nt_errstr(nt_status)));
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
memcpy(buff, challenge, 8);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply for the core protocol.
|
||||
****************************************************************************/
|
||||
static void reply_corep(struct smbsrv_request *req, uint16_t choice)
|
||||
{
|
||||
smbsrv_setup_reply(req, 1, 0);
|
||||
|
||||
SSVAL(req->out.vwv, VWV(0), choice);
|
||||
|
||||
req->smb_conn->negotiate.protocol = PROTOCOL_CORE;
|
||||
|
||||
if (req->smb_conn->signing.mandatory_signing) {
|
||||
smbsrv_terminate_connection(req->smb_conn,
|
||||
"CORE does not support SMB signing, and it is mandatory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
smbsrv_send_reply(req);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply for the coreplus protocol.
|
||||
this is quite incomplete - we only fill in a small part of the reply, but as nobody uses
|
||||
this any more it probably doesn't matter
|
||||
****************************************************************************/
|
||||
static void reply_coreplus(struct smbsrv_request *req, uint16_t choice)
|
||||
{
|
||||
uint16_t raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
|
||||
|
||||
smbsrv_setup_reply(req, 13, 0);
|
||||
|
||||
/* Reply, SMBlockread, SMBwritelock supported. */
|
||||
SCVAL(req->out.hdr,HDR_FLG,
|
||||
CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD);
|
||||
|
||||
SSVAL(req->out.vwv, VWV(0), choice);
|
||||
SSVAL(req->out.vwv, VWV(1), 0x1); /* user level security, don't encrypt */
|
||||
|
||||
/* tell redirector we support
|
||||
readbraw and writebraw (possibly) */
|
||||
SSVAL(req->out.vwv, VWV(5), raw);
|
||||
|
||||
req->smb_conn->negotiate.protocol = PROTOCOL_COREPLUS;
|
||||
|
||||
if (req->smb_conn->signing.mandatory_signing) {
|
||||
smbsrv_terminate_connection(req->smb_conn,
|
||||
"COREPLUS does not support SMB signing, and it is mandatory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
smbsrv_send_reply(req);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply for the lanman 1.0 protocol.
|
||||
****************************************************************************/
|
||||
static void reply_lanman1(struct smbsrv_request *req, uint16_t choice)
|
||||
{
|
||||
int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
|
||||
int secword=0;
|
||||
time_t t = req->request_time.tv_sec;
|
||||
|
||||
req->smb_conn->negotiate.encrypted_passwords = lp_encrypted_passwords();
|
||||
|
||||
if (lp_security() != SEC_SHARE)
|
||||
secword |= NEGOTIATE_SECURITY_USER_LEVEL;
|
||||
|
||||
if (req->smb_conn->negotiate.encrypted_passwords)
|
||||
secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
|
||||
|
||||
req->smb_conn->negotiate.protocol = PROTOCOL_LANMAN1;
|
||||
|
||||
smbsrv_setup_reply(req, 13, req->smb_conn->negotiate.encrypted_passwords ? 8 : 0);
|
||||
|
||||
/* SMBlockread, SMBwritelock supported. */
|
||||
SCVAL(req->out.hdr,HDR_FLG,
|
||||
CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD);
|
||||
|
||||
SSVAL(req->out.vwv, VWV(0), choice);
|
||||
SSVAL(req->out.vwv, VWV(1), secword);
|
||||
SSVAL(req->out.vwv, VWV(2), req->smb_conn->negotiate.max_recv);
|
||||
SSVAL(req->out.vwv, VWV(3), lp_maxmux());
|
||||
SSVAL(req->out.vwv, VWV(4), 1);
|
||||
SSVAL(req->out.vwv, VWV(5), raw);
|
||||
SIVAL(req->out.vwv, VWV(6), req->smb_conn->connection->server_id);
|
||||
srv_push_dos_date(req->smb_conn, req->out.vwv, VWV(8), t);
|
||||
SSVAL(req->out.vwv, VWV(10), req->smb_conn->negotiate.zone_offset/60);
|
||||
SIVAL(req->out.vwv, VWV(11), 0); /* reserved */
|
||||
|
||||
/* Create a token value and add it to the outgoing packet. */
|
||||
if (req->smb_conn->negotiate.encrypted_passwords) {
|
||||
NTSTATUS nt_status;
|
||||
|
||||
SSVAL(req->out.vwv, VWV(11), 8);
|
||||
|
||||
nt_status = get_challenge(req->smb_conn, req->out.data);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
smbsrv_terminate_connection(req->smb_conn, "LANMAN1 get_challenge failed\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (req->smb_conn->signing.mandatory_signing) {
|
||||
smbsrv_terminate_connection(req->smb_conn,
|
||||
"LANMAN1 does not support SMB signing, and it is mandatory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
smbsrv_send_reply(req);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply for the lanman 2.0 protocol.
|
||||
****************************************************************************/
|
||||
static void reply_lanman2(struct smbsrv_request *req, uint16_t choice)
|
||||
{
|
||||
int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
|
||||
int secword=0;
|
||||
time_t t = req->request_time.tv_sec;
|
||||
|
||||
req->smb_conn->negotiate.encrypted_passwords = lp_encrypted_passwords();
|
||||
|
||||
if (lp_security() != SEC_SHARE)
|
||||
secword |= NEGOTIATE_SECURITY_USER_LEVEL;
|
||||
|
||||
if (req->smb_conn->negotiate.encrypted_passwords)
|
||||
secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
|
||||
|
||||
req->smb_conn->negotiate.protocol = PROTOCOL_LANMAN2;
|
||||
|
||||
smbsrv_setup_reply(req, 13, 0);
|
||||
|
||||
SSVAL(req->out.vwv, VWV(0), choice);
|
||||
SSVAL(req->out.vwv, VWV(1), secword);
|
||||
SSVAL(req->out.vwv, VWV(2), req->smb_conn->negotiate.max_recv);
|
||||
SSVAL(req->out.vwv, VWV(3), lp_maxmux());
|
||||
SSVAL(req->out.vwv, VWV(4), 1);
|
||||
SSVAL(req->out.vwv, VWV(5), raw);
|
||||
SIVAL(req->out.vwv, VWV(6), req->smb_conn->connection->server_id);
|
||||
srv_push_dos_date(req->smb_conn, req->out.vwv, VWV(8), t);
|
||||
SSVAL(req->out.vwv, VWV(10), req->smb_conn->negotiate.zone_offset/60);
|
||||
SIVAL(req->out.vwv, VWV(11), 0);
|
||||
|
||||
/* Create a token value and add it to the outgoing packet. */
|
||||
if (req->smb_conn->negotiate.encrypted_passwords) {
|
||||
SSVAL(req->out.vwv, VWV(11), 8);
|
||||
req_grow_data(req, 8);
|
||||
get_challenge(req->smb_conn, req->out.data);
|
||||
}
|
||||
|
||||
req_push_str(req, NULL, lp_workgroup(), -1, STR_TERMINATE);
|
||||
|
||||
if (req->smb_conn->signing.mandatory_signing) {
|
||||
smbsrv_terminate_connection(req->smb_conn,
|
||||
"LANMAN2 does not support SMB signing, and it is mandatory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
smbsrv_send_reply(req);
|
||||
}
|
||||
|
||||
static void reply_nt1_orig(struct smbsrv_request *req)
|
||||
{
|
||||
/* Create a token value and add it to the outgoing packet. */
|
||||
if (req->smb_conn->negotiate.encrypted_passwords) {
|
||||
req_grow_data(req, 8);
|
||||
/* note that we do not send a challenge at all if
|
||||
we are using plaintext */
|
||||
get_challenge(req->smb_conn, req->out.ptr);
|
||||
req->out.ptr += 8;
|
||||
SCVAL(req->out.vwv+1, VWV(16), 8);
|
||||
}
|
||||
req_push_str(req, NULL, lp_workgroup(), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
|
||||
req_push_str(req, NULL, lp_netbios_name(), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
|
||||
DEBUG(3,("not using extended security (SPNEGO or NTLMSSP)\n"));
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply for the nt protocol.
|
||||
****************************************************************************/
|
||||
static void reply_nt1(struct smbsrv_request *req, uint16_t choice)
|
||||
{
|
||||
/* dual names + lock_and_read + nt SMBs + remote API calls */
|
||||
int capabilities;
|
||||
int secword=0;
|
||||
time_t t = req->request_time.tv_sec;
|
||||
NTTIME nttime;
|
||||
BOOL negotiate_spnego = False;
|
||||
char *large_test_path;
|
||||
|
||||
unix_to_nt_time(&nttime, t);
|
||||
|
||||
capabilities =
|
||||
CAP_NT_FIND | CAP_LOCK_AND_READ |
|
||||
CAP_LEVEL_II_OPLOCKS | CAP_NT_SMBS | CAP_RPC_REMOTE_APIS;
|
||||
|
||||
req->smb_conn->negotiate.encrypted_passwords = lp_encrypted_passwords();
|
||||
|
||||
/* do spnego in user level security if the client
|
||||
supports it and we can do encrypted passwords */
|
||||
|
||||
if (req->smb_conn->negotiate.encrypted_passwords &&
|
||||
(lp_security() != SEC_SHARE) &&
|
||||
lp_use_spnego() &&
|
||||
(req->flags2 & FLAGS2_EXTENDED_SECURITY)) {
|
||||
negotiate_spnego = True;
|
||||
capabilities |= CAP_EXTENDED_SECURITY;
|
||||
}
|
||||
|
||||
if (lp_unix_extensions()) {
|
||||
capabilities |= CAP_UNIX;
|
||||
}
|
||||
|
||||
if (lp_large_readwrite()) {
|
||||
capabilities |= CAP_LARGE_READX | CAP_LARGE_WRITEX | CAP_W2K_SMBS;
|
||||
}
|
||||
|
||||
large_test_path = lock_path(req, "large_test.dat");
|
||||
if (large_file_support(large_test_path)) {
|
||||
capabilities |= CAP_LARGE_FILES;
|
||||
}
|
||||
|
||||
if (lp_readraw() && lp_writeraw()) {
|
||||
capabilities |= CAP_RAW_MODE;
|
||||
}
|
||||
|
||||
/* allow for disabling unicode */
|
||||
if (lp_unicode()) {
|
||||
capabilities |= CAP_UNICODE;
|
||||
}
|
||||
|
||||
if (lp_nt_status_support()) {
|
||||
capabilities |= CAP_STATUS32;
|
||||
}
|
||||
|
||||
if (lp_host_msdfs()) {
|
||||
capabilities |= CAP_DFS;
|
||||
}
|
||||
|
||||
if (lp_security() != SEC_SHARE) {
|
||||
secword |= NEGOTIATE_SECURITY_USER_LEVEL;
|
||||
}
|
||||
|
||||
if (req->smb_conn->negotiate.encrypted_passwords) {
|
||||
secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
|
||||
}
|
||||
|
||||
if (req->smb_conn->signing.allow_smb_signing) {
|
||||
secword |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED;
|
||||
}
|
||||
|
||||
if (req->smb_conn->signing.mandatory_signing) {
|
||||
secword |= NEGOTIATE_SECURITY_SIGNATURES_REQUIRED;
|
||||
}
|
||||
|
||||
req->smb_conn->negotiate.protocol = PROTOCOL_NT1;
|
||||
|
||||
smbsrv_setup_reply(req, 17, 0);
|
||||
|
||||
SSVAL(req->out.vwv, VWV(0), choice);
|
||||
SCVAL(req->out.vwv, VWV(1), secword);
|
||||
|
||||
/* notice the strange +1 on vwv here? That's because
|
||||
this is the one and only SMB packet that is malformed in
|
||||
the specification - all the command words after the secword
|
||||
are offset by 1 byte */
|
||||
SSVAL(req->out.vwv+1, VWV(1), lp_maxmux());
|
||||
SSVAL(req->out.vwv+1, VWV(2), 1); /* num vcs */
|
||||
SIVAL(req->out.vwv+1, VWV(3), req->smb_conn->negotiate.max_recv);
|
||||
SIVAL(req->out.vwv+1, VWV(5), 0x10000); /* raw size. full 64k */
|
||||
SIVAL(req->out.vwv+1, VWV(7), req->smb_conn->connection->server_id); /* session key */
|
||||
SIVAL(req->out.vwv+1, VWV(9), capabilities);
|
||||
push_nttime(req->out.vwv+1, VWV(11), nttime);
|
||||
SSVALS(req->out.vwv+1,VWV(15), req->smb_conn->negotiate.zone_offset/60);
|
||||
|
||||
if (!negotiate_spnego) {
|
||||
reply_nt1_orig(req);
|
||||
} else {
|
||||
struct cli_credentials *server_credentials;
|
||||
struct gensec_security *gensec_security;
|
||||
DATA_BLOB null_data_blob = data_blob(NULL, 0);
|
||||
DATA_BLOB blob;
|
||||
const char *oid;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
nt_status = gensec_server_start(req->smb_conn,
|
||||
req->smb_conn->connection->event.ctx,
|
||||
req->smb_conn->connection->msg_ctx,
|
||||
&gensec_security);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status)));
|
||||
smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->smb_conn->negotiate.auth_context) {
|
||||
smbsrv_terminate_connection(req->smb_conn, "reply_nt1: is this a secondary negprot? auth_context is non-NULL!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
server_credentials
|
||||
= cli_credentials_init(req);
|
||||
if (!server_credentials) {
|
||||
smbsrv_terminate_connection(req->smb_conn, "Failed to init server credentials\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cli_credentials_set_conf(server_credentials);
|
||||
nt_status = cli_credentials_set_machine_account(server_credentials);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(server_credentials);
|
||||
server_credentials = NULL;
|
||||
}
|
||||
|
||||
req->smb_conn->negotiate.server_credentials = talloc_steal(req->smb_conn, server_credentials);
|
||||
|
||||
gensec_set_target_service(gensec_security, "cifs");
|
||||
|
||||
gensec_set_credentials(gensec_security, server_credentials);
|
||||
|
||||
oid = GENSEC_OID_SPNEGO;
|
||||
nt_status = gensec_start_mech_by_oid(gensec_security, oid);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
/* Get and push the proposed OID list into the packets */
|
||||
nt_status = gensec_update(gensec_security, req, null_data_blob, &blob);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
DEBUG(1, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
|
||||
}
|
||||
}
|
||||
|
||||
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
DEBUG(3,("using SPNEGO\n"));
|
||||
} else {
|
||||
DEBUG(5, ("Failed to start SPNEGO, falling back to NTLMSSP only: %s\n", nt_errstr(nt_status)));
|
||||
oid = GENSEC_OID_NTLMSSP;
|
||||
nt_status = gensec_start_mech_by_oid(gensec_security, oid);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(0, ("Failed to start SPNEGO as well as NTLMSSP fallback: %s\n", nt_errstr(nt_status)));
|
||||
reply_nt1_orig(req);
|
||||
return;
|
||||
}
|
||||
/* NTLMSSP is a client-first exchange */
|
||||
blob = data_blob(NULL, 0);
|
||||
DEBUG(3,("using raw-NTLMSSP\n"));
|
||||
}
|
||||
|
||||
req->smb_conn->negotiate.oid = oid;
|
||||
|
||||
req_grow_data(req, blob.length + 16);
|
||||
/* a NOT very random guid, perhaps we should get it
|
||||
* from the credentials (kitchen sink...) */
|
||||
memset(req->out.ptr, '\0', 16);
|
||||
req->out.ptr += 16;
|
||||
|
||||
memcpy(req->out.ptr, blob.data, blob.length);
|
||||
SCVAL(req->out.vwv+1, VWV(16), blob.length + 16);
|
||||
req->out.ptr += blob.length;
|
||||
}
|
||||
|
||||
smbsrv_send_reply_nosign(req);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply for the SMB2 2.001 protocol
|
||||
****************************************************************************/
|
||||
static void reply_smb2(struct smbsrv_request *req, uint16_t choice)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = req->smb_conn;
|
||||
|
||||
/* reply with a SMB2 packet */
|
||||
packet_set_callback(smb_conn->packet, smbsrv_recv_smb2_request);
|
||||
smb2srv_reply_smb_negprot(req);
|
||||
req = NULL;
|
||||
}
|
||||
|
||||
/* List of supported protocols, most desired first */
|
||||
static const struct {
|
||||
const char *proto_name;
|
||||
const char *short_name;
|
||||
void (*proto_reply_fn)(struct smbsrv_request *req, uint16_t choice);
|
||||
int protocol_level;
|
||||
} supported_protocols[] = {
|
||||
{"SMB 2.001", "SMB2", reply_smb2, PROTOCOL_SMB2},
|
||||
{"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1},
|
||||
{"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1},
|
||||
{"LANMAN2.1", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
|
||||
{"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
|
||||
{"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
|
||||
{"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
|
||||
{"Windows for Workgroups 3.1a", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
|
||||
{"LANMAN1.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
|
||||
{"MICROSOFT NETWORKS 3.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
|
||||
{"MICROSOFT NETWORKS 1.03", "COREPLUS", reply_coreplus, PROTOCOL_COREPLUS},
|
||||
{"PC NETWORK PROGRAM 1.0", "CORE", reply_corep, PROTOCOL_CORE},
|
||||
{NULL,NULL,NULL,0},
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
Reply to a negprot.
|
||||
****************************************************************************/
|
||||
|
||||
void smbsrv_reply_negprot(struct smbsrv_request *req)
|
||||
{
|
||||
int protocol;
|
||||
uint8_t *p;
|
||||
uint32_t protos_count = 0;
|
||||
char **protos = NULL;
|
||||
|
||||
if (req->smb_conn->negotiate.done_negprot) {
|
||||
smbsrv_terminate_connection(req->smb_conn, "multiple negprot's are not permitted");
|
||||
return;
|
||||
}
|
||||
req->smb_conn->negotiate.done_negprot = True;
|
||||
|
||||
p = req->in.data;
|
||||
while (True) {
|
||||
size_t len;
|
||||
|
||||
protos = talloc_realloc(req, protos, char *, protos_count + 1);
|
||||
if (!protos) {
|
||||
smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
|
||||
return;
|
||||
}
|
||||
protos[protos_count] = NULL;
|
||||
len = req_pull_ascii4(req, (const char **)&protos[protos_count], p, STR_ASCII|STR_TERMINATE);
|
||||
p += len;
|
||||
if (len == 0 || !protos[protos_count]) break;
|
||||
|
||||
DEBUG(5,("Requested protocol [%d][%s]\n", protos_count, protos[protos_count]));
|
||||
protos_count++;
|
||||
}
|
||||
|
||||
/* Check for protocols, most desirable first */
|
||||
for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) {
|
||||
int i;
|
||||
|
||||
if (supported_protocols[protocol].protocol_level > lp_srv_maxprotocol()) continue;
|
||||
if (supported_protocols[protocol].protocol_level < lp_srv_minprotocol()) continue;
|
||||
|
||||
for (i = 0; i < protos_count; i++) {
|
||||
if (strcmp(supported_protocols[protocol].proto_name, protos[i]) != 0) continue;
|
||||
|
||||
supported_protocols[protocol].proto_reply_fn(req, i);
|
||||
DEBUG(3,("Selected protocol [%d][%s]\n",
|
||||
i, supported_protocols[protocol].proto_name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
smbsrv_terminate_connection(req->smb_conn, "No protocol supported !");
|
||||
}
|
||||
@@ -0,0 +1,649 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
NT transaction handling
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
Copyright (C) James J Myers 2003 <myersjj@samba.org>
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/*
|
||||
This file handles the parsing of transact2 requests
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "librpc/gen_ndr/ndr_security.h"
|
||||
|
||||
/*
|
||||
hold the state of a nttrans op while in progress. Needed to allow for async backend
|
||||
functions.
|
||||
*/
|
||||
struct nttrans_op {
|
||||
struct smb_nttrans *trans;
|
||||
NTSTATUS (*send_fn)(struct nttrans_op *);
|
||||
void *op_info;
|
||||
};
|
||||
|
||||
|
||||
/* setup a nttrans reply, given the data and params sizes */
|
||||
static NTSTATUS nttrans_setup_reply(struct nttrans_op *op,
|
||||
struct smb_nttrans *trans,
|
||||
uint32_t param_size, uint32_t data_size,
|
||||
uint8_t setup_count)
|
||||
{
|
||||
trans->out.setup_count = setup_count;
|
||||
if (setup_count != 0) {
|
||||
trans->out.setup = talloc_zero_array(op, uint16_t, setup_count);
|
||||
NT_STATUS_HAVE_NO_MEMORY(trans->out.setup);
|
||||
}
|
||||
trans->out.params = data_blob_talloc(op, NULL, param_size);
|
||||
if (param_size != 0) {
|
||||
NT_STATUS_HAVE_NO_MEMORY(trans->out.params.data);
|
||||
}
|
||||
trans->out.data = data_blob_talloc(op, NULL, data_size);
|
||||
if (data_size != 0) {
|
||||
NT_STATUS_HAVE_NO_MEMORY(trans->out.data.data);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
send a nttrans create reply
|
||||
*/
|
||||
static NTSTATUS nttrans_create_send(struct nttrans_op *op)
|
||||
{
|
||||
union smb_open *io = talloc_get_type(op->op_info, union smb_open);
|
||||
uint8_t *params;
|
||||
NTSTATUS status;
|
||||
|
||||
status = nttrans_setup_reply(op, op->trans, 69, 0, 0);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
params = op->trans->out.params.data;
|
||||
|
||||
SSVAL(params, 0, io->ntcreatex.out.oplock_level);
|
||||
smbsrv_push_fnum(params, 2, io->ntcreatex.out.file.ntvfs);
|
||||
SIVAL(params, 4, io->ntcreatex.out.create_action);
|
||||
SIVAL(params, 8, 0); /* ea error offset */
|
||||
push_nttime(params, 12, io->ntcreatex.out.create_time);
|
||||
push_nttime(params, 20, io->ntcreatex.out.access_time);
|
||||
push_nttime(params, 28, io->ntcreatex.out.write_time);
|
||||
push_nttime(params, 36, io->ntcreatex.out.change_time);
|
||||
SIVAL(params, 44, io->ntcreatex.out.attrib);
|
||||
SBVAL(params, 48, io->ntcreatex.out.alloc_size);
|
||||
SBVAL(params, 56, io->ntcreatex.out.size);
|
||||
SSVAL(params, 64, io->ntcreatex.out.file_type);
|
||||
SSVAL(params, 66, io->ntcreatex.out.ipc_state);
|
||||
SCVAL(params, 68, io->ntcreatex.out.is_directory);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
parse NTTRANS_CREATE request
|
||||
*/
|
||||
static NTSTATUS nttrans_create(struct smbsrv_request *req,
|
||||
struct nttrans_op *op)
|
||||
{
|
||||
struct smb_nttrans *trans = op->trans;
|
||||
union smb_open *io;
|
||||
uint16_t fname_len;
|
||||
uint32_t sd_length, ea_length;
|
||||
NTSTATUS status;
|
||||
uint8_t *params;
|
||||
|
||||
if (trans->in.params.length < 54) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* parse the request */
|
||||
io = talloc(op, union smb_open);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io);
|
||||
|
||||
io->ntcreatex.level = RAW_OPEN_NTTRANS_CREATE;
|
||||
|
||||
params = trans->in.params.data;
|
||||
|
||||
io->ntcreatex.in.flags = IVAL(params, 0);
|
||||
io->ntcreatex.in.root_fid = IVAL(params, 4);
|
||||
io->ntcreatex.in.access_mask = IVAL(params, 8);
|
||||
io->ntcreatex.in.alloc_size = BVAL(params, 12);
|
||||
io->ntcreatex.in.file_attr = IVAL(params, 20);
|
||||
io->ntcreatex.in.share_access = IVAL(params, 24);
|
||||
io->ntcreatex.in.open_disposition = IVAL(params, 28);
|
||||
io->ntcreatex.in.create_options = IVAL(params, 32);
|
||||
sd_length = IVAL(params, 36);
|
||||
ea_length = IVAL(params, 40);
|
||||
fname_len = IVAL(params, 44);
|
||||
io->ntcreatex.in.impersonation = IVAL(params, 48);
|
||||
io->ntcreatex.in.security_flags = CVAL(params, 52);
|
||||
io->ntcreatex.in.sec_desc = NULL;
|
||||
io->ntcreatex.in.ea_list = NULL;
|
||||
|
||||
req_pull_string(req, &io->ntcreatex.in.fname,
|
||||
params + 53,
|
||||
MIN(fname_len+1, trans->in.params.length - 53),
|
||||
STR_NO_RANGE_CHECK | STR_TERMINATE);
|
||||
if (!io->ntcreatex.in.fname) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (sd_length > trans->in.data.length ||
|
||||
ea_length > trans->in.data.length ||
|
||||
(sd_length+ea_length) > trans->in.data.length) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* this call has an optional security descriptor */
|
||||
if (sd_length != 0) {
|
||||
DATA_BLOB blob;
|
||||
blob.data = trans->in.data.data;
|
||||
blob.length = sd_length;
|
||||
io->ntcreatex.in.sec_desc = talloc(io, struct security_descriptor);
|
||||
if (io->ntcreatex.in.sec_desc == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
status = ndr_pull_struct_blob(&blob, io,
|
||||
io->ntcreatex.in.sec_desc,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/* and an optional ea_list */
|
||||
if (ea_length > 4) {
|
||||
DATA_BLOB blob;
|
||||
blob.data = trans->in.data.data + sd_length;
|
||||
blob.length = ea_length;
|
||||
io->ntcreatex.in.ea_list = talloc(io, struct smb_ea_list);
|
||||
if (io->ntcreatex.in.ea_list == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = ea_pull_list_chained(&blob, io,
|
||||
&io->ntcreatex.in.ea_list->num_eas,
|
||||
&io->ntcreatex.in.ea_list->eas);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
op->send_fn = nttrans_create_send;
|
||||
op->op_info = io;
|
||||
|
||||
return ntvfs_open(req->ntvfs, io);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
send NTTRANS_QUERY_SEC_DESC reply
|
||||
*/
|
||||
static NTSTATUS nttrans_query_sec_desc_send(struct nttrans_op *op)
|
||||
{
|
||||
union smb_fileinfo *io = talloc_get_type(op->op_info, union smb_fileinfo);
|
||||
uint8_t *params;
|
||||
NTSTATUS status;
|
||||
|
||||
status = nttrans_setup_reply(op, op->trans, 4, 0, 0);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
params = op->trans->out.params.data;
|
||||
|
||||
status = ndr_push_struct_blob(&op->trans->out.data, op,
|
||||
io->query_secdesc.out.sd,
|
||||
(ndr_push_flags_fn_t)ndr_push_security_descriptor);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
SIVAL(params, 0, op->trans->out.data.length);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
parse NTTRANS_QUERY_SEC_DESC request
|
||||
*/
|
||||
static NTSTATUS nttrans_query_sec_desc(struct smbsrv_request *req,
|
||||
struct nttrans_op *op)
|
||||
{
|
||||
struct smb_nttrans *trans = op->trans;
|
||||
union smb_fileinfo *io;
|
||||
|
||||
if (trans->in.params.length < 8) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* parse the request */
|
||||
io = talloc(op, union smb_fileinfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io);
|
||||
|
||||
io->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
|
||||
io->query_secdesc.in.file.ntvfs = smbsrv_pull_fnum(req, trans->in.params.data, 0);
|
||||
io->query_secdesc.in.secinfo_flags = IVAL(trans->in.params.data, 4);
|
||||
|
||||
op->op_info = io;
|
||||
op->send_fn = nttrans_query_sec_desc_send;
|
||||
|
||||
SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io->query_secdesc.in.file.ntvfs);
|
||||
return ntvfs_qfileinfo(req->ntvfs, io);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
parse NTTRANS_SET_SEC_DESC request
|
||||
*/
|
||||
static NTSTATUS nttrans_set_sec_desc(struct smbsrv_request *req,
|
||||
struct nttrans_op *op)
|
||||
{
|
||||
struct smb_nttrans *trans = op->trans;
|
||||
union smb_setfileinfo *io;
|
||||
NTSTATUS status;
|
||||
|
||||
if (trans->in.params.length < 8) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* parse the request */
|
||||
io = talloc(req, union smb_setfileinfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io);
|
||||
|
||||
io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
|
||||
io->set_secdesc.in.file.ntvfs = smbsrv_pull_fnum(req, trans->in.params.data, 0);
|
||||
io->set_secdesc.in.secinfo_flags = IVAL(trans->in.params.data, 4);
|
||||
|
||||
io->set_secdesc.in.sd = talloc(io, struct security_descriptor);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io->set_secdesc.in.sd);
|
||||
|
||||
status = ndr_pull_struct_blob(&trans->in.data, req,
|
||||
io->set_secdesc.in.sd,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io->set_secdesc.in.file.ntvfs);
|
||||
return ntvfs_setfileinfo(req->ntvfs, io);
|
||||
}
|
||||
|
||||
|
||||
/* parse NTTRANS_RENAME request
|
||||
*/
|
||||
static NTSTATUS nttrans_rename(struct smbsrv_request *req,
|
||||
struct nttrans_op *op)
|
||||
{
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/*
|
||||
parse NTTRANS_IOCTL send
|
||||
*/
|
||||
static NTSTATUS nttrans_ioctl_send(struct nttrans_op *op)
|
||||
{
|
||||
union smb_ioctl *info = talloc_get_type(op->op_info, union smb_ioctl);
|
||||
NTSTATUS status;
|
||||
|
||||
/*
|
||||
* we pass 0 as data_count here,
|
||||
* because we reuse the DATA_BLOB from the smb_ioctl
|
||||
* struct
|
||||
*/
|
||||
status = nttrans_setup_reply(op, op->trans, 0, 0, 1);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
op->trans->out.setup[0] = 0;
|
||||
op->trans->out.data = info->ntioctl.out.blob;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
parse NTTRANS_IOCTL request
|
||||
*/
|
||||
static NTSTATUS nttrans_ioctl(struct smbsrv_request *req,
|
||||
struct nttrans_op *op)
|
||||
{
|
||||
struct smb_nttrans *trans = op->trans;
|
||||
union smb_ioctl *nt;
|
||||
|
||||
/* should have at least 4 setup words */
|
||||
if (trans->in.setup_count != 4) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
nt = talloc(op, union smb_ioctl);
|
||||
NT_STATUS_HAVE_NO_MEMORY(nt);
|
||||
|
||||
nt->ntioctl.level = RAW_IOCTL_NTIOCTL;
|
||||
nt->ntioctl.in.function = IVAL(trans->in.setup, 0);
|
||||
nt->ntioctl.in.file.ntvfs = smbsrv_pull_fnum(req, (uint8_t *)trans->in.setup, 4);
|
||||
nt->ntioctl.in.fsctl = CVAL(trans->in.setup, 6);
|
||||
nt->ntioctl.in.filter = CVAL(trans->in.setup, 7);
|
||||
nt->ntioctl.in.max_data = trans->in.max_data;
|
||||
nt->ntioctl.in.blob = trans->in.data;
|
||||
|
||||
op->op_info = nt;
|
||||
op->send_fn = nttrans_ioctl_send;
|
||||
|
||||
SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(nt->ntioctl.in.file.ntvfs);
|
||||
return ntvfs_ioctl(req->ntvfs, nt);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
send NTTRANS_NOTIFY_CHANGE reply
|
||||
*/
|
||||
static NTSTATUS nttrans_notify_change_send(struct nttrans_op *op)
|
||||
{
|
||||
union smb_notify *info = talloc_get_type(op->op_info, union smb_notify);
|
||||
size_t size = 0;
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
uint8_t *p;
|
||||
#define MAX_BYTES_PER_CHAR 3
|
||||
|
||||
/* work out how big the reply buffer could be */
|
||||
for (i=0;i<info->nttrans.out.num_changes;i++) {
|
||||
size += 12 + 3 + (1+strlen(info->nttrans.out.changes[i].name.s)) * MAX_BYTES_PER_CHAR;
|
||||
}
|
||||
|
||||
status = nttrans_setup_reply(op, op->trans, size, 0, 0);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
p = op->trans->out.params.data;
|
||||
|
||||
/* construct the changes buffer */
|
||||
for (i=0;i<info->nttrans.out.num_changes;i++) {
|
||||
uint32_t ofs;
|
||||
ssize_t len;
|
||||
|
||||
SIVAL(p, 4, info->nttrans.out.changes[i].action);
|
||||
len = push_string(p + 12, info->nttrans.out.changes[i].name.s,
|
||||
op->trans->out.params.length -
|
||||
(p+12 - op->trans->out.params.data), STR_UNICODE);
|
||||
SIVAL(p, 8, len);
|
||||
|
||||
ofs = len + 12;
|
||||
|
||||
if (ofs & 3) {
|
||||
int pad = 4 - (ofs & 3);
|
||||
memset(p+ofs, 0, pad);
|
||||
ofs += pad;
|
||||
}
|
||||
|
||||
if (i == info->nttrans.out.num_changes-1) {
|
||||
SIVAL(p, 0, 0);
|
||||
} else {
|
||||
SIVAL(p, 0, ofs);
|
||||
}
|
||||
|
||||
p += ofs;
|
||||
}
|
||||
|
||||
op->trans->out.params.length = p - op->trans->out.params.data;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
parse NTTRANS_NOTIFY_CHANGE request
|
||||
*/
|
||||
static NTSTATUS nttrans_notify_change(struct smbsrv_request *req,
|
||||
struct nttrans_op *op)
|
||||
{
|
||||
struct smb_nttrans *trans = op->trans;
|
||||
union smb_notify *info;
|
||||
|
||||
/* should have at least 4 setup words */
|
||||
if (trans->in.setup_count != 4) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
info = talloc(op, union smb_notify);
|
||||
NT_STATUS_HAVE_NO_MEMORY(info);
|
||||
|
||||
info->nttrans.level = RAW_NOTIFY_NTTRANS;
|
||||
info->nttrans.in.completion_filter = IVAL(trans->in.setup, 0);
|
||||
info->nttrans.in.file.ntvfs = smbsrv_pull_fnum(req, (uint8_t *)trans->in.setup, 4);
|
||||
info->nttrans.in.recursive = SVAL(trans->in.setup, 6);
|
||||
info->nttrans.in.buffer_size = trans->in.max_param;
|
||||
|
||||
op->op_info = info;
|
||||
op->send_fn = nttrans_notify_change_send;
|
||||
|
||||
SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(info->nttrans.in.file.ntvfs);
|
||||
return ntvfs_notify(req->ntvfs, info);
|
||||
}
|
||||
|
||||
/*
|
||||
backend for nttrans requests
|
||||
*/
|
||||
static NTSTATUS nttrans_backend(struct smbsrv_request *req,
|
||||
struct nttrans_op *op)
|
||||
{
|
||||
/* the nttrans command is in function */
|
||||
switch (op->trans->in.function) {
|
||||
case NT_TRANSACT_CREATE:
|
||||
return nttrans_create(req, op);
|
||||
case NT_TRANSACT_IOCTL:
|
||||
return nttrans_ioctl(req, op);
|
||||
case NT_TRANSACT_RENAME:
|
||||
return nttrans_rename(req, op);
|
||||
case NT_TRANSACT_QUERY_SECURITY_DESC:
|
||||
return nttrans_query_sec_desc(req, op);
|
||||
case NT_TRANSACT_SET_SECURITY_DESC:
|
||||
return nttrans_set_sec_desc(req, op);
|
||||
case NT_TRANSACT_NOTIFY_CHANGE:
|
||||
return nttrans_notify_change(req, op);
|
||||
}
|
||||
|
||||
/* an unknown nttrans command */
|
||||
return NT_STATUS_DOS(ERRSRV, ERRerror);
|
||||
}
|
||||
|
||||
|
||||
static void reply_nttrans_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smbsrv_request *req;
|
||||
uint16_t params_left, data_left;
|
||||
uint8_t *params, *data;
|
||||
struct smb_nttrans *trans;
|
||||
struct nttrans_op *op;
|
||||
|
||||
SMBSRV_CHECK_ASYNC_STATUS(op, struct nttrans_op);
|
||||
|
||||
trans = op->trans;
|
||||
|
||||
/* if this function needs work to form the nttrans reply buffer, then
|
||||
call that now */
|
||||
if (op->send_fn != NULL) {
|
||||
NTSTATUS status;
|
||||
status = op->send_fn(op);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smbsrv_send_error(req, status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
smbsrv_setup_reply(req, 18 + trans->out.setup_count, 0);
|
||||
|
||||
/* note that we don't check the max_setup count (matching w2k3
|
||||
behaviour) */
|
||||
|
||||
if (trans->out.params.length > trans->in.max_param) {
|
||||
smbsrv_setup_error(req, NT_STATUS_BUFFER_TOO_SMALL);
|
||||
trans->out.params.length = trans->in.max_param;
|
||||
}
|
||||
if (trans->out.data.length > trans->in.max_data) {
|
||||
smbsrv_setup_error(req, NT_STATUS_BUFFER_TOO_SMALL);
|
||||
trans->out.data.length = trans->in.max_data;
|
||||
}
|
||||
|
||||
params_left = trans->out.params.length;
|
||||
data_left = trans->out.data.length;
|
||||
params = trans->out.params.data;
|
||||
data = trans->out.data.data;
|
||||
|
||||
/* we need to divide up the reply into chunks that fit into
|
||||
the negotiated buffer size */
|
||||
do {
|
||||
uint16_t this_data, this_param, max_bytes;
|
||||
uint_t align1 = 1, align2 = (params_left ? 2 : 0);
|
||||
struct smbsrv_request *this_req;
|
||||
int i;
|
||||
|
||||
max_bytes = req_max_data(req) - (align1 + align2);
|
||||
|
||||
this_param = params_left;
|
||||
if (this_param > max_bytes) {
|
||||
this_param = max_bytes;
|
||||
}
|
||||
max_bytes -= this_param;
|
||||
|
||||
this_data = data_left;
|
||||
if (this_data > max_bytes) {
|
||||
this_data = max_bytes;
|
||||
}
|
||||
|
||||
/* don't destroy unless this is the last chunk */
|
||||
if (params_left - this_param != 0 ||
|
||||
data_left - this_data != 0) {
|
||||
this_req = smbsrv_setup_secondary_request(req);
|
||||
} else {
|
||||
this_req = req;
|
||||
}
|
||||
|
||||
req_grow_data(req, this_param + this_data + (align1 + align2));
|
||||
|
||||
SSVAL(this_req->out.vwv, 0, 0); /* reserved */
|
||||
SCVAL(this_req->out.vwv, 2, 0); /* reserved */
|
||||
SIVAL(this_req->out.vwv, 3, trans->out.params.length);
|
||||
SIVAL(this_req->out.vwv, 7, trans->out.data.length);
|
||||
|
||||
SIVAL(this_req->out.vwv, 11, this_param);
|
||||
SIVAL(this_req->out.vwv, 15, align1 + PTR_DIFF(this_req->out.data, this_req->out.hdr));
|
||||
SIVAL(this_req->out.vwv, 19, PTR_DIFF(params, trans->out.params.data));
|
||||
|
||||
SIVAL(this_req->out.vwv, 23, this_data);
|
||||
SIVAL(this_req->out.vwv, 27, align1 + align2 +
|
||||
PTR_DIFF(this_req->out.data + this_param, this_req->out.hdr));
|
||||
SIVAL(this_req->out.vwv, 31, PTR_DIFF(data, trans->out.data.data));
|
||||
|
||||
SCVAL(this_req->out.vwv, 35, trans->out.setup_count);
|
||||
for (i=0;i<trans->out.setup_count;i++) {
|
||||
SSVAL(this_req->out.vwv, VWV(18+i), trans->out.setup[i]);
|
||||
}
|
||||
|
||||
memset(this_req->out.data, 0, align1);
|
||||
if (this_param != 0) {
|
||||
memcpy(this_req->out.data + align1, params, this_param);
|
||||
}
|
||||
memset(this_req->out.data+this_param+align1, 0, align2);
|
||||
if (this_data != 0) {
|
||||
memcpy(this_req->out.data+this_param+align1+align2,
|
||||
data, this_data);
|
||||
}
|
||||
|
||||
params_left -= this_param;
|
||||
data_left -= this_data;
|
||||
params += this_param;
|
||||
data += this_data;
|
||||
|
||||
smbsrv_send_reply(this_req);
|
||||
} while (params_left != 0 || data_left != 0);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Reply to an SMBnttrans request
|
||||
****************************************************************************/
|
||||
void smbsrv_reply_nttrans(struct smbsrv_request *req)
|
||||
{
|
||||
struct nttrans_op *op;
|
||||
struct smb_nttrans *trans;
|
||||
int i;
|
||||
uint16_t param_ofs, data_ofs;
|
||||
uint16_t param_count, data_count;
|
||||
uint16_t param_total, data_total;
|
||||
|
||||
/* parse request */
|
||||
if (req->in.wct < 19) {
|
||||
smbsrv_send_error(req, NT_STATUS_FOOBAR);
|
||||
return;
|
||||
}
|
||||
|
||||
SMBSRV_TALLOC_IO_PTR(op, struct nttrans_op);
|
||||
SMBSRV_SETUP_NTVFS_REQUEST(reply_nttrans_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
trans = talloc(op, struct smb_nttrans);
|
||||
if (trans == NULL) {
|
||||
smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
op->trans = trans;
|
||||
op->op_info = NULL;
|
||||
op->send_fn = NULL;
|
||||
|
||||
trans->in.max_setup = CVAL(req->in.vwv, 0);
|
||||
param_total = IVAL(req->in.vwv, 3);
|
||||
data_total = IVAL(req->in.vwv, 7);
|
||||
trans->in.max_param = IVAL(req->in.vwv, 11);
|
||||
trans->in.max_data = IVAL(req->in.vwv, 15);
|
||||
param_count = IVAL(req->in.vwv, 19);
|
||||
param_ofs = IVAL(req->in.vwv, 23);
|
||||
data_count = IVAL(req->in.vwv, 27);
|
||||
data_ofs = IVAL(req->in.vwv, 31);
|
||||
trans->in.setup_count= CVAL(req->in.vwv, 35);
|
||||
trans->in.function = SVAL(req->in.vwv, 36);
|
||||
|
||||
if (req->in.wct != 19 + trans->in.setup_count) {
|
||||
smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse out the setup words */
|
||||
trans->in.setup = talloc_array(req, uint16_t, trans->in.setup_count);
|
||||
if (!trans->in.setup) {
|
||||
smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
|
||||
return;
|
||||
}
|
||||
for (i=0;i<trans->in.setup_count;i++) {
|
||||
trans->in.setup[i] = SVAL(req->in.vwv, VWV(19+i));
|
||||
}
|
||||
|
||||
if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, &trans->in.params) ||
|
||||
!req_pull_blob(req, req->in.hdr + data_ofs, data_count, &trans->in.data)) {
|
||||
smbsrv_send_error(req, NT_STATUS_FOOBAR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* is it a partial request? if so, then send a 'send more' message */
|
||||
if (param_total > param_count ||
|
||||
data_total > data_count) {
|
||||
DEBUG(0,("REWRITE: not handling partial nttrans requests!\n"));
|
||||
smbsrv_send_error(req, NT_STATUS_FOOBAR);
|
||||
return;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(trans->out);
|
||||
SMBSRV_CALL_NTVFS_BACKEND(nttrans_backend(req, op));
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Reply to an SMBnttranss request
|
||||
****************************************************************************/
|
||||
void smbsrv_reply_nttranss(struct smbsrv_request *req)
|
||||
{
|
||||
smbsrv_send_error(req, NT_STATUS_FOOBAR);
|
||||
}
|
||||
@@ -0,0 +1,671 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
process incoming packets - main loop
|
||||
Copyright (C) Andrew Tridgell 1992-2005
|
||||
Copyright (C) James J Myers 2003 <myersjj@samba.org>
|
||||
Copyright (C) Stefan Metzmacher 2004-2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/time.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
#include "system/filesys.h"
|
||||
|
||||
|
||||
/*
|
||||
send an oplock break request to a client
|
||||
*/
|
||||
NTSTATUS smbsrv_send_oplock_break(void *p, struct ntvfs_handle *ntvfs, uint8_t level)
|
||||
{
|
||||
struct smbsrv_tcon *tcon = talloc_get_type(p, struct smbsrv_tcon);
|
||||
struct smbsrv_request *req;
|
||||
|
||||
req = smbsrv_init_request(tcon->smb_conn);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
smbsrv_setup_reply(req, 8, 0);
|
||||
|
||||
SCVAL(req->out.hdr,HDR_COM,SMBlockingX);
|
||||
SSVAL(req->out.hdr,HDR_TID,tcon->tid);
|
||||
SSVAL(req->out.hdr,HDR_PID,0xFFFF);
|
||||
SSVAL(req->out.hdr,HDR_UID,0);
|
||||
SSVAL(req->out.hdr,HDR_MID,0xFFFF);
|
||||
SCVAL(req->out.hdr,HDR_FLG,0);
|
||||
SSVAL(req->out.hdr,HDR_FLG2,0);
|
||||
|
||||
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
|
||||
SSVAL(req->out.vwv, VWV(1), 0);
|
||||
smbsrv_push_fnum(req->out.vwv, VWV(2), ntvfs);
|
||||
SCVAL(req->out.vwv, VWV(3), LOCKING_ANDX_OPLOCK_RELEASE);
|
||||
SCVAL(req->out.vwv, VWV(3)+1, level);
|
||||
SIVAL(req->out.vwv, VWV(4), 0);
|
||||
SSVAL(req->out.vwv, VWV(6), 0);
|
||||
SSVAL(req->out.vwv, VWV(7), 0);
|
||||
|
||||
smbsrv_send_reply(req);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void switch_message(int type, struct smbsrv_request *req);
|
||||
|
||||
/****************************************************************************
|
||||
receive a SMB request header from the wire, forming a request_context
|
||||
from the result
|
||||
****************************************************************************/
|
||||
NTSTATUS smbsrv_recv_smb_request(void *private, DATA_BLOB blob)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = talloc_get_type(private, struct smbsrv_connection);
|
||||
struct smbsrv_request *req;
|
||||
struct timeval cur_time = timeval_current();
|
||||
uint8_t command;
|
||||
|
||||
smb_conn->statistics.last_request_time = cur_time;
|
||||
|
||||
/* see if its a special NBT packet */
|
||||
if (CVAL(blob.data, 0) != 0) {
|
||||
req = smbsrv_init_request(smb_conn);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
ZERO_STRUCT(req->in);
|
||||
|
||||
req->in.buffer = talloc_steal(req, blob.data);
|
||||
req->in.size = blob.length;
|
||||
req->request_time = cur_time;
|
||||
|
||||
smbsrv_reply_special(req);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if ((NBT_HDR_SIZE + MIN_SMB_SIZE) > blob.length) {
|
||||
DEBUG(2,("Invalid SMB packet: length %ld\n", (long)blob.length));
|
||||
smbsrv_terminate_connection(smb_conn, "Invalid SMB packet");
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Make sure this is an SMB packet */
|
||||
if (IVAL(blob.data, NBT_HDR_SIZE) != SMB_MAGIC) {
|
||||
DEBUG(2,("Non-SMB packet of length %ld. Terminating connection\n",
|
||||
(long)blob.length));
|
||||
smbsrv_terminate_connection(smb_conn, "Non-SMB packet");
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
req = smbsrv_init_request(smb_conn);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
req->in.buffer = talloc_steal(req, blob.data);
|
||||
req->in.size = blob.length;
|
||||
req->request_time = cur_time;
|
||||
req->chained_fnum = -1;
|
||||
req->in.allocated = req->in.size;
|
||||
req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
|
||||
req->in.vwv = req->in.hdr + HDR_VWV;
|
||||
req->in.wct = CVAL(req->in.hdr, HDR_WCT);
|
||||
if (req->in.vwv + VWV(req->in.wct) <= req->in.buffer + req->in.size) {
|
||||
req->in.data = req->in.vwv + VWV(req->in.wct) + 2;
|
||||
req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct));
|
||||
|
||||
/* the bcc length is only 16 bits, but some packets
|
||||
(such as SMBwriteX) can be much larger than 64k. We
|
||||
detect this by looking for a large non-chained NBT
|
||||
packet (at least 64k bigger than what is
|
||||
specified). If it is detected then the NBT size is
|
||||
used instead of the bcc size */
|
||||
if (req->in.data_size + 0x10000 <=
|
||||
req->in.size - PTR_DIFF(req->in.data, req->in.buffer) &&
|
||||
(req->in.wct < 1 || SVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE)) {
|
||||
/* its an oversized packet! fun for all the family */
|
||||
req->in.data_size = req->in.size - PTR_DIFF(req->in.data,req->in.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct > req->in.size) {
|
||||
DEBUG(2,("Invalid SMB word count %d\n", req->in.wct));
|
||||
smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct + req->in.data_size > req->in.size) {
|
||||
DEBUG(2,("Invalid SMB buffer length count %d\n",
|
||||
(int)req->in.data_size));
|
||||
smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
|
||||
|
||||
if (!smbsrv_signing_check_incoming(req)) {
|
||||
smbsrv_send_error(req, NT_STATUS_ACCESS_DENIED);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
command = CVAL(req->in.hdr, HDR_COM);
|
||||
switch_message(command, req);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
These flags determine some of the permissions required to do an operation
|
||||
*/
|
||||
#define NEED_SESS (1<<0)
|
||||
#define NEED_TCON (1<<1)
|
||||
#define SIGNING_NO_REPLY (1<<2)
|
||||
|
||||
/*
|
||||
define a list of possible SMB messages and their corresponding
|
||||
functions. Any message that has a NULL function is unimplemented -
|
||||
please feel free to contribute implementations!
|
||||
*/
|
||||
static const struct smb_message_struct
|
||||
{
|
||||
const char *name;
|
||||
void (*fn)(struct smbsrv_request *);
|
||||
int flags;
|
||||
}
|
||||
smb_messages[256] = {
|
||||
/* 0x00 */ { "SMBmkdir", smbsrv_reply_mkdir, NEED_SESS|NEED_TCON },
|
||||
/* 0x01 */ { "SMBrmdir", smbsrv_reply_rmdir, NEED_SESS|NEED_TCON },
|
||||
/* 0x02 */ { "SMBopen", smbsrv_reply_open, NEED_SESS|NEED_TCON },
|
||||
/* 0x03 */ { "SMBcreate", smbsrv_reply_mknew, NEED_SESS|NEED_TCON },
|
||||
/* 0x04 */ { "SMBclose", smbsrv_reply_close, NEED_SESS|NEED_TCON },
|
||||
/* 0x05 */ { "SMBflush", smbsrv_reply_flush, NEED_SESS|NEED_TCON },
|
||||
/* 0x06 */ { "SMBunlink", smbsrv_reply_unlink, NEED_SESS|NEED_TCON },
|
||||
/* 0x07 */ { "SMBmv", smbsrv_reply_mv, NEED_SESS|NEED_TCON },
|
||||
/* 0x08 */ { "SMBgetatr", smbsrv_reply_getatr, NEED_SESS|NEED_TCON },
|
||||
/* 0x09 */ { "SMBsetatr", smbsrv_reply_setatr, NEED_SESS|NEED_TCON },
|
||||
/* 0x0a */ { "SMBread", smbsrv_reply_read, NEED_SESS|NEED_TCON },
|
||||
/* 0x0b */ { "SMBwrite", smbsrv_reply_write, NEED_SESS|NEED_TCON },
|
||||
/* 0x0c */ { "SMBlock", smbsrv_reply_lock, NEED_SESS|NEED_TCON },
|
||||
/* 0x0d */ { "SMBunlock", smbsrv_reply_unlock, NEED_SESS|NEED_TCON },
|
||||
/* 0x0e */ { "SMBctemp", smbsrv_reply_ctemp, NEED_SESS|NEED_TCON },
|
||||
/* 0x0f */ { "SMBmknew", smbsrv_reply_mknew, NEED_SESS|NEED_TCON },
|
||||
/* 0x10 */ { "SMBchkpth", smbsrv_reply_chkpth, NEED_SESS|NEED_TCON },
|
||||
/* 0x11 */ { "SMBexit", smbsrv_reply_exit, NEED_SESS },
|
||||
/* 0x12 */ { "SMBlseek", smbsrv_reply_lseek, NEED_SESS|NEED_TCON },
|
||||
/* 0x13 */ { "SMBlockread", smbsrv_reply_lockread, NEED_SESS|NEED_TCON },
|
||||
/* 0x14 */ { "SMBwriteunlock", smbsrv_reply_writeunlock, NEED_SESS|NEED_TCON },
|
||||
/* 0x15 */ { NULL, NULL, 0 },
|
||||
/* 0x16 */ { NULL, NULL, 0 },
|
||||
/* 0x17 */ { NULL, NULL, 0 },
|
||||
/* 0x18 */ { NULL, NULL, 0 },
|
||||
/* 0x19 */ { NULL, NULL, 0 },
|
||||
/* 0x1a */ { "SMBreadbraw", smbsrv_reply_readbraw, NEED_SESS|NEED_TCON },
|
||||
/* 0x1b */ { "SMBreadBmpx", smbsrv_reply_readbmpx, NEED_SESS|NEED_TCON },
|
||||
/* 0x1c */ { "SMBreadBs", NULL, 0 },
|
||||
/* 0x1d */ { "SMBwritebraw", smbsrv_reply_writebraw, NEED_SESS|NEED_TCON },
|
||||
/* 0x1e */ { "SMBwriteBmpx", smbsrv_reply_writebmpx, NEED_SESS|NEED_TCON },
|
||||
/* 0x1f */ { "SMBwriteBs", smbsrv_reply_writebs, NEED_SESS|NEED_TCON },
|
||||
/* 0x20 */ { "SMBwritec", NULL, 0 },
|
||||
/* 0x21 */ { NULL, NULL, 0 },
|
||||
/* 0x22 */ { "SMBsetattrE", smbsrv_reply_setattrE, NEED_SESS|NEED_TCON },
|
||||
/* 0x23 */ { "SMBgetattrE", smbsrv_reply_getattrE, NEED_SESS|NEED_TCON },
|
||||
/* 0x24 */ { "SMBlockingX", smbsrv_reply_lockingX, NEED_SESS|NEED_TCON },
|
||||
/* 0x25 */ { "SMBtrans", smbsrv_reply_trans, NEED_SESS|NEED_TCON },
|
||||
/* 0x26 */ { "SMBtranss", smbsrv_reply_transs, NEED_SESS|NEED_TCON },
|
||||
/* 0x27 */ { "SMBioctl", smbsrv_reply_ioctl, NEED_SESS|NEED_TCON },
|
||||
/* 0x28 */ { "SMBioctls", NULL, NEED_SESS|NEED_TCON },
|
||||
/* 0x29 */ { "SMBcopy", smbsrv_reply_copy, NEED_SESS|NEED_TCON },
|
||||
/* 0x2a */ { "SMBmove", NULL, NEED_SESS|NEED_TCON },
|
||||
/* 0x2b */ { "SMBecho", smbsrv_reply_echo, 0 },
|
||||
/* 0x2c */ { "SMBwriteclose", smbsrv_reply_writeclose, NEED_SESS|NEED_TCON },
|
||||
/* 0x2d */ { "SMBopenX", smbsrv_reply_open_and_X, NEED_SESS|NEED_TCON },
|
||||
/* 0x2e */ { "SMBreadX", smbsrv_reply_read_and_X, NEED_SESS|NEED_TCON },
|
||||
/* 0x2f */ { "SMBwriteX", smbsrv_reply_write_and_X, NEED_SESS|NEED_TCON},
|
||||
/* 0x30 */ { NULL, NULL, 0 },
|
||||
/* 0x31 */ { NULL, NULL, 0 },
|
||||
/* 0x32 */ { "SMBtrans2", smbsrv_reply_trans2, NEED_SESS|NEED_TCON },
|
||||
/* 0x33 */ { "SMBtranss2", smbsrv_reply_transs2, NEED_SESS|NEED_TCON },
|
||||
/* 0x34 */ { "SMBfindclose", smbsrv_reply_findclose, NEED_SESS|NEED_TCON },
|
||||
/* 0x35 */ { "SMBfindnclose", smbsrv_reply_findnclose, NEED_SESS|NEED_TCON },
|
||||
/* 0x36 */ { NULL, NULL, 0 },
|
||||
/* 0x37 */ { NULL, NULL, 0 },
|
||||
/* 0x38 */ { NULL, NULL, 0 },
|
||||
/* 0x39 */ { NULL, NULL, 0 },
|
||||
/* 0x3a */ { NULL, NULL, 0 },
|
||||
/* 0x3b */ { NULL, NULL, 0 },
|
||||
/* 0x3c */ { NULL, NULL, 0 },
|
||||
/* 0x3d */ { NULL, NULL, 0 },
|
||||
/* 0x3e */ { NULL, NULL, 0 },
|
||||
/* 0x3f */ { NULL, NULL, 0 },
|
||||
/* 0x40 */ { NULL, NULL, 0 },
|
||||
/* 0x41 */ { NULL, NULL, 0 },
|
||||
/* 0x42 */ { NULL, NULL, 0 },
|
||||
/* 0x43 */ { NULL, NULL, 0 },
|
||||
/* 0x44 */ { NULL, NULL, 0 },
|
||||
/* 0x45 */ { NULL, NULL, 0 },
|
||||
/* 0x46 */ { NULL, NULL, 0 },
|
||||
/* 0x47 */ { NULL, NULL, 0 },
|
||||
/* 0x48 */ { NULL, NULL, 0 },
|
||||
/* 0x49 */ { NULL, NULL, 0 },
|
||||
/* 0x4a */ { NULL, NULL, 0 },
|
||||
/* 0x4b */ { NULL, NULL, 0 },
|
||||
/* 0x4c */ { NULL, NULL, 0 },
|
||||
/* 0x4d */ { NULL, NULL, 0 },
|
||||
/* 0x4e */ { NULL, NULL, 0 },
|
||||
/* 0x4f */ { NULL, NULL, 0 },
|
||||
/* 0x50 */ { NULL, NULL, 0 },
|
||||
/* 0x51 */ { NULL, NULL, 0 },
|
||||
/* 0x52 */ { NULL, NULL, 0 },
|
||||
/* 0x53 */ { NULL, NULL, 0 },
|
||||
/* 0x54 */ { NULL, NULL, 0 },
|
||||
/* 0x55 */ { NULL, NULL, 0 },
|
||||
/* 0x56 */ { NULL, NULL, 0 },
|
||||
/* 0x57 */ { NULL, NULL, 0 },
|
||||
/* 0x58 */ { NULL, NULL, 0 },
|
||||
/* 0x59 */ { NULL, NULL, 0 },
|
||||
/* 0x5a */ { NULL, NULL, 0 },
|
||||
/* 0x5b */ { NULL, NULL, 0 },
|
||||
/* 0x5c */ { NULL, NULL, 0 },
|
||||
/* 0x5d */ { NULL, NULL, 0 },
|
||||
/* 0x5e */ { NULL, NULL, 0 },
|
||||
/* 0x5f */ { NULL, NULL, 0 },
|
||||
/* 0x60 */ { NULL, NULL, 0 },
|
||||
/* 0x61 */ { NULL, NULL, 0 },
|
||||
/* 0x62 */ { NULL, NULL, 0 },
|
||||
/* 0x63 */ { NULL, NULL, 0 },
|
||||
/* 0x64 */ { NULL, NULL, 0 },
|
||||
/* 0x65 */ { NULL, NULL, 0 },
|
||||
/* 0x66 */ { NULL, NULL, 0 },
|
||||
/* 0x67 */ { NULL, NULL, 0 },
|
||||
/* 0x68 */ { NULL, NULL, 0 },
|
||||
/* 0x69 */ { NULL, NULL, 0 },
|
||||
/* 0x6a */ { NULL, NULL, 0 },
|
||||
/* 0x6b */ { NULL, NULL, 0 },
|
||||
/* 0x6c */ { NULL, NULL, 0 },
|
||||
/* 0x6d */ { NULL, NULL, 0 },
|
||||
/* 0x6e */ { NULL, NULL, 0 },
|
||||
/* 0x6f */ { NULL, NULL, 0 },
|
||||
/* 0x70 */ { "SMBtcon", smbsrv_reply_tcon, NEED_SESS },
|
||||
/* 0x71 */ { "SMBtdis", smbsrv_reply_tdis, NEED_TCON },
|
||||
/* 0x72 */ { "SMBnegprot", smbsrv_reply_negprot, 0 },
|
||||
/* 0x73 */ { "SMBsesssetupX", smbsrv_reply_sesssetup, 0 },
|
||||
/* 0x74 */ { "SMBulogoffX", smbsrv_reply_ulogoffX, NEED_SESS }, /* ulogoff doesn't give a valid TID */
|
||||
/* 0x75 */ { "SMBtconX", smbsrv_reply_tcon_and_X, NEED_SESS },
|
||||
/* 0x76 */ { NULL, NULL, 0 },
|
||||
/* 0x77 */ { NULL, NULL, 0 },
|
||||
/* 0x78 */ { NULL, NULL, 0 },
|
||||
/* 0x79 */ { NULL, NULL, 0 },
|
||||
/* 0x7a */ { NULL, NULL, 0 },
|
||||
/* 0x7b */ { NULL, NULL, 0 },
|
||||
/* 0x7c */ { NULL, NULL, 0 },
|
||||
/* 0x7d */ { NULL, NULL, 0 },
|
||||
/* 0x7e */ { NULL, NULL, 0 },
|
||||
/* 0x7f */ { NULL, NULL, 0 },
|
||||
/* 0x80 */ { "SMBdskattr", smbsrv_reply_dskattr, NEED_SESS|NEED_TCON },
|
||||
/* 0x81 */ { "SMBsearch", smbsrv_reply_search, NEED_SESS|NEED_TCON },
|
||||
/* 0x82 */ { "SMBffirst", smbsrv_reply_search, NEED_SESS|NEED_TCON },
|
||||
/* 0x83 */ { "SMBfunique", smbsrv_reply_search, NEED_SESS|NEED_TCON },
|
||||
/* 0x84 */ { "SMBfclose", smbsrv_reply_fclose, NEED_SESS|NEED_TCON },
|
||||
/* 0x85 */ { NULL, NULL, 0 },
|
||||
/* 0x86 */ { NULL, NULL, 0 },
|
||||
/* 0x87 */ { NULL, NULL, 0 },
|
||||
/* 0x88 */ { NULL, NULL, 0 },
|
||||
/* 0x89 */ { NULL, NULL, 0 },
|
||||
/* 0x8a */ { NULL, NULL, 0 },
|
||||
/* 0x8b */ { NULL, NULL, 0 },
|
||||
/* 0x8c */ { NULL, NULL, 0 },
|
||||
/* 0x8d */ { NULL, NULL, 0 },
|
||||
/* 0x8e */ { NULL, NULL, 0 },
|
||||
/* 0x8f */ { NULL, NULL, 0 },
|
||||
/* 0x90 */ { NULL, NULL, 0 },
|
||||
/* 0x91 */ { NULL, NULL, 0 },
|
||||
/* 0x92 */ { NULL, NULL, 0 },
|
||||
/* 0x93 */ { NULL, NULL, 0 },
|
||||
/* 0x94 */ { NULL, NULL, 0 },
|
||||
/* 0x95 */ { NULL, NULL, 0 },
|
||||
/* 0x96 */ { NULL, NULL, 0 },
|
||||
/* 0x97 */ { NULL, NULL, 0 },
|
||||
/* 0x98 */ { NULL, NULL, 0 },
|
||||
/* 0x99 */ { NULL, NULL, 0 },
|
||||
/* 0x9a */ { NULL, NULL, 0 },
|
||||
/* 0x9b */ { NULL, NULL, 0 },
|
||||
/* 0x9c */ { NULL, NULL, 0 },
|
||||
/* 0x9d */ { NULL, NULL, 0 },
|
||||
/* 0x9e */ { NULL, NULL, 0 },
|
||||
/* 0x9f */ { NULL, NULL, 0 },
|
||||
/* 0xa0 */ { "SMBnttrans", smbsrv_reply_nttrans, NEED_SESS|NEED_TCON },
|
||||
/* 0xa1 */ { "SMBnttranss", smbsrv_reply_nttranss, NEED_SESS|NEED_TCON },
|
||||
/* 0xa2 */ { "SMBntcreateX", smbsrv_reply_ntcreate_and_X, NEED_SESS|NEED_TCON },
|
||||
/* 0xa3 */ { NULL, NULL, 0 },
|
||||
/* 0xa4 */ { "SMBntcancel", smbsrv_reply_ntcancel, NEED_SESS|NEED_TCON|SIGNING_NO_REPLY },
|
||||
/* 0xa5 */ { "SMBntrename", smbsrv_reply_ntrename, NEED_SESS|NEED_TCON },
|
||||
/* 0xa6 */ { NULL, NULL, 0 },
|
||||
/* 0xa7 */ { NULL, NULL, 0 },
|
||||
/* 0xa8 */ { NULL, NULL, 0 },
|
||||
/* 0xa9 */ { NULL, NULL, 0 },
|
||||
/* 0xaa */ { NULL, NULL, 0 },
|
||||
/* 0xab */ { NULL, NULL, 0 },
|
||||
/* 0xac */ { NULL, NULL, 0 },
|
||||
/* 0xad */ { NULL, NULL, 0 },
|
||||
/* 0xae */ { NULL, NULL, 0 },
|
||||
/* 0xaf */ { NULL, NULL, 0 },
|
||||
/* 0xb0 */ { NULL, NULL, 0 },
|
||||
/* 0xb1 */ { NULL, NULL, 0 },
|
||||
/* 0xb2 */ { NULL, NULL, 0 },
|
||||
/* 0xb3 */ { NULL, NULL, 0 },
|
||||
/* 0xb4 */ { NULL, NULL, 0 },
|
||||
/* 0xb5 */ { NULL, NULL, 0 },
|
||||
/* 0xb6 */ { NULL, NULL, 0 },
|
||||
/* 0xb7 */ { NULL, NULL, 0 },
|
||||
/* 0xb8 */ { NULL, NULL, 0 },
|
||||
/* 0xb9 */ { NULL, NULL, 0 },
|
||||
/* 0xba */ { NULL, NULL, 0 },
|
||||
/* 0xbb */ { NULL, NULL, 0 },
|
||||
/* 0xbc */ { NULL, NULL, 0 },
|
||||
/* 0xbd */ { NULL, NULL, 0 },
|
||||
/* 0xbe */ { NULL, NULL, 0 },
|
||||
/* 0xbf */ { NULL, NULL, 0 },
|
||||
/* 0xc0 */ { "SMBsplopen", smbsrv_reply_printopen, NEED_SESS|NEED_TCON },
|
||||
/* 0xc1 */ { "SMBsplwr", smbsrv_reply_printwrite, NEED_SESS|NEED_TCON },
|
||||
/* 0xc2 */ { "SMBsplclose", smbsrv_reply_printclose, NEED_SESS|NEED_TCON },
|
||||
/* 0xc3 */ { "SMBsplretq", smbsrv_reply_printqueue, NEED_SESS|NEED_TCON },
|
||||
/* 0xc4 */ { NULL, NULL, 0 },
|
||||
/* 0xc5 */ { NULL, NULL, 0 },
|
||||
/* 0xc6 */ { NULL, NULL, 0 },
|
||||
/* 0xc7 */ { NULL, NULL, 0 },
|
||||
/* 0xc8 */ { NULL, NULL, 0 },
|
||||
/* 0xc9 */ { NULL, NULL, 0 },
|
||||
/* 0xca */ { NULL, NULL, 0 },
|
||||
/* 0xcb */ { NULL, NULL, 0 },
|
||||
/* 0xcc */ { NULL, NULL, 0 },
|
||||
/* 0xcd */ { NULL, NULL, 0 },
|
||||
/* 0xce */ { NULL, NULL, 0 },
|
||||
/* 0xcf */ { NULL, NULL, 0 },
|
||||
/* 0xd0 */ { "SMBsends", NULL, 0 },
|
||||
/* 0xd1 */ { "SMBsendb", NULL, 0 },
|
||||
/* 0xd2 */ { "SMBfwdname", NULL, 0 },
|
||||
/* 0xd3 */ { "SMBcancelf", NULL, 0 },
|
||||
/* 0xd4 */ { "SMBgetmac", NULL, 0 },
|
||||
/* 0xd5 */ { "SMBsendstrt", NULL, 0 },
|
||||
/* 0xd6 */ { "SMBsendend", NULL, 0 },
|
||||
/* 0xd7 */ { "SMBsendtxt", NULL, 0 },
|
||||
/* 0xd8 */ { NULL, NULL, 0 },
|
||||
/* 0xd9 */ { NULL, NULL, 0 },
|
||||
/* 0xda */ { NULL, NULL, 0 },
|
||||
/* 0xdb */ { NULL, NULL, 0 },
|
||||
/* 0xdc */ { NULL, NULL, 0 },
|
||||
/* 0xdd */ { NULL, NULL, 0 },
|
||||
/* 0xde */ { NULL, NULL, 0 },
|
||||
/* 0xdf */ { NULL, NULL, 0 },
|
||||
/* 0xe0 */ { NULL, NULL, 0 },
|
||||
/* 0xe1 */ { NULL, NULL, 0 },
|
||||
/* 0xe2 */ { NULL, NULL, 0 },
|
||||
/* 0xe3 */ { NULL, NULL, 0 },
|
||||
/* 0xe4 */ { NULL, NULL, 0 },
|
||||
/* 0xe5 */ { NULL, NULL, 0 },
|
||||
/* 0xe6 */ { NULL, NULL, 0 },
|
||||
/* 0xe7 */ { NULL, NULL, 0 },
|
||||
/* 0xe8 */ { NULL, NULL, 0 },
|
||||
/* 0xe9 */ { NULL, NULL, 0 },
|
||||
/* 0xea */ { NULL, NULL, 0 },
|
||||
/* 0xeb */ { NULL, NULL, 0 },
|
||||
/* 0xec */ { NULL, NULL, 0 },
|
||||
/* 0xed */ { NULL, NULL, 0 },
|
||||
/* 0xee */ { NULL, NULL, 0 },
|
||||
/* 0xef */ { NULL, NULL, 0 },
|
||||
/* 0xf0 */ { NULL, NULL, 0 },
|
||||
/* 0xf1 */ { NULL, NULL, 0 },
|
||||
/* 0xf2 */ { NULL, NULL, 0 },
|
||||
/* 0xf3 */ { NULL, NULL, 0 },
|
||||
/* 0xf4 */ { NULL, NULL, 0 },
|
||||
/* 0xf5 */ { NULL, NULL, 0 },
|
||||
/* 0xf6 */ { NULL, NULL, 0 },
|
||||
/* 0xf7 */ { NULL, NULL, 0 },
|
||||
/* 0xf8 */ { NULL, NULL, 0 },
|
||||
/* 0xf9 */ { NULL, NULL, 0 },
|
||||
/* 0xfa */ { NULL, NULL, 0 },
|
||||
/* 0xfb */ { NULL, NULL, 0 },
|
||||
/* 0xfc */ { NULL, NULL, 0 },
|
||||
/* 0xfd */ { NULL, NULL, 0 },
|
||||
/* 0xfe */ { NULL, NULL, 0 },
|
||||
/* 0xff */ { NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
return a string containing the function name of a SMB command
|
||||
****************************************************************************/
|
||||
static const char *smb_fn_name(uint8_t type)
|
||||
{
|
||||
const char *unknown_name = "SMBunknown";
|
||||
|
||||
if (smb_messages[type].name == NULL)
|
||||
return unknown_name;
|
||||
|
||||
return smb_messages[type].name;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Do a switch on the message type and call the specific reply function for this
|
||||
message. Unlike earlier versions of Samba the reply functions are responsible
|
||||
for sending the reply themselves, rather than returning a size to this function
|
||||
The reply functions may also choose to delay the processing by pushing the message
|
||||
onto the message queue
|
||||
****************************************************************************/
|
||||
static void switch_message(int type, struct smbsrv_request *req)
|
||||
{
|
||||
int flags;
|
||||
struct smbsrv_connection *smb_conn = req->smb_conn;
|
||||
NTSTATUS status;
|
||||
|
||||
type &= 0xff;
|
||||
|
||||
errno = 0;
|
||||
|
||||
if (smb_messages[type].fn == NULL) {
|
||||
DEBUG(0,("Unknown message type %d!\n",type));
|
||||
smbsrv_reply_unknown(req);
|
||||
return;
|
||||
}
|
||||
|
||||
flags = smb_messages[type].flags;
|
||||
|
||||
req->tcon = smbsrv_smb_tcon_find(smb_conn, SVAL(req->in.hdr,HDR_TID), req->request_time);
|
||||
|
||||
if (!req->session) {
|
||||
/* setup the user context for this request if it
|
||||
hasn't already been initialised (to cope with SMB
|
||||
chaining) */
|
||||
|
||||
/* In share mode security we must ignore the vuid. */
|
||||
if (smb_conn->config.security == SEC_SHARE) {
|
||||
if (req->tcon) {
|
||||
req->session = req->tcon->sec_share.session;
|
||||
}
|
||||
} else {
|
||||
req->session = smbsrv_session_find(req->smb_conn, SVAL(req->in.hdr,HDR_UID), req->request_time);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG(5,("switch message %s (task_id %d)\n",smb_fn_name(type), req->smb_conn->connection->server_id));
|
||||
|
||||
/* this must be called before we do any reply */
|
||||
if (flags & SIGNING_NO_REPLY) {
|
||||
smbsrv_signing_no_reply(req);
|
||||
}
|
||||
|
||||
/* see if the vuid is valid */
|
||||
if ((flags & NEED_SESS) && !req->session) {
|
||||
status = NT_STATUS_DOS(ERRSRV, ERRbaduid);
|
||||
/* amazingly, the error code depends on the command */
|
||||
switch (type) {
|
||||
case SMBntcreateX:
|
||||
case SMBntcancel:
|
||||
case SMBulogoffX:
|
||||
break;
|
||||
default:
|
||||
if (req->smb_conn->config.nt_status_support &&
|
||||
req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
|
||||
status = NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* TODO:
|
||||
* don't know how to handle smb signing for this case
|
||||
* so just skip the reply
|
||||
*/
|
||||
if ((flags & SIGNING_NO_REPLY) &&
|
||||
(req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF)) {
|
||||
DEBUG(1,("SKIP ERROR REPLY: %s %s because of unknown smb signing case\n",
|
||||
smb_fn_name(type), nt_errstr(status)));
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smbsrv_send_error(req, status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* does this protocol need a valid tree connection? */
|
||||
if ((flags & NEED_TCON) && !req->tcon) {
|
||||
status = NT_STATUS_DOS(ERRSRV, ERRinvnid);
|
||||
/* amazingly, the error code depends on the command */
|
||||
switch (type) {
|
||||
case SMBntcreateX:
|
||||
case SMBntcancel:
|
||||
case SMBtdis:
|
||||
break;
|
||||
default:
|
||||
if (req->smb_conn->config.nt_status_support &&
|
||||
req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
|
||||
status = NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* TODO:
|
||||
* don't know how to handle smb signing for this case
|
||||
* so just skip the reply
|
||||
*/
|
||||
if ((flags & SIGNING_NO_REPLY) &&
|
||||
(req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF)) {
|
||||
DEBUG(1,("SKIP ERROR REPLY: %s %s because of unknown smb signing case\n",
|
||||
smb_fn_name(type), nt_errstr(status)));
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smbsrv_send_error(req, status);
|
||||
return;
|
||||
}
|
||||
|
||||
smb_messages[type].fn(req);
|
||||
}
|
||||
|
||||
/*
|
||||
we call this when first first part of a possibly chained request has been completed
|
||||
and we need to call the 2nd part, if any
|
||||
*/
|
||||
void smbsrv_chain_reply(struct smbsrv_request *req)
|
||||
{
|
||||
uint16_t chain_cmd, chain_offset;
|
||||
uint8_t *vwv, *data;
|
||||
uint16_t wct;
|
||||
uint16_t data_size;
|
||||
|
||||
if (req->in.wct < 2 || req->out.wct < 2) {
|
||||
smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
|
||||
return;
|
||||
}
|
||||
|
||||
chain_cmd = CVAL(req->in.vwv, VWV(0));
|
||||
chain_offset = SVAL(req->in.vwv, VWV(1));
|
||||
|
||||
if (chain_cmd == SMB_CHAIN_NONE) {
|
||||
/* end of chain */
|
||||
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
|
||||
SSVAL(req->out.vwv, VWV(1), 0);
|
||||
smbsrv_send_reply(req);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chain_offset + req->in.hdr >= req->in.buffer + req->in.size) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
wct = CVAL(req->in.hdr, chain_offset);
|
||||
vwv = req->in.hdr + chain_offset + 1;
|
||||
|
||||
if (vwv + VWV(wct) + 2 > req->in.buffer + req->in.size) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
data_size = SVAL(vwv, VWV(wct));
|
||||
data = vwv + VWV(wct) + 2;
|
||||
|
||||
if (data + data_size > req->in.buffer + req->in.size) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* all seems legit */
|
||||
req->in.vwv = vwv;
|
||||
req->in.wct = wct;
|
||||
req->in.data = data;
|
||||
req->in.data_size = data_size;
|
||||
req->in.ptr = data;
|
||||
|
||||
req->chain_count++;
|
||||
|
||||
SSVAL(req->out.vwv, VWV(0), chain_cmd);
|
||||
SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE);
|
||||
|
||||
/* cleanup somestuff for the next request */
|
||||
talloc_free(req->ntvfs);
|
||||
req->ntvfs = NULL;
|
||||
talloc_free(req->io_ptr);
|
||||
req->io_ptr = NULL;
|
||||
|
||||
switch_message(chain_cmd, req);
|
||||
return;
|
||||
|
||||
error:
|
||||
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
|
||||
SSVAL(req->out.vwv, VWV(1), 0);
|
||||
smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
|
||||
}
|
||||
|
||||
/*
|
||||
* init the SMB protocol related stuff
|
||||
*/
|
||||
NTSTATUS smbsrv_init_smb_connection(struct smbsrv_connection *smb_conn)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
/* now initialise a few default values associated with this smb socket */
|
||||
smb_conn->negotiate.max_send = 0xFFFF;
|
||||
|
||||
/* this is the size that w2k uses, and it appears to be important for
|
||||
good performance */
|
||||
smb_conn->negotiate.max_recv = lp_max_xmit();
|
||||
|
||||
smb_conn->negotiate.zone_offset = get_time_zone(time(NULL));
|
||||
|
||||
smb_conn->config.security = lp_security();
|
||||
smb_conn->config.nt_status_support = lp_nt_status_support();
|
||||
|
||||
status = smbsrv_init_sessions(smb_conn, UINT16_MAX);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = smbsrv_smb_init_tcons(smb_conn);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
smbsrv_init_signing(smb_conn);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,760 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
this file implements functions for manipulating the 'struct smbsrv_request' structure in smbd
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "lib/stream/packet.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
|
||||
/* we over allocate the data buffer to prevent too many realloc calls */
|
||||
#define REQ_OVER_ALLOCATION 0
|
||||
|
||||
static int smbsrv_request_destructor(struct smbsrv_request *req)
|
||||
{
|
||||
DLIST_REMOVE(req->smb_conn->requests, req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
construct a basic request packet, mostly used to construct async packets
|
||||
such as change notify and oplock break requests
|
||||
****************************************************************************/
|
||||
struct smbsrv_request *smbsrv_init_request(struct smbsrv_connection *smb_conn)
|
||||
{
|
||||
struct smbsrv_request *req;
|
||||
|
||||
req = talloc_zero(smb_conn, struct smbsrv_request);
|
||||
if (!req) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* setup the request context */
|
||||
req->smb_conn = smb_conn;
|
||||
|
||||
talloc_set_destructor(req, smbsrv_request_destructor);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
setup a chained reply in req->out with the given word count and initial data buffer size.
|
||||
*/
|
||||
static void req_setup_chain_reply(struct smbsrv_request *req, uint_t wct, uint_t buflen)
|
||||
{
|
||||
uint32_t chain_base_size = req->out.size;
|
||||
|
||||
/* we need room for the wct value, the words, the buffer length and the buffer */
|
||||
req->out.size += 1 + VWV(wct) + 2 + buflen;
|
||||
|
||||
/* over allocate by a small amount */
|
||||
req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
|
||||
|
||||
req->out.buffer = talloc_realloc(req, req->out.buffer,
|
||||
uint8_t, req->out.allocated);
|
||||
if (!req->out.buffer) {
|
||||
smbsrv_terminate_connection(req->smb_conn, "allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
|
||||
req->out.vwv = req->out.buffer + chain_base_size + 1;
|
||||
req->out.wct = wct;
|
||||
req->out.data = req->out.vwv + VWV(wct) + 2;
|
||||
req->out.data_size = buflen;
|
||||
req->out.ptr = req->out.data;
|
||||
|
||||
SCVAL(req->out.buffer, chain_base_size, wct);
|
||||
SSVAL(req->out.vwv, VWV(wct), buflen);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
setup a reply in req->out with the given word count and initial data buffer size.
|
||||
the caller will then fill in the command words and data before calling req_send_reply() to
|
||||
send the reply on its way
|
||||
*/
|
||||
void smbsrv_setup_reply(struct smbsrv_request *req, uint_t wct, size_t buflen)
|
||||
{
|
||||
uint16_t flags2;
|
||||
|
||||
if (req->chain_count != 0) {
|
||||
req_setup_chain_reply(req, wct, buflen);
|
||||
return;
|
||||
}
|
||||
|
||||
req->out.size = NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + buflen;
|
||||
|
||||
/* over allocate by a small amount */
|
||||
req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
|
||||
|
||||
req->out.buffer = talloc_size(req, req->out.allocated);
|
||||
if (!req->out.buffer) {
|
||||
smbsrv_terminate_connection(req->smb_conn, "allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
flags2 = FLAGS2_LONG_PATH_COMPONENTS |
|
||||
FLAGS2_EXTENDED_ATTRIBUTES |
|
||||
FLAGS2_IS_LONG_NAME;
|
||||
flags2 |= (req->flags2 & (FLAGS2_UNICODE_STRINGS|FLAGS2_EXTENDED_SECURITY));
|
||||
if (req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
|
||||
flags2 |= FLAGS2_32_BIT_ERROR_CODES;
|
||||
}
|
||||
|
||||
req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
|
||||
req->out.vwv = req->out.hdr + HDR_VWV;
|
||||
req->out.wct = wct;
|
||||
req->out.data = req->out.vwv + VWV(wct) + 2;
|
||||
req->out.data_size = buflen;
|
||||
req->out.ptr = req->out.data;
|
||||
|
||||
SIVAL(req->out.hdr, HDR_RCLS, 0);
|
||||
|
||||
SCVAL(req->out.hdr, HDR_WCT, wct);
|
||||
SSVAL(req->out.vwv, VWV(wct), buflen);
|
||||
|
||||
memcpy(req->out.hdr, "\377SMB", 4);
|
||||
SCVAL(req->out.hdr,HDR_FLG, FLAG_REPLY | FLAG_CASELESS_PATHNAMES);
|
||||
SSVAL(req->out.hdr,HDR_FLG2, flags2);
|
||||
SSVAL(req->out.hdr,HDR_PIDHIGH,0);
|
||||
memset(req->out.hdr + HDR_SS_FIELD, 0, 10);
|
||||
|
||||
if (req->in.hdr) {
|
||||
/* copy the cmd, tid, pid, uid and mid from the request */
|
||||
SCVAL(req->out.hdr,HDR_COM,CVAL(req->in.hdr,HDR_COM));
|
||||
SSVAL(req->out.hdr,HDR_TID,SVAL(req->in.hdr,HDR_TID));
|
||||
SSVAL(req->out.hdr,HDR_PID,SVAL(req->in.hdr,HDR_PID));
|
||||
SSVAL(req->out.hdr,HDR_UID,SVAL(req->in.hdr,HDR_UID));
|
||||
SSVAL(req->out.hdr,HDR_MID,SVAL(req->in.hdr,HDR_MID));
|
||||
} else {
|
||||
SCVAL(req->out.hdr,HDR_COM,0);
|
||||
SSVAL(req->out.hdr,HDR_TID,0);
|
||||
SSVAL(req->out.hdr,HDR_PID,0);
|
||||
SSVAL(req->out.hdr,HDR_UID,0);
|
||||
SSVAL(req->out.hdr,HDR_MID,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
setup a copy of a request, used when the server needs to send
|
||||
more than one reply for a single request packet
|
||||
*/
|
||||
struct smbsrv_request *smbsrv_setup_secondary_request(struct smbsrv_request *old_req)
|
||||
{
|
||||
struct smbsrv_request *req;
|
||||
ptrdiff_t diff;
|
||||
|
||||
req = talloc_memdup(old_req, old_req, sizeof(struct smbsrv_request));
|
||||
if (req == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
req->out.buffer = talloc_memdup(req, req->out.buffer, req->out.allocated);
|
||||
if (req->out.buffer == NULL) {
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
diff = req->out.buffer - old_req->out.buffer;
|
||||
|
||||
req->out.hdr += diff;
|
||||
req->out.vwv += diff;
|
||||
req->out.data += diff;
|
||||
req->out.ptr += diff;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/*
|
||||
work out the maximum data size we will allow for this reply, given
|
||||
the negotiated max_xmit. The basic reply packet must be setup before
|
||||
this call
|
||||
|
||||
note that this is deliberately a signed integer reply
|
||||
*/
|
||||
int req_max_data(struct smbsrv_request *req)
|
||||
{
|
||||
int ret;
|
||||
ret = req->smb_conn->negotiate.max_send;
|
||||
ret -= PTR_DIFF(req->out.data, req->out.hdr);
|
||||
if (ret < 0) ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
grow the allocation of the data buffer portion of a reply
|
||||
packet. Note that as this can reallocate the packet buffer this
|
||||
invalidates any local pointers into the packet.
|
||||
|
||||
To cope with this req->out.ptr is supplied. This will be updated to
|
||||
point at the same offset into the packet as before this call
|
||||
*/
|
||||
static void req_grow_allocation(struct smbsrv_request *req, uint_t new_size)
|
||||
{
|
||||
int delta;
|
||||
uint8_t *buf2;
|
||||
|
||||
delta = new_size - req->out.data_size;
|
||||
if (delta + req->out.size <= req->out.allocated) {
|
||||
/* it fits in the preallocation */
|
||||
return;
|
||||
}
|
||||
|
||||
/* we need to realloc */
|
||||
req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION;
|
||||
buf2 = talloc_realloc(req, req->out.buffer, uint8_t, req->out.allocated);
|
||||
if (buf2 == NULL) {
|
||||
smb_panic("out of memory in req_grow_allocation");
|
||||
}
|
||||
|
||||
if (buf2 == req->out.buffer) {
|
||||
/* the malloc library gave us the same pointer */
|
||||
return;
|
||||
}
|
||||
|
||||
/* update the pointers into the packet */
|
||||
req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer);
|
||||
req->out.ptr = buf2 + PTR_DIFF(req->out.ptr, req->out.buffer);
|
||||
req->out.vwv = buf2 + PTR_DIFF(req->out.vwv, req->out.buffer);
|
||||
req->out.hdr = buf2 + PTR_DIFF(req->out.hdr, req->out.buffer);
|
||||
|
||||
req->out.buffer = buf2;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
grow the data buffer portion of a reply packet. Note that as this
|
||||
can reallocate the packet buffer this invalidates any local pointers
|
||||
into the packet.
|
||||
|
||||
To cope with this req->out.ptr is supplied. This will be updated to
|
||||
point at the same offset into the packet as before this call
|
||||
*/
|
||||
void req_grow_data(struct smbsrv_request *req, size_t new_size)
|
||||
{
|
||||
int delta;
|
||||
|
||||
if (!(req->control_flags & SMBSRV_REQ_CONTROL_LARGE) && new_size > req_max_data(req)) {
|
||||
smb_panic("reply buffer too large!");
|
||||
}
|
||||
|
||||
req_grow_allocation(req, new_size);
|
||||
|
||||
delta = new_size - req->out.data_size;
|
||||
|
||||
req->out.size += delta;
|
||||
req->out.data_size += delta;
|
||||
|
||||
/* set the BCC to the new data size */
|
||||
SSVAL(req->out.vwv, VWV(req->out.wct), new_size);
|
||||
}
|
||||
|
||||
/*
|
||||
send a reply and destroy the request buffer
|
||||
|
||||
note that this only looks at req->out.buffer and req->out.size, allowing manually
|
||||
constructed packets to be sent
|
||||
*/
|
||||
void smbsrv_send_reply_nosign(struct smbsrv_request *req)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
|
||||
if (req->smb_conn->connection->event.fde == NULL) {
|
||||
/* we are in the process of shutting down this connection */
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->out.size > NBT_HDR_SIZE) {
|
||||
_smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
|
||||
}
|
||||
|
||||
blob = data_blob_const(req->out.buffer, req->out.size);
|
||||
status = packet_send(req->smb_conn->packet, blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
|
||||
}
|
||||
talloc_free(req);
|
||||
}
|
||||
|
||||
/*
|
||||
possibly sign a message then send a reply and destroy the request buffer
|
||||
|
||||
note that this only looks at req->out.buffer and req->out.size, allowing manually
|
||||
constructed packets to be sent
|
||||
*/
|
||||
void smbsrv_send_reply(struct smbsrv_request *req)
|
||||
{
|
||||
if (req->smb_conn->connection->event.fde == NULL) {
|
||||
/* we are in the process of shutting down this connection */
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smbsrv_sign_packet(req);
|
||||
|
||||
smbsrv_send_reply_nosign(req);
|
||||
}
|
||||
|
||||
/*
|
||||
setup the header of a reply to include an NTSTATUS code
|
||||
*/
|
||||
void smbsrv_setup_error(struct smbsrv_request *req, NTSTATUS status)
|
||||
{
|
||||
if (!req->smb_conn->config.nt_status_support || !(req->smb_conn->negotiate.client_caps & CAP_STATUS32)) {
|
||||
/* convert to DOS error codes */
|
||||
uint8_t eclass;
|
||||
uint32_t ecode;
|
||||
ntstatus_to_dos(status, &eclass, &ecode);
|
||||
SCVAL(req->out.hdr, HDR_RCLS, eclass);
|
||||
SSVAL(req->out.hdr, HDR_ERR, ecode);
|
||||
SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES);
|
||||
return;
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_DOS(status)) {
|
||||
/* its a encoded DOS error, using the reserved range */
|
||||
SSVAL(req->out.hdr, HDR_RCLS, NT_STATUS_DOS_CLASS(status));
|
||||
SSVAL(req->out.hdr, HDR_ERR, NT_STATUS_DOS_CODE(status));
|
||||
SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES);
|
||||
} else {
|
||||
SIVAL(req->out.hdr, HDR_RCLS, NT_STATUS_V(status));
|
||||
SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) | FLAGS2_32_BIT_ERROR_CODES);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
construct and send an error packet, then destroy the request
|
||||
auto-converts to DOS error format when appropriate
|
||||
*/
|
||||
void smbsrv_send_error(struct smbsrv_request *req, NTSTATUS status)
|
||||
{
|
||||
if (req->smb_conn->connection->event.fde == NULL) {
|
||||
/* the socket has been destroyed - no point trying to send an error! */
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smbsrv_setup_reply(req, 0, 0);
|
||||
|
||||
/* error returns never have any data */
|
||||
req_grow_data(req, 0);
|
||||
|
||||
smbsrv_setup_error(req, status);
|
||||
smbsrv_send_reply(req);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
push a string into the data portion of the request packet, growing it if necessary
|
||||
this gets quite tricky - please be very careful to cover all cases when modifying this
|
||||
|
||||
if dest is NULL, then put the string at the end of the data portion of the packet
|
||||
|
||||
if dest_len is -1 then no limit applies
|
||||
*/
|
||||
size_t req_push_str(struct smbsrv_request *req, uint8_t *dest, const char *str, int dest_len, size_t flags)
|
||||
{
|
||||
size_t len;
|
||||
uint_t grow_size;
|
||||
uint8_t *buf0;
|
||||
const int max_bytes_per_char = 3;
|
||||
|
||||
if (!(flags & (STR_ASCII|STR_UNICODE))) {
|
||||
flags |= (req->flags2 & FLAGS2_UNICODE_STRINGS) ? STR_UNICODE : STR_ASCII;
|
||||
}
|
||||
|
||||
if (dest == NULL) {
|
||||
dest = req->out.data + req->out.data_size;
|
||||
}
|
||||
|
||||
if (dest_len != -1) {
|
||||
len = dest_len;
|
||||
} else {
|
||||
len = (strlen(str)+2) * max_bytes_per_char;
|
||||
}
|
||||
|
||||
grow_size = len + PTR_DIFF(dest, req->out.data);
|
||||
buf0 = req->out.buffer;
|
||||
|
||||
req_grow_allocation(req, grow_size);
|
||||
|
||||
if (buf0 != req->out.buffer) {
|
||||
dest = req->out.buffer + PTR_DIFF(dest, buf0);
|
||||
}
|
||||
|
||||
len = push_string(dest, str, len, flags);
|
||||
|
||||
grow_size = len + PTR_DIFF(dest, req->out.data);
|
||||
|
||||
if (grow_size > req->out.data_size) {
|
||||
req_grow_data(req, grow_size);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
append raw bytes into the data portion of the request packet
|
||||
return the number of bytes added
|
||||
*/
|
||||
size_t req_append_bytes(struct smbsrv_request *req,
|
||||
const uint8_t *bytes, size_t byte_len)
|
||||
{
|
||||
req_grow_allocation(req, byte_len + req->out.data_size);
|
||||
memcpy(req->out.data + req->out.data_size, bytes, byte_len);
|
||||
req_grow_data(req, byte_len + req->out.data_size);
|
||||
return byte_len;
|
||||
}
|
||||
/*
|
||||
append variable block (type 5 buffer) into the data portion of the request packet
|
||||
return the number of bytes added
|
||||
*/
|
||||
size_t req_append_var_block(struct smbsrv_request *req,
|
||||
const uint8_t *bytes, uint16_t byte_len)
|
||||
{
|
||||
req_grow_allocation(req, byte_len + 3 + req->out.data_size);
|
||||
SCVAL(req->out.data + req->out.data_size, 0, 5);
|
||||
SSVAL(req->out.data + req->out.data_size, 1, byte_len); /* add field length */
|
||||
if (byte_len > 0) {
|
||||
memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len);
|
||||
}
|
||||
req_grow_data(req, byte_len + 3 + req->out.data_size);
|
||||
return byte_len + 3;
|
||||
}
|
||||
/*
|
||||
pull a UCS2 string from a request packet, returning a talloced unix string
|
||||
|
||||
the string length is limited by the 3 things:
|
||||
- the data size in the request (end of packet)
|
||||
- the passed 'byte_len' if it is not -1
|
||||
- the end of string (null termination)
|
||||
|
||||
Note that 'byte_len' is the number of bytes in the packet
|
||||
|
||||
on failure zero is returned and *dest is set to NULL, otherwise the number
|
||||
of bytes consumed in the packet is returned
|
||||
*/
|
||||
static size_t req_pull_ucs2(struct smbsrv_request *req, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
|
||||
{
|
||||
int src_len, src_len2, alignment=0;
|
||||
ssize_t ret;
|
||||
char *dest2;
|
||||
|
||||
if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) {
|
||||
src++;
|
||||
alignment=1;
|
||||
if (byte_len != -1) {
|
||||
byte_len--;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & STR_NO_RANGE_CHECK) {
|
||||
src_len = byte_len;
|
||||
} else {
|
||||
src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
|
||||
if (byte_len != -1 && src_len > byte_len) {
|
||||
src_len = byte_len;
|
||||
}
|
||||
}
|
||||
|
||||
if (src_len < 0) {
|
||||
*dest = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
src_len2 = utf16_len_n(src, src_len);
|
||||
if (src_len2 == 0) {
|
||||
*dest = talloc_strdup(req, "");
|
||||
return src_len2 + alignment;
|
||||
}
|
||||
|
||||
ret = convert_string_talloc(req, CH_UTF16, CH_UNIX, src, src_len2, (void **)&dest2);
|
||||
|
||||
if (ret == -1) {
|
||||
*dest = NULL;
|
||||
return 0;
|
||||
}
|
||||
*dest = dest2;
|
||||
|
||||
return src_len2 + alignment;
|
||||
}
|
||||
|
||||
/*
|
||||
pull a ascii string from a request packet, returning a talloced string
|
||||
|
||||
the string length is limited by the 3 things:
|
||||
- the data size in the request (end of packet)
|
||||
- the passed 'byte_len' if it is not -1
|
||||
- the end of string (null termination)
|
||||
|
||||
Note that 'byte_len' is the number of bytes in the packet
|
||||
|
||||
on failure zero is returned and *dest is set to NULL, otherwise the number
|
||||
of bytes consumed in the packet is returned
|
||||
*/
|
||||
static size_t req_pull_ascii(struct smbsrv_request *req, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
|
||||
{
|
||||
int src_len, src_len2;
|
||||
ssize_t ret;
|
||||
char *dest2;
|
||||
|
||||
if (flags & STR_NO_RANGE_CHECK) {
|
||||
src_len = byte_len;
|
||||
} else {
|
||||
src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
|
||||
if (src_len < 0) {
|
||||
*dest = NULL;
|
||||
return 0;
|
||||
}
|
||||
if (byte_len != -1 && src_len > byte_len) {
|
||||
src_len = byte_len;
|
||||
}
|
||||
}
|
||||
|
||||
src_len2 = strnlen((const char *)src, src_len);
|
||||
if (src_len2 <= src_len - 1) {
|
||||
/* include the termination if we didn't reach the end of the packet */
|
||||
src_len2++;
|
||||
}
|
||||
|
||||
ret = convert_string_talloc(req, CH_DOS, CH_UNIX, src, src_len2, (void **)&dest2);
|
||||
|
||||
if (ret == -1) {
|
||||
*dest = NULL;
|
||||
return 0;
|
||||
}
|
||||
*dest = dest2;
|
||||
|
||||
return src_len2;
|
||||
}
|
||||
|
||||
/*
|
||||
pull a string from a request packet, returning a talloced string
|
||||
|
||||
the string length is limited by the 3 things:
|
||||
- the data size in the request (end of packet)
|
||||
- the passed 'byte_len' if it is not -1
|
||||
- the end of string (null termination)
|
||||
|
||||
Note that 'byte_len' is the number of bytes in the packet
|
||||
|
||||
on failure zero is returned and *dest is set to NULL, otherwise the number
|
||||
of bytes consumed in the packet is returned
|
||||
*/
|
||||
size_t req_pull_string(struct smbsrv_request *req, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
|
||||
{
|
||||
if (!(flags & STR_ASCII) &&
|
||||
(((flags & STR_UNICODE) || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) {
|
||||
return req_pull_ucs2(req, dest, src, byte_len, flags);
|
||||
}
|
||||
|
||||
return req_pull_ascii(req, dest, src, byte_len, flags);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
pull a ASCII4 string buffer from a request packet, returning a talloced string
|
||||
|
||||
an ASCII4 buffer is a null terminated string that has a prefix
|
||||
of the character 0x4. It tends to be used in older parts of the protocol.
|
||||
|
||||
on failure *dest is set to the zero length string. This seems to
|
||||
match win2000 behaviour
|
||||
*/
|
||||
size_t req_pull_ascii4(struct smbsrv_request *req, const char **dest, const uint8_t *src, uint_t flags)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
if (PTR_DIFF(src, req->in.data) + 1 > req->in.data_size) {
|
||||
/* win2000 treats this as the empty string! */
|
||||
(*dest) = talloc_strdup(req, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this consumes the 0x4 byte. We don't check whether the byte
|
||||
is actually 0x4 or not. This matches win2000 server
|
||||
behaviour */
|
||||
src++;
|
||||
|
||||
ret = req_pull_string(req, dest, src, -1, flags);
|
||||
if (ret == -1) {
|
||||
(*dest) = talloc_strdup(req, "");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
pull a DATA_BLOB from a request packet, returning a talloced blob
|
||||
|
||||
return False if any part is outside the data portion of the packet
|
||||
*/
|
||||
BOOL req_pull_blob(struct smbsrv_request *req, const uint8_t *src, int len, DATA_BLOB *blob)
|
||||
{
|
||||
if (len != 0 && req_data_oob(req, src, len)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
(*blob) = data_blob_talloc(req, src, len);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/* check that a lump of data in a request is within the bounds of the data section of
|
||||
the packet */
|
||||
BOOL req_data_oob(struct smbsrv_request *req, const uint8_t *ptr, uint32_t count)
|
||||
{
|
||||
if (count == 0) {
|
||||
return False;
|
||||
}
|
||||
|
||||
/* be careful with wraparound! */
|
||||
if (ptr < req->in.data ||
|
||||
ptr >= req->in.data + req->in.data_size ||
|
||||
count > req->in.data_size ||
|
||||
ptr + count > req->in.data + req->in.data_size) {
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
pull an open file handle from a packet, taking account of the chained_fnum
|
||||
*/
|
||||
static uint16_t req_fnum(struct smbsrv_request *req, const uint8_t *base, uint_t offset)
|
||||
{
|
||||
if (req->chained_fnum != -1) {
|
||||
return req->chained_fnum;
|
||||
}
|
||||
return SVAL(base, offset);
|
||||
}
|
||||
|
||||
struct ntvfs_handle *smbsrv_pull_fnum(struct smbsrv_request *req, const uint8_t *base, uint_t offset)
|
||||
{
|
||||
struct smbsrv_handle *handle;
|
||||
uint16_t fnum = req_fnum(req, base, offset);
|
||||
|
||||
handle = smbsrv_smb_handle_find(req->tcon, fnum, req->request_time);
|
||||
if (!handle) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* For SMB tcons and sessions can be mixed!
|
||||
* But we need to make sure that file handles
|
||||
* are only accessed by the opening session!
|
||||
*
|
||||
* So check if the handle is valid for the given session!
|
||||
*/
|
||||
if (handle->session != req->session) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return handle->ntvfs;
|
||||
}
|
||||
|
||||
void smbsrv_push_fnum(uint8_t *base, uint_t offset, struct ntvfs_handle *ntvfs)
|
||||
{
|
||||
struct smbsrv_handle *handle = talloc_get_type(ntvfs->frontend_data.private_data,
|
||||
struct smbsrv_handle);
|
||||
SSVAL(base, offset, handle->hid);
|
||||
}
|
||||
|
||||
NTSTATUS smbsrv_handle_create_new(void *private_data, struct ntvfs_request *ntvfs, struct ntvfs_handle **_h)
|
||||
{
|
||||
struct smbsrv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
|
||||
struct smbsrv_request);
|
||||
struct smbsrv_handle *handle;
|
||||
struct ntvfs_handle *h;
|
||||
|
||||
handle = smbsrv_handle_new(req->session, req->tcon, req, req->request_time);
|
||||
if (!handle) return NT_STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
h = talloc_zero(handle, struct ntvfs_handle);
|
||||
if (!h) goto nomem;
|
||||
|
||||
/*
|
||||
* note: we don't set handle->ntvfs yet,
|
||||
* this will be done by smbsrv_handle_make_valid()
|
||||
* this makes sure the handle is invalid for clients
|
||||
* until the ntvfs subsystem has made it valid
|
||||
*/
|
||||
h->ctx = ntvfs->ctx;
|
||||
h->session_info = ntvfs->session_info;
|
||||
h->smbpid = ntvfs->smbpid;
|
||||
|
||||
h->frontend_data.private_data = handle;
|
||||
|
||||
*_h = h;
|
||||
return NT_STATUS_OK;
|
||||
nomem:
|
||||
talloc_free(handle);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
NTSTATUS smbsrv_handle_make_valid(void *private_data, struct ntvfs_handle *h)
|
||||
{
|
||||
struct smbsrv_tcon *tcon = talloc_get_type(private_data, struct smbsrv_tcon);
|
||||
struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
|
||||
struct smbsrv_handle);
|
||||
/* this tells the frontend that the handle is valid */
|
||||
handle->ntvfs = h;
|
||||
/* this moves the smbsrv_request to the smbsrv_tcon memory context */
|
||||
talloc_steal(tcon, handle);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
void smbsrv_handle_destroy(void *private_data, struct ntvfs_handle *h)
|
||||
{
|
||||
struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
|
||||
struct smbsrv_handle);
|
||||
talloc_free(handle);
|
||||
}
|
||||
|
||||
struct ntvfs_handle *smbsrv_handle_search_by_wire_key(void *private_data, struct ntvfs_request *ntvfs, const DATA_BLOB *key)
|
||||
{
|
||||
struct smbsrv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
|
||||
struct smbsrv_request);
|
||||
|
||||
if (key->length != 2) return NULL;
|
||||
|
||||
return smbsrv_pull_fnum(req, key->data, 0);
|
||||
}
|
||||
|
||||
DATA_BLOB smbsrv_handle_get_wire_key(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
uint8_t key[2];
|
||||
|
||||
smbsrv_push_fnum(key, 0, handle);
|
||||
|
||||
return data_blob_talloc(mem_ctx, key, sizeof(key));
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMBsearch handling
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
Copyright (C) James J Myers 2003 <myersjj@samba.org>
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/*
|
||||
This file handles the parsing of transact2 requests
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
|
||||
/* a structure to encapsulate the state information about
|
||||
* an in-progress search first/next operation */
|
||||
struct search_state {
|
||||
struct smbsrv_request *req;
|
||||
union smb_search_data *file;
|
||||
uint16_t last_entry_offset;
|
||||
};
|
||||
|
||||
/*
|
||||
fill a single entry in a search find reply
|
||||
*/
|
||||
static BOOL find_fill_info(struct smbsrv_request *req,
|
||||
union smb_search_data *file)
|
||||
{
|
||||
uint8_t *p;
|
||||
|
||||
if (req->out.data_size + 43 > req_max_data(req)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
req_grow_data(req, req->out.data_size + 43);
|
||||
p = req->out.data + req->out.data_size - 43;
|
||||
|
||||
SCVAL(p, 0, file->search.id.reserved);
|
||||
memcpy(p+1, file->search.id.name, 11);
|
||||
SCVAL(p, 12, file->search.id.handle);
|
||||
SIVAL(p, 13, file->search.id.server_cookie);
|
||||
SIVAL(p, 17, file->search.id.client_cookie);
|
||||
SCVAL(p, 21, file->search.attrib);
|
||||
srv_push_dos_date(req->smb_conn, p, 22, file->search.write_time);
|
||||
SIVAL(p, 26, file->search.size);
|
||||
memset(p+30, ' ', 12);
|
||||
memcpy(p+30, file->search.name, MIN(strlen(file->search.name)+1, 12));
|
||||
SCVAL(p,42,0);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/* callback function for search first/next */
|
||||
static BOOL find_callback(void *private, union smb_search_data *file)
|
||||
{
|
||||
struct search_state *state = (struct search_state *)private;
|
||||
|
||||
return find_fill_info(state->req, file);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply to a search first (async reply)
|
||||
****************************************************************************/
|
||||
static void reply_search_first_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smbsrv_request *req;
|
||||
union smb_search_first *sf;
|
||||
|
||||
SMBSRV_CHECK_ASYNC_STATUS(sf, union smb_search_first);
|
||||
|
||||
SSVAL(req->out.vwv, VWV(0), sf->search_first.out.count);
|
||||
|
||||
smbsrv_send_reply(req);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply to a search next (async reply)
|
||||
****************************************************************************/
|
||||
static void reply_search_next_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smbsrv_request *req;
|
||||
union smb_search_next *sn;
|
||||
|
||||
SMBSRV_CHECK_ASYNC_STATUS(sn, union smb_search_next);
|
||||
|
||||
SSVAL(req->out.vwv, VWV(0), sn->search_next.out.count);
|
||||
|
||||
smbsrv_send_reply(req);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply to a search.
|
||||
****************************************************************************/
|
||||
void smbsrv_reply_search(struct smbsrv_request *req)
|
||||
{
|
||||
union smb_search_first *sf;
|
||||
uint16_t resume_key_length;
|
||||
struct search_state *state;
|
||||
uint8_t *p;
|
||||
enum smb_search_level level = RAW_SEARCH_SEARCH;
|
||||
uint8_t op = CVAL(req->in.hdr,HDR_COM);
|
||||
|
||||
if (op == SMBffirst) {
|
||||
level = RAW_SEARCH_FFIRST;
|
||||
} else if (op == SMBfunique) {
|
||||
level = RAW_SEARCH_FUNIQUE;
|
||||
}
|
||||
|
||||
/* parse request */
|
||||
if (req->in.wct != 2) {
|
||||
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
SMBSRV_TALLOC_IO_PTR(sf, union smb_search_first);
|
||||
|
||||
p = req->in.data;
|
||||
p += req_pull_ascii4(req, &sf->search_first.in.pattern,
|
||||
p, STR_TERMINATE);
|
||||
if (!sf->search_first.in.pattern) {
|
||||
smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req_data_oob(req, p, 3)) {
|
||||
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
if (*p != 5) {
|
||||
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
resume_key_length = SVAL(p, 1);
|
||||
p += 3;
|
||||
|
||||
/* setup state for callback */
|
||||
state = talloc(req, struct search_state);
|
||||
if (!state) {
|
||||
smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
state->req = req;
|
||||
state->file = NULL;
|
||||
state->last_entry_offset = 0;
|
||||
|
||||
/* construct reply */
|
||||
smbsrv_setup_reply(req, 1, 0);
|
||||
SSVAL(req->out.vwv, VWV(0), 0);
|
||||
req_append_var_block(req, NULL, 0);
|
||||
|
||||
if (resume_key_length != 0) {
|
||||
union smb_search_next *sn;
|
||||
|
||||
if (resume_key_length != 21 ||
|
||||
req_data_oob(req, p, 21) ||
|
||||
level == RAW_SEARCH_FUNIQUE) {
|
||||
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
/* do a search next operation */
|
||||
SMBSRV_TALLOC_IO_PTR(sn, union smb_search_next);
|
||||
SMBSRV_SETUP_NTVFS_REQUEST(reply_search_next_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
sn->search_next.in.id.reserved = CVAL(p, 0);
|
||||
memcpy(sn->search_next.in.id.name, p+1, 11);
|
||||
sn->search_next.in.id.handle = CVAL(p, 12);
|
||||
sn->search_next.in.id.server_cookie = IVAL(p, 13);
|
||||
sn->search_next.in.id.client_cookie = IVAL(p, 17);
|
||||
|
||||
sn->search_next.level = level;
|
||||
sn->search_next.data_level = RAW_SEARCH_DATA_SEARCH;
|
||||
sn->search_next.in.max_count = SVAL(req->in.vwv, VWV(0));
|
||||
sn->search_next.in.search_attrib = SVAL(req->in.vwv, VWV(1));
|
||||
|
||||
/* call backend */
|
||||
SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_next(req->ntvfs, sn, state, find_callback));
|
||||
} else {
|
||||
SMBSRV_SETUP_NTVFS_REQUEST(reply_search_first_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
/* do a search first operation */
|
||||
sf->search_first.level = level;
|
||||
sf->search_first.data_level = RAW_SEARCH_DATA_SEARCH;
|
||||
sf->search_first.in.search_attrib = SVAL(req->in.vwv, VWV(1));
|
||||
sf->search_first.in.max_count = SVAL(req->in.vwv, VWV(0));
|
||||
|
||||
SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_first(req->ntvfs, sf, state, find_callback));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Reply to a fclose (async reply)
|
||||
****************************************************************************/
|
||||
static void reply_fclose_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smbsrv_request *req;
|
||||
|
||||
SMBSRV_CHECK_ASYNC_STATUS_SIMPLE;
|
||||
|
||||
/* construct reply */
|
||||
smbsrv_setup_reply(req, 1, 0);
|
||||
|
||||
SSVAL(req->out.vwv, VWV(0), 0);
|
||||
|
||||
smbsrv_send_reply(req);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Reply to fclose (stop directory search).
|
||||
****************************************************************************/
|
||||
void smbsrv_reply_fclose(struct smbsrv_request *req)
|
||||
{
|
||||
union smb_search_close *sc;
|
||||
uint16_t resume_key_length;
|
||||
uint8_t *p;
|
||||
const char *pattern;
|
||||
|
||||
/* parse request */
|
||||
if (req->in.wct != 2) {
|
||||
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
SMBSRV_TALLOC_IO_PTR(sc, union smb_search_close);
|
||||
SMBSRV_SETUP_NTVFS_REQUEST(reply_fclose_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
p = req->in.data;
|
||||
p += req_pull_ascii4(req, &pattern, p, STR_TERMINATE);
|
||||
if (pattern && *pattern) {
|
||||
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req_data_oob(req, p, 3)) {
|
||||
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
if (*p != 5) {
|
||||
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
resume_key_length = SVAL(p, 1);
|
||||
p += 3;
|
||||
|
||||
if (resume_key_length != 21) {
|
||||
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req_data_oob(req, p, 21)) {
|
||||
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
sc->fclose.level = RAW_FINDCLOSE_FCLOSE;
|
||||
sc->fclose.in.max_count = SVAL(req->in.vwv, VWV(0));
|
||||
sc->fclose.in.search_attrib = SVAL(req->in.vwv, VWV(1));
|
||||
sc->fclose.in.id.reserved = CVAL(p, 0);
|
||||
memcpy(sc->fclose.in.id.name, p+1, 11);
|
||||
sc->fclose.in.id.handle = CVAL(p, 12);
|
||||
sc->fclose.in.id.server_cookie = IVAL(p, 13);
|
||||
sc->fclose.in.id.client_cookie = IVAL(p, 17);
|
||||
|
||||
SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_close(req->ntvfs, sc));
|
||||
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
service (connection) handling
|
||||
Copyright (C) Andrew Tridgell 1992-2003
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
/****************************************************************************
|
||||
Make a connection, given the snum to connect to, and the vuser of the
|
||||
connecting user if appropriate.
|
||||
****************************************************************************/
|
||||
static NTSTATUS make_connection_scfg(struct smbsrv_request *req,
|
||||
struct share_config *scfg,
|
||||
enum ntvfs_type type,
|
||||
DATA_BLOB password,
|
||||
const char *dev)
|
||||
{
|
||||
struct smbsrv_tcon *tcon;
|
||||
NTSTATUS status;
|
||||
|
||||
tcon = smbsrv_smb_tcon_new(req->smb_conn, scfg->name);
|
||||
if (!tcon) {
|
||||
DEBUG(0,("Couldn't find free connection.\n"));
|
||||
return NT_STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
req->tcon = tcon;
|
||||
|
||||
/* init ntvfs function pointers */
|
||||
status = ntvfs_init_connection(tcon, scfg, type,
|
||||
req->smb_conn->negotiate.protocol,
|
||||
req->smb_conn->connection->event.ctx,
|
||||
req->smb_conn->connection->msg_ctx,
|
||||
req->smb_conn->connection->server_id,
|
||||
&tcon->ntvfs);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("make_connection_scfg: connection failed for service %s\n",
|
||||
scfg->name));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = ntvfs_set_oplock_handler(tcon->ntvfs, smbsrv_send_oplock_break, tcon);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("make_connection: NTVFS failed to set the oplock handler!\n"));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = ntvfs_set_addr_callbacks(tcon->ntvfs, smbsrv_get_my_addr, smbsrv_get_peer_addr, req->smb_conn);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("make_connection: NTVFS failed to set the addr callbacks!\n"));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = ntvfs_set_handle_callbacks(tcon->ntvfs,
|
||||
smbsrv_handle_create_new,
|
||||
smbsrv_handle_make_valid,
|
||||
smbsrv_handle_destroy,
|
||||
smbsrv_handle_search_by_wire_key,
|
||||
smbsrv_handle_get_wire_key,
|
||||
tcon);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("make_connection: NTVFS failed to set the handle callbacks!\n"));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req,
|
||||
req->session->session_info,
|
||||
SVAL(req->in.hdr,HDR_PID),
|
||||
req->request_time,
|
||||
req, NULL, 0);
|
||||
if (!req->ntvfs) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Invoke NTVFS connection hook */
|
||||
status = ntvfs_connect(req->ntvfs, scfg->name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("make_connection: NTVFS make connection failed!\n"));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
failed:
|
||||
req->tcon = NULL;
|
||||
talloc_free(tcon);
|
||||
return status;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Make a connection to a service.
|
||||
*
|
||||
* @param service
|
||||
****************************************************************************/
|
||||
static NTSTATUS make_connection(struct smbsrv_request *req,
|
||||
const char *service, DATA_BLOB password,
|
||||
const char *dev)
|
||||
{
|
||||
NTSTATUS status;
|
||||
enum ntvfs_type type;
|
||||
const char *type_str;
|
||||
struct share_config *scfg;
|
||||
const char *sharetype;
|
||||
|
||||
/* the service might be of the form \\SERVER\SHARE. Should we put
|
||||
the server name we get from this somewhere? */
|
||||
if (strncmp(service, "\\\\", 2) == 0) {
|
||||
char *p = strchr(service+2, '\\');
|
||||
if (p) {
|
||||
service = p + 1;
|
||||
}
|
||||
}
|
||||
|
||||
status = share_get_config(req, req->smb_conn->share_context, service, &scfg);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("make_connection: couldn't find service %s\n", service));
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
|
||||
/* TODO: check the password, when it's share level security! */
|
||||
|
||||
if (!socket_check_access(req->smb_conn->connection->socket,
|
||||
scfg->name,
|
||||
share_string_list_option(req, scfg, SHARE_HOSTS_ALLOW),
|
||||
share_string_list_option(req, scfg, SHARE_HOSTS_DENY))) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* work out what sort of connection this is */
|
||||
sharetype = share_string_option(scfg, "type", "DISK");
|
||||
if (sharetype && strcmp(sharetype, "IPC") == 0) {
|
||||
type = NTVFS_IPC;
|
||||
type_str = "IPC";
|
||||
} else if (sharetype && strcmp(sharetype, "PRINTER") == 0) {
|
||||
type = NTVFS_PRINT;
|
||||
type_str = "LPT:";
|
||||
} else {
|
||||
type = NTVFS_DISK;
|
||||
type_str = "A:";
|
||||
}
|
||||
|
||||
if (strcmp(dev, "?????") != 0 && strcasecmp(type_str, dev) != 0) {
|
||||
/* the client gave us the wrong device type */
|
||||
return NT_STATUS_BAD_DEVICE_TYPE;
|
||||
}
|
||||
|
||||
return make_connection_scfg(req, scfg, type, password, dev);
|
||||
}
|
||||
|
||||
/*
|
||||
backend for tree connect call
|
||||
*/
|
||||
NTSTATUS smbsrv_tcon_backend(struct smbsrv_request *req, union smb_tcon *con)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (con->generic.level == RAW_TCON_TCON) {
|
||||
DATA_BLOB password;
|
||||
password = data_blob(con->tcon.in.password, strlen(con->tcon.in.password) + 1);
|
||||
|
||||
status = make_connection(req, con->tcon.in.service, password, con->tcon.in.dev);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
con->tcon.out.max_xmit = req->smb_conn->negotiate.max_recv;
|
||||
con->tcon.out.tid = req->tcon->tid;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
status = make_connection(req, con->tconx.in.path, con->tconx.in.password,
|
||||
con->tconx.in.device);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
con->tconx.out.tid = req->tcon->tid;
|
||||
con->tconx.out.dev_type = talloc_strdup(req, req->tcon->ntvfs->dev_type);
|
||||
con->tconx.out.fs_type = talloc_strdup(req, req->tcon->ntvfs->fs_type);
|
||||
con->tconx.out.options = SMB_SUPPORT_SEARCH_BITS | (share_int_option(req->tcon->ntvfs->config, SHARE_CSC_POLICY, SHARE_CSC_POLICY_DEFAULT) << 2);
|
||||
if (share_bool_option(req->tcon->ntvfs->config, SHARE_MSDFS_ROOT, SHARE_MSDFS_ROOT_DEFAULT) && lp_host_msdfs()) {
|
||||
con->tconx.out.options |= SMB_SHARE_IN_DFS;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,457 @@
|
||||
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
handle SMBsessionsetup
|
||||
Copyright (C) Andrew Tridgell 1998-2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
|
||||
Copyright (C) Jim McDonough 2002
|
||||
Copyright (C) Luke Howard 2003
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "version.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "auth/auth.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "librpc/gen_ndr/nbt.h"
|
||||
|
||||
/*
|
||||
setup the OS, Lanman and domain portions of a session setup reply
|
||||
*/
|
||||
static void sesssetup_common_strings(struct smbsrv_request *req,
|
||||
char **os, char **lanman, char **domain)
|
||||
{
|
||||
(*os) = talloc_asprintf(req, "Unix");
|
||||
(*lanman) = talloc_asprintf(req, "Samba %s", SAMBA_VERSION_STRING);
|
||||
(*domain) = talloc_asprintf(req, "%s", lp_workgroup());
|
||||
}
|
||||
|
||||
static void smbsrv_sesssetup_backend_send(struct smbsrv_request *req,
|
||||
union smb_sesssetup *sess,
|
||||
NTSTATUS status)
|
||||
{
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
req->smb_conn->negotiate.done_sesssetup = True;
|
||||
}
|
||||
smbsrv_reply_sesssetup_send(req, sess, status);
|
||||
}
|
||||
|
||||
static void sesssetup_old_send(struct auth_check_password_request *areq,
|
||||
void *private_data)
|
||||
{
|
||||
struct smbsrv_request *req = talloc_get_type(private_data, struct smbsrv_request);
|
||||
union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
|
||||
struct auth_serversupplied_info *server_info = NULL;
|
||||
struct auth_session_info *session_info;
|
||||
struct smbsrv_session *smb_sess;
|
||||
NTSTATUS status;
|
||||
|
||||
status = auth_check_password_recv(areq, req, &server_info);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
/* This references server_info into session_info */
|
||||
status = auth_generate_session_info(req, server_info, &session_info);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
/* allocate a new session */
|
||||
smb_sess = smbsrv_session_new(req->smb_conn, NULL);
|
||||
if (!smb_sess) {
|
||||
status = NT_STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Ensure this is marked as a 'real' vuid, not one
|
||||
* simply valid for the session setup leg */
|
||||
status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
/* To correctly process any AndX packet (like a tree connect)
|
||||
* we need to fill in the session on the request here */
|
||||
req->session = smb_sess;
|
||||
sess->old.out.vuid = smb_sess->vuid;
|
||||
|
||||
failed:
|
||||
status = auth_nt_status_squash(status);
|
||||
smbsrv_sesssetup_backend_send(req, sess, status);
|
||||
}
|
||||
|
||||
/*
|
||||
handler for old style session setup
|
||||
*/
|
||||
static void sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess)
|
||||
{
|
||||
struct auth_usersupplied_info *user_info = NULL;
|
||||
struct socket_address *remote_address;
|
||||
const char *remote_machine = NULL;
|
||||
|
||||
sess->old.out.vuid = 0;
|
||||
sess->old.out.action = 0;
|
||||
|
||||
sesssetup_common_strings(req,
|
||||
&sess->old.out.os,
|
||||
&sess->old.out.lanman,
|
||||
&sess->old.out.domain);
|
||||
|
||||
if (!req->smb_conn->negotiate.done_sesssetup) {
|
||||
req->smb_conn->negotiate.max_send = sess->old.in.bufsize;
|
||||
}
|
||||
|
||||
if (req->smb_conn->negotiate.calling_name) {
|
||||
remote_machine = req->smb_conn->negotiate.calling_name->name;
|
||||
}
|
||||
|
||||
remote_address = socket_get_peer_addr(req->smb_conn->connection->socket, req);
|
||||
if (!remote_address) goto nomem;
|
||||
|
||||
if (!remote_machine) {
|
||||
remote_machine = remote_address->addr;
|
||||
}
|
||||
|
||||
user_info = talloc(req, struct auth_usersupplied_info);
|
||||
if (!user_info) goto nomem;
|
||||
|
||||
user_info->mapped_state = False;
|
||||
user_info->logon_parameters = 0;
|
||||
user_info->flags = 0;
|
||||
user_info->client.account_name = sess->old.in.user;
|
||||
user_info->client.domain_name = sess->old.in.domain;
|
||||
user_info->workstation_name = remote_machine;
|
||||
user_info->remote_host = talloc_steal(user_info, remote_address);
|
||||
|
||||
user_info->password_state = AUTH_PASSWORD_RESPONSE;
|
||||
user_info->password.response.lanman = sess->old.in.password;
|
||||
user_info->password.response.lanman.data = talloc_steal(user_info, sess->old.in.password.data);
|
||||
user_info->password.response.nt = data_blob(NULL, 0);
|
||||
|
||||
auth_check_password_send(req->smb_conn->negotiate.auth_context, user_info,
|
||||
sesssetup_old_send, req);
|
||||
return;
|
||||
|
||||
nomem:
|
||||
smbsrv_sesssetup_backend_send(req, sess, NT_STATUS_NO_MEMORY);
|
||||
}
|
||||
|
||||
static void sesssetup_nt1_send(struct auth_check_password_request *areq,
|
||||
void *private_data)
|
||||
{
|
||||
struct smbsrv_request *req = talloc_get_type(private_data, struct smbsrv_request);
|
||||
union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
|
||||
struct auth_serversupplied_info *server_info = NULL;
|
||||
struct auth_session_info *session_info;
|
||||
struct smbsrv_session *smb_sess;
|
||||
NTSTATUS status;
|
||||
|
||||
status = auth_check_password_recv(areq, req, &server_info);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
/* This references server_info into session_info */
|
||||
status = auth_generate_session_info(req, server_info, &session_info);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
/* allocate a new session */
|
||||
smb_sess = smbsrv_session_new(req->smb_conn, NULL);
|
||||
if (!smb_sess) {
|
||||
status = NT_STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Ensure this is marked as a 'real' vuid, not one
|
||||
* simply valid for the session setup leg */
|
||||
status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
/* To correctly process any AndX packet (like a tree connect)
|
||||
* we need to fill in the session on the request here */
|
||||
req->session = smb_sess;
|
||||
sess->nt1.out.vuid = smb_sess->vuid;
|
||||
|
||||
if (!session_info->server_info->authenticated) {
|
||||
/* don't try signing as anonymous */
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!smbsrv_setup_signing(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2)) {
|
||||
/* Already signing, or disabled */
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Force check of the request packet, now we know the session key */
|
||||
smbsrv_signing_check_incoming(req);
|
||||
/* TODO: why don't we check the result here? */
|
||||
|
||||
/* Unfortunetly win2k3 as a client doesn't sign the request
|
||||
* packet here, so we have to force signing to start again */
|
||||
|
||||
smbsrv_signing_restart(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2);
|
||||
|
||||
done:
|
||||
status = NT_STATUS_OK;
|
||||
failed:
|
||||
status = auth_nt_status_squash(status);
|
||||
smbsrv_sesssetup_backend_send(req, sess, status);
|
||||
}
|
||||
|
||||
/*
|
||||
handler for NT1 style session setup
|
||||
*/
|
||||
static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct auth_context *auth_context;
|
||||
struct auth_usersupplied_info *user_info = NULL;
|
||||
struct socket_address *remote_address;
|
||||
const char *remote_machine = NULL;
|
||||
|
||||
sess->nt1.out.vuid = 0;
|
||||
sess->nt1.out.action = 0;
|
||||
|
||||
sesssetup_common_strings(req,
|
||||
&sess->nt1.out.os,
|
||||
&sess->nt1.out.lanman,
|
||||
&sess->nt1.out.domain);
|
||||
|
||||
if (!req->smb_conn->negotiate.done_sesssetup) {
|
||||
req->smb_conn->negotiate.max_send = sess->nt1.in.bufsize;
|
||||
req->smb_conn->negotiate.client_caps = sess->nt1.in.capabilities;
|
||||
}
|
||||
|
||||
if (req->smb_conn->negotiate.oid) {
|
||||
if (sess->nt1.in.user && *sess->nt1.in.user) {
|
||||
/* We can't accept a normal login, because we
|
||||
* don't have a challenge */
|
||||
status = NT_STATUS_LOGON_FAILURE;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* TODO: should we use just "anonymous" here? */
|
||||
status = auth_context_create(req, lp_auth_methods(),
|
||||
req->smb_conn->connection->event.ctx,
|
||||
req->smb_conn->connection->msg_ctx,
|
||||
&auth_context);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
} else {
|
||||
auth_context = req->smb_conn->negotiate.auth_context;
|
||||
}
|
||||
|
||||
if (req->smb_conn->negotiate.calling_name) {
|
||||
remote_machine = req->smb_conn->negotiate.calling_name->name;
|
||||
}
|
||||
|
||||
remote_address = socket_get_peer_addr(req->smb_conn->connection->socket, req);
|
||||
if (!remote_address) goto nomem;
|
||||
|
||||
if (!remote_machine) {
|
||||
remote_machine = remote_address->addr;
|
||||
}
|
||||
|
||||
user_info = talloc(req, struct auth_usersupplied_info);
|
||||
if (!user_info) goto nomem;
|
||||
|
||||
user_info->mapped_state = False;
|
||||
user_info->logon_parameters = 0;
|
||||
user_info->flags = 0;
|
||||
user_info->client.account_name = sess->nt1.in.user;
|
||||
user_info->client.domain_name = sess->nt1.in.domain;
|
||||
user_info->workstation_name = remote_machine;
|
||||
user_info->remote_host = talloc_steal(user_info, remote_address);
|
||||
|
||||
user_info->password_state = AUTH_PASSWORD_RESPONSE;
|
||||
user_info->password.response.lanman = sess->nt1.in.password1;
|
||||
user_info->password.response.lanman.data = talloc_steal(user_info, sess->nt1.in.password1.data);
|
||||
user_info->password.response.nt = sess->nt1.in.password2;
|
||||
user_info->password.response.nt.data = talloc_steal(user_info, sess->nt1.in.password2.data);
|
||||
|
||||
auth_check_password_send(auth_context, user_info,
|
||||
sesssetup_nt1_send, req);
|
||||
return;
|
||||
|
||||
nomem:
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
failed:
|
||||
status = auth_nt_status_squash(status);
|
||||
smbsrv_sesssetup_backend_send(req, sess, status);
|
||||
}
|
||||
|
||||
struct sesssetup_spnego_state {
|
||||
struct smbsrv_request *req;
|
||||
union smb_sesssetup *sess;
|
||||
struct smbsrv_session *smb_sess;
|
||||
};
|
||||
|
||||
static void sesssetup_spnego_send(struct gensec_update_request *greq, void *private_data)
|
||||
{
|
||||
struct sesssetup_spnego_state *s = talloc_get_type(private_data,
|
||||
struct sesssetup_spnego_state);
|
||||
struct smbsrv_request *req = s->req;
|
||||
union smb_sesssetup *sess = s->sess;
|
||||
struct smbsrv_session *smb_sess = s->smb_sess;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
NTSTATUS status;
|
||||
NTSTATUS skey_status;
|
||||
DATA_BLOB session_key;
|
||||
|
||||
status = gensec_update_recv(greq, req, &sess->spnego.out.secblob);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
goto done;
|
||||
} else if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = gensec_session_info(smb_sess->gensec_ctx, &session_info);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
skey_status = gensec_session_key(smb_sess->gensec_ctx, &session_key);
|
||||
if (NT_STATUS_IS_OK(skey_status) &&
|
||||
session_info->server_info->authenticated &&
|
||||
smbsrv_setup_signing(req->smb_conn, &session_key, NULL)) {
|
||||
/* Force check of the request packet, now we know the session key */
|
||||
smbsrv_signing_check_incoming(req);
|
||||
|
||||
smbsrv_signing_restart(req->smb_conn, &session_key, NULL);
|
||||
}
|
||||
|
||||
/* Ensure this is marked as a 'real' vuid, not one
|
||||
* simply valid for the session setup leg */
|
||||
status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
req->session = smb_sess;
|
||||
|
||||
done:
|
||||
sess->spnego.out.vuid = smb_sess->vuid;
|
||||
failed:
|
||||
status = auth_nt_status_squash(status);
|
||||
smbsrv_sesssetup_backend_send(req, sess, status);
|
||||
}
|
||||
|
||||
/*
|
||||
handler for SPNEGO style session setup
|
||||
*/
|
||||
static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *sess)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct smbsrv_session *smb_sess = NULL;
|
||||
struct sesssetup_spnego_state *s = NULL;
|
||||
uint16_t vuid;
|
||||
|
||||
sess->spnego.out.vuid = 0;
|
||||
sess->spnego.out.action = 0;
|
||||
|
||||
sesssetup_common_strings(req,
|
||||
&sess->spnego.out.os,
|
||||
&sess->spnego.out.lanman,
|
||||
&sess->spnego.out.workgroup);
|
||||
|
||||
if (!req->smb_conn->negotiate.done_sesssetup) {
|
||||
req->smb_conn->negotiate.max_send = sess->spnego.in.bufsize;
|
||||
req->smb_conn->negotiate.client_caps = sess->spnego.in.capabilities;
|
||||
}
|
||||
|
||||
vuid = SVAL(req->in.hdr,HDR_UID);
|
||||
|
||||
/* lookup an existing session */
|
||||
smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid);
|
||||
if (!smb_sess) {
|
||||
struct gensec_security *gensec_ctx;
|
||||
|
||||
status = gensec_server_start(req,
|
||||
req->smb_conn->connection->event.ctx,
|
||||
req->smb_conn->connection->msg_ctx,
|
||||
&gensec_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
gensec_set_credentials(gensec_ctx, req->smb_conn->negotiate.server_credentials);
|
||||
|
||||
gensec_set_target_service(gensec_ctx, "cifs");
|
||||
|
||||
gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
|
||||
|
||||
status = gensec_start_mech_by_oid(gensec_ctx, req->smb_conn->negotiate.oid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1, ("Failed to start GENSEC %s server code: %s\n",
|
||||
gensec_get_name_by_oid(req->smb_conn->negotiate.oid), nt_errstr(status)));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* allocate a new session */
|
||||
smb_sess = smbsrv_session_new(req->smb_conn, gensec_ctx);
|
||||
if (!smb_sess) {
|
||||
status = NT_STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
if (!smb_sess) {
|
||||
status = NT_STATUS_ACCESS_DENIED;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (!smb_sess->gensec_ctx) {
|
||||
status = NT_STATUS_INTERNAL_ERROR;
|
||||
DEBUG(1, ("Internal ERROR: no gensec_ctx on session: %s\n", nt_errstr(status)));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
s = talloc(req, struct sesssetup_spnego_state);
|
||||
if (!s) goto nomem;
|
||||
s->req = req;
|
||||
s->sess = sess;
|
||||
s->smb_sess = smb_sess;
|
||||
|
||||
gensec_update_send(smb_sess->gensec_ctx, sess->spnego.in.secblob,
|
||||
sesssetup_spnego_send, s);
|
||||
return;
|
||||
|
||||
nomem:
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
failed:
|
||||
talloc_free(smb_sess);
|
||||
status = auth_nt_status_squash(status);
|
||||
smbsrv_sesssetup_backend_send(req, sess, status);
|
||||
}
|
||||
|
||||
/*
|
||||
backend for sessionsetup call - this takes all 3 variants of the call
|
||||
*/
|
||||
void smbsrv_sesssetup_backend(struct smbsrv_request *req,
|
||||
union smb_sesssetup *sess)
|
||||
{
|
||||
switch (sess->old.level) {
|
||||
case RAW_SESSSETUP_OLD:
|
||||
sesssetup_old(req, sess);
|
||||
return;
|
||||
|
||||
case RAW_SESSSETUP_NT1:
|
||||
sesssetup_nt1(req, sess);
|
||||
return;
|
||||
|
||||
case RAW_SESSSETUP_SPNEGO:
|
||||
sesssetup_spnego(req, sess);
|
||||
return;
|
||||
|
||||
case RAW_SESSSETUP_SMB2:
|
||||
break;
|
||||
}
|
||||
|
||||
smbsrv_sesssetup_backend_send(req, sess, NT_STATUS_INVALID_LEVEL);
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
|
||||
|
||||
/*
|
||||
sign an outgoing packet
|
||||
*/
|
||||
void smbsrv_sign_packet(struct smbsrv_request *req)
|
||||
{
|
||||
#if 0
|
||||
/* enable this when packet signing is preventing you working out why valgrind
|
||||
says that data is uninitialised */
|
||||
file_save("pkt.dat", req->out.buffer, req->out.size);
|
||||
#endif
|
||||
|
||||
switch (req->smb_conn->signing.signing_state) {
|
||||
case SMB_SIGNING_ENGINE_OFF:
|
||||
break;
|
||||
|
||||
case SMB_SIGNING_ENGINE_BSRSPYL:
|
||||
/* mark the packet as signed - BEFORE we sign it...*/
|
||||
mark_packet_signed(&req->out);
|
||||
|
||||
/* I wonder what BSRSPYL stands for - but this is what MS
|
||||
actually sends! */
|
||||
memcpy((req->out.hdr + HDR_SS_FIELD), "BSRSPYL ", 8);
|
||||
break;
|
||||
|
||||
case SMB_SIGNING_ENGINE_ON:
|
||||
|
||||
sign_outgoing_message(&req->out,
|
||||
&req->smb_conn->signing.mac_key,
|
||||
req->seq_num+1);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
setup the signing key for a connection. Called after authentication succeeds
|
||||
in a session setup
|
||||
*/
|
||||
BOOL smbsrv_setup_signing(struct smbsrv_connection *smb_conn,
|
||||
DATA_BLOB *session_key,
|
||||
DATA_BLOB *response)
|
||||
{
|
||||
if (!set_smb_signing_common(&smb_conn->signing)) {
|
||||
return False;
|
||||
}
|
||||
return smbcli_simple_set_signing(smb_conn,
|
||||
&smb_conn->signing, session_key, response);
|
||||
}
|
||||
|
||||
void smbsrv_signing_restart(struct smbsrv_connection *smb_conn,
|
||||
DATA_BLOB *session_key,
|
||||
DATA_BLOB *response)
|
||||
{
|
||||
if (!smb_conn->signing.seen_valid) {
|
||||
DEBUG(5, ("Client did not send a valid signature on "
|
||||
"SPNEGO session setup - ignored, expect good next time\n"));
|
||||
/* force things back on (most clients do not sign this packet)... */
|
||||
smbsrv_setup_signing(smb_conn, session_key, response);
|
||||
smb_conn->signing.next_seq_num = 2;
|
||||
if (smb_conn->signing.mandatory_signing) {
|
||||
DEBUG(5, ("Configured for mandatory signing, 'good packet seen' forced on\n"));
|
||||
/* if this is mandatory, then
|
||||
* pretend we have seen a
|
||||
* valid packet, so we don't
|
||||
* turn it off */
|
||||
smb_conn->signing.seen_valid = True;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL smbsrv_init_signing(struct smbsrv_connection *smb_conn)
|
||||
{
|
||||
smb_conn->signing.mac_key = data_blob(NULL, 0);
|
||||
if (!smbcli_set_signing_off(&smb_conn->signing)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
switch (lp_server_signing()) {
|
||||
case SMB_SIGNING_OFF:
|
||||
smb_conn->signing.allow_smb_signing = False;
|
||||
break;
|
||||
case SMB_SIGNING_SUPPORTED:
|
||||
smb_conn->signing.allow_smb_signing = True;
|
||||
break;
|
||||
case SMB_SIGNING_REQUIRED:
|
||||
smb_conn->signing.allow_smb_signing = True;
|
||||
smb_conn->signing.mandatory_signing = True;
|
||||
break;
|
||||
case SMB_SIGNING_AUTO:
|
||||
if (lp_domain_logons()) {
|
||||
smb_conn->signing.allow_smb_signing = True;
|
||||
} else {
|
||||
smb_conn->signing.allow_smb_signing = False;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
allocate a sequence number to a request
|
||||
*/
|
||||
static void req_signing_alloc_seq_num(struct smbsrv_request *req)
|
||||
{
|
||||
req->seq_num = req->smb_conn->signing.next_seq_num;
|
||||
|
||||
if (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF) {
|
||||
req->smb_conn->signing.next_seq_num += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
called for requests that do not produce a reply of their own
|
||||
*/
|
||||
void smbsrv_signing_no_reply(struct smbsrv_request *req)
|
||||
{
|
||||
if (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF) {
|
||||
req->smb_conn->signing.next_seq_num--;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************
|
||||
SMB signing - Simple implementation - check a MAC sent by client
|
||||
************************************************************/
|
||||
/**
|
||||
* Check a packet supplied by the server.
|
||||
* @return False if we had an established signing connection
|
||||
* which had a back checksum, True otherwise
|
||||
*/
|
||||
BOOL smbsrv_signing_check_incoming(struct smbsrv_request *req)
|
||||
{
|
||||
BOOL good;
|
||||
|
||||
req_signing_alloc_seq_num(req);
|
||||
|
||||
switch (req->smb_conn->signing.signing_state)
|
||||
{
|
||||
case SMB_SIGNING_ENGINE_OFF:
|
||||
return True;
|
||||
case SMB_SIGNING_ENGINE_BSRSPYL:
|
||||
case SMB_SIGNING_ENGINE_ON:
|
||||
{
|
||||
if (req->in.size < (HDR_SS_FIELD + 8)) {
|
||||
return False;
|
||||
} else {
|
||||
good = check_signed_incoming_message(&req->in,
|
||||
&req->smb_conn->signing.mac_key,
|
||||
req->seq_num);
|
||||
|
||||
return signing_good(&req->smb_conn->signing,
|
||||
req->seq_num+1, good);
|
||||
}
|
||||
}
|
||||
}
|
||||
return False;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
server side time handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
put a dos date into a buffer (time/date format)
|
||||
This takes GMT time and puts local time for zone_offset in the buffer
|
||||
********************************************************************/
|
||||
void srv_push_dos_date(struct smbsrv_connection *smb_server,
|
||||
uint8_t *buf, int offset, time_t unixdate)
|
||||
{
|
||||
push_dos_date(buf, offset, unixdate, smb_server->negotiate.zone_offset);
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
put a dos date into a buffer (date/time format)
|
||||
This takes GMT time and puts local time in the buffer
|
||||
********************************************************************/
|
||||
void srv_push_dos_date2(struct smbsrv_connection *smb_server,
|
||||
uint8_t *buf, int offset, time_t unixdate)
|
||||
{
|
||||
push_dos_date2(buf, offset, unixdate, smb_server->negotiate.zone_offset);
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
put a dos 32 bit "unix like" date into a buffer. This routine takes
|
||||
GMT and converts it to LOCAL time in zone_offset before putting it
|
||||
********************************************************************/
|
||||
void srv_push_dos_date3(struct smbsrv_connection *smb_server,
|
||||
uint8_t *buf, int offset, time_t unixdate)
|
||||
{
|
||||
push_dos_date3(buf, offset, unixdate, smb_server->negotiate.zone_offset);
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
convert a dos date
|
||||
********************************************************************/
|
||||
time_t srv_pull_dos_date(struct smbsrv_connection *smb_server,
|
||||
const uint8_t *date_ptr)
|
||||
{
|
||||
return pull_dos_date(date_ptr, smb_server->negotiate.zone_offset);
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
like srv_pull_dos_date() but the words are reversed
|
||||
********************************************************************/
|
||||
time_t srv_pull_dos_date2(struct smbsrv_connection *smb_server,
|
||||
const uint8_t *date_ptr)
|
||||
{
|
||||
return pull_dos_date2(date_ptr, smb_server->negotiate.zone_offset);
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
create a unix GMT date from a dos date in 32 bit "unix like" format
|
||||
these arrive in server zone, with corresponding DST
|
||||
******************************************************************/
|
||||
time_t srv_pull_dos_date3(struct smbsrv_connection *smb_server,
|
||||
const uint8_t *date_ptr)
|
||||
{
|
||||
return pull_dos_date3(date_ptr, smb_server->negotiate.zone_offset);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
#######################
|
||||
# Start SUBSYSTEM SMB2_PROTOCOL
|
||||
[SUBSYSTEM::SMB2_PROTOCOL]
|
||||
PRIVATE_PROTO_HEADER = smb2_proto.h
|
||||
OBJ_FILES = \
|
||||
receive.o \
|
||||
negprot.o \
|
||||
sesssetup.o \
|
||||
tcon.o \
|
||||
fileio.o \
|
||||
fileinfo.o \
|
||||
find.o \
|
||||
keepalive.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
ntvfs LIBPACKET LIBCLI_SMB2
|
||||
LDFLAGS = $(SUBSYSTEM_SMB_SERVER_OUTPUT)
|
||||
# End SUBSYSTEM SMB2_PROTOCOL
|
||||
#######################
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
Unix SMB2 implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2006
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
#include "librpc/gen_ndr/ndr_security.h"
|
||||
|
||||
struct smb2srv_getinfo_op {
|
||||
struct smb2srv_request *req;
|
||||
struct smb2_getinfo *info;
|
||||
void *io_ptr;
|
||||
NTSTATUS (*send_fn)(struct smb2srv_getinfo_op *op);
|
||||
};
|
||||
|
||||
static void smb2srv_getinfo_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smb2srv_getinfo_op *op;
|
||||
struct smb2srv_request *req;
|
||||
|
||||
/*
|
||||
* SMB2 uses NT_STATUS_INVALID_INFO_CLASS
|
||||
* so we need to translated it here
|
||||
*/
|
||||
if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, ntvfs->async_states->status)) {
|
||||
ntvfs->async_states->status = NT_STATUS_INVALID_INFO_CLASS;
|
||||
}
|
||||
|
||||
SMB2SRV_CHECK_ASYNC_STATUS(op, struct smb2srv_getinfo_op);
|
||||
|
||||
ZERO_STRUCT(op->info->out);
|
||||
if (op->send_fn) {
|
||||
SMB2SRV_CHECK(op->send_fn(op));
|
||||
}
|
||||
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, True, op->info->out.blob.length));
|
||||
|
||||
/* TODO: this is maybe a o16s32_blob */
|
||||
SMB2SRV_CHECK(smb2_push_o16s16_blob(&req->out, 0x02, op->info->out.blob));
|
||||
SSVAL(req->out.body, 0x06, 0);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_getinfo_file_send(struct smb2srv_getinfo_op *op)
|
||||
{
|
||||
union smb_fileinfo *io = talloc_get_type(op->io_ptr, union smb_fileinfo);
|
||||
NTSTATUS status;
|
||||
|
||||
status = smbsrv_push_passthru_fileinfo(op->req,
|
||||
&op->info->out.blob,
|
||||
io->generic.level, io,
|
||||
STR_UNICODE);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_getinfo_file(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
|
||||
{
|
||||
union smb_fileinfo *io;
|
||||
|
||||
io = talloc(op, union smb_fileinfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io);
|
||||
|
||||
switch (op->info->in.level) {
|
||||
case RAW_FILEINFO_SMB2_ALL_EAS:
|
||||
io->all_eas.level = op->info->in.level;
|
||||
io->all_eas.in.file.ntvfs = op->info->in.file.ntvfs;
|
||||
io->all_eas.in.continue_flags = op->info->in.flags2;
|
||||
break;
|
||||
|
||||
case RAW_FILEINFO_SMB2_ALL_INFORMATION:
|
||||
io->all_info2.level = op->info->in.level;
|
||||
io->all_info2.in.file.ntvfs = op->info->in.file.ntvfs;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* the rest directly maps to the passthru levels */
|
||||
io->generic.level = smb2_level + 1000;
|
||||
io->generic.in.file.ntvfs = op->info->in.file.ntvfs;
|
||||
break;
|
||||
}
|
||||
|
||||
op->io_ptr = io;
|
||||
op->send_fn = smb2srv_getinfo_file_send;
|
||||
|
||||
return ntvfs_qfileinfo(op->req->ntvfs, io);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_getinfo_fs_send(struct smb2srv_getinfo_op *op)
|
||||
{
|
||||
union smb_fsinfo *io = talloc_get_type(op->io_ptr, union smb_fsinfo);
|
||||
NTSTATUS status;
|
||||
|
||||
status = smbsrv_push_passthru_fsinfo(op->req,
|
||||
&op->info->out.blob,
|
||||
io->generic.level, io,
|
||||
STR_UNICODE);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_getinfo_fs(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
|
||||
{
|
||||
union smb_fsinfo *io;
|
||||
|
||||
io = talloc(op, union smb_fsinfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io);
|
||||
|
||||
/* the rest directly maps to the passthru levels */
|
||||
io->generic.level = smb2_level + 1000;
|
||||
|
||||
/* TODO: allow qfsinfo only the share root directory handle */
|
||||
|
||||
op->io_ptr = io;
|
||||
op->send_fn = smb2srv_getinfo_fs_send;
|
||||
|
||||
return ntvfs_fsinfo(op->req->ntvfs, io);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_getinfo_security_send(struct smb2srv_getinfo_op *op)
|
||||
{
|
||||
union smb_fileinfo *io = talloc_get_type(op->io_ptr, union smb_fileinfo);
|
||||
NTSTATUS status;
|
||||
|
||||
status = ndr_push_struct_blob(&op->info->out.blob, op->req,
|
||||
io->query_secdesc.out.sd,
|
||||
(ndr_push_flags_fn_t)ndr_push_security_descriptor);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_getinfo_security(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
|
||||
{
|
||||
union smb_fileinfo *io;
|
||||
|
||||
switch (smb2_level) {
|
||||
case 0x00:
|
||||
io = talloc(op, union smb_fileinfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io);
|
||||
|
||||
io->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
|
||||
io->query_secdesc.in.file.ntvfs = op->info->in.file.ntvfs;
|
||||
io->query_secdesc.in.secinfo_flags = op->info->in.flags;
|
||||
|
||||
op->io_ptr = io;
|
||||
op->send_fn = smb2srv_getinfo_security_send;
|
||||
|
||||
return ntvfs_qfileinfo(op->req->ntvfs, io);
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_getinfo_backend(struct smb2srv_getinfo_op *op)
|
||||
{
|
||||
uint8_t smb2_class;
|
||||
uint8_t smb2_level;
|
||||
|
||||
smb2_class = 0xFF & op->info->in.level;
|
||||
smb2_level = 0xFF & (op->info->in.level>>8);
|
||||
|
||||
switch (smb2_class) {
|
||||
case SMB2_GETINFO_FILE:
|
||||
return smb2srv_getinfo_file(op, smb2_level);
|
||||
|
||||
case SMB2_GETINFO_FS:
|
||||
return smb2srv_getinfo_fs(op, smb2_level);
|
||||
|
||||
case SMB2_GETINFO_SECURITY:
|
||||
return smb2srv_getinfo_security(op, smb2_level);
|
||||
|
||||
case 0x04:
|
||||
return NT_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void smb2srv_getinfo_recv(struct smb2srv_request *req)
|
||||
{
|
||||
struct smb2_getinfo *info;
|
||||
struct smb2srv_getinfo_op *op;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x28, True);
|
||||
SMB2SRV_TALLOC_IO_PTR(info, struct smb2_getinfo);
|
||||
/* this overwrites req->io_ptr !*/
|
||||
SMB2SRV_TALLOC_IO_PTR(op, struct smb2srv_getinfo_op);
|
||||
op->req = req;
|
||||
op->info = info;
|
||||
op->io_ptr = NULL;
|
||||
op->send_fn = NULL;
|
||||
SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_getinfo_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
info->in.level = SVAL(req->in.body, 0x02);
|
||||
info->in.max_response_size = IVAL(req->in.body, 0x04);
|
||||
info->in.unknown1 = IVAL(req->in.body, 0x08);
|
||||
info->in.unknown2 = IVAL(req->in.body, 0x0C);
|
||||
info->in.flags = IVAL(req->in.body, 0x10);
|
||||
info->in.flags2 = IVAL(req->in.body, 0x14);
|
||||
info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x18);
|
||||
|
||||
SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
|
||||
SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_getinfo_backend(op));
|
||||
}
|
||||
|
||||
struct smb2srv_setinfo_op {
|
||||
struct smb2srv_request *req;
|
||||
struct smb2_setinfo *info;
|
||||
};
|
||||
|
||||
static void smb2srv_setinfo_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smb2srv_setinfo_op *op;
|
||||
struct smb2srv_request *req;
|
||||
|
||||
/*
|
||||
* SMB2 uses NT_STATUS_INVALID_INFO_CLASS
|
||||
* so we need to translated it here
|
||||
*/
|
||||
if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, ntvfs->async_states->status)) {
|
||||
ntvfs->async_states->status = NT_STATUS_INVALID_INFO_CLASS;
|
||||
}
|
||||
|
||||
SMB2SRV_CHECK_ASYNC_STATUS(op, struct smb2srv_setinfo_op);
|
||||
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x02, False, 0));
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_setinfo_file(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
|
||||
{
|
||||
union smb_setfileinfo *io;
|
||||
NTSTATUS status;
|
||||
|
||||
io = talloc(op, union smb_setfileinfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io);
|
||||
|
||||
/* the levels directly map to the passthru levels */
|
||||
io->generic.level = smb2_level + 1000;
|
||||
io->generic.in.file.ntvfs = op->info->in.file.ntvfs;
|
||||
|
||||
status = smbsrv_pull_passthru_sfileinfo(io, io->generic.level, io,
|
||||
&op->info->in.blob,
|
||||
STR_UNICODE, NULL);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return ntvfs_setfileinfo(op->req->ntvfs, io);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_setinfo_fs(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
|
||||
{
|
||||
switch (smb2_level) {
|
||||
case 0x02:
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
|
||||
case 0x06:
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
|
||||
case 0x08:
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
|
||||
case 0x0A:
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_INFO_CLASS;
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_setinfo_security(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
|
||||
{
|
||||
union smb_setfileinfo *io;
|
||||
NTSTATUS status;
|
||||
|
||||
switch (smb2_level) {
|
||||
case 0x00:
|
||||
io = talloc(op, union smb_setfileinfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io);
|
||||
|
||||
io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
|
||||
io->set_secdesc.in.file.ntvfs = op->info->in.file.ntvfs;
|
||||
io->set_secdesc.in.secinfo_flags = op->info->in.flags;
|
||||
|
||||
io->set_secdesc.in.sd = talloc(io, struct security_descriptor);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io->set_secdesc.in.sd);
|
||||
|
||||
status = ndr_pull_struct_blob(&op->info->in.blob, io,
|
||||
io->set_secdesc.in.sd,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return ntvfs_setfileinfo(op->req->ntvfs, io);
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_INFO_CLASS;
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_setinfo_backend(struct smb2srv_setinfo_op *op)
|
||||
{
|
||||
uint8_t smb2_class;
|
||||
uint8_t smb2_level;
|
||||
|
||||
smb2_class = 0xFF & op->info->in.level;
|
||||
smb2_level = 0xFF & (op->info->in.level>>8);
|
||||
|
||||
switch (smb2_class) {
|
||||
case SMB2_GETINFO_FILE:
|
||||
return smb2srv_setinfo_file(op, smb2_level);
|
||||
|
||||
case SMB2_GETINFO_FS:
|
||||
return smb2srv_setinfo_fs(op, smb2_level);
|
||||
|
||||
case SMB2_GETINFO_SECURITY:
|
||||
return smb2srv_setinfo_security(op, smb2_level);
|
||||
|
||||
case 0x04:
|
||||
return NT_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void smb2srv_setinfo_recv(struct smb2srv_request *req)
|
||||
{
|
||||
struct smb2_setinfo *info;
|
||||
struct smb2srv_setinfo_op *op;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x20, True);
|
||||
SMB2SRV_TALLOC_IO_PTR(info, struct smb2_setinfo);
|
||||
/* this overwrites req->io_ptr !*/
|
||||
SMB2SRV_TALLOC_IO_PTR(op, struct smb2srv_setinfo_op);
|
||||
op->req = req;
|
||||
op->info = info;
|
||||
SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_setinfo_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
info->in.level = SVAL(req->in.body, 0x02);
|
||||
SMB2SRV_CHECK(smb2_pull_s32o32_blob(&req->in, info, req->in.body+0x04, &info->in.blob));
|
||||
info->in.flags = IVAL(req->in.body, 0x0C);
|
||||
info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
|
||||
|
||||
SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
|
||||
SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_setinfo_backend(op));
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
Unix SMB2 implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
static void smb2srv_create_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
union smb_open *io;
|
||||
|
||||
SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_open);
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x58, True, io->smb2.out.blob.length));
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->smb2.out.oplock_flags);
|
||||
SIVAL(req->out.body, 0x04, io->smb2.out.create_action);
|
||||
SBVAL(req->out.body, 0x08, io->smb2.out.create_time);
|
||||
SBVAL(req->out.body, 0x10, io->smb2.out.access_time);
|
||||
SBVAL(req->out.body, 0x18, io->smb2.out.write_time);
|
||||
SBVAL(req->out.body, 0x20, io->smb2.out.change_time);
|
||||
SBVAL(req->out.body, 0x28, io->smb2.out.alloc_size);
|
||||
SBVAL(req->out.body, 0x30, io->smb2.out.size);
|
||||
SIVAL(req->out.body, 0x38, io->smb2.out.file_attr);
|
||||
SIVAL(req->out.body, 0x3C, io->smb2.out._pad);
|
||||
smb2srv_push_handle(req->out.body, 0x40,io->smb2.out.file.ntvfs);
|
||||
SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x50, io->smb2.out.blob));
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_create_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_open *io;
|
||||
DATA_BLOB blob;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x38, True);
|
||||
SMB2SRV_TALLOC_IO_PTR(io, union smb_open);
|
||||
SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_create_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
io->smb2.level = RAW_OPEN_SMB2;
|
||||
io->smb2.in.oplock_flags = SVAL(req->in.body, 0x02);
|
||||
io->smb2.in.impersonation = IVAL(req->in.body, 0x04);
|
||||
io->smb2.in.unknown3[0] = IVAL(req->in.body, 0x08);
|
||||
io->smb2.in.unknown3[1] = IVAL(req->in.body, 0x0C);
|
||||
io->smb2.in.unknown3[2] = IVAL(req->in.body, 0x10);
|
||||
io->smb2.in.unknown3[3] = IVAL(req->in.body, 0x14);
|
||||
io->smb2.in.access_mask = IVAL(req->in.body, 0x18);
|
||||
io->smb2.in.file_attr = IVAL(req->in.body, 0x1C);
|
||||
io->smb2.in.share_access = IVAL(req->in.body, 0x20);
|
||||
io->smb2.in.open_disposition = IVAL(req->in.body, 0x24);
|
||||
io->smb2.in.create_options = IVAL(req->in.body, 0x28);
|
||||
SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, io, req->in.body+0x2C, &io->smb2.in.fname));
|
||||
SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x30, &blob));
|
||||
/* TODO: parse the blob */
|
||||
ZERO_STRUCT(io->smb2.in.eas);
|
||||
|
||||
SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, io));
|
||||
}
|
||||
|
||||
static void smb2srv_close_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
union smb_close *io;
|
||||
|
||||
SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_close);
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x3C, False, 0));
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->smb2.out.flags);
|
||||
SIVAL(req->out.body, 0x04, io->smb2.out._pad);
|
||||
SBVAL(req->out.body, 0x08, io->smb2.out.create_time);
|
||||
SBVAL(req->out.body, 0x10, io->smb2.out.access_time);
|
||||
SBVAL(req->out.body, 0x18, io->smb2.out.write_time);
|
||||
SBVAL(req->out.body, 0x20, io->smb2.out.change_time);
|
||||
SBVAL(req->out.body, 0x28, io->smb2.out.alloc_size);
|
||||
SBVAL(req->out.body, 0x30, io->smb2.out.size);
|
||||
SIVAL(req->out.body, 0x38, io->smb2.out.file_attr);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_close_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_close *io;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x18, False);
|
||||
SMB2SRV_TALLOC_IO_PTR(io, union smb_close);
|
||||
SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_close_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
io->smb2.level = RAW_CLOSE_SMB2;
|
||||
io->smb2.in.flags = SVAL(req->in.body, 0x02);
|
||||
io->smb2.in._pad = IVAL(req->in.body, 0x04);
|
||||
io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
|
||||
|
||||
SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
|
||||
SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_close(req->ntvfs, io));
|
||||
}
|
||||
|
||||
static void smb2srv_flush_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
union smb_flush *io;
|
||||
|
||||
SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_flush);
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, False, 0));
|
||||
|
||||
SSVAL(req->out.body, 0x02, 0);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_flush_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_flush *io;
|
||||
uint16_t _pad;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x18, False);
|
||||
SMB2SRV_TALLOC_IO_PTR(io, union smb_flush);
|
||||
SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_flush_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
io->smb2.level = RAW_FLUSH_SMB2;
|
||||
_pad = SVAL(req->in.body, 0x02);
|
||||
io->smb2.in.unknown = IVAL(req->in.body, 0x04);
|
||||
io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
|
||||
|
||||
SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
|
||||
SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_flush(req->ntvfs, io));
|
||||
}
|
||||
|
||||
static void smb2srv_read_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
union smb_read *io;
|
||||
|
||||
SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_read);
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, True, io->smb2.out.data.length));
|
||||
|
||||
SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, io->smb2.out.data));
|
||||
SBVAL(req->out.body, 0x08, io->smb2.out.unknown1);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_read_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_read *io;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x30, True);
|
||||
SMB2SRV_TALLOC_IO_PTR(io, union smb_read);
|
||||
SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_read_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
io->smb2.level = RAW_READ_SMB2;
|
||||
io->smb2.in._pad = SVAL(req->in.body, 0x02);
|
||||
io->smb2.in.length = IVAL(req->in.body, 0x04);
|
||||
io->smb2.in.offset = BVAL(req->in.body, 0x08);
|
||||
io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
|
||||
io->smb2.in.unknown1 = BVAL(req->in.body, 0x20);
|
||||
io->smb2.in.unknown2 = BVAL(req->in.body, 0x28);
|
||||
|
||||
SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
|
||||
|
||||
/* preallocate the buffer for the backends */
|
||||
io->smb2.out.data = data_blob_talloc(io, NULL, io->smb2.in.length);
|
||||
if (io->smb2.out.data.length != io->smb2.in.length) {
|
||||
SMB2SRV_CHECK(NT_STATUS_NO_MEMORY);
|
||||
}
|
||||
|
||||
SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io));
|
||||
}
|
||||
|
||||
static void smb2srv_write_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
union smb_write *io;
|
||||
|
||||
SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_write);
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, True, 0));
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->smb2.out._pad);
|
||||
SIVAL(req->out.body, 0x04, io->smb2.out.nwritten);
|
||||
SBVAL(req->out.body, 0x08, io->smb2.out.unknown1);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_write_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_write *io;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x30, True);
|
||||
SMB2SRV_TALLOC_IO_PTR(io, union smb_write);
|
||||
SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_write_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
/* TODO: avoid the memcpy */
|
||||
io->smb2.level = RAW_WRITE_SMB2;
|
||||
SMB2SRV_CHECK(smb2_pull_o16s32_blob(&req->in, io, req->in.body+0x02, &io->smb2.in.data));
|
||||
io->smb2.in.offset = BVAL(req->in.body, 0x08);
|
||||
io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
|
||||
io->smb2.in.unknown1 = BVAL(req->in.body, 0x20);
|
||||
io->smb2.in.unknown2 = BVAL(req->in.body, 0x28);
|
||||
|
||||
SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
|
||||
SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
|
||||
}
|
||||
|
||||
void smb2srv_lock_recv(struct smb2srv_request *req)
|
||||
{
|
||||
smb2srv_send_error(req, NT_STATUS_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
static void smb2srv_ioctl_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
union smb_ioctl *io;
|
||||
|
||||
SMB2SRV_CHECK_ASYNC_STATUS_ERR(io, union smb_ioctl);
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x30, True, 0));
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->smb2.out._pad);
|
||||
SIVAL(req->out.body, 0x04, io->smb2.out.function);
|
||||
if (io->smb2.level == RAW_IOCTL_SMB2_NO_HANDLE) {
|
||||
struct smb2_handle h;
|
||||
h.data[0] = UINT64_MAX;
|
||||
h.data[1] = UINT64_MAX;
|
||||
smb2_push_handle(req->out.body + 0x08, &h);
|
||||
} else {
|
||||
smb2srv_push_handle(req->out.body, 0x08,io->smb2.in.file.ntvfs);
|
||||
}
|
||||
SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x18, io->smb2.out.in));
|
||||
SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x20, io->smb2.out.out));
|
||||
SIVAL(req->out.body, 0x28, io->smb2.out.unknown2);
|
||||
SIVAL(req->out.body, 0x2C, io->smb2.out.unknown3);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_ioctl_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_ioctl *io;
|
||||
struct smb2_handle h;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x38, True);
|
||||
SMB2SRV_TALLOC_IO_PTR(io, union smb_ioctl);
|
||||
SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_ioctl_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
/* TODO: avoid the memcpy */
|
||||
io->smb2.in._pad = SVAL(req->in.body, 0x02);
|
||||
io->smb2.in.function = IVAL(req->in.body, 0x04);
|
||||
/* file handle ... */
|
||||
SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x18, &io->smb2.in.out));
|
||||
io->smb2.in.unknown2 = IVAL(req->in.body, 0x20);
|
||||
SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x24, &io->smb2.in.in));
|
||||
io->smb2.in.max_response_size = IVAL(req->in.body, 0x2C);
|
||||
io->smb2.in.flags = BVAL(req->in.body, 0x30);
|
||||
|
||||
smb2_pull_handle(req->in.body + 0x08, &h);
|
||||
if (h.data[0] == UINT64_MAX && h.data[1] == UINT64_MAX) {
|
||||
io->smb2.level = RAW_IOCTL_SMB2_NO_HANDLE;
|
||||
} else {
|
||||
io->smb2.level = RAW_IOCTL_SMB2;
|
||||
io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
|
||||
SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
|
||||
}
|
||||
|
||||
SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_ioctl(req->ntvfs, io));
|
||||
}
|
||||
|
||||
static void smb2srv_notify_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
union smb_notify *io;
|
||||
size_t size = 0;
|
||||
int i;
|
||||
uint8_t *p;
|
||||
DATA_BLOB blob = data_blob(NULL, 0);
|
||||
|
||||
SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_notify);
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, True, 0));
|
||||
|
||||
#define MAX_BYTES_PER_CHAR 3
|
||||
|
||||
/* work out how big the reply buffer could be */
|
||||
for (i=0;i<io->smb2.out.num_changes;i++) {
|
||||
size += 12 + 3 + (1+strlen(io->smb2.out.changes[i].name.s)) * MAX_BYTES_PER_CHAR;
|
||||
}
|
||||
|
||||
blob = data_blob_talloc(req, NULL, size);
|
||||
if (size > 0 && !blob.data) {
|
||||
SMB2SRV_CHECK(NT_STATUS_NO_MEMORY);
|
||||
}
|
||||
|
||||
p = blob.data;
|
||||
|
||||
/* construct the changes buffer */
|
||||
for (i=0;i<io->smb2.out.num_changes;i++) {
|
||||
uint32_t ofs;
|
||||
ssize_t len;
|
||||
|
||||
SIVAL(p, 4, io->smb2.out.changes[i].action);
|
||||
len = push_string(p + 12, io->smb2.out.changes[i].name.s,
|
||||
blob.length - (p+12 - blob.data), STR_UNICODE);
|
||||
SIVAL(p, 8, len);
|
||||
|
||||
ofs = len + 12;
|
||||
|
||||
if (ofs & 3) {
|
||||
int pad = 4 - (ofs & 3);
|
||||
memset(p+ofs, 0, pad);
|
||||
ofs += pad;
|
||||
}
|
||||
|
||||
if (i == io->smb2.out.num_changes-1) {
|
||||
SIVAL(p, 0, 0);
|
||||
} else {
|
||||
SIVAL(p, 0, ofs);
|
||||
}
|
||||
|
||||
p += ofs;
|
||||
}
|
||||
|
||||
blob.length = p - blob.data;
|
||||
|
||||
SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, blob));
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_notify_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_notify *io;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x20, False);
|
||||
SMB2SRV_TALLOC_IO_PTR(io, union smb_notify);
|
||||
SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_notify_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
io->smb2.level = RAW_NOTIFY_SMB2;
|
||||
io->smb2.in.recursive = SVAL(req->in.body, 0x02);
|
||||
io->smb2.in.buffer_size = IVAL(req->in.body, 0x04);
|
||||
io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
|
||||
io->smb2.in.completion_filter = IVAL(req->in.body, 0x18);
|
||||
io->smb2.in.unknown = BVAL(req->in.body, 0x1C);
|
||||
|
||||
SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
|
||||
SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_notify(req->ntvfs, io));
|
||||
}
|
||||
|
||||
void smb2srv_break_recv(struct smb2srv_request *req)
|
||||
{
|
||||
smb2srv_send_error(req, NT_STATUS_NOT_IMPLEMENTED);
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMB2 Find
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
Copyright (c) Stefan Metzmacher 2006
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/*
|
||||
This file handles the parsing of transact2 requests
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
|
||||
/* a structure to encapsulate the state information about an in-progress ffirst/fnext operation */
|
||||
struct smb2srv_find_state {
|
||||
struct smb2srv_request *req;
|
||||
struct smb2_find *info;
|
||||
union smb_search_first *ff;
|
||||
union smb_search_next *fn;
|
||||
uint32_t last_entry_offset;
|
||||
};
|
||||
|
||||
/* callback function for SMB2 Find */
|
||||
static BOOL smb2srv_find_callback(void *private, union smb_search_data *file)
|
||||
{
|
||||
struct smb2srv_find_state *state = talloc_get_type(private, struct smb2srv_find_state);
|
||||
struct smb2_find *info = state->info;
|
||||
uint32_t old_length;
|
||||
NTSTATUS status;
|
||||
|
||||
old_length = info->out.blob.length;
|
||||
|
||||
status = smbsrv_push_passthru_search(state, &info->out.blob, info->data_level, file, STR_UNICODE);
|
||||
if (!NT_STATUS_IS_OK(status) ||
|
||||
info->out.blob.length > info->in.max_response_size) {
|
||||
/* restore the old length and tell the backend to stop */
|
||||
smbsrv_blob_grow_data(state, &info->out.blob, old_length);
|
||||
return False;
|
||||
}
|
||||
|
||||
state->last_entry_offset = old_length;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static void smb2srv_find_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
struct smb2srv_find_state *state;
|
||||
|
||||
SMB2SRV_CHECK_ASYNC_STATUS(state, struct smb2srv_find_state);
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, True, state->info->out.blob.length));
|
||||
|
||||
if (state->info->out.blob.length > 0) {
|
||||
SIVAL(state->info->out.blob.data + state->last_entry_offset, 0, 0);
|
||||
}
|
||||
|
||||
SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, state->info->out.blob));
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_find_backend(struct smb2srv_find_state *state)
|
||||
{
|
||||
struct smb2_find *info = state->info;
|
||||
|
||||
switch (info->in.level) {
|
||||
case SMB2_FIND_DIRECTORY_INFO:
|
||||
info->data_level = RAW_SEARCH_DATA_DIRECTORY_INFO;
|
||||
break;
|
||||
|
||||
case SMB2_FIND_FULL_DIRECTORY_INFO:
|
||||
info->data_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO;
|
||||
break;
|
||||
|
||||
case SMB2_FIND_BOTH_DIRECTORY_INFO:
|
||||
info->data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
|
||||
break;
|
||||
|
||||
case SMB2_FIND_NAME_INFO:
|
||||
info->data_level = RAW_SEARCH_DATA_NAME_INFO;
|
||||
break;
|
||||
|
||||
case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
|
||||
info->data_level = RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO;
|
||||
break;
|
||||
|
||||
case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
|
||||
info->data_level = RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO;
|
||||
break;
|
||||
|
||||
default:
|
||||
return NT_STATUS_FOOBAR;
|
||||
}
|
||||
|
||||
if (info->in.continue_flags & SMB2_CONTINUE_FLAG_NEW) {
|
||||
state->ff = talloc(state, union smb_search_first);
|
||||
NT_STATUS_HAVE_NO_MEMORY(state->ff);
|
||||
|
||||
state->ff->smb2 = *info;
|
||||
state->info = &state->ff->smb2;
|
||||
ZERO_STRUCT(state->ff->smb2.out);
|
||||
|
||||
return ntvfs_search_first(state->req->ntvfs, state->ff, state, smb2srv_find_callback);
|
||||
} else {
|
||||
state->fn = talloc(state, union smb_search_next);
|
||||
NT_STATUS_HAVE_NO_MEMORY(state->fn);
|
||||
|
||||
state->fn->smb2 = *info;
|
||||
state->info = &state->fn->smb2;
|
||||
ZERO_STRUCT(state->fn->smb2.out);
|
||||
|
||||
return ntvfs_search_next(state->req->ntvfs, state->fn, state, smb2srv_find_callback);
|
||||
}
|
||||
|
||||
/* should not be reached */
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
void smb2srv_find_recv(struct smb2srv_request *req)
|
||||
{
|
||||
struct smb2srv_find_state *state;
|
||||
struct smb2_find *info;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x20, True);
|
||||
SMB2SRV_TALLOC_IO_PTR(info, struct smb2_find);
|
||||
/* this overwrites req->io_ptr !*/
|
||||
SMB2SRV_TALLOC_IO_PTR(state, struct smb2srv_find_state);
|
||||
state->req = req;
|
||||
state->info = info;
|
||||
state->ff = NULL;
|
||||
state->fn = NULL;
|
||||
state->last_entry_offset= 0;
|
||||
SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_find_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
|
||||
|
||||
info->level = RAW_SEARCH_SMB2;
|
||||
info->data_level = RAW_SEARCH_DATA_GENERIC;/* will be overwritten later */
|
||||
info->in.level = CVAL(req->in.body, 0x02);
|
||||
info->in.continue_flags = CVAL(req->in.body, 0x03);
|
||||
info->in.unknown = IVAL(req->in.body, 0x04);
|
||||
info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
|
||||
SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, info, req->in.body+0x18, &info->in.pattern));
|
||||
info->in.max_response_size = IVAL(req->in.body, 0x1C);
|
||||
|
||||
SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
|
||||
SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_find_backend(state));
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
Unix SMB2 implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
|
||||
static NTSTATUS smb2srv_keepalive_backend(struct smb2srv_request *req)
|
||||
{
|
||||
/* TODO: maybe update some flags on the connection struct */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void smb2srv_keepalive_send(struct smb2srv_request *req)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (NT_STATUS_IS_ERR(req->status)) {
|
||||
smb2srv_send_error(req, req->status);
|
||||
return;
|
||||
}
|
||||
|
||||
status = smb2srv_setup_reply(req, 0x04, False, 0);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
SSVAL(req->out.body, 0x02, 0);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_keepalive_recv(struct smb2srv_request *req)
|
||||
{
|
||||
uint16_t _pad;
|
||||
|
||||
if (req->in.body_size < 0x04) {
|
||||
smb2srv_send_error(req, NT_STATUS_FOOBAR);
|
||||
return;
|
||||
}
|
||||
|
||||
_pad = SVAL(req->in.body, 0x02);
|
||||
|
||||
req->status = smb2srv_keepalive_backend(req);
|
||||
|
||||
if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smb2srv_keepalive_send(req);
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
Unix SMB2 implementation.
|
||||
|
||||
Copyright (C) Andrew Bartlett 2001-2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "smbd/service_stream.h"
|
||||
|
||||
static NTSTATUS smb2srv_negprot_secblob(struct smb2srv_request *req, DATA_BLOB *_blob)
|
||||
{
|
||||
struct gensec_security *gensec_security;
|
||||
DATA_BLOB null_data_blob = data_blob(NULL, 0);
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS nt_status;
|
||||
struct cli_credentials *server_credentials;
|
||||
|
||||
nt_status = gensec_server_start(req,
|
||||
req->smb_conn->connection->event.ctx,
|
||||
req->smb_conn->connection->msg_ctx,
|
||||
&gensec_security);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status)));
|
||||
smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n");
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
server_credentials = cli_credentials_init(req);
|
||||
if (!server_credentials) {
|
||||
smbsrv_terminate_connection(req->smb_conn, "Failed to init server credentials\n");
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
cli_credentials_set_conf(server_credentials);
|
||||
nt_status = cli_credentials_set_machine_account(server_credentials);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(server_credentials);
|
||||
server_credentials = NULL;
|
||||
}
|
||||
|
||||
req->smb_conn->negotiate.server_credentials = talloc_steal(req->smb_conn, server_credentials);
|
||||
|
||||
gensec_set_target_service(gensec_security, "cifs");
|
||||
|
||||
gensec_set_credentials(gensec_security, server_credentials);
|
||||
|
||||
nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_SPNEGO);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(0, ("Failed to start SPNEGO: %s\n", nt_errstr(nt_status)));
|
||||
smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO\n");
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = gensec_update(gensec_security, req, null_data_blob, &blob);
|
||||
if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
DEBUG(0, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
|
||||
smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO - no first token\n");
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
*_blob = blob;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_negprot_backend(struct smb2srv_request *req, struct smb2_negprot *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct timeval current_time;
|
||||
struct timeval boot_time;
|
||||
|
||||
req->smb_conn->negotiate.protocol = PROTOCOL_SMB2;
|
||||
|
||||
current_time = timeval_current(); /* TODO: handle timezone?! */
|
||||
boot_time = timeval_current(); /* TODO: fix me */
|
||||
|
||||
io->out._pad = 0;
|
||||
io->out.unknown2 = 0x06;
|
||||
ZERO_STRUCT(io->out.sessid);
|
||||
io->out.unknown3 = 0x0d;
|
||||
io->out.unknown4 = 0x00;
|
||||
io->out.unknown5 = 0x01;
|
||||
io->out.unknown6 = 0x01;
|
||||
io->out.unknown7 = 0x01;
|
||||
io->out.current_time = timeval_to_nttime(¤t_time);
|
||||
io->out.boot_time = timeval_to_nttime(&boot_time);
|
||||
status = smb2srv_negprot_secblob(req, &io->out.secblob);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
io->out.unknown9 = 0x204d4c20;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void smb2srv_negprot_send(struct smb2srv_request *req, struct smb2_negprot *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (NT_STATUS_IS_ERR(req->status)) {
|
||||
smb2srv_send_error(req, req->status); /* TODO: is this correct? */
|
||||
return;
|
||||
}
|
||||
|
||||
status = smb2srv_setup_reply(req, 0x40, True, io->out.secblob.length);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->out._pad);
|
||||
SIVAL(req->out.body, 0x04, io->out.unknown2);
|
||||
memcpy(req->out.body+0x08, io->out.sessid, 16);
|
||||
SIVAL(req->out.body, 0x18, io->out.unknown3);
|
||||
SSVAL(req->out.body, 0x1C, io->out.unknown4);
|
||||
SIVAL(req->out.body, 0x1E, io->out.unknown5);
|
||||
SIVAL(req->out.body, 0x22, io->out.unknown6);
|
||||
SSVAL(req->out.body, 0x26, io->out.unknown7);
|
||||
push_nttime(req->out.body, 0x28, io->out.current_time);
|
||||
push_nttime(req->out.body, 0x30, io->out.boot_time);
|
||||
status = smb2_push_o16s16_blob(&req->out, 0x38, io->out.secblob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
SIVAL(req->out.body, 0x3C, io->out.unknown9);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_negprot_recv(struct smb2srv_request *req)
|
||||
{
|
||||
struct smb2_negprot *io;
|
||||
|
||||
if (req->in.body_size < 0x26) {
|
||||
smb2srv_send_error(req, NT_STATUS_FOOBAR);
|
||||
return;
|
||||
}
|
||||
|
||||
io = talloc(req, struct smb2_negprot);
|
||||
if (!io) {
|
||||
smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
io->in.unknown1 = SVAL(req->in.body, 0x02);
|
||||
memcpy(io->in.unknown2, req->in.body + 0x04, 0x20);
|
||||
io->in.unknown3 = SVAL(req->in.body, 0x24);
|
||||
|
||||
req->status = smb2srv_negprot_backend(req, io);
|
||||
|
||||
if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smb2srv_negprot_send(req, io);
|
||||
}
|
||||
|
||||
/*
|
||||
* reply to a SMB negprot request with dialect "SMB 2.001"
|
||||
*/
|
||||
void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
uint32_t body_fixed_size = 0x26;
|
||||
|
||||
/* create a fake SMB2 negprot request */
|
||||
req = talloc_zero(smb_req->smb_conn, struct smb2srv_request);
|
||||
if (!req) goto nomem;
|
||||
req->smb_conn = smb_req->smb_conn;
|
||||
req->request_time = smb_req->request_time;
|
||||
talloc_steal(req, smb_req);
|
||||
|
||||
req->in.size = NBT_HDR_SIZE+SMB2_HDR_BODY+body_fixed_size;
|
||||
req->in.allocated = req->in.size;
|
||||
req->in.buffer = talloc_size(req, req->in.allocated);
|
||||
if (!req->in.buffer) goto nomem;
|
||||
req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
|
||||
req->in.body = req->in.hdr + SMB2_HDR_BODY;
|
||||
req->in.body_size = body_fixed_size;
|
||||
req->in.dynamic = NULL;
|
||||
|
||||
SIVAL(req->in.hdr, 0, SMB2_MAGIC);
|
||||
SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
|
||||
SSVAL(req->in.hdr, SMB2_HDR_PAD1, 0);
|
||||
SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0);
|
||||
SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_NEGPROT);
|
||||
SSVAL(req->in.hdr, SMB2_HDR_UNKNOWN1,0);
|
||||
SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0);
|
||||
SIVAL(req->in.hdr, SMB2_HDR_UNKNOWN2,0);
|
||||
SBVAL(req->in.hdr, SMB2_HDR_SEQNUM, 0);
|
||||
SIVAL(req->in.hdr, SMB2_HDR_PID, 0);
|
||||
SIVAL(req->in.hdr, SMB2_HDR_TID, 0);
|
||||
SBVAL(req->in.hdr, SMB2_HDR_UID, 0);
|
||||
memset(req->in.hdr+SMB2_HDR_SIG, 0, 16);
|
||||
|
||||
/* this seems to be a bug, they use 0x24 but the length is 0x26 */
|
||||
SSVAL(req->in.body, 0x00, 0x24);
|
||||
|
||||
SSVAL(req->in.body, 0x02, 1);
|
||||
memset(req->in.body+0x04, 0, 32);
|
||||
SSVAL(req->in.body, 0x24, 0);
|
||||
|
||||
smb2srv_negprot_recv(req);
|
||||
return;
|
||||
nomem:
|
||||
smbsrv_terminate_connection(smb_req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
Unix SMB2 implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/time.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "lib/stream/packet.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
static int smb2srv_request_destructor(struct smb2srv_request *req)
|
||||
{
|
||||
DLIST_REMOVE(req->smb_conn->requests2.list, req);
|
||||
if (req->pending_id) {
|
||||
idr_remove(req->smb_conn->requests2.idtree_req, req->pending_id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smb2srv_request_deny_destructor(struct smb2srv_request *req)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct smb2srv_request *smb2srv_init_request(struct smbsrv_connection *smb_conn)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
|
||||
req = talloc_zero(smb_conn, struct smb2srv_request);
|
||||
if (!req) return NULL;
|
||||
|
||||
req->smb_conn = smb_conn;
|
||||
|
||||
talloc_set_destructor(req, smb2srv_request_destructor);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_size,
|
||||
BOOL body_dynamic_present, uint32_t body_dynamic_size)
|
||||
{
|
||||
uint32_t flags = 0x00000001;
|
||||
uint32_t pid = IVAL(req->in.hdr, SMB2_HDR_PID);
|
||||
uint32_t tid = IVAL(req->in.hdr, SMB2_HDR_TID);
|
||||
|
||||
if (req->pending_id) {
|
||||
flags |= 0x00000002;
|
||||
pid = req->pending_id;
|
||||
tid = 0;
|
||||
}
|
||||
|
||||
if (body_dynamic_present) {
|
||||
if (body_dynamic_size == 0) {
|
||||
body_dynamic_size = 1;
|
||||
}
|
||||
} else {
|
||||
body_dynamic_size = 0;
|
||||
}
|
||||
|
||||
req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
|
||||
|
||||
req->out.allocated = req->out.size + body_dynamic_size;
|
||||
req->out.buffer = talloc_size(req, req->out.allocated);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req->out.buffer);
|
||||
|
||||
req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
|
||||
req->out.body = req->out.hdr + SMB2_HDR_BODY;
|
||||
req->out.body_fixed = body_fixed_size;
|
||||
req->out.body_size = body_fixed_size;
|
||||
req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL);
|
||||
|
||||
SIVAL(req->out.hdr, 0, SMB2_MAGIC);
|
||||
SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
|
||||
SSVAL(req->out.hdr, SMB2_HDR_PAD1, 0);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(req->status));
|
||||
SSVAL(req->out.hdr, SMB2_HDR_OPCODE, SVAL(req->in.hdr, SMB2_HDR_OPCODE));
|
||||
SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1,0x0001);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_FLAGS, flags);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_UNKNOWN2,0);
|
||||
SBVAL(req->out.hdr, SMB2_HDR_SEQNUM, req->seqnum);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_PID, pid);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_TID, tid);
|
||||
SBVAL(req->out.hdr, SMB2_HDR_UID, BVAL(req->in.hdr, SMB2_HDR_UID));
|
||||
memset(req->out.hdr+SMB2_HDR_SIG, 0, 16);
|
||||
|
||||
/* set the length of the fixed body part and +1 if there's a dynamic part also */
|
||||
SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
|
||||
|
||||
/*
|
||||
* if we have a dynamic part, make sure the first byte
|
||||
* which is always be part of the packet is initialized
|
||||
*/
|
||||
if (body_dynamic_size) {
|
||||
req->out.size += 1;
|
||||
SCVAL(req->out.dynamic, 0, 0);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
void smb2srv_send_reply(struct smb2srv_request *req)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
|
||||
if (req->smb_conn->connection->event.fde == NULL) {
|
||||
/* the socket has been destroyed - no point trying to send a reply! */
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->out.size > NBT_HDR_SIZE) {
|
||||
_smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
|
||||
}
|
||||
|
||||
blob = data_blob_const(req->out.buffer, req->out.size);
|
||||
status = packet_send(req->smb_conn->packet, blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
|
||||
}
|
||||
talloc_free(req);
|
||||
}
|
||||
|
||||
void smb2srv_send_error(struct smb2srv_request *req, NTSTATUS error)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (req->smb_conn->connection->event.fde == NULL) {
|
||||
/* the socket has been destroyed - no point trying to send an error! */
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
status = smb2srv_setup_reply(req, 8, True, 0);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(error));
|
||||
|
||||
SSVAL(req->out.body, 0x02, 0);
|
||||
SIVAL(req->out.body, 0x04, 0);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_reply(struct smb2srv_request *req)
|
||||
{
|
||||
uint16_t opcode;
|
||||
uint32_t tid;
|
||||
uint64_t uid;
|
||||
|
||||
opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
|
||||
req->seqnum = BVAL(req->in.hdr, SMB2_HDR_SEQNUM);
|
||||
tid = IVAL(req->in.hdr, SMB2_HDR_TID);
|
||||
uid = BVAL(req->in.hdr, SMB2_HDR_UID);
|
||||
|
||||
req->session = smbsrv_session_find(req->smb_conn, uid, req->request_time);
|
||||
req->tcon = smbsrv_smb2_tcon_find(req->session, tid, req->request_time);
|
||||
|
||||
errno = 0;
|
||||
|
||||
/* TODO: check the seqnum */
|
||||
|
||||
switch (opcode) {
|
||||
case SMB2_OP_NEGPROT:
|
||||
smb2srv_negprot_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_SESSSETUP:
|
||||
smb2srv_sesssetup_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_LOGOFF:
|
||||
if (!req->session) goto nosession;
|
||||
smb2srv_logoff_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_TCON:
|
||||
if (!req->session) goto nosession;
|
||||
smb2srv_tcon_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_TDIS:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_tdis_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_CREATE:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_create_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_CLOSE:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_close_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_FLUSH:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_flush_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_READ:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_read_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_WRITE:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_write_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_LOCK:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_lock_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_IOCTL:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_ioctl_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_CANCEL:
|
||||
smb2srv_cancel_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_KEEPALIVE:
|
||||
smb2srv_keepalive_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_FIND:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_find_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_NOTIFY:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_notify_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_GETINFO:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_getinfo_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_SETINFO:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_setinfo_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
case SMB2_OP_BREAK:
|
||||
if (!req->session) goto nosession;
|
||||
if (!req->tcon) goto notcon;
|
||||
smb2srv_break_recv(req);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(1,("Invalid SMB2 opcode: 0x%04X\n", opcode));
|
||||
smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 opcode");
|
||||
return NT_STATUS_OK;
|
||||
|
||||
nosession:
|
||||
smb2srv_send_error(req, NT_STATUS_USER_SESSION_DELETED);
|
||||
return NT_STATUS_OK;
|
||||
notcon:
|
||||
smb2srv_send_error(req, NT_STATUS_NETWORK_NAME_DELETED);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS smbsrv_recv_smb2_request(void *private, DATA_BLOB blob)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = talloc_get_type(private, struct smbsrv_connection);
|
||||
struct smb2srv_request *req;
|
||||
struct timeval cur_time = timeval_current();
|
||||
uint32_t protocol_version;
|
||||
uint16_t buffer_code;
|
||||
uint32_t dynamic_size;
|
||||
|
||||
smb_conn->statistics.last_request_time = cur_time;
|
||||
|
||||
/* see if its a special NBT packet */
|
||||
if (CVAL(blob.data,0) != 0) {
|
||||
DEBUG(2,("Special NBT packet on SMB2 connection"));
|
||||
smbsrv_terminate_connection(smb_conn, "Special NBT packet on SMB2 connection");
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (blob.length < (NBT_HDR_SIZE + SMB2_MIN_SIZE)) {
|
||||
DEBUG(2,("Invalid SMB2 packet length count %ld\n", (long)blob.length));
|
||||
smbsrv_terminate_connection(smb_conn, "Invalid SMB2 packet");
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
protocol_version = IVAL(blob.data, NBT_HDR_SIZE);
|
||||
|
||||
if (protocol_version != SMB2_MAGIC) {
|
||||
DEBUG(2,("Invalid SMB packet: protocol prefix: 0x%08X\n",
|
||||
protocol_version));
|
||||
smbsrv_terminate_connection(smb_conn, "NON-SMB2 packet");
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
req = smb2srv_init_request(smb_conn);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
req->in.buffer = talloc_steal(req, blob.data);
|
||||
req->in.size = blob.length;
|
||||
req->request_time = cur_time;
|
||||
req->in.allocated = req->in.size;
|
||||
|
||||
req->in.hdr = req->in.buffer+ NBT_HDR_SIZE;
|
||||
req->in.body = req->in.hdr + SMB2_HDR_BODY;
|
||||
req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
|
||||
req->in.dynamic = NULL;
|
||||
|
||||
buffer_code = SVAL(req->in.body, 0);
|
||||
req->in.body_fixed = (buffer_code & ~1);
|
||||
dynamic_size = req->in.body_size - req->in.body_fixed;
|
||||
|
||||
if (dynamic_size != 0 && (buffer_code & 1)) {
|
||||
req->in.dynamic = req->in.body + req->in.body_fixed;
|
||||
if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
|
||||
DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n",
|
||||
dynamic_size));
|
||||
smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: - make sure the length field is 64
|
||||
* - make sure it's a request
|
||||
*/
|
||||
|
||||
return smb2srv_reply(req);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_init_pending(struct smbsrv_connection *smb_conn)
|
||||
{
|
||||
smb_conn->requests2.idtree_req = idr_init(smb_conn);
|
||||
NT_STATUS_HAVE_NO_MEMORY(smb_conn->requests2.idtree_req);
|
||||
smb_conn->requests2.idtree_limit = 0x00FFFFFF & (UINT32_MAX - 1);
|
||||
smb_conn->requests2.list = NULL;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS smb2srv_queue_pending(struct smb2srv_request *req)
|
||||
{
|
||||
int id;
|
||||
|
||||
if (req->pending_id) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
id = idr_get_new_above(req->smb_conn->requests2.idtree_req, req,
|
||||
1, req->smb_conn->requests2.idtree_limit);
|
||||
if (id == -1) {
|
||||
return NT_STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
DLIST_ADD_END(req->smb_conn->requests2.list, req, struct smb2srv_request *);
|
||||
req->pending_id = id;
|
||||
|
||||
talloc_set_destructor(req, smb2srv_request_deny_destructor);
|
||||
smb2srv_send_error(req, STATUS_PENDING);
|
||||
talloc_set_destructor(req, smb2srv_request_destructor);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
void smb2srv_cancel_recv(struct smb2srv_request *req)
|
||||
{
|
||||
uint32_t pending_id;
|
||||
uint32_t flags;
|
||||
void *p;
|
||||
struct smb2srv_request *r;
|
||||
|
||||
if (!req->session) goto done;
|
||||
|
||||
flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
|
||||
pending_id = IVAL(req->in.hdr, SMB2_HDR_PID);
|
||||
|
||||
if (!(flags & 0x00000002)) {
|
||||
/* TODO: what to do here? */
|
||||
goto done;
|
||||
}
|
||||
|
||||
p = idr_find(req->smb_conn->requests2.idtree_req, pending_id);
|
||||
if (!p) goto done;
|
||||
|
||||
r = talloc_get_type(p, struct smb2srv_request);
|
||||
if (!r) goto done;
|
||||
|
||||
if (!r->ntvfs) goto done;
|
||||
|
||||
ntvfs_cancel(r->ntvfs);
|
||||
|
||||
done:
|
||||
/* we never generate a reply for a SMB2 Cancel */
|
||||
talloc_free(req);
|
||||
}
|
||||
|
||||
/*
|
||||
* init the SMB2 protocol related stuff
|
||||
*/
|
||||
NTSTATUS smbsrv_init_smb2_connection(struct smbsrv_connection *smb_conn)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
/* now initialise a few default values associated with this smb socket */
|
||||
smb_conn->negotiate.max_send = 0xFFFF;
|
||||
|
||||
/* this is the size that w2k uses, and it appears to be important for
|
||||
good performance */
|
||||
smb_conn->negotiate.max_recv = lp_max_xmit();
|
||||
|
||||
smb_conn->negotiate.zone_offset = get_time_zone(time(NULL));
|
||||
|
||||
smb_conn->config.security = SEC_USER;
|
||||
smb_conn->config.nt_status_support = True;
|
||||
|
||||
status = smbsrv_init_sessions(smb_conn, UINT64_MAX);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = smb2srv_init_pending(smb_conn);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
Unix SMB2 implementation.
|
||||
|
||||
Copyright (C) Andrew Bartlett 2001-2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "auth/auth.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "smbd/service_stream.h"
|
||||
|
||||
static void smb2srv_sesssetup_send(struct smb2srv_request *req, union smb_sesssetup *io)
|
||||
{
|
||||
uint16_t unknown1;
|
||||
|
||||
if (NT_STATUS_IS_OK(req->status)) {
|
||||
unknown1 = 0x0003;
|
||||
} else if (NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
unknown1 = 0x0002;
|
||||
} else {
|
||||
smb2srv_send_error(req, req->status);
|
||||
return;
|
||||
}
|
||||
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, True, io->smb2.out.secblob.length));
|
||||
|
||||
SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1, unknown1);
|
||||
SBVAL(req->out.hdr, SMB2_HDR_UID, io->smb2.out.uid);
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->smb2.out._pad);
|
||||
SMB2SRV_CHECK(smb2_push_o16s16_blob(&req->out, 0x04, io->smb2.out.secblob));
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
struct smb2srv_sesssetup_callback_ctx {
|
||||
struct smb2srv_request *req;
|
||||
union smb_sesssetup *io;
|
||||
struct smbsrv_session *smb_sess;
|
||||
};
|
||||
|
||||
static void smb2srv_sesssetup_callback(struct gensec_update_request *greq, void *private_data)
|
||||
{
|
||||
struct smb2srv_sesssetup_callback_ctx *ctx = talloc_get_type(private_data,
|
||||
struct smb2srv_sesssetup_callback_ctx);
|
||||
struct smb2srv_request *req = ctx->req;
|
||||
union smb_sesssetup *io = ctx->io;
|
||||
struct smbsrv_session *smb_sess = ctx->smb_sess;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
NTSTATUS status;
|
||||
|
||||
status = gensec_update_recv(greq, req, &io->smb2.out.secblob);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
goto done;
|
||||
} else if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = gensec_session_info(smb_sess->gensec_ctx, &session_info);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Ensure this is marked as a 'real' vuid, not one
|
||||
* simply valid for the session setup leg */
|
||||
status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
}
|
||||
req->session = smb_sess;
|
||||
|
||||
done:
|
||||
io->smb2.out.uid = smb_sess->vuid;
|
||||
failed:
|
||||
req->status = auth_nt_status_squash(status);
|
||||
smb2srv_sesssetup_send(req, io);
|
||||
}
|
||||
|
||||
static void smb2srv_sesssetup_backend(struct smb2srv_request *req, union smb_sesssetup *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct smb2srv_sesssetup_callback_ctx *callback_ctx;
|
||||
struct smbsrv_session *smb_sess = NULL;
|
||||
uint64_t vuid;
|
||||
|
||||
io->smb2.out._pad = 0;
|
||||
io->smb2.out.uid = 0;
|
||||
io->smb2.out.secblob = data_blob(NULL, 0);
|
||||
|
||||
vuid = BVAL(req->in.hdr, SMB2_HDR_UID);
|
||||
|
||||
/*
|
||||
* only when we got '0' we should allocate a new session
|
||||
*/
|
||||
if (vuid == 0) {
|
||||
struct gensec_security *gensec_ctx;
|
||||
|
||||
status = gensec_server_start(req,
|
||||
req->smb_conn->connection->event.ctx,
|
||||
req->smb_conn->connection->msg_ctx,
|
||||
&gensec_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
gensec_set_credentials(gensec_ctx, req->smb_conn->negotiate.server_credentials);
|
||||
|
||||
gensec_set_target_service(gensec_ctx, "cifs");
|
||||
|
||||
gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
|
||||
|
||||
status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1, ("Failed to start GENSEC SPNEGO server code: %s\n", nt_errstr(status)));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* allocate a new session */
|
||||
smb_sess = smbsrv_session_new(req->smb_conn, gensec_ctx);
|
||||
if (!smb_sess) {
|
||||
status = NT_STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto failed;
|
||||
}
|
||||
status = smbsrv_smb2_init_tcons(smb_sess);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
}
|
||||
} else {
|
||||
/* lookup an existing session */
|
||||
smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid);
|
||||
}
|
||||
|
||||
if (!smb_sess) {
|
||||
status = NT_STATUS_USER_SESSION_DELETED;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (!smb_sess->gensec_ctx) {
|
||||
status = NT_STATUS_INTERNAL_ERROR;
|
||||
DEBUG(1, ("Internal ERROR: no gensec_ctx on session: %s\n", nt_errstr(status)));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
callback_ctx = talloc(req, struct smb2srv_sesssetup_callback_ctx);
|
||||
if (!callback_ctx) goto nomem;
|
||||
callback_ctx->req = req;
|
||||
callback_ctx->io = io;
|
||||
callback_ctx->smb_sess = smb_sess;
|
||||
|
||||
gensec_update_send(smb_sess->gensec_ctx, io->smb2.in.secblob,
|
||||
smb2srv_sesssetup_callback, callback_ctx);
|
||||
return;
|
||||
nomem:
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
failed:
|
||||
talloc_free(smb_sess);
|
||||
req->status = auth_nt_status_squash(status);
|
||||
smb2srv_sesssetup_send(req, io);
|
||||
}
|
||||
|
||||
void smb2srv_sesssetup_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_sesssetup *io;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x18, True);
|
||||
SMB2SRV_TALLOC_IO_PTR(io, union smb_sesssetup);
|
||||
|
||||
io->smb2.level = RAW_SESSSETUP_SMB2;
|
||||
io->smb2.in._pad = SVAL(req->in.body, 0x02);
|
||||
io->smb2.in.unknown2 = IVAL(req->in.body, 0x04);
|
||||
io->smb2.in.unknown3 = IVAL(req->in.body, 0x08);
|
||||
SMB2SRV_CHECK(smb2_pull_o16s16_blob(&req->in, io, req->in.body+0x0C, &io->smb2.in.secblob));
|
||||
io->smb2.in.unknown4 = BVAL(req->in.body, 0x10);
|
||||
|
||||
smb2srv_sesssetup_backend(req, io);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_logoff_backend(struct smb2srv_request *req)
|
||||
{
|
||||
/* TODO: call ntvfs backends to close file of this session */
|
||||
talloc_free(req->session);
|
||||
req->session = NULL;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void smb2srv_logoff_send(struct smb2srv_request *req)
|
||||
{
|
||||
if (NT_STATUS_IS_ERR(req->status)) {
|
||||
smb2srv_send_error(req, req->status);
|
||||
return;
|
||||
}
|
||||
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, False, 0));
|
||||
|
||||
SSVAL(req->out.body, 0x02, 0);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_logoff_recv(struct smb2srv_request *req)
|
||||
{
|
||||
uint16_t _pad;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x04, False);
|
||||
|
||||
_pad = SVAL(req->in.body, 0x02);
|
||||
|
||||
req->status = smb2srv_logoff_backend(req);
|
||||
|
||||
if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smb2srv_logoff_send(req);
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Unix SMB2 implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* the context for a single SMB2 request. This is passed to any request-context
|
||||
functions */
|
||||
struct smb2srv_request {
|
||||
/* the smbsrv_connection needs a list of requests queued for send */
|
||||
struct smb2srv_request *next, *prev;
|
||||
|
||||
/* the server_context contains all context specific to this SMB socket */
|
||||
struct smbsrv_connection *smb_conn;
|
||||
|
||||
/* conn is only set for operations that have a valid TID */
|
||||
struct smbsrv_tcon *tcon;
|
||||
|
||||
/* the session context is derived from the vuid */
|
||||
struct smbsrv_session *session;
|
||||
|
||||
#define SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY (1<<0)
|
||||
uint32_t control_flags;
|
||||
|
||||
/* the system time when the request arrived */
|
||||
struct timeval request_time;
|
||||
|
||||
/* a pointer to the per request union smb_* io structure */
|
||||
void *io_ptr;
|
||||
|
||||
/* the ntvfs_request */
|
||||
struct ntvfs_request *ntvfs;
|
||||
|
||||
/* Now the SMB2 specific stuff */
|
||||
|
||||
/* the status the backend returned */
|
||||
NTSTATUS status;
|
||||
|
||||
/* for matching request and reply */
|
||||
uint64_t seqnum;
|
||||
|
||||
/* the id that can be used to cancel the request */
|
||||
uint32_t pending_id;
|
||||
|
||||
struct smb2_request_buffer in;
|
||||
struct smb2_request_buffer out;
|
||||
};
|
||||
|
||||
struct smbsrv_request;
|
||||
|
||||
#include "smb_server/smb2/smb2_proto.h"
|
||||
|
||||
/* useful way of catching wct errors with file and line number */
|
||||
#define SMB2SRV_CHECK_BODY_SIZE(req, size, dynamic) do { \
|
||||
size_t is_size = req->in.body_size; \
|
||||
uint16_t field_size = SVAL(req->in.body, 0); \
|
||||
uint16_t want_size = ((dynamic)?(size)+1:(size)); \
|
||||
if (is_size < (size)) { \
|
||||
DEBUG(0,("%s: buffer too small 0x%x. Expected 0x%x\n", \
|
||||
__location__, (unsigned)is_size, (unsigned)want_size)); \
|
||||
smb2srv_send_error(req, NT_STATUS_FOOBAR); \
|
||||
return; \
|
||||
}\
|
||||
if (field_size != want_size) { \
|
||||
DEBUG(0,("%s: unexpected fixed body size 0x%x. Expected 0x%x\n", \
|
||||
__location__, (unsigned)field_size, (unsigned)want_size)); \
|
||||
smb2srv_send_error(req, NT_STATUS_FOOBAR); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SMB2SRV_CHECK(cmd) do {\
|
||||
NTSTATUS _status; \
|
||||
_status = cmd; \
|
||||
if (!NT_STATUS_IS_OK(_status)) { \
|
||||
smb2srv_send_error(req, _status); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* useful wrapper for talloc with NO_MEMORY reply */
|
||||
#define SMB2SRV_TALLOC_IO_PTR(ptr, type) do { \
|
||||
ptr = talloc(req, type); \
|
||||
if (!ptr) { \
|
||||
smb2srv_send_error(req, NT_STATUS_NO_MEMORY); \
|
||||
return; \
|
||||
} \
|
||||
req->io_ptr = ptr; \
|
||||
} while (0)
|
||||
|
||||
#define SMB2SRV_SETUP_NTVFS_REQUEST(send_fn, state) do { \
|
||||
req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req, \
|
||||
req->session->session_info,\
|
||||
0, \
|
||||
req->request_time, \
|
||||
req, send_fn, state); \
|
||||
if (!req->ntvfs) { \
|
||||
smb2srv_send_error(req, NT_STATUS_NO_MEMORY); \
|
||||
return; \
|
||||
} \
|
||||
(void)talloc_steal(req->tcon->ntvfs, req); \
|
||||
req->ntvfs->frontend_data.private_data = req; \
|
||||
} while (0)
|
||||
|
||||
#define SMB2SRV_CHECK_FILE_HANDLE(handle) do { \
|
||||
if (!handle) { \
|
||||
smb2srv_send_error(req, NT_STATUS_FILE_CLOSED); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
check if the backend wants to handle the request asynchronously.
|
||||
if it wants it handled synchronously then call the send function
|
||||
immediately
|
||||
*/
|
||||
#define SMB2SRV_CALL_NTVFS_BACKEND(cmd) do { \
|
||||
req->ntvfs->async_states->status = cmd; \
|
||||
if (req->ntvfs->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { \
|
||||
NTSTATUS _status; \
|
||||
_status = smb2srv_queue_pending(req); \
|
||||
if (!NT_STATUS_IS_OK(_status)) { \
|
||||
ntvfs_cancel(req->ntvfs); \
|
||||
} \
|
||||
} else { \
|
||||
req->ntvfs->async_states->send_fn(req->ntvfs); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* check req->ntvfs->async_states->status and if not OK then send an error reply */
|
||||
#define SMB2SRV_CHECK_ASYNC_STATUS_ERR_SIMPLE do { \
|
||||
req = talloc_get_type(ntvfs->async_states->private_data, struct smb2srv_request); \
|
||||
req->status = ntvfs->async_states->status; \
|
||||
if (NT_STATUS_IS_ERR(ntvfs->async_states->status)) { \
|
||||
smb2srv_send_error(req, ntvfs->async_states->status); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
#define SMB2SRV_CHECK_ASYNC_STATUS_ERR(ptr, type) do { \
|
||||
SMB2SRV_CHECK_ASYNC_STATUS_ERR_SIMPLE; \
|
||||
ptr = talloc_get_type(req->io_ptr, type); \
|
||||
} while (0)
|
||||
#define SMB2SRV_CHECK_ASYNC_STATUS_SIMPLE do { \
|
||||
req = talloc_get_type(ntvfs->async_states->private_data, struct smb2srv_request); \
|
||||
req->status = ntvfs->async_states->status; \
|
||||
if (!NT_STATUS_IS_OK(ntvfs->async_states->status)) { \
|
||||
smb2srv_send_error(req, ntvfs->async_states->status); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
#define SMB2SRV_CHECK_ASYNC_STATUS(ptr, type) do { \
|
||||
SMB2SRV_CHECK_ASYNC_STATUS_SIMPLE; \
|
||||
ptr = talloc_get_type(req->io_ptr, type); \
|
||||
} while (0)
|
||||
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
Unix SMB2 implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
/*
|
||||
send an oplock break request to a client
|
||||
*/
|
||||
static NTSTATUS smb2srv_send_oplock_break(void *p, struct ntvfs_handle *ntvfs, uint8_t level)
|
||||
{
|
||||
DEBUG(0,("TODO: we don't pass SMB2 oplock breaks to the Clients yet!\n"));
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
struct ntvfs_handle *smb2srv_pull_handle(struct smb2srv_request *req, const uint8_t *base, uint_t offset)
|
||||
{
|
||||
struct smbsrv_tcon *tcon;
|
||||
struct smbsrv_handle *handle;
|
||||
uint64_t hid;
|
||||
uint32_t tid;
|
||||
uint32_t pad;
|
||||
|
||||
hid = BVAL(base, offset);
|
||||
tid = IVAL(base, offset + 8);
|
||||
pad = IVAL(base, offset + 12);
|
||||
|
||||
if (pad != UINT32_MAX) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* if it's the wildcard handle, don't waste time to search it... */
|
||||
if (hid == UINT64_MAX && tid == UINT32_MAX) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* the handle can belong to a different tcon
|
||||
* as that TID in the SMB2 header says, but
|
||||
* the request should succeed nevertheless!
|
||||
*
|
||||
* because if this we put the 32 bit TID into the
|
||||
* 128 bit handle, so that we can extract the tcon from the
|
||||
* handle
|
||||
*/
|
||||
tcon = req->tcon;
|
||||
if (tid != req->tcon->tid) {
|
||||
tcon = smbsrv_smb2_tcon_find(req->session, tid, req->request_time);
|
||||
if (!tcon) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
handle = smbsrv_smb2_handle_find(tcon, hid, req->request_time);
|
||||
if (!handle) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* as the smb2srv_tcon is a child object of the smb2srv_session
|
||||
* the handle belongs to the correct session!
|
||||
*
|
||||
* Note: no check is needed here for SMB2
|
||||
*/
|
||||
|
||||
/*
|
||||
* as the handle may have overwritten the tcon
|
||||
* we need to set it on the request so that the
|
||||
* correct ntvfs context will be used for the ntvfs_*() request
|
||||
*/
|
||||
req->tcon = tcon;
|
||||
return handle->ntvfs;
|
||||
}
|
||||
|
||||
void smb2srv_push_handle(uint8_t *base, uint_t offset, struct ntvfs_handle *ntvfs)
|
||||
{
|
||||
struct smbsrv_handle *handle = talloc_get_type(ntvfs->frontend_data.private_data,
|
||||
struct smbsrv_handle);
|
||||
|
||||
/*
|
||||
* the handle is 128 bit on the wire
|
||||
*/
|
||||
SBVAL(base, offset, handle->hid);
|
||||
SIVAL(base, offset + 8, handle->tcon->tid);
|
||||
SIVAL(base, offset + 12,UINT32_MAX);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_handle_create_new(void *private_data, struct ntvfs_request *ntvfs, struct ntvfs_handle **_h)
|
||||
{
|
||||
struct smb2srv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
|
||||
struct smb2srv_request);
|
||||
struct smbsrv_handle *handle;
|
||||
struct ntvfs_handle *h;
|
||||
|
||||
handle = smbsrv_handle_new(req->session, req->tcon, req, req->request_time);
|
||||
if (!handle) return NT_STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
h = talloc_zero(handle, struct ntvfs_handle);
|
||||
if (!h) goto nomem;
|
||||
|
||||
/*
|
||||
* note: we don't set handle->ntvfs yet,
|
||||
* this will be done by smbsrv_handle_make_valid()
|
||||
* this makes sure the handle is invalid for clients
|
||||
* until the ntvfs subsystem has made it valid
|
||||
*/
|
||||
h->ctx = ntvfs->ctx;
|
||||
h->session_info = ntvfs->session_info;
|
||||
h->smbpid = ntvfs->smbpid;
|
||||
|
||||
h->frontend_data.private_data = handle;
|
||||
|
||||
*_h = h;
|
||||
return NT_STATUS_OK;
|
||||
nomem:
|
||||
talloc_free(handle);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_handle_make_valid(void *private_data, struct ntvfs_handle *h)
|
||||
{
|
||||
struct smbsrv_tcon *tcon = talloc_get_type(private_data, struct smbsrv_tcon);
|
||||
struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
|
||||
struct smbsrv_handle);
|
||||
/* this tells the frontend that the handle is valid */
|
||||
handle->ntvfs = h;
|
||||
/* this moves the smbsrv_request to the smbsrv_tcon memory context */
|
||||
talloc_steal(tcon, handle);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void smb2srv_handle_destroy(void *private_data, struct ntvfs_handle *h)
|
||||
{
|
||||
struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
|
||||
struct smbsrv_handle);
|
||||
talloc_free(handle);
|
||||
}
|
||||
|
||||
static struct ntvfs_handle *smb2srv_handle_search_by_wire_key(void *private_data, struct ntvfs_request *ntvfs, const DATA_BLOB *key)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static DATA_BLOB smb2srv_handle_get_wire_key(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
return data_blob(NULL, 0);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_tcon_backend(struct smb2srv_request *req, union smb_tcon *io)
|
||||
{
|
||||
struct smbsrv_tcon *tcon;
|
||||
NTSTATUS status;
|
||||
enum ntvfs_type type;
|
||||
uint16_t type_smb2;
|
||||
uint32_t unknown2;
|
||||
const char *service = io->smb2.in.path;
|
||||
struct share_config *scfg;
|
||||
const char *sharetype;
|
||||
|
||||
if (strncmp(service, "\\\\", 2) == 0) {
|
||||
const char *p = strchr(service+2, '\\');
|
||||
if (p) {
|
||||
service = p + 1;
|
||||
}
|
||||
}
|
||||
|
||||
status = share_get_config(req, req->smb_conn->share_context, service, &scfg);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("smb2srv_tcon_backend: couldn't find service %s\n", service));
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
|
||||
if (!socket_check_access(req->smb_conn->connection->socket,
|
||||
scfg->name,
|
||||
share_string_list_option(req, scfg, SHARE_HOSTS_ALLOW),
|
||||
share_string_list_option(req, scfg, SHARE_HOSTS_DENY))) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* work out what sort of connection this is */
|
||||
sharetype = share_string_option(scfg, SHARE_TYPE, "DISK");
|
||||
if (sharetype && strcmp(sharetype, "IPC") == 0) {
|
||||
type = NTVFS_IPC;
|
||||
type_smb2 = 0x0002;
|
||||
unknown2 = 0x00000030;
|
||||
} else if (sharetype && strcmp(sharetype, "PRINTER") == 0) {
|
||||
type = NTVFS_PRINT;
|
||||
type_smb2 = 0x0003;
|
||||
unknown2 = 0x00000000;
|
||||
} else {
|
||||
type = NTVFS_DISK;
|
||||
type_smb2 = 0x0001;
|
||||
unknown2 = 0x00000800;
|
||||
}
|
||||
|
||||
tcon = smbsrv_smb2_tcon_new(req->session, scfg->name);
|
||||
if (!tcon) {
|
||||
DEBUG(0,("smb2srv_tcon_backend: Couldn't find free connection.\n"));
|
||||
return NT_STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
req->tcon = tcon;
|
||||
|
||||
/* init ntvfs function pointers */
|
||||
status = ntvfs_init_connection(tcon, scfg, type,
|
||||
req->smb_conn->negotiate.protocol,
|
||||
req->smb_conn->connection->event.ctx,
|
||||
req->smb_conn->connection->msg_ctx,
|
||||
req->smb_conn->connection->server_id,
|
||||
&tcon->ntvfs);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("smb2srv_tcon_backend: ntvfs_init_connection failed for service %s\n",
|
||||
scfg->name));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = ntvfs_set_oplock_handler(tcon->ntvfs, smb2srv_send_oplock_break, tcon);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the oplock handler!\n"));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = ntvfs_set_addr_callbacks(tcon->ntvfs, smbsrv_get_my_addr, smbsrv_get_peer_addr, req->smb_conn);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the addr callbacks!\n"));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = ntvfs_set_handle_callbacks(tcon->ntvfs,
|
||||
smb2srv_handle_create_new,
|
||||
smb2srv_handle_make_valid,
|
||||
smb2srv_handle_destroy,
|
||||
smb2srv_handle_search_by_wire_key,
|
||||
smb2srv_handle_get_wire_key,
|
||||
tcon);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the handle callbacks!\n"));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req,
|
||||
req->session->session_info,
|
||||
0, /* TODO: fill in PID */
|
||||
req->request_time,
|
||||
req, NULL, 0);
|
||||
if (!req->ntvfs) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Invoke NTVFS connection hook */
|
||||
status = ntvfs_connect(req->ntvfs, scfg->name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("smb2srv_tcon_backend: NTVFS ntvfs_connect() failed!\n"));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
io->smb2.out.unknown1 = type_smb2; /* 1 - DISK, 2 - Print, 3 - IPC */
|
||||
io->smb2.out.unknown2 = unknown2;
|
||||
io->smb2.out.unknown3 = 0x00000000;
|
||||
io->smb2.out.access_mask= SEC_RIGHTS_FILE_ALL;
|
||||
|
||||
io->smb2.out.tid = tcon->tid;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
failed:
|
||||
req->tcon = NULL;
|
||||
talloc_free(tcon);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void smb2srv_tcon_send(struct smb2srv_request *req, union smb_tcon *io)
|
||||
{
|
||||
uint16_t unknown1;
|
||||
|
||||
if (!NT_STATUS_IS_OK(req->status)) {
|
||||
smb2srv_send_error(req, req->status);
|
||||
return;
|
||||
}
|
||||
if (io->smb2.out.unknown1 == 0x0002) {
|
||||
/* if it's an IPC share vista returns 0x0005 */
|
||||
unknown1 = 0x0005;
|
||||
} else {
|
||||
unknown1 = 0x0001;
|
||||
}
|
||||
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, False, 0));
|
||||
|
||||
SIVAL(req->out.hdr, SMB2_HDR_TID, io->smb2.out.tid);
|
||||
SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1,unknown1);
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->smb2.out.unknown1);
|
||||
SIVAL(req->out.body, 0x04, io->smb2.out.unknown2);
|
||||
SIVAL(req->out.body, 0x08, io->smb2.out.unknown3);
|
||||
SIVAL(req->out.body, 0x0C, io->smb2.out.access_mask);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_tcon_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_tcon *io;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x08, True);
|
||||
SMB2SRV_TALLOC_IO_PTR(io, union smb_tcon);
|
||||
|
||||
io->smb2.level = RAW_TCON_SMB2;
|
||||
io->smb2.in.unknown1 = SVAL(req->in.body, 0x02);
|
||||
SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, io, req->in.body+0x04, &io->smb2.in.path));
|
||||
|
||||
req->status = smb2srv_tcon_backend(req, io);
|
||||
|
||||
if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smb2srv_tcon_send(req, io);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_tdis_backend(struct smb2srv_request *req)
|
||||
{
|
||||
/* TODO: call ntvfs backends to close file of this tcon */
|
||||
talloc_free(req->tcon);
|
||||
req->tcon = NULL;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void smb2srv_tdis_send(struct smb2srv_request *req)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (NT_STATUS_IS_ERR(req->status)) {
|
||||
smb2srv_send_error(req, req->status);
|
||||
return;
|
||||
}
|
||||
|
||||
status = smb2srv_setup_reply(req, 0x04, False, 0);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
SSVAL(req->out.body, 0x02, 0);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_tdis_recv(struct smb2srv_request *req)
|
||||
{
|
||||
uint16_t _pad;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x04, False);
|
||||
|
||||
_pad = SVAL(req->in.body, 0x02);
|
||||
|
||||
req->status = smb2srv_tdis_backend(req);
|
||||
|
||||
if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smb2srv_tdis_send(req);
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
process incoming packets - main loop
|
||||
Copyright (C) Andrew Tridgell 2004-2005
|
||||
Copyright (C) Stefan Metzmacher 2004-2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smbd/service_task.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "smbd/service.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "lib/stream/packet.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "system/network.h"
|
||||
#include "lib/socket/netif.h"
|
||||
#include "param/share.h"
|
||||
|
||||
static NTSTATUS smbsrv_recv_generic_request(void *private, DATA_BLOB blob)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct smbsrv_connection *smb_conn = talloc_get_type(private, struct smbsrv_connection);
|
||||
uint32_t protocol_version;
|
||||
|
||||
/* see if its a special NBT packet */
|
||||
if (CVAL(blob.data,0) != 0) {
|
||||
status = smbsrv_init_smb_connection(smb_conn);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
packet_set_callback(smb_conn->packet, smbsrv_recv_smb_request);
|
||||
return smbsrv_recv_smb_request(smb_conn, blob);
|
||||
}
|
||||
|
||||
if (blob.length < (NBT_HDR_SIZE + MIN_SMB_SIZE)) {
|
||||
DEBUG(2,("Invalid SMB packet length count %ld\n", (long)blob.length));
|
||||
smbsrv_terminate_connection(smb_conn, "Invalid SMB packet");
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
protocol_version = IVAL(blob.data, NBT_HDR_SIZE);
|
||||
|
||||
switch (protocol_version) {
|
||||
case SMB_MAGIC:
|
||||
status = smbsrv_init_smb_connection(smb_conn);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
packet_set_callback(smb_conn->packet, smbsrv_recv_smb_request);
|
||||
return smbsrv_recv_smb_request(smb_conn, blob);
|
||||
case SMB2_MAGIC:
|
||||
if (lp_srv_maxprotocol() < PROTOCOL_SMB2) break;
|
||||
status = smbsrv_init_smb2_connection(smb_conn);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
packet_set_callback(smb_conn->packet, smbsrv_recv_smb2_request);
|
||||
return smbsrv_recv_smb2_request(smb_conn, blob);
|
||||
}
|
||||
|
||||
DEBUG(2,("Invalid SMB packet: protocol prefix: 0x%08X\n", protocol_version));
|
||||
smbsrv_terminate_connection(smb_conn, "NON-SMB packet");
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
close the socket and shutdown a server_context
|
||||
*/
|
||||
void smbsrv_terminate_connection(struct smbsrv_connection *smb_conn, const char *reason)
|
||||
{
|
||||
stream_terminate_connection(smb_conn->connection, reason);
|
||||
}
|
||||
|
||||
/*
|
||||
called when a SMB socket becomes readable
|
||||
*/
|
||||
static void smbsrv_recv(struct stream_connection *conn, uint16_t flags)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = talloc_get_type(conn->private,
|
||||
struct smbsrv_connection);
|
||||
|
||||
DEBUG(10,("smbsrv_recv\n"));
|
||||
|
||||
packet_recv(smb_conn->packet);
|
||||
|
||||
/* free up temporary memory */
|
||||
lp_talloc_free();
|
||||
}
|
||||
|
||||
/*
|
||||
called when a SMB socket becomes writable
|
||||
*/
|
||||
static void smbsrv_send(struct stream_connection *conn, uint16_t flags)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = talloc_get_type(conn->private,
|
||||
struct smbsrv_connection);
|
||||
packet_queue_run(smb_conn->packet);
|
||||
}
|
||||
|
||||
/*
|
||||
handle socket recv errors
|
||||
*/
|
||||
static void smbsrv_recv_error(void *private, NTSTATUS status)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = talloc_get_type(private, struct smbsrv_connection);
|
||||
|
||||
smbsrv_terminate_connection(smb_conn, nt_errstr(status));
|
||||
}
|
||||
|
||||
/*
|
||||
initialise a server_context from a open socket and register a event handler
|
||||
for reading from that socket
|
||||
*/
|
||||
static void smbsrv_accept(struct stream_connection *conn)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn;
|
||||
|
||||
DEBUG(5,("smbsrv_accept\n"));
|
||||
|
||||
smb_conn = talloc_zero(conn, struct smbsrv_connection);
|
||||
if (!smb_conn) {
|
||||
stream_terminate_connection(conn, "out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
smb_conn->packet = packet_init(smb_conn);
|
||||
if (!smb_conn->packet) {
|
||||
smbsrv_terminate_connection(smb_conn, "out of memory");
|
||||
return;
|
||||
}
|
||||
packet_set_private(smb_conn->packet, smb_conn);
|
||||
packet_set_socket(smb_conn->packet, conn->socket);
|
||||
packet_set_callback(smb_conn->packet, smbsrv_recv_generic_request);
|
||||
packet_set_full_request(smb_conn->packet, packet_full_request_nbt);
|
||||
packet_set_error_handler(smb_conn->packet, smbsrv_recv_error);
|
||||
packet_set_event_context(smb_conn->packet, conn->event.ctx);
|
||||
packet_set_fde(smb_conn->packet, conn->event.fde);
|
||||
packet_set_serialise(smb_conn->packet);
|
||||
|
||||
smb_conn->connection = conn;
|
||||
conn->private = smb_conn;
|
||||
|
||||
irpc_add_name(conn->msg_ctx, "smb_server");
|
||||
|
||||
smb_conn->statistics.connect_time = timeval_current();
|
||||
|
||||
smbsrv_management_init(smb_conn);
|
||||
|
||||
if (!NT_STATUS_IS_OK(share_get_context(smb_conn, &(smb_conn->share_context)))) {
|
||||
smbsrv_terminate_connection(smb_conn, "share_init failed!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct stream_server_ops smb_stream_ops = {
|
||||
.name = "smbsrv",
|
||||
.accept_connection = smbsrv_accept,
|
||||
.recv_handler = smbsrv_recv,
|
||||
.send_handler = smbsrv_send,
|
||||
};
|
||||
|
||||
/*
|
||||
setup a listening socket on all the SMB ports for a particular address
|
||||
*/
|
||||
static NTSTATUS smb_add_socket(struct event_context *event_context,
|
||||
const struct model_ops *model_ops,
|
||||
const char *address)
|
||||
{
|
||||
const char **ports = lp_smb_ports();
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
|
||||
for (i=0;ports[i];i++) {
|
||||
uint16_t port = atoi(ports[i]);
|
||||
if (port == 0) continue;
|
||||
status = stream_setup_socket(event_context, model_ops, &smb_stream_ops,
|
||||
"ipv4", address, &port, NULL);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
open the smb server sockets
|
||||
*/
|
||||
static void smbsrv_task_init(struct task_server *task)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
task_server_set_title(task, "task[smbsrv]");
|
||||
|
||||
if (lp_interfaces() && lp_bind_interfaces_only()) {
|
||||
int num_interfaces = iface_count();
|
||||
int i;
|
||||
|
||||
/* We have been given an interfaces line, and been
|
||||
told to only bind to those interfaces. Create a
|
||||
socket per interface and bind to only these.
|
||||
*/
|
||||
for(i = 0; i < num_interfaces; i++) {
|
||||
const char *address = iface_n_ip(i);
|
||||
status = smb_add_socket(task->event_ctx, task->model_ops, address);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
}
|
||||
} else {
|
||||
/* Just bind to lp_socket_address() (usually 0.0.0.0) */
|
||||
status = smb_add_socket(task->event_ctx, task->model_ops, lp_socket_address());
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
}
|
||||
|
||||
return;
|
||||
failed:
|
||||
task_server_terminate(task, "Failed to startup smb server task");
|
||||
}
|
||||
|
||||
/*
|
||||
called on startup of the smb server service It's job is to start
|
||||
listening on all configured sockets
|
||||
*/
|
||||
static NTSTATUS smbsrv_init(struct event_context *event_context,
|
||||
const struct model_ops *model_ops)
|
||||
{
|
||||
return task_server_startup(event_context, model_ops, smbsrv_task_init);
|
||||
}
|
||||
|
||||
/* called at smbd startup - register ourselves as a server service */
|
||||
NTSTATUS server_service_smb_init(void)
|
||||
{
|
||||
return register_server_service("smb", smbsrv_init);
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
Copyright (C) James J Myers 2003 <myersjj@samba.org>
|
||||
Copyright (C) Stefan Metzmacher 2004-2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "libcli/raw/request.h"
|
||||
#include "libcli/raw/interfaces.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
|
||||
/*
|
||||
this header declares the core context structures associated with smb
|
||||
sockets, tree connects, requests etc
|
||||
|
||||
the idea is that we will eventually get rid of all our global
|
||||
variables and instead store our state from structures hanging off
|
||||
these basic elements
|
||||
*/
|
||||
|
||||
struct smbsrv_tcons_context {
|
||||
/* an id tree used to allocate tids */
|
||||
struct idr_context *idtree_tid;
|
||||
|
||||
/* this is the limit of vuid values for this connection */
|
||||
uint32_t idtree_limit;
|
||||
|
||||
/* list of open tree connects */
|
||||
struct smbsrv_tcon *list;
|
||||
};
|
||||
|
||||
struct smbsrv_sessions_context {
|
||||
/* an id tree used to allocate vuids */
|
||||
/* this holds info on session vuids that are already
|
||||
* validated for this VC */
|
||||
struct idr_context *idtree_vuid;
|
||||
|
||||
/* this is the limit of vuid values for this connection */
|
||||
uint64_t idtree_limit;
|
||||
|
||||
/* also kept as a link list so it can be enumerated by
|
||||
the management code */
|
||||
struct smbsrv_session *list;
|
||||
};
|
||||
|
||||
struct smbsrv_handles_context {
|
||||
/* an id tree used to allocate file handles */
|
||||
struct idr_context *idtree_hid;
|
||||
|
||||
/* this is the limit of handle values for this context */
|
||||
uint64_t idtree_limit;
|
||||
|
||||
/* also kept as a link list so it can be enumerated by
|
||||
the management code */
|
||||
struct smbsrv_handle *list;
|
||||
};
|
||||
|
||||
/* the current user context for a request */
|
||||
struct smbsrv_session {
|
||||
struct smbsrv_session *prev, *next;
|
||||
|
||||
struct smbsrv_connection *smb_conn;
|
||||
|
||||
/*
|
||||
* in SMB2 tcons belong to just one session
|
||||
* and not to the whole connection
|
||||
*/
|
||||
struct smbsrv_tcons_context smb2_tcons;
|
||||
|
||||
/*
|
||||
* the open file handles for this session,
|
||||
* used for SMBexit, SMBulogoff and SMB2 SessionLogoff
|
||||
*/
|
||||
struct smbsrv_handle_session_item *handles;
|
||||
|
||||
/*
|
||||
* an index passed over the wire:
|
||||
* - 16 bit for smb
|
||||
* - 64 bit for smb2
|
||||
*/
|
||||
uint64_t vuid;
|
||||
|
||||
struct gensec_security *gensec_ctx;
|
||||
|
||||
struct auth_session_info *session_info;
|
||||
|
||||
/* some statistics for the management tools */
|
||||
struct {
|
||||
/* the time when the session setup started */
|
||||
struct timeval connect_time;
|
||||
/* the time when the session setup was finished */
|
||||
struct timeval auth_time;
|
||||
/* the time when the last request comes in */
|
||||
struct timeval last_request_time;
|
||||
} statistics;
|
||||
};
|
||||
|
||||
/* we need a forward declaration of the ntvfs_ops strucutre to prevent
|
||||
include recursion */
|
||||
struct ntvfs_context;
|
||||
|
||||
struct smbsrv_tcon {
|
||||
struct smbsrv_tcon *next, *prev;
|
||||
|
||||
/* the server context that this was created on */
|
||||
struct smbsrv_connection *smb_conn;
|
||||
|
||||
/* the open file handles on this tcon */
|
||||
struct smbsrv_handles_context handles;
|
||||
|
||||
/*
|
||||
* an index passed over the wire:
|
||||
* - 16 bit for smb
|
||||
* - 32 bit for smb2
|
||||
*/
|
||||
uint32_t tid; /* an index passed over the wire (the TID) */
|
||||
|
||||
/* the share name */
|
||||
const char *share_name;
|
||||
|
||||
/* the NTVFS context - see source/ntvfs/ for details */
|
||||
struct ntvfs_context *ntvfs;
|
||||
|
||||
/* some stuff to support share level security */
|
||||
struct {
|
||||
/* in share level security we need to fake up a session */
|
||||
struct smbsrv_session *session;
|
||||
} sec_share;
|
||||
|
||||
/* some stuff to support share level security */
|
||||
struct {
|
||||
/* in SMB2 a tcon always belongs to one session */
|
||||
struct smbsrv_session *session;
|
||||
} smb2;
|
||||
|
||||
/* some statistics for the management tools */
|
||||
struct {
|
||||
/* the time when the tree connect started */
|
||||
struct timeval connect_time;
|
||||
/* the time when the last request comes in */
|
||||
struct timeval last_request_time;
|
||||
} statistics;
|
||||
};
|
||||
|
||||
struct smbsrv_handle {
|
||||
struct smbsrv_handle *next, *prev;
|
||||
|
||||
/* the tcon the handle belongs to */
|
||||
struct smbsrv_tcon *tcon;
|
||||
|
||||
/* the session the handle was opened on */
|
||||
struct smbsrv_session *session;
|
||||
|
||||
/* the smbpid used on the open, used for SMBexit */
|
||||
uint16_t smbpid;
|
||||
|
||||
/*
|
||||
* this is for adding the handle into a linked list
|
||||
* on the smbsrv_session, we can't use *next,*prev
|
||||
* for this because they're used for the linked list on the
|
||||
* smbsrv_tcon
|
||||
*/
|
||||
struct smbsrv_handle_session_item {
|
||||
struct smbsrv_handle_session_item *prev, *next;
|
||||
struct smbsrv_handle *handle;
|
||||
} session_item;
|
||||
|
||||
/*
|
||||
* the value passed over the wire
|
||||
* - 16 bit for smb
|
||||
* - 64 bit for smb2
|
||||
* Note: for SMB2 handles are 128 bit
|
||||
* we'll fill the 2nd 64 bit with:
|
||||
* - 32 bit TID
|
||||
* - 32 bit 0xFFFFFFFF
|
||||
*/
|
||||
uint64_t hid;
|
||||
|
||||
/*
|
||||
* the ntvfs handle passed to the ntvfs backend
|
||||
*/
|
||||
struct ntvfs_handle *ntvfs;
|
||||
|
||||
/* some statistics for the management tools */
|
||||
struct {
|
||||
/* the time when the tree connect started */
|
||||
struct timeval open_time;
|
||||
/* the time when the last request comes in */
|
||||
struct timeval last_use_time;
|
||||
} statistics;
|
||||
};
|
||||
|
||||
/* a set of flags to control handling of request structures */
|
||||
#define SMBSRV_REQ_CONTROL_LARGE (1<<1) /* allow replies larger than max_xmit */
|
||||
|
||||
#define SMBSRV_REQ_DEFAULT_STR_FLAGS(req) (((req)->flags2 & FLAGS2_UNICODE_STRINGS) ? STR_UNICODE : STR_ASCII)
|
||||
|
||||
/* the context for a single SMB request. This is passed to any request-context
|
||||
functions */
|
||||
struct smbsrv_request {
|
||||
/* the smbsrv_connection needs a list of requests queued for send */
|
||||
struct smbsrv_request *next, *prev;
|
||||
|
||||
/* the server_context contains all context specific to this SMB socket */
|
||||
struct smbsrv_connection *smb_conn;
|
||||
|
||||
/* conn is only set for operations that have a valid TID */
|
||||
struct smbsrv_tcon *tcon;
|
||||
|
||||
/* the session context is derived from the vuid */
|
||||
struct smbsrv_session *session;
|
||||
|
||||
/* a set of flags to control usage of the request. See SMBSRV_REQ_CONTROL_* */
|
||||
uint32_t control_flags;
|
||||
|
||||
/* the system time when the request arrived */
|
||||
struct timeval request_time;
|
||||
|
||||
/* a pointer to the per request union smb_* io structure */
|
||||
void *io_ptr;
|
||||
|
||||
/* the ntvfs_request */
|
||||
struct ntvfs_request *ntvfs;
|
||||
|
||||
/* Now the SMB specific stuff */
|
||||
|
||||
/* the flags from the SMB request, in raw form (host byte order) */
|
||||
uint16_t flags2;
|
||||
|
||||
/* this can contain a fnum from an earlier part of a chained
|
||||
* message (such as an SMBOpenX), or -1 */
|
||||
int chained_fnum;
|
||||
|
||||
/* how far through the chain of SMB commands have we gone? */
|
||||
unsigned chain_count;
|
||||
|
||||
/* the sequence number for signing */
|
||||
uint64_t seq_num;
|
||||
|
||||
struct request_buffer in;
|
||||
struct request_buffer out;
|
||||
};
|
||||
|
||||
enum security_types {SEC_SHARE,SEC_USER};
|
||||
|
||||
/* smb server context structure. This should contain all the state
|
||||
* information associated with a SMB server connection
|
||||
*/
|
||||
struct smbsrv_connection {
|
||||
/* context that has been negotiated between the client and server */
|
||||
struct {
|
||||
/* have we already done the NBT session establishment? */
|
||||
BOOL done_nbt_session;
|
||||
|
||||
/* only one negprot per connection is allowed */
|
||||
BOOL done_negprot;
|
||||
|
||||
/* multiple session setups are allowed, but some parameters are
|
||||
ignored in any but the first */
|
||||
BOOL done_sesssetup;
|
||||
|
||||
/*
|
||||
* Size of data we can send to client. Set
|
||||
* by the client for all protocols above CORE.
|
||||
* Set by us for CORE protocol.
|
||||
*/
|
||||
unsigned max_send; /* init to BUFFER_SIZE */
|
||||
|
||||
/*
|
||||
* Size of the data we can receive. Set by us.
|
||||
* Can be modified by the max xmit parameter.
|
||||
*/
|
||||
unsigned max_recv; /* init to BUFFER_SIZE */
|
||||
|
||||
/* the negotiatiated protocol */
|
||||
enum protocol_types protocol;
|
||||
|
||||
/* authentication context for multi-part negprot */
|
||||
struct auth_context *auth_context;
|
||||
|
||||
/* reference to the kerberos keytab, or machine trust account */
|
||||
struct cli_credentials *server_credentials;
|
||||
|
||||
/* did we tell the client we support encrypted passwords? */
|
||||
BOOL encrypted_passwords;
|
||||
|
||||
/* Did we choose SPNEGO, or perhaps raw NTLMSSP, or even no extended security at all? */
|
||||
const char *oid;
|
||||
|
||||
/* client capabilities */
|
||||
uint32_t client_caps;
|
||||
|
||||
/* the timezone we sent to the client */
|
||||
int zone_offset;
|
||||
|
||||
/* NBT names only set when done_nbt_session is true */
|
||||
struct nbt_name *called_name;
|
||||
struct nbt_name *calling_name;
|
||||
} negotiate;
|
||||
|
||||
/* the context associated with open tree connects on a smb socket, not for SMB2 */
|
||||
struct smbsrv_tcons_context smb_tcons;
|
||||
|
||||
/* context associated with currently valid session setups */
|
||||
struct smbsrv_sessions_context sessions;
|
||||
|
||||
/*
|
||||
* the server_context holds a linked list of pending requests,
|
||||
* this is used for finding the request structures on ntcancel requests
|
||||
* For SMB only
|
||||
*/
|
||||
struct smbsrv_request *requests;
|
||||
|
||||
/*
|
||||
* the server_context holds a linked list of pending requests,
|
||||
* and an idtree for finding the request structures on SMB2 Cancel
|
||||
* For SMB2 only
|
||||
*/
|
||||
struct {
|
||||
/* an id tree used to allocate ids */
|
||||
struct idr_context *idtree_req;
|
||||
|
||||
/* this is the limit of pending requests values for this connection */
|
||||
uint32_t idtree_limit;
|
||||
|
||||
/* list of open tree connects */
|
||||
struct smb2srv_request *list;
|
||||
} requests2;
|
||||
|
||||
struct smb_signing_context signing;
|
||||
|
||||
struct stream_connection *connection;
|
||||
|
||||
/* this holds a partially received request */
|
||||
struct packet_context *packet;
|
||||
|
||||
/* a list of partially received transaction requests */
|
||||
struct smbsrv_trans_partial {
|
||||
struct smbsrv_trans_partial *next, *prev;
|
||||
struct smbsrv_request *req;
|
||||
struct smb_trans2 *trans;
|
||||
uint8_t command;
|
||||
} *trans_partial;
|
||||
|
||||
/* configuration parameters */
|
||||
struct {
|
||||
enum security_types security;
|
||||
BOOL nt_status_support;
|
||||
} config;
|
||||
|
||||
/* some statictics for the management tools */
|
||||
struct {
|
||||
/* the time when the client connects */
|
||||
struct timeval connect_time;
|
||||
/* the time when the last request comes in */
|
||||
struct timeval last_request_time;
|
||||
} statistics;
|
||||
|
||||
struct share_context *share_context;
|
||||
};
|
||||
|
||||
#include "smb_server/smb_server_proto.h"
|
||||
#include "smb_server/smb/smb_proto.h"
|
||||
|
||||
/* useful way of catching wct errors with file and line number */
|
||||
#define SMBSRV_CHECK_WCT(req, wcount) do { \
|
||||
if ((req)->in.wct != (wcount)) { \
|
||||
DEBUG(1,("Unexpected WCT %u at %s(%d) - expected %d\n", \
|
||||
(req)->in.wct, __FILE__, __LINE__, wcount)); \
|
||||
smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror)); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* useful wrapper for talloc with NO_MEMORY reply */
|
||||
#define SMBSRV_TALLOC_IO_PTR(ptr, type) do { \
|
||||
ptr = talloc(req, type); \
|
||||
if (!ptr) { \
|
||||
smbsrv_send_error(req, NT_STATUS_NO_MEMORY); \
|
||||
return; \
|
||||
} \
|
||||
req->io_ptr = ptr; \
|
||||
} while (0)
|
||||
|
||||
#define SMBSRV_SETUP_NTVFS_REQUEST(send_fn, state) do { \
|
||||
req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req, \
|
||||
req->session->session_info,\
|
||||
SVAL(req->in.hdr,HDR_PID), \
|
||||
req->request_time, \
|
||||
req, send_fn, state); \
|
||||
if (!req->ntvfs) { \
|
||||
smbsrv_send_error(req, NT_STATUS_NO_MEMORY); \
|
||||
return; \
|
||||
} \
|
||||
(void)talloc_steal(req->tcon->ntvfs, req); \
|
||||
req->ntvfs->frontend_data.private_data = req; \
|
||||
} while (0)
|
||||
|
||||
#define SMBSRV_CHECK_FILE_HANDLE(handle) do { \
|
||||
if (!handle) { \
|
||||
smbsrv_send_error(req, NT_STATUS_INVALID_HANDLE); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SMBSRV_CHECK_FILE_HANDLE_ERROR(handle, _status) do { \
|
||||
if (!handle) { \
|
||||
smbsrv_send_error(req, _status); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(handle) do { \
|
||||
if (!handle) { \
|
||||
return NT_STATUS_INVALID_HANDLE; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SMBSRV_CHECK(cmd) do {\
|
||||
NTSTATUS _status; \
|
||||
_status = cmd; \
|
||||
if (!NT_STATUS_IS_OK(_status)) { \
|
||||
smbsrv_send_error(req, _status); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
check if the backend wants to handle the request asynchronously.
|
||||
if it wants it handled synchronously then call the send function
|
||||
immediately
|
||||
*/
|
||||
#define SMBSRV_CALL_NTVFS_BACKEND(cmd) do { \
|
||||
req->ntvfs->async_states->status = cmd; \
|
||||
if (req->ntvfs->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { \
|
||||
DLIST_ADD_END(req->smb_conn->requests, req, struct smbsrv_request *); \
|
||||
} else { \
|
||||
req->ntvfs->async_states->send_fn(req->ntvfs); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* check req->ntvfs->async_states->status and if not OK then send an error reply */
|
||||
#define SMBSRV_CHECK_ASYNC_STATUS_ERR_SIMPLE do { \
|
||||
req = talloc_get_type(ntvfs->async_states->private_data, struct smbsrv_request); \
|
||||
if (NT_STATUS_IS_ERR(ntvfs->async_states->status)) { \
|
||||
smbsrv_send_error(req, ntvfs->async_states->status); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
#define SMBSRV_CHECK_ASYNC_STATUS_ERR(ptr, type) do { \
|
||||
SMBSRV_CHECK_ASYNC_STATUS_ERR_SIMPLE; \
|
||||
ptr = talloc_get_type(req->io_ptr, type); \
|
||||
} while (0)
|
||||
#define SMBSRV_CHECK_ASYNC_STATUS_SIMPLE do { \
|
||||
req = talloc_get_type(ntvfs->async_states->private_data, struct smbsrv_request); \
|
||||
if (!NT_STATUS_IS_OK(ntvfs->async_states->status)) { \
|
||||
smbsrv_send_error(req, ntvfs->async_states->status); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
#define SMBSRV_CHECK_ASYNC_STATUS(ptr, type) do { \
|
||||
SMBSRV_CHECK_ASYNC_STATUS_SIMPLE; \
|
||||
ptr = talloc_get_type(req->io_ptr, type); \
|
||||
} while (0)
|
||||
|
||||
/* zero out some reserved fields in a reply */
|
||||
#define SMBSRV_VWV_RESERVED(start, count) memset(req->out.vwv + VWV(start), 0, (count)*2)
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Manage smbsrv_tcon structures
|
||||
Copyright (C) Andrew Tridgell 1998
|
||||
Copyright (C) Alexander Bokovoy 2002
|
||||
Copyright (C) Stefan Metzmacher 2005-2006
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
struct socket_address *smbsrv_get_my_addr(void *p, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = talloc_get_type(p,
|
||||
struct smbsrv_connection);
|
||||
|
||||
return socket_get_my_addr(smb_conn->connection->socket, mem_ctx);
|
||||
}
|
||||
|
||||
struct socket_address *smbsrv_get_peer_addr(void *p, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = talloc_get_type(p,
|
||||
struct smbsrv_connection);
|
||||
|
||||
return socket_get_peer_addr(smb_conn->connection->socket, mem_ctx);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
init the tcon structures
|
||||
****************************************************************************/
|
||||
static NTSTATUS smbsrv_init_tcons(struct smbsrv_tcons_context *tcons_ctx, TALLOC_CTX *mem_ctx, uint32_t limit)
|
||||
{
|
||||
/*
|
||||
* the idr_* functions take 'int' as limit,
|
||||
* and only work with a max limit 0x00FFFFFF
|
||||
*/
|
||||
limit &= 0x00FFFFFF;
|
||||
|
||||
tcons_ctx->idtree_tid = idr_init(mem_ctx);
|
||||
NT_STATUS_HAVE_NO_MEMORY(tcons_ctx->idtree_tid);
|
||||
tcons_ctx->idtree_limit = limit;
|
||||
tcons_ctx->list = NULL;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS smbsrv_smb_init_tcons(struct smbsrv_connection *smb_conn)
|
||||
{
|
||||
return smbsrv_init_tcons(&smb_conn->smb_tcons, smb_conn, UINT16_MAX);
|
||||
}
|
||||
|
||||
NTSTATUS smbsrv_smb2_init_tcons(struct smbsrv_session *smb_sess)
|
||||
{
|
||||
return smbsrv_init_tcons(&smb_sess->smb2_tcons, smb_sess, UINT32_MAX);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
find a tcon given a tid for SMB
|
||||
****************************************************************************/
|
||||
static struct smbsrv_tcon *smbsrv_tcon_find(struct smbsrv_tcons_context *tcons_ctx,
|
||||
uint32_t tid, struct timeval request_time)
|
||||
{
|
||||
void *p;
|
||||
struct smbsrv_tcon *tcon;
|
||||
|
||||
if (tid == 0) return NULL;
|
||||
|
||||
if (tid > tcons_ctx->idtree_limit) return NULL;
|
||||
|
||||
p = idr_find(tcons_ctx->idtree_tid, tid);
|
||||
if (!p) return NULL;
|
||||
|
||||
tcon = talloc_get_type(p, struct smbsrv_tcon);
|
||||
if (!tcon) return NULL;
|
||||
|
||||
tcon->statistics.last_request_time = request_time;
|
||||
|
||||
return tcon;
|
||||
}
|
||||
|
||||
struct smbsrv_tcon *smbsrv_smb_tcon_find(struct smbsrv_connection *smb_conn,
|
||||
uint32_t tid, struct timeval request_time)
|
||||
{
|
||||
return smbsrv_tcon_find(&smb_conn->smb_tcons, tid, request_time);
|
||||
}
|
||||
|
||||
struct smbsrv_tcon *smbsrv_smb2_tcon_find(struct smbsrv_session *smb_sess,
|
||||
uint32_t tid, struct timeval request_time)
|
||||
{
|
||||
if (!smb_sess) return NULL;
|
||||
return smbsrv_tcon_find(&smb_sess->smb2_tcons, tid, request_time);
|
||||
}
|
||||
|
||||
/*
|
||||
destroy a connection structure
|
||||
*/
|
||||
static int smbsrv_tcon_destructor(struct smbsrv_tcon *tcon)
|
||||
{
|
||||
struct smbsrv_tcons_context *tcons_ctx;
|
||||
struct socket_address *client_addr;
|
||||
|
||||
client_addr = socket_get_peer_addr(tcon->smb_conn->connection->socket, tcon);
|
||||
DEBUG(3,("%s closed connection to service %s\n",
|
||||
client_addr ? client_addr->addr : "(unknown)",
|
||||
tcon->share_name));
|
||||
|
||||
/* tell the ntvfs backend that we are disconnecting */
|
||||
if (tcon->ntvfs) {
|
||||
ntvfs_disconnect(tcon->ntvfs);
|
||||
tcon->ntvfs = NULL;
|
||||
}
|
||||
|
||||
if (tcon->smb2.session) {
|
||||
tcons_ctx = &tcon->smb2.session->smb2_tcons;
|
||||
} else {
|
||||
tcons_ctx = &tcon->smb_conn->smb_tcons;
|
||||
}
|
||||
|
||||
idr_remove(tcons_ctx->idtree_tid, tcon->tid);
|
||||
DLIST_REMOVE(tcons_ctx->list, tcon);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
find first available connection slot
|
||||
*/
|
||||
static struct smbsrv_tcon *smbsrv_tcon_new(struct smbsrv_connection *smb_conn,
|
||||
struct smbsrv_session *smb_sess,
|
||||
const char *share_name)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
struct smbsrv_tcons_context *tcons_ctx;
|
||||
struct smbsrv_tcon *tcon;
|
||||
NTSTATUS status;
|
||||
int i;
|
||||
|
||||
if (smb_sess) {
|
||||
mem_ctx = smb_sess;
|
||||
tcons_ctx = &smb_sess->smb2_tcons;
|
||||
} else {
|
||||
mem_ctx = smb_conn;
|
||||
tcons_ctx = &smb_conn->smb_tcons;
|
||||
}
|
||||
|
||||
tcon = talloc_zero(mem_ctx, struct smbsrv_tcon);
|
||||
if (!tcon) return NULL;
|
||||
tcon->smb_conn = smb_conn;
|
||||
tcon->smb2.session = smb_sess;
|
||||
tcon->share_name = talloc_strdup(tcon, share_name);
|
||||
if (!tcon->share_name) goto failed;
|
||||
|
||||
/*
|
||||
* the use -1 here, because we don't want to give away the wildcard
|
||||
* fnum used in SMBflush
|
||||
*/
|
||||
status = smbsrv_init_handles(tcon, UINT16_MAX - 1);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1,("ERROR! failed to init handles: %s\n", nt_errstr(status)));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
i = idr_get_new_random(tcons_ctx->idtree_tid, tcon, tcons_ctx->idtree_limit);
|
||||
if (i == -1) {
|
||||
DEBUG(1,("ERROR! Out of connection structures\n"));
|
||||
goto failed;
|
||||
}
|
||||
tcon->tid = i;
|
||||
|
||||
DLIST_ADD(tcons_ctx->list, tcon);
|
||||
talloc_set_destructor(tcon, smbsrv_tcon_destructor);
|
||||
|
||||
/* now fill in some statistics */
|
||||
tcon->statistics.connect_time = timeval_current();
|
||||
|
||||
return tcon;
|
||||
|
||||
failed:
|
||||
talloc_free(tcon);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct smbsrv_tcon *smbsrv_smb_tcon_new(struct smbsrv_connection *smb_conn, const char *share_name)
|
||||
{
|
||||
return smbsrv_tcon_new(smb_conn, NULL, share_name);
|
||||
}
|
||||
|
||||
struct smbsrv_tcon *smbsrv_smb2_tcon_new(struct smbsrv_session *smb_sess, const char *share_name)
|
||||
{
|
||||
return smbsrv_tcon_new(smb_sess->smb_conn, smb_sess, share_name);
|
||||
}
|
||||
Reference in New Issue
Block a user