X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fsmbd%2Fsmb2_server.c;h=3b07f74487a7c071f93380a376a1634443e9b8d2;hb=286a57cdfc76d02b36f3fb41a24c9e9e3b6c6f09;hp=924e41fa484079e7b11d4ce45e696033f60803ce;hpb=a8eed184a0f9e5fdeec9a40d8ffbc2f3d56beb74;p=metze%2Fsamba%2Fwip.git diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 924e41fa4840..3b07f74487a7 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -20,9 +20,15 @@ */ #include "includes.h" +#include "smbd/smbd.h" #include "smbd/globals.h" #include "../libcli/smb/smb_common.h" #include "../lib/tsocket/tsocket.h" +#include "../lib/util/tevent_ntstatus.h" +#include "smbprofile.h" +#include "../lib/util/bitmap.h" +#include "../librpc/gen_ndr/krb5pac.h" +#include "auth.h" #define OUTVEC_ALLOC_SIZE (SMB2_HDR_BODY + 9) @@ -92,7 +98,7 @@ static NTSTATUS smbd_initialize_smb2(struct smbd_server_connection *sconn) TALLOC_FREE(sconn->smb1.fde); - sconn->smb2.event_ctx = smbd_event_context(); + sconn->smb2.event_ctx = server_event_context(); sconn->smb2.recv_queue = tevent_queue_create(sconn, "smb2 recv queue"); if (sconn->smb2.recv_queue == NULL) { @@ -113,7 +119,8 @@ static NTSTATUS smbd_initialize_smb2(struct smbd_server_connection *sconn) sconn->smb2.seqnum_low = 0; sconn->smb2.credits_granted = 0; sconn->smb2.max_credits = lp_smb2_max_credits(); - sconn->smb2.credits_bitmap = bitmap_talloc(sconn, 2*sconn->smb2.max_credits); + sconn->smb2.credits_bitmap = bitmap_talloc(sconn, + DEFAULT_SMB2_MAX_CREDIT_BITMAP_FACTOR*sconn->smb2.max_credits); if (sconn->smb2.credits_bitmap == NULL) { return NT_STATUS_NO_MEMORY; } @@ -265,15 +272,15 @@ static NTSTATUS smbd_smb2_request_create(struct smbd_server_connection *sconn, memcpy(req->in.nbt_hdr, inbuf, 4); ofs = 0; - req->in.vector[0].iov_base = (void *)req->in.nbt_hdr; + req->in.vector[0].iov_base = discard_const_p(void, req->in.nbt_hdr); req->in.vector[0].iov_len = 4; ofs += req->in.vector[0].iov_len; - req->in.vector[1].iov_base = (void *)(inbuf + ofs); + req->in.vector[1].iov_base = discard_const_p(void, (inbuf + ofs)); req->in.vector[1].iov_len = SMB2_HDR_BODY; ofs += req->in.vector[1].iov_len; - req->in.vector[2].iov_base = (void *)(inbuf + ofs); + req->in.vector[2].iov_base = discard_const_p(void, (inbuf + ofs)); req->in.vector[2].iov_len = SVAL(inbuf, ofs) & 0xFFFE; ofs += req->in.vector[2].iov_len; @@ -281,7 +288,7 @@ static NTSTATUS smbd_smb2_request_create(struct smbd_server_connection *sconn, return NT_STATUS_INVALID_PARAMETER; } - req->in.vector[3].iov_base = (void *)(inbuf + ofs); + req->in.vector[3].iov_base = discard_const_p(void, (inbuf + ofs)); req->in.vector[3].iov_len = size - ofs; ofs += req->in.vector[3].iov_len; @@ -306,12 +313,12 @@ static bool smb2_validate_message_id(struct smbd_server_connection *sconn, if (message_id < sconn->smb2.seqnum_low || message_id > (sconn->smb2.seqnum_low + - (2*sconn->smb2.credits_granted))) { + (sconn->smb2.max_credits * DEFAULT_SMB2_MAX_CREDIT_BITMAP_FACTOR))) { DEBUG(0,("smb2_validate_message_id: bad message_id " - "%llu (low = %llu, granted = %lu)\n", + "%llu (low = %llu, max = %lu)\n", (unsigned long long)message_id, (unsigned long long)sconn->smb2.seqnum_low, - (unsigned long)sconn->smb2.credits_granted )); + (unsigned long)sconn->smb2.max_credits )); return false; } @@ -321,7 +328,7 @@ static bool smb2_validate_message_id(struct smbd_server_connection *sconn, /* Mark the message_id as seen in the bitmap. */ bitmap_offset = (unsigned int)(message_id % - (uint64_t)(sconn->smb2.max_credits * 2)); + (uint64_t)(sconn->smb2.max_credits * DEFAULT_SMB2_MAX_CREDIT_BITMAP_FACTOR)); if (bitmap_query(credits_bm, bitmap_offset)) { DEBUG(0,("smb2_validate_message_id: duplicate message_id " "%llu (bm offset %u)\n", @@ -342,7 +349,7 @@ static bool smb2_validate_message_id(struct smbd_server_connection *sconn, bitmap_clear(credits_bm, bitmap_offset); sconn->smb2.seqnum_low += 1; bitmap_offset = (bitmap_offset + 1) % - (sconn->smb2.max_credits * 2); + (sconn->smb2.max_credits * DEFAULT_SMB2_MAX_CREDIT_BITMAP_FACTOR); } } @@ -437,7 +444,7 @@ static void smb2_set_operation_credit(struct smbd_server_connection *sconn, const struct iovec *in_vector, struct iovec *out_vector) { - uint8_t *outhdr = out_vector->iov_base; + uint8_t *outhdr = (uint8_t *)out_vector->iov_base; uint16_t credits_requested = 0; uint16_t credits_granted = 0; @@ -448,9 +455,30 @@ static void smb2_set_operation_credit(struct smbd_server_connection *sconn, SMB_ASSERT(sconn->smb2.max_credits >= sconn->smb2.credits_granted); - /* Remember what we gave out. */ - credits_granted = MIN(credits_requested, (sconn->smb2.max_credits - - sconn->smb2.credits_granted)); + if (credits_requested) { + uint16_t modified_credits_requested; + uint32_t multiplier; + + /* + * Split up max_credits into 1/16ths, and then scale + * the requested credits by how many 16ths have been + * currently granted. Less than 1/16th == grant all + * requested (100%), scale down as more have been + * granted. Never ask for less than 1 as the client + * asked for at least 1. JRA. + */ + + multiplier = 16 - (((sconn->smb2.credits_granted * 16) / sconn->smb2.max_credits) % 16); + + modified_credits_requested = (multiplier * credits_requested) / 16; + if (modified_credits_requested == 0) { + modified_credits_requested = 1; + } + + /* Remember what we gave out. */ + credits_granted = MIN(modified_credits_requested, + (sconn->smb2.max_credits - sconn->smb2.credits_granted)); + } if (credits_granted == 0 && sconn->smb2.credits_granted == 0) { /* First negprot packet, or ensure the client credits can @@ -604,7 +632,7 @@ static bool dup_smb2_vec3(TALLOC_CTX *ctx, srcvec[1].iov_base == ((uint8_t *)srcvec[0].iov_base) + SMB2_HDR_BODY) { - outvec[1].iov_base = ((uint8_t *)outvec[1].iov_base) + + outvec[1].iov_base = ((uint8_t *)outvec[0].iov_base) + SMB2_HDR_BODY; outvec[1].iov_len = 8; } else { @@ -661,10 +689,18 @@ static struct smbd_smb2_request *dup_smb2_req(const struct smbd_smb2_request *re } newreq->sconn = req->sconn; + newreq->session = req->session; newreq->do_signing = req->do_signing; newreq->current_idx = req->current_idx; newreq->async = false; newreq->cancelled = false; + /* Note we are leaving: + ->tcon + ->smb1req + ->compat_chain_fsp + uninitialized as NULL here as + they're not used in the interim + response code. JRA. */ outvec = talloc_zero_array(newreq, struct iovec, count); if (!outvec) { @@ -911,7 +947,7 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, if (req->do_signing) { status = smb2_signing_sign_pdu(req->session->session_key, - state->vector, 3); + &state->vector[1], 2); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -1059,6 +1095,134 @@ static NTSTATUS smbd_smb2_request_process_cancel(struct smbd_smb2_request *req) return NT_STATUS_OK; } +/************************************************************* + Ensure an incoming tid is a valid one for us to access. + Change to the associated uid credentials and chdir to the + valid tid directory. +*************************************************************/ + +static NTSTATUS smbd_smb2_request_check_tcon(struct smbd_smb2_request *req) +{ + const uint8_t *inhdr; + const uint8_t *outhdr; + int i = req->current_idx; + uint32_t in_tid; + void *p; + struct smbd_smb2_tcon *tcon; + bool chained_fixup = false; + + inhdr = (const uint8_t *)req->in.vector[i+0].iov_base; + + in_tid = IVAL(inhdr, SMB2_HDR_TID); + + if (in_tid == (0xFFFFFFFF)) { + if (req->async) { + /* + * async request - fill in tid from + * already setup out.vector[].iov_base. + */ + outhdr = (const uint8_t *)req->out.vector[i].iov_base; + in_tid = IVAL(outhdr, SMB2_HDR_TID); + } else if (i > 2) { + /* + * Chained request - fill in tid from + * the previous request out.vector[].iov_base. + */ + outhdr = (const uint8_t *)req->out.vector[i-3].iov_base; + in_tid = IVAL(outhdr, SMB2_HDR_TID); + chained_fixup = true; + } + } + + /* lookup an existing session */ + p = idr_find(req->session->tcons.idtree, in_tid); + if (p == NULL) { + return NT_STATUS_NETWORK_NAME_DELETED; + } + tcon = talloc_get_type_abort(p, struct smbd_smb2_tcon); + + if (!change_to_user(tcon->compat_conn,req->session->vuid)) { + return NT_STATUS_ACCESS_DENIED; + } + + /* should we pass FLAG_CASELESS_PATHNAMES here? */ + if (!set_current_service(tcon->compat_conn, 0, true)) { + return NT_STATUS_ACCESS_DENIED; + } + + req->tcon = tcon; + + if (chained_fixup) { + /* Fix up our own outhdr. */ + outhdr = (const uint8_t *)req->out.vector[i].iov_base; + SIVAL(discard_const_p(uint8_t, outhdr), SMB2_HDR_TID, in_tid); + } + + return NT_STATUS_OK; +} + +/************************************************************* + Ensure an incoming session_id is a valid one for us to access. +*************************************************************/ + +static NTSTATUS smbd_smb2_request_check_session(struct smbd_smb2_request *req) +{ + const uint8_t *inhdr; + const uint8_t *outhdr; + int i = req->current_idx; + uint64_t in_session_id; + void *p; + struct smbd_smb2_session *session; + bool chained_fixup = false; + + inhdr = (const uint8_t *)req->in.vector[i+0].iov_base; + + in_session_id = BVAL(inhdr, SMB2_HDR_SESSION_ID); + + if (in_session_id == (0xFFFFFFFFFFFFFFFFLL)) { + if (req->async) { + /* + * async request - fill in session_id from + * already setup request out.vector[].iov_base. + */ + outhdr = (const uint8_t *)req->out.vector[i].iov_base; + in_session_id = BVAL(outhdr, SMB2_HDR_SESSION_ID); + } else if (i > 2) { + /* + * Chained request - fill in session_id from + * the previous request out.vector[].iov_base. + */ + outhdr = (const uint8_t *)req->out.vector[i-3].iov_base; + in_session_id = BVAL(outhdr, SMB2_HDR_SESSION_ID); + chained_fixup = true; + } + } + + /* lookup an existing session */ + p = idr_find(req->sconn->smb2.sessions.idtree, in_session_id); + if (p == NULL) { + return NT_STATUS_USER_SESSION_DELETED; + } + session = talloc_get_type_abort(p, struct smbd_smb2_session); + + if (!NT_STATUS_IS_OK(session->status)) { + return NT_STATUS_ACCESS_DENIED; + } + + set_current_user_info(session->session_info->unix_info->sanitized_username, + session->session_info->unix_info->unix_name, + session->session_info->info->domain_name); + + req->session = session; + + if (chained_fixup) { + /* Fix up our own outhdr. */ + outhdr = (const uint8_t *)req->out.vector[i].iov_base; + SBVAL(discard_const_p(uint8_t, outhdr), SMB2_HDR_SESSION_ID, in_session_id); + } + return NT_STATUS_OK; +} + NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) { const uint8_t *inhdr; @@ -1092,6 +1256,14 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } + /* + * Check if the client provided a valid session id, + * if so smbd_smb2_request_check_session() calls + * set_current_user_info(). + * + * As some command don't require a valid session id + * we defer the check of the session_status + */ session_status = smbd_smb2_request_check_session(req); req->do_signing = false; @@ -1127,6 +1299,9 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) switch (opcode) { case SMB2_OP_NEGPROT: + /* This call needs to be run as root */ + change_to_root_user(); + { START_PROFILE(smb2_negprot); return_value = smbd_smb2_request_process_negprot(req); @@ -1135,6 +1310,9 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) break; case SMB2_OP_SESSSETUP: + /* This call needs to be run as root */ + change_to_root_user(); + { START_PROFILE(smb2_sesssetup); return_value = smbd_smb2_request_process_sesssetup(req); @@ -1148,6 +1326,9 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) break; } + /* This call needs to be run as root */ + change_to_root_user(); + { START_PROFILE(smb2_logoff); return_value = smbd_smb2_request_process_logoff(req); @@ -1160,11 +1341,15 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } - status = smbd_smb2_request_check_session(req); - if (!NT_STATUS_IS_OK(status)) { - return_value = smbd_smb2_request_error(req, status); - break; - } + + /* + * This call needs to be run as root. + * + * smbd_smb2_request_process_tcon() + * calls make_connection_snum(), which will call + * change_to_user(), when needed. + */ + change_to_root_user(); { START_PROFILE(smb2_tcon); @@ -1178,11 +1363,20 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); break; } + /* This call needs to be run as root */ + change_to_root_user(); + { START_PROFILE(smb2_tdis); @@ -1196,6 +1390,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); @@ -1214,6 +1414,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); @@ -1232,6 +1438,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); @@ -1250,6 +1462,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); @@ -1268,6 +1486,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); @@ -1290,6 +1514,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { /* Too ugly to live ? JRA. */ @@ -1312,6 +1542,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); @@ -1326,6 +1562,13 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) break; case SMB2_OP_CANCEL: + /* + * This call needs to be run as root + * + * That is what we also do in the SMB1 case. + */ + change_to_root_user(); + { START_PROFILE(smb2_cancel); return_value = smbd_smb2_request_process_cancel(req); @@ -1334,9 +1577,14 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) break; case SMB2_OP_KEEPALIVE: - {START_PROFILE(smb2_keepalive); - return_value = smbd_smb2_request_process_keepalive(req); - END_PROFILE(smb2_keepalive);} + /* This call needs to be run as root */ + change_to_root_user(); + + { + START_PROFILE(smb2_keepalive); + return_value = smbd_smb2_request_process_keepalive(req); + END_PROFILE(smb2_keepalive); + } break; case SMB2_OP_FIND: @@ -1344,6 +1592,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); @@ -1362,6 +1616,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); @@ -1380,6 +1640,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); @@ -1398,6 +1664,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); @@ -1416,6 +1688,12 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) return_value = smbd_smb2_request_error(req, session_status); break; } + /* + * This call needs to be run as user. + * + * smbd_smb2_request_check_tcon() + * calls change_to_user() on success. + */ status = smbd_smb2_request_check_tcon(req); if (!NT_STATUS_IS_OK(status)) { return_value = smbd_smb2_request_error(req, status); @@ -1443,23 +1721,6 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req) req->subreq = NULL; - smb2_setup_nbt_length(req->out.vector, req->out.vector_count); - - /* Set credit for this operation (zero credits if this - is a final reply for an async operation). */ - smb2_set_operation_credit(req->sconn, - req->async ? NULL : &req->in.vector[i], - &req->out.vector[i]); - - if (req->do_signing) { - NTSTATUS status; - status = smb2_signing_sign_pdu(req->session->session_key, - &req->out.vector[i], 3); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - } - req->current_idx += 3; if (req->current_idx < req->out.vector_count) { @@ -1482,6 +1743,23 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req) return NT_STATUS_OK; } + smb2_setup_nbt_length(req->out.vector, req->out.vector_count); + + /* Set credit for this operation (zero credits if this + is a final reply for an async operation). */ + smb2_set_operation_credit(req->sconn, + req->async ? NULL : &req->in.vector[i], + &req->out.vector[i]); + + if (req->do_signing) { + NTSTATUS status; + status = smb2_signing_sign_pdu(req->session->session_key, + &req->out.vector[i], 3); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + if (DEBUGLEVEL >= 10) { dbgtext("smbd_smb2_request_reply: sending...\n"); print_req_vectors(req); @@ -1539,6 +1817,8 @@ void smbd_smb2_request_dispatch_immediate(struct tevent_context *ctx, } } +static NTSTATUS smbd_smb2_request_next_incoming(struct smbd_server_connection *sconn); + static void smbd_smb2_request_writev_done(struct tevent_req *subreq) { struct smbd_smb2_request *req = tevent_req_callback_data(subreq, @@ -1546,17 +1826,24 @@ static void smbd_smb2_request_writev_done(struct tevent_req *subreq) 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) { - NTSTATUS status = map_nt_error_from_unix(sys_errno); + 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)); return; } + + status = smbd_smb2_request_next_incoming(sconn); + if (!NT_STATUS_IS_OK(status)) { + smbd_server_connection_terminate(sconn, nt_errstr(status)); + return; + } } NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req, @@ -2014,9 +2301,11 @@ static int smbd_smb2_request_next_vector(struct tstream_context *stream, invalid = true; } - if ((body_size % 2) != 0) { - body_size -= 1; - } + /* + * Mask out the lowest bit, the "dynamic" part + * of body_size. + */ + body_size &= ~1; if (body_size > (full_size - SMB2_HDR_BODY)) { /* @@ -2161,20 +2450,47 @@ static NTSTATUS smbd_smb2_request_read_recv(struct tevent_req *req, static void smbd_smb2_request_incoming(struct tevent_req *subreq); +static NTSTATUS smbd_smb2_request_next_incoming(struct smbd_server_connection *sconn) +{ + size_t max_send_queue_len; + size_t cur_send_queue_len; + struct tevent_req *subreq; + + if (tevent_queue_length(sconn->smb2.recv_queue) > 0) { + /* + * if there is already a smbd_smb2_request_read + * pending, we are done. + */ + 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 (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; + } + + /* ask for the next request */ + subreq = smbd_smb2_request_read_send(sconn, sconn->smb2.event_ctx, sconn); + if (subreq == NULL) { + return NT_STATUS_NO_MEMORY; + } + tevent_req_set_callback(subreq, smbd_smb2_request_incoming, sconn); + + return NT_STATUS_OK; +} + void smbd_smb2_first_negprot(struct smbd_server_connection *sconn, const uint8_t *inbuf, size_t size) { NTSTATUS status; struct smbd_smb2_request *req = NULL; - struct tevent_req *subreq; - - if (lp_security() == SEC_SHARE) { - DEBUG(2,("WARNING!!: \"security = share\" is deprecated for " - "SMB2 servers. Mapping to \"security = user\" and " - "\"map to guest = Bad User\"\n" )); - lp_do_parameter(-1, "security", "user"); - lp_do_parameter(-1, "map to guest", "Bad User"); - } DEBUG(10,("smbd_smb2_first_negprot: packet length %u\n", (unsigned int)size)); @@ -2203,13 +2519,13 @@ void smbd_smb2_first_negprot(struct smbd_server_connection *sconn, return; } - /* ask for the next request */ - subreq = smbd_smb2_request_read_send(sconn, sconn->smb2.event_ctx, sconn); - if (subreq == NULL) { - smbd_server_connection_terminate(sconn, "no memory for reading"); + status = smbd_smb2_request_next_incoming(sconn); + if (!NT_STATUS_IS_OK(status)) { + smbd_server_connection_terminate(sconn, nt_errstr(status)); return; } - tevent_req_set_callback(subreq, smbd_smb2_request_incoming, sconn); + + sconn->num_requests++; } static void smbd_smb2_request_incoming(struct tevent_req *subreq) @@ -2259,11 +2575,24 @@ static void smbd_smb2_request_incoming(struct tevent_req *subreq) } next: - /* ask for the next request (this constructs the main loop) */ - subreq = smbd_smb2_request_read_send(sconn, sconn->smb2.event_ctx, sconn); - if (subreq == NULL) { - smbd_server_connection_terminate(sconn, "no memory for reading"); + status = smbd_smb2_request_next_incoming(sconn); + if (!NT_STATUS_IS_OK(status)) { + smbd_server_connection_terminate(sconn, nt_errstr(status)); return; } - tevent_req_set_callback(subreq, smbd_smb2_request_incoming, sconn); + + sconn->num_requests++; + + /* The timeout_processing function isn't run nearly + often enough to implement 'max log size' without + overrunning the size of the file by many megabytes. + This is especially true if we are running at debug + level 10. Checking every 50 SMB2s is a nice + tradeoff of performance vs log file size overrun. */ + + if ((sconn->num_requests % 50) == 0 && + need_to_check_log_size()) { + change_to_root_user(); + check_log_size(); + } }