wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+747
View File
@@ -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;
}
+29
View File
@@ -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
+145
View File
@@ -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;
}
+136
View File
@@ -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);
}
+169
View File
@@ -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;
}
+21
View File
@@ -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
#######################
+521
View File
@@ -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 !");
}
+649
View File
@@ -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);
}
+671
View File
@@ -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
+760
View File
@@ -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));
}
+284
View File
@@ -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));
}
+206
View File
@@ -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;
}
+457
View File
@@ -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);
}
+183
View File
@@ -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;
}
+83
View File
@@ -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
+18
View File
@@ -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
#######################
+368
View File
@@ -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));
}
+371
View File
@@ -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);
}
+167
View File
@@ -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));
}
+72
View File
@@ -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);
}
+234
View File
@@ -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(&current_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;
}
+450
View File
@@ -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;
}
+237
View File
@@ -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);
}
+168
View File
@@ -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)
+388
View File
@@ -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);
}
+242
View File
@@ -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);
}
+484
View File
@@ -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)
+207
View File
@@ -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);
}