CVE-2016-2111: s4:smb_server: implement "raw NTLMv2 auth" checks
[samba.git] / source4 / smb_server / smb / sesssetup.c
index 225f0f7c479a53bf89cd9b946d2f2da1abd00a27..e06853afcd4fb67826c08b3fa57bf9620b3aab9b 100644 (file)
@@ -10,7 +10,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include <tevent.h>
 #include "version.h"
+#include "auth/gensec/gensec.h"
 #include "auth/auth.h"
 #include "smb_server/smb_server.h"
 #include "smbd/service_stream.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+#include "lib/stream/packet.h"
+
+struct sesssetup_context {
+       struct auth4_context *auth_context;
+       struct smbsrv_request *req;
+};
 
 /*
   setup the OS, Lanman and domain portions of a session setup reply
@@ -37,45 +46,110 @@ static void sesssetup_common_strings(struct smbsrv_request *req,
 {
        (*os) = talloc_asprintf(req, "Unix");
        (*lanman) = talloc_asprintf(req, "Samba %s", SAMBA_VERSION_STRING);
-       (*domain) = talloc_asprintf(req, "%s", lp_workgroup());
+       (*domain) = talloc_asprintf(req, "%s", 
+                                   lpcfg_workgroup(req->smb_conn->lp_ctx));
+}
+
+static void smbsrv_sesssetup_backend_send(struct smbsrv_request *req,
+                                         union smb_sesssetup *sess,
+                                         NTSTATUS status)
+{
+       if (NT_STATUS_IS_OK(status)) {
+               req->smb_conn->negotiate.done_sesssetup = true;
+               /* we need to keep the session long term */
+               req->session = talloc_steal(req->smb_conn, req->session);
+       }
+       smbsrv_reply_sesssetup_send(req, sess, status);
 }
 
+static void sesssetup_old_send(struct tevent_req *subreq)
+{
+       struct sesssetup_context *state = tevent_req_callback_data(subreq, struct sesssetup_context);
+       struct smbsrv_request *req = state->req;
+
+       union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
+       struct auth_user_info_dc *user_info_dc = NULL;
+       struct auth_session_info *session_info;
+       struct smbsrv_session *smb_sess;
+       NTSTATUS status;
+       uint32_t flags;
+
+       status = auth_check_password_recv(subreq, req, &user_info_dc);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
+
+       flags = AUTH_SESSION_INFO_DEFAULT_GROUPS;
+       if (user_info_dc->info->authenticated) {
+               flags |= AUTH_SESSION_INFO_AUTHENTICATED;
+       }
+       /* This references user_info_dc into session_info */
+       status = req->smb_conn->negotiate.auth_context->generate_session_info(req->smb_conn->negotiate.auth_context,
+                                                                             req,
+                                                                             user_info_dc, sess->old.in.user, 
+                                                                             flags, &session_info);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
+
+       /* allocate a new session */
+       smb_sess = smbsrv_session_new(req->smb_conn, req, NULL);
+       if (!smb_sess) {
+               status = NT_STATUS_INSUFFICIENT_RESOURCES;
+               goto failed;
+       }
+
+       /* Ensure this is marked as a 'real' vuid, not one
+        * simply valid for the session setup leg */
+       status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
+
+       /* To correctly process any AndX packet (like a tree connect)
+        * we need to fill in the session on the request here */
+       req->session = smb_sess;
+       sess->old.out.vuid = smb_sess->vuid;
+
+failed:
+       status = nt_status_squash(status);
+       smbsrv_sesssetup_backend_send(req, sess, status);
+}
 
 /*
   handler for old style session setup
 */
-static NTSTATUS sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess)
+static void sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess)
 {
-       NTSTATUS status;
        struct auth_usersupplied_info *user_info = NULL;
-       struct auth_serversupplied_info *server_info = NULL;
-       struct auth_session_info *session_info;
-       struct smbsrv_session *smb_sess;
-       struct socket_address *remote_address;
+       struct tsocket_address *remote_address;
        const char *remote_machine = NULL;
+       struct tevent_req *subreq;
+       struct sesssetup_context *state;
 
        sess->old.out.vuid = 0;
        sess->old.out.action = 0;
 
+       sesssetup_common_strings(req, 
+                                &sess->old.out.os,
+                                &sess->old.out.lanman,
+                                &sess->old.out.domain);
+
        if (!req->smb_conn->negotiate.done_sesssetup) {
                req->smb_conn->negotiate.max_send = sess->old.in.bufsize;
        }
-       
+
        if (req->smb_conn->negotiate.calling_name) {
                remote_machine = req->smb_conn->negotiate.calling_name->name;
        }
        
-       remote_address = socket_get_peer_addr(req->smb_conn->connection->socket, req);
-       NT_STATUS_HAVE_NO_MEMORY(remote_address);
+       remote_address = socket_get_remote_addr(req->smb_conn->connection->socket, req);
+       if (!remote_address) goto nomem;
 
        if (!remote_machine) {
-               remote_machine = remote_address->addr;
+               remote_machine = tsocket_address_inet_addr_string(remote_address, req);
+               if (!remote_machine) goto nomem;
        }
 
-       user_info = talloc(req, struct auth_usersupplied_info);
-       NT_STATUS_HAVE_NO_MEMORY(user_info);
+       user_info = talloc_zero(req, struct auth_usersupplied_info);
+       if (!user_info) goto nomem;
        
-       user_info->mapped_state = False;
+       user_info->mapped_state = false;
        user_info->logon_parameters = 0;
        user_info->flags = 0;
        user_info->client.account_name = sess->old.in.user;
@@ -88,98 +162,170 @@ static NTSTATUS sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *s
        user_info->password.response.lanman.data = talloc_steal(user_info, sess->old.in.password.data);
        user_info->password.response.nt = data_blob(NULL, 0);
 
-       status = auth_check_password(req->smb_conn->negotiate.auth_context,
-                                    req, user_info, &server_info);
-       if (!NT_STATUS_IS_OK(status)) {
-               return auth_nt_status_squash(status);
+       state = talloc(req, struct sesssetup_context);
+       if (!state) goto nomem;
+
+       if (req->smb_conn->negotiate.auth_context) {
+               state->auth_context = req->smb_conn->negotiate.auth_context;
+       } else {
+               /* TODO: should we use just "anonymous" here? */
+               NTSTATUS status = auth_context_create(state,
+                                                     req->smb_conn->connection->event.ctx,
+                                                     req->smb_conn->connection->msg_ctx,
+                                                     req->smb_conn->lp_ctx,
+                                                     &state->auth_context);
+               if (!NT_STATUS_IS_OK(status)) {
+                       smbsrv_sesssetup_backend_send(req, sess, status);
+                       return;
+               }
        }
 
-       /* This references server_info into session_info */
-       status = auth_generate_session_info(req, server_info, &session_info);
-       if (!NT_STATUS_IS_OK(status)) {
-               return auth_nt_status_squash(status);
+       state->req = req;
+
+       subreq = auth_check_password_send(state,
+                                         req->smb_conn->connection->event.ctx,
+                                         req->smb_conn->negotiate.auth_context,
+                                         user_info);
+       if (!subreq) goto nomem;
+       tevent_req_set_callback(subreq, sesssetup_old_send, state);
+       return;
+
+nomem:
+       smbsrv_sesssetup_backend_send(req, sess, NT_STATUS_NO_MEMORY);
+}
+
+static void sesssetup_nt1_send(struct tevent_req *subreq)
+{
+       struct sesssetup_context *state = tevent_req_callback_data(subreq, struct sesssetup_context);
+       struct smbsrv_request *req = state->req;
+       union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
+       struct auth_user_info_dc *user_info_dc = NULL;
+       struct auth_session_info *session_info;
+       struct smbsrv_session *smb_sess;
+
+       uint32_t flags;
+       NTSTATUS status;
+
+       status = auth_check_password_recv(subreq, req, &user_info_dc);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
+
+       flags = AUTH_SESSION_INFO_DEFAULT_GROUPS;
+       if (user_info_dc->info->authenticated) {
+               flags |= AUTH_SESSION_INFO_AUTHENTICATED;
        }
+       /* This references user_info_dc into session_info */
+       status = state->auth_context->generate_session_info(state->auth_context,
+                                                           req,
+                                                           user_info_dc,
+                                                           sess->nt1.in.user,
+                                                           flags,
+                                                           &session_info);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
 
        /* allocate a new session */
-       smb_sess = smbsrv_session_new(req->smb_conn, NULL);
+       smb_sess = smbsrv_session_new(req->smb_conn, req, NULL);
        if (!smb_sess) {
-               return NT_STATUS_ACCESS_DENIED;
+               status = NT_STATUS_INSUFFICIENT_RESOURCES;
+               goto failed;
        }
 
        /* Ensure this is marked as a 'real' vuid, not one
         * simply valid for the session setup leg */
        status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
-       if (!NT_STATUS_IS_OK(status)) {
-               return auth_nt_status_squash(status);
-       }
+       if (!NT_STATUS_IS_OK(status)) goto failed;
 
        /* To correctly process any AndX packet (like a tree connect)
         * we need to fill in the session on the request here */
        req->session = smb_sess;
-       sess->old.out.vuid = smb_sess->vuid;
+       sess->nt1.out.vuid = smb_sess->vuid;
 
-       sesssetup_common_strings(req, 
-                                &sess->old.out.os,
-                                &sess->old.out.lanman,
-                                &sess->old.out.domain);
+       if (!smbsrv_setup_signing(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2)) {
+               /* Already signing, or disabled */
+               goto done;
+       }
 
-       return NT_STATUS_OK;
+done:
+       status = NT_STATUS_OK;
+failed:
+       status = nt_status_squash(status);
+       smbsrv_sesssetup_backend_send(req, sess, status);
 }
 
-
 /*
   handler for NT1 style session setup
 */
-static NTSTATUS sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
+static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
 {
        NTSTATUS status;
-       struct auth_context *auth_context;
        struct auth_usersupplied_info *user_info = NULL;
-       struct auth_serversupplied_info *server_info = NULL;
-       struct auth_session_info *session_info;
-       struct smbsrv_session *smb_sess;
-       struct socket_address *remote_address;
+       struct tsocket_address *remote_address;
        const char *remote_machine = NULL;
-       
+       struct tevent_req *subreq;
+       struct sesssetup_context *state;
+       bool allow_raw = lpcfg_raw_ntlmv2_auth(req->smb_conn->lp_ctx);
+
        sess->nt1.out.vuid = 0;
        sess->nt1.out.action = 0;
 
+       sesssetup_common_strings(req, 
+                                &sess->nt1.out.os,
+                                &sess->nt1.out.lanman,
+                                &sess->nt1.out.domain);
+
        if (!req->smb_conn->negotiate.done_sesssetup) {
                req->smb_conn->negotiate.max_send = sess->nt1.in.bufsize;
                req->smb_conn->negotiate.client_caps = sess->nt1.in.capabilities;
        }
 
-       if (req->smb_conn->negotiate.spnego_negotiated) {
+       state = talloc(req, struct sesssetup_context);
+       if (!state) goto nomem;
+
+       state->req = req;
+
+       if (req->smb_conn->negotiate.oid) {
                if (sess->nt1.in.user && *sess->nt1.in.user) {
                        /* We can't accept a normal login, because we
                         * don't have a challenge */
-                       return NT_STATUS_LOGON_FAILURE;
+                       status = NT_STATUS_LOGON_FAILURE;
+                       goto failed;
                }
 
                /* TODO: should we use just "anonymous" here? */
-               status = auth_context_create(req, lp_auth_methods(), 
-                                            &auth_context, 
-                                            req->smb_conn->connection->event.ctx);
-               NT_STATUS_NOT_OK_RETURN(status);
+               status = auth_context_create(state,
+                                            req->smb_conn->connection->event.ctx,
+                                            req->smb_conn->connection->msg_ctx,
+                                            req->smb_conn->lp_ctx,
+                                            &state->auth_context);
+               if (!NT_STATUS_IS_OK(status)) goto failed;
+       } else if (req->smb_conn->negotiate.auth_context) {
+               state->auth_context = req->smb_conn->negotiate.auth_context;
        } else {
-               auth_context = req->smb_conn->negotiate.auth_context;
+               /* TODO: should we use just "anonymous" here? */
+               status = auth_context_create(state,
+                                            req->smb_conn->connection->event.ctx,
+                                            req->smb_conn->connection->msg_ctx,
+                                            req->smb_conn->lp_ctx,
+                                            &state->auth_context);
+               if (!NT_STATUS_IS_OK(status)) goto failed;
        }
 
        if (req->smb_conn->negotiate.calling_name) {
                remote_machine = req->smb_conn->negotiate.calling_name->name;
        }
 
-       remote_address = socket_get_peer_addr(req->smb_conn->connection->socket, req);
-       NT_STATUS_HAVE_NO_MEMORY(remote_address);
+       remote_address = socket_get_remote_addr(req->smb_conn->connection->socket, req);
+       if (!remote_address) goto nomem;
 
        if (!remote_machine) {
-               remote_machine = remote_address->addr;
+               remote_machine = tsocket_address_inet_addr_string(remote_address, req);
+               if (!remote_machine) goto nomem;
        }
 
-       user_info = talloc(req, struct auth_usersupplied_info);
-       NT_STATUS_HAVE_NO_MEMORY(user_info);
-       
-       user_info->mapped_state = False;
+       user_info = talloc_zero(req, struct auth_usersupplied_info);
+       if (!user_info) goto nomem;
+
+       user_info->mapped_state = false;
        user_info->logon_parameters = 0;
        user_info->flags = 0;
        user_info->client.account_name = sess->nt1.in.user;
@@ -193,73 +339,96 @@ static NTSTATUS sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *s
        user_info->password.response.nt = sess->nt1.in.password2;
        user_info->password.response.nt.data = talloc_steal(user_info, sess->nt1.in.password2.data);
 
-       status = auth_check_password(auth_context, req, user_info, &server_info);
-       if (!NT_STATUS_IS_OK(status)) {
-               return auth_nt_status_squash(status);
+       if (!allow_raw && user_info->password.response.nt.length >= 48) {
+               /*
+                * NTLMv2_RESPONSE has at least 48 bytes
+                * and should only be supported via NTLMSSP.
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto failed;
        }
 
-       /* This references server_info into session_info */
-       status = auth_generate_session_info(req, server_info, &session_info);
-       if (!NT_STATUS_IS_OK(status)) {
-               return auth_nt_status_squash(status);
-       }
+       subreq = auth_check_password_send(state,
+                                         req->smb_conn->connection->event.ctx,
+                                         state->auth_context,
+                                         user_info);
+       if (!subreq) goto nomem;
+       tevent_req_set_callback(subreq, sesssetup_nt1_send, state);
 
-       /* allocate a new session */
-       smb_sess = smbsrv_session_new(req->smb_conn, NULL);
-       if (!smb_sess) {
-               return NT_STATUS_ACCESS_DENIED;
-       }
+       return;
 
-       /* Ensure this is marked as a 'real' vuid, not one
-        * simply valid for the session setup leg */
-       status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
-       if (!NT_STATUS_IS_OK(status)) {
-               return auth_nt_status_squash(status);
-       }
+nomem:
+       status = NT_STATUS_NO_MEMORY;
+failed:
+       status = nt_status_squash(status);
+       smbsrv_sesssetup_backend_send(req, sess, status);
+}
 
-       /* To correctly process any AndX packet (like a tree connect)
-        * we need to fill in the session on the request here */
-       req->session = smb_sess;
-       sess->nt1.out.vuid = smb_sess->vuid;
+struct sesssetup_spnego_state {
+       struct smbsrv_request *req;
+       union smb_sesssetup *sess;
+       struct smbsrv_session *smb_sess;
+};
 
-       sesssetup_common_strings(req, 
-                                &sess->nt1.out.os,
-                                &sess->nt1.out.lanman,
-                                &sess->nt1.out.domain);
+static void sesssetup_spnego_send(struct tevent_req *subreq)
+{
+       struct sesssetup_spnego_state *s = tevent_req_callback_data(subreq,
+                                          struct sesssetup_spnego_state);
+       struct smbsrv_request *req = s->req;
+       union smb_sesssetup *sess = s->sess;
+       struct smbsrv_session *smb_sess = s->smb_sess;
+       struct auth_session_info *session_info = NULL;
+       NTSTATUS status;
+       NTSTATUS skey_status;
+       DATA_BLOB session_key;
 
-       if (!session_info->server_info->authenticated) {
-               return NT_STATUS_OK;
+       status = gensec_update_recv(subreq, req, &sess->spnego.out.secblob);
+       packet_recv_enable(req->smb_conn->packet);
+       TALLOC_FREE(subreq);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               goto done;
+       } else if (!NT_STATUS_IS_OK(status)) {
+               goto failed;
        }
 
-       if (!srv_setup_signing(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2)) {
-               /* Already signing, or disabled */
-               return NT_STATUS_OK;
-       }
+       status = gensec_session_info(smb_sess->gensec_ctx, smb_sess, &session_info);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
 
-       /* Force check of the request packet, now we know the session key */
-       req_signing_check_incoming(req);
-/* TODO: why don't we check the result here? */
+       /* The session_key is only needed until the end of the smbsrv_setup_signing() call */
+       skey_status = gensec_session_key(smb_sess->gensec_ctx, req, &session_key);
+       if (NT_STATUS_IS_OK(skey_status)) {
+               smbsrv_setup_signing(req->smb_conn, &session_key, NULL);
+       }
 
-       /* Unfortunetly win2k3 as a client doesn't sign the request
-        * packet here, so we have to force signing to start again */
+       /* Ensure this is marked as a 'real' vuid, not one
+        * simply valid for the session setup leg */
+       status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
 
-       srv_signing_restart(req->smb_conn,  &session_info->session_key, &sess->nt1.in.password2);
+       req->session = smb_sess;
 
-       return NT_STATUS_OK;
+done:
+       sess->spnego.out.vuid = smb_sess->vuid;
+failed:
+       status = nt_status_squash(status);
+       smbsrv_sesssetup_backend_send(req, sess, status);
+       if (!NT_STATUS_IS_OK(status) && 
+           !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               talloc_free(smb_sess);
+       }
 }
 
-
 /*
   handler for SPNEGO style session setup
 */
-static NTSTATUS sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *sess)
+static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *sess)
 {
        NTSTATUS status;
-       NTSTATUS skey_status;
        struct smbsrv_session *smb_sess = NULL;
-       struct auth_session_info *session_info = NULL;
-       DATA_BLOB session_key;
+       bool is_smb_sess_new = false;
+       struct sesssetup_spnego_state *s = NULL;
        uint16_t vuid;
+       struct tevent_req *subreq;
 
        sess->spnego.out.vuid = 0;
        sess->spnego.out.action = 0;
@@ -270,114 +439,118 @@ static NTSTATUS sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup
                                 &sess->spnego.out.workgroup);
 
        if (!req->smb_conn->negotiate.done_sesssetup) {
-               req->smb_conn->negotiate.max_send = sess->nt1.in.bufsize;
-               req->smb_conn->negotiate.client_caps = sess->nt1.in.capabilities;
+               req->smb_conn->negotiate.max_send = sess->spnego.in.bufsize;
+               req->smb_conn->negotiate.client_caps = sess->spnego.in.capabilities;
        }
 
        vuid = SVAL(req->in.hdr,HDR_UID);
 
        /* lookup an existing session */
-       smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid);
-       if (!smb_sess) {
+       if (vuid == 0) {
                struct gensec_security *gensec_ctx;
 
-               status = gensec_server_start(req, &gensec_ctx,
-                                            req->smb_conn->connection->event.ctx);
+               status = samba_server_gensec_start(req,
+                                                  req->smb_conn->connection->event.ctx,
+                                                  req->smb_conn->connection->msg_ctx,
+                                                  req->smb_conn->lp_ctx,
+                                                  req->smb_conn->negotiate.server_credentials,
+                                                  "cifs",
+                                                  &gensec_ctx);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
-                       return status;
+                       goto failed;
                }
 
-               gensec_set_credentials(gensec_ctx, req->smb_conn->negotiate.server_credentials);
-
-               gensec_set_target_service(gensec_ctx, "cifs");
-
                gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
 
-               status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
+               status = gensec_start_mech_by_oid(gensec_ctx, req->smb_conn->negotiate.oid);
                if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(1, ("Failed to start GENSEC SPNEGO server code: %s\n", nt_errstr(status)));
-                       return status;
+                       DEBUG(1, ("Failed to start GENSEC %s server code: %s\n", 
+                                 gensec_get_name_by_oid(gensec_ctx, req->smb_conn->negotiate.oid), nt_errstr(status)));
+                       goto failed;
                }
 
                /* allocate a new session */
-               smb_sess = smbsrv_session_new(req->smb_conn, gensec_ctx);
+               smb_sess = smbsrv_session_new(req->smb_conn, req->smb_conn, gensec_ctx);
+               if (!smb_sess) {
+                       status = NT_STATUS_INSUFFICIENT_RESOURCES;
+                       goto failed;
+               }
+               is_smb_sess_new = true;
+       } else {
+               smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid);
        }
 
        if (!smb_sess) {
-               return NT_STATUS_ACCESS_DENIED;
-       }
-
-       if (!smb_sess->gensec_ctx) {
-               status = NT_STATUS_INTERNAL_ERROR;
-               DEBUG(1, ("Internal ERROR: no gensec_ctx on session: %s\n", nt_errstr(status)));
+               status = NT_STATUS_DOS(ERRSRV, ERRbaduid);
                goto failed;
        }
 
-       status = gensec_update(smb_sess->gensec_ctx, req, sess->spnego.in.secblob, &sess->spnego.out.secblob);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-               sess->spnego.out.vuid = smb_sess->vuid;
-               return status;
-       } else if (!NT_STATUS_IS_OK(status)) {
+       if (smb_sess->session_info) {
+               status = NT_STATUS_INVALID_PARAMETER;
                goto failed;
        }
 
-       status = gensec_session_info(smb_sess->gensec_ctx, &session_info);
-       if (!NT_STATUS_IS_OK(status)) {
+       if (!smb_sess->gensec_ctx) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               DEBUG(1, ("Internal ERROR: no gensec_ctx on session: %s\n", nt_errstr(status)));
                goto failed;
        }
 
-       skey_status = gensec_session_key(smb_sess->gensec_ctx, &session_key);
-       if (NT_STATUS_IS_OK(skey_status) &&
-           session_info->server_info->authenticated &&
-           srv_setup_signing(req->smb_conn, &session_key, NULL)) {
-               /* Force check of the request packet, now we know the session key */
-               req_signing_check_incoming(req);
-
-               srv_signing_restart(req->smb_conn, &session_key, NULL);
-       }
-
-       /* Ensure this is marked as a 'real' vuid, not one
-        * simply valid for the session setup leg */
-       status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto failed;
+       s = talloc(req, struct sesssetup_spnego_state);
+       if (!s) goto nomem;
+       s->req          = req;
+       s->sess         = sess;
+       s->smb_sess     = smb_sess;
+
+       subreq = gensec_update_send(s,
+                                   req->smb_conn->connection->event.ctx,
+                                   smb_sess->gensec_ctx,
+                                   sess->spnego.in.secblob);
+       if (!subreq) {
+               goto nomem;
        }
-       req->session = smb_sess;
+       /* disable receipt of more packets on this socket until we've
+          finished with the session setup. This avoids a problem with
+          crashes if we get EOF on the socket while processing a session
+          setup */
+       packet_recv_disable(req->smb_conn->packet);
+       tevent_req_set_callback(subreq, sesssetup_spnego_send, s);
 
-       sess->spnego.out.vuid = smb_sess->vuid;
-       return NT_STATUS_OK;
+       return;
 
+nomem:
+       status = NT_STATUS_NO_MEMORY;
 failed:
-       talloc_free(smb_sess);
-       return auth_nt_status_squash(status);
+       if (is_smb_sess_new) {
+               talloc_free(smb_sess);
+       }
+       status = nt_status_squash(status);
+       smbsrv_sesssetup_backend_send(req, sess, status);
 }
 
 /*
   backend for sessionsetup call - this takes all 3 variants of the call
 */
-NTSTATUS sesssetup_backend(struct smbsrv_request *req, 
-                          union smb_sesssetup *sess)
+void smbsrv_sesssetup_backend(struct smbsrv_request *req,
+                             union smb_sesssetup *sess)
 {
-       NTSTATUS status = NT_STATUS_INVALID_LEVEL;
-
        switch (sess->old.level) {
                case RAW_SESSSETUP_OLD:
-                       status = sesssetup_old(req, sess);
-                       break;
+                       sesssetup_old(req, sess);
+                       return;
+
                case RAW_SESSSETUP_NT1:
-                       status = sesssetup_nt1(req, sess);
-                       break;
+                       sesssetup_nt1(req, sess);
+                       return;
+
                case RAW_SESSSETUP_SPNEGO:
-                       status = sesssetup_spnego(req, sess);
-                       break;
-       }
+                       sesssetup_spnego(req, sess);
+                       return;
 
-       if (NT_STATUS_IS_OK(status)) {
-               req->smb_conn->negotiate.done_sesssetup = True;
+               case RAW_SESSSETUP_SMB2:
+                       break;
        }
 
-       return status;
+       smbsrv_sesssetup_backend_send(req, sess, NT_STATUS_INVALID_LEVEL);
 }
-
-