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

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