NetApp found that we never set the smb signing flags2.
[kai/samba.git] / source4 / smb_server / smb / request.c
index 4b501ad973a84d71ff267b859be5d902be764277..eb3e100b96c4d3d354381f43e4ad5687a06c2ec4 100644 (file)
@@ -5,7 +5,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 /*
 
 #include "includes.h"
 #include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
 #include "smbd/service_stream.h"
 #include "lib/stream/packet.h"
 #include "ntvfs/ntvfs.h"
+#include "param/param.h"
 
 
 /* we over allocate the data buffer to prevent too many realloc calls */
 #define REQ_OVER_ALLOCATION 0
 
+/* setup the bufinfo used for strings and range checking */
+void smbsrv_setup_bufinfo(struct smbsrv_request *req)
+{
+       req->in.bufinfo.mem_ctx    = req;
+       req->in.bufinfo.flags      = 0;
+       if (req->flags2 & FLAGS2_UNICODE_STRINGS) {
+               req->in.bufinfo.flags |= BUFINFO_FLAG_UNICODE;
+       }
+       req->in.bufinfo.align_base = req->in.buffer;
+       req->in.bufinfo.data       = req->in.data;
+       req->in.bufinfo.data_size  = req->in.data_size;
+}
+
+
+static int smbsrv_request_destructor(struct smbsrv_request *req)
+{
+       DLIST_REMOVE(req->smb_conn->requests, req);
+       return 0;
+}
+
 /****************************************************************************
 construct a basic request packet, mostly used to construct async packets
 such as change notify and oplock break requests
@@ -40,22 +61,15 @@ struct smbsrv_request *smbsrv_init_request(struct smbsrv_connection *smb_conn)
 {
        struct smbsrv_request *req;
 
-       req = talloc(smb_conn, struct smbsrv_request);
+       req = talloc_zero(smb_conn, struct smbsrv_request);
        if (!req) {
                return NULL;
        }
 
-       ZERO_STRUCTP(req);
-
        /* setup the request context */
        req->smb_conn = smb_conn;
 
-       req->async_states = talloc(req, struct ntvfs_async_state);
-       if (!req->async_states) {
-               talloc_free(req);
-               return NULL;
-       }
-       req->async_states->state = 0;
+       talloc_set_destructor(req, smbsrv_request_destructor);
 
        return req;
 }
@@ -98,7 +112,7 @@ static void req_setup_chain_reply(struct smbsrv_request *req, uint_t wct, uint_t
   the caller will then fill in the command words and data before calling req_send_reply() to 
   send the reply on its way
 */
-void smbsrv_setup_reply(struct smbsrv_request *req, uint_t wct, uint_t buflen)
+void smbsrv_setup_reply(struct smbsrv_request *req, uint_t wct, size_t buflen)
 {
        uint16_t flags2;
 
@@ -107,7 +121,7 @@ void smbsrv_setup_reply(struct smbsrv_request *req, uint_t wct, uint_t buflen)
                return;
        }
 
-       req->out.size = NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen;
+       req->out.size = NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + buflen;
 
        /* over allocate by a small amount */
        req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; 
@@ -126,6 +140,10 @@ void smbsrv_setup_reply(struct smbsrv_request *req, uint_t wct, uint_t buflen)
                flags2 |= FLAGS2_32_BIT_ERROR_CODES;
        }
 
+       if (req->smb_conn->signing.allow_smb_signing || req->smb_conn->signing.mandatory_signing) {
+               flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES; 
+       }
+       
        req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
        req->out.vwv = req->out.hdr + HDR_VWV;
        req->out.wct = wct;
@@ -257,11 +275,11 @@ static void req_grow_allocation(struct smbsrv_request *req, uint_t new_size)
   To cope with this req->out.ptr is supplied. This will be updated to
   point at the same offset into the packet as before this call
 */
-void req_grow_data(struct smbsrv_request *req, uint_t new_size)
+void req_grow_data(struct smbsrv_request *req, size_t new_size)
 {
        int delta;
 
-       if (!(req->control_flags & REQ_CONTROL_LARGE) && new_size > req_max_data(req)) {
+       if (!(req->control_flags & SMBSRV_REQ_CONTROL_LARGE) && new_size > req_max_data(req)) {
                smb_panic("reply buffer too large!");
        }
 
@@ -287,6 +305,12 @@ void smbsrv_send_reply_nosign(struct smbsrv_request *req)
        DATA_BLOB blob;
        NTSTATUS status;
 
+       if (req->smb_conn->connection->event.fde == NULL) {
+               /* we are in the process of shutting down this connection */
+               talloc_free(req);
+               return;
+       }
+
        if (req->out.size > NBT_HDR_SIZE) {
                _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
        }
@@ -307,30 +331,16 @@ void smbsrv_send_reply_nosign(struct smbsrv_request *req)
 */
 void smbsrv_send_reply(struct smbsrv_request *req)
 {
+       if (req->smb_conn->connection->event.fde == NULL) {
+               /* we are in the process of shutting down this connection */
+               talloc_free(req);
+               return;
+       }
        smbsrv_sign_packet(req);
 
        smbsrv_send_reply_nosign(req);
 }
 
-
-
-/* 
-   construct and send an error packet with a forced DOS error code
-   this is needed to match win2000 behaviour for some parts of the protocol
-*/
-void smbsrv_send_dos_error(struct smbsrv_request *req, uint8_t eclass, uint16_t ecode)
-{
-       /* if the basic packet hasn't been setup yet then do it now */
-       if (req->out.buffer == NULL) {
-               smbsrv_setup_reply(req, 0, 0);
-       }
-
-       SCVAL(req->out.hdr, HDR_RCLS, eclass);
-       SSVAL(req->out.hdr, HDR_ERR, ecode);
-       SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES);       
-       smbsrv_send_reply(req);
-}
-
 /* 
    setup the header of a reply to include an NTSTATUS code
 */
@@ -387,7 +397,7 @@ void smbsrv_send_error(struct smbsrv_request *req, NTSTATUS status)
 
   if dest_len is -1 then no limit applies
 */
-size_t req_push_str(struct smbsrv_request *req, uint8_t *dest, const char *str, int dest_len, uint_t flags)
+size_t req_push_str(struct smbsrv_request *req, uint8_t *dest, const char *str, int dest_len, size_t flags)
 {
        size_t len;
        uint_t grow_size;
@@ -417,7 +427,7 @@ size_t req_push_str(struct smbsrv_request *req, uint8_t *dest, const char *str,
                dest = req->out.buffer + PTR_DIFF(dest, buf0);
        }
 
-       len = push_string(dest, str, len, flags);
+       len = push_string(lp_iconv_convenience(req->smb_conn->lp_ctx), dest, str, len, flags);
 
        grow_size = len + PTR_DIFF(dest, req->out.data);
 
@@ -456,7 +466,7 @@ size_t req_append_var_block(struct smbsrv_request *req,
        req_grow_data(req, byte_len + 3 + req->out.data_size);
        return byte_len + 3;
 }
-/*
+/**
   pull a UCS2 string from a request packet, returning a talloced unix string
 
   the string length is limited by the 3 things:
@@ -469,13 +479,13 @@ size_t req_append_var_block(struct smbsrv_request *req,
   on failure zero is returned and *dest is set to NULL, otherwise the number
   of bytes consumed in the packet is returned
 */
-static size_t req_pull_ucs2(struct smbsrv_request *req, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
+static size_t req_pull_ucs2(struct request_bufinfo *bufinfo, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
 {
        int src_len, src_len2, alignment=0;
        ssize_t ret;
        char *dest2;
 
-       if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) {
+       if (!(flags & STR_NOALIGN) && ucs2_align(bufinfo->align_base, src, flags)) {
                src++;
                alignment=1;
                if (byte_len != -1) {
@@ -486,7 +496,7 @@ static size_t req_pull_ucs2(struct smbsrv_request *req, const char **dest, const
        if (flags & STR_NO_RANGE_CHECK) {
                src_len = byte_len;
        } else {
-               src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
+               src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data);
                if (byte_len != -1 && src_len > byte_len) {
                        src_len = byte_len;
                }
@@ -499,11 +509,11 @@ static size_t req_pull_ucs2(struct smbsrv_request *req, const char **dest, const
        
        src_len2 = utf16_len_n(src, src_len);
        if (src_len2 == 0) {
-               *dest = talloc_strdup(req, "");
+               *dest = talloc_strdup(bufinfo->mem_ctx, "");
                return src_len2 + alignment;
        }
 
-       ret = convert_string_talloc(req, CH_UTF16, CH_UNIX, src, src_len2, (void **)&dest2);
+       ret = convert_string_talloc(bufinfo->mem_ctx, lp_iconv_convenience(global_loadparm), CH_UTF16, CH_UNIX, src, src_len2, (void **)&dest2);
 
        if (ret == -1) {
                *dest = NULL;
@@ -514,7 +524,7 @@ static size_t req_pull_ucs2(struct smbsrv_request *req, const char **dest, const
        return src_len2 + alignment;
 }
 
-/*
+/**
   pull a ascii string from a request packet, returning a talloced string
 
   the string length is limited by the 3 things:
@@ -527,7 +537,7 @@ static size_t req_pull_ucs2(struct smbsrv_request *req, const char **dest, const
   on failure zero is returned and *dest is set to NULL, otherwise the number
   of bytes consumed in the packet is returned
 */
-static size_t req_pull_ascii(struct smbsrv_request *req, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
+static size_t req_pull_ascii(struct request_bufinfo *bufinfo, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
 {
        int src_len, src_len2;
        ssize_t ret;
@@ -536,7 +546,7 @@ static size_t req_pull_ascii(struct smbsrv_request *req, const char **dest, cons
        if (flags & STR_NO_RANGE_CHECK) {
                src_len = byte_len;
        } else {
-               src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
+               src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data);
                if (src_len < 0) {
                        *dest = NULL;
                        return 0;
@@ -552,7 +562,7 @@ static size_t req_pull_ascii(struct smbsrv_request *req, const char **dest, cons
                src_len2++;
        }
 
-       ret = convert_string_talloc(req, CH_DOS, CH_UNIX, src, src_len2, (void **)&dest2);
+       ret = convert_string_talloc(bufinfo->mem_ctx, lp_iconv_convenience(global_loadparm), CH_DOS, CH_UNIX, src, src_len2, (void **)&dest2);
 
        if (ret == -1) {
                *dest = NULL;
@@ -563,7 +573,7 @@ static size_t req_pull_ascii(struct smbsrv_request *req, const char **dest, cons
        return src_len2;
 }
 
-/*
+/**
   pull a string from a request packet, returning a talloced string
 
   the string length is limited by the 3 things:
@@ -576,18 +586,18 @@ static size_t req_pull_ascii(struct smbsrv_request *req, const char **dest, cons
   on failure zero is returned and *dest is set to NULL, otherwise the number
   of bytes consumed in the packet is returned
 */
-size_t req_pull_string(struct smbsrv_request *req, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
+size_t req_pull_string(struct request_bufinfo *bufinfo, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
 {
        if (!(flags & STR_ASCII) && 
-           (((flags & STR_UNICODE) || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) {
-               return req_pull_ucs2(req, dest, src, byte_len, flags);
+           (((flags & STR_UNICODE) || (bufinfo->flags & BUFINFO_FLAG_UNICODE)))) {
+               return req_pull_ucs2(bufinfo, dest, src, byte_len, flags);
        }
 
-       return req_pull_ascii(req, dest, src, byte_len, flags);
+       return req_pull_ascii(bufinfo, dest, src, byte_len, flags);
 }
 
 
-/*
+/**
   pull a ASCII4 string buffer from a request packet, returning a talloced string
   
   an ASCII4 buffer is a null terminated string that has a prefix
@@ -596,13 +606,13 @@ size_t req_pull_string(struct smbsrv_request *req, const char **dest, const uint
   on failure *dest is set to the zero length string. This seems to
   match win2000 behaviour
 */
-size_t req_pull_ascii4(struct smbsrv_request *req, const char **dest, const uint8_t *src, uint_t flags)
+size_t req_pull_ascii4(struct request_bufinfo *bufinfo, const char **dest, const uint8_t *src, uint_t flags)
 {
        ssize_t ret;
 
-       if (PTR_DIFF(src, req->in.data) + 1 > req->in.data_size) {
+       if (PTR_DIFF(src, bufinfo->data) + 1 > bufinfo->data_size) {
                /* win2000 treats this as the empty string! */
-               (*dest) = talloc_strdup(req, "");
+               (*dest) = talloc_strdup(bufinfo->mem_ctx, "");
                return 0;
        }
 
@@ -611,57 +621,158 @@ size_t req_pull_ascii4(struct smbsrv_request *req, const char **dest, const uint
           behaviour */
        src++;
 
-       ret = req_pull_string(req, dest, src, -1, flags);
+       ret = req_pull_string(bufinfo, dest, src, -1, flags);
        if (ret == -1) {
-               (*dest) = talloc_strdup(req, "");
+               (*dest) = talloc_strdup(bufinfo->mem_ctx, "");
                return 1;
        }
        
        return ret + 1;
 }
 
-/*
+/**
   pull a DATA_BLOB from a request packet, returning a talloced blob
 
-  return False if any part is outside the data portion of the packet
+  return false if any part is outside the data portion of the packet
 */
-BOOL req_pull_blob(struct smbsrv_request *req, const uint8_t *src, int len, DATA_BLOB *blob)
+bool req_pull_blob(struct request_bufinfo *bufinfo, const uint8_t *src, int len, DATA_BLOB *blob)
 {
-       if (len != 0 && req_data_oob(req, src, len)) {
-               return False;
+       if (len != 0 && req_data_oob(bufinfo, src, len)) {
+               return false;
        }
 
-       (*blob) = data_blob_talloc(req, src, len);
+       (*blob) = data_blob_talloc(bufinfo->mem_ctx, src, len);
 
-       return True;
+       return true;
 }
 
 /* check that a lump of data in a request is within the bounds of the data section of
    the packet */
-BOOL req_data_oob(struct smbsrv_request *req, const uint8_t *ptr, uint32_t count)
+bool req_data_oob(struct request_bufinfo *bufinfo, const uint8_t *ptr, uint32_t count)
 {
        if (count == 0) {
-               return False;
+               return false;
        }
        
        /* be careful with wraparound! */
-       if (ptr < req->in.data ||
-           ptr >= req->in.data + req->in.data_size ||
-           count > req->in.data_size ||
-           ptr + count > req->in.data + req->in.data_size) {
-               return True;
+       if ((uintptr_t)ptr < (uintptr_t)bufinfo->data ||
+           (uintptr_t)ptr >= (uintptr_t)bufinfo->data + bufinfo->data_size ||
+           count > bufinfo->data_size ||
+           (uintptr_t)ptr + count > (uintptr_t)bufinfo->data + bufinfo->data_size) {
+               return true;
        }
-       return False;
+       return false;
 }
 
 
 /* 
    pull an open file handle from a packet, taking account of the chained_fnum
 */
-uint16_t req_fnum(struct smbsrv_request *req, const uint8_t *base, uint_t offset)
+static uint16_t req_fnum(struct smbsrv_request *req, const uint8_t *base, uint_t offset)
 {
        if (req->chained_fnum != -1) {
                return req->chained_fnum;
        }
        return SVAL(base, offset);
 }
+
+struct ntvfs_handle *smbsrv_pull_fnum(struct smbsrv_request *req, const uint8_t *base, uint_t offset)
+{
+       struct smbsrv_handle *handle;
+       uint16_t fnum = req_fnum(req, base, offset);
+
+       handle = smbsrv_smb_handle_find(req->tcon, fnum, req->request_time);
+       if (!handle) {
+               return NULL;
+       }
+
+       /*
+        * For SMB tcons and sessions can be mixed!
+        * But we need to make sure that file handles
+        * are only accessed by the opening session!
+        *
+        * So check if the handle is valid for the given session!
+        */
+       if (handle->session != req->session) {
+               return NULL;
+       }
+
+       return handle->ntvfs;
+}
+
+void smbsrv_push_fnum(uint8_t *base, uint_t offset, struct ntvfs_handle *ntvfs)
+{
+       struct smbsrv_handle *handle = talloc_get_type(ntvfs->frontend_data.private_data,
+                                      struct smbsrv_handle);
+       SSVAL(base, offset, handle->hid);
+}
+
+NTSTATUS smbsrv_handle_create_new(void *private_data, struct ntvfs_request *ntvfs, struct ntvfs_handle **_h)
+{
+       struct smbsrv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
+                                    struct smbsrv_request);
+       struct smbsrv_handle *handle;
+       struct ntvfs_handle *h;
+
+       handle = smbsrv_handle_new(req->session, req->tcon, req, req->request_time);
+       if (!handle) return NT_STATUS_INSUFFICIENT_RESOURCES;
+
+       h = talloc_zero(handle, struct ntvfs_handle);
+       if (!h) goto nomem;
+
+       /* 
+        * note: we don't set handle->ntvfs yet,
+        *       this will be done by smbsrv_handle_make_valid()
+        *       this makes sure the handle is invalid for clients
+        *       until the ntvfs subsystem has made it valid
+        */
+       h->ctx          = ntvfs->ctx;
+       h->session_info = ntvfs->session_info;
+       h->smbpid       = ntvfs->smbpid;
+
+       h->frontend_data.private_data = handle;
+
+       *_h = h;
+       return NT_STATUS_OK;
+nomem:
+       talloc_free(handle);
+       return NT_STATUS_NO_MEMORY;
+}
+
+NTSTATUS smbsrv_handle_make_valid(void *private_data, struct ntvfs_handle *h)
+{
+       struct smbsrv_tcon *tcon = talloc_get_type(private_data, struct smbsrv_tcon);
+       struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+                                                      struct smbsrv_handle);
+       /* this tells the frontend that the handle is valid */
+       handle->ntvfs = h;
+       /* this moves the smbsrv_request to the smbsrv_tcon memory context */
+       talloc_steal(tcon, handle);
+       return NT_STATUS_OK;
+}
+
+void smbsrv_handle_destroy(void *private_data, struct ntvfs_handle *h)
+{
+       struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+                                                      struct smbsrv_handle);
+       talloc_free(handle);
+}
+
+struct ntvfs_handle *smbsrv_handle_search_by_wire_key(void *private_data, struct ntvfs_request *ntvfs, const DATA_BLOB *key)
+{
+       struct smbsrv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
+                                    struct smbsrv_request);
+
+       if (key->length != 2) return NULL;
+
+       return smbsrv_pull_fnum(req, key->data, 0);
+}
+
+DATA_BLOB smbsrv_handle_get_wire_key(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx)
+{
+       uint8_t key[2];
+
+       smbsrv_push_fnum(key, 0, handle);
+
+       return data_blob_talloc(mem_ctx, key, sizeof(key));
+}