wafsamba: try to fix the build on AIX with xlc_r
[Samba/gebeck_regimport.git] / source4 / libcli / smb2 / session.c
blob57033b866af57c772166916fea4bc912e8262f6f
1 /*
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/>.
22 #include "includes.h"
23 #include "system/network.h"
24 #include <tevent.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"
34 /**
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;
42 NTSTATUS status;
44 session = talloc_zero(parent_ctx, struct smb2_session);
45 if (!session) {
46 return NULL;
48 if (primary) {
49 session->transport = talloc_steal(session, transport);
50 } else {
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) {
58 talloc_free(session);
59 return NULL;
62 /* prepare a gensec context for later use */
63 status = gensec_client_start(session, &session->gensec,
64 settings);
65 if (!NT_STATUS_IS_OK(status)) {
66 talloc_free(session);
67 return NULL;
70 gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
72 return session;
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;
80 bool reauth;
81 NTSTATUS gensec_status;
82 DATA_BLOB in_secblob;
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(
92 TALLOC_CTX *mem_ctx,
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;
103 NTSTATUS status;
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);
112 if (req == NULL) {
113 return NULL;
115 state->ev = ev;
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;
148 } else {
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,
158 state->ev,
159 negprot_secblob,
160 &state->in_secblob);
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,
169 timeout_msec,
170 session->smbXcli,
171 0, /* in_flags */
172 0, /* in_capabilities */
173 0, /* in_channel */
174 state->previous_session_id,
175 &state->in_secblob);
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);
181 return 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,
191 struct tevent_req);
192 struct smb2_session_setup_spnego_state *state =
193 tevent_req_data(req,
194 struct smb2_session_setup_spnego_state);
195 struct smb2_session *session = state->session;
196 NTSTATUS peer_status;
197 NTSTATUS 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,
204 &recv_iov,
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);
209 return;
211 peer_status = status;
213 if (NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
214 status = gensec_update(session->gensec, state,
215 state->ev,
216 state->out_secblob,
217 &state->in_secblob);
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);
224 return;
227 if (NT_STATUS_IS_OK(peer_status) && NT_STATUS_IS_OK(state->gensec_status)) {
228 DATA_BLOB session_key;
230 if (state->reauth) {
231 tevent_req_done(req);
232 return;
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);
246 return;
249 status = gensec_session_key(session->gensec, state,
250 &session_key);
251 if (tevent_req_nterror(req, status)) {
252 return;
255 status = smb2cli_session_set_session_key(session->smbXcli,
256 session_key,
257 recv_iov);
258 if (tevent_req_nterror(req, status)) {
259 return;
262 tevent_req_done(req);
263 return;
266 subreq = smb2cli_session_setup_send(state, state->ev,
267 session->transport->conn,
268 timeout_msec,
269 session->smbXcli,
270 0, /* in_flags */
271 0, /* in_capabilities */
272 0, /* in_channel */
273 state->previous_session_id,
274 &state->in_secblob);
275 if (tevent_req_nomem(subreq, req)) {
276 return;
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;
297 NTSTATUS status;
298 bool ok;
299 TALLOC_CTX *frame = talloc_stackframe();
300 struct tevent_context *ev = session->transport->ev;
302 if (frame == NULL) {
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) {
310 TALLOC_FREE(frame);
311 return NT_STATUS_NO_MEMORY;
314 ok = tevent_req_poll(subreq, ev);
315 if (!ok) {
316 status = map_nt_error_from_unix_common(errno);
317 TALLOC_FREE(frame);
318 return status;
321 status = smb2_session_setup_spnego_recv(subreq);
322 TALLOC_FREE(subreq);
323 if (!NT_STATUS_IS_OK(status)) {
324 TALLOC_FREE(frame);
325 return status;
328 TALLOC_FREE(frame);
329 return NT_STATUS_OK;