Add support for NTLMv1 Signing and Sealing
authorSimo Sorce <simo@redhat.com>
Tue, 22 Oct 2013 15:50:13 +0000 (11:50 -0400)
committerSimo Sorce <simo@redhat.com>
Wed, 23 Oct 2013 12:53:12 +0000 (08:53 -0400)
Including tests to verify conformance to MS-NLMP

src/ntlm.c
src/ntlm_crypto.c
tests/ntlmssptest.c

index 3e00cebdd911a17f92a08edf664a3ec1c3f456d2..af6d57aa25523a4c806801b4eea4ad1eb1c9960a 100644 (file)
@@ -142,23 +142,6 @@ struct wire_ntlm_cli_chal {
 };
 #pragma pack(pop)
 
-/* signature structure, v1 or v2 */
-#pragma pack(push, 1)
-union wire_msg_signature {
-    struct {
-        uint32_t version;
-        uint8_t random_pad[4];
-        uint8_t checksum[4];
-        uint32_t seq_num;
-    } v1;
-    struct {
-        uint32_t version;
-        uint8_t checksum[8];
-        uint32_t seq_num;
-    } v2;
-};
-#pragma pack(pop)
-
 /* Version information.
  * Used only for debugging and usually placed as the head of the payload when
  * used */
index a51b03e706d543fde03d29793dba2560419b6459..8c1c1010a43efaef342ca7df4eb86e0bc6e43a9d 100644 (file)
@@ -75,6 +75,23 @@ struct wire_ntlmv2_cli_chal {
 };
 #pragma pack(pop)
 
+/* signature structure, v1 or v2 */
+#pragma pack(push, 1)
+union wire_msg_signature {
+    struct {
+        uint32_t version;
+        uint32_t random_pad;
+        uint32_t checksum;
+        uint32_t seq_num;
+    } v1;
+    struct {
+        uint32_t version;
+        uint64_t checksum;
+        uint32_t seq_num;
+    } v2;
+};
+#pragma pack(pop)
+
 /* the max username is 20 chars, max NB domain len is 15, so 128 should be
  * plenty including conversion to UTF8 using max lenght for each code point
  */
@@ -649,8 +666,8 @@ static int ntlmv2_sign(struct ntlm_key *sign_key, uint32_t seq_num,
                        struct ntlm_buffer *message,
                        struct ntlm_buffer *signature)
 {
-    uint32_t ver;
     struct ntlm_buffer key = { sign_key->data, sign_key->length };
+    union wire_msg_signature *msg_sig;
     uint32_t le_seq;
     uint8_t le8seq[8];
     struct ntlm_buffer seq = { le8seq, 4 };
@@ -662,6 +679,7 @@ static int ntlmv2_sign(struct ntlm_key *sign_key, uint32_t seq_num,
     struct ntlm_buffer rc4res;
     int ret;
 
+    msg_sig = (union wire_msg_signature *)signature->data;
     if (signature->length != 16) {
         return EINVAL;
     }
@@ -677,8 +695,7 @@ static int ntlmv2_sign(struct ntlm_key *sign_key, uint32_t seq_num,
     if (ret) return ret;
 
     /* put version */
-    ver = htole32(NTLMSSP_MESSAGE_SIGNATURE_VERSION);
-    memcpy(signature->data, &ver, 4);
+    msg_sig->v2.version = htole32(NTLMSSP_MESSAGE_SIGNATURE_VERSION);
 
     /* put actual MAC */
     if (keyex) {
@@ -686,16 +703,49 @@ static int ntlmv2_sign(struct ntlm_key *sign_key, uint32_t seq_num,
         rc4buf.data = hmac.data;
         rc4buf.length = 8;
         /* and put it in the middle of the output signature */
-        rc4res.data = &signature->data[4];
+        rc4res.data = (uint8_t *)&msg_sig->v2.checksum;
         rc4res.length = 8;
         ret = RC4_UPDATE(handle, &rc4buf, &rc4res);
         if (ret) return ret;
     } else {
-        memcpy(&signature->data[4], hmac.data, 8);
+        memcpy(&msg_sig->v2.checksum, hmac.data, 8);
     }
 
     /* put used seq_num */
-    memcpy(&signature->data[12], seq.data, 4);
+    msg_sig->v2.seq_num = le_seq;
+
+    return 0;
+}
+
+static int ntlmv1_sign(struct ntlm_rc4_handle *handle,
+                       uint32_t random_pad, uint32_t seq_num,
+                       struct ntlm_buffer *message,
+                       struct ntlm_buffer *signature)
+{
+    union wire_msg_signature *msg_sig;
+    uint32_t rc4buf[3];
+    struct ntlm_buffer payload;
+    struct ntlm_buffer result;
+    int ret;
+
+    msg_sig = (union wire_msg_signature *)signature->data;
+    if (signature->length != 16) {
+        return EINVAL;
+    }
+
+    rc4buf[0] = 0;
+    rc4buf[1] = htole32(CRC32(0, message));
+    rc4buf[2] = htole32(seq_num);
+
+    payload.data = (uint8_t *)rc4buf;
+    payload.length = 12;
+    result.data = (uint8_t *)&msg_sig->v1.random_pad;
+    result.length = 12;
+    ret = RC4_UPDATE(handle, &payload, &result);
+    if (ret) return ret;
+
+    msg_sig->v1.version = htole32(NTLMSSP_MESSAGE_SIGNATURE_VERSION);
+    msg_sig->v1.random_pad = random_pad;
 
     return 0;
 }
@@ -710,7 +760,7 @@ int ntlm_sign(struct ntlm_key *sign_key, uint32_t seq_num,
                            (flags & NTLMSSP_NEGOTIATE_KEY_EXCH),
                            message, signature);
     } else if (flags & NTLMSSP_NEGOTIATE_SIGN) {
-        /* FIXME: needs an implementation of CRC32 maybe use zlib ? */
+        return ntlmv1_sign(handle, 0, seq_num, message, signature);
     } else if (flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) {
         uint32_t le_seq = htole32(seq_num);
         memcpy(signature->data, &le_seq, 4);
@@ -728,19 +778,20 @@ int ntlm_seal(struct ntlm_rc4_handle *handle, uint32_t flags,
 {
     int ret;
 
-    if (!((flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)
-        && (flags & NTLMSSP_NEGOTIATE_SEAL))) {
-        /* we only support v2 for now as we can't sign w/o session security
-         * anyway */
-        return ENOTSUP;
-    }
+    if (flags & NTLMSSP_NEGOTIATE_SEAL) {
+        ret = RC4_UPDATE(handle, message, output);
+        if (ret) return ret;
 
-    ret = RC4_UPDATE(handle, message, output);
-    if (ret) return ret;
+        if (flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) {
+            return ntlmv2_sign(sign_key, seq_num, handle,
+                               (flags & NTLMSSP_NEGOTIATE_KEY_EXCH),
+                               message, signature);
+        } else {
+            return ntlmv1_sign(handle, 0, seq_num, message, signature);
+        }
+    }
 
-    return ntlmv2_sign(sign_key, seq_num, handle,
-                       (flags & NTLMSSP_NEGOTIATE_KEY_EXCH),
-                       message, signature);
+    return ENOTSUP;
 }
 
 int ntlm_unseal(struct ntlm_rc4_handle *handle, uint32_t flags,
index e8a7384c87551afb6f552c47d8e1c433a7261902..e6b7748878c3823c8b9e6bc652ba60fd1fc134f1 100644 (file)
@@ -367,6 +367,45 @@ struct t_gsswrapex_data {
     struct ntlm_buffer Signature;
 };
 
+/* Basic GSS_WrapEx V1 Test Data */
+struct t_gsswrapex_data T_GSSWRAPv1noESS = {
+    (
+      NTLMSSP_NEGOTIATE_56 |
+      NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL
+    ),
+    0,
+    {
+      .data = (uint8_t *)"\x50\x00\x6c\x00\x61\x00\x69\x00"
+                 "\x6e\x00\x74\x00\x65\x00\x78\x00\x74\x00",
+      .length = 18
+    },
+    {
+      .data = {
+        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55
+      },
+      .length = 16
+    },
+    {
+      .data = { 0 },
+      .length = 0
+    },
+    {
+      .data = { 0 },
+      .length = 0
+    },
+    {
+      .data = (uint8_t *)"\x56\xfe\x04\xd8\x61\xf9\x31\x9a"
+                 "\xf0\xd7\x23\x8a\x2e\x3b\x4d\x45\x7f\xb8",
+      .length = 18
+    },
+    {
+      .data = (uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00"
+                         "\x09\xdc\xd1\xdf\x2e\x45\x9d\x36",
+      .length = 16
+    },
+};
+
 /* GSS_WrapEx V1 Extended Session Security Test Data */
 struct t_gsswrapex_data T_GSSWRAPEXv1 = {
     (
@@ -996,22 +1035,30 @@ int test_GSS_Wrap_EX(struct ntlm_ctx *ctx, struct t_gsswrapex_data *data)
                              &seal_send_handle, &seal_recv_handle);
     if (ret) return ret;
 
-    if (memcmp(seal_send_key.data, data->ClientSealKey.data, 16) != 0) {
-        fprintf(stderr, "Client Sealing Keys differ!\n");
-        fprintf(stderr, "expected:\n%s",
-                        hex_to_dump(data->ClientSealKey.data, 16));
-        fprintf(stderr, "obtained:\n%s",
-                        hex_to_dump(seal_send_key.data, sign_send_key.length));
-        ret = EINVAL;
+    if (data->ClientSealKey.length) {
+        if (memcmp(seal_send_key.data, data->ClientSealKey.data,
+                                       data->ClientSealKey.length) != 0) {
+            fprintf(stderr, "Client Sealing Keys differ!\n");
+            fprintf(stderr, "expected:\n%s",
+                    hex_to_dump(data->ClientSealKey.data,
+                                data->ClientSealKey.length));
+            fprintf(stderr, "obtained:\n%s",
+                    hex_to_dump(seal_send_key.data, sign_send_key.length));
+            ret = EINVAL;
+        }
     }
 
-    if (memcmp(sign_send_key.data, data->ClientSignKey.data, 16) != 0) {
-        fprintf(stderr, "Client Signing Keys differ!\n");
-        fprintf(stderr, "expected:\n%s",
-                        hex_to_dump(data->ClientSignKey.data, 16));
-        fprintf(stderr, "obtained:\n%s",
-                        hex_to_dump(sign_send_key.data, sign_send_key.length));
-        ret = EINVAL;
+    if (data->ClientSignKey.length) {
+        if (memcmp(sign_send_key.data, data->ClientSignKey.data,
+                                       data->ClientSignKey.length) != 0) {
+            fprintf(stderr, "Client Signing Keys differ!\n");
+            fprintf(stderr, "expected:\n%s",
+                    hex_to_dump(data->ClientSignKey.data,
+                                data->ClientSignKey.length));
+            fprintf(stderr, "obtained:\n%s",
+                    hex_to_dump(sign_send_key.data, sign_send_key.length));
+            ret = EINVAL;
+        }
     }
 
     if (ret) return ret;
@@ -1604,6 +1651,10 @@ int main(int argc, const char *argv[])
     ret = test_EncodeAuthenticateMessageV2(ctx);
     fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS"));
 
+    fprintf(stdout, "Test sealing a Message with No Extended Security\n");
+    ret = test_GSS_Wrap_EX(ctx, &T_GSSWRAPv1noESS);
+    fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS"));
+
     fprintf(stdout, "Test sealing a Message with NTLMv1 Extended Security\n");
     ret = test_GSS_Wrap_EX(ctx, &T_GSSWRAPEXv1);
     fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS"));