#include "smbprofile.h"
#include "../lib/util/bitmap.h"
#include "../librpc/gen_ndr/krb5pac.h"
+#include "lib/util/iov_buf.h"
#include "auth.h"
+#include "lib/crypto/sha512.h"
-#define OUTVEC_ALLOC_SIZE (SMB2_HDR_BODY + 9)
+static void smbd_smb2_connection_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data);
+static NTSTATUS smbd_smb2_flush_send_queue(struct smbXsrv_connection *xconn);
static const struct smbd_smb2_dispatch_table {
uint16_t opcode;
_OP(SMB2_OP_KEEPALIVE),
.as_root = true,
},{
- _OP(SMB2_OP_FIND),
+ _OP(SMB2_OP_QUERY_DIRECTORY),
.need_session = true,
.need_tcon = true,
.fileid_ofs = 0x08,
return true;
}
-static NTSTATUS smbd_initialize_smb2(struct smbd_server_connection *sconn)
+static NTSTATUS smbd_initialize_smb2(struct smbXsrv_connection *xconn)
{
- NTSTATUS status;
- int ret;
-
- TALLOC_FREE(sconn->smb1.fde);
-
- sconn->smb2.recv_queue = tevent_queue_create(sconn, "smb2 recv queue");
- if (sconn->smb2.recv_queue == NULL) {
+ TALLOC_FREE(xconn->transport.fde);
+
+ xconn->smb2.credits.seq_low = 0;
+ xconn->smb2.credits.seq_range = 1;
+ xconn->smb2.credits.granted = 1;
+ xconn->smb2.credits.max = lp_smb2_max_credits();
+ xconn->smb2.credits.bitmap = bitmap_talloc(xconn,
+ xconn->smb2.credits.max);
+ if (xconn->smb2.credits.bitmap == NULL) {
return NT_STATUS_NO_MEMORY;
}
- sconn->smb2.send_queue = tevent_queue_create(sconn, "smb2 send queue");
- if (sconn->smb2.send_queue == NULL) {
+ xconn->transport.fde = tevent_add_fd(xconn->ev_ctx,
+ xconn,
+ xconn->transport.sock,
+ TEVENT_FD_READ,
+ smbd_smb2_connection_handler,
+ xconn);
+ if (xconn->transport.fde == NULL) {
return NT_STATUS_NO_MEMORY;
}
- sconn->smb2.seqnum_low = 0;
- sconn->smb2.seqnum_range = 1;
- sconn->smb2.credits_granted = 1;
- sconn->smb2.max_credits = lp_smb2_max_credits();
- sconn->smb2.credits_bitmap = bitmap_talloc(sconn,
- sconn->smb2.max_credits);
- if (sconn->smb2.credits_bitmap == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
-
- ret = tstream_bsd_existing_socket(sconn, sconn->sock,
- &sconn->smb2.stream);
- if (ret == -1) {
- status = map_nt_error_from_unix(errno);
- return status;
- }
-
/* Ensure child is set to non-blocking mode */
- set_blocking(sconn->sock, false);
+ set_blocking(xconn->transport.sock, false);
return NT_STATUS_OK;
}
buf[3] = (len)&0xFF; \
} while (0)
-static void smb2_setup_nbt_length(struct iovec *vector, int count)
+static bool smb2_setup_nbt_length(struct iovec *vector, int count)
{
- size_t len = 0;
- int i;
+ ssize_t len;
+
+ if (count == 0) {
+ return false;
+ }
- for (i=1; i < count; i++) {
- len += vector[i].iov_len;
+ len = iov_buflen(vector+1, count-1);
+
+ if ((len == -1) || (len > 0xFFFFFF)) {
+ return false;
}
_smb2_setlen(vector[0].iov_base, len);
+ return true;
}
static int smbd_smb2_request_destructor(struct smbd_smb2_request *req)
{
- data_blob_clear_free(&req->first_key);
- data_blob_clear_free(&req->last_key);
+ if (req->first_key.length > 0) {
+ data_blob_clear_free(&req->first_key);
+ }
+ if (req->last_key.length > 0) {
+ data_blob_clear_free(&req->last_key);
+ }
return 0;
}
/* Enable this to find subtle valgrind errors. */
mem_pool = talloc_init("smbd_smb2_request_allocate");
#else
- mem_pool = talloc_pool(mem_ctx, 8192);
+ mem_pool = talloc_tos();
#endif
if (mem_pool == NULL) {
return NULL;
return NULL;
}
talloc_reparent(mem_pool, mem_ctx, req);
+#if 0
TALLOC_FREE(mem_pool);
+#endif
req->last_session_id = UINT64_MAX;
req->last_tid = UINT32_MAX;
return req;
}
-static NTSTATUS smbd_smb2_inbuf_parse_compound(struct smbXsrv_connection *conn,
+static NTSTATUS smbd_smb2_inbuf_parse_compound(struct smbXsrv_connection *xconn,
NTTIME now,
uint8_t *buf,
size_t buflen,
- TALLOC_CTX *mem_ctx,
+ struct smbd_smb2_request *req,
struct iovec **piov,
int *pnum_iov)
{
+ TALLOC_CTX *mem_ctx = req;
struct iovec *iov;
int num_iov = 1;
size_t taken = 0;
/*
* Note: index '0' is reserved for the transport protocol
*/
- iov = talloc_zero_array(mem_ctx, struct iovec, num_iov);
- if (iov == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
+ iov = req->in._vector;
while (taken < buflen) {
size_t len = buflen - taken;
uint8_t *body = NULL;
uint32_t dyn_size;
uint8_t *dyn = NULL;
- struct iovec *iov_tmp;
+ struct iovec *iov_alloc = NULL;
+
+ if (iov != req->in._vector) {
+ iov_alloc = iov;
+ }
if (verified_buflen > taken) {
len = verified_buflen - taken;
NTSTATUS status;
size_t enc_len;
- if (conn->protocol < PROTOCOL_SMB2_24) {
+ if (xconn->protocol < PROTOCOL_SMB2_24) {
DEBUG(10, ("Got SMB2_TRANSFORM header, "
"but dialect[0x%04X] is used\n",
- conn->smb2.server.dialect));
+ xconn->smb2.server.dialect));
goto inval;
}
- if (!(conn->smb2.server.capabilities & SMB2_CAP_ENCRYPTION)) {
+ if (xconn->smb2.server.cipher == 0) {
DEBUG(10, ("Got SMB2_TRANSFORM header, "
"but not negotiated "
"client[0x%08X] server[0x%08X]\n",
- conn->smb2.client.capabilities,
- conn->smb2.server.capabilities));
+ xconn->smb2.client.capabilities,
+ xconn->smb2.server.capabilities));
goto inval;
}
goto inval;
}
- status = smb2srv_session_lookup(conn, uid, now, &s);
+ status = smb2srv_session_lookup_conn(xconn, uid, now,
+ &s);
if (s == NULL) {
DEBUG(1, ("invalid session[%llu] in "
"SMB2_TRANSFORM header\n",
(unsigned long long)uid));
- TALLOC_FREE(iov);
+ TALLOC_FREE(iov_alloc);
return NT_STATUS_USER_SESSION_DELETED;
}
tf_iov[1].iov_len = enc_len;
status = smb2_signing_decrypt_pdu(s->global->decryption_key,
- conn->protocol,
+ xconn->smb2.server.cipher,
tf_iov, 2);
if (!NT_STATUS_IS_OK(status)) {
- TALLOC_FREE(iov);
+ TALLOC_FREE(iov_alloc);
return status;
}
dyn = body + body_size;
dyn_size = full_size - (SMB2_HDR_BODY + body_size);
- iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
- num_iov + SMBD_SMB2_NUM_IOV_PER_REQ);
- if (iov_tmp == NULL) {
- TALLOC_FREE(iov);
- return NT_STATUS_NO_MEMORY;
+ if (num_iov >= ARRAY_SIZE(req->in._vector)) {
+ struct iovec *iov_tmp = NULL;
+
+ iov_tmp = talloc_realloc(mem_ctx, iov_alloc,
+ struct iovec,
+ num_iov +
+ SMBD_SMB2_NUM_IOV_PER_REQ);
+ if (iov_tmp == NULL) {
+ TALLOC_FREE(iov_alloc);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (iov_alloc == NULL) {
+ memcpy(iov_tmp,
+ req->in._vector,
+ sizeof(req->in._vector));
+ }
+
+ iov = iov_tmp;
}
- iov = iov_tmp;
cur = &iov[num_iov];
num_iov += SMBD_SMB2_NUM_IOV_PER_REQ;
return NT_STATUS_OK;
inval:
- TALLOC_FREE(iov);
+ if (iov != req->in._vector) {
+ TALLOC_FREE(iov);
+ }
return NT_STATUS_INVALID_PARAMETER;
}
-static NTSTATUS smbd_smb2_request_create(struct smbd_server_connection *sconn,
- uint8_t *inbuf, size_t size,
+static NTSTATUS smbd_smb2_request_create(struct smbXsrv_connection *xconn,
+ const uint8_t *_inpdu, size_t size,
struct smbd_smb2_request **_req)
{
+ struct smbd_server_connection *sconn = xconn->client->sconn;
struct smbd_smb2_request *req;
uint32_t protocol_version;
+ uint8_t *inpdu = NULL;
const uint8_t *inhdr = NULL;
uint16_t cmd;
uint32_t next_command_ofs;
NTSTATUS status;
NTTIME now;
- if (size < (4 + SMB2_HDR_BODY + 2)) {
+ if (size < (SMB2_HDR_BODY + 2)) {
DEBUG(0,("Invalid SMB2 packet length count %ld\n", (long)size));
return NT_STATUS_INVALID_PARAMETER;
}
- inhdr = inbuf + 4;
+ inhdr = _inpdu;
protocol_version = IVAL(inhdr, SMB2_HDR_PROTOCOL_ID);
if (protocol_version != SMB2_MAGIC) {
return NT_STATUS_INVALID_PARAMETER;
}
- req = smbd_smb2_request_allocate(sconn);
+ req = smbd_smb2_request_allocate(xconn);
if (req == NULL) {
return NT_STATUS_NO_MEMORY;
}
req->sconn = sconn;
+ req->xconn = xconn;
- talloc_steal(req, inbuf);
+ inpdu = talloc_memdup(req, _inpdu, size);
+ if (inpdu == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
req->request_time = timeval_current();
now = timeval_to_nttime(&req->request_time);
- status = smbd_smb2_inbuf_parse_compound(sconn->conn,
+ status = smbd_smb2_inbuf_parse_compound(xconn,
now,
- inbuf + NBT_HDR_SIZE,
- size - NBT_HDR_SIZE,
+ inpdu,
+ size,
req, &req->in.vector,
&req->in.vector_count);
if (!NT_STATUS_IS_OK(status)) {
return NT_STATUS_OK;
}
-static bool smb2_validate_sequence_number(struct smbd_server_connection *sconn,
+static bool smb2_validate_sequence_number(struct smbXsrv_connection *xconn,
uint64_t message_id, uint64_t seq_id)
{
- struct bitmap *credits_bm = sconn->smb2.credits_bitmap;
+ struct bitmap *credits_bm = xconn->smb2.credits.bitmap;
unsigned int offset;
+ uint64_t seq_tmp;
- if (seq_id < sconn->smb2.seqnum_low) {
+ seq_tmp = xconn->smb2.credits.seq_low;
+ if (seq_id < seq_tmp) {
DEBUG(0,("smb2_validate_sequence_number: bad message_id "
"%llu (sequence id %llu) "
"(granted = %u, low = %llu, range = %u)\n",
(unsigned long long)message_id,
(unsigned long long)seq_id,
- (unsigned int)sconn->smb2.credits_granted,
- (unsigned long long)sconn->smb2.seqnum_low,
- (unsigned int)sconn->smb2.seqnum_range));
+ (unsigned int)xconn->smb2.credits.granted,
+ (unsigned long long)xconn->smb2.credits.seq_low,
+ (unsigned int)xconn->smb2.credits.seq_range));
return false;
}
- if (seq_id >= sconn->smb2.seqnum_low + sconn->smb2.seqnum_range) {
+ seq_tmp += xconn->smb2.credits.seq_range;
+ if (seq_id >= seq_tmp) {
DEBUG(0,("smb2_validate_sequence_number: bad message_id "
"%llu (sequence id %llu) "
"(granted = %u, low = %llu, range = %u)\n",
(unsigned long long)message_id,
(unsigned long long)seq_id,
- (unsigned int)sconn->smb2.credits_granted,
- (unsigned long long)sconn->smb2.seqnum_low,
- (unsigned int)sconn->smb2.seqnum_range));
+ (unsigned int)xconn->smb2.credits.granted,
+ (unsigned long long)xconn->smb2.credits.seq_low,
+ (unsigned int)xconn->smb2.credits.seq_range));
return false;
}
- offset = seq_id % sconn->smb2.max_credits;
+ offset = seq_id % xconn->smb2.credits.max;
if (bitmap_query(credits_bm, offset)) {
DEBUG(0,("smb2_validate_sequence_number: duplicate message_id "
"(bm offset %u)\n",
(unsigned long long)message_id,
(unsigned long long)seq_id,
- (unsigned int)sconn->smb2.credits_granted,
- (unsigned long long)sconn->smb2.seqnum_low,
- (unsigned int)sconn->smb2.seqnum_range,
+ (unsigned int)xconn->smb2.credits.granted,
+ (unsigned long long)xconn->smb2.credits.seq_low,
+ (unsigned int)xconn->smb2.credits.seq_range,
offset));
return false;
}
/* Mark the message_ids as seen in the bitmap. */
bitmap_set(credits_bm, offset);
- if (seq_id != sconn->smb2.seqnum_low) {
+ if (seq_id != xconn->smb2.credits.seq_low) {
return true;
}
while (bitmap_query(credits_bm, offset)) {
DEBUG(10,("smb2_validate_sequence_number: clearing "
"id %llu (position %u) from bitmap\n",
- (unsigned long long)(sconn->smb2.seqnum_low),
+ (unsigned long long)(xconn->smb2.credits.seq_low),
offset));
bitmap_clear(credits_bm, offset);
- sconn->smb2.seqnum_low += 1;
- sconn->smb2.seqnum_range -= 1;
- offset = sconn->smb2.seqnum_low % sconn->smb2.max_credits;
+ xconn->smb2.credits.seq_low += 1;
+ xconn->smb2.credits.seq_range -= 1;
+ offset = xconn->smb2.credits.seq_low % xconn->smb2.credits.max;
}
return true;
}
-static bool smb2_validate_message_id(struct smbd_server_connection *sconn,
- const uint8_t *inhdr)
+static bool smb2_validate_message_id(struct smbXsrv_connection *xconn,
+ const uint8_t *inhdr)
{
uint64_t message_id = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
uint16_t opcode = SVAL(inhdr, SMB2_HDR_OPCODE);
return true;
}
- if (sconn->smb2.supports_multicredit) {
+ if (xconn->smb2.credits.multicredit) {
credit_charge = SVAL(inhdr, SMB2_HDR_CREDIT_CHARGE);
credit_charge = MAX(credit_charge, 1);
}
"seqnum low/range: %llu/%llu\n",
(unsigned long long) message_id,
(unsigned long long) credit_charge,
- (unsigned long long) sconn->smb2.credits_granted,
- (unsigned long long) sconn->smb2.seqnum_low,
- (unsigned long long) sconn->smb2.seqnum_range));
+ (unsigned long long) xconn->smb2.credits.granted,
+ (unsigned long long) xconn->smb2.credits.seq_low,
+ (unsigned long long) xconn->smb2.credits.seq_range));
- if (sconn->smb2.credits_granted < credit_charge) {
+ if (xconn->smb2.credits.granted < credit_charge) {
DEBUG(0, ("smb2_validate_message_id: client used more "
"credits than granted, mid %llu, charge %llu, "
"credits_granted %llu, "
"seqnum low/range: %llu/%llu\n",
(unsigned long long) message_id,
(unsigned long long) credit_charge,
- (unsigned long long) sconn->smb2.credits_granted,
- (unsigned long long) sconn->smb2.seqnum_low,
- (unsigned long long) sconn->smb2.seqnum_range));
+ (unsigned long long) xconn->smb2.credits.granted,
+ (unsigned long long) xconn->smb2.credits.seq_low,
+ (unsigned long long) xconn->smb2.credits.seq_range));
return false;
}
credit_charge,
(unsigned long long)id));
- ok = smb2_validate_sequence_number(sconn, message_id, id);
+ ok = smb2_validate_sequence_number(xconn, message_id, id);
if (!ok) {
return false;
}
}
/* substract used credits */
- sconn->smb2.credits_granted -= credit_charge;
+ xconn->smb2.credits.granted -= credit_charge;
return true;
}
return NT_STATUS_INVALID_PARAMETER;
}
- if (!smb2_validate_message_id(req->sconn, inhdr)) {
+ if (!smb2_validate_message_id(req->xconn, inhdr)) {
return NT_STATUS_INVALID_PARAMETER;
}
}
return NT_STATUS_OK;
}
-static void smb2_set_operation_credit(struct smbd_server_connection *sconn,
- const struct iovec *in_vector,
- struct iovec *out_vector)
+static void smb2_set_operation_credit(struct smbXsrv_connection *xconn,
+ const struct iovec *in_vector,
+ struct iovec *out_vector)
{
const uint8_t *inhdr = (const uint8_t *)in_vector->iov_base;
uint8_t *outhdr = (uint8_t *)out_vector->iov_base;
* Which means we would grant more credits
* for client which use multi credit requests.
*/
- current_max_credits = sconn->smb2.max_credits / 16;
+ current_max_credits = xconn->smb2.credits.max / 16;
current_max_credits = MAX(current_max_credits, 1);
- if (sconn->smb2.supports_multicredit) {
+ if (xconn->smb2.credits.multicredit) {
credit_charge = SVAL(inhdr, SMB2_HDR_CREDIT_CHARGE);
credit_charge = MAX(credit_charge, 1);
}
cmd = SVAL(inhdr, SMB2_HDR_OPCODE);
credits_requested = SVAL(inhdr, SMB2_HDR_CREDIT);
+ credits_requested = MAX(credits_requested, 1);
out_flags = IVAL(outhdr, SMB2_HDR_FLAGS);
out_status = NT_STATUS(IVAL(outhdr, SMB2_HDR_STATUS));
- SMB_ASSERT(sconn->smb2.max_credits >= sconn->smb2.credits_granted);
+ SMB_ASSERT(xconn->smb2.credits.max >= xconn->smb2.credits.granted);
- if (sconn->smb2.max_credits < credit_charge) {
- smbd_server_connection_terminate(sconn,
+ if (xconn->smb2.credits.max < credit_charge) {
+ smbd_server_connection_terminate(xconn,
"client error: credit charge > max credits\n");
return;
}
* credits on the final response.
*/
credits_granted = 0;
- } else if (credits_requested > 0) {
+ } else {
+ uint16_t additional_possible =
+ xconn->smb2.credits.max - credit_charge;
uint16_t additional_max = 0;
uint16_t additional_credits = credits_requested - 1;
break;
}
+ additional_max = MIN(additional_max, additional_possible);
additional_credits = MIN(additional_credits, additional_max);
credits_granted = credit_charge + additional_credits;
- } else if (sconn->smb2.credits_granted == 0) {
- /*
- * Make sure the client has always at least one credit
- */
- credits_granted = 1;
}
/*
* this makes sure the client consumes the lowest sequence
* number, before we can grant additional credits.
*/
- credits_possible = UINT64_MAX - sconn->smb2.seqnum_low;
+ credits_possible = UINT64_MAX - xconn->smb2.credits.seq_low;
if (credits_possible > 0) {
/* remove UINT64_MAX */
credits_possible -= 1;
}
credits_possible = MIN(credits_possible, current_max_credits);
- credits_possible -= sconn->smb2.seqnum_range;
+ credits_possible -= xconn->smb2.credits.seq_range;
credits_granted = MIN(credits_granted, credits_possible);
SSVAL(outhdr, SMB2_HDR_CREDIT, credits_granted);
- sconn->smb2.credits_granted += credits_granted;
- sconn->smb2.seqnum_range += credits_granted;
+ xconn->smb2.credits.granted += credits_granted;
+ xconn->smb2.credits.seq_range += credits_granted;
DEBUG(10,("smb2_set_operation_credit: requested %u, charge %u, "
"granted %u, current possible/max %u/%u, "
(unsigned int)credits_granted,
(unsigned int)credits_possible,
(unsigned int)current_max_credits,
- (unsigned int)sconn->smb2.credits_granted,
- (unsigned int)sconn->smb2.max_credits,
- (unsigned long long)sconn->smb2.seqnum_low,
- (unsigned int)sconn->smb2.seqnum_range));
+ (unsigned int)xconn->smb2.credits.granted,
+ (unsigned int)xconn->smb2.credits.max,
+ (unsigned long long)xconn->smb2.credits.seq_low,
+ (unsigned int)xconn->smb2.credits.seq_range));
}
static void smb2_calculate_credits(const struct smbd_smb2_request *inreq,
struct iovec *outhdr_v = SMBD_SMB2_IDX_HDR_IOV(outreq,out,idx);
uint8_t *outhdr = (uint8_t *)outhdr_v->iov_base;
- smb2_set_operation_credit(outreq->sconn, inhdr_v, outhdr_v);
+ smb2_set_operation_credit(outreq->xconn, inhdr_v, outhdr_v);
/* To match Windows, count up what we
just granted. */
}
}
+DATA_BLOB smbd_smb2_generate_outbody(struct smbd_smb2_request *req, size_t size)
+{
+ if (req->current_idx <= 1) {
+ if (size <= sizeof(req->out._body)) {
+ return data_blob_const(req->out._body, size);
+ }
+ }
+
+ return data_blob_talloc(req, NULL, size);
+}
+
static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
{
+ struct smbXsrv_connection *xconn = req->xconn;
+ TALLOC_CTX *mem_ctx;
struct iovec *vector;
int count;
int idx;
+ bool ok;
count = req->in.vector_count;
- vector = talloc_zero_array(req, struct iovec, count);
- if (vector == NULL) {
- return NT_STATUS_NO_MEMORY;
+ if (count <= ARRAY_SIZE(req->out._vector)) {
+ mem_ctx = req;
+ vector = req->out._vector;
+ } else {
+ vector = talloc_zero_array(req, struct iovec, count);
+ if (vector == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ mem_ctx = vector;
}
vector[0].iov_base = req->out.nbt_hdr;
next_command_ofs = SMB2_HDR_BODY + 9;
}
- outhdr = talloc_zero_array(vector, uint8_t,
- OUTVEC_ALLOC_SIZE);
- if (outhdr == NULL) {
- return NT_STATUS_NO_MEMORY;
+ if (idx == 1) {
+ outhdr = req->out._hdr;
+ } else {
+ outhdr = talloc_zero_array(mem_ctx, uint8_t,
+ OUTVEC_ALLOC_SIZE);
+ if (outhdr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
}
outbody = outhdr + SMB2_HDR_BODY;
req->out.vector_count = count;
/* setup the length of the NBT packet */
- smb2_setup_nbt_length(req->out.vector, req->out.vector_count);
+ ok = smb2_setup_nbt_length(req->out.vector, req->out.vector_count);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
- DLIST_ADD_END(req->sconn->smb2.requests, req, struct smbd_smb2_request *);
+ DLIST_ADD_END(xconn->smb2.requests, req, struct smbd_smb2_request *);
return NT_STATUS_OK;
}
-void smbd_server_connection_terminate_ex(struct smbd_server_connection *sconn,
+void smbd_server_connection_terminate_ex(struct smbXsrv_connection *xconn,
const char *reason,
const char *location)
{
struct iovec *outvec = NULL;
int count = req->out.vector_count;
int i;
+ bool ok;
- newreq = smbd_smb2_request_allocate(req->sconn);
+ newreq = smbd_smb2_request_allocate(req->xconn);
if (!newreq) {
return NULL;
}
newreq->sconn = req->sconn;
+ newreq->xconn = req->xconn;
newreq->session = req->session;
newreq->do_encryption = req->do_encryption;
newreq->do_signing = req->do_signing;
return NULL;
}
- smb2_setup_nbt_length(newreq->out.vector,
- newreq->out.vector_count);
+ ok = smb2_setup_nbt_length(newreq->out.vector,
+ newreq->out.vector_count);
+ if (!ok) {
+ TALLOC_FREE(newreq);
+ return NULL;
+ }
return newreq;
}
-static void smbd_smb2_request_writev_done(struct tevent_req *subreq);
-
static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request *req)
{
- struct smbXsrv_connection *conn = req->sconn->conn;
+ struct smbXsrv_connection *xconn = req->xconn;
int first_idx = 1;
struct iovec *firsttf = NULL;
struct iovec *outhdr_v = NULL;
uint8_t *outhdr = NULL;
struct smbd_smb2_request *nreq = NULL;
NTSTATUS status;
+ bool ok;
/* Create a new smb2 request we'll use
for the interim return. */
ones we'll be using for the async reply. */
nreq->out.vector_count -= SMBD_SMB2_NUM_IOV_PER_REQ;
- smb2_setup_nbt_length(nreq->out.vector,
- nreq->out.vector_count);
+ ok = smb2_setup_nbt_length(nreq->out.vector,
+ nreq->out.vector_count);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
/* Step back to the previous reply. */
nreq->current_idx -= SMBD_SMB2_NUM_IOV_PER_REQ;
*/
if (firsttf->iov_len == SMB2_TF_HDR_SIZE) {
status = smb2_signing_encrypt_pdu(req->first_key,
- conn->protocol,
+ xconn->smb2.server.cipher,
firsttf,
nreq->out.vector_count - first_idx);
if (!NT_STATUS_IS_OK(status)) {
}
} else if (req->last_key.length > 0) {
status = smb2_signing_sign_pdu(req->last_key,
- conn->protocol,
+ xconn->protocol,
outhdr_v,
SMBD_SMB2_NUM_IOV_PER_REQ - 1);
if (!NT_STATUS_IS_OK(status)) {
}
}
- nreq->subreq = tstream_writev_queue_send(nreq,
- nreq->sconn->ev_ctx,
- nreq->sconn->smb2.stream,
- nreq->sconn->smb2.send_queue,
- nreq->out.vector,
- nreq->out.vector_count);
+ nreq->queue_entry.mem_ctx = nreq;
+ nreq->queue_entry.vector = nreq->out.vector;
+ nreq->queue_entry.count = nreq->out.vector_count;
+ DLIST_ADD_END(xconn->smb2.send_queue, &nreq->queue_entry, NULL);
+ xconn->smb2.send_queue_len++;
- if (nreq->subreq == NULL) {
- return NT_STATUS_NO_MEMORY;
+ status = smbd_smb2_flush_send_queue(xconn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
- tevent_req_set_callback(nreq->subreq,
- smbd_smb2_request_writev_done,
- nreq);
-
return NT_STATUS_OK;
}
struct smbd_smb2_request_pending_state {
- struct smbd_server_connection *sconn;
+ struct smbd_smb2_send_queue queue_entry;
uint8_t buf[NBT_HDR_SIZE + SMB2_TF_HDR_SIZE + SMB2_HDR_BODY + 0x08 + 1];
struct iovec vector[1 + SMBD_SMB2_NUM_IOV_PER_REQ];
};
-static void smbd_smb2_request_pending_writev_done(struct tevent_req *subreq)
-{
- struct smbd_smb2_request_pending_state *state =
- tevent_req_callback_data(subreq,
- struct smbd_smb2_request_pending_state);
- struct smbd_server_connection *sconn = state->sconn;
- int ret;
- int sys_errno;
-
- ret = tstream_writev_queue_recv(subreq, &sys_errno);
- TALLOC_FREE(subreq);
- if (ret == -1) {
- NTSTATUS status = map_nt_error_from_unix(sys_errno);
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
- }
-
- TALLOC_FREE(state);
-}
-
static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval current_time,
uint32_t flags;
if (!tevent_req_is_in_progress(subreq)) {
+ /*
+ * This is a performance optimization,
+ * it avoids one tevent_loop iteration,
+ * which means we avoid one
+ * talloc_stackframe_pool/talloc_free pair.
+ */
+ tevent_req_notify_callback(subreq);
return NT_STATUS_OK;
}
if (req->in.vector_count > req->current_idx + SMBD_SMB2_NUM_IOV_PER_REQ) {
/*
- * We're trying to go async in a compound
- * request chain.
- * This is only allowed for opens that
- * cause an oplock break, otherwise it
- * is not allowed. See [MS-SMB2].pdf
- * note <194> on Section 3.3.5.2.7.
+ * We're trying to go async in a compound request
+ * chain. This is only allowed for opens that cause an
+ * oplock break or for the last operation in the
+ * chain, otherwise it is not allowed. See
+ * [MS-SMB2].pdf note <206> on Section 3.3.5.2.7.
*/
const uint8_t *inhdr = SMBD_SMB2_IN_HDR_PTR(req);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
- data_blob_clear_free(&req->first_key);
+ if (req->first_key.length > 0) {
+ data_blob_clear_free(&req->first_key);
+ }
req->current_idx = 1;
SIVAL(outhdr, SMB2_HDR_FLAGS, flags);
}
}
- data_blob_clear_free(&req->last_key);
+ if (req->last_key.length > 0) {
+ data_blob_clear_free(&req->last_key);
+ }
defer_endtime = timeval_current_ofs_usec(defer_time);
req->async_te = tevent_add_timer(req->sconn->ev_ctx,
return NT_STATUS_OK;
}
+static DATA_BLOB smbd_smb2_signing_key(struct smbXsrv_session *session,
+ struct smbXsrv_connection *xconn)
+{
+ struct smbXsrv_channel_global0 *c = NULL;
+ NTSTATUS status;
+ DATA_BLOB key = data_blob_null;
+
+ status = smbXsrv_session_find_channel(session, xconn, &c);
+ if (NT_STATUS_IS_OK(status)) {
+ key = c->signing_key;
+ }
+
+ if (key.length == 0) {
+ key = session->global->signing_key;
+ }
+
+ return key;
+}
+
+static NTSTATUS smb2_get_new_nonce(struct smbXsrv_session *session,
+ uint64_t *new_nonce_high,
+ uint64_t *new_nonce_low)
+{
+ uint64_t nonce_high;
+ uint64_t nonce_low;
+
+ session->nonce_low += 1;
+ if (session->nonce_low == 0) {
+ session->nonce_low += 1;
+ session->nonce_high += 1;
+ }
+
+ /*
+ * CCM and GCM algorithms must never have their
+ * nonce wrap, or the security of the whole
+ * communication and the keys is destroyed.
+ * We must drop the connection once we have
+ * transfered too much data.
+ *
+ * NOTE: We assume nonces greater than 8 bytes.
+ */
+ if (session->nonce_high >= session->nonce_high_max) {
+ return NT_STATUS_ENCRYPTION_FAILED;
+ }
+
+ nonce_high = session->nonce_high_random;
+ nonce_high += session->nonce_high;
+ nonce_low = session->nonce_low;
+
+ *new_nonce_high = nonce_high;
+ *new_nonce_low = nonce_low;
+ return NT_STATUS_OK;
+}
+
static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval current_time,
struct smbd_smb2_request *req =
talloc_get_type_abort(private_data,
struct smbd_smb2_request);
+ struct smbXsrv_connection *xconn = req->xconn;
struct smbd_smb2_request_pending_state *state = NULL;
uint8_t *outhdr = NULL;
const uint8_t *inhdr = NULL;
uint64_t nonce_high = 0;
uint64_t nonce_low = 0;
uint64_t async_id = 0;
- struct tevent_req *subreq = NULL;
+ NTSTATUS status;
+ bool ok;
TALLOC_FREE(req->async_te);
* of this fact sometime when refactoring. JRA.
*/
- state = talloc_zero(req->sconn, struct smbd_smb2_request_pending_state);
+ state = talloc_zero(req->xconn, struct smbd_smb2_request_pending_state);
if (state == NULL) {
- smbd_server_connection_terminate(req->sconn,
+ smbd_server_connection_terminate(xconn,
nt_errstr(NT_STATUS_NO_MEMORY));
return;
}
- state->sconn = req->sconn;
tf = state->buf + NBT_HDR_SIZE;
tf_len = SMB2_TF_HDR_SIZE;
dyn = body + 8;
if (req->do_encryption) {
- struct smbXsrv_session *x = req->session;
-
- nonce_high = x->nonce_high;
- nonce_low = x->nonce_low;
-
- x->nonce_low += 1;
- if (x->nonce_low == 0) {
- x->nonce_low += 1;
- x->nonce_high += 1;
+ status = smb2_get_new_nonce(req->session,
+ &nonce_high,
+ &nonce_low);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbd_server_connection_terminate(xconn,
+ nt_errstr(status));
+ return;
}
}
state->vector[1+SMBD_SMB2_DYN_IOV_OFS].iov_base = dyn;
state->vector[1+SMBD_SMB2_DYN_IOV_OFS].iov_len = 1;
- smb2_setup_nbt_length(state->vector, 1 + SMBD_SMB2_NUM_IOV_PER_REQ);
+ ok = smb2_setup_nbt_length(state->vector,
+ 1 + SMBD_SMB2_NUM_IOV_PER_REQ);
+ if (!ok) {
+ smbd_server_connection_terminate(
+ xconn, nt_errstr(NT_STATUS_INTERNAL_ERROR));
+ return;
+ }
/* Ensure we correctly go through crediting. Grant
the credits now, and zero credits on the final
response. */
- smb2_set_operation_credit(req->sconn,
+ smb2_set_operation_credit(req->xconn,
SMBD_SMB2_IN_HDR_IOV(req),
&state->vector[1+SMBD_SMB2_HDR_IOV_OFS]);
}
if (req->do_encryption) {
- NTSTATUS status;
struct smbXsrv_session *x = req->session;
- struct smbXsrv_connection *conn = x->connection;
DATA_BLOB encryption_key = x->global->encryption_key;
status = smb2_signing_encrypt_pdu(encryption_key,
- conn->protocol,
+ xconn->smb2.server.cipher,
&state->vector[1+SMBD_SMB2_TF_IOV_OFS],
SMBD_SMB2_NUM_IOV_PER_REQ);
if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(req->sconn,
+ smbd_server_connection_terminate(xconn,
nt_errstr(status));
return;
}
} else if (req->do_signing) {
- NTSTATUS status;
struct smbXsrv_session *x = req->session;
- struct smbXsrv_connection *conn = x->connection;
- DATA_BLOB signing_key = x->global->channels[0].signing_key;
+ DATA_BLOB signing_key = smbd_smb2_signing_key(x, xconn);
status = smb2_signing_sign_pdu(signing_key,
- conn->protocol,
+ xconn->protocol,
&state->vector[1+SMBD_SMB2_HDR_IOV_OFS],
SMBD_SMB2_NUM_IOV_PER_REQ - 1);
if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(req->sconn,
+ smbd_server_connection_terminate(xconn,
nt_errstr(status));
return;
}
}
- subreq = tstream_writev_queue_send(state,
- state->sconn->ev_ctx,
- state->sconn->smb2.stream,
- state->sconn->smb2.send_queue,
- state->vector,
- ARRAY_SIZE(state->vector));
- if (subreq == NULL) {
- smbd_server_connection_terminate(state->sconn,
- nt_errstr(NT_STATUS_NO_MEMORY));
+ state->queue_entry.mem_ctx = state;
+ state->queue_entry.vector = state->vector;
+ state->queue_entry.count = ARRAY_SIZE(state->vector);
+ DLIST_ADD_END(xconn->smb2.send_queue, &state->queue_entry, NULL);
+ xconn->smb2.send_queue_len++;
+
+ status = smbd_smb2_flush_send_queue(xconn);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbd_server_connection_terminate(xconn,
+ nt_errstr(status));
return;
}
- tevent_req_set_callback(subreq,
- smbd_smb2_request_pending_writev_done,
- state);
}
static NTSTATUS smbd_smb2_request_process_cancel(struct smbd_smb2_request *req)
{
- struct smbd_server_connection *sconn = req->sconn;
+ struct smbXsrv_connection *xconn = req->xconn;
struct smbd_smb2_request *cur;
const uint8_t *inhdr;
uint32_t flags;
* we don't need the request anymore
* cancel requests never have a response
*/
- DLIST_REMOVE(req->sconn->smb2.requests, req);
+ DLIST_REMOVE(xconn->smb2.requests, req);
TALLOC_FREE(req);
- for (cur = sconn->smb2.requests; cur; cur = cur->next) {
+ for (cur = xconn->smb2.requests; cur; cur = cur->next) {
const uint8_t *outhdr;
uint64_t message_id;
uint64_t async_id;
req->last_session_id = 0;
- /* lookup an existing session */
- status = smb2srv_session_lookup(req->sconn->conn,
- in_session_id, now,
- &session);
+ /* look an existing session up */
+ switch (in_opcode) {
+ case SMB2_OP_SESSSETUP:
+ /*
+ * For a session bind request, we don't have the
+ * channel set up at this point yet, so we defer
+ * the verification that the connection belongs
+ * to the session to the session setup code, which
+ * can look at the session binding flags.
+ */
+ status = smb2srv_session_lookup_client(req->xconn->client,
+ in_session_id, now,
+ &session);
+ break;
+ default:
+ status = smb2srv_session_lookup_conn(req->xconn,
+ in_session_id, now,
+ &session);
+ break;
+ }
if (session) {
req->session = session;
req->last_session_id = in_session_id;
return NT_STATUS_INVALID_HANDLE;
}
- set_current_user_info(session_info->unix_info->sanitized_username,
- session_info->unix_info->unix_name,
- session_info->info->domain_name);
+ if (in_session_id != req->xconn->client->last_session_id) {
+ req->xconn->client->last_session_id = in_session_id;
+ set_current_user_info(session_info->unix_info->sanitized_username,
+ session_info->unix_info->unix_name,
+ session_info->info->domain_name);
+ }
return NT_STATUS_OK;
}
NTSTATUS smbd_smb2_request_verify_creditcharge(struct smbd_smb2_request *req,
uint32_t data_length)
{
+ struct smbXsrv_connection *xconn = req->xconn;
uint16_t needed_charge;
uint16_t credit_charge = 1;
const uint8_t *inhdr;
inhdr = SMBD_SMB2_IN_HDR_PTR(req);
- if (req->sconn->smb2.supports_multicredit) {
+ if (xconn->smb2.credits.multicredit) {
credit_charge = SVAL(inhdr, SMB2_HDR_CREDIT_CHARGE);
credit_charge = MAX(credit_charge, 1);
}
case SMB2_OP_GETINFO:
min_dyn_size = 0;
break;
+ case SMB2_OP_WRITE:
+ if (req->smb1req != NULL && req->smb1req->unread_bytes > 0) {
+ if (req->smb1req->unread_bytes < min_dyn_size) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ min_dyn_size = 0;
+ }
+ break;
}
/*
NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
{
- struct smbXsrv_connection *conn = req->sconn->conn;
+ struct smbXsrv_connection *xconn = req->xconn;
const struct smbd_smb2_dispatch_table *call = NULL;
const struct iovec *intf_v = SMBD_SMB2_IN_TF_IOV(req);
const uint8_t *inhdr;
NTSTATUS return_value;
struct smbXsrv_session *x = NULL;
bool signing_required = false;
+ bool encryption_desired = false;
bool encryption_required = false;
inhdr = SMBD_SMB2_IN_HDR_PTR(req);
+ DO_PROFILE_INC(request);
+
/* TODO: verify more things */
flags = IVAL(inhdr, SMB2_HDR_FLAGS);
smb2_opcode_name(opcode),
(unsigned long long)mid));
- if (conn->protocol >= PROTOCOL_SMB2_02) {
+ if (xconn->protocol >= PROTOCOL_SMB2_02) {
/*
* once the protocol is negotiated
* SMB2_OP_NEGPROT is not allowed anymore
x = req->session;
if (x != NULL) {
signing_required = x->global->signing_required;
+ encryption_desired = x->encryption_desired;
encryption_required = x->global->encryption_required;
-
- if (opcode == SMB2_OP_SESSSETUP &&
- x->global->channels[0].signing_key.length) {
- signing_required = true;
- }
}
req->do_signing = false;
req->do_encryption = false;
+ req->was_encrypted = false;
if (intf_v->iov_len == SMB2_TF_HDR_SIZE) {
const uint8_t *intf = SMBD_SMB2_IN_TF_PTR(req);
uint64_t tf_session_id = BVAL(intf, SMB2_TF_SESSION_ID);
NT_STATUS_ACCESS_DENIED);
}
- req->do_encryption = true;
+ req->was_encrypted = true;
}
- if (encryption_required && !req->do_encryption) {
+ if (encryption_required && !req->was_encrypted) {
return smbd_smb2_request_error(req,
NT_STATUS_ACCESS_DENIED);
}
allowed_flags = SMB2_HDR_FLAG_CHAINED |
SMB2_HDR_FLAG_SIGNED |
SMB2_HDR_FLAG_DFS;
+ if (xconn->protocol >= PROTOCOL_SMB3_11) {
+ allowed_flags |= SMB2_HDR_FLAG_PRIORITY_MASK;
+ }
+ if (opcode == SMB2_OP_NEGPROT) {
+ if (lp_server_max_protocol() >= PROTOCOL_SMB3_11) {
+ allowed_flags |= SMB2_HDR_FLAG_PRIORITY_MASK;
+ }
+ }
if (opcode == SMB2_OP_CANCEL) {
allowed_flags |= SMB2_HDR_FLAG_ASYNC;
}
req->compat_chain_fsp = NULL;
}
- if (req->do_encryption) {
+ if (req->was_encrypted) {
signing_required = false;
} else if (signing_required || (flags & SMB2_HDR_FLAG_SIGNED)) {
- DATA_BLOB signing_key;
+ DATA_BLOB signing_key = data_blob_null;
if (x == NULL) {
- return smbd_smb2_request_error(
- req, NT_STATUS_USER_SESSION_DELETED);
+ /*
+ * MS-SMB2: 3.3.5.2.4 Verifying the Signature.
+ * If the SMB2 header of the SMB2 NEGOTIATE
+ * request has the SMB2_FLAGS_SIGNED bit set in the
+ * Flags field, the server MUST fail the request
+ * with STATUS_INVALID_PARAMETER.
+ *
+ * Microsoft test tool checks this.
+ */
+
+ if ((opcode == SMB2_OP_NEGPROT) &&
+ (flags & SMB2_HDR_FLAG_SIGNED)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else {
+ status = NT_STATUS_USER_SESSION_DELETED;
+ }
+ return smbd_smb2_request_error(req, status);
}
- signing_key = x->global->channels[0].signing_key;
+ signing_key = smbd_smb2_signing_key(x, xconn);
/*
* If we have a signing key, we should
}
status = smb2_signing_check_pdu(signing_key,
- conn->protocol,
+ xconn->protocol,
SMBD_SMB2_IN_HDR_IOV(req),
SMBD_SMB2_NUM_IOV_PER_REQ - 1);
if (!NT_STATUS_IS_OK(status)) {
if (!NT_STATUS_IS_OK(status)) {
return smbd_smb2_request_error(req, status);
}
+ if (req->tcon->encryption_desired) {
+ encryption_desired = true;
+ }
if (req->tcon->global->encryption_required) {
encryption_required = true;
}
- if (encryption_required && !req->do_encryption) {
+ if (encryption_required && !req->was_encrypted) {
return smbd_smb2_request_error(req,
NT_STATUS_ACCESS_DENIED);
}
}
+ if (req->was_encrypted || encryption_desired) {
+ req->do_encryption = true;
+ }
+
if (call->fileid_ofs != 0) {
size_t needed = call->fileid_ofs + 16;
const uint8_t *body = SMBD_SMB2_IN_BODY_PTR(req);
SMB_ASSERT(call->need_tcon);
}
+#define _INBYTES(_r) \
+ iov_buflen(SMBD_SMB2_IN_HDR_IOV(_r), SMBD_SMB2_NUM_IOV_PER_REQ-1)
+
switch (opcode) {
case SMB2_OP_NEGPROT:
- {
- START_PROFILE(smb2_negprot);
- return_value = smbd_smb2_request_process_negprot(req);
- END_PROFILE(smb2_negprot);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_negprot, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_negprot(req);
break;
case SMB2_OP_SESSSETUP:
- {
- START_PROFILE(smb2_sesssetup);
- return_value = smbd_smb2_request_process_sesssetup(req);
- END_PROFILE(smb2_sesssetup);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_sesssetup, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_sesssetup(req);
break;
case SMB2_OP_LOGOFF:
- {
- START_PROFILE(smb2_logoff);
- return_value = smbd_smb2_request_process_logoff(req);
- END_PROFILE(smb2_logoff);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_logoff, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_logoff(req);
break;
case SMB2_OP_TCON:
- {
- START_PROFILE(smb2_tcon);
- return_value = smbd_smb2_request_process_tcon(req);
- END_PROFILE(smb2_tcon);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_tcon, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_tcon(req);
break;
case SMB2_OP_TDIS:
- {
- START_PROFILE(smb2_tdis);
- return_value = smbd_smb2_request_process_tdis(req);
- END_PROFILE(smb2_tdis);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_tdis, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_tdis(req);
break;
case SMB2_OP_CREATE:
- {
- START_PROFILE(smb2_create);
- return_value = smbd_smb2_request_process_create(req);
- END_PROFILE(smb2_create);
+ if (req->subreq == NULL) {
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_create, profile_p,
+ req->profile, _INBYTES(req));
+ } else {
+ SMBPROFILE_IOBYTES_ASYNC_SET_BUSY(req->profile);
}
+ return_value = smbd_smb2_request_process_create(req);
break;
case SMB2_OP_CLOSE:
- {
- START_PROFILE(smb2_close);
- return_value = smbd_smb2_request_process_close(req);
- END_PROFILE(smb2_close);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_close, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_close(req);
break;
case SMB2_OP_FLUSH:
- {
- START_PROFILE(smb2_flush);
- return_value = smbd_smb2_request_process_flush(req);
- END_PROFILE(smb2_flush);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_flush, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_flush(req);
break;
case SMB2_OP_READ:
- {
- START_PROFILE(smb2_read);
- return_value = smbd_smb2_request_process_read(req);
- END_PROFILE(smb2_read);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_read, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_read(req);
break;
case SMB2_OP_WRITE:
- {
- START_PROFILE(smb2_write);
- return_value = smbd_smb2_request_process_write(req);
- END_PROFILE(smb2_write);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_write, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_write(req);
break;
case SMB2_OP_LOCK:
- {
- START_PROFILE(smb2_lock);
- return_value = smbd_smb2_request_process_lock(req);
- END_PROFILE(smb2_lock);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_lock, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_lock(req);
break;
case SMB2_OP_IOCTL:
- {
- START_PROFILE(smb2_ioctl);
- return_value = smbd_smb2_request_process_ioctl(req);
- END_PROFILE(smb2_ioctl);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_ioctl, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_ioctl(req);
break;
case SMB2_OP_CANCEL:
- {
- START_PROFILE(smb2_cancel);
- return_value = smbd_smb2_request_process_cancel(req);
- END_PROFILE(smb2_cancel);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_cancel, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_cancel(req);
+ SMBPROFILE_IOBYTES_ASYNC_END(req->profile, 0);
break;
case SMB2_OP_KEEPALIVE:
- {
- START_PROFILE(smb2_keepalive);
- return_value = smbd_smb2_request_process_keepalive(req);
- END_PROFILE(smb2_keepalive);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_keepalive, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_keepalive(req);
break;
- case SMB2_OP_FIND:
- {
- START_PROFILE(smb2_find);
- return_value = smbd_smb2_request_process_find(req);
- END_PROFILE(smb2_find);
- }
+ case SMB2_OP_QUERY_DIRECTORY:
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_find, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_query_directory(req);
break;
case SMB2_OP_NOTIFY:
- {
- START_PROFILE(smb2_notify);
- return_value = smbd_smb2_request_process_notify(req);
- END_PROFILE(smb2_notify);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_notify, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_notify(req);
break;
case SMB2_OP_GETINFO:
- {
- START_PROFILE(smb2_getinfo);
- return_value = smbd_smb2_request_process_getinfo(req);
- END_PROFILE(smb2_getinfo);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_getinfo, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_getinfo(req);
break;
case SMB2_OP_SETINFO:
- {
- START_PROFILE(smb2_setinfo);
- return_value = smbd_smb2_request_process_setinfo(req);
- END_PROFILE(smb2_setinfo);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_setinfo, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_setinfo(req);
break;
case SMB2_OP_BREAK:
- {
- START_PROFILE(smb2_break);
- return_value = smbd_smb2_request_process_break(req);
- END_PROFILE(smb2_break);
- }
+ SMBPROFILE_IOBYTES_ASYNC_START(smb2_break, profile_p,
+ req->profile, _INBYTES(req));
+ return_value = smbd_smb2_request_process_break(req);
break;
default:
static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
{
- struct smbXsrv_connection *conn = req->sconn->conn;
- struct tevent_req *subreq;
+ struct smbXsrv_connection *xconn = req->xconn;
int first_idx = 1;
struct iovec *firsttf = SMBD_SMB2_IDX_TF_IOV(req,out,first_idx);
struct iovec *outhdr = SMBD_SMB2_OUT_HDR_IOV(req);
struct iovec *outdyn = SMBD_SMB2_OUT_DYN_IOV(req);
+ NTSTATUS status;
+ bool ok;
req->subreq = NULL;
TALLOC_FREE(req->async_te);
DATA_BLOB encryption_key = req->session->global->encryption_key;
uint8_t *tf;
uint64_t session_id = req->session->global->session_wire_id;
- struct smbXsrv_session *x = req->session;
uint64_t nonce_high;
uint64_t nonce_low;
- nonce_high = x->nonce_high;
- nonce_low = x->nonce_low;
-
- x->nonce_low += 1;
- if (x->nonce_low == 0) {
- x->nonce_low += 1;
- x->nonce_high += 1;
+ status = smb2_get_new_nonce(req->session,
+ &nonce_high,
+ &nonce_low);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
/*
return NT_STATUS_NO_MEMORY;
}
- tf = talloc_zero_array(req->out.vector, uint8_t,
+ tf = talloc_zero_array(req, uint8_t,
SMB2_TF_HDR_SIZE);
if (tf == NULL) {
return NT_STATUS_NO_MEMORY;
{
int last_idx = req->current_idx - SMBD_SMB2_NUM_IOV_PER_REQ;
struct iovec *lasthdr = SMBD_SMB2_IDX_HDR_IOV(req,out,last_idx);
- NTSTATUS status;
/*
* As we are sure the header of the last request in the
* with the last signing key we remembered.
*/
status = smb2_signing_sign_pdu(req->last_key,
- conn->protocol,
+ xconn->protocol,
lasthdr,
SMBD_SMB2_NUM_IOV_PER_REQ - 1);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
}
- data_blob_clear_free(&req->last_key);
+ if (req->last_key.length > 0) {
+ data_blob_clear_free(&req->last_key);
+ }
+
+ SMBPROFILE_IOBYTES_ASYNC_END(req->profile,
+ iov_buflen(outhdr, SMBD_SMB2_NUM_IOV_PER_REQ-1));
req->current_idx += SMBD_SMB2_NUM_IOV_PER_REQ;
if (req->do_signing && firsttf->iov_len == 0) {
struct smbXsrv_session *x = req->session;
- DATA_BLOB signing_key = x->global->channels[0].signing_key;
+ DATA_BLOB signing_key = smbd_smb2_signing_key(x, xconn);
/*
* we need to remember the signing key
req->compound_related = false;
}
- smb2_setup_nbt_length(req->out.vector, req->out.vector_count);
+ ok = smb2_setup_nbt_length(req->out.vector, req->out.vector_count);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
/* Set credit for these operations (zero credits if this
is a final reply for an async operation). */
* now check if we need to sign the current response
*/
if (firsttf->iov_len == SMB2_TF_HDR_SIZE) {
- NTSTATUS status;
-
status = smb2_signing_encrypt_pdu(req->first_key,
- conn->protocol,
+ xconn->smb2.server.cipher,
firsttf,
req->out.vector_count - first_idx);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
} else if (req->do_signing) {
- NTSTATUS status;
struct smbXsrv_session *x = req->session;
- DATA_BLOB signing_key = x->global->channels[0].signing_key;
+ DATA_BLOB signing_key = smbd_smb2_signing_key(x, xconn);
status = smb2_signing_sign_pdu(signing_key,
- conn->protocol,
+ xconn->protocol,
outhdr,
SMBD_SMB2_NUM_IOV_PER_REQ - 1);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
}
- data_blob_clear_free(&req->first_key);
+ if (req->first_key.length > 0) {
+ data_blob_clear_free(&req->first_key);
+ }
+
+ if (req->preauth != NULL) {
+ struct hc_sha512state sctx;
+ int i;
+
+ samba_SHA512_Init(&sctx);
+ samba_SHA512_Update(&sctx, req->preauth->sha512_value,
+ sizeof(req->preauth->sha512_value));
+ for (i = 1; i < req->in.vector_count; i++) {
+ samba_SHA512_Update(&sctx,
+ req->in.vector[i].iov_base,
+ req->in.vector[i].iov_len);
+ }
+ samba_SHA512_Final(req->preauth->sha512_value, &sctx);
+
+ samba_SHA512_Init(&sctx);
+ samba_SHA512_Update(&sctx, req->preauth->sha512_value,
+ sizeof(req->preauth->sha512_value));
+ for (i = 1; i < req->out.vector_count; i++) {
+ samba_SHA512_Update(&sctx,
+ req->out.vector[i].iov_base,
+ req->out.vector[i].iov_len);
+ }
+ samba_SHA512_Final(req->preauth->sha512_value, &sctx);
+
+ req->preauth = NULL;
+ }
/* I am a sick, sick man... :-). Sendfile hack ... JRA. */
if (req->out.vector_count < (2*SMBD_SMB2_NUM_IOV_PER_REQ) &&
req->out.vector_count -= 1;
}
- subreq = tstream_writev_queue_send(req,
- req->sconn->ev_ctx,
- req->sconn->smb2.stream,
- req->sconn->smb2.send_queue,
- req->out.vector,
- req->out.vector_count);
- if (subreq == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
- tevent_req_set_callback(subreq, smbd_smb2_request_writev_done, req);
/*
* We're done with this request -
* move it off the "being processed" queue.
*/
- DLIST_REMOVE(req->sconn->smb2.requests, req);
+ DLIST_REMOVE(xconn->smb2.requests, req);
+
+ req->queue_entry.mem_ctx = req;
+ req->queue_entry.vector = req->out.vector;
+ req->queue_entry.count = req->out.vector_count;
+ DLIST_ADD_END(xconn->smb2.send_queue, &req->queue_entry, NULL);
+ xconn->smb2.send_queue_len++;
+
+ status = smbd_smb2_flush_send_queue(xconn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
return NT_STATUS_OK;
}
-static NTSTATUS smbd_smb2_request_next_incoming(struct smbd_server_connection *sconn);
+static NTSTATUS smbd_smb2_request_next_incoming(struct smbXsrv_connection *xconn);
void smbd_smb2_request_dispatch_immediate(struct tevent_context *ctx,
struct tevent_immediate *im,
{
struct smbd_smb2_request *req = talloc_get_type_abort(private_data,
struct smbd_smb2_request);
- struct smbd_server_connection *sconn = req->sconn;
+ struct smbXsrv_connection *xconn = req->xconn;
NTSTATUS status;
TALLOC_FREE(im);
status = smbd_smb2_request_dispatch(req);
if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
- }
-
- status = smbd_smb2_request_next_incoming(sconn);
- if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
- }
-}
-
-static void smbd_smb2_request_writev_done(struct tevent_req *subreq)
-{
- struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
- struct smbd_smb2_request);
- struct smbd_server_connection *sconn = req->sconn;
- int ret;
- int sys_errno;
- NTSTATUS status;
-
- ret = tstream_writev_queue_recv(subreq, &sys_errno);
- TALLOC_FREE(subreq);
- TALLOC_FREE(req);
- if (ret == -1) {
- status = map_nt_error_from_unix(sys_errno);
- DEBUG(2,("smbd_smb2_request_writev_done: client write error %s\n",
- nt_errstr(status)));
- smbd_server_connection_terminate(sconn, nt_errstr(status));
+ smbd_server_connection_terminate(xconn, nt_errstr(status));
return;
}
- status = smbd_smb2_request_next_incoming(sconn);
+ status = smbd_smb2_request_next_incoming(xconn);
if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
+ smbd_server_connection_terminate(xconn, nt_errstr(status));
return;
}
}
outdyn_v->iov_len = 0;
}
- /* see if we need to recalculate the offset to the next response */
- if (next_command_ofs > 0) {
+ /*
+ * See if we need to recalculate the offset to the next response
+ *
+ * Note that all responses may require padding (including the very last
+ * one).
+ */
+ if (req->out.vector_count >= (2 * SMBD_SMB2_NUM_IOV_PER_REQ)) {
next_command_ofs = SMB2_HDR_BODY;
next_command_ofs += SMBD_SMB2_OUT_BODY_LEN(req);
next_command_ofs += SMBD_SMB2_OUT_DYN_LEN(req);
*/
uint8_t *pad;
- pad = talloc_zero_array(req->out.vector,
+ pad = talloc_zero_array(req,
uint8_t, pad_size);
if (pad == NULL) {
return smbd_smb2_request_error(req,
old_dyn = SMBD_SMB2_OUT_DYN_PTR(req);
new_size = old_size + pad_size;
- new_dyn = talloc_zero_array(req->out.vector,
+ new_dyn = talloc_zero_array(req,
uint8_t, new_size);
if (new_dyn == NULL) {
return smbd_smb2_request_error(req,
next_command_ofs += pad_size;
}
- SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, next_command_ofs);
-
+ if ((req->current_idx + SMBD_SMB2_NUM_IOV_PER_REQ) >= req->out.vector_count) {
+ SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, 0);
+ } else {
+ SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, next_command_ofs);
+ }
return smbd_smb2_request_reply(req);
}
DATA_BLOB *info,
const char *location)
{
+ struct smbXsrv_connection *xconn = req->xconn;
DATA_BLOB body;
+ DATA_BLOB _dyn;
uint8_t *outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
size_t unread_bytes = smbd_smb2_unread_bytes(req);
- DEBUG(10,("smbd_smb2_request_error_ex: idx[%d] status[%s] |%s| at %s\n",
- req->current_idx, nt_errstr(status), info ? " +info" : "",
- location));
+ DBG_NOTICE("smbd_smb2_request_error_ex: idx[%d] status[%s] |%s| "
+ "at %s\n", req->current_idx, nt_errstr(status),
+ info ? " +info" : "", location);
if (unread_bytes) {
/* Recvfile error. Drain incoming socket. */
size_t ret;
errno = 0;
- ret = drain_socket(req->sconn->sock, unread_bytes);
+ ret = drain_socket(xconn->transport.sock, unread_bytes);
if (ret != unread_bytes) {
NTSTATUS error;
* *MUST BE* OUTVEC_ALLOC_SIZE. So we have room for
* 1 byte without having to do an alloc.
*/
- info = talloc_zero_array(req->out.vector,
- DATA_BLOB,
- 1);
- if (!info) {
- return NT_STATUS_NO_MEMORY;
- }
+ info = &_dyn;
info->data = ((uint8_t *)outhdr) +
OUTVEC_ALLOC_SIZE - 1;
info->length = 1;
}
-struct smbd_smb2_send_oplock_break_state {
- struct smbd_server_connection *sconn;
- uint8_t buf[NBT_HDR_SIZE + SMB2_TF_HDR_SIZE + SMB2_HDR_BODY + 0x18];
+struct smbd_smb2_send_break_state {
+ struct smbd_smb2_send_queue queue_entry;
+ uint8_t nbt_hdr[NBT_HDR_SIZE];
+ uint8_t tf[SMB2_TF_HDR_SIZE];
+ uint8_t hdr[SMB2_HDR_BODY];
struct iovec vector[1+SMBD_SMB2_NUM_IOV_PER_REQ];
+ uint8_t body[1];
};
-static void smbd_smb2_oplock_break_writev_done(struct tevent_req *subreq);
-
-NTSTATUS smbd_smb2_send_oplock_break(struct smbd_server_connection *sconn,
+static NTSTATUS smbd_smb2_send_break(struct smbXsrv_connection *xconn,
struct smbXsrv_session *session,
struct smbXsrv_tcon *tcon,
- struct smbXsrv_open *op,
- uint8_t oplock_level)
+ const uint8_t *body,
+ size_t body_len)
{
- struct smbd_smb2_send_oplock_break_state *state;
- struct smbXsrv_connection *conn = sconn->conn;
- struct tevent_req *subreq;
- uint8_t *tf;
- size_t tf_len;
- uint8_t *hdr;
- uint8_t *body;
- size_t body_len;
- uint8_t *dyn;
- size_t dyn_len;
- bool do_encryption = session->global->encryption_required;
+ struct smbd_smb2_send_break_state *state;
+ bool do_encryption = false;
+ uint64_t session_wire_id = 0;
uint64_t nonce_high = 0;
uint64_t nonce_low = 0;
+ NTSTATUS status;
+ size_t statelen;
+ bool ok;
- if (tcon->global->encryption_required) {
- do_encryption = true;
+ if (session != NULL) {
+ session_wire_id = session->global->session_wire_id;
+ do_encryption = session->encryption_desired;
+ if (tcon->encryption_desired) {
+ do_encryption = true;
+ }
}
- state = talloc(sconn, struct smbd_smb2_send_oplock_break_state);
+ statelen = offsetof(struct smbd_smb2_send_break_state, body) +
+ body_len;
+
+ state = talloc_zero_size(xconn, statelen);
if (state == NULL) {
return NT_STATUS_NO_MEMORY;
}
- state->sconn = sconn;
-
- tf = state->buf + NBT_HDR_SIZE;
- tf_len = SMB2_TF_HDR_SIZE;
- hdr = tf + tf_len;
- body = hdr + SMB2_HDR_BODY;
- body_len = 0x18;
- dyn = body + body_len;
- dyn_len = 0;
+ talloc_set_name_const(state, "struct smbd_smb2_send_break_state");
if (do_encryption) {
- nonce_high = session->nonce_high;
- nonce_low = session->nonce_low;
-
- session->nonce_low += 1;
- if (session->nonce_low == 0) {
- session->nonce_low += 1;
- session->nonce_high += 1;
+ status = smb2_get_new_nonce(session,
+ &nonce_high,
+ &nonce_low);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
}
- SIVAL(tf, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC);
- SBVAL(tf, SMB2_TF_NONCE+0, nonce_low);
- SBVAL(tf, SMB2_TF_NONCE+8, nonce_high);
- SBVAL(tf, SMB2_TF_SESSION_ID, session->global->session_wire_id);
-
- SIVAL(hdr, 0, SMB2_MAGIC);
- SSVAL(hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
- SSVAL(hdr, SMB2_HDR_EPOCH, 0);
- SIVAL(hdr, SMB2_HDR_STATUS, 0);
- SSVAL(hdr, SMB2_HDR_OPCODE, SMB2_OP_BREAK);
- SSVAL(hdr, SMB2_HDR_CREDIT, 0);
- SIVAL(hdr, SMB2_HDR_FLAGS, SMB2_HDR_FLAG_REDIRECT);
- SIVAL(hdr, SMB2_HDR_NEXT_COMMAND, 0);
- SBVAL(hdr, SMB2_HDR_MESSAGE_ID, UINT64_MAX);
- SIVAL(hdr, SMB2_HDR_PID, 0);
- SIVAL(hdr, SMB2_HDR_TID, 0);
- SBVAL(hdr, SMB2_HDR_SESSION_ID, 0);
- memset(hdr+SMB2_HDR_SIGNATURE, 0, 16);
-
- SSVAL(body, 0x00, body_len);
+ SIVAL(state->tf, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC);
+ SBVAL(state->tf, SMB2_TF_NONCE+0, nonce_low);
+ SBVAL(state->tf, SMB2_TF_NONCE+8, nonce_high);
+ SBVAL(state->tf, SMB2_TF_SESSION_ID, session_wire_id);
- SCVAL(body, 0x02, oplock_level);
- SCVAL(body, 0x03, 0); /* reserved */
- SIVAL(body, 0x04, 0); /* reserved */
- SBVAL(body, 0x08, op->global->open_persistent_id);
- SBVAL(body, 0x10, op->global->open_volatile_id);
+ SIVAL(state->hdr, 0, SMB2_MAGIC);
+ SSVAL(state->hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(state->hdr, SMB2_HDR_EPOCH, 0);
+ SIVAL(state->hdr, SMB2_HDR_STATUS, 0);
+ SSVAL(state->hdr, SMB2_HDR_OPCODE, SMB2_OP_BREAK);
+ SSVAL(state->hdr, SMB2_HDR_CREDIT, 0);
+ SIVAL(state->hdr, SMB2_HDR_FLAGS, SMB2_HDR_FLAG_REDIRECT);
+ SIVAL(state->hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(state->hdr, SMB2_HDR_MESSAGE_ID, UINT64_MAX);
+ SIVAL(state->hdr, SMB2_HDR_PID, 0);
+ SIVAL(state->hdr, SMB2_HDR_TID, 0);
+ SBVAL(state->hdr, SMB2_HDR_SESSION_ID, 0);
+ memset(state->hdr+SMB2_HDR_SIGNATURE, 0, 16);
- state->vector[0].iov_base = (void *)state->buf;
- state->vector[0].iov_len = NBT_HDR_SIZE;
+ state->vector[0] = (struct iovec) {
+ .iov_base = state->nbt_hdr,
+ .iov_len = sizeof(state->nbt_hdr)
+ };
if (do_encryption) {
- state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_base = tf;
- state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_len = tf_len;
+ state->vector[1+SMBD_SMB2_TF_IOV_OFS] = (struct iovec) {
+ .iov_base = state->tf,
+ .iov_len = sizeof(state->tf)
+ };
} else {
- state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_base = NULL;
- state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_len = 0;
+ state->vector[1+SMBD_SMB2_TF_IOV_OFS] = (struct iovec) {
+ .iov_base = NULL,
+ .iov_len = 0
+ };
}
- state->vector[1+SMBD_SMB2_HDR_IOV_OFS].iov_base = hdr;
- state->vector[1+SMBD_SMB2_HDR_IOV_OFS].iov_len = SMB2_HDR_BODY;
+ state->vector[1+SMBD_SMB2_HDR_IOV_OFS] = (struct iovec) {
+ .iov_base = state->hdr,
+ .iov_len = sizeof(state->hdr)
+ };
- state->vector[1+SMBD_SMB2_BODY_IOV_OFS].iov_base = body;
- state->vector[1+SMBD_SMB2_BODY_IOV_OFS].iov_len = body_len;
+ memcpy(state->body, body, body_len);
- state->vector[1+SMBD_SMB2_DYN_IOV_OFS].iov_base = dyn;
- state->vector[1+SMBD_SMB2_DYN_IOV_OFS].iov_len = dyn_len;
+ state->vector[1+SMBD_SMB2_BODY_IOV_OFS] = (struct iovec) {
+ .iov_base = state->body,
+ .iov_len = body_len /* no sizeof(state->body) .. :-) */
+ };
- smb2_setup_nbt_length(state->vector, 1 + SMBD_SMB2_NUM_IOV_PER_REQ);
+ /*
+ * state->vector[1+SMBD_SMB2_DYN_IOV_OFS] is NULL by talloc_zero above
+ */
+
+ ok = smb2_setup_nbt_length(state->vector,
+ 1 + SMBD_SMB2_NUM_IOV_PER_REQ);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
if (do_encryption) {
- NTSTATUS status;
DATA_BLOB encryption_key = session->global->encryption_key;
status = smb2_signing_encrypt_pdu(encryption_key,
- conn->protocol,
+ xconn->smb2.server.cipher,
&state->vector[1+SMBD_SMB2_TF_IOV_OFS],
SMBD_SMB2_NUM_IOV_PER_REQ);
if (!NT_STATUS_IS_OK(status)) {
}
}
- subreq = tstream_writev_queue_send(state,
- sconn->ev_ctx,
- sconn->smb2.stream,
- sconn->smb2.send_queue,
- state->vector,
- ARRAY_SIZE(state->vector));
- if (subreq == NULL) {
- return NT_STATUS_NO_MEMORY;
+ state->queue_entry.mem_ctx = state;
+ state->queue_entry.vector = state->vector;
+ state->queue_entry.count = ARRAY_SIZE(state->vector);
+ DLIST_ADD_END(xconn->smb2.send_queue, &state->queue_entry, NULL);
+ xconn->smb2.send_queue_len++;
+
+ status = smbd_smb2_flush_send_queue(xconn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
- tevent_req_set_callback(subreq,
- smbd_smb2_oplock_break_writev_done,
- state);
return NT_STATUS_OK;
}
-static void smbd_smb2_oplock_break_writev_done(struct tevent_req *subreq)
+NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_connection *xconn,
+ struct smbXsrv_session *session,
+ struct smbXsrv_tcon *tcon,
+ struct smbXsrv_open *op,
+ uint8_t oplock_level)
{
- struct smbd_smb2_send_oplock_break_state *state =
- tevent_req_callback_data(subreq,
- struct smbd_smb2_send_oplock_break_state);
- struct smbd_server_connection *sconn = state->sconn;
- int ret;
- int sys_errno;
+ uint8_t body[0x18];
- ret = tstream_writev_queue_recv(subreq, &sys_errno);
- TALLOC_FREE(subreq);
- if (ret == -1) {
- NTSTATUS status = map_nt_error_from_unix(sys_errno);
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
- }
+ SSVAL(body, 0x00, sizeof(body));
+ SCVAL(body, 0x02, oplock_level);
+ SCVAL(body, 0x03, 0); /* reserved */
+ SIVAL(body, 0x04, 0); /* reserved */
+ SBVAL(body, 0x08, op->global->open_persistent_id);
+ SBVAL(body, 0x10, op->global->open_volatile_id);
- TALLOC_FREE(state);
+ return smbd_smb2_send_break(xconn, session, tcon, body, sizeof(body));
}
-struct smbd_smb2_request_read_state {
- struct tevent_context *ev;
- struct smbd_server_connection *sconn;
- struct smbd_smb2_request *smb2_req;
- struct {
- uint8_t nbt[NBT_HDR_SIZE];
- bool done;
- } hdr;
- bool doing_receivefile;
- size_t min_recv_size;
- size_t pktlen;
- uint8_t *pktbuf;
-};
-
-static int smbd_smb2_request_next_vector(struct tstream_context *stream,
- void *private_data,
- TALLOC_CTX *mem_ctx,
- struct iovec **_vector,
- size_t *_count);
-static void smbd_smb2_request_read_done(struct tevent_req *subreq);
-
-static size_t get_min_receive_file_size(struct smbd_smb2_request *smb2_req)
+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
+ uint16_t new_epoch,
+ uint32_t lease_flags,
+ struct smb2_lease_key *lease_key,
+ uint32_t current_lease_state,
+ uint32_t new_lease_state)
{
- if (smb2_req->do_signing) {
- return 0;
- }
- if (smb2_req->do_encryption) {
- return 0;
- }
- return (size_t)lp_min_receive_file_size();
-}
-
-static struct tevent_req *smbd_smb2_request_read_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct smbd_server_connection *sconn)
-{
- struct tevent_req *req;
- struct smbd_smb2_request_read_state *state;
- struct tevent_req *subreq;
-
- req = tevent_req_create(mem_ctx, &state,
- struct smbd_smb2_request_read_state);
- if (req == NULL) {
- return NULL;
- }
- state->ev = ev;
- state->sconn = sconn;
-
- state->smb2_req = smbd_smb2_request_allocate(state);
- if (tevent_req_nomem(state->smb2_req, req)) {
- return tevent_req_post(req, ev);
- }
- state->smb2_req->sconn = sconn;
- state->min_recv_size = get_min_receive_file_size(state->smb2_req);
-
- subreq = tstream_readv_pdu_queue_send(state->smb2_req,
- state->ev,
- state->sconn->smb2.stream,
- state->sconn->smb2.recv_queue,
- smbd_smb2_request_next_vector,
- state);
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
- }
- tevent_req_set_callback(subreq, smbd_smb2_request_read_done, req);
-
- return req;
+ uint8_t body[0x2c];
+
+ SSVAL(body, 0x00, sizeof(body));
+ SSVAL(body, 0x02, new_epoch);
+ SIVAL(body, 0x04, lease_flags);
+ SBVAL(body, 0x08, lease_key->data[0]);
+ SBVAL(body, 0x10, lease_key->data[1]);
+ SIVAL(body, 0x18, current_lease_state);
+ SIVAL(body, 0x1c, new_lease_state);
+ SIVAL(body, 0x20, 0); /* BreakReason, MUST be 0 */
+ SIVAL(body, 0x24, 0); /* AccessMaskHint, MUST be 0 */
+ SIVAL(body, 0x28, 0); /* ShareMaskHint, MUST be 0 */
+
+ return smbd_smb2_send_break(xconn, NULL, NULL, body, sizeof(body));
}
static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state)
{
+ NTSTATUS status;
uint32_t flags;
+ uint64_t file_id_persistent;
+ uint64_t file_id_volatile;
+ struct smbXsrv_open *op = NULL;
+ struct files_struct *fsp = NULL;
+ const uint8_t *body = NULL;
+
+ /*
+ * This is only called with a pktbuf
+ * of at least SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN
+ * bytes
+ */
if (IVAL(state->pktbuf, 0) == SMB2_TF_MAGIC) {
/* Transform header. Cannot recvfile. */
return false;
}
+ body = &state->pktbuf[SMB2_HDR_BODY];
+
+ file_id_persistent = BVAL(body, 0x10);
+ file_id_volatile = BVAL(body, 0x18);
+
+ status = smb2srv_open_lookup(state->req->xconn,
+ file_id_persistent,
+ file_id_volatile,
+ 0, /* now */
+ &op);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ fsp = op->compat;
+ if (fsp == NULL) {
+ return false;
+ }
+ if (fsp->conn == NULL) {
+ return false;
+ }
+
+ if (IS_IPC(fsp->conn)) {
+ return false;
+ }
+ if (IS_PRINT(fsp->conn)) {
+ return false;
+ }
+
DEBUG(10,("Doing recvfile write len = %u\n",
- (unsigned int)(state->pktlen -
- SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN)));
+ (unsigned int)(state->pktfull - state->pktlen)));
return true;
}
-static int smbd_smb2_request_next_vector(struct tstream_context *stream,
- void *private_data,
- TALLOC_CTX *mem_ctx,
- struct iovec **_vector,
- size_t *_count)
+static NTSTATUS smbd_smb2_request_next_incoming(struct smbXsrv_connection *xconn)
{
- struct smbd_smb2_request_read_state *state =
- talloc_get_type_abort(private_data,
- struct smbd_smb2_request_read_state);
- struct iovec *vector = NULL;
- size_t min_recvfile_size = UINT32_MAX;
+ struct smbd_server_connection *sconn = xconn->client->sconn;
+ struct smbd_smb2_request_read_state *state = &xconn->smb2.request_read_state;
+ size_t max_send_queue_len;
+ size_t cur_send_queue_len;
- if (state->pktlen > 0) {
- if (state->doing_receivefile && !is_smb2_recvfile_write(state)) {
- /*
- * Not a possible receivefile write.
- * Read the rest of the data.
- */
- state->doing_receivefile = false;
- vector = talloc_array(mem_ctx, struct iovec, 1);
- if (vector == NULL) {
- return -1;
- }
- vector[0].iov_base = (void *)(state->pktbuf +
- SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN);
- vector[0].iov_len = (state->pktlen -
- SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN);
- *_vector = vector;
- *_count = 1;
- } else {
- /*
- * Either this is a receivefile write so we've
- * done a short read, or if not we have all the data.
- * Either way, we're done and
- * smbd_smb2_request_read_done() will handle
- * and short read case by looking at the
- * state->doing_receivefile value.
- */
- *_vector = NULL;
- *_count = 0;
- }
- return 0;
+ if (!NT_STATUS_IS_OK(xconn->transport.status)) {
+ /*
+ * we're not supposed to do any io
+ */
+ return NT_STATUS_OK;
}
- if (!state->hdr.done) {
+ if (state->req != NULL) {
/*
- * first we need to get the NBT header
+ * if there is already a tstream_readv_pdu
+ * pending, we are done.
*/
- vector = talloc_array(mem_ctx, struct iovec, 1);
- if (vector == NULL) {
- return -1;
- }
+ return NT_STATUS_OK;
+ }
- vector[0].iov_base = (void *)state->hdr.nbt;
- vector[0].iov_len = NBT_HDR_SIZE;
+ max_send_queue_len = MAX(1, xconn->smb2.credits.max/16);
+ cur_send_queue_len = xconn->smb2.send_queue_len;
- *_vector = vector;
- *_count = 1;
+ if (cur_send_queue_len > max_send_queue_len) {
+ /*
+ * if we have a lot of requests to send,
+ * we wait until they are on the wire until we
+ * ask for the next request.
+ */
+ return NT_STATUS_OK;
+ }
- state->hdr.done = true;
- return 0;
+ /* ask for the next request */
+ ZERO_STRUCTP(state);
+ state->req = smbd_smb2_request_allocate(xconn);
+ if (state->req == NULL) {
+ return NT_STATUS_NO_MEMORY;
}
+ state->req->sconn = sconn;
+ state->req->xconn = xconn;
+ state->min_recv_size = lp_min_receive_file_size();
- /*
- * Now we analyze the NBT header
- */
- state->pktlen = smb2_len(state->hdr.nbt);
+ TEVENT_FD_READABLE(xconn->transport.fde);
- if (state->pktlen == 0) {
- /* if there're no remaining bytes, we're done */
- *_vector = NULL;
- *_count = 0;
- return 0;
+ return NT_STATUS_OK;
+}
+
+void smbd_smb2_first_negprot(struct smbXsrv_connection *xconn,
+ const uint8_t *inpdu, size_t size)
+{
+ struct smbd_server_connection *sconn = xconn->client->sconn;
+ NTSTATUS status;
+ struct smbd_smb2_request *req = NULL;
+
+ DEBUG(10,("smbd_smb2_first_negprot: packet length %u\n",
+ (unsigned int)size));
+
+ status = smbd_initialize_smb2(xconn);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbd_server_connection_terminate(xconn, nt_errstr(status));
+ return;
}
- state->pktbuf = talloc_array(state->smb2_req, uint8_t, state->pktlen);
- if (state->pktbuf == NULL) {
- return -1;
+ status = smbd_smb2_request_create(xconn, inpdu, size, &req);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbd_server_connection_terminate(xconn, nt_errstr(status));
+ return;
}
- vector = talloc_array(mem_ctx, struct iovec, 1);
- if (vector == NULL) {
- return -1;
+ status = smbd_smb2_request_validate(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbd_server_connection_terminate(xconn, nt_errstr(status));
+ return;
}
- vector[0].iov_base = (void *)state->pktbuf;
+ status = smbd_smb2_request_setup_out(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbd_server_connection_terminate(xconn, nt_errstr(status));
+ return;
+ }
- if (state->min_recv_size != 0) {
- min_recvfile_size = SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN;
- min_recvfile_size += state->min_recv_size;
+#ifdef WITH_PROFILE
+ /*
+ * this was already counted at the SMB1 layer =>
+ * smbd_smb2_request_dispatch() should not count it twice.
+ */
+ if (profile_p->values.request_stats.count > 0) {
+ profile_p->values.request_stats.count--;
+ }
+#endif
+ status = smbd_smb2_request_dispatch(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbd_server_connection_terminate(xconn, nt_errstr(status));
+ return;
}
- if (state->pktlen > min_recvfile_size) {
- /*
- * Might be a receivefile write. Read the SMB2 HEADER +
- * SMB2_WRITE header first. Set 'doing_receivefile'
- * as we're *attempting* receivefile write. If this
- * turns out not to be a SMB2_WRITE request or otherwise
- * not suitable then we'll just read the rest of the data
- * the next time this function is called.
- */
- vector[0].iov_len = SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN;
- state->doing_receivefile = true;
- } else {
- vector[0].iov_len = state->pktlen;
+ status = smbd_smb2_request_next_incoming(xconn);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbd_server_connection_terminate(xconn, nt_errstr(status));
+ return;
}
- *_vector = vector;
- *_count = 1;
- return 0;
+ sconn->num_requests++;
}
-static void smbd_smb2_request_read_done(struct tevent_req *subreq)
+static int socket_error_from_errno(int ret,
+ int sys_errno,
+ bool *retry)
{
- struct tevent_req *req =
- tevent_req_callback_data(subreq,
- struct tevent_req);
- struct smbd_smb2_request_read_state *state =
- tevent_req_data(req,
- struct smbd_smb2_request_read_state);
- int ret;
- int sys_errno;
- NTSTATUS status;
- NTTIME now;
+ *retry = false;
- ret = tstream_readv_pdu_queue_recv(subreq, &sys_errno);
- TALLOC_FREE(subreq);
- if (ret == -1) {
- status = map_nt_error_from_unix(sys_errno);
- tevent_req_nterror(req, status);
- return;
+ if (ret >= 0) {
+ return 0;
}
- if (state->hdr.nbt[0] != 0x00) {
- DEBUG(1,("smbd_smb2_request_read_done: ignore NBT[0x%02X] msg\n",
- state->hdr.nbt[0]));
+ if (ret != -1) {
+ return EIO;
+ }
- ZERO_STRUCT(state->hdr);
- TALLOC_FREE(state->pktbuf);
- state->pktlen = 0;
-
- subreq = tstream_readv_pdu_queue_send(state->smb2_req,
- state->ev,
- state->sconn->smb2.stream,
- state->sconn->smb2.recv_queue,
- smbd_smb2_request_next_vector,
- state);
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
- tevent_req_set_callback(subreq, smbd_smb2_request_read_done, req);
- return;
+ if (sys_errno == 0) {
+ return EIO;
+ }
+
+ if (sys_errno == EINTR) {
+ *retry = true;
+ return sys_errno;
}
- state->smb2_req->request_time = timeval_current();
- now = timeval_to_nttime(&state->smb2_req->request_time);
+ if (sys_errno == EINPROGRESS) {
+ *retry = true;
+ return sys_errno;
+ }
- status = smbd_smb2_inbuf_parse_compound(state->smb2_req->sconn->conn,
- now,
- state->pktbuf,
- state->pktlen,
- state->smb2_req,
- &state->smb2_req->in.vector,
- &state->smb2_req->in.vector_count);
- if (tevent_req_nterror(req, status)) {
- return;
+ if (sys_errno == EAGAIN) {
+ *retry = true;
+ return sys_errno;
}
- if (state->doing_receivefile) {
- state->smb2_req->smb1req = talloc_zero(state->smb2_req,
- struct smb_request);
- if (tevent_req_nomem(state->smb2_req->smb1req, req)) {
- return;
- }
- state->smb2_req->smb1req->unread_bytes =
- state->pktlen - SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN;
+ /* ENOMEM is retryable on Solaris/illumos, and possibly other systems. */
+ if (sys_errno == ENOMEM) {
+ *retry = true;
+ return sys_errno;
}
- state->smb2_req->current_idx = 1;
+#ifdef EWOULDBLOCK
+#if EWOULDBLOCK != EAGAIN
+ if (sys_errno == EWOULDBLOCK) {
+ *retry = true;
+ return sys_errno;
+ }
+#endif
+#endif
- tevent_req_done(req);
+ return sys_errno;
}
-static NTSTATUS smbd_smb2_request_read_recv(struct tevent_req *req,
- TALLOC_CTX *mem_ctx,
- struct smbd_smb2_request **_smb2_req)
+static NTSTATUS smbd_smb2_flush_send_queue(struct smbXsrv_connection *xconn)
{
- struct smbd_smb2_request_read_state *state =
- tevent_req_data(req,
- struct smbd_smb2_request_read_state);
- NTSTATUS status;
+ int ret;
+ int err;
+ bool retry;
- if (tevent_req_is_nterror(req, &status)) {
- tevent_req_received(req);
- return status;
+ if (xconn->smb2.send_queue == NULL) {
+ TEVENT_FD_NOT_WRITEABLE(xconn->transport.fde);
+ return NT_STATUS_OK;
+ }
+
+ while (xconn->smb2.send_queue != NULL) {
+ struct smbd_smb2_send_queue *e = xconn->smb2.send_queue;
+ bool ok;
+
+ if (e->sendfile_header != NULL) {
+ NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+ size_t size = 0;
+ size_t i = 0;
+ uint8_t *buf;
+
+ for (i=0; i < e->count; i++) {
+ size += e->vector[i].iov_len;
+ }
+
+ if (size <= e->sendfile_header->length) {
+ buf = e->sendfile_header->data;
+ } else {
+ buf = talloc_array(e->mem_ctx, uint8_t, size);
+ if (buf == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ size = 0;
+ for (i=0; i < e->count; i++) {
+ memcpy(buf+size,
+ e->vector[i].iov_base,
+ e->vector[i].iov_len);
+ size += e->vector[i].iov_len;
+ }
+
+ e->sendfile_header->data = buf;
+ e->sendfile_header->length = size;
+ e->sendfile_status = &status;
+ e->count = 0;
+
+ xconn->smb2.send_queue_len--;
+ DLIST_REMOVE(xconn->smb2.send_queue, e);
+ /*
+ * This triggers the sendfile path via
+ * the destructor.
+ */
+ talloc_free(e->mem_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ continue;
+ }
+
+ ret = writev(xconn->transport.sock, e->vector, e->count);
+ if (ret == 0) {
+ /* propagate end of file */
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ err = socket_error_from_errno(ret, errno, &retry);
+ if (retry) {
+ /* retry later */
+ TEVENT_FD_WRITEABLE(xconn->transport.fde);
+ return NT_STATUS_OK;
+ }
+ if (err != 0) {
+ return map_nt_error_from_unix_common(err);
+ }
+
+ ok = iov_advance(&e->vector, &e->count, ret);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (e->count > 0) {
+ /* we have more to write */
+ TEVENT_FD_WRITEABLE(xconn->transport.fde);
+ return NT_STATUS_OK;
+ }
+
+ xconn->smb2.send_queue_len--;
+ DLIST_REMOVE(xconn->smb2.send_queue, e);
+ talloc_free(e->mem_ctx);
}
- *_smb2_req = talloc_move(mem_ctx, &state->smb2_req);
- tevent_req_received(req);
return NT_STATUS_OK;
}
-static void smbd_smb2_request_incoming(struct tevent_req *subreq);
-
-static NTSTATUS smbd_smb2_request_next_incoming(struct smbd_server_connection *sconn)
+static NTSTATUS smbd_smb2_io_handler(struct smbXsrv_connection *xconn,
+ uint16_t fde_flags)
{
- size_t max_send_queue_len;
- size_t cur_send_queue_len;
- struct tevent_req *subreq;
+ struct smbd_server_connection *sconn = xconn->client->sconn;
+ struct smbd_smb2_request_read_state *state = &xconn->smb2.request_read_state;
+ struct smbd_smb2_request *req = NULL;
+ size_t min_recvfile_size = UINT32_MAX;
+ int ret;
+ int err;
+ bool retry;
+ NTSTATUS status;
+ NTTIME now;
- if (tevent_queue_length(sconn->smb2.recv_queue) > 0) {
+ if (!NT_STATUS_IS_OK(xconn->transport.status)) {
/*
- * if there is already a smbd_smb2_request_read
- * pending, we are done.
+ * we're not supposed to do any io
*/
+ TEVENT_FD_NOT_READABLE(xconn->transport.fde);
+ TEVENT_FD_NOT_WRITEABLE(xconn->transport.fde);
return NT_STATUS_OK;
}
- max_send_queue_len = MAX(1, sconn->smb2.max_credits/16);
- cur_send_queue_len = tevent_queue_length(sconn->smb2.send_queue);
+ if (fde_flags & TEVENT_FD_WRITE) {
+ status = smbd_smb2_flush_send_queue(xconn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
- if (cur_send_queue_len > max_send_queue_len) {
- /*
- * if we have a lot of requests to send,
- * we wait until they are on the wire until we
- * ask for the next request.
- */
+ if (!(fde_flags & TEVENT_FD_READ)) {
return NT_STATUS_OK;
}
- /* ask for the next request */
- subreq = smbd_smb2_request_read_send(sconn, sconn->ev_ctx, sconn);
- if (subreq == NULL) {
- return NT_STATUS_NO_MEMORY;
+ if (state->req == NULL) {
+ TEVENT_FD_NOT_READABLE(xconn->transport.fde);
+ return NT_STATUS_OK;
}
- tevent_req_set_callback(subreq, smbd_smb2_request_incoming, sconn);
- return NT_STATUS_OK;
-}
+again:
+ if (!state->hdr.done) {
+ state->hdr.done = true;
-void smbd_smb2_first_negprot(struct smbd_server_connection *sconn,
- uint8_t *inbuf, size_t size)
-{
- NTSTATUS status;
- struct smbd_smb2_request *req = NULL;
+ state->vector.iov_base = (void *)state->hdr.nbt;
+ state->vector.iov_len = NBT_HDR_SIZE;
+ }
- DEBUG(10,("smbd_smb2_first_negprot: packet length %u\n",
- (unsigned int)size));
+ ret = readv(xconn->transport.sock, &state->vector, 1);
+ if (ret == 0) {
+ /* propagate end of file */
+ return NT_STATUS_END_OF_FILE;
+ }
+ err = socket_error_from_errno(ret, errno, &retry);
+ if (retry) {
+ /* retry later */
+ TEVENT_FD_READABLE(xconn->transport.fde);
+ return NT_STATUS_OK;
+ }
+ if (err != 0) {
+ return map_nt_error_from_unix_common(err);
+ }
- status = smbd_initialize_smb2(sconn);
- if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
+ if (ret < state->vector.iov_len) {
+ uint8_t *base;
+ base = (uint8_t *)state->vector.iov_base;
+ base += ret;
+ state->vector.iov_base = (void *)base;
+ state->vector.iov_len -= ret;
+ /* we have more to read */
+ TEVENT_FD_READABLE(xconn->transport.fde);
+ return NT_STATUS_OK;
}
- status = smbd_smb2_request_create(sconn, inbuf, size, &req);
- if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
+ if (state->pktlen > 0) {
+ if (state->doing_receivefile && !is_smb2_recvfile_write(state)) {
+ /*
+ * Not a possible receivefile write.
+ * Read the rest of the data.
+ */
+ state->doing_receivefile = false;
+
+ state->pktbuf = talloc_realloc(state->req,
+ state->pktbuf,
+ uint8_t,
+ state->pktfull);
+ if (state->pktbuf == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->vector.iov_base = (void *)(state->pktbuf +
+ state->pktlen);
+ state->vector.iov_len = (state->pktfull -
+ state->pktlen);
+
+ state->pktlen = state->pktfull;
+ goto again;
+ }
+
+ /*
+ * Either this is a receivefile write so we've
+ * done a short read, or if not we have all the data.
+ */
+ goto got_full;
}
- status = smbd_smb2_request_validate(req);
- if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
+ /*
+ * Now we analyze the NBT header
+ */
+ if (state->hdr.nbt[0] != 0x00) {
+ state->min_recv_size = 0;
+ }
+ state->pktfull = smb2_len(state->hdr.nbt);
+ if (state->pktfull == 0) {
+ goto got_full;
}
- status = smbd_smb2_request_setup_out(req);
- if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
+ if (state->min_recv_size != 0) {
+ min_recvfile_size = SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN;
+ min_recvfile_size += state->min_recv_size;
}
- status = smbd_smb2_request_dispatch(req);
- if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
+ if (state->pktfull > min_recvfile_size) {
+ /*
+ * Might be a receivefile write. Read the SMB2 HEADER +
+ * SMB2_WRITE header first. Set 'doing_receivefile'
+ * as we're *attempting* receivefile write. If this
+ * turns out not to be a SMB2_WRITE request or otherwise
+ * not suitable then we'll just read the rest of the data
+ * the next time this function is called.
+ */
+ state->pktlen = SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN;
+ state->doing_receivefile = true;
+ } else {
+ state->pktlen = state->pktfull;
}
- status = smbd_smb2_request_next_incoming(sconn);
- if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
+ state->pktbuf = talloc_array(state->req, uint8_t, state->pktlen);
+ if (state->pktbuf == NULL) {
+ return NT_STATUS_NO_MEMORY;
}
- sconn->num_requests++;
-}
+ state->vector.iov_base = (void *)state->pktbuf;
+ state->vector.iov_len = state->pktlen;
-static void smbd_smb2_request_incoming(struct tevent_req *subreq)
-{
- struct smbd_server_connection *sconn = tevent_req_callback_data(subreq,
- struct smbd_server_connection);
- NTSTATUS status;
- struct smbd_smb2_request *req = NULL;
+ goto again;
+
+got_full:
+
+ if (state->hdr.nbt[0] != 0x00) {
+ DEBUG(1,("ignore NBT[0x%02X] msg\n",
+ state->hdr.nbt[0]));
+
+ req = state->req;
+ ZERO_STRUCTP(state);
+ state->req = req;
+ state->min_recv_size = lp_min_receive_file_size();
+ req = NULL;
+ goto again;
+ }
+
+ req = state->req;
+ state->req = NULL;
- status = smbd_smb2_request_read_recv(subreq, sconn, &req);
- TALLOC_FREE(subreq);
+ req->request_time = timeval_current();
+ now = timeval_to_nttime(&req->request_time);
+
+ status = smbd_smb2_inbuf_parse_compound(xconn,
+ now,
+ state->pktbuf,
+ state->pktlen,
+ req,
+ &req->in.vector,
+ &req->in.vector_count);
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(2,("smbd_smb2_request_incoming: client read error %s\n",
- nt_errstr(status)));
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
+ return status;
}
- DEBUG(10,("smbd_smb2_request_incoming: idx[%d] of %d vectors\n",
+ if (state->doing_receivefile) {
+ req->smb1req = talloc_zero(req, struct smb_request);
+ if (req->smb1req == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ req->smb1req->unread_bytes = state->pktfull - state->pktlen;
+ }
+
+ ZERO_STRUCTP(state);
+
+ req->current_idx = 1;
+
+ DEBUG(10,("smbd_smb2_request idx[%d] of %d vectors\n",
req->current_idx, req->in.vector_count));
status = smbd_smb2_request_validate(req);
if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
+ return status;
}
status = smbd_smb2_request_setup_out(req);
if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
+ return status;
}
status = smbd_smb2_request_dispatch(req);
if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
- }
-
- status = smbd_smb2_request_next_incoming(sconn);
- if (!NT_STATUS_IS_OK(status)) {
- smbd_server_connection_terminate(sconn, nt_errstr(status));
- return;
+ return status;
}
sconn->num_requests++;
change_to_root_user();
check_log_size();
}
+
+ status = smbd_smb2_request_next_incoming(xconn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void smbd_smb2_connection_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct smbXsrv_connection *xconn =
+ talloc_get_type_abort(private_data,
+ struct smbXsrv_connection);
+ NTSTATUS status;
+
+ status = smbd_smb2_io_handler(xconn, flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbd_server_connection_terminate(xconn, nt_errstr(status));
+ return;
+ }
}