2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 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 2 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, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 a composite API for making handling a generic async session setup
25 #include "libcli/raw/libcliraw.h"
26 #include "libcli/composite/composite.h"
27 #include "auth/auth.h"
30 struct sesssetup_state
{
31 union smb_sesssetup setup
;
32 NTSTATUS gensec_status
;
33 struct smb_composite_sesssetup
*io
;
34 struct smbcli_request
*req
;
39 form an encrypted lanman password from a plaintext password
40 and the server supplied challenge
42 static DATA_BLOB
lanman_blob(TALLOC_CTX
*mem_ctx
, const char *pass
, DATA_BLOB challenge
)
44 DATA_BLOB blob
= data_blob_talloc(mem_ctx
, NULL
, 24);
45 SMBencrypt(pass
, challenge
.data
, blob
.data
);
50 form an encrypted NT password from a plaintext password
51 and the server supplied challenge
53 static DATA_BLOB
nt_blob(TALLOC_CTX
*mem_ctx
, const struct samr_Password
*nt_hash
, DATA_BLOB challenge
)
55 DATA_BLOB blob
= data_blob_talloc(mem_ctx
, NULL
, 24);
56 SMBOWFencrypt(nt_hash
->hash
, challenge
.data
, blob
.data
);
61 store the user session key for a transport
63 static void set_user_session_key(struct smbcli_session
*session
,
64 const DATA_BLOB
*session_key
)
66 session
->user_session_key
= data_blob_talloc(session
,
72 handler for completion of a smbcli_request sub-request
74 static void request_handler(struct smbcli_request
*req
)
76 struct composite_context
*c
= req
->async
.private;
77 struct sesssetup_state
*state
= talloc_get_type(c
->private, struct sesssetup_state
);
78 struct smbcli_session
*session
= req
->session
;
79 DATA_BLOB session_key
= data_blob(NULL
, 0);
80 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
81 NTSTATUS session_key_err
;
83 c
->status
= smb_raw_sesssetup_recv(req
, state
, &state
->setup
);
85 switch (state
->setup
.old
.level
) {
86 case RAW_SESSSETUP_OLD
:
87 state
->io
->out
.vuid
= state
->setup
.old
.out
.vuid
;
90 case RAW_SESSSETUP_NT1
:
91 state
->io
->out
.vuid
= state
->setup
.nt1
.out
.vuid
;
94 case RAW_SESSSETUP_SPNEGO
:
95 session
->vuid
= state
->io
->out
.vuid
= state
->setup
.spnego
.out
.vuid
;
96 if (!NT_STATUS_EQUAL(c
->status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) &&
97 !NT_STATUS_IS_OK(c
->status
)) {
100 if (NT_STATUS_EQUAL(state
->gensec_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
102 /* The status value here, from the earlier pass at GENSEC is
103 * vital to the security of the system. Even if the other end
104 * accepts, if GENSEC claims 'MORE_PROCESSING_REQUIRED' then
105 * you must keep feeding it blobs, or else the remote
106 * host/attacker might avoid mutal authentication
109 state
->gensec_status
= gensec_update(session
->gensec
, state
,
110 state
->setup
.spnego
.out
.secblob
,
111 &state
->setup
.spnego
.in
.secblob
);
112 c
->status
= state
->gensec_status
;
113 if (!NT_STATUS_EQUAL(c
->status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) &&
114 !NT_STATUS_IS_OK(c
->status
)) {
118 state
->setup
.spnego
.in
.secblob
= data_blob(NULL
, 0);
121 /* we need to do another round of session setup. We keep going until both sides
123 session_key_err
= gensec_session_key(session
->gensec
, &session_key
);
124 if (NT_STATUS_IS_OK(session_key_err
)) {
125 set_user_session_key(session
, &session_key
);
126 smbcli_transport_simple_set_signing(session
->transport
, session_key
, null_data_blob
);
129 if (state
->setup
.spnego
.in
.secblob
.length
) {
130 state
->req
= smb_raw_sesssetup_send(session
, &state
->setup
);
131 state
->req
->async
.fn
= request_handler
;
132 state
->req
->async
.private = c
;
137 /* enforce the local signing required flag */
138 if (NT_STATUS_IS_OK(c
->status
) && !cli_credentials_is_anonymous(state
->io
->in
.credentials
)) {
139 if (!session
->transport
->negotiate
.sign_info
.doing_signing
140 && session
->transport
->negotiate
.sign_info
.mandatory_signing
) {
141 DEBUG(0, ("SMB signing required, but server does not support it\n"));
142 c
->status
= NT_STATUS_ACCESS_DENIED
;
146 if (NT_STATUS_IS_OK(c
->status
)) {
147 c
->state
= SMBCLI_REQUEST_DONE
;
149 c
->state
= SMBCLI_REQUEST_ERROR
;
158 send a nt1 style session setup
160 static NTSTATUS
session_setup_nt1(struct composite_context
*c
,
161 struct smbcli_session
*session
,
162 struct smb_composite_sesssetup
*io
,
163 struct smbcli_request
**req
)
165 struct sesssetup_state
*state
= talloc_get_type(c
->private, struct sesssetup_state
);
166 const struct samr_Password
*nt_hash
= cli_credentials_get_nt_hash(io
->in
.credentials
, state
);
167 const char *password
= cli_credentials_get_password(io
->in
.credentials
);
169 state
->setup
.nt1
.level
= RAW_SESSSETUP_NT1
;
170 state
->setup
.nt1
.in
.bufsize
= session
->transport
->options
.max_xmit
;
171 state
->setup
.nt1
.in
.mpx_max
= session
->transport
->options
.max_mux
;
172 state
->setup
.nt1
.in
.vc_num
= 1;
173 state
->setup
.nt1
.in
.sesskey
= io
->in
.sesskey
;
174 state
->setup
.nt1
.in
.capabilities
= io
->in
.capabilities
;
175 state
->setup
.nt1
.in
.os
= "Unix";
176 state
->setup
.nt1
.in
.lanman
= talloc_asprintf(state
, "Samba %s", SAMBA_VERSION_STRING
);
177 state
->setup
.nt1
.in
.user
= cli_credentials_get_username(io
->in
.credentials
);
178 state
->setup
.nt1
.in
.domain
= cli_credentials_get_domain(io
->in
.credentials
);
181 state
->setup
.nt1
.in
.password1
= data_blob(NULL
, 0);
182 state
->setup
.nt1
.in
.password2
= data_blob(NULL
, 0);
183 } else if (session
->transport
->negotiate
.sec_mode
&
184 NEGOTIATE_SECURITY_CHALLENGE_RESPONSE
) {
185 DATA_BLOB session_key
;
186 if (lp_client_ntlmv2_auth()) {
187 DATA_BLOB names_blob
= NTLMv2_generate_names_blob(state
, lp_netbios_name(), lp_workgroup());
188 DATA_BLOB lmv2_response
, ntlmv2_response
, lmv2_session_key
;
190 if (!SMBNTLMv2encrypt_hash(state
,
191 state
->setup
.nt1
.in
.user
, state
->setup
.nt1
.in
.domain
,
192 nt_hash
->hash
, &session
->transport
->negotiate
.secblob
,
194 &lmv2_response
, &ntlmv2_response
,
195 &lmv2_session_key
, &session_key
)) {
196 data_blob_free(&names_blob
);
197 return NT_STATUS_NO_MEMORY
;
199 data_blob_free(&names_blob
);
200 state
->setup
.nt1
.in
.password1
= lmv2_response
;
201 state
->setup
.nt1
.in
.password2
= ntlmv2_response
;
203 smbcli_transport_simple_set_signing(session
->transport
, session_key
,
204 state
->setup
.nt1
.in
.password2
);
205 set_user_session_key(session
, &session_key
);
207 data_blob_free(&lmv2_session_key
);
208 data_blob_free(&session_key
);
211 state
->setup
.nt1
.in
.password2
= nt_blob(state
, nt_hash
,
212 session
->transport
->negotiate
.secblob
);
213 if (lp_client_lanman_auth()) {
214 state
->setup
.nt1
.in
.password1
= lanman_blob(state
, password
,
215 session
->transport
->negotiate
.secblob
);
217 /* if not sending the LM password, send the NT password twice */
218 state
->setup
.nt1
.in
.password1
= state
->setup
.nt1
.in
.password2
;
221 session_key
= data_blob_talloc(session
, NULL
, 16);
222 SMBsesskeygen_ntv1(nt_hash
->hash
, session_key
.data
);
223 smbcli_transport_simple_set_signing(session
->transport
, session_key
,
224 state
->setup
.nt1
.in
.password2
);
225 set_user_session_key(session
, &session_key
);
227 data_blob_free(&session_key
);
230 } else if (lp_client_plaintext_auth()) {
231 state
->setup
.nt1
.in
.password1
= data_blob_talloc(state
, password
, strlen(password
));
232 state
->setup
.nt1
.in
.password2
= data_blob(NULL
, 0);
234 /* could match windows client and return 'cannot logon from this workstation', but it just confuses everybody */
235 return NT_STATUS_INVALID_PARAMETER
;
238 *req
= smb_raw_sesssetup_send(session
, &state
->setup
);
240 return NT_STATUS_NO_MEMORY
;
242 return (*req
)->status
;
247 old style session setup (pre NT1 protocol level)
249 static NTSTATUS
session_setup_old(struct composite_context
*c
,
250 struct smbcli_session
*session
,
251 struct smb_composite_sesssetup
*io
,
252 struct smbcli_request
**req
)
254 struct sesssetup_state
*state
= talloc_get_type(c
->private, struct sesssetup_state
);
255 const char *password
= cli_credentials_get_password(io
->in
.credentials
);
257 state
->setup
.old
.level
= RAW_SESSSETUP_OLD
;
258 state
->setup
.old
.in
.bufsize
= session
->transport
->options
.max_xmit
;
259 state
->setup
.old
.in
.mpx_max
= session
->transport
->options
.max_mux
;
260 state
->setup
.old
.in
.vc_num
= 1;
261 state
->setup
.old
.in
.sesskey
= io
->in
.sesskey
;
262 state
->setup
.old
.in
.domain
= cli_credentials_get_domain(io
->in
.credentials
);
263 state
->setup
.old
.in
.user
= cli_credentials_get_username(io
->in
.credentials
);
264 state
->setup
.old
.in
.os
= "Unix";
265 state
->setup
.old
.in
.lanman
= talloc_asprintf(state
, "Samba %s", SAMBA_VERSION_STRING
);
268 state
->setup
.old
.in
.password
= data_blob(NULL
, 0);
269 } else if (session
->transport
->negotiate
.sec_mode
& NEGOTIATE_SECURITY_CHALLENGE_RESPONSE
) {
270 state
->setup
.old
.in
.password
= lanman_blob(state
, password
,
271 session
->transport
->negotiate
.secblob
);
273 state
->setup
.old
.in
.password
= data_blob_talloc(state
,
278 *req
= smb_raw_sesssetup_send(session
, &state
->setup
);
280 return NT_STATUS_NO_MEMORY
;
282 return (*req
)->status
;
287 Modern, all singing, all dancing extended security (and possibly SPNEGO) request
289 static NTSTATUS
session_setup_spnego(struct composite_context
*c
,
290 struct smbcli_session
*session
,
291 struct smb_composite_sesssetup
*io
,
292 struct smbcli_request
**req
)
294 struct sesssetup_state
*state
= talloc_get_type(c
->private, struct sesssetup_state
);
295 NTSTATUS status
, session_key_err
;
296 DATA_BLOB session_key
= data_blob(NULL
, 0);
297 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
298 const char *chosen_oid
= NULL
;
300 state
->setup
.spnego
.level
= RAW_SESSSETUP_SPNEGO
;
301 state
->setup
.spnego
.in
.bufsize
= session
->transport
->options
.max_xmit
;
302 state
->setup
.spnego
.in
.mpx_max
= session
->transport
->options
.max_mux
;
303 state
->setup
.spnego
.in
.vc_num
= 1;
304 state
->setup
.spnego
.in
.sesskey
= io
->in
.sesskey
;
305 state
->setup
.spnego
.in
.capabilities
= io
->in
.capabilities
;
306 state
->setup
.spnego
.in
.os
= "Unix";
307 state
->setup
.spnego
.in
.lanman
= talloc_asprintf(state
, "Samba %s", SAMBA_VERSION_STRING
);
308 state
->setup
.spnego
.in
.workgroup
= io
->in
.workgroup
;
310 state
->setup
.spnego
.out
.vuid
= session
->vuid
;
312 smbcli_temp_set_signing(session
->transport
);
314 status
= gensec_client_start(session
, &session
->gensec
, c
->event_ctx
);
315 if (!NT_STATUS_IS_OK(status
)) {
316 DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status
)));
320 gensec_want_feature(session
->gensec
, GENSEC_FEATURE_SESSION_KEY
);
322 status
= gensec_set_credentials(session
->gensec
, io
->in
.credentials
);
323 if (!NT_STATUS_IS_OK(status
)) {
324 DEBUG(1, ("Failed to start set GENSEC client credentails: %s\n",
329 status
= gensec_set_target_hostname(session
->gensec
, session
->transport
->socket
->hostname
);
330 if (!NT_STATUS_IS_OK(status
)) {
331 DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n",
336 status
= gensec_set_target_service(session
->gensec
, "cifs");
337 if (!NT_STATUS_IS_OK(status
)) {
338 DEBUG(1, ("Failed to start set GENSEC target service: %s\n",
343 if (session
->transport
->negotiate
.secblob
.length
) {
344 chosen_oid
= GENSEC_OID_SPNEGO
;
346 /* without a sec blob, means raw NTLMSSP */
347 chosen_oid
= GENSEC_OID_NTLMSSP
;
350 status
= gensec_start_mech_by_oid(session
->gensec
, chosen_oid
);
351 if (!NT_STATUS_IS_OK(status
)) {
352 DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism %s: %s\n",
353 gensec_get_name_by_oid(chosen_oid
), nt_errstr(status
)));
357 status
= gensec_update(session
->gensec
, state
,
358 session
->transport
->negotiate
.secblob
,
359 &state
->setup
.spnego
.in
.secblob
);
360 if (!NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) &&
361 !NT_STATUS_IS_OK(status
)) {
362 DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n",
363 gensec_get_name_by_oid(chosen_oid
), nt_errstr(status
)));
366 state
->gensec_status
= status
;
368 session_key_err
= gensec_session_key(session
->gensec
, &session_key
);
369 if (NT_STATUS_IS_OK(session_key_err
)) {
370 smbcli_transport_simple_set_signing(session
->transport
, session_key
, null_data_blob
);
373 *req
= smb_raw_sesssetup_send(session
, &state
->setup
);
375 return NT_STATUS_NO_MEMORY
;
377 return (*req
)->status
;
382 composite session setup function that hides the details of all the
383 different session setup varients, including the multi-pass nature of
386 struct composite_context
*smb_composite_sesssetup_send(struct smbcli_session
*session
,
387 struct smb_composite_sesssetup
*io
)
389 struct composite_context
*c
;
390 struct sesssetup_state
*state
;
393 c
= talloc_zero(session
, struct composite_context
);
394 if (c
== NULL
) return NULL
;
396 state
= talloc(c
, struct sesssetup_state
);
398 c
->state
= SMBCLI_REQUEST_ERROR
;
399 c
->status
= NT_STATUS_NO_MEMORY
;
404 c
->state
= SMBCLI_REQUEST_SEND
;
406 c
->event_ctx
= session
->transport
->socket
->event
.ctx
;
408 /* no session setup at all in earliest protocol varients */
409 if (session
->transport
->negotiate
.protocol
< PROTOCOL_LANMAN1
) {
410 ZERO_STRUCT(io
->out
);
411 c
->state
= SMBCLI_REQUEST_DONE
;
415 /* see what session setup interface we will use */
416 if (session
->transport
->negotiate
.protocol
< PROTOCOL_NT1
) {
417 status
= session_setup_old(c
, session
, io
, &state
->req
);
418 } else if (!session
->transport
->options
.use_spnego
||
419 !(io
->in
.capabilities
& CAP_EXTENDED_SECURITY
)) {
420 status
= session_setup_nt1(c
, session
, io
, &state
->req
);
422 status
= session_setup_spnego(c
, session
, io
, &state
->req
);
425 if (NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) ||
426 NT_STATUS_IS_OK(status
)) {
427 state
->req
->async
.fn
= request_handler
;
428 state
->req
->async
.private = c
;
432 c
->state
= SMBCLI_REQUEST_ERROR
;
439 receive a composite session setup reply
441 NTSTATUS
smb_composite_sesssetup_recv(struct composite_context
*c
)
444 status
= composite_wait(c
);
450 sync version of smb_composite_sesssetup
452 NTSTATUS
smb_composite_sesssetup(struct smbcli_session
*session
, struct smb_composite_sesssetup
*io
)
454 struct composite_context
*c
= smb_composite_sesssetup_send(session
, io
);
455 return smb_composite_sesssetup_recv(c
);