Blame SOURCES/Change-the-way-we-handle-encrypted-buffers.patch

1f3cc3
From 51bba6bf325716534c509e0528d2ccfd0050d28c Mon Sep 17 00:00:00 2001
1f3cc3
From: Simo Sorce <simo@redhat.com>
1f3cc3
Date: Wed, 17 Apr 2019 18:00:59 -0400
1f3cc3
Subject: [PATCH] Change the way we handle encrypted buffers
1f3cc3
1f3cc3
The previous change has backwards incompatible behavior that may also
1f3cc3
lead to buffer overruns.
1f3cc3
1f3cc3
Because we have no easy way to indicate a format change and to maintain
1f3cc3
backwards compatibility for the ciphers that were working (those that
1f3cc3
added padding were hopelessly borken anyway) introduce code to simply
1f3cc3
add padding that we can recognize and remove when we read back the token.
1f3cc3
1f3cc3
On ciphers that do not add padding this is basically a no op and the
1f3cc3
tokens will be identical to the ones we previously emitted.
1f3cc3
1f3cc3
On ciphers that add padding we pad the plaintext so that we hit a block
1f3cc3
boundary and cause no extra padding to be added by krb5_c_encrypt
1f3cc3
itself. On decryption we check if padding bytes are appended to the
1f3cc3
buffer and remove them.
1f3cc3
1f3cc3
Signed-off-by: Simo Sorce <simo@redhat.com>
1f3cc3
Reviewed-by: Robbie Harwood <rharwood@redhat.com>
1f3cc3
Merges: #246
1f3cc3
(cherry picked from commit 839be8aa7e54e93819e8291b570e4c7cfe7e98f1)
1f3cc3
---
1f3cc3
 src/gp_export.c | 110 +++++++++++++++++++++++++++++++++++++-----------
1f3cc3
 1 file changed, 86 insertions(+), 24 deletions(-)
1f3cc3
1f3cc3
diff --git a/src/gp_export.c b/src/gp_export.c
1f3cc3
index aa0a8ec..dbfddeb 100644
1f3cc3
--- a/src/gp_export.c
1f3cc3
+++ b/src/gp_export.c
1f3cc3
@@ -193,9 +193,15 @@ done:
1f3cc3
     return ret_maj;
1f3cc3
 }
1f3cc3
 
1f3cc3
-/* We need to include a length in our payloads because krb5_c_decrypt() will
1f3cc3
- * pad the contents for some enctypes, and gss_import_cred() doesn't like
1f3cc3
- * having extra bytes on tokens. */
1f3cc3
+#define ENC_MIN_PAD_LEN 8
1f3cc3
+
1f3cc3
+/* We need to pad our payloads because krb5_c_decrypt() may pad the
1f3cc3
+ * contents for some enctypes, and gss_import_cred() doesn't like
1f3cc3
+ * having extra bytes on tokens.
1f3cc3
+ * Explicit padding and depadding is used in order to maintain backwards
1f3cc3
+ * compatibility over upgrades (and downgrades), it would have been
1f3cc3
+ * better if we simply had a better formatting of the returned blob
1f3cc3
+ * so we could simply change a "blob version" number */
1f3cc3
 static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
1f3cc3
                              size_t len, void *buf, octet_string *out)
1f3cc3
 {
1f3cc3
@@ -203,8 +209,9 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
1f3cc3
     krb5_data data_in;
1f3cc3
     krb5_enc_data enc_handle;
1f3cc3
     size_t cipherlen;
1f3cc3
-    char *packed = NULL;
1f3cc3
-    uint32_t netlen;
1f3cc3
+    size_t padcheck;
1f3cc3
+    uint8_t pad = 0;
1f3cc3
+    char *padded = NULL;
1f3cc3
 
1f3cc3
     if (len > (uint32_t)(-1)) {
1f3cc3
         /* Needs to fit in 4 bytes of payload, so... */
1f3cc3
@@ -212,28 +219,72 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
1f3cc3
         goto done;
1f3cc3
     }
1f3cc3
 
1f3cc3
-    packed = malloc(len);
1f3cc3
-    if (!packed) {
1f3cc3
-        ret = errno;
1f3cc3
+    ret = krb5_c_encrypt_length(context,
1f3cc3
+                                key->enctype,
1f3cc3
+                                len, &cipherlen);
1f3cc3
+    if (ret) {
1f3cc3
         goto done;
1f3cc3
     }
1f3cc3
 
1f3cc3
-    netlen = htonl(len);
1f3cc3
-    memcpy(packed, (uint8_t *)&netlen, 4);
1f3cc3
-    memcpy(packed + 4, buf, len);
1f3cc3
-
1f3cc3
-    data_in.length = len + 4;
1f3cc3
-    data_in.data = packed;
1f3cc3
-
1f3cc3
-    memset(&enc_handle, '\0', sizeof(krb5_enc_data));
1f3cc3
-
1f3cc3
+    /* try again with len + 1 to see if padding is required */
1f3cc3
     ret = krb5_c_encrypt_length(context,
1f3cc3
                                 key->enctype,
1f3cc3
-                                data_in.length,
1f3cc3
-                                &cipherlen);
1f3cc3
+                                len + 1, &padcheck);
1f3cc3
     if (ret) {
1f3cc3
         goto done;
1f3cc3
     }
1f3cc3
+    if (padcheck == cipherlen) {
1f3cc3
+        int i;
1f3cc3
+        /* padding required */
1f3cc3
+        pad = ENC_MIN_PAD_LEN;
1f3cc3
+        /* always add enough padding that it makes it extremely unlikley
1f3cc3
+         * legitimate plaintext will be incorrectly depadded in the
1f3cc3
+         * decrypt function */
1f3cc3
+        ret = krb5_c_encrypt_length(context,
1f3cc3
+                                    key->enctype,
1f3cc3
+                                    len + pad, &cipherlen);
1f3cc3
+        if (ret) {
1f3cc3
+            goto done;
1f3cc3
+        }
1f3cc3
+        /* we support only block sizes up to 16 bytes as this is the largest
1f3cc3
+         * supported block size in krb ciphers for now */
1f3cc3
+        for (i = 0; i < 15; i++) {
1f3cc3
+            /* find the point at which padcheck increases, that's when we
1f3cc3
+             * cross a blocksize boundary internally and we can calculate
1f3cc3
+             * the padding that will be used */
1f3cc3
+            ret = krb5_c_encrypt_length(context,
1f3cc3
+                                        key->enctype,
1f3cc3
+                                        len + pad + i + 1, &padcheck);
1f3cc3
+            if (ret) {
1f3cc3
+                goto done;
1f3cc3
+            }
1f3cc3
+            if (padcheck > cipherlen) {
1f3cc3
+                pad += i;
1f3cc3
+                break;
1f3cc3
+            }
1f3cc3
+        }
1f3cc3
+        if (i > 15) {
1f3cc3
+            ret = EINVAL;
1f3cc3
+            goto done;
1f3cc3
+        }
1f3cc3
+    }
1f3cc3
+
1f3cc3
+    if (pad != 0) {
1f3cc3
+        padded = malloc(len + pad);
1f3cc3
+        if (!padded) {
1f3cc3
+            ret = errno;
1f3cc3
+            goto done;
1f3cc3
+        }
1f3cc3
+
1f3cc3
+        memcpy(padded, buf, len);
1f3cc3
+        memset(padded + len, pad, pad);
1f3cc3
+
1f3cc3
+        data_in.length = len + pad;
1f3cc3
+        data_in.data = padded;
1f3cc3
+    } else {
1f3cc3
+        data_in.length = len;
1f3cc3
+        data_in.data = buf;
1f3cc3
+    }
1f3cc3
 
1f3cc3
     enc_handle.ciphertext.length = cipherlen;
1f3cc3
     enc_handle.ciphertext.data = malloc(enc_handle.ciphertext.length);
1f3cc3
@@ -261,7 +312,7 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
1f3cc3
     }
1f3cc3
 
1f3cc3
 done:
1f3cc3
-    free(packed);
1f3cc3
+    free(padded);
1f3cc3
     free(enc_handle.ciphertext.data);
1f3cc3
     return ret;
1f3cc3
 }
1f3cc3
@@ -273,7 +324,8 @@ static int gp_decrypt_buffer(krb5_context context, krb5_keyblock *key,
1f3cc3
     int ret;
1f3cc3
     krb5_data data_out;
1f3cc3
     krb5_enc_data enc_handle;
1f3cc3
-    uint32_t netlen;
1f3cc3
+    uint8_t pad;
1f3cc3
+    int i, j;
1f3cc3
 
1f3cc3
     memset(&enc_handle, '\0', sizeof(krb5_enc_data));
1f3cc3
 
1f3cc3
@@ -295,9 +347,19 @@ static int gp_decrypt_buffer(krb5_context context, krb5_keyblock *key,
1f3cc3
     }
1f3cc3
 
1f3cc3
     /* And handle the padding. */
1f3cc3
-    memcpy(&netlen, buf, 4);
1f3cc3
-    *len = ntohl(netlen);
1f3cc3
-    memmove(buf, buf + 4, *len);
1f3cc3
+    i = data_out.length - 1;
1f3cc3
+    pad = data_out.data[i];
1f3cc3
+    if (pad >= ENC_MIN_PAD_LEN && pad < i) {
1f3cc3
+        j = pad;
1f3cc3
+        while (j > 0) {
1f3cc3
+            j--;
1f3cc3
+            if (pad != data_out.data[i - j]) break;
1f3cc3
+        }
1f3cc3
+        if (j == 0) {
1f3cc3
+            data_out.length -= pad;
1f3cc3
+        }
1f3cc3
+    }
1f3cc3
+    *len = data_out.length;
1f3cc3
 
1f3cc3
     return 0;
1f3cc3
 }