2 Unix SMB/CIFS implementation.
4 SMB2 client tree handling
6 Copyright (C) Andrew Tridgell 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "libcli/raw/libcliraw.h"
24 #include "libcli/raw/raw_proto.h"
25 #include "libcli/smb2/smb2.h"
26 #include "libcli/smb2/smb2_calls.h"
27 #include "librpc/gen_ndr/ndr_security.h"
32 struct smb2_request
*smb2_create_send(struct smb2_tree
*tree
, struct smb2_create
*io
)
34 struct smb2_request
*req
;
37 struct smb2_create_blobs blobs
;
42 req
= smb2_request_init_tree(tree
, SMB2_OP_CREATE
, 0x38, true, 0);
43 if (req
== NULL
) return NULL
;
45 SCVAL(req
->out
.body
, 0x02, io
->in
.security_flags
);
46 SCVAL(req
->out
.body
, 0x03, io
->in
.oplock_level
);
47 SIVAL(req
->out
.body
, 0x04, io
->in
.impersonation_level
);
48 SBVAL(req
->out
.body
, 0x08, io
->in
.create_flags
);
49 SBVAL(req
->out
.body
, 0x10, io
->in
.reserved
);
50 SIVAL(req
->out
.body
, 0x18, io
->in
.desired_access
);
51 SIVAL(req
->out
.body
, 0x1C, io
->in
.file_attributes
);
52 SIVAL(req
->out
.body
, 0x20, io
->in
.share_access
);
53 SIVAL(req
->out
.body
, 0x24, io
->in
.create_disposition
);
54 SIVAL(req
->out
.body
, 0x28, io
->in
.create_options
);
56 status
= smb2_push_o16s16_string(&req
->out
, 0x2C, io
->in
.fname
);
57 if (!NT_STATUS_IS_OK(status
)) {
62 /* now add all the optional blobs */
63 if (io
->in
.eas
.num_eas
!= 0) {
64 DATA_BLOB b
= data_blob_talloc(req
, NULL
,
65 ea_list_size_chained(io
->in
.eas
.num_eas
, io
->in
.eas
.eas
, 4));
66 ea_put_list_chained(b
.data
, io
->in
.eas
.num_eas
, io
->in
.eas
.eas
, 4);
67 status
= smb2_create_blob_add(req
, &blobs
,
68 SMB2_CREATE_TAG_EXTA
, b
);
69 if (!NT_STATUS_IS_OK(status
)) {
76 /* an empty MxAc tag seems to be used to ask the server to
77 return the maximum access mask allowed on the file */
78 if (io
->in
.query_maximal_access
) {
79 /* TODO: MS-SMB2 2.2.13.2.5 says this can contain a timestamp? What to do
80 with that if it doesn't match? */
81 status
= smb2_create_blob_add(req
, &blobs
,
82 SMB2_CREATE_TAG_MXAC
, data_blob(NULL
, 0));
83 if (!NT_STATUS_IS_OK(status
)) {
89 if (io
->in
.alloc_size
!= 0) {
91 SBVAL(data
, 0, io
->in
.alloc_size
);
92 status
= smb2_create_blob_add(req
, &blobs
,
93 SMB2_CREATE_TAG_ALSI
, data_blob_const(data
, 8));
94 if (!NT_STATUS_IS_OK(status
)) {
100 if (io
->in
.durable_open
) {
101 status
= smb2_create_blob_add(req
, &blobs
,
102 SMB2_CREATE_TAG_DHNQ
, data_blob_talloc_zero(req
, 16));
103 if (!NT_STATUS_IS_OK(status
)) {
109 if (io
->in
.durable_open_v2
) {
112 struct GUID_ndr_buf guid_buf
= { .buf
= {0}, };
114 SIVAL(data
, 0, io
->in
.timeout
);
115 if (io
->in
.persistent_open
) {
116 flags
= SMB2_DHANDLE_FLAG_PERSISTENT
;
118 SIVAL(data
, 4, flags
);
119 SBVAL(data
, 8, 0x0); /* reserved */
120 status
= GUID_to_ndr_buf(&io
->in
.create_guid
, &guid_buf
);
121 if (!NT_STATUS_IS_OK(status
)) {
125 memcpy(data
+16, guid_buf
.buf
, sizeof(guid_buf
.buf
));
127 status
= smb2_create_blob_add(req
, &blobs
,
128 SMB2_CREATE_TAG_DH2Q
,
129 data_blob_const(data
, 32));
130 if (!NT_STATUS_IS_OK(status
)) {
136 if (io
->in
.durable_handle
) {
138 smb2_push_handle(data
, io
->in
.durable_handle
);
139 status
= smb2_create_blob_add(req
, &blobs
,
140 SMB2_CREATE_TAG_DHNC
, data_blob_const(data
, 16));
141 if (!NT_STATUS_IS_OK(status
)) {
147 if (io
->in
.durable_handle_v2
) {
149 struct GUID_ndr_buf guid_buf
= { .buf
= {0}, };
152 smb2_push_handle(data
, io
->in
.durable_handle_v2
);
153 status
= GUID_to_ndr_buf(&io
->in
.create_guid
, &guid_buf
);
154 if (!NT_STATUS_IS_OK(status
)) {
158 memcpy(data
+16, guid_buf
.buf
, sizeof(guid_buf
.buf
));
159 if (io
->in
.persistent_open
) {
160 flags
= SMB2_DHANDLE_FLAG_PERSISTENT
;
162 SIVAL(data
, 32, flags
);
164 status
= smb2_create_blob_add(req
, &blobs
,
165 SMB2_CREATE_TAG_DH2C
,
166 data_blob_const(data
, 36));
167 if (!NT_STATUS_IS_OK(status
)) {
173 if (io
->in
.timewarp
) {
175 SBVAL(data
, 0, io
->in
.timewarp
);
176 status
= smb2_create_blob_add(req
, &blobs
,
177 SMB2_CREATE_TAG_TWRP
, data_blob_const(data
, 8));
178 if (!NT_STATUS_IS_OK(status
)) {
184 if (io
->in
.sec_desc
) {
185 enum ndr_err_code ndr_err
;
187 ndr_err
= ndr_push_struct_blob(&sd_blob
, req
, io
->in
.sec_desc
,
188 (ndr_push_flags_fn_t
)ndr_push_security_descriptor
);
189 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
193 status
= smb2_create_blob_add(req
, &blobs
,
194 SMB2_CREATE_TAG_SECD
, sd_blob
);
195 if (!NT_STATUS_IS_OK(status
)) {
201 if (io
->in
.query_on_disk_id
) {
202 status
= smb2_create_blob_add(req
, &blobs
,
203 SMB2_CREATE_TAG_QFID
, data_blob(NULL
, 0));
204 if (!NT_STATUS_IS_OK(status
)) {
210 if (io
->in
.lease_request
) {
213 if (!smb2_lease_push(io
->in
.lease_request
, data
,
219 status
= smb2_create_blob_add(
220 req
, &blobs
, SMB2_CREATE_TAG_RQLS
,
221 data_blob_const(data
, sizeof(data
)));
222 if (!NT_STATUS_IS_OK(status
)) {
228 if (io
->in
.lease_request_v2
) {
231 if (!smb2_lease_push(io
->in
.lease_request_v2
, data
,
237 status
= smb2_create_blob_add(
238 req
, &blobs
, SMB2_CREATE_TAG_RQLS
,
239 data_blob_const(data
, sizeof(data
)));
240 if (!NT_STATUS_IS_OK(status
)) {
246 if (io
->in
.app_instance_id
) {
248 struct GUID_ndr_buf guid_buf
= { .buf
= {0}, };
250 SSVAL(data
, 0, 20); /* structure size */
251 SSVAL(data
, 2, 0); /* reserved */
253 status
= GUID_to_ndr_buf(io
->in
.app_instance_id
, &guid_buf
);
254 if (!NT_STATUS_IS_OK(status
)) {
258 memcpy(data
+4, guid_buf
.buf
, sizeof(guid_buf
.buf
));
260 status
= smb2_create_blob_add(req
, &blobs
,
261 SMB2_CREATE_TAG_APP_INSTANCE_ID
,
262 data_blob_const(data
, 20));
263 if (!NT_STATUS_IS_OK(status
)) {
269 /* and any custom blobs */
270 for (i
=0;i
<io
->in
.blobs
.num_blobs
;i
++) {
271 status
= smb2_create_blob_add(req
, &blobs
,
272 io
->in
.blobs
.blobs
[i
].tag
,
273 io
->in
.blobs
.blobs
[i
].data
);
274 if (!NT_STATUS_IS_OK(status
)) {
281 status
= smb2_create_blob_push(req
, &blob
, blobs
);
282 if (!NT_STATUS_IS_OK(status
)) {
287 status
= smb2_push_o32s32_blob(&req
->out
, 0x30, blob
);
288 if (!NT_STATUS_IS_OK(status
)) {
293 if (((io
->in
.fname
== NULL
) || (strlen(io
->in
.fname
) == 0)) &&
294 (blob
.length
== 0)) {
295 struct smb2_request_buffer
*buf
= &req
->out
;
297 status
= smb2_grow_buffer(buf
, 1);
298 if (!NT_STATUS_IS_OK(status
)) {
308 data_blob_free(&blob
);
310 smb2_transport_send(req
);
319 NTSTATUS
smb2_create_recv(struct smb2_request
*req
, TALLOC_CTX
*mem_ctx
, struct smb2_create
*io
)
325 if (!smb2_request_receive(req
) ||
326 !smb2_request_is_ok(req
)) {
327 return smb2_request_destroy(req
);
330 SMB2_CHECK_PACKET_RECV(req
, 0x58, true);
331 ZERO_STRUCT(io
->out
);
332 io
->out
.oplock_level
= CVAL(req
->in
.body
, 0x02);
333 io
->out
.reserved
= CVAL(req
->in
.body
, 0x03);
334 io
->out
.create_action
= IVAL(req
->in
.body
, 0x04);
335 io
->out
.create_time
= smbcli_pull_nttime(req
->in
.body
, 0x08);
336 io
->out
.access_time
= smbcli_pull_nttime(req
->in
.body
, 0x10);
337 io
->out
.write_time
= smbcli_pull_nttime(req
->in
.body
, 0x18);
338 io
->out
.change_time
= smbcli_pull_nttime(req
->in
.body
, 0x20);
339 io
->out
.alloc_size
= BVAL(req
->in
.body
, 0x28);
340 io
->out
.size
= BVAL(req
->in
.body
, 0x30);
341 io
->out
.file_attr
= IVAL(req
->in
.body
, 0x38);
342 io
->out
.reserved2
= IVAL(req
->in
.body
, 0x3C);
343 smb2_pull_handle(req
->in
.body
+0x40, &io
->out
.file
.handle
);
344 status
= smb2_pull_o32s32_blob(&req
->in
, mem_ctx
, req
->in
.body
+0x50, &blob
);
345 if (!NT_STATUS_IS_OK(status
)) {
346 smb2_request_destroy(req
);
350 status
= smb2_create_blob_parse(mem_ctx
, blob
, &io
->out
.blobs
);
351 if (!NT_STATUS_IS_OK(status
)) {
352 smb2_request_destroy(req
);
356 /* pull out the parsed blobs */
357 for (i
=0;i
<io
->out
.blobs
.num_blobs
;i
++) {
358 if (strcmp(io
->out
.blobs
.blobs
[i
].tag
, SMB2_CREATE_TAG_MXAC
) == 0) {
359 if (io
->out
.blobs
.blobs
[i
].data
.length
!= 8) {
360 smb2_request_destroy(req
);
361 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
363 io
->out
.maximal_access_status
=
364 IVAL(io
->out
.blobs
.blobs
[i
].data
.data
, 0);
365 io
->out
.maximal_access
= IVAL(io
->out
.blobs
.blobs
[i
].data
.data
, 4);
367 if (strcmp(io
->out
.blobs
.blobs
[i
].tag
, SMB2_CREATE_TAG_QFID
) == 0) {
368 if (io
->out
.blobs
.blobs
[i
].data
.length
!= 32) {
369 smb2_request_destroy(req
);
370 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
372 memcpy(io
->out
.on_disk_id
, io
->out
.blobs
.blobs
[i
].data
.data
, 32);
374 if (strcmp(io
->out
.blobs
.blobs
[i
].tag
, SMB2_CREATE_TAG_RQLS
) == 0) {
375 struct smb2_lease
*ls
= NULL
;
378 ZERO_STRUCT(io
->out
.lease_response
);
379 ZERO_STRUCT(io
->out
.lease_response_v2
);
381 switch (io
->out
.blobs
.blobs
[i
].data
.length
) {
383 ls
= &io
->out
.lease_response
;
384 ls
->lease_version
= 1;
387 ls
= &io
->out
.lease_response_v2
;
388 ls
->lease_version
= 2;
391 smb2_request_destroy(req
);
392 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
395 data
= io
->out
.blobs
.blobs
[i
].data
.data
;
396 memcpy(&ls
->lease_key
, data
, 16);
397 ls
->lease_state
= IVAL(data
, 16);
398 ls
->lease_flags
= IVAL(data
, 20);
399 ls
->lease_duration
= BVAL(data
, 24);
401 if (io
->out
.blobs
.blobs
[i
].data
.length
== 52) {
402 memcpy(&ls
->parent_lease_key
, data
+32, 16);
403 ls
->lease_epoch
= SVAL(data
, 48);
406 if (strcmp(io
->out
.blobs
.blobs
[i
].tag
, SMB2_CREATE_TAG_DHNQ
) == 0) {
407 if (io
->out
.blobs
.blobs
[i
].data
.length
!= 8) {
408 smb2_request_destroy(req
);
409 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
411 io
->out
.durable_open
= true;
413 if (strcmp(io
->out
.blobs
.blobs
[i
].tag
, SMB2_CREATE_TAG_DH2Q
) == 0) {
417 if (io
->out
.blobs
.blobs
[i
].data
.length
!= 8) {
418 smb2_request_destroy(req
);
419 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
422 io
->out
.durable_open
= false;
423 io
->out
.durable_open_v2
= true;
425 data
= io
->out
.blobs
.blobs
[i
].data
.data
;
426 io
->out
.timeout
= IVAL(data
, 0);
427 flags
= IVAL(data
, 4);
428 if ((flags
& SMB2_DHANDLE_FLAG_PERSISTENT
) != 0) {
429 io
->out
.persistent_open
= true;
434 data_blob_free(&blob
);
436 return smb2_request_destroy(req
);
442 NTSTATUS
smb2_create(struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
, struct smb2_create
*io
)
444 struct smb2_request
*req
= smb2_create_send(tree
, io
);
445 return smb2_create_recv(req
, mem_ctx
, io
);