2 Unix SMB/CIFS implementation.
4 SMB2 client session 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/>.
24 #include "lib/util/tevent_ntstatus.h"
25 #include "libcli/raw/libcliraw.h"
26 #include "libcli/smb2/smb2.h"
27 #include "libcli/smb2/smb2_calls.h"
28 #include "auth/gensec/gensec.h"
33 initialise a smb2_session structure
35 struct smb2_session
*smb2_session_init(struct smb2_transport
*transport
,
36 struct gensec_settings
*settings
,
37 TALLOC_CTX
*parent_ctx
, bool primary
)
39 struct smb2_session
*session
;
42 session
= talloc_zero(parent_ctx
, struct smb2_session
);
47 session
->transport
= talloc_steal(session
, transport
);
49 session
->transport
= talloc_reference(session
, transport
);
52 session
->pid
= getpid();
54 /* prepare a gensec context for later use */
55 status
= gensec_client_start(session
, &session
->gensec
,
56 session
->transport
->socket
->event
.ctx
,
58 if (!NT_STATUS_IS_OK(status
)) {
63 gensec_want_feature(session
->gensec
, GENSEC_FEATURE_SESSION_KEY
);
69 send a session setup request
71 struct smb2_request
*smb2_session_setup_send(struct smb2_session
*session
,
72 struct smb2_session_setup
*io
)
74 struct smb2_request
*req
;
77 req
= smb2_request_init(session
->transport
, SMB2_OP_SESSSETUP
,
78 0x18, true, io
->in
.secblob
.length
);
79 if (req
== NULL
) return NULL
;
81 SBVAL(req
->out
.hdr
, SMB2_HDR_SESSION_ID
, session
->uid
);
82 SCVAL(req
->out
.body
, 0x02, io
->in
.vc_number
);
83 SCVAL(req
->out
.body
, 0x03, io
->in
.security_mode
);
84 SIVAL(req
->out
.body
, 0x04, io
->in
.capabilities
);
85 SIVAL(req
->out
.body
, 0x08, io
->in
.channel
);
86 SBVAL(req
->out
.body
, 0x10, io
->in
.previous_sessionid
);
88 req
->session
= session
;
90 status
= smb2_push_o16s16_blob(&req
->out
, 0x0C, io
->in
.secblob
);
91 if (!NT_STATUS_IS_OK(status
)) {
96 smb2_transport_send(req
);
103 recv a session setup reply
105 NTSTATUS
smb2_session_setup_recv(struct smb2_request
*req
, TALLOC_CTX
*mem_ctx
,
106 struct smb2_session_setup
*io
)
110 if (!smb2_request_receive(req
) ||
111 (smb2_request_is_error(req
) &&
112 !NT_STATUS_EQUAL(req
->status
, NT_STATUS_MORE_PROCESSING_REQUIRED
))) {
113 return smb2_request_destroy(req
);
116 SMB2_CHECK_PACKET_RECV(req
, 0x08, true);
118 io
->out
.session_flags
= SVAL(req
->in
.body
, 0x02);
119 io
->out
.uid
= BVAL(req
->in
.hdr
, SMB2_HDR_SESSION_ID
);
121 status
= smb2_pull_o16s16_blob(&req
->in
, mem_ctx
, req
->in
.body
+0x04, &io
->out
.secblob
);
122 if (!NT_STATUS_IS_OK(status
)) {
123 smb2_request_destroy(req
);
127 return smb2_request_destroy(req
);
131 sync session setup request
133 NTSTATUS
smb2_session_setup(struct smb2_session
*session
,
134 TALLOC_CTX
*mem_ctx
, struct smb2_session_setup
*io
)
136 struct smb2_request
*req
= smb2_session_setup_send(session
, io
);
137 return smb2_session_setup_recv(req
, mem_ctx
, io
);
140 struct smb2_session_setup_spnego_state
{
141 struct smb2_session_setup io
;
142 struct smb2_request
*req
;
143 NTSTATUS gensec_status
;
146 static void smb2_session_setup_spnego_handler(struct smb2_request
*req
);
149 a composite function that does a full SPNEGO session setup
151 struct tevent_req
*smb2_session_setup_spnego_send(TALLOC_CTX
*mem_ctx
,
152 struct tevent_context
*ev
,
153 struct smb2_session
*session
,
154 struct cli_credentials
*credentials
)
156 struct tevent_req
*req
;
157 struct smb2_session_setup_spnego_state
*state
;
158 const char *chosen_oid
;
159 struct smb2_request
*subreq
;
162 req
= tevent_req_create(mem_ctx
, &state
,
163 struct smb2_session_setup_spnego_state
);
168 ZERO_STRUCT(state
->io
);
169 state
->io
.in
.vc_number
= 0;
170 if (session
->transport
->signing_required
) {
171 state
->io
.in
.security_mode
=
172 SMB2_NEGOTIATE_SIGNING_ENABLED
| SMB2_NEGOTIATE_SIGNING_REQUIRED
;
174 state
->io
.in
.capabilities
= 0;
175 state
->io
.in
.channel
= 0;
176 state
->io
.in
.previous_sessionid
= 0;
178 status
= gensec_set_credentials(session
->gensec
, credentials
);
179 if (tevent_req_nterror(req
, status
)) {
180 return tevent_req_post(req
, ev
);
183 status
= gensec_set_target_hostname(session
->gensec
,
184 session
->transport
->socket
->hostname
);
185 if (tevent_req_nterror(req
, status
)) {
186 return tevent_req_post(req
, ev
);
189 status
= gensec_set_target_service(session
->gensec
, "cifs");
190 if (tevent_req_nterror(req
, status
)) {
191 return tevent_req_post(req
, ev
);
194 if (session
->transport
->negotiate
.secblob
.length
> 0) {
195 chosen_oid
= GENSEC_OID_SPNEGO
;
197 chosen_oid
= GENSEC_OID_NTLMSSP
;
200 status
= gensec_start_mech_by_oid(session
->gensec
, chosen_oid
);
201 if (tevent_req_nterror(req
, status
)) {
202 return tevent_req_post(req
, ev
);
205 status
= gensec_update(session
->gensec
, state
,
206 session
->transport
->negotiate
.secblob
,
207 &state
->io
.in
.secblob
);
208 if (!NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
209 tevent_req_nterror(req
, status
);
210 return tevent_req_post(req
, ev
);
212 state
->gensec_status
= status
;
214 subreq
= smb2_session_setup_send(session
, &state
->io
);
215 if (tevent_req_nomem(subreq
, req
)) {
216 return tevent_req_post(req
, ev
);
218 subreq
->async
.fn
= smb2_session_setup_spnego_handler
;
219 subreq
->async
.private_data
= req
;
225 handle continuations of the spnego session setup
227 static void smb2_session_setup_spnego_handler(struct smb2_request
*subreq
)
229 struct tevent_req
*req
=
230 talloc_get_type_abort(subreq
->async
.private_data
,
232 struct smb2_session_setup_spnego_state
*state
=
234 struct smb2_session_setup_spnego_state
);
235 struct smb2_session
*session
= subreq
->session
;
236 NTSTATUS peer_status
;
239 status
= smb2_session_setup_recv(subreq
, state
, &state
->io
);
240 peer_status
= status
;
241 if (NT_STATUS_EQUAL(peer_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) ||
242 (NT_STATUS_IS_OK(peer_status
) &&
243 NT_STATUS_EQUAL(state
->gensec_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
))) {
244 status
= gensec_update(session
->gensec
, state
,
245 state
->io
.out
.secblob
,
246 &state
->io
.in
.secblob
);
247 state
->gensec_status
= status
;
248 session
->uid
= state
->io
.out
.uid
;
251 if (!NT_STATUS_IS_OK(status
) &&
252 !NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
253 tevent_req_nterror(req
, status
);
257 if (NT_STATUS_EQUAL(peer_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
258 subreq
= smb2_session_setup_send(session
, &state
->io
);
259 if (tevent_req_nomem(subreq
, req
)) {
263 subreq
->async
.fn
= smb2_session_setup_spnego_handler
;
264 subreq
->async
.private_data
= req
;
268 gensec_session_key(session
->gensec
, session
, &session
->session_key
);
270 if (session
->transport
->signing_required
) {
271 if (session
->session_key
.length
== 0) {
272 DEBUG(0,("Wrong session key length %u for SMB2 signing\n",
273 (unsigned)session
->session_key
.length
));
274 tevent_req_nterror(req
, NT_STATUS_ACCESS_DENIED
);
277 session
->signing_active
= true;
280 tevent_req_done(req
);
284 receive a composite session setup reply
286 NTSTATUS
smb2_session_setup_spnego_recv(struct tevent_req
*req
)
288 return tevent_req_simple_recv_ntstatus(req
);
292 sync version of smb2_session_setup_spnego
294 NTSTATUS
smb2_session_setup_spnego(struct smb2_session
*session
,
295 struct cli_credentials
*credentials
)
297 struct tevent_req
*subreq
;
300 TALLOC_CTX
*frame
= talloc_stackframe();
301 struct tevent_context
*ev
= session
->transport
->socket
->event
.ctx
;
304 return NT_STATUS_NO_MEMORY
;
307 subreq
= smb2_session_setup_spnego_send(frame
, ev
,
308 session
, credentials
);
309 if (subreq
== NULL
) {
311 return NT_STATUS_NO_MEMORY
;
314 ok
= tevent_req_poll(subreq
, ev
);
316 status
= map_nt_error_from_unix_common(errno
);
321 status
= smb2_session_setup_spnego_recv(subreq
);
323 if (!NT_STATUS_IS_OK(status
)) {