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

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