TODO: libcli/auth: add support for AES/HMAC-SHA256 schannel support
[metze/samba/wip.git] / libcli / auth / credentials.c
index e268b9ddff34bd0e711218e15eaaaa39bdff11d4..f6054830b9cc912b29692ef1a4b58c756f85ddd3 100644 (file)
 #include "system/time.h"
 #include "../lib/crypto/crypto.h"
 #include "libcli/auth/libcli_auth.h"
+#include "heimdal/lib/hcrypto/aes.h"
+
+static void netlogon_creds_step_crypt(struct netlogon_creds_CredentialState *creds,
+                                     const struct netr_Credential *in,
+                                     struct netr_Credential *out)
+{
+       if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+               AES_KEY key;
+               uint8_t aes_in[AES_BLOCK_SIZE];
+               uint8_t aes_out[AES_BLOCK_SIZE];
+
+               AES_set_encrypt_key(creds->session_key, 128, &key);
+
+               ZERO_STRUCT(aes_in);
+               memcpy(aes_in, in->data, 8);
+               AES_encrypt(aes_in, aes_out, &key);
+               memcpy(out->data, aes_out, 8);
+
+               ZERO_STRUCT(key);
+               ZERO_STRUCT(aes_in);
+               ZERO_STRUCT(aes_out);
+       } else {
+               des_crypt112(out->data, in->data, creds->session_key, 1);
+       }
+}
 
 /*
   initialise the credentials state for old-style 64 bit session keys
@@ -47,11 +72,6 @@ static void netlogon_creds_init_64bit(struct netlogon_creds_CredentialState *cre
        ZERO_STRUCT(creds->session_key);
 
        des_crypt128(creds->session_key, sum2, machine_password->hash);
-
-       des_crypt112(creds->client.data, client_challenge->data, creds->session_key, 1);
-       des_crypt112(creds->server.data, server_challenge->data, creds->session_key, 1);
-
-       creds->seed = creds->client;
 }
 
 /*
@@ -80,13 +100,46 @@ static void netlogon_creds_init_128bit(struct netlogon_creds_CredentialState *cr
        MD5Final(tmp, &md5);
        hmac_md5_update(tmp, sizeof(tmp), &ctx);
        hmac_md5_final(creds->session_key, &ctx);
+}
 
-       des_crypt112(creds->client.data, client_challenge->data, creds->session_key, 1);
-       des_crypt112(creds->server.data, server_challenge->data, creds->session_key, 1);
+/*
+  initialise the credentials state for AES/HMAC-SHA256-style 128 bit session keys
 
-       creds->seed = creds->client;
+  this call is made after the netr_ServerReqChallenge call
+*/
+static void netlogon_creds_init_hmac_sha256(struct netlogon_creds_CredentialState *creds,
+                                           const struct netr_Credential *client_challenge,
+                                           const struct netr_Credential *server_challenge,
+                                           const struct samr_Password *machine_password)
+{
+       struct HMACSHA256Context ctx;
+       uint8_t digest[SHA256_DIGEST_LENGTH];
+
+       ZERO_STRUCT(creds->session_key);
+
+       hmac_sha256_init(machine_password->hash,
+                        sizeof(machine_password->hash),
+                        &ctx);
+       hmac_sha256_update(client_challenge->data, 8, &ctx);
+       hmac_sha256_update(server_challenge->data, 8, &ctx);
+       hmac_sha256_final(digest, &ctx);
+
+       memcpy(creds->session_key, digest, sizeof(creds->session_key));
+
+       ZERO_STRUCT(digest);
+       ZERO_STRUCT(ctx);
 }
 
+static void netlogon_creds_first_step(struct netlogon_creds_CredentialState *creds,
+                                     const struct netr_Credential *client_challenge,
+                                     const struct netr_Credential *server_challenge)
+{
+       netlogon_creds_step_crypt(creds, client_challenge, &creds->client);
+
+       netlogon_creds_step_crypt(creds, server_challenge, &creds->server);
+
+       creds->seed = creds->client;
+}
 
 /*
   step the credentials to the next element in the chain, updating the
@@ -104,7 +157,7 @@ static void netlogon_creds_step(struct netlogon_creds_CredentialState *creds)
 
        DEBUG(5,("\tseed+time   %08x:%08x\n", IVAL(time_cred.data, 0), IVAL(time_cred.data, 4)));
 
-       des_crypt112(creds->client.data, time_cred.data, creds->session_key, 1);
+       netlogon_creds_step_crypt(creds, &time_cred, &creds->client);
 
        DEBUG(5,("\tCLIENT      %08x:%08x\n", 
                 IVAL(creds->client.data, 0), IVAL(creds->client.data, 4)));
@@ -115,7 +168,7 @@ static void netlogon_creds_step(struct netlogon_creds_CredentialState *creds)
        DEBUG(5,("\tseed+time+1 %08x:%08x\n", 
                 IVAL(time_cred.data, 0), IVAL(time_cred.data, 4)));
 
-       des_crypt112(creds->server.data, time_cred.data, creds->session_key, 1);
+       netlogon_creds_step_crypt(creds, &time_cred, &creds->server);
 
        DEBUG(5,("\tSERVER      %08x:%08x\n", 
                 IVAL(creds->server.data, 0), IVAL(creds->server.data, 4)));
@@ -219,12 +272,19 @@ struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *me
        dump_data_pw("Server chall", server_challenge->data, sizeof(server_challenge->data));
        dump_data_pw("Machine Pass", machine_password->hash, sizeof(machine_password->hash));
 
-       if (negotiate_flags & NETLOGON_NEG_128BIT) {
+       if (negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+               netlogon_creds_init_hmac_sha256(creds,
+                                               client_challenge,
+                                               server_challenge,
+                                               machine_password);
+       } else if (negotiate_flags & NETLOGON_NEG_STRONG_KEYS) {
                netlogon_creds_init_128bit(creds, client_challenge, server_challenge, machine_password);
        } else {
                netlogon_creds_init_64bit(creds, client_challenge, server_challenge, machine_password);
        }
 
+       netlogon_creds_first_step(creds, client_challenge, server_challenge);
+
        dump_data_pw("Session key", creds->session_key, 16);
        dump_data_pw("Credential ", creds->client.data, 8);
 
@@ -317,9 +377,13 @@ struct netlogon_creds_CredentialState *netlogon_creds_server_init(TALLOC_CTX *me
                                                                  struct netr_Credential *credentials_out,
                                                                  uint32_t negotiate_flags)
 {
-       
        struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
-       
+       bool ok = false;
+       bool try_aes = true;
+       bool try_strong = true;
+       bool try_weak = true;
+       bool fallback = false;
+
        if (!creds) {
                return NULL;
        }
@@ -338,17 +402,54 @@ struct netlogon_creds_CredentialState *netlogon_creds_server_init(TALLOC_CTX *me
                return NULL;
        }
 
-       if (negotiate_flags & NETLOGON_NEG_128BIT) {
+       if (!ok && try_aes) {
+               if (fallback) {
+                       DEBUG(2,("credentials check fallback to aes key\n"));
+               }
+               creds->negotiate_flags |= NETLOGON_NEG_SUPPORTS_AES;
+               netlogon_creds_init_hmac_sha256(creds,
+                                               client_challenge,
+                                               server_challenge,
+                                               machine_password);
+               netlogon_creds_first_step(creds, client_challenge, server_challenge);
+               ok = netlogon_creds_server_check_internal(creds, credentials_in);
+               if (!ok) {
+                       creds->negotiate_flags &= ~NETLOGON_NEG_SUPPORTS_AES;
+                       fallback = true;
+               }
+       }
+
+       if (!ok && try_strong) {
+               if (fallback) {
+                       DEBUG(2,("credentials check fallback to strong key\n"));
+               }
+               creds->negotiate_flags &= ~NETLOGON_NEG_SUPPORTS_AES;
+               creds->negotiate_flags |= NETLOGON_NEG_STRONG_KEYS;
                netlogon_creds_init_128bit(creds, client_challenge, server_challenge, 
                                           machine_password);
-       } else {
+               netlogon_creds_first_step(creds, client_challenge, server_challenge);
+               ok = netlogon_creds_server_check_internal(creds, credentials_in);
+               if (!ok) {
+                       creds->negotiate_flags &= ~NETLOGON_NEG_STRONG_KEYS;
+                       fallback = true;
+               }
+       }
+
+       if (!ok && try_weak) {
+               if (fallback) {
+                       DEBUG(2,("credentials check fallback to weak key\n"));
+               }
+               creds->negotiate_flags &= ~NETLOGON_NEG_SUPPORTS_AES;
+               creds->negotiate_flags &= ~NETLOGON_NEG_STRONG_KEYS;
                netlogon_creds_init_64bit(creds, client_challenge, server_challenge, 
                                          machine_password);
+               netlogon_creds_first_step(creds, client_challenge, server_challenge);
+               ok = netlogon_creds_server_check_internal(creds, credentials_in);
        }
 
        /* And before we leak information about the machine account
         * password, check that they got the first go right */
-       if (!netlogon_creds_server_check_internal(creds, credentials_in)) {
+       if (!ok) {
                talloc_free(creds);
                return NULL;
        }