Correctly use SPNEGO to negotiate down from krb5 to NTLMSSP.
authorJeremy Allison <jra@samba.org>
Fri, 15 Feb 2008 02:10:50 +0000 (18:10 -0800)
committerJeremy Allison <jra@samba.org>
Fri, 15 Feb 2008 02:10:50 +0000 (18:10 -0800)
Previously we didn't implement the 'NEGO' part of SPNEGO :-).
Jeremy.
(This used to be commit 8767a0dab95c544878b4187157e494e740974bb8)

source3/smbd/sesssetup.c

index 1e4e20895138a6efad3c4d0a2b34ecde8ed9aacd..3e04da35df7139d0ccbc426db20a4bf7b8d6dc3b 100644 (file)
@@ -620,6 +620,7 @@ static void reply_spnego_ntlmssp(struct smb_request *req,
                                 uint16 vuid,
                                 AUTH_NTLMSSP_STATE **auth_ntlmssp_state,
                                 DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status,
+                                const char *OID,
                                 bool wrap)
 {
        DATA_BLOB response;
@@ -680,7 +681,7 @@ static void reply_spnego_ntlmssp(struct smb_request *req,
 
        if (wrap) {
                response = spnego_gen_auth_response(ntlmssp_blob,
-                               nt_status, OID_NTLMSSP);
+                               nt_status, OID);
        } else {
                response = *ntlmssp_blob;
        }
@@ -744,6 +745,28 @@ NTSTATUS parse_spnego_mechanisms(DATA_BLOB blob_in, DATA_BLOB *pblob_out,
        return NT_STATUS_OK;
 }
 
+/****************************************************************************
+ Fall back from krb5 to NTLMSSP.
+****************************************************************************/
+
+static void reply_spnego_downgrade_to_ntlmssp(struct smb_request *req,
+                                               uint16 vuid)
+{
+       DATA_BLOB response;
+
+       reply_outbuf(req, 4, 0);
+        SSVAL(req->outbuf,smb_uid,vuid);
+
+       DEBUG(3,("reply_spnego_downgrade_to_ntlmssp: Got krb5 ticket in SPNEGO "
+               "but set to downgrade to NTLMSSP\n"));
+
+       response = spnego_gen_auth_response(NULL,
+                       NT_STATUS_MORE_PROCESSING_REQUIRED,
+                       OID_NTLMSSP);
+       reply_sesssetup_blob(req, response, NT_STATUS_MORE_PROCESSING_REQUIRED);
+       data_blob_free(&response);
+}
+
 /****************************************************************************
  Reply to a session setup spnego negotiate packet.
 ****************************************************************************/
@@ -789,6 +812,15 @@ static void reply_spnego_negotiate(struct smb_request *req,
                auth_ntlmssp_end(auth_ntlmssp_state);
        }
 
+       if (got_kerberos_mechanism) {
+               data_blob_free(&secblob);
+               /* The mechtoken is a krb5 ticket, but
+                * we need to fall back to NTLM. */
+               reply_spnego_downgrade_to_ntlmssp(req,
+                                       vuid);
+               return;
+       }
+
        status = auth_ntlmssp_start(auth_ntlmssp_state);
        if (!NT_STATUS_IS_OK(status)) {
                /* Kill the intermediate vuid */
@@ -803,7 +835,7 @@ static void reply_spnego_negotiate(struct smb_request *req,
        data_blob_free(&secblob);
 
        reply_spnego_ntlmssp(req, vuid, auth_ntlmssp_state,
-                            &chal, status, True);
+                            &chal, status, OID_NTLMSSP, true);
 
        data_blob_free(&chal);
 
@@ -823,7 +855,7 @@ static void reply_spnego_auth(struct smb_request *req,
        DATA_BLOB auth = data_blob_null;
        DATA_BLOB auth_reply = data_blob_null;
        DATA_BLOB secblob = data_blob_null;
-       NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
+       NTSTATUS status = NT_STATUS_LOGON_FAILURE;
 
        if (!spnego_parse_auth(blob1, &auth)) {
 #if 0
@@ -833,7 +865,7 @@ static void reply_spnego_auth(struct smb_request *req,
                invalidate_vuid(vuid);
 
                reply_nterror(req, nt_status_squash(
-                                     NT_STATUS_INVALID_PARAMETER));
+                                     NT_STATUS_LOGON_FAILURE));
                return;
        }
 
@@ -843,24 +875,43 @@ static void reply_spnego_auth(struct smb_request *req,
                bool got_krb5_mechanism = False;
                status = parse_spnego_mechanisms(auth, &secblob,
                                &got_krb5_mechanism);
-               if (NT_STATUS_IS_OK(status)) {
-                       DEBUG(3,("reply_spnego_auth: Got secblob of size %lu\n",
-                                       (unsigned long)secblob.length));
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       /* Kill the intermediate vuid */
+                       invalidate_vuid(vuid);
+                       reply_nterror(req, nt_status_squash(status));
+                       return;
+               }
+
+               DEBUG(3,("reply_spnego_auth: Got secblob of size %lu\n",
+                               (unsigned long)secblob.length));
 #ifdef HAVE_KRB5
-                       if ( got_krb5_mechanism && ((lp_security()==SEC_ADS) ||
-                                               lp_use_kerberos_keytab()) ) {
-                               bool destroy_vuid = True;
-                               reply_spnego_kerberos(req, &secblob,
-                                                     vuid, &destroy_vuid);
-                               data_blob_free(&secblob);
-                               data_blob_free(&auth);
-                               if (destroy_vuid) {
-                                       /* Kill the intermediate vuid */
-                                       invalidate_vuid(vuid);
-                               }
-                               return;
+               if ( got_krb5_mechanism && ((lp_security()==SEC_ADS) ||
+                                       lp_use_kerberos_keytab()) ) {
+                       bool destroy_vuid = True;
+                       reply_spnego_kerberos(req, &secblob,
+                                             vuid, &destroy_vuid);
+                       data_blob_free(&secblob);
+                       data_blob_free(&auth);
+                       if (destroy_vuid) {
+                               /* Kill the intermediate vuid */
+                               invalidate_vuid(vuid);
                        }
+                       return;
+               }
 #endif
+               /* Can't blunder into NTLMSSP auth if we have
+                * a krb5 ticket. */
+
+               if (got_krb5_mechanism) {
+                       /* Kill the intermediate vuid */
+                       invalidate_vuid(vuid);
+                       DEBUG(3,("reply_spnego_auth: network "
+                               "misconfiguration, client sent us a "
+                               "krb5 ticket and kerberos security "
+                               "not enabled"));
+                       reply_nterror(req, nt_status_squash(
+                                       NT_STATUS_LOGON_FAILURE));
                }
        }
 
@@ -868,13 +919,13 @@ static void reply_spnego_auth(struct smb_request *req,
        data_blob_free(&secblob);
 
        if (!*auth_ntlmssp_state) {
-               /* Kill the intermediate vuid */
-               invalidate_vuid(vuid);
-
-               /* auth before negotiatiate? */
-               reply_nterror(req, nt_status_squash(
-                                     NT_STATUS_INVALID_PARAMETER));
-               return;
+               status = auth_ntlmssp_start(auth_ntlmssp_state);
+               if (!NT_STATUS_IS_OK(status)) {
+                       /* Kill the intermediate vuid */
+                       invalidate_vuid(vuid);
+                       reply_nterror(req, nt_status_squash(status));
+                       return;
+               }
        }
 
        status = auth_ntlmssp_update(*auth_ntlmssp_state,
@@ -882,9 +933,11 @@ static void reply_spnego_auth(struct smb_request *req,
 
        data_blob_free(&auth);
 
+       /* Don't send the mechid as we've already sent this (RFC4178). */
+
        reply_spnego_ntlmssp(req, vuid,
                             auth_ntlmssp_state,
-                            &auth_reply, status, True);
+                            &auth_reply, status, NULL, true);
 
        data_blob_free(&auth_reply);
 
@@ -1251,7 +1304,7 @@ static void reply_sesssetup_and_X_spnego(struct smb_request *req)
 
                reply_spnego_ntlmssp(req, vuid,
                                     &vuser->auth_ntlmssp_state,
-                                    &chal, status, False);
+                                    &chal, status, OID_NTLMSSP, false);
                data_blob_free(&chal);
                return;
        }