Finish removal of iconv_convenience in public API's.
[bbaumbach/samba-autobuild/.git] / source4 / libcli / smb2 / create.c
index 999c10ab085c44b37c5146e41bf77fd08435eacc..4e150641850ccef81fe019d4824871356da16611 100644 (file)
 #include "libcli/raw/raw_proto.h"
 #include "libcli/smb2/smb2.h"
 #include "libcli/smb2/smb2_calls.h"
-
-/*
-  add a blob to a smb2_create attribute blob
-*/
-NTSTATUS smb2_create_blob_add(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, 
-                             const char *tag,
-                             DATA_BLOB add, bool last)
-{
-       uint32_t ofs = blob->length;
-       size_t tag_length = strlen(tag);
-       uint8_t pad = smb2_padding_size(add.length+tag_length, 8);
-       if (!data_blob_realloc(mem_ctx, blob, 
-                              blob->length + 0x14 + tag_length + add.length + pad))
-               return NT_STATUS_NO_MEMORY;
-       
-       if (last) {
-               SIVAL(blob->data, ofs+0x00, 0);
-       } else {
-               SIVAL(blob->data, ofs+0x00, 0x14 + tag_length + add.length + pad);
-       }
-       SSVAL(blob->data, ofs+0x04, 0x10); /* offset of tag */
-       SIVAL(blob->data, ofs+0x06, tag_length); /* tag length */
-       SSVAL(blob->data, ofs+0x0A, 0x14 + tag_length); /* offset of data */
-       SIVAL(blob->data, ofs+0x0C, add.length);
-       memcpy(blob->data+ofs+0x10, tag, tag_length);
-       SIVAL(blob->data, ofs+0x10+tag_length, 0); /* pad? */
-       memcpy(blob->data+ofs+0x14+tag_length, add.data, add.length);
-       memset(blob->data+ofs+0x14+tag_length+add.length, 0, pad);
-
-       return NT_STATUS_OK;
-}
+#include "librpc/gen_ndr/ndr_security.h"
 
 /*
   send a create request
@@ -63,7 +33,11 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create
 {
        struct smb2_request *req;
        NTSTATUS status;
-       DATA_BLOB blob = data_blob(NULL, 0);
+       DATA_BLOB blob;
+       struct smb2_create_blobs blobs;
+       int i;
+
+       ZERO_STRUCT(blobs);
 
        req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x38, true, 0);
        if (req == NULL) return NULL;
@@ -85,11 +59,13 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create
                return NULL;
        }
 
+       /* now add all the optional blobs */
        if (io->in.eas.num_eas != 0) {
                DATA_BLOB b = data_blob_talloc(req, NULL, 
-                                              ea_list_size_chained(io->in.eas.num_eas, io->in.eas.eas));
-               ea_put_list_chained(b.data, io->in.eas.num_eas, io->in.eas.eas);
-               status = smb2_create_blob_add(req, &blob, SMB2_CREATE_TAG_EXTA, b, false);
+                                              ea_list_size_chained(io->in.eas.num_eas, io->in.eas.eas, 4));
+               ea_put_list_chained(b.data, io->in.eas.num_eas, io->in.eas.eas, 4);
+               status = smb2_create_blob_add(req, &blobs,
+                                             SMB2_CREATE_TAG_EXTA, b);
                if (!NT_STATUS_IS_OK(status)) {
                        talloc_free(req);
                        return NULL;
@@ -99,19 +75,128 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create
 
        /* an empty MxAc tag seems to be used to ask the server to
           return the maximum access mask allowed on the file */
-       status = smb2_create_blob_add(req, &blob, SMB2_CREATE_TAG_MXAC, 
-                                     data_blob(NULL, 0), true);
+       if (io->in.query_maximal_access) {
+               /* TODO: MS-SMB2 2.2.13.2.5 says this can contain a timestamp? What to do
+                  with that if it doesn't match? */
+               status = smb2_create_blob_add(req, &blobs,
+                                             SMB2_CREATE_TAG_MXAC, data_blob(NULL, 0));
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(req);
+                       return NULL;
+               }
+       }
+
+       if (io->in.alloc_size != 0) {
+               uint8_t data[8];
+               SBVAL(data, 0, io->in.alloc_size);
+               status = smb2_create_blob_add(req, &blobs,
+                                             SMB2_CREATE_TAG_ALSI, data_blob_const(data, 8));
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(req);
+                       return NULL;
+               }
+       }
+
+       if (io->in.durable_open) {
+               status = smb2_create_blob_add(req, &blobs,
+                                             SMB2_CREATE_TAG_DHNQ, data_blob_talloc_zero(req, 16));
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(req);
+                       return NULL;
+               }
+       }
+
+       if (io->in.durable_handle) {
+               uint8_t data[16];
+               smb2_push_handle(data, io->in.durable_handle);
+               status = smb2_create_blob_add(req, &blobs,
+                                             SMB2_CREATE_TAG_DHNC, data_blob_const(data, 16));
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(req);
+                       return NULL;
+               }
+       }
+
+       if (io->in.timewarp) {
+               uint8_t data[8];
+               SBVAL(data, 0, io->in.timewarp);                
+               status = smb2_create_blob_add(req, &blobs,
+                                             SMB2_CREATE_TAG_TWRP, data_blob_const(data, 8));
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(req);
+                       return NULL;
+               }
+       }
+
+       if (io->in.sec_desc) {
+               enum ndr_err_code ndr_err;
+               DATA_BLOB sd_blob;
+               ndr_err = ndr_push_struct_blob(&sd_blob, req, io->in.sec_desc,
+                                              (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       talloc_free(req);
+                       return NULL;
+               }
+               status = smb2_create_blob_add(req, &blobs,
+                                             SMB2_CREATE_TAG_SECD, sd_blob);
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(req);
+                       return NULL;
+               }
+       }
+
+       if (io->in.query_on_disk_id) {
+               status = smb2_create_blob_add(req, &blobs,
+                                             SMB2_CREATE_TAG_QFID, data_blob(NULL, 0));
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(req);
+                       return NULL;
+               }
+       }
+
+       if (io->in.lease_request) {
+               uint8_t data[32];
+
+               memcpy(&data[0], &io->in.lease_request->lease_key, 16);
+               SIVAL(data, 16, io->in.lease_request->lease_state);
+               SIVAL(data, 20, io->in.lease_request->lease_flags);
+               SBVAL(data, 24, io->in.lease_request->lease_duration);
+
+               status = smb2_create_blob_add(req, &blobs,
+                                             SMB2_CREATE_TAG_RQLS,
+                                             data_blob_const(data, 32));
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(req);
+                       return NULL;
+               }
+       }
+
+       /* and any custom blobs */
+       for (i=0;i<io->in.blobs.num_blobs;i++) {
+               status = smb2_create_blob_add(req, &blobs,
+                                             io->in.blobs.blobs[i].tag, 
+                                             io->in.blobs.blobs[i].data);
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(req);
+                       return NULL;
+               }
+       }
+
 
+       status = smb2_create_blob_push(req, &blob, blobs);
        if (!NT_STATUS_IS_OK(status)) {
                talloc_free(req);
                return NULL;
        }
+
        status = smb2_push_o32s32_blob(&req->out, 0x30, blob);
        if (!NT_STATUS_IS_OK(status)) {
                talloc_free(req);
                return NULL;
        }
 
+       data_blob_free(&blob);
+
        smb2_transport_send(req);
 
        return req;
@@ -124,6 +209,8 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create
 NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_create *io)
 {
        NTSTATUS status;
+       DATA_BLOB blob;
+       int i;
 
        if (!smb2_request_receive(req) || 
            !smb2_request_is_ok(req)) {
@@ -131,7 +218,7 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct
        }
 
        SMB2_CHECK_PACKET_RECV(req, 0x58, true);
-
+       ZERO_STRUCT(io->out);
        io->out.oplock_level   = CVAL(req->in.body, 0x02);
        io->out.reserved       = CVAL(req->in.body, 0x03);
        io->out.create_action  = IVAL(req->in.body, 0x04);
@@ -144,12 +231,53 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct
        io->out.file_attr      = IVAL(req->in.body, 0x38);
        io->out.reserved2      = IVAL(req->in.body, 0x3C);
        smb2_pull_handle(req->in.body+0x40, &io->out.file.handle);
-       status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x50, &io->out.blob);
+       status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x50, &blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               smb2_request_destroy(req);
+               return status;
+       }
+
+       status = smb2_create_blob_parse(mem_ctx, blob, &io->out.blobs);
        if (!NT_STATUS_IS_OK(status)) {
                smb2_request_destroy(req);
                return status;
        }
 
+       /* pull out the parsed blobs */
+       for (i=0;i<io->out.blobs.num_blobs;i++) {
+               if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_MXAC) == 0) {
+                       /* TODO: this also contains a status field in
+                          first 4 bytes */
+                       if (io->out.blobs.blobs[i].data.length != 8) {
+                               smb2_request_destroy(req);
+                               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       }
+                       io->out.maximal_access = IVAL(io->out.blobs.blobs[i].data.data, 4);
+               }
+               if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_QFID) == 0) {
+                       if (io->out.blobs.blobs[i].data.length != 32) {
+                               smb2_request_destroy(req);
+                               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       }
+                       memcpy(io->out.on_disk_id, io->out.blobs.blobs[i].data.data, 32);
+               }
+               if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_RQLS) == 0) {
+                       uint8_t *data;
+                       if (io->out.blobs.blobs[i].data.length != 32) {
+                               smb2_request_destroy(req);
+                               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       }
+
+                       data = io->out.blobs.blobs[i].data.data;
+                       memcpy(&io->out.lease_response.lease_key, data, 16);
+                       io->out.lease_response.lease_state = IVAL(data, 16);
+                       io->out.lease_response.lease_flags = IVAL(data, 20);
+                       io->out.lease_response.lease_duration = BVAL(data, 24);
+               }
+       }
+
+       data_blob_free(&blob);
+
        return smb2_request_destroy(req);
 }