s4:auth/ntlmssp: let _unwrap fallback to seal if sign only doesn't work
[metze/samba/wip.git] / source4 / auth / ntlmssp / ntlmssp_sign.c
index 8eea4fd8ae77e64503fa4bd94d3bec8fdaaa0d1a..957d0a8fbc7696ee90fcd600caa4bb4190c88759 100644 (file)
@@ -7,7 +7,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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "includes.h"
-#include "auth/auth.h"
 #include "auth/ntlmssp/ntlmssp.h"
-#include "lib/crypto/crypto.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../lib/crypto/crypto.h"
+#include "auth/gensec/gensec.h"
 
 #define CLI_SIGN "session key to client-to-server signing key magic constant"
 #define CLI_SEAL "session key to client-to-server sealing key magic constant"
@@ -31,7 +31,7 @@
 #define SRV_SEAL "session key to server-to-client sealing key magic constant"
 
 /**
- * Some notes on then NTLM2 code:
+ * Some notes on the NTLM2 code:
  *
  * NTLM2 is a AEAD system.  This means that the data encrypted is not
  * all the data that is signed.  In DCE-RPC case, the headers of the
@@ -63,7 +63,7 @@ static NTSTATUS ntlmssp_make_packet_signature(struct gensec_ntlmssp_state *gense
                                              const uint8_t *data, size_t length, 
                                              const uint8_t *whole_pdu, size_t pdu_length, 
                                              enum ntlmssp_direction direction,
-                                             DATA_BLOB *sig, BOOL encrypt_sig)
+                                             DATA_BLOB *sig, bool encrypt_sig)
 {
        if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
 
@@ -109,32 +109,39 @@ static NTSTATUS ntlmssp_make_packet_signature(struct gensec_ntlmssp_state *gense
                memcpy(sig->data + 4, digest, 8);
                memcpy(sig->data + 12, seq_num, 4);
 
+               DEBUG(10, ("NTLM2: created signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
+               dump_data(11, sig->data, sig->length);
+                       
        } else {
                uint32_t crc;
                crc = crc32_calc_buffer(data, length);
-               if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
+               if (!msrpc_gen(sig_mem_ctx, 
+                              sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
                        return NT_STATUS_NO_MEMORY;
                }
                gensec_ntlmssp_state->crypt.ntlm.seq_num++;
 
                arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, sig->data+4, sig->length-4);
+
+               DEBUG(10, ("NTLM1: created signature over %llu bytes of input:\n", (unsigned long long)length));
+               dump_data(11, sig->data, sig->length);
        }
-       dump_data_pw("calculated ntlmssp signature\n", sig->data, sig->length);
        return NT_STATUS_OK;
 }
 
+/* TODO: make this non-public */
 NTSTATUS gensec_ntlmssp_sign_packet(struct gensec_security *gensec_security, 
                                    TALLOC_CTX *sig_mem_ctx, 
                                    const uint8_t *data, size_t length, 
                                    const uint8_t *whole_pdu, size_t pdu_length, 
                                    DATA_BLOB *sig)
 {
-       struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
+       struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
 
        return ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx, 
                                             data, length, 
                                             whole_pdu, pdu_length, 
-                                            NTLMSSP_SEND, sig, True);
+                                            NTLMSSP_SEND, sig, true);
 }
 
 /**
@@ -148,7 +155,7 @@ NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security,
                                     const uint8_t *whole_pdu, size_t pdu_length, 
                                     const DATA_BLOB *sig)
 {
-       struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
+       struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
 
        DATA_BLOB local_sig;
        NTSTATUS nt_status;
@@ -158,15 +165,10 @@ NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security,
                return NT_STATUS_NO_USER_SESSION_KEY;
        }
 
-       if (sig->length < 8) {
-               DEBUG(0, ("NTLMSSP packet check failed due to short signature (%lu bytes)!\n", 
-                         (unsigned long)sig->length));
-       }
-
        nt_status = ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx, 
                                                  data, length, 
                                                  whole_pdu, pdu_length, 
-                                                 NTLMSSP_RECEIVE, &local_sig, True);
+                                                 NTLMSSP_RECEIVE, &local_sig, true);
        
        if (!NT_STATUS_IS_OK(nt_status)) {
                DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status)));
@@ -177,26 +179,26 @@ NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security,
                if (local_sig.length != sig->length ||
                    memcmp(local_sig.data, 
                           sig->data, sig->length) != 0) {
-                       DEBUG(5, ("BAD SIG NTLM2: wanted signature of\n"));
+                       DEBUG(5, ("BAD SIG NTLM2: wanted signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
                        dump_data(5, local_sig.data, local_sig.length);
                        
-                       DEBUG(5, ("BAD SIG: got signature of\n"));
+                       DEBUG(5, ("BAD SIG: got signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
                        dump_data(5, sig->data, sig->length);
                        
-                       DEBUG(0, ("NTLMSSP NTLM2 packet check failed due to invalid signature!\n"));
+                       DEBUG(1, ("NTLMSSP NTLM2 packet check failed due to invalid signature on %llu bytes of input!\n", (unsigned long long)pdu_length));
                        return NT_STATUS_ACCESS_DENIED;
                }
        } else {
                if (local_sig.length != sig->length ||
                    memcmp(local_sig.data + 8, 
                           sig->data + 8, sig->length - 8) != 0) {
-                       DEBUG(5, ("BAD SIG NTLM1: wanted signature of\n"));
+                       DEBUG(5, ("BAD SIG NTLM1: wanted signature of %llu bytes of input:\n", (unsigned long long)length));
                        dump_data(5, local_sig.data, local_sig.length);
                        
-                       DEBUG(5, ("BAD SIG: got signature of\n"));
+                       DEBUG(5, ("BAD SIG: got signature of %llu bytes of input:\n", (unsigned long long)length));
                        dump_data(5, sig->data, sig->length);
                        
-                       DEBUG(0, ("NTLMSSP NTLM1 packet check failed due to invalid signature!\n"));
+                       DEBUG(1, ("NTLMSSP NTLM1 packet check failed due to invalid signature on %llu bytes of input:\n", (unsigned long long)length));
                        return NT_STATUS_ACCESS_DENIED;
                }
        }
@@ -217,7 +219,7 @@ NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security,
                                    const uint8_t *whole_pdu, size_t pdu_length, 
                                    DATA_BLOB *sig)
 {
-       struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
+       struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
        NTSTATUS nt_status;
        if (!gensec_ntlmssp_state->session_key.length) {
                DEBUG(3, ("NO session key, cannot seal packet\n"));
@@ -233,7 +235,7 @@ NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security,
                nt_status = ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx, 
                                                          data, length, 
                                                          whole_pdu, pdu_length, 
-                                                         NTLMSSP_SEND, sig, False);
+                                                         NTLMSSP_SEND, sig, false);
                arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, data, length);
                if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
                        arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, sig->data+4, 8);
@@ -241,7 +243,8 @@ NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security,
        } else {
                uint32_t crc;
                crc = crc32_calc_buffer(data, length);
-               if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
+               if (!msrpc_gen(sig_mem_ctx, 
+                              sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
                        return NT_STATUS_NO_MEMORY;
                }
 
@@ -278,7 +281,7 @@ NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security,
                                      const uint8_t *whole_pdu, size_t pdu_length, 
                                      const DATA_BLOB *sig)
 {
-       struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
+       struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
        if (!gensec_ntlmssp_state->session_key.length) {
                DEBUG(3, ("NO session key, cannot unseal packet\n"));
                return NT_STATUS_NO_USER_SESSION_KEY;
@@ -297,6 +300,7 @@ NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security,
 /**
    Initialise the state for NTLMSSP signing.
 */
+/* TODO: make this non-public */
 NTSTATUS ntlmssp_sign_init(struct gensec_ntlmssp_state *gensec_ntlmssp_state)
 {
        TALLOC_CTX *mem_ctx = talloc_new(gensec_ntlmssp_state);
@@ -453,6 +457,9 @@ NTSTATUS gensec_ntlmssp_wrap(struct gensec_security *gensec_security,
        if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
 
                *out = data_blob_talloc(sig_mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE);
+               if (!out->data) {
+                       return NT_STATUS_NO_MEMORY;
+               }
                memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length);
                
                nt_status = gensec_ntlmssp_seal_packet(gensec_security, sig_mem_ctx, 
@@ -470,6 +477,9 @@ NTSTATUS gensec_ntlmssp_wrap(struct gensec_security *gensec_security,
        } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
 
                *out = data_blob_talloc(sig_mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE);
+               if (!out->data) {
+                       return NT_STATUS_NO_MEMORY;
+               }
                memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length);
 
                nt_status = gensec_ntlmssp_sign_packet(gensec_security, sig_mem_ctx, 
@@ -513,18 +523,69 @@ NTSTATUS gensec_ntlmssp_unwrap(struct gensec_security *gensec_security,
                                                    &sig);
                                                  
        } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+               struct gensec_ntlmssp_state *gensec_ntlmssp_state =
+               (struct gensec_ntlmssp_state *)gensec_security->private_data;
+               NTSTATUS status;
+               uint32_t ntlm_seqnum;
+               struct arcfour_state ntlm_state;
+               uint32_t ntlm2_seqnum_r;
+               uint8_t ntlm2_key_r[16];
+               struct arcfour_state ntlm2_state_r;
+
                if (in->length < NTLMSSP_SIG_SIZE) {
                        return NT_STATUS_INVALID_PARAMETER;
                }
                sig.data = in->data;
                sig.length = NTLMSSP_SIG_SIZE;
-
                *out = data_blob_talloc(sig_mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE);
-               
-               return gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx, 
-                                                  out->data, out->length, 
-                                                  out->data, out->length, 
-                                                  &sig);
+
+               if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+                       ntlm2_seqnum_r = gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num;
+                       ntlm2_state_r = *gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state;
+                       memcpy(ntlm2_key_r,
+                              gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.data,
+                              16);
+               } else {
+                       ntlm_seqnum = gensec_ntlmssp_state->crypt.ntlm.seq_num;
+                       ntlm_state = *gensec_ntlmssp_state->crypt.ntlm.arcfour_state;
+               }
+
+               status = gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx,
+                                                    out->data, out->length,
+                                                    out->data, out->length,
+                                                    &sig);
+               if (!NT_STATUS_IS_OK(status)) {
+                       NTSTATUS check_status = status;
+                       /*
+                        * The Windows LDAP libraries seems to have a bug
+                        * and always use sealing even if only signing was
+                        * negotiated. So we need to fallback.
+                        */
+
+                       if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+                               gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num = ntlm2_seqnum_r;
+                               *gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state = ntlm2_state_r;
+                               memcpy(gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.data,
+                                      ntlm2_key_r, 16);
+                       } else {
+                               gensec_ntlmssp_state->crypt.ntlm.seq_num = ntlm_seqnum;
+                               *gensec_ntlmssp_state->crypt.ntlm.arcfour_state = ntlm_state;
+                       }
+
+                       status = gensec_ntlmssp_unseal_packet(gensec_security,
+                                                             sig_mem_ctx,
+                                                             out->data,
+                                                             out->length,
+                                                             out->data,
+                                                             out->length,
+                                                             &sig);
+                       if (NT_STATUS_IS_OK(status)) {
+                               gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
+                       } else {
+                               status = check_status;
+                       }
+               }
+               return status;
        } else {
                *out = *in;
                return NT_STATUS_OK;