/* recv a create reply */ NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_create *io) { NTSTATUS status; if (!smb2_request_receive(req) || !smb2_request_is_ok(req)) { return smb2_request_destroy(req); } SMB2_CHECK_PACKET_RECV(req, 0x58, True); io->out.oplock_flags = SVAL(req->in.body, 0x02); io->out.create_action = IVAL(req->in.body, 0x04); io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08); io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10); io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18); io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20); io->out.alloc_size = BVAL(req->in.body, 0x28); io->out.size = BVAL(req->in.body, 0x30); io->out.file_attr = IVAL(req->in.body, 0x38); io->out._pad = 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); if (!NT_STATUS_IS_OK(status)) { smb2_request_destroy(req); return status; } return smb2_request_destroy(req); }
/* recv a keepalive reply */ NTSTATUS smb2_keepalive_recv(struct smb2_request *req) { if (!smb2_request_receive(req) || !smb2_request_is_ok(req)) { return smb2_request_destroy(req); } SMB2_CHECK_PACKET_RECV(req, 0x04, False); return smb2_request_destroy(req); }
/* recv a write reply */ NTSTATUS smb2_write_recv(struct smb2_request *req, struct smb2_write *io) { if (!smb2_request_receive(req) || !smb2_request_is_ok(req)) { return smb2_request_destroy(req); } SMB2_CHECK_PACKET_RECV(req, 0x10, true); io->out._pad = SVAL(req->in.body, 0x02); io->out.nwritten = IVAL(req->in.body, 0x04); io->out.unknown1 = BVAL(req->in.body, 0x08); return smb2_request_destroy(req); }
/* Receive a Lease Break Response */ NTSTATUS smb2_lease_break_ack_recv(struct smb2_request *req, struct smb2_lease_break_ack *io) { if (!smb2_request_receive(req) || !smb2_request_is_ok(req)) { return smb2_request_destroy(req); } SMB2_CHECK_PACKET_RECV(req, 0x24, false); io->out.reserved = IVAL(req->in.body, 0x02); io->out.lease.lease_flags = IVAL(req->in.body, 0x04); memcpy(&io->out.lease.lease_key, req->in.body+0x8, sizeof(struct smb2_lease_key)); io->out.lease.lease_state = IVAL(req->in.body, 0x18); io->out.lease.lease_duration = IVAL(req->in.body, 0x1C); return smb2_request_destroy(req); }
/* recv a read reply */ NTSTATUS smb2_read_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_read *io) { NTSTATUS status; if (!smb2_request_receive(req) || !smb2_request_is_ok(req)) { return smb2_request_destroy(req); } SMB2_CHECK_PACKET_RECV(req, 0x10, true); status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.data); if (!NT_STATUS_IS_OK(status)) { smb2_request_destroy(req); return status; } io->out.remaining = IVAL(req->in.body, 0x08); io->out.reserved = IVAL(req->in.body, 0x0C); return smb2_request_destroy(req); }
/* recv a create reply */ 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)) { return smb2_request_destroy(req); } 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); io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08); io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10); io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18); io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20); io->out.alloc_size = BVAL(req->in.body, 0x28); io->out.size = BVAL(req->in.body, 0x30); 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, &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); }
static bool test_compound_padding(struct torture_context *tctx, struct smb2_tree *tree) { struct smb2_handle h; struct smb2_create cr; struct smb2_read r; const char *fname = "compound_read.dat"; const char *sname = "compound_read.dat:foo"; struct smb2_request *req[3]; NTSTATUS status; bool ret = false; smb2_util_unlink(tree, fname); /* Write file */ ZERO_STRUCT(cr); cr.in.desired_access = SEC_FILE_WRITE_DATA; cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; cr.in.create_disposition = NTCREATEX_DISP_CREATE; cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; cr.in.fname = fname; cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE| NTCREATEX_SHARE_ACCESS_DELETE; status = smb2_create(tree, tctx, &cr); CHECK_STATUS(status, NT_STATUS_OK); h = cr.out.file.handle; status = smb2_util_write(tree, h, "123", 0, 3); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, h); /* Write stream */ ZERO_STRUCT(cr); cr.in.desired_access = SEC_FILE_WRITE_DATA; cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; cr.in.create_disposition = NTCREATEX_DISP_CREATE; cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; cr.in.fname = sname; cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE| NTCREATEX_SHARE_ACCESS_DELETE; status = smb2_create(tree, tctx, &cr); CHECK_STATUS(status, NT_STATUS_OK); h = cr.out.file.handle; status = smb2_util_write(tree, h, "456", 0, 3); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, h); /* Check compound read from basefile */ smb2_transport_compound_start(tree->session->transport, 2); ZERO_STRUCT(cr); cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; cr.in.desired_access = SEC_FILE_READ_DATA; cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; cr.in.create_disposition = NTCREATEX_DISP_OPEN; cr.in.fname = fname; cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE| NTCREATEX_SHARE_ACCESS_DELETE; req[0] = smb2_create_send(tree, &cr); smb2_transport_compound_set_related(tree->session->transport, true); ZERO_STRUCT(r); h.data[0] = UINT64_MAX; h.data[1] = UINT64_MAX; r.in.file.handle = h; r.in.length = 3; r.in.offset = 0; r.in.min_count = 1; req[1] = smb2_read_send(tree, &r); status = smb2_create_recv(req[0], tree, &cr); CHECK_STATUS(status, NT_STATUS_OK); /* * We must do a manual smb2_request_receive() in order to be * able to check the transport layer info, as smb2_read_recv() * will destroy the req. smb2_read_recv() will call * smb2_request_receive() again, but that's ok. */ if (!smb2_request_receive(req[1]) || !smb2_request_is_ok(req[1])) { torture_fail(tctx, "failed to receive read request"); } /* * size must be 24: 16 byte read response header plus 3 * requested bytes padded to an 8 byte boundary. */ CHECK_VALUE(req[1]->in.body_size, 24); status = smb2_read_recv(req[1], tree, &r); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, cr.out.file.handle); /* Check compound read from stream */ smb2_transport_compound_start(tree->session->transport, 2); ZERO_STRUCT(cr); cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; cr.in.desired_access = SEC_FILE_READ_DATA; cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; cr.in.create_disposition = NTCREATEX_DISP_OPEN; cr.in.fname = sname; cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE| NTCREATEX_SHARE_ACCESS_DELETE; req[0] = smb2_create_send(tree, &cr); smb2_transport_compound_set_related(tree->session->transport, true); ZERO_STRUCT(r); h.data[0] = UINT64_MAX; h.data[1] = UINT64_MAX; r.in.file.handle = h; r.in.length = 3; r.in.offset = 0; r.in.min_count = 1; req[1] = smb2_read_send(tree, &r); status = smb2_create_recv(req[0], tree, &cr); CHECK_STATUS(status, NT_STATUS_OK); /* * We must do a manual smb2_request_receive() in order to be * able to check the transport layer info, as smb2_read_recv() * will destroy the req. smb2_read_recv() will call * smb2_request_receive() again, but that's ok. */ if (!smb2_request_receive(req[1]) || !smb2_request_is_ok(req[1])) { torture_fail(tctx, "failed to receive read request"); } /* * size must be 24: 16 byte read response header plus 3 * requested bytes padded to an 8 byte boundary. */ CHECK_VALUE(req[1]->in.body_size, 24); status = smb2_read_recv(req[1], tree, &r); CHECK_STATUS(status, NT_STATUS_OK); h = cr.out.file.handle; /* Check 2 compound (unrelateated) reads from existing stream handle */ smb2_transport_compound_start(tree->session->transport, 2); ZERO_STRUCT(r); r.in.file.handle = h; r.in.length = 3; r.in.offset = 0; r.in.min_count = 1; req[0] = smb2_read_send(tree, &r); req[1] = smb2_read_send(tree, &r); /* * We must do a manual smb2_request_receive() in order to be * able to check the transport layer info, as smb2_read_recv() * will destroy the req. smb2_read_recv() will call * smb2_request_receive() again, but that's ok. */ if (!smb2_request_receive(req[0]) || !smb2_request_is_ok(req[0])) { torture_fail(tctx, "failed to receive read request"); } if (!smb2_request_receive(req[1]) || !smb2_request_is_ok(req[1])) { torture_fail(tctx, "failed to receive read request"); } /* * size must be 24: 16 byte read response header plus 3 * requested bytes padded to an 8 byte boundary. */ CHECK_VALUE(req[0]->in.body_size, 24); CHECK_VALUE(req[1]->in.body_size, 24); status = smb2_read_recv(req[0], tree, &r); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_read_recv(req[1], tree, &r); CHECK_STATUS(status, NT_STATUS_OK); /* * now try a single read from the stream and verify there's no padding */ ZERO_STRUCT(r); r.in.file.handle = h; r.in.length = 3; r.in.offset = 0; r.in.min_count = 1; req[0] = smb2_read_send(tree, &r); /* * We must do a manual smb2_request_receive() in order to be * able to check the transport layer info, as smb2_read_recv() * will destroy the req. smb2_read_recv() will call * smb2_request_receive() again, but that's ok. */ if (!smb2_request_receive(req[0]) || !smb2_request_is_ok(req[0])) { torture_fail(tctx, "failed to receive read request"); } /* * size must be 19: 16 byte read response header plus 3 * requested bytes without padding. */ CHECK_VALUE(req[0]->in.body_size, 19); status = smb2_read_recv(req[0], tree, &r); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, h); status = smb2_util_unlink(tree, fname); CHECK_STATUS(status, NT_STATUS_OK); ret = true; done: return ret; }