diff --git a/SOURCES/Add-a-safety-timeout-to-epoll.patch b/SOURCES/Add-a-safety-timeout-to-epoll.patch
new file mode 100644
index 0000000..af46776
--- /dev/null
+++ b/SOURCES/Add-a-safety-timeout-to-epoll.patch
@@ -0,0 +1,48 @@
+From dcc5ec2c848f57c600ef127f1ed3640154a67cbe Mon Sep 17 00:00:00 2001
+From: Simo Sorce <simo@redhat.com>
+Date: Wed, 6 Mar 2019 10:36:11 -0500
+Subject: [PATCH] Add a safety timeout to epoll
+
+Add a safety timeout just in case something goes wrong with the use of
+timerfd. This way the process should't be stuck forever.
+
+Signed-off-by: Simo Sorce <simo@redhat.com>
+[rharwood@redhat.com: remove outdated comment]
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Merges: #241
+(cherry picked from commit d55be9fa2455fe52b6eb904ad427f22141ab3f26)
+(cherry picked from commit a494f23b6d8d43fe1a824cd69c3dd93a18fc75a1)
+---
+ proxy/src/client/gpm_common.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/proxy/src/client/gpm_common.c b/proxy/src/client/gpm_common.c
+index 554c91c..462d88b 100644
+--- a/proxy/src/client/gpm_common.c
++++ b/proxy/src/client/gpm_common.c
+@@ -14,6 +14,7 @@
+ #define FRAGMENT_BIT (1 << 31)
+ 
+ #define RESPONSE_TIMEOUT 15
++#define SAFETY_TIMEOUT RESPONSE_TIMEOUT * 10 * 1000
+ #define MAX_TIMEOUT_RETRY 3
+ 
+ struct gpm_ctx {
+@@ -287,7 +288,7 @@ static int gpm_epoll_wait(struct gpm_ctx *gpmctx, uint32_t event_flags)
+     }
+ 
+     do {
+-        epoll_ret = epoll_wait(gpmctx->epollfd, events, 2, -1);
++        epoll_ret = epoll_wait(gpmctx->epollfd, events, 2, SAFETY_TIMEOUT);
+     } while (epoll_ret < 0 && errno == EINTR);
+ 
+     if (epoll_ret < 0) {
+@@ -295,8 +296,6 @@ static int gpm_epoll_wait(struct gpm_ctx *gpmctx, uint32_t event_flags)
+         ret = errno;
+         gpm_epoll_close(gpmctx);
+     } else if (epoll_ret == 0) {
+-        /* Shouldn't happen as timeout == -1; treat it like a timeout
+-         * occurred. */
+         ret = ETIMEDOUT;
+         gpm_epoll_close(gpmctx);
+     } else if (epoll_ret == 1 && events[0].data.fd == gpmctx->timerfd) {
diff --git a/SOURCES/Always-initialize-out-cred-in-gp_import_gssx_cred.patch b/SOURCES/Always-initialize-out-cred-in-gp_import_gssx_cred.patch
new file mode 100644
index 0000000..bbec739
--- /dev/null
+++ b/SOURCES/Always-initialize-out-cred-in-gp_import_gssx_cred.patch
@@ -0,0 +1,34 @@
+From ad753abb87a561ae72db09f210390343d0084431 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Mon, 15 Apr 2019 19:54:17 -0400
+Subject: [PATCH] Always initialize out cred in gp_import_gssx_cred()
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Simo Sorce <simo@redhat.com>
+Merges: #244
+(cherry picked from commit 5697dfd94345c945f93070c40b9d4480f3d3d7ea)
+---
+ proxy/src/gp_export.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/proxy/src/gp_export.c b/proxy/src/gp_export.c
+index 5e8e160..403e339 100644
+--- a/proxy/src/gp_export.c
++++ b/proxy/src/gp_export.c
+@@ -449,6 +449,8 @@ uint32_t gp_import_gssx_cred(uint32_t *min, struct gp_call_ctx *gpcall,
+     uint32_t ret_min = 0;
+     int ret;
+ 
++    *out = GSS_C_NO_CREDENTIAL;
++
+     handle = gp_service_get_creds_handle(gpcall->service);
+     if (!handle) {
+         ret_maj = GSS_S_FAILURE;
+@@ -470,7 +472,6 @@ uint32_t gp_import_gssx_cred(uint32_t *min, struct gp_call_ctx *gpcall,
+     if (ret) {
+         /* Allow for re-issuance of the keytab. */
+         GPDEBUG("Stored ccache failed to decrypt; treating as empty\n");
+-        *out = GSS_C_NO_CREDENTIAL;
+         goto done;
+     }
+ 
diff --git a/SOURCES/Avoid-uninitialized-free-when-allocating-buffers.patch b/SOURCES/Avoid-uninitialized-free-when-allocating-buffers.patch
new file mode 100644
index 0000000..27ddb7d
--- /dev/null
+++ b/SOURCES/Avoid-uninitialized-free-when-allocating-buffers.patch
@@ -0,0 +1,43 @@
+From e660bc0594b6ddca143b460bb7edbbfba69f84ed Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Wed, 1 May 2019 11:27:13 -0400
+Subject: [PATCH] Avoid uninitialized free when allocating buffers
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Simo Sorce <simo@redhat.com>
+Resolves: #248
+(cherry picked from commit eafa3c9272c95646400123f8e4d6fb50cf36d36c)
+(cherry picked from commit e19466d172e0fd6d86b98b1423e9d99e0be30313)
+[rharwood@redhat.com: fuzz around padded]
+(cherry picked from commit abcc6cc90c6da5ea4fce65dd60e46b76767be557)
+[rharwood@redhat.com: more padded fuzz]
+---
+ proxy/src/gp_export.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/proxy/src/gp_export.c b/proxy/src/gp_export.c
+index dbfddeb..a5681c0 100644
+--- a/proxy/src/gp_export.c
++++ b/proxy/src/gp_export.c
+@@ -300,6 +300,7 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
+                          &data_in,
+                          &enc_handle);
+     if (ret) {
++        free(enc_handle.ciphertext.data);
+         ret = EINVAL;
+         goto done;
+     }
+@@ -308,12 +309,12 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
+                                enc_handle.ciphertext.data,
+                                out);
+     if (ret) {
++        free(enc_handle.ciphertext.data);
+         goto done;
+     }
+ 
+ done:
+     free(padded);
+-    free(enc_handle.ciphertext.data);
+     return ret;
+ }
+ 
diff --git a/SOURCES/Change-the-way-we-handle-encrypted-buffers.patch b/SOURCES/Change-the-way-we-handle-encrypted-buffers.patch
new file mode 100644
index 0000000..aad7c21
--- /dev/null
+++ b/SOURCES/Change-the-way-we-handle-encrypted-buffers.patch
@@ -0,0 +1,193 @@
+From 1311e03610fc4bdc0dbcba8803802e3cff5c232b Mon Sep 17 00:00:00 2001
+From: Simo Sorce <simo@redhat.com>
+Date: Wed, 17 Apr 2019 18:00:59 -0400
+Subject: [PATCH] Change the way we handle encrypted buffers
+
+The previous change has backwards incompatible behavior that may also
+lead to buffer overruns.
+
+Because we have no easy way to indicate a format change and to maintain
+backwards compatibility for the ciphers that were working (those that
+added padding were hopelessly borken anyway) introduce code to simply
+add padding that we can recognize and remove when we read back the token.
+
+On ciphers that do not add padding this is basically a no op and the
+tokens will be identical to the ones we previously emitted.
+
+On ciphers that add padding we pad the plaintext so that we hit a block
+boundary and cause no extra padding to be added by krb5_c_encrypt
+itself. On decryption we check if padding bytes are appended to the
+buffer and remove them.
+
+Signed-off-by: Simo Sorce <simo@redhat.com>
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Merges: #246
+(cherry picked from commit 839be8aa7e54e93819e8291b570e4c7cfe7e98f1)
+---
+ proxy/src/gp_export.c | 110 +++++++++++++++++++++++++++++++++---------
+ 1 file changed, 86 insertions(+), 24 deletions(-)
+
+diff --git a/proxy/src/gp_export.c b/proxy/src/gp_export.c
+index aa0a8ec..dbfddeb 100644
+--- a/proxy/src/gp_export.c
++++ b/proxy/src/gp_export.c
+@@ -193,9 +193,15 @@ done:
+     return ret_maj;
+ }
+ 
+-/* We need to include a length in our payloads because krb5_c_decrypt() will
+- * pad the contents for some enctypes, and gss_import_cred() doesn't like
+- * having extra bytes on tokens. */
++#define ENC_MIN_PAD_LEN 8
++
++/* We need to pad our payloads because krb5_c_decrypt() may pad the
++ * contents for some enctypes, and gss_import_cred() doesn't like
++ * having extra bytes on tokens.
++ * Explicit padding and depadding is used in order to maintain backwards
++ * compatibility over upgrades (and downgrades), it would have been
++ * better if we simply had a better formatting of the returned blob
++ * so we could simply change a "blob version" number */
+ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
+                              size_t len, void *buf, octet_string *out)
+ {
+@@ -203,8 +209,9 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
+     krb5_data data_in;
+     krb5_enc_data enc_handle;
+     size_t cipherlen;
+-    char *packed = NULL;
+-    uint32_t netlen;
++    size_t padcheck;
++    uint8_t pad = 0;
++    char *padded = NULL;
+ 
+     if (len > (uint32_t)(-1)) {
+         /* Needs to fit in 4 bytes of payload, so... */
+@@ -212,28 +219,72 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
+         goto done;
+     }
+ 
+-    packed = malloc(len);
+-    if (!packed) {
+-        ret = errno;
++    ret = krb5_c_encrypt_length(context,
++                                key->enctype,
++                                len, &cipherlen);
++    if (ret) {
+         goto done;
+     }
+ 
+-    netlen = htonl(len);
+-    memcpy(packed, (uint8_t *)&netlen, 4);
+-    memcpy(packed + 4, buf, len);
+-
+-    data_in.length = len + 4;
+-    data_in.data = packed;
+-
+-    memset(&enc_handle, '\0', sizeof(krb5_enc_data));
+-
++    /* try again with len + 1 to see if padding is required */
+     ret = krb5_c_encrypt_length(context,
+                                 key->enctype,
+-                                data_in.length,
+-                                &cipherlen);
++                                len + 1, &padcheck);
+     if (ret) {
+         goto done;
+     }
++    if (padcheck == cipherlen) {
++        int i;
++        /* padding required */
++        pad = ENC_MIN_PAD_LEN;
++        /* always add enough padding that it makes it extremely unlikley
++         * legitimate plaintext will be incorrectly depadded in the
++         * decrypt function */
++        ret = krb5_c_encrypt_length(context,
++                                    key->enctype,
++                                    len + pad, &cipherlen);
++        if (ret) {
++            goto done;
++        }
++        /* we support only block sizes up to 16 bytes as this is the largest
++         * supported block size in krb ciphers for now */
++        for (i = 0; i < 15; i++) {
++            /* find the point at which padcheck increases, that's when we
++             * cross a blocksize boundary internally and we can calculate
++             * the padding that will be used */
++            ret = krb5_c_encrypt_length(context,
++                                        key->enctype,
++                                        len + pad + i + 1, &padcheck);
++            if (ret) {
++                goto done;
++            }
++            if (padcheck > cipherlen) {
++                pad += i;
++                break;
++            }
++        }
++        if (i > 15) {
++            ret = EINVAL;
++            goto done;
++        }
++    }
++
++    if (pad != 0) {
++        padded = malloc(len + pad);
++        if (!padded) {
++            ret = errno;
++            goto done;
++        }
++
++        memcpy(padded, buf, len);
++        memset(padded + len, pad, pad);
++
++        data_in.length = len + pad;
++        data_in.data = padded;
++    } else {
++        data_in.length = len;
++        data_in.data = buf;
++    }
+ 
+     enc_handle.ciphertext.length = cipherlen;
+     enc_handle.ciphertext.data = malloc(enc_handle.ciphertext.length);
+@@ -261,7 +312,7 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
+     }
+ 
+ done:
+-    free(packed);
++    free(padded);
+     free(enc_handle.ciphertext.data);
+     return ret;
+ }
+@@ -273,7 +324,8 @@ static int gp_decrypt_buffer(krb5_context context, krb5_keyblock *key,
+     int ret;
+     krb5_data data_out;
+     krb5_enc_data enc_handle;
+-    uint32_t netlen;
++    uint8_t pad;
++    int i, j;
+ 
+     memset(&enc_handle, '\0', sizeof(krb5_enc_data));
+ 
+@@ -295,9 +347,19 @@ static int gp_decrypt_buffer(krb5_context context, krb5_keyblock *key,
+     }
+ 
+     /* And handle the padding. */
+-    memcpy(&netlen, buf, 4);
+-    *len = ntohl(netlen);
+-    memmove(buf, buf + 4, *len);
++    i = data_out.length - 1;
++    pad = data_out.data[i];
++    if (pad >= ENC_MIN_PAD_LEN && pad < i) {
++        j = pad;
++        while (j > 0) {
++            j--;
++            if (pad != data_out.data[i - j]) break;
++        }
++        if (j == 0) {
++            data_out.length -= pad;
++        }
++    }
++    *len = data_out.length;
+ 
+     return 0;
+ }
diff --git a/SOURCES/Close-epoll-fd-within-the-lock.patch b/SOURCES/Close-epoll-fd-within-the-lock.patch
new file mode 100644
index 0000000..d92e914
--- /dev/null
+++ b/SOURCES/Close-epoll-fd-within-the-lock.patch
@@ -0,0 +1,165 @@
+From 69e1baa7d7f271d294e619148b4a2bae10230b5e Mon Sep 17 00:00:00 2001
+From: Simo Sorce <simo@redhat.com>
+Date: Wed, 6 Mar 2019 10:31:13 -0500
+Subject: [PATCH] Close epoll fd within the lock
+
+A race condition may happen where we close the epoll socket, after
+another thread grabbed the lock and is using epoll itself.
+On some kernels this may cause epoll to not fire any event leaving the
+thread stuck forever.
+
+Signed-off-by: Simo Sorce <simo@redhat.com>
+[rharwood@redhat.com: cleanup commit message, adjusted function ordering]
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Merges: #241
+(cherry picked from commit 0ccfd32f8ef16caf65698c5319dfa251d43433af)
+
+Squashed with:
+
+Reorder functions
+
+Keep related functions closer together like before
+
+Signed-off-by: Simo Sorce <simo@redhat.com>
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Resolves: #242
+(cherry picked from commit 6accc0afead574e11447447c949f2abcb1a34826)
+(cherry picked from commit c33de0c213d570f370fd954869c2ad99901b2cf3)
+[rharwood@redhat.com: I learned an important lesson upstream]
+---
+ proxy/src/client/gpm_common.c | 100 ++++++++++++++++++----------------
+ 1 file changed, 54 insertions(+), 46 deletions(-)
+
+diff --git a/proxy/src/client/gpm_common.c b/proxy/src/client/gpm_common.c
+index b482efb..554c91c 100644
+--- a/proxy/src/client/gpm_common.c
++++ b/proxy/src/client/gpm_common.c
+@@ -139,44 +139,8 @@ static void gpm_close_socket(struct gpm_ctx *gpmctx)
+     gpmctx->fd = -1;
+ }
+ 
+-static int gpm_grab_sock(struct gpm_ctx *gpmctx)
++static void gpm_timer_close(struct gpm_ctx *gpmctx)
+ {
+-    int ret;
+-    pid_t p;
+-    uid_t u;
+-    gid_t g;
+-
+-    ret = pthread_mutex_lock(&gpmctx->lock);
+-    if (ret) {
+-        return ret;
+-    }
+-
+-    /* Detect fork / setresuid and friends */
+-    p = getpid();
+-    u = geteuid();
+-    g = getegid();
+-
+-    if (gpmctx->fd != -1 &&
+-        (p != gpmctx->pid || u != gpmctx->uid || g != gpmctx->gid)) {
+-        gpm_close_socket(gpmctx);
+-    }
+-
+-    if (gpmctx->fd == -1) {
+-        ret = gpm_open_socket(gpmctx);
+-    }
+-
+-    if (ret) {
+-        pthread_mutex_unlock(&gpmctx->lock);
+-    }
+-    return ret;
+-}
+-
+-static int gpm_release_sock(struct gpm_ctx *gpmctx)
+-{
+-    return pthread_mutex_unlock(&gpmctx->lock);
+-}
+-
+-static void gpm_timer_close(struct gpm_ctx *gpmctx) {
+     if (gpmctx->timerfd < 0) {
+         return;
+     }
+@@ -248,7 +212,59 @@ static int gpm_epoll_setup(struct gpm_ctx *gpmctx) {
+     return ret;
+ }
+ 
+-static int gpm_epoll_wait(struct gpm_ctx *gpmctx, uint32_t event_flags) {
++static int gpm_release_sock(struct gpm_ctx *gpmctx)
++{
++    gpm_epoll_close(gpmctx);
++    gpm_timer_close(gpmctx);
++    return pthread_mutex_unlock(&gpmctx->lock);
++}
++
++static int gpm_grab_sock(struct gpm_ctx *gpmctx)
++{
++    int ret;
++    pid_t p;
++    uid_t u;
++    gid_t g;
++
++    ret = pthread_mutex_lock(&gpmctx->lock);
++    if (ret) {
++        return ret;
++    }
++
++    /* Detect fork / setresuid and friends */
++    p = getpid();
++    u = geteuid();
++    g = getegid();
++
++    if (gpmctx->fd != -1 &&
++        (p != gpmctx->pid || u != gpmctx->uid || g != gpmctx->gid)) {
++        gpm_close_socket(gpmctx);
++    }
++
++    if (gpmctx->fd == -1) {
++        ret = gpm_open_socket(gpmctx);
++        if (ret) {
++            goto done;
++        }
++    }
++
++    /* setup timer */
++    ret = gpm_timer_setup(gpmctx, RESPONSE_TIMEOUT);
++    if (ret) {
++        goto done;
++    }
++    /* create epoll fd as well */
++    ret = gpm_epoll_setup(gpmctx);
++
++done:
++    if (ret) {
++        gpm_release_sock(gpmctx);
++    }
++    return ret;
++}
++
++static int gpm_epoll_wait(struct gpm_ctx *gpmctx, uint32_t event_flags)
++{
+     int ret;
+     int epoll_ret;
+     struct epoll_event ev;
+@@ -526,11 +542,6 @@ static int gpm_send_recv_loop(struct gpm_ctx *gpmctx, char *send_buffer,
+     int ret;
+     int retry_count;
+ 
+-    /* setup timer */
+-    ret = gpm_timer_setup(gpmctx, RESPONSE_TIMEOUT);
+-    if (ret)
+-        return ret;
+-
+     for (retry_count = 0; retry_count < MAX_TIMEOUT_RETRY; retry_count++) {
+         /* send to proxy */
+         ret = gpm_send_buffer(gpmctx, send_buffer, send_length);
+@@ -757,9 +768,6 @@ int gpm_make_call(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res)
+     }
+ 
+ done:
+-    gpm_timer_close(gpmctx);
+-    gpm_epoll_close(gpmctx);
+-
+     if (sockgrab) {
+         gpm_release_sock(gpmctx);
+     }
diff --git a/SOURCES/Handle-gss_import_cred-failure-when-importing-gssx-c.patch b/SOURCES/Handle-gss_import_cred-failure-when-importing-gssx-c.patch
new file mode 100644
index 0000000..eabce35
--- /dev/null
+++ b/SOURCES/Handle-gss_import_cred-failure-when-importing-gssx-c.patch
@@ -0,0 +1,31 @@
+From 5c6f2fc4f63b2812b0305a3f0de9904c5ebd38ef Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Mon, 15 Apr 2019 19:56:50 -0400
+Subject: [PATCH] Handle gss_import_cred() failure when importing gssx creds
+
+Otherwise, we might attempt to set options on a non-existent handle,
+leading to a segfault.
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Simo Sorce <simo@redhat.com>
+Merges: #244
+(cherry picked from commit 84cf88f6c6cfeb8e3fd2c26ed0fe9fe5bf3810d2)
+---
+ proxy/src/gp_export.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/proxy/src/gp_export.c b/proxy/src/gp_export.c
+index 403e339..7ad8037 100644
+--- a/proxy/src/gp_export.c
++++ b/proxy/src/gp_export.c
+@@ -476,6 +476,10 @@ uint32_t gp_import_gssx_cred(uint32_t *min, struct gp_call_ctx *gpcall,
+     }
+ 
+     ret_maj = gss_import_cred(&ret_min, &token, out);
++    if (ret_maj) {
++        GPDEBUG("gss_import_cred failed when importing gssx cred\n");
++        goto done;
++    }
+ 
+     /* check if there is any client option we need to set on credentials */
+     gp_set_cred_options(cred, *out);
diff --git a/SOURCES/Include-length-when-using-krb5_c_decrypt.patch b/SOURCES/Include-length-when-using-krb5_c_decrypt.patch
new file mode 100644
index 0000000..9780736
--- /dev/null
+++ b/SOURCES/Include-length-when-using-krb5_c_decrypt.patch
@@ -0,0 +1,98 @@
+From d535ea0d4b3cfa8e1cc26a060ec2ed818f2b69ac Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Tue, 16 Apr 2019 16:08:32 -0400
+Subject: [PATCH] Include length when using krb5_c_decrypt()
+
+For some enctypes, krb5_c_decrypt() will add padding bytes which are
+included in the returned length.  However, functions which use the
+objects we're storing aren't always prepared for that: in particular,
+gss_import_cred() will declare a token invalid if there's trailing
+garbage.
+
+Work around this by including 4 bytes of length on encrypted objects.
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Simo Sorce <simo@redhat.com>
+Merges: #244
+(cherry picked from commit 87957caf541114f6f15a495dd7d30556dc5801d9)
+---
+ proxy/src/gp_export.c | 35 +++++++++++++++++++++++++++++++----
+ 1 file changed, 31 insertions(+), 4 deletions(-)
+
+diff --git a/proxy/src/gp_export.c b/proxy/src/gp_export.c
+index 7ad8037..aa0a8ec 100644
+--- a/proxy/src/gp_export.c
++++ b/proxy/src/gp_export.c
+@@ -193,6 +193,9 @@ done:
+     return ret_maj;
+ }
+ 
++/* We need to include a length in our payloads because krb5_c_decrypt() will
++ * pad the contents for some enctypes, and gss_import_cred() doesn't like
++ * having extra bytes on tokens. */
+ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
+                              size_t len, void *buf, octet_string *out)
+ {
+@@ -200,9 +203,27 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
+     krb5_data data_in;
+     krb5_enc_data enc_handle;
+     size_t cipherlen;
++    char *packed = NULL;
++    uint32_t netlen;
+ 
+-    data_in.length = len;
+-    data_in.data = buf;
++    if (len > (uint32_t)(-1)) {
++        /* Needs to fit in 4 bytes of payload, so... */
++        ret = ENOMEM;
++        goto done;
++    }
++
++    packed = malloc(len);
++    if (!packed) {
++        ret = errno;
++        goto done;
++    }
++
++    netlen = htonl(len);
++    memcpy(packed, (uint8_t *)&netlen, 4);
++    memcpy(packed + 4, buf, len);
++
++    data_in.length = len + 4;
++    data_in.data = packed;
+ 
+     memset(&enc_handle, '\0', sizeof(krb5_enc_data));
+ 
+@@ -240,16 +261,19 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
+     }
+ 
+ done:
++    free(packed);
+     free(enc_handle.ciphertext.data);
+     return ret;
+ }
+ 
++/* See comment above on gp_encrypt_buffer(). */
+ static int gp_decrypt_buffer(krb5_context context, krb5_keyblock *key,
+-                             octet_string *in, size_t *len, void *buf)
++                             octet_string *in, size_t *len, char *buf)
+ {
+     int ret;
+     krb5_data data_out;
+     krb5_enc_data enc_handle;
++    uint32_t netlen;
+ 
+     memset(&enc_handle, '\0', sizeof(krb5_enc_data));
+ 
+@@ -270,7 +294,10 @@ static int gp_decrypt_buffer(krb5_context context, krb5_keyblock *key,
+         return ret;
+     }
+ 
+-    *len = data_out.length;
++    /* And handle the padding. */
++    memcpy(&netlen, buf, 4);
++    *len = ntohl(netlen);
++    memmove(buf, buf + 4, *len);
+ 
+     return 0;
+ }
diff --git a/SOURCES/Use-pthread-keys-for-thread-local-storage.patch b/SOURCES/Use-pthread-keys-for-thread-local-storage.patch
new file mode 100644
index 0000000..ba59036
--- /dev/null
+++ b/SOURCES/Use-pthread-keys-for-thread-local-storage.patch
@@ -0,0 +1,150 @@
+From 17dbcfaff5e7d917eb48579dc8e5f60c95a30981 Mon Sep 17 00:00:00 2001
+From: Simo Sorce <simo@redhat.com>
+Date: Thu, 20 Sep 2018 17:37:53 -0400
+Subject: [PATCH] Use pthread keys for thread local storage
+
+This interface is slower but also more portable, and more importantly
+it provides a way to specify destructor that is called when a thread
+is canceled so we stop leaking memory.
+
+Signed-off-by: Simo Sorce <simo@redhat.com>
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Merges: #233
+(cherry picked from commit 0faccc1441bc7a6b3e8bd806f22c8a961e5f586e)
+(cherry picked from commit 89dc0ee157caa4617d32fd72287849296d7fe26d)
+---
+ proxy/src/client/gpm_common.c         |  2 +
+ proxy/src/client/gpm_display_status.c | 57 ++++++++++++++++++---------
+ proxy/src/client/gssapi_gpm.h         |  1 +
+ 3 files changed, 42 insertions(+), 18 deletions(-)
+
+diff --git a/proxy/src/client/gpm_common.c b/proxy/src/client/gpm_common.c
+index 0d314fa..b482efb 100644
+--- a/proxy/src/client/gpm_common.c
++++ b/proxy/src/client/gpm_common.c
+@@ -55,6 +55,8 @@ static void gpm_init_once(void)
+     gpm_global_ctx.next_xid = rand_r(&seedp);
+ 
+     pthread_mutexattr_destroy(&attr);
++
++    gpm_display_status_init_once();
+ }
+ 
+ static int get_pipe_name(char *name)
+diff --git a/proxy/src/client/gpm_display_status.c b/proxy/src/client/gpm_display_status.c
+index bbb546f..e3aa4ea 100644
+--- a/proxy/src/client/gpm_display_status.c
++++ b/proxy/src/client/gpm_display_status.c
+@@ -1,27 +1,47 @@
+ /* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */
+ 
+ #include "gssapi_gpm.h"
++#include <pthread.h>
+ 
+-__thread gssx_status *tls_last_status = NULL;
++static pthread_key_t gpm_last_status;
+ 
+-/* Thread local storage for return status.
+- * FIXME: it's not the most portable construct, so may need fixing in future */
++static void gpm_destroy_last_status(void *arg)
++{
++    gssx_status *status = (gssx_status *)arg;
++    xdr_free((xdrproc_t)xdr_gssx_status, (char *)status);
++    free(status);
++}
++
++void gpm_display_status_init_once(void)
++{
++    (void)pthread_key_create(&gpm_last_status, gpm_destroy_last_status);
++}
++
++/* Portable thread local storage for return status. */
+ void gpm_save_status(gssx_status *status)
+ {
++    gssx_status *last_status;
+     int ret;
+ 
+-    if (tls_last_status) {
+-        xdr_free((xdrproc_t)xdr_gssx_status, (char *)tls_last_status);
+-        free(tls_last_status);
++    last_status = (gssx_status *)pthread_getspecific(gpm_last_status);
++    if (last_status != NULL) {
++        /* store NULL first so we do not risk a double free if we are
++         * racing on a pthread_cancel */
++        pthread_setspecific(gpm_last_status, NULL);
++        gpm_destroy_last_status(last_status);
+     }
+ 
+-    ret = gp_copy_gssx_status_alloc(status, &tls_last_status);
+-    if (ret) {
+-        /* make sure tls_last_status is zeored on error */
+-        tls_last_status = NULL;
++    ret = gp_copy_gssx_status_alloc(status, &last_status);
++    if (ret == 0) {
++        pthread_setspecific(gpm_last_status, last_status);
+     }
+ }
+ 
++gssx_status *gpm_get_saved_status(void)
++{
++    return (gssx_status *)pthread_getspecific(gpm_last_status);
++}
++
+ /* This funciton is used to record internal mech errors that are
+  * generated by the proxy client code */
+ void gpm_save_internal_status(uint32_t err, char *err_str)
+@@ -47,15 +67,16 @@ OM_uint32 gpm_display_status(OM_uint32 *minor_status,
+                              OM_uint32 *message_context,
+                              gss_buffer_t status_string)
+ {
++    gssx_status *last_status = gpm_get_saved_status();
+     utf8string tmp;
+     int ret;
+ 
+     switch(status_type) {
+     case GSS_C_GSS_CODE:
+-        if (tls_last_status &&
+-            tls_last_status->major_status == status_value &&
+-            tls_last_status->major_status_string.utf8string_len) {
+-                ret = gp_copy_utf8string(&tls_last_status->major_status_string,
++        if (last_status &&
++            last_status->major_status == status_value &&
++            last_status->major_status_string.utf8string_len) {
++                ret = gp_copy_utf8string(&last_status->major_status_string,
+                                          &tmp);
+                 if (ret) {
+                     *minor_status = ret;
+@@ -70,9 +91,9 @@ OM_uint32 gpm_display_status(OM_uint32 *minor_status,
+             return GSS_S_UNAVAILABLE;
+         }
+     case GSS_C_MECH_CODE:
+-        if (tls_last_status &&
+-            tls_last_status->minor_status == status_value &&
+-            tls_last_status->minor_status_string.utf8string_len) {
++        if (last_status &&
++            last_status->minor_status == status_value &&
++            last_status->minor_status_string.utf8string_len) {
+ 
+             if (*message_context) {
+                 /* we do not support multiple messages for now */
+@@ -80,7 +101,7 @@ OM_uint32 gpm_display_status(OM_uint32 *minor_status,
+                 return GSS_S_FAILURE;
+             }
+ 
+-            ret = gp_copy_utf8string(&tls_last_status->minor_status_string,
++            ret = gp_copy_utf8string(&last_status->minor_status_string,
+                                      &tmp);
+             if (ret) {
+                 *minor_status = ret;
+diff --git a/proxy/src/client/gssapi_gpm.h b/proxy/src/client/gssapi_gpm.h
+index 22beecf..61124e0 100644
+--- a/proxy/src/client/gssapi_gpm.h
++++ b/proxy/src/client/gssapi_gpm.h
+@@ -23,6 +23,7 @@ OM_uint32 gpm_release_name(OM_uint32 *minor_status,
+ OM_uint32 gpm_release_buffer(OM_uint32 *minor_status,
+                              gss_buffer_t buffer);
+ 
++void gpm_display_status_init_once(void);
+ void gpm_save_status(gssx_status *status);
+ void gpm_save_internal_status(uint32_t err, char *err_str);
+ 
diff --git a/SOURCES/rwtab b/SOURCES/rwtab
new file mode 100644
index 0000000..f4cdfb5
--- /dev/null
+++ b/SOURCES/rwtab
@@ -0,0 +1 @@
+dirs /var/lib/gssproxy
diff --git a/SPECS/gssproxy.spec b/SPECS/gssproxy.spec
index b46584a..a667d8b 100644
--- a/SPECS/gssproxy.spec
+++ b/SPECS/gssproxy.spec
@@ -1,12 +1,13 @@
 Name:		gssproxy
 Version:	0.7.0
-Release:	21%{?dist}
+Release:	26%{?dist}
 Summary:	GSSAPI Proxy
 
 Group:		System Environment/Libraries
 License:	MIT
 URL:		https://pagure.io/gssproxy
 Source0:	https://releases.pagure.org/gssproxy/gssproxy-%{version}.tar.gz
+Source1:	rwtab
 BuildRoot:	%(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 
 %global servicename gssproxy
@@ -52,6 +53,14 @@ Patch35: Don-t-leak-mech_type-when-CONTINUE_NEEDED-from-init_.patch
 Patch36: Always-use-the-encype-we-selected.patch
 Patch37: Clarify-debug-and-debug_level-in-man-pages.patch
 Patch38: Always-choose-highest-requested-debug-level.patch
+Patch39: Use-pthread-keys-for-thread-local-storage.patch
+Patch40: Close-epoll-fd-within-the-lock.patch
+Patch41: Add-a-safety-timeout-to-epoll.patch
+Patch42: Always-initialize-out-cred-in-gp_import_gssx_cred.patch
+Patch43: Handle-gss_import_cred-failure-when-importing-gssx-c.patch
+Patch44: Include-length-when-using-krb5_c_decrypt.patch
+Patch45: Change-the-way-we-handle-encrypted-buffers.patch
+Patch46: Avoid-uninitialized-free-when-allocating-buffers.patch
 
 ### Dependencies ###
 
@@ -136,6 +145,14 @@ A proxy for GSSAPI credential handling
 %patch36 -p2 -b .Always-use-the-encype-we-selected
 %patch37 -p2 -b .Clarify-debug-and-debug_level-in-man-pages
 %patch38 -p2 -b .Always-choose-highest-requested-debug-level
+%patch39 -p2 -b .Use-pthread-keys-for-thread-local-storage
+%patch40 -p2 -b .Close-epoll-fd-within-the-lock
+%patch41 -p2 -b .Add-a-safety-timeout-to-epoll
+%patch42 -p2 -b .Always-initialize-out-cred-in-gp_import_gssx_cred
+%patch43 -p2 -b .Handle-gss_import_cred-failure-when-importing-gssx-c
+%patch44 -p2 -b .Include-length-when-using-krb5_c_decrypt
+%patch45 -p2 -b .Change-the-way-we-handle-encrypted-buffers
+%patch46 -p2 -b .Avoid-uninitialized-free-when-allocating-buffers
 
 %build
 autoreconf -f -i
@@ -160,6 +177,8 @@ install -m644 examples/99-nfs-client.conf %{buildroot}%{_sysconfdir}/gssproxy/99
 mkdir -p %{buildroot}%{_sysconfdir}/gss/mech.d
 install -m644 examples/mech %{buildroot}%{_sysconfdir}/gss/mech.d/gssproxy.conf
 mkdir -p %{buildroot}/var/lib/gssproxy/rcache
+mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/rwtab.d
+install -m644 %{SOURCE1} $RPM_BUILD_ROOT/%{_sysconfdir}/rwtab.d/gssproxy
 
 %clean
 rm -rf -- "%{buildroot}"
@@ -181,7 +200,7 @@ rm -rf -- "%{buildroot}"
 %{_mandir}/man5/gssproxy.conf.5*
 %{_mandir}/man8/gssproxy.8*
 %{_mandir}/man8/gssproxy-mech.8*
-
+%config(noreplace) %{_sysconfdir}/rwtab.d/gssproxy
 
 %post
 %systemd_post gssproxy.service
@@ -196,6 +215,26 @@ rm -rf -- "%{buildroot}"
 
 
 %changelog
+* Wed May 01 2019 Robbie Harwood <rharwood@redhat.com> 0.7.0-26
+- Avoid uninitialized free when allocating buffers
+- Resolves: #1699331
+
+* Tue Apr 30 2019 Robbie Harwood <rharwood@redhat.com> 0.7.0-25
+- Fix explicit NULL deref on some enctypes
+- Resolves: #1699331
+
+* Mon Mar 18 2019 Robbie Harwood <rharwood@redhat.com> 0.7.0-24
+- Add a safety timeout to epoll
+- Resolves: #1687899
+
+* Mon Dec 17 2018 Robbie Harwood <rharwood@redhat.com> 0.7.0-23
+- Use pthread keys for thread local storage
+- Resolves: #1618375
+
+* Tue Dec 11 2018 Robbie Harwood <rharwood@redhat.com> 0.7.0-22
+- Add hack to support read-only root
+- Resolves: #1542567
+
 * Fri Jun 08 2018 Robbie Harwood <rharwood@redhat.com> 0.7.0-21
 - Always choose highest requested debug level
 - Resolves: #1505741