CVE-2016-2111: s4:smb_server: implement "raw NTLMv2 auth" checks
[samba.git] / source4 / smb_server / smb / sesssetup.c
index 7372347e9ae3b3e1d4fdb7a33aa818014418b3ed..e06853afcd4fb67826c08b3fa57bf9620b3aab9b 100644 (file)
 #include "smbd/service_stream.h"
 #include "param/param.h"
 #include "../lib/tsocket/tsocket.h"
+#include "lib/stream/packet.h"
 
 struct sesssetup_context {
-       struct auth_context *auth_context;
+       struct auth4_context *auth_context;
        struct smbsrv_request *req;
 };
 
@@ -67,24 +68,25 @@ static void sesssetup_old_send(struct tevent_req *subreq)
        struct smbsrv_request *req = state->req;
 
        union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
-       struct auth_serversupplied_info *server_info = NULL;
+       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, &server_info);
+       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 (server_info->authenticated) {
+       if (user_info_dc->info->authenticated) {
                flags |= AUTH_SESSION_INFO_AUTHENTICATED;
        }
-       /* This references server_info into session_info */
-       status = req->smb_conn->negotiate.auth_context->generate_session_info(req,
-                                                                             req->smb_conn->negotiate.auth_context,
-                                                                             server_info, flags, &session_info);
+       /* 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 */
@@ -105,7 +107,7 @@ static void sesssetup_old_send(struct tevent_req *subreq)
        sess->old.out.vuid = smb_sess->vuid;
 
 failed:
-       status = auth_nt_status_squash(status);
+       status = nt_status_squash(status);
        smbsrv_sesssetup_backend_send(req, sess, status);
 }
 
@@ -144,7 +146,7 @@ static void sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess)
                if (!remote_machine) goto nomem;
        }
 
-       user_info = talloc(req, struct auth_usersupplied_info);
+       user_info = talloc_zero(req, struct auth_usersupplied_info);
        if (!user_info) goto nomem;
        
        user_info->mapped_state = false;
@@ -197,26 +199,26 @@ 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_serversupplied_info *server_info = NULL;
+       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, &server_info);
+       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 (server_info->authenticated) {
+       if (user_info_dc->info->authenticated) {
                flags |= AUTH_SESSION_INFO_AUTHENTICATED;
        }
-
-       /* This references server_info into session_info */
-       status = state->auth_context->generate_session_info(req,
-                                                           state->auth_context,
-                                                           server_info,
+       /* 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;
@@ -246,7 +248,7 @@ static void sesssetup_nt1_send(struct tevent_req *subreq)
 done:
        status = NT_STATUS_OK;
 failed:
-       status = auth_nt_status_squash(status);
+       status = nt_status_squash(status);
        smbsrv_sesssetup_backend_send(req, sess, status);
 }
 
@@ -261,6 +263,7 @@ static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
        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;
@@ -319,7 +322,7 @@ static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
                if (!remote_machine) goto nomem;
        }
 
-       user_info = talloc(req, struct auth_usersupplied_info);
+       user_info = talloc_zero(req, struct auth_usersupplied_info);
        if (!user_info) goto nomem;
 
        user_info->mapped_state = false;
@@ -336,6 +339,15 @@ static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
        user_info->password.response.nt = sess->nt1.in.password2;
        user_info->password.response.nt.data = talloc_steal(user_info, sess->nt1.in.password2.data);
 
+       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;
+       }
+
        subreq = auth_check_password_send(state,
                                          req->smb_conn->connection->event.ctx,
                                          state->auth_context,
@@ -348,7 +360,7 @@ static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
 nomem:
        status = NT_STATUS_NO_MEMORY;
 failed:
-       status = auth_nt_status_squash(status);
+       status = nt_status_squash(status);
        smbsrv_sesssetup_backend_send(req, sess, status);
 }
 
@@ -371,6 +383,7 @@ static void sesssetup_spnego_send(struct tevent_req *subreq)
        DATA_BLOB session_key;
 
        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;
@@ -378,10 +391,11 @@ static void sesssetup_spnego_send(struct tevent_req *subreq)
                goto failed;
        }
 
-       status = gensec_session_info(smb_sess->gensec_ctx, &session_info);
+       status = gensec_session_info(smb_sess->gensec_ctx, smb_sess, &session_info);
        if (!NT_STATUS_IS_OK(status)) goto failed;
 
-       skey_status = gensec_session_key(smb_sess->gensec_ctx, &session_key);
+       /* 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);
        }
@@ -396,7 +410,7 @@ static void sesssetup_spnego_send(struct tevent_req *subreq)
 done:
        sess->spnego.out.vuid = smb_sess->vuid;
 failed:
-       status = auth_nt_status_squash(status);
+       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)) {
@@ -411,6 +425,7 @@ static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *se
 {
        NTSTATUS status;
        struct smbsrv_session *smb_sess = NULL;
+       bool is_smb_sess_new = false;
        struct sesssetup_spnego_state *s = NULL;
        uint16_t vuid;
        struct tevent_req *subreq;
@@ -431,8 +446,7 @@ static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *se
        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 = samba_server_gensec_start(req,
@@ -462,10 +476,18 @@ static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *se
                        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) {
-               status = NT_STATUS_ACCESS_DENIED;
+               status = NT_STATUS_DOS(ERRSRV, ERRbaduid);
+               goto failed;
+       }
+
+       if (smb_sess->session_info) {
+               status = NT_STATUS_INVALID_PARAMETER;
                goto failed;
        }
 
@@ -488,6 +510,11 @@ static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *se
        if (!subreq) {
                goto nomem;
        }
+       /* 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);
 
        return;
@@ -495,8 +522,10 @@ static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *se
 nomem:
        status = NT_STATUS_NO_MEMORY;
 failed:
-       talloc_free(smb_sess);
-       status = 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);
 }