2 Unix SMB2 implementation.
4 Copyright (C) Stefan Metzmacher 2005
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "libcli/smb2/smb2.h"
22 #include "libcli/smb2/smb2_calls.h"
23 #include "smb_server/smb_server.h"
24 #include "smb_server/service_smb_proto.h"
25 #include "smb_server/smb2/smb2_server.h"
26 #include "librpc/gen_ndr/security.h"
27 #include "smbd/service_stream.h"
28 #include "ntvfs/ntvfs.h"
29 #include "param/param.h"
32 send an oplock break request to a client
34 static NTSTATUS
smb2srv_send_oplock_break(void *p
, struct ntvfs_handle
*h
, uint8_t level
)
36 struct smbsrv_handle
*handle
= talloc_get_type(h
->frontend_data
.private_data
,
37 struct smbsrv_handle
);
38 struct smb2srv_request
*req
;
41 /* setup a dummy request structure */
42 req
= smb2srv_init_request(handle
->tcon
->smb_conn
);
43 NT_STATUS_HAVE_NO_MEMORY(req
);
45 req
->in
.buffer
= talloc_array(req
, uint8_t,
46 NBT_HDR_SIZE
+ SMB2_MIN_SIZE
);
47 NT_STATUS_HAVE_NO_MEMORY(req
->in
.buffer
);
48 req
->in
.size
= NBT_HDR_SIZE
+ SMB2_MIN_SIZE
;
49 req
->in
.allocated
= req
->in
.size
;
51 req
->in
.hdr
= req
->in
.buffer
+ NBT_HDR_SIZE
;
52 req
->in
.body
= req
->in
.hdr
+ SMB2_HDR_BODY
;
53 req
->in
.body_size
= req
->in
.size
- (SMB2_HDR_BODY
+NBT_HDR_SIZE
);
54 req
->in
.dynamic
= NULL
;
56 req
->seqnum
= UINT64_MAX
;
58 smb2srv_setup_bufinfo(req
);
60 SIVAL(req
->in
.hdr
, 0, SMB2_MAGIC
);
61 SSVAL(req
->in
.hdr
, SMB2_HDR_LENGTH
, SMB2_HDR_BODY
);
62 SSVAL(req
->in
.hdr
, SMB2_HDR_EPOCH
, 0);
63 SIVAL(req
->in
.hdr
, SMB2_HDR_STATUS
, 0);
64 SSVAL(req
->in
.hdr
, SMB2_HDR_OPCODE
, SMB2_OP_BREAK
);
65 SSVAL(req
->in
.hdr
, SMB2_HDR_CREDIT
, 0);
66 SIVAL(req
->in
.hdr
, SMB2_HDR_FLAGS
, 0);
67 SIVAL(req
->in
.hdr
, SMB2_HDR_NEXT_COMMAND
, 0);
68 SBVAL(req
->in
.hdr
, SMB2_HDR_MESSAGE_ID
, 0);
69 SIVAL(req
->in
.hdr
, SMB2_HDR_PID
, 0);
70 SIVAL(req
->in
.hdr
, SMB2_HDR_TID
, 0);
71 SBVAL(req
->in
.hdr
, SMB2_HDR_SESSION_ID
, 0);
72 memset(req
->in
.hdr
+SMB2_HDR_SIGNATURE
, 0, 16);
74 SSVAL(req
->in
.body
, 0, 2);
76 status
= smb2srv_setup_reply(req
, 0x18, false, 0);
77 NT_STATUS_NOT_OK_RETURN(status
);
79 SSVAL(req
->out
.hdr
, SMB2_HDR_CREDIT
, 0x0000);
81 SSVAL(req
->out
.body
, 0x02, 0x0001);
82 SIVAL(req
->out
.body
, 0x04, 0x00000000);
83 smb2srv_push_handle(req
->out
.body
, 0x08, h
);
85 smb2srv_send_reply(req
);
90 struct ntvfs_handle
*smb2srv_pull_handle(struct smb2srv_request
*req
, const uint8_t *base
, uint_t offset
)
92 struct smbsrv_tcon
*tcon
;
93 struct smbsrv_handle
*handle
;
99 * if there're chained requests used the cached handle
101 * TODO: check if this also correct when the given handle
104 if (req
->chained_file_handle
) {
105 base
= req
->chained_file_handle
;
109 hid
= IVAL(base
, offset
);
110 tid
= IVAL(base
, offset
+ 4);
111 uid
= BVAL(base
, offset
+ 8);
113 /* if it's the wildcard handle, don't waste time to search it... */
114 if (hid
== UINT32_MAX
&& tid
== UINT32_MAX
&& uid
== UINT64_MAX
) {
119 * if the (v)uid part doesn't match the given session the handle isn't
122 if (uid
!= req
->session
->vuid
) {
127 * the handle can belong to a different tcon
128 * as that TID in the SMB2 header says, but
129 * the request should succeed nevertheless!
131 * because of this we put the 32 bit TID into the
132 * 128 bit handle, so that we can extract the tcon from the
136 if (tid
!= req
->tcon
->tid
) {
137 tcon
= smbsrv_smb2_tcon_find(req
->session
, tid
, req
->request_time
);
143 handle
= smbsrv_smb2_handle_find(tcon
, hid
, req
->request_time
);
149 * as the smb2srv_tcon is a child object of the smb2srv_session
150 * the handle belongs to the correct session!
152 * Note: no check is needed here for SMB2
156 * as the handle may have overwritten the tcon
157 * we need to set it on the request so that the
158 * correct ntvfs context will be used for the ntvfs_*() request
160 * TODO: check if that's correct for chained requests as well!
163 return handle
->ntvfs
;
166 void smb2srv_push_handle(uint8_t *base
, uint_t offset
, struct ntvfs_handle
*ntvfs
)
168 struct smbsrv_handle
*handle
= talloc_get_type(ntvfs
->frontend_data
.private_data
,
169 struct smbsrv_handle
);
172 * the handle is 128 bit on the wire
174 SIVAL(base
, offset
, handle
->hid
);
175 SIVAL(base
, offset
+ 4, handle
->tcon
->tid
);
176 SBVAL(base
, offset
+ 8, handle
->session
->vuid
);
179 static NTSTATUS
smb2srv_handle_create_new(void *private_data
, struct ntvfs_request
*ntvfs
, struct ntvfs_handle
**_h
)
181 struct smb2srv_request
*req
= talloc_get_type(ntvfs
->frontend_data
.private_data
,
182 struct smb2srv_request
);
183 struct smbsrv_handle
*handle
;
184 struct ntvfs_handle
*h
;
186 handle
= smbsrv_handle_new(req
->session
, req
->tcon
, req
, req
->request_time
);
187 if (!handle
) return NT_STATUS_INSUFFICIENT_RESOURCES
;
189 h
= talloc_zero(handle
, struct ntvfs_handle
);
193 * note: we don't set handle->ntvfs yet,
194 * this will be done by smbsrv_handle_make_valid()
195 * this makes sure the handle is invalid for clients
196 * until the ntvfs subsystem has made it valid
199 h
->session_info
= ntvfs
->session_info
;
200 h
->smbpid
= ntvfs
->smbpid
;
202 h
->frontend_data
.private_data
= handle
;
208 return NT_STATUS_NO_MEMORY
;
211 static NTSTATUS
smb2srv_handle_make_valid(void *private_data
, struct ntvfs_handle
*h
)
213 struct smbsrv_tcon
*tcon
= talloc_get_type(private_data
, struct smbsrv_tcon
);
214 struct smbsrv_handle
*handle
= talloc_get_type(h
->frontend_data
.private_data
,
215 struct smbsrv_handle
);
216 /* this tells the frontend that the handle is valid */
218 /* this moves the smbsrv_request to the smbsrv_tcon memory context */
219 talloc_steal(tcon
, handle
);
223 static void smb2srv_handle_destroy(void *private_data
, struct ntvfs_handle
*h
)
225 struct smbsrv_handle
*handle
= talloc_get_type(h
->frontend_data
.private_data
,
226 struct smbsrv_handle
);
230 static struct ntvfs_handle
*smb2srv_handle_search_by_wire_key(void *private_data
, struct ntvfs_request
*ntvfs
, const DATA_BLOB
*key
)
235 static DATA_BLOB
smb2srv_handle_get_wire_key(void *private_data
, struct ntvfs_handle
*handle
, TALLOC_CTX
*mem_ctx
)
237 return data_blob(NULL
, 0);
240 static NTSTATUS
smb2srv_tcon_backend(struct smb2srv_request
*req
, union smb_tcon
*io
)
242 struct smbsrv_tcon
*tcon
;
244 enum ntvfs_type type
;
245 const char *service
= io
->smb2
.in
.path
;
246 struct share_config
*scfg
;
247 const char *sharetype
;
248 uint64_t ntvfs_caps
= 0;
250 if (strncmp(service
, "\\\\", 2) == 0) {
251 const char *p
= strchr(service
+2, '\\');
257 status
= share_get_config(req
, req
->smb_conn
->share_context
, service
, &scfg
);
258 if (!NT_STATUS_IS_OK(status
)) {
259 DEBUG(0,("smb2srv_tcon_backend: couldn't find service %s\n", service
));
260 return NT_STATUS_BAD_NETWORK_NAME
;
263 if (!socket_check_access(req
->smb_conn
->connection
->socket
,
265 share_string_list_option(req
, scfg
, SHARE_HOSTS_ALLOW
),
266 share_string_list_option(req
, scfg
, SHARE_HOSTS_DENY
))) {
267 return NT_STATUS_ACCESS_DENIED
;
270 /* work out what sort of connection this is */
271 sharetype
= share_string_option(scfg
, SHARE_TYPE
, "DISK");
272 if (sharetype
&& strcmp(sharetype
, "IPC") == 0) {
274 } else if (sharetype
&& strcmp(sharetype
, "PRINTER") == 0) {
280 tcon
= smbsrv_smb2_tcon_new(req
->session
, scfg
->name
);
282 DEBUG(0,("smb2srv_tcon_backend: Couldn't find free connection.\n"));
283 return NT_STATUS_INSUFFICIENT_RESOURCES
;
287 ntvfs_caps
= NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS
;
289 /* init ntvfs function pointers */
290 status
= ntvfs_init_connection(tcon
, scfg
, type
,
291 req
->smb_conn
->negotiate
.protocol
,
293 req
->smb_conn
->connection
->event
.ctx
,
294 req
->smb_conn
->connection
->msg_ctx
,
295 req
->smb_conn
->lp_ctx
,
296 req
->smb_conn
->connection
->server_id
,
298 if (!NT_STATUS_IS_OK(status
)) {
299 DEBUG(0, ("smb2srv_tcon_backend: ntvfs_init_connection failed for service %s\n",
304 status
= ntvfs_set_oplock_handler(tcon
->ntvfs
, smb2srv_send_oplock_break
, tcon
);
305 if (!NT_STATUS_IS_OK(status
)) {
306 DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the oplock handler!\n"));
310 status
= ntvfs_set_addr_callbacks(tcon
->ntvfs
, smbsrv_get_my_addr
, smbsrv_get_peer_addr
, req
->smb_conn
);
311 if (!NT_STATUS_IS_OK(status
)) {
312 DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the addr callbacks!\n"));
316 status
= ntvfs_set_handle_callbacks(tcon
->ntvfs
,
317 smb2srv_handle_create_new
,
318 smb2srv_handle_make_valid
,
319 smb2srv_handle_destroy
,
320 smb2srv_handle_search_by_wire_key
,
321 smb2srv_handle_get_wire_key
,
323 if (!NT_STATUS_IS_OK(status
)) {
324 DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the handle callbacks!\n"));
328 req
->ntvfs
= ntvfs_request_create(req
->tcon
->ntvfs
, req
,
329 req
->session
->session_info
,
330 SVAL(req
->in
.hdr
, SMB2_HDR_PID
),
334 status
= NT_STATUS_NO_MEMORY
;
338 io
->smb2
.out
.share_type
= (unsigned)type
; /* 1 - DISK, 2 - Print, 3 - IPC */
339 io
->smb2
.out
.reserved
= 0;
340 io
->smb2
.out
.flags
= 0x00000000;
341 io
->smb2
.out
.capabilities
= 0;
342 io
->smb2
.out
.access_mask
= SEC_RIGHTS_FILE_ALL
;
344 io
->smb2
.out
.tid
= tcon
->tid
;
346 /* Invoke NTVFS connection hook */
347 status
= ntvfs_connect(req
->ntvfs
, io
);
348 if (!NT_STATUS_IS_OK(status
)) {
349 DEBUG(0,("smb2srv_tcon_backend: NTVFS ntvfs_connect() failed!\n"));
361 static void smb2srv_tcon_send(struct smb2srv_request
*req
, union smb_tcon
*io
)
365 if (!NT_STATUS_IS_OK(req
->status
)) {
366 smb2srv_send_error(req
, req
->status
);
369 if (io
->smb2
.out
.share_type
== NTVFS_IPC
) {
370 /* if it's an IPC share vista returns 0x0005 */
376 SMB2SRV_CHECK(smb2srv_setup_reply(req
, 0x10, false, 0));
378 SIVAL(req
->out
.hdr
, SMB2_HDR_TID
, io
->smb2
.out
.tid
);
379 SSVAL(req
->out
.hdr
, SMB2_HDR_CREDIT
,credit
);
381 SCVAL(req
->out
.body
, 0x02, io
->smb2
.out
.share_type
);
382 SCVAL(req
->out
.body
, 0x03, io
->smb2
.out
.reserved
);
383 SIVAL(req
->out
.body
, 0x04, io
->smb2
.out
.flags
);
384 SIVAL(req
->out
.body
, 0x08, io
->smb2
.out
.capabilities
);
385 SIVAL(req
->out
.body
, 0x0C, io
->smb2
.out
.access_mask
);
387 smb2srv_send_reply(req
);
390 void smb2srv_tcon_recv(struct smb2srv_request
*req
)
394 SMB2SRV_CHECK_BODY_SIZE(req
, 0x08, true);
395 SMB2SRV_TALLOC_IO_PTR(io
, union smb_tcon
);
397 io
->smb2
.level
= RAW_TCON_SMB2
;
398 io
->smb2
.in
.reserved
= SVAL(req
->in
.body
, 0x02);
399 SMB2SRV_CHECK(smb2_pull_o16s16_string(&req
->in
, io
, req
->in
.body
+0x04, &io
->smb2
.in
.path
));
401 /* the VFS backend does not yet handle NULL paths */
402 if (io
->smb2
.in
.path
== NULL
) {
403 io
->smb2
.in
.path
= "";
406 req
->status
= smb2srv_tcon_backend(req
, io
);
408 if (req
->control_flags
& SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY
) {
412 smb2srv_tcon_send(req
, io
);
415 static NTSTATUS
smb2srv_tdis_backend(struct smb2srv_request
*req
)
417 /* TODO: call ntvfs backends to close file of this tcon */
418 talloc_free(req
->tcon
);
423 static void smb2srv_tdis_send(struct smb2srv_request
*req
)
427 if (NT_STATUS_IS_ERR(req
->status
)) {
428 smb2srv_send_error(req
, req
->status
);
432 status
= smb2srv_setup_reply(req
, 0x04, false, 0);
433 if (!NT_STATUS_IS_OK(status
)) {
434 smbsrv_terminate_connection(req
->smb_conn
, nt_errstr(status
));
439 SSVAL(req
->out
.body
, 0x02, 0);
441 smb2srv_send_reply(req
);
444 void smb2srv_tdis_recv(struct smb2srv_request
*req
)
448 SMB2SRV_CHECK_BODY_SIZE(req
, 0x04, false);
450 _pad
= SVAL(req
->in
.body
, 0x02);
452 req
->status
= smb2srv_tdis_backend(req
);
454 if (req
->control_flags
& SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY
) {
458 smb2srv_tdis_send(req
);