2 Unix SMB/Netbios implementation.
4 handle NTLMSSP, server side
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Andrew Bartlett 2001-2010
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "auth/ntlmssp/ntlmssp.h"
25 #include "auth/ntlmssp/ntlmssp_private.h"
26 #include "../librpc/gen_ndr/ndr_ntlmssp.h"
27 #include "auth/ntlmssp/ntlmssp_ndr.h"
28 #include "../libcli/auth/libcli_auth.h"
29 #include "../lib/crypto/crypto.h"
30 #include "auth/gensec/gensec.h"
31 #include "auth/gensec/gensec_internal.h"
32 #include "auth/common_auth.h"
35 * Determine correct target name flags for reply, given server role
36 * and negotiated flags
38 * @param ntlmssp_state NTLMSSP State
39 * @param neg_flags The flags from the packet
40 * @param chal_flags The flags to be set in the reply packet
41 * @return The 'target name' string.
44 const char *ntlmssp_target_name(struct ntlmssp_state *ntlmssp_state,
45 uint32_t neg_flags, uint32_t *chal_flags)
47 if (neg_flags & NTLMSSP_REQUEST_TARGET) {
48 *chal_flags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
49 *chal_flags |= NTLMSSP_REQUEST_TARGET;
50 if (ntlmssp_state->server.is_standalone) {
51 *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER;
52 return ntlmssp_state->server.netbios_name;
54 *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
55 return ntlmssp_state->server.netbios_domain;
63 * Next state function for the NTLMSSP Negotiate packet
65 * @param gensec_security GENSEC state
66 * @param out_mem_ctx Memory context for *out
67 * @param in The request, as a DATA_BLOB. reply.data must be NULL
68 * @param out The reply, as an allocated DATA_BLOB, caller to free.
69 * @return Errors or MORE_PROCESSING_REQUIRED if (normal) a reply is required.
72 NTSTATUS gensec_ntlmssp_server_negotiate(struct gensec_security *gensec_security,
73 TALLOC_CTX *out_mem_ctx,
74 const DATA_BLOB request, DATA_BLOB *reply)
76 struct gensec_ntlmssp_context *gensec_ntlmssp =
77 talloc_get_type_abort(gensec_security->private_data,
78 struct gensec_ntlmssp_context);
79 struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state;
80 struct auth4_context *auth_context = gensec_security->auth_context;
81 DATA_BLOB struct_blob;
82 uint32_t neg_flags = 0;
83 uint32_t ntlmssp_command, chal_flags;
85 const char *target_name;
88 /* parse the NTLMSSP packet */
90 file_save("ntlmssp_negotiate.dat", request.data, request.length);
94 if ((request.length < 16) || !msrpc_parse(ntlmssp_state, &request, "Cdd",
98 DEBUG(1, ("ntlmssp_server_negotiate: failed to parse NTLMSSP Negotiate of length %u\n",
99 (unsigned int)request.length));
100 dump_data(2, request.data, request.length);
101 return NT_STATUS_INVALID_PARAMETER;
103 debug_ntlmssp_flags(neg_flags);
105 if (DEBUGLEVEL >= 10) {
106 struct NEGOTIATE_MESSAGE *negotiate = talloc(
107 ntlmssp_state, struct NEGOTIATE_MESSAGE);
108 if (negotiate != NULL) {
109 status = ntlmssp_pull_NEGOTIATE_MESSAGE(
110 &request, negotiate, negotiate);
111 if (NT_STATUS_IS_OK(status)) {
112 NDR_PRINT_DEBUG(NEGOTIATE_MESSAGE,
115 TALLOC_FREE(negotiate);
120 status = ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, "negotiate");
121 if (!NT_STATUS_IS_OK(status)){
125 /* Ask our caller what challenge they would like in the packet */
126 if (auth_context->get_ntlm_challenge) {
127 status = auth_context->get_ntlm_challenge(auth_context, cryptkey);
128 if (!NT_STATUS_IS_OK(status)) {
129 DEBUG(1, ("gensec_ntlmssp_server_negotiate: failed to get challenge: %s\n",
134 DEBUG(1, ("gensec_ntlmssp_server_negotiate: backend doesn't give a challenge\n"));
135 return NT_STATUS_NOT_IMPLEMENTED;
138 /* The flags we send back are not just the negotiated flags,
139 * they are also 'what is in this packet'. Therfore, we
140 * operate on 'chal_flags' from here on
143 chal_flags = ntlmssp_state->neg_flags;
145 /* get the right name to fill in as 'target' */
146 target_name = ntlmssp_target_name(ntlmssp_state,
147 neg_flags, &chal_flags);
148 if (target_name == NULL)
149 return NT_STATUS_INVALID_PARAMETER;
151 ntlmssp_state->chal = data_blob_talloc(ntlmssp_state, cryptkey, 8);
152 ntlmssp_state->internal_chal = data_blob_talloc(ntlmssp_state,
155 /* This creates the 'blob' of names that appears at the end of the packet */
156 if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) {
157 enum ndr_err_code err;
158 struct AV_PAIR *pairs = NULL;
161 pairs = talloc_zero_array(ntlmssp_state, struct AV_PAIR, count);
163 return NT_STATUS_NO_MEMORY;
166 pairs[0].AvId = MsvAvNbDomainName;
167 pairs[0].Value.AvNbDomainName = target_name;
169 pairs[1].AvId = MsvAvNbComputerName;
170 pairs[1].Value.AvNbComputerName = ntlmssp_state->server.netbios_name;
172 pairs[2].AvId = MsvAvDnsDomainName;
173 pairs[2].Value.AvDnsDomainName = ntlmssp_state->server.dns_domain;
175 pairs[3].AvId = MsvAvDnsComputerName;
176 pairs[3].Value.AvDnsComputerName= ntlmssp_state->server.dns_name;
178 pairs[4].AvId = MsvAvEOL;
180 ntlmssp_state->server.av_pair_list.count = count;
181 ntlmssp_state->server.av_pair_list.pair = pairs;
183 err = ndr_push_struct_blob(&struct_blob,
185 &ntlmssp_state->server.av_pair_list,
186 (ndr_push_flags_fn_t)ndr_push_AV_PAIR_LIST);
187 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
188 return NT_STATUS_NO_MEMORY;
191 struct_blob = data_blob_null;
195 /* Marshal the packet in the right format, be it unicode or ASCII */
196 const char *gen_string;
197 const DATA_BLOB version_blob = ntlmssp_version_blob();
199 if (ntlmssp_state->unicode) {
200 gen_string = "CdUdbddBb";
202 gen_string = "CdAdbddBb";
205 status = msrpc_gen(out_mem_ctx, reply, gen_string,
212 struct_blob.data, struct_blob.length,
213 version_blob.data, version_blob.length);
215 if (!NT_STATUS_IS_OK(status)) {
216 data_blob_free(&struct_blob);
220 if (DEBUGLEVEL >= 10) {
221 struct CHALLENGE_MESSAGE *challenge = talloc(
222 ntlmssp_state, struct CHALLENGE_MESSAGE);
223 if (challenge != NULL) {
224 challenge->NegotiateFlags = chal_flags;
225 status = ntlmssp_pull_CHALLENGE_MESSAGE(
226 reply, challenge, challenge);
227 if (NT_STATUS_IS_OK(status)) {
228 NDR_PRINT_DEBUG(CHALLENGE_MESSAGE,
231 TALLOC_FREE(challenge);
236 data_blob_free(&struct_blob);
238 ntlmssp_state->expected_state = NTLMSSP_AUTH;
240 return NT_STATUS_MORE_PROCESSING_REQUIRED;
243 struct ntlmssp_server_auth_state {
244 DATA_BLOB user_session_key;
245 DATA_BLOB lm_session_key;
246 /* internal variables used by KEY_EXCH (client-supplied user session key */
247 DATA_BLOB encrypted_session_key;
249 /* internal variables used by NTLM2 */
250 uint8_t session_nonce[16];
254 * Next state function for the Authenticate packet
256 * @param ntlmssp_state NTLMSSP State
257 * @param request The request, as a DATA_BLOB
258 * @return Errors or NT_STATUS_OK.
261 static NTSTATUS ntlmssp_server_preauth(struct gensec_security *gensec_security,
262 struct gensec_ntlmssp_context *gensec_ntlmssp,
263 struct ntlmssp_server_auth_state *state,
264 const DATA_BLOB request)
266 struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state;
267 struct auth4_context *auth_context = gensec_security->auth_context;
268 uint32_t ntlmssp_command, auth_flags;
271 uint8_t session_nonce_hash[16];
273 const char *parse_string;
276 file_save("ntlmssp_auth.dat", request.data, request.length);
279 if (ntlmssp_state->unicode) {
280 parse_string = "CdBBUUUBd";
282 parse_string = "CdBBAAABd";
286 data_blob_free(&ntlmssp_state->session_key);
287 data_blob_free(&ntlmssp_state->lm_resp);
288 data_blob_free(&ntlmssp_state->nt_resp);
290 ntlmssp_state->user = NULL;
291 ntlmssp_state->domain = NULL;
292 ntlmssp_state->client.netbios_name = NULL;
294 /* now the NTLMSSP encoded auth hashes */
295 if (!msrpc_parse(ntlmssp_state, &request, parse_string,
298 &ntlmssp_state->lm_resp,
299 &ntlmssp_state->nt_resp,
300 &ntlmssp_state->domain,
301 &ntlmssp_state->user,
302 &ntlmssp_state->client.netbios_name,
303 &state->encrypted_session_key,
305 DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n"));
306 dump_data(10, request.data, request.length);
309 data_blob_free(&state->encrypted_session_key);
312 /* Try again with a shorter string (Win9X truncates this packet) */
313 if (ntlmssp_state->unicode) {
314 parse_string = "CdBBUUU";
316 parse_string = "CdBBAAA";
319 /* now the NTLMSSP encoded auth hashes */
320 if (!msrpc_parse(ntlmssp_state, &request, parse_string,
323 &ntlmssp_state->lm_resp,
324 &ntlmssp_state->nt_resp,
325 &ntlmssp_state->domain,
326 &ntlmssp_state->user,
327 &ntlmssp_state->client.netbios_name)) {
328 DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP (tried both formats):\n"));
329 dump_data(2, request.data, request.length);
331 return NT_STATUS_INVALID_PARAMETER;
335 talloc_steal(state, state->encrypted_session_key.data);
337 if (auth_flags != 0) {
338 nt_status = ntlmssp_handle_neg_flags(ntlmssp_state,
341 if (!NT_STATUS_IS_OK(nt_status)){
346 if (DEBUGLEVEL >= 10) {
347 struct AUTHENTICATE_MESSAGE *authenticate = talloc(
348 ntlmssp_state, struct AUTHENTICATE_MESSAGE);
349 if (authenticate != NULL) {
351 authenticate->NegotiateFlags = auth_flags;
352 status = ntlmssp_pull_AUTHENTICATE_MESSAGE(
353 &request, authenticate, authenticate);
354 if (NT_STATUS_IS_OK(status)) {
355 NDR_PRINT_DEBUG(AUTHENTICATE_MESSAGE,
358 TALLOC_FREE(authenticate);
362 DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n",
363 ntlmssp_state->user, ntlmssp_state->domain,
364 ntlmssp_state->client.netbios_name,
365 (unsigned long)ntlmssp_state->lm_resp.length,
366 (unsigned long)ntlmssp_state->nt_resp.length));
369 file_save("nthash1.dat", &ntlmssp_state->nt_resp.data, &ntlmssp_state->nt_resp.length);
370 file_save("lmhash1.dat", &ntlmssp_state->lm_resp.data, &ntlmssp_state->lm_resp.length);
373 /* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a
376 However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful.
378 if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
379 if (ntlmssp_state->nt_resp.length == 24 && ntlmssp_state->lm_resp.length == 24) {
380 MD5_CTX md5_session_nonce_ctx;
381 state->doing_ntlm2 = true;
383 memcpy(state->session_nonce, ntlmssp_state->internal_chal.data, 8);
384 memcpy(&state->session_nonce[8], ntlmssp_state->lm_resp.data, 8);
386 SMB_ASSERT(ntlmssp_state->internal_chal.data && ntlmssp_state->internal_chal.length == 8);
388 MD5Init(&md5_session_nonce_ctx);
389 MD5Update(&md5_session_nonce_ctx, state->session_nonce, 16);
390 MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
392 /* LM response is no longer useful */
393 data_blob_free(&ntlmssp_state->lm_resp);
395 /* We changed the effective challenge - set it */
396 if (auth_context->set_ntlm_challenge) {
397 nt_status = auth_context->set_ntlm_challenge(auth_context,
399 "NTLMSSP callback (NTLM2)");
400 if (!NT_STATUS_IS_OK(nt_status)) {
401 DEBUG(1, ("gensec_ntlmssp_server_negotiate: failed to get challenge: %s\n",
402 nt_errstr(nt_status)));
406 DEBUG(1, ("gensec_ntlmssp_server_negotiate: backend doesn't have facility for challenge to be set\n"));
408 return NT_STATUS_NOT_IMPLEMENTED;
411 /* LM Key is incompatible. */
412 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
419 * Check the password on an NTLMSSP login.
421 * Return the session keys used on the connection.
424 static NTSTATUS ntlmssp_server_check_password(struct gensec_security *gensec_security,
425 struct gensec_ntlmssp_context *gensec_ntlmssp,
427 DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
429 struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state;
430 struct auth4_context *auth_context = gensec_security->auth_context;
431 NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
432 struct auth_usersupplied_info *user_info;
434 user_info = talloc_zero(ntlmssp_state, struct auth_usersupplied_info);
436 return NT_STATUS_NO_MEMORY;
439 user_info->logon_parameters = MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
440 user_info->flags = 0;
441 user_info->mapped_state = false;
442 user_info->client.account_name = ntlmssp_state->user;
443 user_info->client.domain_name = ntlmssp_state->domain;
444 user_info->workstation_name = ntlmssp_state->client.netbios_name;
445 user_info->remote_host = gensec_get_remote_address(gensec_security);
447 user_info->password_state = AUTH_PASSWORD_RESPONSE;
448 user_info->password.response.lanman = ntlmssp_state->lm_resp;
449 user_info->password.response.lanman.data = talloc_steal(user_info, ntlmssp_state->lm_resp.data);
450 user_info->password.response.nt = ntlmssp_state->nt_resp;
451 user_info->password.response.nt.data = talloc_steal(user_info, ntlmssp_state->nt_resp.data);
453 if (auth_context->check_ntlm_password) {
454 nt_status = auth_context->check_ntlm_password(auth_context,
457 &gensec_ntlmssp->server_returned_info,
458 user_session_key, lm_session_key);
461 if (!NT_STATUS_IS_OK(nt_status)) {
462 DEBUG(5, (__location__ ": Checking NTLMSSP password for %s\\%s failed: %s\n", user_info->client.domain_name, user_info->client.account_name, nt_errstr(nt_status)));
464 TALLOC_FREE(user_info);
466 NT_STATUS_NOT_OK_RETURN(nt_status);
468 talloc_steal(mem_ctx, user_session_key->data);
469 talloc_steal(mem_ctx, lm_session_key->data);
475 * Next state function for the Authenticate packet
476 * (after authentication - figures out the session keys etc)
478 * @param ntlmssp_state NTLMSSP State
479 * @return Errors or NT_STATUS_OK.
482 static NTSTATUS ntlmssp_server_postauth(struct gensec_security *gensec_security,
483 struct gensec_ntlmssp_context *gensec_ntlmssp,
484 struct ntlmssp_server_auth_state *state)
486 struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state;
487 DATA_BLOB user_session_key = state->user_session_key;
488 DATA_BLOB lm_session_key = state->lm_session_key;
489 NTSTATUS nt_status = NT_STATUS_OK;
490 DATA_BLOB session_key = data_blob(NULL, 0);
492 dump_data_pw("NT session key:\n", user_session_key.data, user_session_key.length);
493 dump_data_pw("LM first-8:\n", lm_session_key.data, lm_session_key.length);
495 /* Handle the different session key derivation for NTLM2 */
496 if (state->doing_ntlm2) {
497 if (user_session_key.data && user_session_key.length == 16) {
498 session_key = data_blob_talloc(ntlmssp_state,
500 hmac_md5(user_session_key.data, state->session_nonce,
501 sizeof(state->session_nonce), session_key.data);
502 DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n"));
503 dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
506 DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n"));
507 session_key = data_blob_null;
509 } else if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
510 /* Ensure we can never get here on NTLMv2 */
511 && (ntlmssp_state->nt_resp.length == 0 || ntlmssp_state->nt_resp.length == 24)) {
513 if (lm_session_key.data && lm_session_key.length >= 8) {
514 if (ntlmssp_state->lm_resp.data && ntlmssp_state->lm_resp.length == 24) {
515 session_key = data_blob_talloc(ntlmssp_state,
517 if (session_key.data == NULL) {
518 return NT_STATUS_NO_MEMORY;
520 SMBsesskeygen_lm_sess_key(lm_session_key.data, ntlmssp_state->lm_resp.data,
522 DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
524 static const uint8_t zeros[24] = {0, };
525 session_key = data_blob_talloc(
526 ntlmssp_state, NULL, 16);
527 if (session_key.data == NULL) {
528 return NT_STATUS_NO_MEMORY;
530 SMBsesskeygen_lm_sess_key(zeros, zeros,
532 DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
534 dump_data_pw("LM session key:\n", session_key.data,
537 /* LM Key not selected */
538 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
540 DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n"));
541 session_key = data_blob_null;
544 } else if (user_session_key.data) {
545 session_key = user_session_key;
546 DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n"));
547 dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
549 /* LM Key not selected */
550 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
552 } else if (lm_session_key.data) {
553 /* Very weird to have LM key, but no user session key, but anyway.. */
554 session_key = lm_session_key;
555 DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n"));
556 dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
558 /* LM Key not selected */
559 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
562 DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n"));
563 session_key = data_blob_null;
565 /* LM Key not selected */
566 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
569 /* With KEY_EXCH, the client supplies the proposed session key,
570 but encrypts it with the long-term key */
571 if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
572 if (!state->encrypted_session_key.data
573 || state->encrypted_session_key.length != 16) {
574 DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n",
575 (unsigned)state->encrypted_session_key.length));
576 return NT_STATUS_INVALID_PARAMETER;
577 } else if (!session_key.data || session_key.length != 16) {
578 DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n",
579 (unsigned int)session_key.length));
580 ntlmssp_state->session_key = session_key;
581 talloc_steal(ntlmssp_state, session_key.data);
583 dump_data_pw("KEY_EXCH session key (enc):\n",
584 state->encrypted_session_key.data,
585 state->encrypted_session_key.length);
586 arcfour_crypt(state->encrypted_session_key.data,
588 state->encrypted_session_key.length);
589 ntlmssp_state->session_key = data_blob_talloc(ntlmssp_state,
590 state->encrypted_session_key.data,
591 state->encrypted_session_key.length);
592 dump_data_pw("KEY_EXCH session key:\n",
593 state->encrypted_session_key.data,
594 state->encrypted_session_key.length);
597 ntlmssp_state->session_key = session_key;
598 talloc_steal(ntlmssp_state, session_key.data);
601 if (gensec_ntlmssp_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
602 nt_status = ntlmssp_sign_init(ntlmssp_state);
605 ntlmssp_state->expected_state = NTLMSSP_DONE;
612 * Next state function for the NTLMSSP Authenticate packet
614 * @param gensec_security GENSEC state
615 * @param out_mem_ctx Memory context for *out
616 * @param in The request, as a DATA_BLOB. reply.data must be NULL
617 * @param out The reply, as an allocated DATA_BLOB, caller to free.
618 * @return Errors or NT_STATUS_OK if authentication sucessful
621 NTSTATUS gensec_ntlmssp_server_auth(struct gensec_security *gensec_security,
622 TALLOC_CTX *out_mem_ctx,
623 const DATA_BLOB in, DATA_BLOB *out)
625 struct gensec_ntlmssp_context *gensec_ntlmssp =
626 talloc_get_type_abort(gensec_security->private_data,
627 struct gensec_ntlmssp_context);
628 struct ntlmssp_server_auth_state *state;
631 /* zero the outbound NTLMSSP packet */
632 *out = data_blob_null;
634 state = talloc_zero(gensec_ntlmssp, struct ntlmssp_server_auth_state);
636 return NT_STATUS_NO_MEMORY;
639 nt_status = ntlmssp_server_preauth(gensec_security, gensec_ntlmssp, state, in);
640 if (!NT_STATUS_IS_OK(nt_status)) {
646 * Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth
647 * is required (by "ntlm auth = no" and "lm auth = no" being set in the
648 * smb.conf file) and no NTLMv2 response was sent then the password check
649 * will fail here. JRA.
652 /* Finally, actually ask if the password is OK */
653 nt_status = ntlmssp_server_check_password(gensec_security, gensec_ntlmssp,
655 &state->user_session_key,
656 &state->lm_session_key);
657 if (!NT_STATUS_IS_OK(nt_status)) {
662 /* When we get more async in the auth code behind
663 ntlmssp_state->check_password, the ntlmssp_server_postpath
664 can be done in a callback */
666 nt_status = ntlmssp_server_postauth(gensec_security, gensec_ntlmssp, state);