s3:smb2_server: make sure we preferr responses over requests on the client socket
[metze/samba/wip.git] / source3 / smbd / smb2_server.c
index 924e41fa484079e7b11d4ce45e696033f60803ce..3b07f74487a7c071f93380a376a1634443e9b8d2 100644 (file)
 */
 
 #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();
+       }
 }