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/>.
23 #include "system/network.h"
25 #include "lib/util/tevent_ntstatus.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/smb2/smb2.h"
28 #include "libcli/smb2/smb2_calls.h"
29 #include "auth/gensec/gensec.h"
30 #include "auth/credentials/credentials.h"
31 #include "../libcli/smb/smbXcli_base.h"
32 #include "../source3/libsmb/smb2cli.h"
35 initialise a smb2_session structure
37 struct smb2_session
*smb2_session_init(struct smb2_transport
*transport
,
38 struct gensec_settings
*settings
,
39 TALLOC_CTX
*parent_ctx
, bool primary
)
41 struct smb2_session
*session
;
44 session
= talloc_zero(parent_ctx
, struct smb2_session
);
49 session
->transport
= talloc_steal(session
, transport
);
51 session
->transport
= talloc_reference(session
, transport
);
54 session
->pid
= getpid();
56 session
->smbXcli
= smbXcli_session_create(session
, transport
->conn
);
57 if (session
->smbXcli
== NULL
) {
62 /* prepare a gensec context for later use */
63 status
= gensec_client_start(session
, &session
->gensec
,
65 if (!NT_STATUS_IS_OK(status
)) {
70 gensec_want_feature(session
->gensec
, GENSEC_FEATURE_SESSION_KEY
);
75 struct smb2_session_setup_spnego_state
{
76 struct tevent_context
*ev
;
77 struct smb2_session
*session
;
78 struct cli_credentials
*credentials
;
79 uint64_t previous_session_id
;
81 NTSTATUS gensec_status
;
83 DATA_BLOB out_secblob
;
86 static void smb2_session_setup_spnego_done(struct tevent_req
*subreq
);
89 a composite function that does a full SPNEGO session setup
91 struct tevent_req
*smb2_session_setup_spnego_send(
93 struct tevent_context
*ev
,
94 struct smb2_session
*session
,
95 struct cli_credentials
*credentials
,
96 uint64_t previous_session_id
)
98 struct tevent_req
*req
;
99 struct smb2_session_setup_spnego_state
*state
;
100 uint64_t current_session_id
;
101 const char *chosen_oid
;
102 struct tevent_req
*subreq
;
104 const DATA_BLOB
*server_gss_blob
;
105 DATA_BLOB negprot_secblob
= data_blob_null
;
106 uint32_t timeout_msec
;
108 timeout_msec
= session
->transport
->options
.request_timeout
* 1000;
110 req
= tevent_req_create(mem_ctx
, &state
,
111 struct smb2_session_setup_spnego_state
);
116 state
->session
= session
;
117 state
->credentials
= credentials
;
118 state
->previous_session_id
= previous_session_id
;
120 current_session_id
= smb2cli_session_current_id(state
->session
->smbXcli
);
121 if (current_session_id
!= 0) {
122 state
->reauth
= true;
125 server_gss_blob
= smbXcli_conn_server_gss_blob(session
->transport
->conn
);
126 if (server_gss_blob
) {
127 negprot_secblob
= *server_gss_blob
;
130 status
= gensec_set_credentials(session
->gensec
, credentials
);
131 if (tevent_req_nterror(req
, status
)) {
132 return tevent_req_post(req
, ev
);
135 status
= gensec_set_target_hostname(session
->gensec
,
136 smbXcli_conn_remote_name(session
->transport
->conn
));
137 if (tevent_req_nterror(req
, status
)) {
138 return tevent_req_post(req
, ev
);
141 status
= gensec_set_target_service(session
->gensec
, "cifs");
142 if (tevent_req_nterror(req
, status
)) {
143 return tevent_req_post(req
, ev
);
146 if (negprot_secblob
.length
> 0) {
147 chosen_oid
= GENSEC_OID_SPNEGO
;
149 chosen_oid
= GENSEC_OID_NTLMSSP
;
152 status
= gensec_start_mech_by_oid(session
->gensec
, chosen_oid
);
153 if (tevent_req_nterror(req
, status
)) {
154 return tevent_req_post(req
, ev
);
157 status
= gensec_update(session
->gensec
, state
,
161 if (!NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
162 tevent_req_nterror(req
, status
);
163 return tevent_req_post(req
, ev
);
165 state
->gensec_status
= status
;
167 subreq
= smb2cli_session_setup_send(state
, state
->ev
,
168 session
->transport
->conn
,
172 0, /* in_capabilities */
174 state
->previous_session_id
,
176 if (tevent_req_nomem(subreq
, req
)) {
177 return tevent_req_post(req
, ev
);
179 tevent_req_set_callback(subreq
, smb2_session_setup_spnego_done
, req
);
185 handle continuations of the spnego session setup
187 static void smb2_session_setup_spnego_done(struct tevent_req
*subreq
)
189 struct tevent_req
*req
=
190 tevent_req_callback_data(subreq
,
192 struct smb2_session_setup_spnego_state
*state
=
194 struct smb2_session_setup_spnego_state
);
195 struct smb2_session
*session
= state
->session
;
196 NTSTATUS peer_status
;
198 struct iovec
*recv_iov
;
199 uint32_t timeout_msec
;
201 timeout_msec
= session
->transport
->options
.request_timeout
* 1000;
203 status
= smb2cli_session_setup_recv(subreq
, state
,
205 &state
->out_secblob
);
206 if (!NT_STATUS_IS_OK(status
) &&
207 !NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
208 tevent_req_nterror(req
, status
);
211 peer_status
= status
;
213 if (NT_STATUS_EQUAL(state
->gensec_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
214 status
= gensec_update(session
->gensec
, state
,
218 state
->gensec_status
= status
;
221 if (!NT_STATUS_IS_OK(status
) &&
222 !NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
223 tevent_req_nterror(req
, status
);
227 if (NT_STATUS_IS_OK(peer_status
) && NT_STATUS_IS_OK(state
->gensec_status
)) {
228 DATA_BLOB session_key
;
231 tevent_req_done(req
);
235 if (cli_credentials_is_anonymous(state
->credentials
)) {
237 * Windows server does not set the
238 * SMB2_SESSION_FLAG_IS_GUEST nor
239 * SMB2_SESSION_FLAG_IS_NULL flag.
241 * This fix makes sure we do not try
242 * to verify a signature on the final
243 * session setup response.
245 tevent_req_done(req
);
249 status
= gensec_session_key(session
->gensec
, state
,
251 if (tevent_req_nterror(req
, status
)) {
255 status
= smb2cli_session_set_session_key(session
->smbXcli
,
258 if (tevent_req_nterror(req
, status
)) {
262 tevent_req_done(req
);
266 subreq
= smb2cli_session_setup_send(state
, state
->ev
,
267 session
->transport
->conn
,
271 0, /* in_capabilities */
273 state
->previous_session_id
,
275 if (tevent_req_nomem(subreq
, req
)) {
278 tevent_req_set_callback(subreq
, smb2_session_setup_spnego_done
, req
);
282 receive a composite session setup reply
284 NTSTATUS
smb2_session_setup_spnego_recv(struct tevent_req
*req
)
286 return tevent_req_simple_recv_ntstatus(req
);
290 sync version of smb2_session_setup_spnego
292 NTSTATUS
smb2_session_setup_spnego(struct smb2_session
*session
,
293 struct cli_credentials
*credentials
,
294 uint64_t previous_session_id
)
296 struct tevent_req
*subreq
;
299 TALLOC_CTX
*frame
= talloc_stackframe();
300 struct tevent_context
*ev
= session
->transport
->ev
;
303 return NT_STATUS_NO_MEMORY
;
306 subreq
= smb2_session_setup_spnego_send(frame
, ev
,
307 session
, credentials
,
308 previous_session_id
);
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
)) {