smbd: add an effective {smb,smbd_smb2}_request->ev_ctx that holds the event context...
[amitay/samba.git] / source3 / smbd / smb2_sesssetup.c
index 7d1aaf54f5c96e2b00f7e4ca61f2421883c84056..fe5835b83f34c070c69a563d9ac260907214e142 100644 (file)
@@ -33,6 +33,9 @@
 #include "lib/crypto/aes_ccm_128.h"
 #include "lib/crypto/aes_gcm_128.h"
 
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SMB2
+
 static struct tevent_req *smbd_smb2_session_setup_wrap_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct smbd_smb2_request *smb2req,
@@ -92,7 +95,7 @@ NTSTATUS smbd_smb2_request_process_sesssetup(struct smbd_smb2_request *smb2req)
        in_security_buffer.length = in_security_length;
 
        subreq = smbd_smb2_session_setup_wrap_send(smb2req,
-                                                  smb2req->sconn->ev_ctx,
+                                                  smb2req->ev_ctx,
                                                   smb2req,
                                                   in_session_id,
                                                   in_flags,
@@ -190,6 +193,7 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
        struct smbXsrv_session *x = session;
        struct smbXsrv_session_auth0 *auth = *_auth;
        struct smbXsrv_connection *xconn = smb2req->xconn;
+       size_t i;
        struct _derivation {
                DATA_BLOB label;
                DATA_BLOB context;
@@ -208,7 +212,6 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
                struct _derivation *d;
                DATA_BLOB p;
                struct hc_sha512state sctx;
-               size_t i;
 
                preauth = talloc_move(smb2req, &auth->preauth);
 
@@ -262,44 +265,48 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
        }
 
        if ((in_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) ||
-           lp_server_signing() == SMB_SIGNING_REQUIRED) {
-               x->global->signing_required = true;
+           (xconn->smb2.server.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED))
+       {
+               x->global->signing_flags = SMBXSRV_SIGNING_REQUIRED;
        }
 
        if ((lp_smb_encrypt(-1) >= SMB_SIGNING_DESIRED) &&
            (xconn->smb2.client.capabilities & SMB2_CAP_ENCRYPTION)) {
-               x->encryption_desired = true;
+               x->global->encryption_flags = SMBXSRV_ENCRYPTION_DESIRED;
        }
 
        if (lp_smb_encrypt(-1) == SMB_SIGNING_REQUIRED) {
-               x->encryption_desired = true;
-               x->global->encryption_required = true;
+               x->global->encryption_flags = SMBXSRV_ENCRYPTION_REQUIRED |
+                       SMBXSRV_ENCRYPTION_DESIRED;
        }
 
        if (security_session_user_level(session_info, NULL) < SECURITY_USER) {
-               /* we map anonymous to guest internally */
-               *out_session_flags |= SMB2_SESSION_FLAG_IS_GUEST;
-               *out_session_flags |= SMB2_SESSION_FLAG_IS_NULL;
+               if (security_session_user_level(session_info, NULL) == SECURITY_GUEST) {
+                       *out_session_flags |= SMB2_SESSION_FLAG_IS_GUEST;
+               }
                /* force no signing */
-               x->global->signing_required = false;
+               x->global->signing_flags &= ~SMBXSRV_SIGNING_REQUIRED;
+               /* we map anonymous to guest internally */
                guest = true;
        }
 
-       if (guest && x->global->encryption_required) {
+       if (guest && (x->global->encryption_flags & SMBXSRV_ENCRYPTION_REQUIRED)) {
                DEBUG(1,("reject guest session as encryption is required\n"));
                return NT_STATUS_ACCESS_DENIED;
        }
 
        if (xconn->smb2.server.cipher == 0) {
-               if (x->global->encryption_required) {
+               if (x->global->encryption_flags & SMBXSRV_ENCRYPTION_REQUIRED) {
                        DEBUG(1,("reject session with dialect[0x%04X] "
                                 "as encryption is required\n",
                                 xconn->smb2.server.dialect));
                        return NT_STATUS_ACCESS_DENIED;
                }
+       } else {
+               x->global->channels[0].encryption_cipher = xconn->smb2.server.cipher;
        }
 
-       if (x->encryption_desired) {
+       if (x->global->encryption_flags & SMBXSRV_ENCRYPTION_DESIRED) {
                *out_session_flags |= SMB2_SESSION_FLAG_ENCRYPT_DATA;
        }
 
@@ -439,10 +446,16 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
        reload_services(smb2req->sconn, conn_snum_used, true);
 
        session->status = NT_STATUS_OK;
-       session->global->auth_session_info = session_info;
+       session->global->auth_session_info = talloc_move(session->global,
+                                                        &session_info);
        session->global->auth_session_info_seqnum += 1;
-       session->global->channels[0].auth_session_info_seqnum =
-               session->global->auth_session_info_seqnum;
+       for (i=0; i < session->global->num_channels; i++) {
+               struct smbXsrv_channel_global0 *_c =
+                       &session->global->channels[i];
+
+               _c->auth_session_info_seqnum =
+                       session->global->auth_session_info_seqnum;
+       }
        session->global->auth_time = timeval_to_nttime(&smb2req->request_time);
        session->global->expiration_time = gensec_expire_time(auth->gensec);
 
@@ -473,6 +486,7 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
        global_client_caps |= (CAP_LEVEL_II_OPLOCKS|CAP_STATUS32);
 
        *out_session_id = session->global->session_wire_id;
+       smb2req->last_session_id = session->global->session_wire_id;
 
        return NT_STATUS_OK;
 }
@@ -487,6 +501,8 @@ static NTSTATUS smbd_smb2_reauth_generic_return(struct smbXsrv_session *session,
        NTSTATUS status;
        struct smbXsrv_session *x = session;
        struct smbXsrv_session_auth0 *auth = *_auth;
+       struct smbXsrv_connection *xconn = smb2req->xconn;
+       size_t i;
 
        *_auth = NULL;
 
@@ -511,10 +527,16 @@ static NTSTATUS smbd_smb2_reauth_generic_return(struct smbXsrv_session *session,
 
        session->status = NT_STATUS_OK;
        TALLOC_FREE(session->global->auth_session_info);
-       session->global->auth_session_info = session_info;
+       session->global->auth_session_info = talloc_move(session->global,
+                                                        &session_info);
        session->global->auth_session_info_seqnum += 1;
-       session->global->channels[0].auth_session_info_seqnum =
-               session->global->auth_session_info_seqnum;
+       for (i=0; i < session->global->num_channels; i++) {
+               struct smbXsrv_channel_global0 *_c =
+                       &session->global->channels[i];
+
+               _c->auth_session_info_seqnum =
+                       session->global->auth_session_info_seqnum;
+       }
        session->global->auth_time = timeval_to_nttime(&smb2req->request_time);
        session->global->expiration_time = gensec_expire_time(auth->gensec);
 
@@ -527,7 +549,7 @@ static NTSTATUS smbd_smb2_reauth_generic_return(struct smbXsrv_session *session,
                return NT_STATUS_LOGON_FAILURE;
        }
 
-       conn_clear_vuid_caches(smb2req->sconn, session->compat->vuid);
+       conn_clear_vuid_caches(xconn->client->sconn, session->compat->vuid);
 
        if (security_session_user_level(session_info, NULL) >= SECURITY_USER) {
                smb2req->do_signing = true;
@@ -538,6 +560,116 @@ static NTSTATUS smbd_smb2_reauth_generic_return(struct smbXsrv_session *session,
        return NT_STATUS_OK;
 }
 
+static NTSTATUS smbd_smb2_bind_auth_return(struct smbXsrv_session *session,
+                                          struct smbXsrv_session_auth0 **_auth,
+                                          struct smbd_smb2_request *smb2req,
+                                          struct auth_session_info *session_info,
+                                          uint16_t *out_session_flags,
+                                          uint64_t *out_session_id)
+{
+       NTSTATUS status;
+       struct smbXsrv_session *x = session;
+       struct smbXsrv_session_auth0 *auth = *_auth;
+       struct smbXsrv_connection *xconn = smb2req->xconn;
+       struct smbXsrv_channel_global0 *c = NULL;
+       uint8_t session_key[16];
+       size_t i;
+       struct _derivation {
+               DATA_BLOB label;
+               DATA_BLOB context;
+       };
+       struct {
+               struct _derivation signing;
+       } derivation = { };
+       bool ok;
+
+       *_auth = NULL;
+
+       if (xconn->protocol >= PROTOCOL_SMB3_10) {
+               struct smbXsrv_preauth *preauth;
+               struct _derivation *d;
+               DATA_BLOB p;
+               struct hc_sha512state sctx;
+
+               preauth = talloc_move(smb2req, &auth->preauth);
+
+               samba_SHA512_Init(&sctx);
+               samba_SHA512_Update(&sctx, preauth->sha512_value,
+                                   sizeof(preauth->sha512_value));
+               for (i = 1; i < smb2req->in.vector_count; i++) {
+                       samba_SHA512_Update(&sctx,
+                                           smb2req->in.vector[i].iov_base,
+                                           smb2req->in.vector[i].iov_len);
+               }
+               samba_SHA512_Final(preauth->sha512_value, &sctx);
+
+               p = data_blob_const(preauth->sha512_value,
+                                   sizeof(preauth->sha512_value));
+
+               d = &derivation.signing;
+               d->label = data_blob_string_const_null("SMBSigningKey");
+               d->context = p;
+
+       } else if (xconn->protocol >= PROTOCOL_SMB2_24) {
+               struct _derivation *d;
+
+               d = &derivation.signing;
+               d->label = data_blob_string_const_null("SMB2AESCMAC");
+               d->context = data_blob_string_const_null("SmbSign");
+       }
+
+       status = smbXsrv_session_find_channel(session, xconn, &c);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       ok = security_token_is_sid(session_info->security_token,
+                       &x->global->auth_session_info->security_token->sids[0]);
+       if (!ok) {
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       if (session_info->session_key.length == 0) {
+               /* See [MS-SMB2] 3.3.5.2.4 for the return code. */
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       ZERO_STRUCT(session_key);
+       memcpy(session_key, session_info->session_key.data,
+              MIN(session_info->session_key.length, sizeof(session_key)));
+
+       c->signing_key = data_blob_talloc(x->global,
+                                         session_key,
+                                         sizeof(session_key));
+       if (c->signing_key.data == NULL) {
+               ZERO_STRUCT(session_key);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (xconn->protocol >= PROTOCOL_SMB2_24) {
+               struct _derivation *d = &derivation.signing;
+
+               smb2_key_derivation(session_key, sizeof(session_key),
+                                   d->label.data, d->label.length,
+                                   d->context.data, d->context.length,
+                                   c->signing_key.data);
+       }
+       ZERO_STRUCT(session_key);
+
+       TALLOC_FREE(auth);
+       status = smbXsrv_session_update(session);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("smb2: Failed to update session for vuid=%llu - %s\n",
+                         (unsigned long long)session->compat->vuid,
+                         nt_errstr(status)));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       *out_session_id = session->global->session_wire_id;
+
+       return NT_STATUS_OK;
+}
+
 struct smbd_smb2_session_setup_state {
        struct tevent_context *ev;
        struct smbd_smb2_request *smb2req;
@@ -573,6 +705,7 @@ static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx,
        NTTIME now = timeval_to_nttime(&smb2req->request_time);
        struct tevent_req *subreq;
        struct smbXsrv_channel_global0 *c = NULL;
+       enum security_user_level seclvl;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct smbd_smb2_session_setup_state);
@@ -593,13 +726,87 @@ static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx,
                        return tevent_req_post(req, ev);
                }
 
+               if (!smb2req->xconn->client->server_multi_channel_enabled) {
+                       tevent_req_nterror(req, NT_STATUS_REQUEST_NOT_ACCEPTED);
+                       return tevent_req_post(req, ev);
+               }
+
+               if (in_session_id == 0) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, ev);
+               }
+
+               if (smb2req->session == NULL) {
+                       tevent_req_nterror(req, NT_STATUS_USER_SESSION_DELETED);
+                       return tevent_req_post(req, ev);
+               }
+
+               if (!smb2req->do_signing) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, ev);
+               }
+
+               status = smbXsrv_session_find_channel(smb2req->session,
+                                                     smb2req->xconn,
+                                                     &c);
+               if (NT_STATUS_IS_OK(status)) {
+                       if (c->signing_key.length == 0) {
+                               goto auth;
+                       }
+                       tevent_req_nterror(req, NT_STATUS_REQUEST_NOT_ACCEPTED);
+                       return tevent_req_post(req, ev);
+               }
+
                /*
-                * We do not support multi channel.
+                * OLD: 3.00 NEW 3.02 => INVALID_PARAMETER
+                * OLD: 3.02 NEW 3.00 => INVALID_PARAMETER
+                * OLD: 2.10 NEW 3.02 => ACCESS_DENIED
+                * OLD: 3.02 NEW 2.10 => ACCESS_DENIED
                 */
-               tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
-               return tevent_req_post(req, ev);
+               if (smb2req->session->global->connection_dialect
+                   < SMB2_DIALECT_REVISION_222)
+               {
+                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+                       return tevent_req_post(req, ev);
+               }
+               if (smb2req->xconn->smb2.server.dialect
+                   < SMB2_DIALECT_REVISION_222)
+               {
+                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+                       return tevent_req_post(req, ev);
+               }
+               if (smb2req->session->global->connection_dialect
+                   != smb2req->xconn->smb2.server.dialect)
+               {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, ev);
+               }
+
+               seclvl = security_session_user_level(
+                               smb2req->session->global->auth_session_info,
+                               NULL);
+               if (seclvl < SECURITY_USER) {
+                       tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
+                       return tevent_req_post(req, ev);
+               }
+
+               status = smbXsrv_session_add_channel(smb2req->session,
+                                                    smb2req->xconn,
+                                                    &c);
+               if (!NT_STATUS_IS_OK(status)) {
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, ev);
+               }
+
+               status = smbXsrv_session_update(smb2req->session);
+               if (!NT_STATUS_IS_OK(status)) {
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, ev);
+               }
        }
 
+auth:
+
        if (state->in_session_id == 0) {
                /* create a new session */
                status = smbXsrv_session_create(state->smb2req->xconn,
@@ -652,6 +859,8 @@ static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx,
        if (state->auth->gensec == NULL) {
                status = auth_generic_prepare(state->auth,
                                              state->smb2req->xconn->remote_address,
+                                             state->smb2req->xconn->local_address,
+                                             "SMB2",
                                              &state->auth->gensec);
                if (tevent_req_nterror(req, status)) {
                        return tevent_req_post(req, ev);
@@ -659,6 +868,7 @@ static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx,
 
                gensec_want_feature(state->auth->gensec, GENSEC_FEATURE_SESSION_KEY);
                gensec_want_feature(state->auth->gensec, GENSEC_FEATURE_UNIX_TOKEN);
+               gensec_want_feature(state->auth->gensec, GENSEC_FEATURE_SMB_TRANSPORT);
 
                status = gensec_start_mech_by_oid(state->auth->gensec,
                                                  GENSEC_OID_SPNEGO);
@@ -714,7 +924,7 @@ static void smbd_smb2_session_setup_gensec_done(struct tevent_req *subreq)
        }
 
        status = gensec_session_info(state->auth->gensec,
-                                    state->session->global,
+                                    state,
                                     &state->session_info);
        if (tevent_req_nterror(req, status)) {
                return;
@@ -764,6 +974,20 @@ static void smbd_smb2_session_setup_auth_return(struct tevent_req *req)
                struct smbd_smb2_session_setup_state);
        NTSTATUS status;
 
+       if (state->in_flags & SMB2_SESSION_FLAG_BINDING) {
+               status = smbd_smb2_bind_auth_return(state->session,
+                                                   &state->auth,
+                                                   state->smb2req,
+                                                   state->session_info,
+                                                   &state->out_session_flags,
+                                                   &state->out_session_id);
+               if (tevent_req_nterror(req, status)) {
+                       return;
+               }
+               tevent_req_done(req);
+               return;
+       }
+
        if (state->session->global->auth_session_info != NULL) {
                status = smbd_smb2_reauth_generic_return(state->session,
                                                         &state->auth,
@@ -994,7 +1218,7 @@ NTSTATUS smbd_smb2_request_process_logoff(struct smbd_smb2_request *req)
                return smbd_smb2_request_error(req, status);
        }
 
-       subreq = smbd_smb2_logoff_send(req, req->sconn->ev_ctx, req);
+       subreq = smbd_smb2_logoff_send(req, req->ev_ctx, req);
        if (subreq == NULL) {
                return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
        }
@@ -1089,6 +1313,9 @@ static void smbd_smb2_logoff_shutdown_done(struct tevent_req *subreq)
        struct smbd_smb2_logoff_state *state = tevent_req_data(
                req, struct smbd_smb2_logoff_state);
        NTSTATUS status;
+       bool ok;
+       const struct GUID *client_guid =
+               &state->smb2req->session->client->connections->smb2.client.guid;
 
        status = smb2srv_session_shutdown_recv(subreq);
        if (tevent_req_nterror(req, status)) {
@@ -1096,6 +1323,14 @@ static void smbd_smb2_logoff_shutdown_done(struct tevent_req *subreq)
        }
        TALLOC_FREE(subreq);
 
+       if (!GUID_all_zero(client_guid)) {
+               ok = remote_arch_cache_delete(client_guid);
+               if (!ok) {
+                       /* Most likely not an error, but not in cache */
+                       DBG_DEBUG("Deletion from remote arch cache failed\n");
+               }
+       }
+
        /*
         * As we've been awoken, we may have changed
         * uid in the meantime. Ensure we're still