krb5 commit: Modernize KRB-PRIV/KRB-SAFE/KRB-CRED functions

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/44b601ec43f34a6ed5bf9a737328d2ee4c438f12
commit 44b601ec43f34a6ed5bf9a737328d2ee4c438f12
Author: Greg Hudson <[email protected]>
Date:   Fri Feb 15 11:59:06 2019 -0500

    Modernize KRB-PRIV/KRB-SAFE/KRB-CRED functions
    
    In krb5_mk_safe(), krb5_mk_priv(), krb5_mk_ncred(), krb5_rd_safe(),
    and krb5_rd_priv(), modify naming, formatting, and memory management
    to meet current standards.  Add k5_privsafe helpers to generate replay
    data and addresses and to store replay records.  For krb5_mk_ncred(),
    expand the contract of the encoding function to be similar to the
    other two krb5_mk functions, and use pointer aliases to reduce the
    number of copies required.

 src/include/krb5/krb5.hin    |  128 +++++++------
 src/lib/krb5/krb/cleanup.h   |   30 ---
 src/lib/krb5/krb/deps        |   27 ++--
 src/lib/krb5/krb/int-proto.h |   24 +++
 src/lib/krb5/krb/mk_cred.c   |  433 +++++++++++++++++-------------------------
 src/lib/krb5/krb/mk_priv.c   |  330 +++++++++++++-------------------
 src/lib/krb5/krb/mk_safe.c   |  314 +++++++++++-------------------
 src/lib/krb5/krb/privsafe.c  |  148 +++++++++++++--
 src/lib/krb5/krb/rd_cred.c   |   75 ++++----
 src/lib/krb5/krb/rd_priv.c   |  266 +++++++++++---------------
 src/lib/krb5/krb/rd_safe.c   |  270 ++++++++++++---------------
 11 files changed, 913 insertions(+), 1132 deletions(-)

diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 15b7677..a67eec1 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -3310,20 +3310,20 @@ krb5_rd_error(krb5_context context, const krb5_data *enc_errbuf,
  * @param [in]  context         Library context
  * @param [in]  auth_context    Authentication context
  * @param [in]  inbuf           @c KRB-SAFE message to be parsed
- * @param [out] outbuf          Data parsed from @c KRB-SAFE message
- * @param [out] outdata         Replay data. Specify NULL if not needed
+ * @param [out] userdata_out    Data parsed from @c KRB-SAFE message
+ * @param [out] rdata_out       Replay data. Specify NULL if not needed
  *
  * This function parses a @c KRB-SAFE message, verifies its integrity, and
- * stores its data into @a outbuf.
+ * stores its data into @a userdata_out.
  *
- * @note The @a outdata argument is required if #KRB5_AUTH_CONTEXT_RET_TIME or
- *       #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in the @a auth_context.
+ * @note The @a rdata_out argument is required if #KRB5_AUTH_CONTEXT_RET_TIME
+ * or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in the @a auth_context.
  *
  * @note @a auth_context must have a remote address set.  This address will be
- *       used to verify the sender address in the KRB-SAFE message.  If @a
- *       auth_context has a local address set, it will be used to verify the
- *       receiver address in the KRB-SAFE message if the message contains one.
- *       Both addresses must use type @c ADDRTYPE_ADDRPORT.
+ * used to verify the sender address in the KRB-SAFE message.  If @a
+ * auth_context has a local address set, it will be used to verify the receiver
+ * address in the KRB-SAFE message if the message contains one.  Both addresses
+ * must use type @c ADDRTYPE_ADDRPORT.
  *
  * If the #KRB5_AUTH_CONTEXT_DO_SEQUENCE flag is set in @a auth_context, the
  * sequence number of the KRB-SAFE message is checked against the remote
@@ -3336,14 +3336,15 @@ krb5_rd_error(krb5_context context, const krb5_data *enc_errbuf,
  *     (which is usually five minutes).
  * @li The message must not be a replayed message field in @a auth_context.
  *
- * Use krb5_free_data_contents() to free @a outbuf when it is no longer needed.
+ * Use krb5_free_data_contents() to free @a userdata_out when it is no longer
+ * needed.
  *
  * @retval 0 Success; otherwise - Kerberos error codes
  */
 krb5_error_code KRB5_CALLCONV
 krb5_rd_safe(krb5_context context, krb5_auth_context auth_context,
-             const krb5_data *inbuf, krb5_data *outbuf,
-             krb5_replay_data *outdata);
+             const krb5_data *inbuf, krb5_data *userdata_out,
+             krb5_replay_data *rdata_out);
 
 /**
  * Process a @c KRB-PRIV message.
@@ -3351,21 +3352,20 @@ krb5_rd_safe(krb5_context context, krb5_auth_context auth_context,
  * @param [in]  context         Library context
  * @param [in]  auth_context    Authentication structure
  * @param [in]  inbuf           @c KRB-PRIV message to be parsed
- * @param [out] outbuf          Data parsed from @c KRB-PRIV message
- * @param [out] outdata         Replay data. Specify NULL if not needed
+ * @param [out] userdata_out    Data parsed from @c KRB-PRIV message
+ * @param [out] rdata_out       Replay data. Specify NULL if not needed
  *
  * This function parses a @c KRB-PRIV message, verifies its integrity, and
- * stores its unencrypted data into @a outbuf.
+ * stores its unencrypted data into @a userdata_out.
  *
- * @note If the #KRB5_AUTH_CONTEXT_RET_TIME or
- *       #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in @a auth_context, @a
- *       outdata is required.
+ * @note If the #KRB5_AUTH_CONTEXT_RET_TIME or #KRB5_AUTH_CONTEXT_RET_SEQUENCE
+ * flag is set in @a auth_context, @a rdata_out is required.
  *
  * @note @a auth_context must have a remote address set.  This address will be
- *       used to verify the sender address in the KRB-PRIV message.  If @a
- *       auth_context has a local address set, it will be used to verify the
- *       receiver address in the KRB-PRIV message if the message contains one.
- *       Both addresses must use type @c ADDRTYPE_ADDRPORT.
+ * used to verify the sender address in the KRB-PRIV message.  If @a
+ * auth_context has a local address set, it will be used to verify the receiver
+ * address in the KRB-PRIV message if the message contains one.  Both addresses
+ * must use type @c ADDRTYPE_ADDRPORT.
  *
  * If the #KRB5_AUTH_CONTEXT_DO_SEQUENCE flag is set in @a auth_context, the
  * sequence number of the KRB-SAFE message is checked against the remote
@@ -3378,12 +3378,15 @@ krb5_rd_safe(krb5_context context, krb5_auth_context auth_context,
  *     (which is usually five minutes).
  * @li The message must not be a replayed message field in @a auth_context.
  *
+ * Use krb5_free_data_contents() to free @a userdata_out when it is no longer
+ * needed.
+ *
  * @retval 0 Success; otherwise - Kerberos error codes
  */
 krb5_error_code KRB5_CALLCONV
 krb5_rd_priv(krb5_context context, krb5_auth_context auth_context,
-             const krb5_data *inbuf, krb5_data *outbuf,
-             krb5_replay_data *outdata);
+             const krb5_data *inbuf, krb5_data *userdata_out,
+             krb5_replay_data *rdata_out);
 
 /**
  * Convert a string principal name to a krb5_principal structure.
@@ -5245,8 +5248,8 @@ krb5_kt_read_service_key(krb5_context context, krb5_pointer keyprocarg,
  * @param [in]  context         Library context
  * @param [in]  auth_context    Authentication context
  * @param [in]  userdata        User data in the message
- * @param [out] outbuf          Formatted @c KRB-SAFE buffer
- * @param [out] outdata         Replay data. Specify NULL if not needed
+ * @param [out] der_out         Formatted @c KRB-SAFE buffer
+ * @param [out] rdata_out       Replay data. Specify NULL if not needed
  *
  * This function creates an integrity protected @c KRB-SAFE message
  * using data supplied by the application.
@@ -5268,19 +5271,20 @@ krb5_kt_read_service_key(krb5_context context, krb5_pointer keyprocarg,
  *
  * If either #KRB5_AUTH_CONTEXT_DO_SEQUENCE or
  * #KRB5_AUTH_CONTEXT_RET_SEQUENCE is set, the @a auth_context local sequence
- * number will be placed in @a outdata as its sequence number.
+ * number will be placed in @a rdata_out as its sequence number.
  *
- * @note The @a outdata argument is required if #KRB5_AUTH_CONTEXT_RET_TIME or
- *       #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in the @a auth_context.
+ * @note The @a rdata_out argument is required if #KRB5_AUTH_CONTEXT_RET_TIME
+ * or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in the @a auth_context.
  *
- * Use krb5_free_data_contents() to free @a outbuf when it is no longer needed.
+ * Use krb5_free_data_contents() to free @a der_out when it is no longer
+ * needed.
  *
  * @retval 0 Success; otherwise - Kerberos error codes
  */
 krb5_error_code KRB5_CALLCONV
 krb5_mk_safe(krb5_context context, krb5_auth_context auth_context,
-             const krb5_data *userdata, krb5_data *outbuf,
-             krb5_replay_data *outdata);
+             const krb5_data *userdata, krb5_data *der_out,
+             krb5_replay_data *rdata_out);
 
 /**
  * Format a @c KRB-PRIV message.
@@ -5288,8 +5292,8 @@ krb5_mk_safe(krb5_context context, krb5_auth_context auth_context,
  * @param [in]  context         Library context
  * @param [in]  auth_context    Authentication context
  * @param [in]  userdata        User data for @c KRB-PRIV message
- * @param [out] outbuf          Formatted @c KRB-PRIV message
- * @param [out] outdata         Replay cache handle (NULL if not needed)
+ * @param [out] der_out         Formatted @c KRB-PRIV message
+ * @param [out] rdata_out       Replay cache handle (NULL if not needed)
  *
  * This function is similar to krb5_mk_safe(), but the message is encrypted and
  * integrity-protected, not just integrity-protected.
@@ -5301,7 +5305,7 @@ krb5_mk_safe(krb5_context context, krb5_auth_context auth_context,
  *
  * @note If the #KRB5_AUTH_CONTEXT_RET_TIME or
  * #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in @a auth_context, the @a
- * outdata is required.
+ * rdata_out is required.
  *
  * @note The flags from @a auth_context specify whether sequence numbers or
  * timestamps will be used to identify the message.  Valid values are:
@@ -5312,14 +5316,14 @@ krb5_mk_safe(krb5_context context, krb5_auth_context auth_context,
  *                                       @a auth_context in replay cache.
  * @li #KRB5_AUTH_CONTEXT_RET_SEQUENCE - Use local sequence numbers from
  *                                       @a auth_context as a sequence number
- *                                       in the encrypted message @a outbuf.
+ *                                       in the encrypted message @a der_out.
  *
  * @retval 0 Success; otherwise - Kerberos error codes
  */
 krb5_error_code KRB5_CALLCONV
 krb5_mk_priv(krb5_context context, krb5_auth_context auth_context,
-             const krb5_data *userdata, krb5_data *outbuf,
-             krb5_replay_data *outdata);
+             const krb5_data *userdata, krb5_data *der_out,
+             krb5_replay_data *rdata_out);
 
 /**
  * Client function for @c sendauth protocol.
@@ -5437,15 +5441,15 @@ krb5_recvauth_version(krb5_context context,
  *
  * @param [in]  context         Library context
  * @param [in]  auth_context    Authentication context
- * @param [in]  ppcreds         Null-terminated array of credentials
- * @param [out] ppdata          Encoded credentials
- * @param [out] outdata         Replay cache information (NULL if not needed)
+ * @param [in]  creds           Null-terminated array of credentials
+ * @param [out] der_out         Encoded credentials
+ * @param [out] rdata_out       Replay cache information (NULL if not needed)
  *
- * This function takes an array of credentials @a ppcreds and formats
- * a @c KRB-CRED message @a ppdata to pass to krb5_rd_cred().
+ * This function takes an array of credentials @a creds and formats
+ * a @c KRB-CRED message @a der_out to pass to krb5_rd_cred().
  *
  * @note If the #KRB5_AUTH_CONTEXT_RET_TIME or #KRB5_AUTH_CONTEXT_RET_SEQUENCE
- *       flag is set in @a auth_context, @a outdata is required.
+ * flag is set in @a auth_context, @a rdata_out is required.
  *
  * The message will be encrypted using the send subkey of @a auth_context if it
  * is present, or the session key otherwise.
@@ -5461,17 +5465,17 @@ krb5_recvauth_version(krb5_context context,
  */
 krb5_error_code KRB5_CALLCONV
 krb5_mk_ncred(krb5_context context, krb5_auth_context auth_context,
-              krb5_creds **ppcreds, krb5_data **ppdata,
-              krb5_replay_data *outdata);
+              krb5_creds **creds, krb5_data **der_out,
+              krb5_replay_data *rdata_out);
 
 /**
  * Format a @c KRB-CRED message for a single set of credentials.
  *
  * @param [in]  context         Library context
  * @param [in]  auth_context    Authentication context
- * @param [in]  pcreds          Pointer to credentials
- * @param [out] ppdata          Encoded credentials
- * @param [out] outdata         Replay cache data (NULL if not needed)
+ * @param [in]  creds           Pointer to credentials
+ * @param [out] der_out         Encoded credentials
+ * @param [out] rdata_out       Replay cache data (NULL if not needed)
  *
  * This is a convenience function that calls krb5_mk_ncred() with a single set
  * of credentials.
@@ -5487,33 +5491,33 @@ krb5_mk_ncred(krb5_context context, krb5_auth_context auth_context,
  */
 krb5_error_code KRB5_CALLCONV
 krb5_mk_1cred(krb5_context context, krb5_auth_context auth_context,
-              krb5_creds *pcreds, krb5_data **ppdata,
-              krb5_replay_data *outdata);
+              krb5_creds *creds, krb5_data **der_out,
+              krb5_replay_data *rdata_out);
 
 /**
  * Read and validate a @c KRB-CRED message.
  *
  * @param [in]  context         Library context
  * @param [in]  auth_context    Authentication context
- * @param [in]  pcreddata       @c KRB-CRED message
- * @param [out] pppcreds        Null-terminated array of forwarded credentials
- * @param [out] outdata         Replay data (NULL if not needed)
+ * @param [in]  creddata        @c KRB-CRED message
+ * @param [out] creds_out       Null-terminated array of forwarded credentials
+ * @param [out] rdata_out       Replay data (NULL if not needed)
  *
- * @note The @a outdata argument is required if #KRB5_AUTH_CONTEXT_RET_TIME or
- *       #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in the @a auth_context.`
+ * @note The @a rdata_out argument is required if #KRB5_AUTH_CONTEXT_RET_TIME
+ * or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in the @a auth_context.`
  *
- * @a pcreddata will be decrypted using the receiving subkey if it is present
- * in @a auth_context, or the session key if the receiving subkey is not
- * present or fails to decrypt the message.
+ * @a creddata will be decrypted using the receiving subkey if it is present in
+ * @a auth_context, or the session key if the receiving subkey is not present
+ * or fails to decrypt the message.
  *
- * Use krb5_free_tgt_creds() to free @a pppcreds when it is no longer needed.
+ * Use krb5_free_tgt_creds() to free @a creds_out when it is no longer needed.
  *
  * @retval 0 Success; otherwise - Kerberos error codes
  */
 krb5_error_code KRB5_CALLCONV
 krb5_rd_cred(krb5_context context, krb5_auth_context auth_context,
-             krb5_data *pcreddata, krb5_creds ***pppcreds,
-             krb5_replay_data *outdata);
+             krb5_data *creddata, krb5_creds ***creds_out,
+             krb5_replay_data *rdata_out);
 
 /**
  * Get a forwarded TGT and format a @c KRB-CRED message.
diff --git a/src/lib/krb5/krb/cleanup.h b/src/lib/krb5/krb/cleanup.h
deleted file mode 100644
index 3a01833..0000000
--- a/src/lib/krb5/krb/cleanup.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-
-#ifndef KRB5_CLEANUP
-#define KRB5_CLEANUP
-
-struct cleanup {
-    void                * arg;
-    void                (*func)(void *);
-};
-
-#define CLEANUP_INIT(x)                         \
-    struct cleanup cleanup_data[x];             \
-    int cleanup_count = 0;
-
-#define CLEANUP_PUSH(x, y)                      \
-    cleanup_data[cleanup_count].arg = x;        \
-    cleanup_data[cleanup_count].func = y;       \
-    cleanup_count++;
-
-#define CLEANUP_POP(x)                                                  \
-    if ((--cleanup_count) && x && (cleanup_data[cleanup_count].func))   \
-        cleanup_data[cleanup_count].func(cleanup_data[cleanup_count].arg);
-
-#define CLEANUP_DONE()                                                  \
-    while(cleanup_count--)                                              \
-        if (cleanup_data[cleanup_count].func)                           \
-            cleanup_data[cleanup_count].func(cleanup_data[cleanup_count].arg);
-
-
-#endif
diff --git a/src/lib/krb5/krb/deps b/src/lib/krb5/krb/deps
index a4a809b..fc2a16e 100644
--- a/src/lib/krb5/krb/deps
+++ b/src/lib/krb5/krb/deps
@@ -460,12 +460,13 @@ gen_save_subkey.so gen_save_subkey.po $(OUTPRE)gen_save_subkey.$(OBJEXT): \
 get_creds.so get_creds.po $(OUTPRE)get_creds.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
   $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
-  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-  $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-  $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
-  $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
-  $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+  $(COM_ERR_DEPS) $(srcdir)/../os/os-proto.h $(top_srcdir)/include/k5-buf.h \
+  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+  $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+  $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
   fast.h get_creds.c int-proto.h
 get_etype_info.so get_etype_info.po $(OUTPRE)get_etype_info.$(OBJEXT): \
@@ -627,8 +628,8 @@ mk_cred.so mk_cred.po $(OUTPRE)mk_cred.$(OBJEXT): $(BUILDTOP)/include/autoconf.h
   $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
   $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
   $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-  $(top_srcdir)/include/socket-utils.h auth_con.h cleanup.h \
-  int-proto.h mk_cred.c
+  $(top_srcdir)/include/socket-utils.h auth_con.h int-proto.h \
+  mk_cred.c
 mk_error.so mk_error.po $(OUTPRE)mk_error.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
   $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
@@ -649,7 +650,7 @@ mk_priv.so mk_priv.po $(OUTPRE)mk_priv.$(OBJEXT): $(BUILDTOP)/include/autoconf.h
   $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
   $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
   $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-  $(top_srcdir)/include/socket-utils.h auth_con.h cleanup.h \
+  $(top_srcdir)/include/socket-utils.h auth_con.h int-proto.h \
   mk_priv.c
 mk_rep.so mk_rep.po $(OUTPRE)mk_rep.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
@@ -692,7 +693,7 @@ mk_safe.so mk_safe.po $(OUTPRE)mk_safe.$(OBJEXT): $(BUILDTOP)/include/autoconf.h
   $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
   $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
   $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-  $(top_srcdir)/include/socket-utils.h auth_con.h cleanup.h \
+  $(top_srcdir)/include/socket-utils.h auth_con.h int-proto.h \
   mk_safe.c
 pac.so pac.po $(OUTPRE)pac.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
@@ -875,7 +876,7 @@ rd_cred.so rd_cred.po $(OUTPRE)rd_cred.$(OBJEXT): $(BUILDTOP)/include/autoconf.h
   $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
   $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
   $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-  $(top_srcdir)/include/socket-utils.h auth_con.h cleanup.h \
+  $(top_srcdir)/include/socket-utils.h auth_con.h int-proto.h \
   rd_cred.c
 rd_error.so rd_error.po $(OUTPRE)rd_error.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
@@ -940,8 +941,8 @@ rd_safe.so rd_safe.po $(OUTPRE)rd_safe.$(OBJEXT): $(BUILDTOP)/include/autoconf.h
   $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
   $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
   $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-  $(top_srcdir)/include/socket-utils.h auth_con.h cleanup.h \
-  int-proto.h rd_safe.c
+  $(top_srcdir)/include/socket-utils.h auth_con.h int-proto.h \
+  rd_safe.c
 recvauth.so recvauth.po $(OUTPRE)recvauth.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
   $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
index 9783548..4464a13 100644
--- a/src/lib/krb5/krb/int-proto.h
+++ b/src/lib/krb5/krb/int-proto.h
@@ -143,6 +143,30 @@ krb5int_validate_times(krb5_context, krb5_ticket_times *);
 krb5_error_code
 krb5int_copy_authdatum(krb5_context, const krb5_authdata *, krb5_authdata **);
 
+/* Set replay data fields in rdata and caller_rdata according to the flags in
+ * authcon. */
+krb5_error_code
+k5_privsafe_gen_rdata(krb5_context context, krb5_auth_context authcon,
+                      krb5_replay_data *rdata, krb5_replay_data *caller_rdata);
+
+/*
+ * Set *local_out and *remote_out to addresses based on authcon.  The resulting
+ * pointers should not be freed, but addresses may be placed into *lstorage and
+ * *rstorage which the caller must free, even on error.
+ */
+krb5_error_code
+k5_privsafe_gen_addrs(krb5_context context, krb5_auth_context authcon,
+                      krb5_address *lstorage, krb5_address *rstorage,
+                      krb5_address **local_out, krb5_address **remote_out);
+
+/* If the DO_TIME flag is set in authcon, store a replay record.  If check_time
+ * is true, also check that the timestamp is within clock skew. */
+krb5_error_code
+k5_privsafe_check_replay(krb5_context context, krb5_auth_context authcon,
+                         krb5_address *addr, const char *uniq,
+                         const krb5_replay_data *rdata,
+                         krb5_boolean check_time);
+
 krb5_boolean
 k5_privsafe_check_seqnum(krb5_context ctx, krb5_auth_context ac,
                          krb5_ui_4 in_seq);
diff --git a/src/lib/krb5/krb/mk_cred.c b/src/lib/krb5/krb/mk_cred.c
index 7616c3a..003a0fd 100644
--- a/src/lib/krb5/krb/mk_cred.c
+++ b/src/lib/krb5/krb/mk_cred.c
@@ -1,308 +1,225 @@
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/krb/mk_cred.c - definition of krb5_mk_ncred(), krb5_mk_1cred() */
 /*
- * NAME
- *    cred.c
+ * Copyright (C) 2019 by the Massachusetts Institute of Technology.
+ * All rights reserved.
  *
- * DESCRIPTION
- *    Provide an interface to assemble and disassemble krb5_cred
- *    structures.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
+
 #include "k5-int.h"
 #include "int-proto.h"
-#include "cleanup.h"
 #include "auth_con.h"
 
-#include <stddef.h>           /* NULL */
-#include <stdlib.h>           /* malloc */
-#include <errno.h>            /* ENOMEM */
-
-/*-------------------- encrypt_credencpart --------------------*/
-
-/*
- * encrypt the enc_part of krb5_cred
- */
+/* Encrypt the enc_part of krb5_cred.  key may be NULL to use the unencrypted
+ * KRB-CRED form (RFC 6448). */
 static krb5_error_code
-encrypt_credencpart(krb5_context context, krb5_cred_enc_part *pcredpart,
-                    krb5_key pkey, krb5_enc_data *pencdata)
+encrypt_credencpart(krb5_context context, krb5_cred_enc_part *encpart,
+                    krb5_key key, krb5_enc_data *encdata_out)
 {
-    krb5_error_code       retval;
-    krb5_data           * scratch;
-
-    /* start by encoding to-be-encrypted part of the message */
-    if ((retval = encode_krb5_enc_cred_part(pcredpart, &scratch)))
-        return retval;
-
-    /*
-     * If the keyblock is NULL, just copy the data from the encoded
-     * data to the ciphertext area.
-     */
-    if (pkey == NULL) {
-        pencdata->ciphertext.data = scratch->data;
-        pencdata->ciphertext.length = scratch->length;
-        free(scratch);
+    krb5_error_code ret;
+    krb5_data *der_enccred;
+
+    /* Start by encoding to-be-encrypted part of the message. */
+    ret = encode_krb5_enc_cred_part(encpart, &der_enccred);
+    if (ret)
+        return ret;
+
+    if (key == NULL) {
+        /* Just copy the encoded data to the ciphertext area. */
+        encdata_out->enctype = ENCTYPE_NULL;
+        encdata_out->ciphertext = *der_enccred;
+        free(der_enccred);
         return 0;
     }
 
-    /* call the encryption routine */
-    retval = k5_encrypt_keyhelper(context, pkey,
-                                  KRB5_KEYUSAGE_KRB_CRED_ENCPART, scratch,
-                                  pencdata);
+    ret = k5_encrypt_keyhelper(context, key, KRB5_KEYUSAGE_KRB_CRED_ENCPART,
+                               der_enccred, encdata_out);
 
-    memset(scratch->data, 0, scratch->length);
-    krb5_free_data(context, scratch);
-
-    return retval;
+    zap(der_enccred->data, der_enccred->length);
+    krb5_free_data(context, der_enccred);
+    return ret;
 }
 
-/*----------------------- krb5_mk_ncred_basic -----------------------*/
-
+/*
+ * Marshal a KRB-CRED message into der_out, encrypted with key (or unencrypted
+ * if key is NULL).  Use the timestamp and sequence number from rdata and the
+ * addresses from local_addr and remote_addr (either of which may be NULL).
+ * der_out should be freed by the caller when finished.
+ */
 static krb5_error_code
-krb5_mk_ncred_basic(krb5_context context,
-                    krb5_creds **ppcreds, krb5_int32 nppcreds,
-                    krb5_key key, krb5_replay_data *replaydata,
-                    krb5_address *local_addr, krb5_address *remote_addr,
-                    krb5_cred *pcred)
+create_krbcred(krb5_context context, krb5_creds **creds, krb5_key key,
+               const krb5_replay_data *rdata, krb5_address *local_addr,
+               krb5_address *remote_addr, krb5_data **der_out)
 {
-    krb5_cred_enc_part    credenc;
-    krb5_error_code       retval;
-    size_t                size;
-    int                   i;
-
-    credenc.magic = KV5M_CRED_ENC_PART;
-
-    credenc.s_address = 0;
-    credenc.r_address = 0;
-    if (local_addr) krb5_copy_addr(context, local_addr, &credenc.s_address);
-    if (remote_addr) krb5_copy_addr(context, remote_addr, &credenc.r_address);
-
-    credenc.nonce = replaydata->seq;
-    credenc.usec = replaydata->usec;
-    credenc.timestamp = replaydata->timestamp;
-
-    /* Get memory for creds and initialize it */
-    size = sizeof(krb5_cred_info *) * (nppcreds + 1);
-    credenc.ticket_info = (krb5_cred_info **) calloc(1, size);
-    if (credenc.ticket_info == NULL)
-        return ENOMEM;
-
-    /*
-     * For each credential in the list, initialize a cred info
-     * structure and copy the ticket into the ticket list.
-     */
-    for (i = 0; i < nppcreds; i++) {
-        credenc.ticket_info[i] = calloc(1, sizeof(krb5_cred_info));
-        if (credenc.ticket_info[i] == NULL) {
-            retval = ENOMEM;
-            goto cleanup;
-        }
-        credenc.ticket_info[i+1] = NULL;
-
-        credenc.ticket_info[i]->magic = KV5M_CRED_INFO;
-        credenc.ticket_info[i]->times = ppcreds[i]->times;
-        credenc.ticket_info[i]->flags = ppcreds[i]->ticket_flags;
-
-        if ((retval = decode_krb5_ticket(&ppcreds[i]->ticket,
-                                         &pcred->tickets[i])))
-            goto cleanup;
-
-        if ((retval = krb5_copy_keyblock(context, &ppcreds[i]->keyblock,
-                                         &credenc.ticket_info[i]->session)))
-            goto cleanup;
-
-        if ((retval = krb5_copy_principal(context, ppcreds[i]->client,
-                                          &credenc.ticket_info[i]->client)))
-            goto cleanup;
-
-        if ((retval = krb5_copy_principal(context, ppcreds[i]->server,
-                                          &credenc.ticket_info[i]->server)))
+    krb5_error_code ret;
+    krb5_cred_enc_part credenc;
+    krb5_cred cred;
+    krb5_ticket **tickets = NULL;
+    krb5_cred_info **ticket_info = NULL, *tinfos = NULL;
+    krb5_enc_data enc;
+    size_t i, ncreds;
+
+    *der_out = NULL;
+    memset(&enc, 0, sizeof(enc));
+
+    for (ncreds = 0; creds[ncreds] != NULL; ncreds++);
+
+    tickets = k5calloc(ncreds + 1, sizeof(*tickets), &ret);
+    if (tickets == NULL)
+        goto cleanup;
+
+    ticket_info = k5calloc(ncreds + 1, sizeof(*ticket_info), &ret);
+    if (ticket_info == NULL)
+        goto cleanup;
+
+    tinfos = k5calloc(ncreds, sizeof(*tinfos), &ret);
+    if (tinfos == NULL)
+        goto cleanup;
+
+    /* For each credential in the list, decode the ticket and create a cred
+     * info structure using alias pointers. */
+    for (i = 0; i < ncreds; i++) {
+        ret = decode_krb5_ticket(&creds[i]->ticket, &tickets[i]);
+        if (ret)
             goto cleanup;
 
-        if ((retval = krb5_copy_addresses(context, ppcreds[i]->addresses,
-                                          &credenc.ticket_info[i]->caddrs)))
-            goto cleanup;
+        tinfos[i].magic = KV5M_CRED_INFO;
+        tinfos[i].times = creds[i]->times;
+        tinfos[i].flags = creds[i]->ticket_flags;
+        tinfos[i].session = &creds[i]->keyblock;
+        tinfos[i].client = creds[i]->client;
+        tinfos[i].server = creds[i]->server;
+        tinfos[i].caddrs = creds[i]->addresses;
+        ticket_info[i] = &tinfos[i];
     }
 
-    /*
-     * NULL terminate the lists.
-     */
-    pcred->tickets[i] = NULL;
-
-    /* encrypt the credential encrypted part */
-    retval = encrypt_credencpart(context, &credenc, key, &pcred->enc_part);
+    /* Encrypt the credential encrypted part. */
+    credenc.magic = KV5M_CRED_ENC_PART;
+    credenc.s_address = local_addr;
+    credenc.r_address = remote_addr;
+    credenc.nonce = rdata->seq;
+    credenc.usec = rdata->usec;
+    credenc.timestamp = rdata->timestamp;
+    credenc.ticket_info = ticket_info;
+    ret = encrypt_credencpart(context, &credenc, key, &enc);
+    if (ret)
+        goto cleanup;
+
+    /* Encode the KRB-CRED message. */
+    cred.magic = KV5M_CRED;
+    cred.tickets = tickets;
+    cred.enc_part = enc;
+    ret = encode_krb5_cred(&cred, der_out);
+    if (ret)
+        goto cleanup;
 
 cleanup:
-    krb5_free_cred_enc_part(context, &credenc);
-    return retval;
+    krb5_free_tickets(context, tickets);
+    krb5_free_data_contents(context, &enc.ciphertext);
+    free(tinfos);
+    free(ticket_info);
+    return ret;
 }
 
-/*----------------------- krb5_mk_ncred -----------------------*/
-
-/*
- * This functions takes as input an array of krb5_credentials, and
- * outputs an encoded KRB_CRED message suitable for krb5_rd_cred
- */
 krb5_error_code KRB5_CALLCONV
-krb5_mk_ncred(krb5_context context, krb5_auth_context auth_context,
-              krb5_creds **ppcreds, krb5_data **ppdata,
-              krb5_replay_data *outdata)
+krb5_mk_ncred(krb5_context context, krb5_auth_context authcon,
+              krb5_creds **creds, krb5_data **der_out,
+              krb5_replay_data *rdata_out)
 {
-    krb5_address * premote_fulladdr = NULL;
-    krb5_address * plocal_fulladdr = NULL;
-    krb5_address remote_fulladdr;
-    krb5_address local_fulladdr;
-    krb5_error_code     retval;
-    krb5_key            key;
-    krb5_replay_data    replaydata;
-    krb5_cred            * pcred;
-    krb5_int32          ncred;
-    krb5_boolean increased_sequence = FALSE;
+    krb5_error_code ret;
+    krb5_key key;
+    krb5_replay_data rdata;
+    krb5_data *der_krbcred = NULL;
+    krb5_address *local_addr, *remote_addr, lstorage, rstorage;
 
-    local_fulladdr.contents = 0;
-    remote_fulladdr.contents = 0;
-    memset(&replaydata, 0, sizeof(krb5_replay_data));
+    *der_out = NULL;
+    memset(&lstorage, 0, sizeof(lstorage));
+    memset(&rstorage, 0, sizeof(rstorage));
 
-    if (ppcreds == NULL)
+    if (creds == NULL)
         return KRB5KRB_AP_ERR_BADADDR;
 
-    /*
-     * Allocate memory for a NULL terminated list of tickets.
-     */
-    for (ncred = 0; ppcreds[ncred]; ncred++)
-        ;
-
-    if ((pcred = (krb5_cred *)calloc(1, sizeof(krb5_cred))) == NULL)
-        return ENOMEM;
-
-    if ((pcred->tickets
-         = (krb5_ticket **)calloc((size_t)ncred+1,
-                                  sizeof(krb5_ticket *))) == NULL) {
-        retval = ENOMEM;
-        goto error;
-    }
-
-    /* Get keyblock */
-    if ((key = auth_context->send_subkey) == NULL)
-        key = auth_context->key;
-
-    /* Get replay info */
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
-        (auth_context->rcache == NULL)) {
-        retval = KRB5_RC_REQUIRED;
-        goto error;
-    }
-
-    if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
-         (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
-        && (outdata == NULL)) {
-        /* Need a better error */
-        retval = KRB5_RC_REQUIRED;
-        goto error;
-    }
-
-    if ((retval = krb5_us_timeofday(context, &replaydata.timestamp,
-                                    &replaydata.usec)))
-        goto error;
-    if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) {
-        outdata->timestamp = replaydata.timestamp;
-        outdata->usec = replaydata.usec;
-    }
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
-        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
-        replaydata.seq = auth_context->local_seq_number++;
-        increased_sequence = TRUE;
-        if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)
-            outdata->seq = replaydata.seq;
-    }
-
-    if (auth_context->local_addr) {
-        if (auth_context->local_port) {
-            if ((retval = krb5_make_fulladdr(context, auth_context->local_addr,
-                                             auth_context->local_port,
-                                             &local_fulladdr)))
-                goto error;
-            plocal_fulladdr = &local_fulladdr;
-        } else {
-            plocal_fulladdr = auth_context->local_addr;
-        }
-    }
-
-    if (auth_context->remote_addr) {
-        if (auth_context->remote_port) {
-            if ((retval = krb5_make_fulladdr(context,auth_context->remote_addr,
-                                             auth_context->remote_port,
-                                             &remote_fulladdr)))
-                goto error;
-            premote_fulladdr = &remote_fulladdr;
-        } else {
-            premote_fulladdr = auth_context->remote_addr;
-        }
-    }
-
-    /* Setup creds structure */
-    if ((retval = krb5_mk_ncred_basic(context, ppcreds, ncred, key,
-                                      &replaydata, plocal_fulladdr,
-                                      premote_fulladdr, pcred))) {
-        goto error;
+    ret = k5_privsafe_gen_rdata(context, authcon, &rdata, rdata_out);
+    if (ret)
+        goto cleanup;
+    /* Historically we always set the timestamp, so keep doing that. */
+    if (rdata.timestamp == 0) {
+        ret = krb5_us_timeofday(context, &rdata.timestamp, &rdata.usec);
+        if (ret)
+            goto cleanup;
     }
 
-    if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
-        krb5_donot_replay replay;
+    ret = k5_privsafe_gen_addrs(context, authcon, &lstorage, &rstorage,
+                                &local_addr, &remote_addr);
+    if (ret)
+        goto cleanup;
 
-        if ((retval = krb5_gen_replay_name(context, auth_context->local_addr,
-                                           "_forw", &replay.client)))
-            goto error;
-
-        replay.server = "";             /* XXX */
-        replay.msghash = NULL;
-        replay.cusec = replaydata.usec;
-        replay.ctime = replaydata.timestamp;
-        if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
-            /* should we really error out here? XXX */
-            free(replay.client);
-            goto error;
-        }
-        free(replay.client);
-    }
+    key = (authcon->send_subkey != NULL) ? authcon->send_subkey : authcon->key;
+    ret = create_krbcred(context, creds, key, &rdata, local_addr, remote_addr,
+                         &der_krbcred);
+    if (ret)
+        goto cleanup;
 
-    /* Encode creds structure */
-    retval = encode_krb5_cred(pcred, ppdata);
+    ret = k5_privsafe_check_replay(context, authcon, authcon->local_addr,
+                                   "_forw", &rdata, FALSE);
+    if (ret)
+        goto cleanup;
 
-error:
-    free(local_fulladdr.contents);
-    free(remote_fulladdr.contents);
-    krb5_free_cred(context, pcred);
+    *der_out = der_krbcred;
+    der_krbcred = NULL;
+    if ((authcon->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
+        (authcon->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
+        authcon->local_seq_number++;
 
-    if (retval) {
-        if (increased_sequence)
-            auth_context->local_seq_number--;
+cleanup:
+    free(lstorage.contents);
+    free(rstorage.contents);
+    if (der_krbcred != NULL) {
+        zap(der_krbcred->data, der_krbcred->length);
+        krb5_free_data(context, der_krbcred);
     }
-    return retval;
+    return ret;
 }
 
-/*----------------------- krb5_mk_1cred -----------------------*/
-
-/*
- * A convenience function that calls krb5_mk_ncred.
- */
 krb5_error_code KRB5_CALLCONV
-krb5_mk_1cred(krb5_context context, krb5_auth_context auth_context,
-              krb5_creds *pcreds, krb5_data **ppdata,
-              krb5_replay_data *outdata)
+krb5_mk_1cred(krb5_context context, krb5_auth_context authcon,
+              krb5_creds *creds, krb5_data **der_out,
+              krb5_replay_data *rdata_out)
 {
     krb5_error_code retval;
-    krb5_creds **ppcreds;
+    krb5_creds **list;
 
-    if ((ppcreds = (krb5_creds **)malloc(sizeof(*ppcreds) * 2)) == NULL) {
+    list = calloc(2, sizeof(*list));
+    if (list == NULL)
         return ENOMEM;
-    }
-
-    ppcreds[0] = pcreds;
-    ppcreds[1] = NULL;
-
-    retval = krb5_mk_ncred(context, auth_context, ppcreds,
-                           ppdata, outdata);
 
-    free(ppcreds);
+    list[0] = creds;
+    list[1] = NULL;
+    retval = krb5_mk_ncred(context, authcon, list, der_out, rdata_out);
+    free(list);
     return retval;
 }
diff --git a/src/lib/krb5/krb/mk_priv.c b/src/lib/krb5/krb/mk_priv.c
index b3d9e68..d66ab8a 100644
--- a/src/lib/krb5/krb/mk_priv.c
+++ b/src/lib/krb5/krb/mk_priv.c
@@ -1,225 +1,153 @@
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/* lib/krb5/krb/mk_priv.c */
+/* lib/krb5/krb/mk_priv.c - definition of krb5_mk_priv() */
 /*
- * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991,2019 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
- * Export of this software from the United States of America may
- *   require a specific license from the United States Government.
- *   It is the responsibility of any person or organization contemplating
- *   export to obtain such a license before exporting.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission.  Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose.  It is provided "as is" without express
- * or implied warranty.
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "k5-int.h"
-#include "cleanup.h"
+#include "int-proto.h"
 #include "auth_con.h"
 
+/*
+ * Marshal a KRB-PRIV message into der_out, encrypted with key.  Use the
+ * timestamp and sequence number from rdata and the addresses from local_addr
+ * and remote_addr (the second of which may be NULL).  der_out should be freed
+ * by the caller when finished.
+ */
 static krb5_error_code
-mk_priv_basic(krb5_context context, const krb5_data *userdata,
-              krb5_key key, krb5_replay_data *replaydata,
-              krb5_address *local_addr, krb5_address *remote_addr,
-              krb5_data *cstate, krb5_data *outbuf)
+create_krbpriv(krb5_context context, const krb5_data *userdata,
+               krb5_key key, const krb5_replay_data *rdata,
+               krb5_address *local_addr, krb5_address *remote_addr,
+               krb5_data *cstate, krb5_data *der_out)
 {
-    krb5_enctype        enctype = krb5_k_key_enctype(context, key);
-    krb5_error_code     retval;
-    krb5_priv           privmsg;
-    krb5_priv_enc_part  privmsg_enc_part;
-    krb5_data           *scratch1, *scratch2;
-    size_t              enclen;
-
-    privmsg.enc_part.kvno = 0;  /* XXX allow user-set? */
+    krb5_enctype enctype = krb5_k_key_enctype(context, key);
+    krb5_error_code ret;
+    krb5_priv privmsg;
+    krb5_priv_enc_part encpart;
+    krb5_data *der_encpart, *der_krbpriv;
+    size_t enclen;
+
+    memset(&privmsg, 0, sizeof(privmsg));
+    privmsg.enc_part.kvno = 0;
     privmsg.enc_part.enctype = enctype;
-
-    privmsg_enc_part.user_data = *userdata;
-    privmsg_enc_part.s_address = local_addr;
-    privmsg_enc_part.r_address = remote_addr;
-
-    /* We should check too make sure one exists. */
-    privmsg_enc_part.timestamp  = replaydata->timestamp;
-    privmsg_enc_part.usec       = replaydata->usec;
-    privmsg_enc_part.seq_number = replaydata->seq;
-
-    /* start by encoding to-be-encrypted part of the message */
-    if ((retval = encode_krb5_enc_priv_part(&privmsg_enc_part, &scratch1)))
-        return retval;
+    encpart.user_data = *userdata;
+    encpart.s_address = local_addr;
+    encpart.r_address = remote_addr;
+    encpart.timestamp = rdata->timestamp;
+    encpart.usec = rdata->usec;
+    encpart.seq_number = rdata->seq;
+
+    /* Start by encoding the to-be-encrypted part of the message. */
+    ret = encode_krb5_enc_priv_part(&encpart, &der_encpart);
+    if (ret)
+        return ret;
 
     /* put together an eblock for this encryption */
-    if ((retval = krb5_c_encrypt_length(context, enctype,
-                                        scratch1->length, &enclen)))
-        goto clean_scratch;
-
-    privmsg.enc_part.ciphertext.length = enclen;
-    if (!(privmsg.enc_part.ciphertext.data =
-          malloc(privmsg.enc_part.ciphertext.length))) {
-        retval = ENOMEM;
-        goto clean_scratch;
+    ret = krb5_c_encrypt_length(context, enctype, der_encpart->length,
+                                &enclen);
+    if (ret)
+        goto cleanup;
+
+    ret = alloc_data(&privmsg.enc_part.ciphertext, enclen);
+    if (ret)
+        goto cleanup;
+
+    ret = krb5_k_encrypt(context, key, KRB5_KEYUSAGE_KRB_PRIV_ENCPART,
+                         (cstate->length > 0) ? cstate : NULL, der_encpart,
+                         &privmsg.enc_part);
+    if (ret)
+        goto cleanup;
+
+    ret = encode_krb5_priv(&privmsg, &der_krbpriv);
+    if (ret)
+        goto cleanup;
+
+    *der_out = *der_krbpriv;
+    free(der_krbpriv);
+
+cleanup:
+    zapfree(privmsg.enc_part.ciphertext.data,
+            privmsg.enc_part.ciphertext.length);
+    if (der_encpart != NULL) {
+        zap(der_encpart->data, der_encpart->length);
+        krb5_free_data(context, der_encpart);
     }
-
-    if ((retval = krb5_k_encrypt(context, key,
-                                 KRB5_KEYUSAGE_KRB_PRIV_ENCPART,
-                                 (cstate->length > 0) ? cstate : NULL,
-                                 scratch1, &privmsg.enc_part)))
-        goto clean_encpart;
-
-    if ((retval = encode_krb5_priv(&privmsg, &scratch2)))
-        goto clean_encpart;
-
-    *outbuf = *scratch2;
-    free(scratch2);
-    retval = 0;
-
-clean_encpart:
-    memset(privmsg.enc_part.ciphertext.data, 0,
-           privmsg.enc_part.ciphertext.length);
-    free(privmsg.enc_part.ciphertext.data);
-    privmsg.enc_part.ciphertext.length = 0;
-    privmsg.enc_part.ciphertext.data = 0;
-
-clean_scratch:
-    memset(scratch1->data, 0, scratch1->length);
-    krb5_free_data(context, scratch1);
-
-    return retval;
+    return ret;
 }
 
 
 krb5_error_code KRB5_CALLCONV
-krb5_mk_priv(krb5_context context, krb5_auth_context auth_context,
-             const krb5_data *userdata, krb5_data *outbuf,
-             krb5_replay_data *outdata)
+krb5_mk_priv(krb5_context context, krb5_auth_context authcon,
+             const krb5_data *userdata, krb5_data *der_out,
+             krb5_replay_data *rdata_out)
 {
-    krb5_error_code       retval;
-    krb5_key              key;
-    krb5_replay_data      replaydata;
-    krb5_data             buf = empty_data();
-
-    *outbuf = empty_data();
-
-    /* Clear replaydata block */
-    memset(&replaydata, 0, sizeof(krb5_replay_data));
-
-    /* Get keyblock */
-    if ((key = auth_context->send_subkey) == NULL)
-        key = auth_context->key;
-
-    /* Get replay info */
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
-        (auth_context->rcache == NULL))
-        return KRB5_RC_REQUIRED;
-
-    if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
-         (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
-        (outdata == NULL))
-        /* Need a better error */
-        return KRB5_RC_REQUIRED;
-
-    if (!auth_context->local_addr)
+    krb5_error_code ret;
+    krb5_key key;
+    krb5_replay_data rdata;
+    krb5_data der_krbpriv = empty_data();
+    krb5_address *local_addr, *remote_addr, lstorage, rstorage;
+
+    *der_out = empty_data();
+    memset(&lstorage, 0, sizeof(lstorage));
+    memset(&rstorage, 0, sizeof(rstorage));
+    if (!authcon->local_addr)
         return KRB5_LOCAL_ADDR_REQUIRED;
 
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) ||
-        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME)) {
-        if ((retval = krb5_us_timeofday(context, &replaydata.timestamp,
-                                        &replaydata.usec)))
-            return retval;
-        if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) {
-            outdata->timestamp = replaydata.timestamp;
-            outdata->usec = replaydata.usec;
-        }
-    }
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
-        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
-        replaydata.seq = auth_context->local_seq_number++;
-        if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)
-            outdata->seq = replaydata.seq;
-    }
-
-    {
-        krb5_address * premote_fulladdr = NULL;
-        krb5_address * plocal_fulladdr;
-        krb5_address remote_fulladdr;
-        krb5_address local_fulladdr;
-        CLEANUP_INIT(2);
-
-        if (auth_context->local_port) {
-            if (!(retval = krb5_make_fulladdr(context, auth_context->local_addr,
-                                              auth_context->local_port,
-                                              &local_fulladdr))) {
-                CLEANUP_PUSH(local_fulladdr.contents, free);
-                plocal_fulladdr = &local_fulladdr;
-            } else {
-                goto error;
-            }
-        } else {
-            plocal_fulladdr = auth_context->local_addr;
-        }
-
-        if (auth_context->remote_addr) {
-            if (auth_context->remote_port) {
-                if (!(retval = krb5_make_fulladdr(context,auth_context->remote_addr,
-                                                  auth_context->remote_port,
-                                                  &remote_fulladdr))){
-                    CLEANUP_PUSH(remote_fulladdr.contents, free);
-                    premote_fulladdr = &remote_fulladdr;
-                } else {
-                    CLEANUP_DONE();
-                    goto error;
-                }
-            } else {
-                premote_fulladdr = auth_context->remote_addr;
-            }
-        }
-
-        if ((retval = mk_priv_basic(context, userdata, key, &replaydata,
-                                    plocal_fulladdr, premote_fulladdr,
-                                    &auth_context->cstate, &buf))) {
-            CLEANUP_DONE();
-            goto error;
-        }
-
-        CLEANUP_DONE();
-    }
-
-    if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
-        krb5_donot_replay replay;
-
-        if ((retval = krb5_gen_replay_name(context, auth_context->local_addr,
-                                           "_priv", &replay.client)))
-            goto error;
-
-        replay.server = "";             /* XXX */
-        replay.msghash = NULL;
-        replay.cusec = replaydata.usec;
-        replay.ctime = replaydata.timestamp;
-        if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
-            /* should we really error out here? XXX */
-            free(replay.client);
-            goto error;
-        }
-        free(replay.client);
-    }
-
-    *outbuf = buf;
-    return 0;
-
-error:
-    krb5_free_data_contents(context, &buf);
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
-        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
-        auth_context->local_seq_number--;
-
-    return retval;
+    ret = k5_privsafe_gen_rdata(context, authcon, &rdata, rdata_out);
+    if (ret)
+        goto cleanup;
+
+    ret = k5_privsafe_gen_addrs(context, authcon, &lstorage, &rstorage,
+                                &local_addr, &remote_addr);
+    if (ret)
+        goto cleanup;
+
+    key = (authcon->send_subkey != NULL) ? authcon->send_subkey : authcon->key;
+    ret = create_krbpriv(context, userdata, key, &rdata, local_addr,
+                         remote_addr, &authcon->cstate, &der_krbpriv);
+    if (ret)
+        goto cleanup;
+
+    ret = k5_privsafe_check_replay(context, authcon, authcon->local_addr,
+                                   "_priv", &rdata, FALSE);
+    if (ret)
+        goto cleanup;
+
+    *der_out = der_krbpriv;
+    der_krbpriv = empty_data();
+    if ((authcon->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
+        (authcon->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
+        authcon->local_seq_number++;
+
+cleanup:
+    krb5_free_data_contents(context, &der_krbpriv);
+    free(lstorage.contents);
+    free(rstorage.contents);
+    return ret;
 }
diff --git a/src/lib/krb5/krb/mk_safe.c b/src/lib/krb5/krb/mk_safe.c
index 1453365..02ee725 100644
--- a/src/lib/krb5/krb/mk_safe.c
+++ b/src/lib/krb5/krb/mk_safe.c
@@ -1,59 +1,56 @@
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/* lib/krb5/krb/mk_safe.c */
+/* lib/krb5/krb/mk_safe.c - definition of krb5_mk_safe() */
 /*
- * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991,2019 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
- * Export of this software from the United States of America may
- *   require a specific license from the United States Government.
- *   It is the responsibility of any person or organization contemplating
- *   export to obtain such a license before exporting.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission.  Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose.  It is provided "as is" without express
- * or implied warranty.
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "k5-int.h"
-#include "cleanup.h"
+#include "int-proto.h"
 #include "auth_con.h"
 
 /*
-  Formats a KRB_SAFE message into outbuf.
-
-  userdata is formatted as the user data in the message.
-  sumtype specifies the encryption type; key specifies the key which
-  might be used to seed the checksum; sender_addr and recv_addr specify
-  the full addresses (host and port) of the sender and receiver.
-  The host portion of sender_addr is used to form the addresses used in the
-  KRB_SAFE message.
-
-  The outbuf buffer storage is allocated, and should be freed by the
-  caller when finished.
-
-  returns system errors
-*/
+ * Marshal a KRB-SAFE message into der_out, with a keyed checksum of type
+ * sumtype.  Use the timestamp and sequence number from rdata and the addresses
+ * from local_addr and remote_addr (the second of which may be NULL).  der_out
+ * should be freed by the caller when finished.
+ */
 static krb5_error_code
-krb5_mk_safe_basic(krb5_context context, const krb5_data *userdata,
-                   krb5_key key, krb5_replay_data *replaydata,
-                   krb5_address *local_addr, krb5_address *remote_addr,
-                   krb5_cksumtype sumtype, krb5_data *outbuf)
+create_krbsafe(krb5_context context, const krb5_data *userdata, krb5_key key,
+               const krb5_replay_data *rdata, krb5_address *local_addr,
+               krb5_address *remote_addr, krb5_cksumtype sumtype,
+               krb5_data *der_out)
 {
-    krb5_error_code retval;
+    krb5_error_code ret;
     krb5_safe safemsg;
     krb5_octet zero_octet = 0;
     krb5_checksum safe_checksum;
-    krb5_data *scratch1, *scratch2;
+    krb5_data *der_krbsafe;
 
     if (sumtype && !krb5_c_valid_cksumtype(sumtype))
         return KRB5_PROG_SUMTYPE_NOSUPP;
@@ -61,48 +58,40 @@ krb5_mk_safe_basic(krb5_context context, const krb5_data *userdata,
         return KRB5KRB_AP_ERR_INAPP_CKSUM;
 
     safemsg.user_data = *userdata;
-    safemsg.s_address = (krb5_address *) local_addr;
-    safemsg.r_address = (krb5_address *) remote_addr;
-
-    /* We should check too make sure one exists. */
-    safemsg.timestamp  = replaydata->timestamp;
-    safemsg.usec       = replaydata->usec;
-    safemsg.seq_number = replaydata->seq;
-
-    /*
-     * To do the checksum stuff, we need to encode the message with a
-     * zero-length zero-type checksum, then checksum the encoding, then
-     * re-encode with the checksum.
-     */
+    safemsg.s_address = local_addr;
+    safemsg.r_address = remote_addr;
+    safemsg.timestamp = rdata->timestamp;
+    safemsg.usec = rdata->usec;
+    safemsg.seq_number = rdata->seq;
 
+    /* Encode the message with a zero-length zero-type checksum. */
     safe_checksum.length = 0;
     safe_checksum.checksum_type = 0;
     safe_checksum.contents = &zero_octet;
-
     safemsg.checksum = &safe_checksum;
-
-    if ((retval = encode_krb5_safe(&safemsg, &scratch1)))
-        return retval;
-
-    if ((retval = krb5_k_make_checksum(context, sumtype, key,
-                                       KRB5_KEYUSAGE_KRB_SAFE_CKSUM,
-                                       scratch1, &safe_checksum)))
-        goto cleanup_checksum;
-
+    ret = encode_krb5_safe(&safemsg, &der_krbsafe);
+    if (ret)
+        return ret;
+
+    /* Checksum the encoding. */
+    ret = krb5_k_make_checksum(context, sumtype, key,
+                               KRB5_KEYUSAGE_KRB_SAFE_CKSUM, der_krbsafe,
+                               &safe_checksum);
+    zap(der_krbsafe->data, der_krbsafe->length);
+    krb5_free_data(context, der_krbsafe);
+    if (ret)
+        return ret;
+
+    /* Encode the message again with the real checksum. */
     safemsg.checksum = &safe_checksum;
-    if ((retval = encode_krb5_safe(&safemsg, &scratch2))) {
-        goto cleanup_checksum;
-    }
-    *outbuf = *scratch2;
-    free(scratch2);
-    retval = 0;
+    ret = encode_krb5_safe(&safemsg, &der_krbsafe);
+    krb5_free_checksum_contents(context, &safe_checksum);
+    if (ret)
+        return ret;
 
-cleanup_checksum:
-    free(safe_checksum.contents);
-
-    memset(scratch1->data, 0, scratch1->length);
-    krb5_free_data(context, scratch1);
-    return retval;
+    *der_out = *der_krbsafe;
+    free(der_krbsafe);
+    return 0;
 }
 
 /* Return the checksum type for the KRB-SAFE message, or 0 to use the enctype's
@@ -111,15 +100,14 @@ static krb5_cksumtype
 safe_cksumtype(krb5_context context, krb5_auth_context auth_context,
                krb5_enctype enctype)
 {
-    krb5_error_code retval;
+    krb5_error_code ret;
     unsigned int nsumtypes, i;
     krb5_cksumtype *sumtypes;
 
     /* Use the auth context's safe_cksumtype if it is valid for the enctype.
      * Otherwise return 0 for the mandatory checksum. */
-    retval = krb5_c_keyed_checksum_types(context, enctype, &nsumtypes,
-                                         &sumtypes);
-    if (retval != 0)
+    ret = krb5_c_keyed_checksum_types(context, enctype, &nsumtypes, &sumtypes);
+    if (ret != 0)
         return 0;
     for (i = 0; i < nsumtypes; i++) {
         if (auth_context->safe_cksumtype == sumtypes[i])
@@ -130,129 +118,53 @@ safe_cksumtype(krb5_context context, krb5_auth_context auth_context,
 }
 
 krb5_error_code KRB5_CALLCONV
-krb5_mk_safe(krb5_context context, krb5_auth_context auth_context,
-             const krb5_data *userdata, krb5_data *outbuf,
-             krb5_replay_data *outdata)
+krb5_mk_safe(krb5_context context, krb5_auth_context authcon,
+             const krb5_data *userdata, krb5_data *der_out,
+             krb5_replay_data *rdata_out)
 {
-    krb5_error_code       retval;
-    krb5_key              key;
-    krb5_replay_data      replaydata;
-    krb5_data             buf = empty_data();
-
-    *outbuf = empty_data();
-
-    /* Clear replaydata block */
-    memset(&replaydata, 0, sizeof(krb5_replay_data));
-
-    /* Get key */
-    if ((key = auth_context->send_subkey) == NULL)
-        key = auth_context->key;
-
-    /* Get replay info */
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
-        (auth_context->rcache == NULL))
-        return KRB5_RC_REQUIRED;
-
-    if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
-         (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
-        (outdata == NULL))
-        /* Need a better error */
-        return KRB5_RC_REQUIRED;
-
-    if (!auth_context->local_addr)
+    krb5_error_code ret;
+    krb5_key key;
+    krb5_replay_data rdata;
+    krb5_data der_krbsafe = empty_data();
+    krb5_address *local_addr, *remote_addr, lstorage, rstorage;
+    krb5_cksumtype sumtype;
+
+    *der_out = empty_data();
+    memset(&lstorage, 0, sizeof(lstorage));
+    memset(&rstorage, 0, sizeof(rstorage));
+    if (authcon->local_addr == NULL)
         return KRB5_LOCAL_ADDR_REQUIRED;
 
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) ||
-        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME)) {
-        if ((retval = krb5_us_timeofday(context, &replaydata.timestamp,
-                                        &replaydata.usec)))
-            return retval;
-        if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) {
-            outdata->timestamp = replaydata.timestamp;
-            outdata->usec = replaydata.usec;
-        }
-    }
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
-        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
-        replaydata.seq = auth_context->local_seq_number++;
-        if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)
-            outdata->seq = replaydata.seq;
-    }
-
-    {
-        krb5_address * premote_fulladdr = NULL;
-        krb5_address * plocal_fulladdr;
-        krb5_address remote_fulladdr;
-        krb5_address local_fulladdr;
-        krb5_cksumtype sumtype;
-
-        CLEANUP_INIT(2);
-
-        if (auth_context->local_port) {
-            if (!(retval = krb5_make_fulladdr(context, auth_context->local_addr,
-                                              auth_context->local_port,
-                                              &local_fulladdr))){
-                CLEANUP_PUSH(local_fulladdr.contents, free);
-                plocal_fulladdr = &local_fulladdr;
-            } else {
-                goto error;
-            }
-        } else {
-            plocal_fulladdr = auth_context->local_addr;
-        }
-
-        if (auth_context->remote_addr) {
-            if (auth_context->remote_port) {
-                if (!(retval = krb5_make_fulladdr(context,auth_context->remote_addr,
-                                                  auth_context->remote_port,
-                                                  &remote_fulladdr))){
-                    CLEANUP_PUSH(remote_fulladdr.contents, free);
-                    premote_fulladdr = &remote_fulladdr;
-                } else {
-                    CLEANUP_DONE();
-                    goto error;
-                }
-            } else {
-                premote_fulladdr = auth_context->remote_addr;
-            }
-        }
-
-        sumtype = safe_cksumtype(context, auth_context, key->keyblock.enctype);
-        if ((retval = krb5_mk_safe_basic(context, userdata, key, &replaydata,
-                                         plocal_fulladdr, premote_fulladdr,
-                                         sumtype, &buf))) {
-            CLEANUP_DONE();
-            goto error;
-        }
-
-        CLEANUP_DONE();
-    }
-
-    if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
-        krb5_donot_replay replay;
-
-        if ((retval = krb5_gen_replay_name(context, auth_context->local_addr,
-                                           "_safe", &replay.client)))
-            goto error;
-
-        replay.server = "";             /* XXX */
-        replay.msghash = NULL;
-        replay.cusec = replaydata.usec;
-        replay.ctime = replaydata.timestamp;
-        /* should we really error out here? XXX */
-        if ((retval = krb5_rc_store(context, auth_context->rcache, &replay)))
-            goto error;
-        free(replay.client);
-    }
-
-    *outbuf = buf;
-    return 0;
-
-error:
-    krb5_free_data_contents(context, &buf);
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
-        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
-        auth_context->local_seq_number--;
-
-    return retval;
+    ret = k5_privsafe_gen_rdata(context, authcon, &rdata, rdata_out);
+    if (ret)
+        goto cleanup;
+
+    ret = k5_privsafe_gen_addrs(context, authcon, &lstorage, &rstorage,
+                                &local_addr, &remote_addr);
+    if (ret)
+        goto cleanup;
+
+    key = (authcon->send_subkey != NULL) ? authcon->send_subkey : authcon->key;
+    sumtype = safe_cksumtype(context, authcon, key->keyblock.enctype);
+    ret = create_krbsafe(context, userdata, key, &rdata, local_addr,
+                         remote_addr, sumtype, &der_krbsafe);
+    if (ret)
+        goto cleanup;
+
+    ret = k5_privsafe_check_replay(context, authcon, authcon->local_addr,
+                                   "_safe", &rdata, FALSE);
+    if (ret)
+        goto cleanup;
+
+    *der_out = der_krbsafe;
+    der_krbsafe = empty_data();
+    if ((authcon->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
+        (authcon->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
+        authcon->local_seq_number++;
+
+cleanup:
+    krb5_free_data_contents(context, &der_krbsafe);
+    free(lstorage.contents);
+    free(rstorage.contents);
+    return ret;
 }
diff --git a/src/lib/krb5/krb/privsafe.c b/src/lib/krb5/krb/privsafe.c
index 264b076..a1e5230 100644
--- a/src/lib/krb5/krb/privsafe.c
+++ b/src/lib/krb5/krb/privsafe.c
@@ -1,33 +1,145 @@
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /* lib/krb5/krb/privsafe.c - Shared logic for KRB-SAFE and KRB-PRIV messages */
 /*
- * Copyright (C) 2011 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2011,2019 by the Massachusetts Institute of Technology.
  * All rights reserved.
  *
- * Export of this software from the United States of America may
- *   require a specific license from the United States Government.
- *   It is the responsibility of any person or organization contemplating
- *   export to obtain such a license before exporting.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission.  Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose.  It is provided "as is" without express
- * or implied warranty.
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "k5-int.h"
 #include "int-proto.h"
 #include "auth_con.h"
 
+krb5_error_code
+k5_privsafe_gen_rdata(krb5_context context, krb5_auth_context authcon,
+                      krb5_replay_data *rdata, krb5_replay_data *caller_rdata)
+{
+    krb5_error_code ret;
+    krb5_int32 flags = authcon->auth_context_flags;
+    krb5_boolean do_time = !!(flags & KRB5_AUTH_CONTEXT_DO_TIME);
+    krb5_boolean do_sequence = !!(flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+    krb5_boolean ret_time = !!(flags & KRB5_AUTH_CONTEXT_RET_TIME);
+    krb5_boolean ret_sequence = !!(flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE);
+
+    memset(rdata, 0, sizeof(*rdata));
+    if ((ret_time || ret_sequence) && caller_rdata == NULL)
+        return KRB5_RC_REQUIRED;
+
+    if (do_time || ret_time) {
+        ret = krb5_us_timeofday(context, &rdata->timestamp, &rdata->usec);
+        if (ret)
+            return ret;
+        if (ret_time) {
+            caller_rdata->timestamp = rdata->timestamp;
+            caller_rdata->usec = rdata->usec;
+        }
+    }
+    if (do_sequence || ret_sequence) {
+        rdata->seq = authcon->local_seq_number;
+        if (ret_sequence)
+            caller_rdata->seq = rdata->seq;
+    }
+
+    return 0;
+}
+
+krb5_error_code
+k5_privsafe_gen_addrs(krb5_context context, krb5_auth_context authcon,
+                      krb5_address *lstorage, krb5_address *rstorage,
+                      krb5_address **local_out, krb5_address **remote_out)
+{
+    krb5_error_code ret;
+
+    *local_out = NULL;
+    *remote_out = NULL;
+
+    if (authcon->local_addr != NULL) {
+        if (authcon->local_port != NULL) {
+            ret = krb5_make_fulladdr(context, authcon->local_addr,
+                                     authcon->local_port, lstorage);
+            if (ret)
+                return ret;
+            *local_out = lstorage;
+        } else {
+            *local_out = authcon->local_addr;
+        }
+    }
+
+    if (authcon->remote_addr != NULL) {
+        if (authcon->remote_port != NULL) {
+            ret = krb5_make_fulladdr(context, authcon->remote_addr,
+                                     authcon->remote_port, rstorage);
+            if (ret)
+                return ret;
+            *remote_out = rstorage;
+        } else {
+            *remote_out = authcon->remote_addr;
+        }
+    }
+
+    return 0;
+}
+
+krb5_error_code
+k5_privsafe_check_replay(krb5_context context, krb5_auth_context authcon,
+                         krb5_address *addr, const char *uniq,
+                         const krb5_replay_data *rdata,
+                         krb5_boolean check_time)
+{
+    krb5_error_code ret;
+    krb5_donot_replay replay;
+    char *client = NULL;
+
+    if (!(authcon->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME))
+        return 0;
+
+    if (authcon->rcache == NULL)
+        return KRB5_RC_REQUIRED;
+
+    if (check_time) {
+        ret = krb5_check_clockskew(context, rdata->timestamp);
+        if (ret)
+            return ret;
+    }
+
+    ret = krb5_gen_replay_name(context, addr, uniq, &client);
+    if (ret)
+        return ret;
+
+    replay.client = client;
+    replay.server = "";
+    replay.msghash = NULL;
+    replay.cusec = rdata->usec;
+    replay.ctime = rdata->timestamp;
+    ret = krb5_rc_store(context, authcon->rcache, &replay);
+    free(replay.client);
+    return ret;
+}
+
 /*
  * k5_privsafe_check_seqnum
  *
diff --git a/src/lib/krb5/krb/rd_cred.c b/src/lib/krb5/krb/rd_cred.c
index b08108b..7fdd103 100644
--- a/src/lib/krb5/krb/rd_cred.c
+++ b/src/lib/krb5/krb/rd_cred.c
@@ -4,37 +4,40 @@
  * Copyright 1994-2009,2014 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
- * Export of this software from the United States of America may
- *   require a specific license from the United States Government.
- *   It is the responsibility of any person or organization contemplating
- *   export to obtain such a license before exporting.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission.  Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose.  It is provided "as is" without express
- * or implied warranty.
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "k5-int.h"
-#include "cleanup.h"
+#include "int-proto.h"
 #include "auth_con.h"
 
-#include <stdlib.h>
-#include <errno.h>
-
 /*
  * Decrypt and decode the enc_part of a krb5_cred using the receiving subkey or
  * the session key of authcon.  If neither key is present, ctext->ciphertext is
- * assumed to be unencrypted plain text.
+ * assumed to be unencrypted plain text (RFC 6448).
  */
 static krb5_error_code
 decrypt_encpart(krb5_context context, krb5_enc_data *ctext,
@@ -145,7 +148,7 @@ krb5_rd_cred(krb5_context context, krb5_auth_context authcon,
     krb5_creds **credlist = NULL;
     krb5_cred *krbcred = NULL;
     krb5_cred_enc_part *encpart = NULL;
-    krb5_donot_replay replay;
+    krb5_replay_data rdata;
     const krb5_int32 flags = authcon->auth_context_flags;
 
     *creds_out = NULL;
@@ -170,25 +173,13 @@ krb5_rd_cred(krb5_context context, krb5_auth_context authcon,
     if (ret)
         goto cleanup;
 
-    if (flags & KRB5_AUTH_CONTEXT_DO_TIME) {
-        ret = krb5_check_clockskew(context, encpart->timestamp);
-        if (ret)
-            goto cleanup;
-
-        ret = krb5_gen_replay_name(context, authcon->remote_addr, "_forw",
-                                   &replay.client);
-        if (ret)
-            goto cleanup;
-
-        replay.server = "";
-        replay.msghash = NULL;
-        replay.cusec = encpart->usec;
-        replay.ctime = encpart->timestamp;
-        ret = krb5_rc_store(context, authcon->rcache, &replay);
-        free(replay.client);
-        if (ret)
-            goto cleanup;
-    }
+    rdata.timestamp = encpart->timestamp;
+    rdata.usec = encpart->usec;
+    rdata.seq = encpart->nonce;
+    ret = k5_privsafe_check_replay(context, authcon, authcon->remote_addr,
+                                   "_forw", &rdata, TRUE);
+    if (ret)
+        goto cleanup;
 
     if (flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
         if (authcon->remote_seq_number != (uint32_t)encpart->nonce) {
diff --git a/src/lib/krb5/krb/rd_priv.c b/src/lib/krb5/krb/rd_priv.c
index 2912ab1b..34eb656 100644
--- a/src/lib/krb5/krb/rd_priv.c
+++ b/src/lib/krb5/krb/rd_priv.c
@@ -1,27 +1,33 @@
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/* lib/krb5/krb/rd_priv.c */
+/* lib/krb5/krb/rd_priv.c - krb5_rd_priv() */
 /*
- * Copyright 1990,1991,2007 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991,2007,2019 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
- * Export of this software from the United States of America may
- *   require a specific license from the United States Government.
- *   It is the responsibility of any person or organization contemplating
- *   export to obtain such a license before exporting.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission.  Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose.  It is provided "as is" without express
- * or implied warranty.
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "k5-int.h"
@@ -29,160 +35,112 @@
 #include "auth_con.h"
 
 /*
-
-  Parses a KRB_PRIV message from inbuf, placing the confidential user
-  data in *outbuf.
-
-  key specifies the key to be used for decryption of the message.
-
-  outbuf points to allocated storage which the caller should
-  free when finished.
-
-  Returns system errors, integrity errors.
-
-*/
-
+ * Unmarshal a KRB-PRIV message from der_krbpriv, placing the confidential user
+ * data in *userdata_out and replay data in *rdata_out.  The caller should free
+ * *userdata_out when finished.
+ */
 static krb5_error_code
-rd_priv_basic(krb5_context context, krb5_auth_context ac,
-              const krb5_data *inbuf, const krb5_key key,
-              krb5_replay_data *replaydata, krb5_data *outbuf)
+read_krbpriv(krb5_context context, krb5_auth_context authcon,
+             const krb5_data *der_krbpriv, const krb5_key key,
+             krb5_replay_data *rdata_out, krb5_data *userdata_out)
 {
-    krb5_error_code       retval;
-    krb5_priv           * privmsg;
-    krb5_data             scratch;
-    krb5_priv_enc_part  * privmsg_enc_part;
-    krb5_data             *iv = NULL;
+    krb5_error_code ret;
+    krb5_priv *privmsg = NULL;
+    krb5_data plaintext = empty_data();
+    krb5_priv_enc_part *encpart = NULL;
+    krb5_data *cstate;
 
-    if (!krb5_is_krb_priv(inbuf))
+    if (!krb5_is_krb_priv(der_krbpriv))
         return KRB5KRB_AP_ERR_MSG_TYPE;
 
     /* decode private message */
-    if ((retval = decode_krb5_priv(inbuf, &privmsg)))
-        return retval;
-
-    if (ac->cstate.length > 0)
-        iv = &ac->cstate;
-
-    scratch.length = privmsg->enc_part.ciphertext.length;
-    if (!(scratch.data = malloc(scratch.length))) {
-        retval = ENOMEM;
-        goto cleanup_privmsg;
-    }
-
-    if ((retval = krb5_k_decrypt(context, key,
-                                 KRB5_KEYUSAGE_KRB_PRIV_ENCPART, iv,
-                                 &privmsg->enc_part, &scratch)))
-        goto cleanup_scratch;
-
-    /*  now decode the decrypted stuff */
-    if ((retval = decode_krb5_enc_priv_part(&scratch, &privmsg_enc_part)))
-        goto cleanup_scratch;
-
-    retval = k5_privsafe_check_addrs(context, ac, privmsg_enc_part->s_address,
-                                     privmsg_enc_part->r_address);
-    if (retval)
-        goto cleanup_data;
-
-    replaydata->timestamp = privmsg_enc_part->timestamp;
-    replaydata->usec = privmsg_enc_part->usec;
-    replaydata->seq = privmsg_enc_part->seq_number;
-
-    /* everything is ok - return data to the user */
-    *outbuf = privmsg_enc_part->user_data;
-    retval = 0;
-
-cleanup_data:;
-    if (retval == 0)
-        privmsg_enc_part->user_data.data = 0;
-    krb5_free_priv_enc_part(context, privmsg_enc_part);
-
-cleanup_scratch:;
-    memset(scratch.data, 0, scratch.length);
-    free(scratch.data);
-
-cleanup_privmsg:;
-    free(privmsg->enc_part.ciphertext.data);
-    free(privmsg);
-
-    return retval;
+    ret = decode_krb5_priv(der_krbpriv, &privmsg);
+    if (ret)
+        return ret;
+
+    ret = alloc_data(&plaintext, privmsg->enc_part.ciphertext.length);
+    if (ret)
+        goto cleanup;
+
+    cstate = (authcon->cstate.length > 0) ? &authcon->cstate : NULL;
+    ret = krb5_k_decrypt(context, key, KRB5_KEYUSAGE_KRB_PRIV_ENCPART, cstate,
+                         &privmsg->enc_part, &plaintext);
+    if (ret)
+        goto cleanup;
+
+    ret = decode_krb5_enc_priv_part(&plaintext, &encpart);
+    if (ret)
+        goto cleanup;
+
+    ret = k5_privsafe_check_addrs(context, authcon, encpart->s_address,
+                                  encpart->r_address);
+    if (ret)
+        goto cleanup;
+
+    rdata_out->timestamp = encpart->timestamp;
+    rdata_out->usec = encpart->usec;
+    rdata_out->seq = encpart->seq_number;
+
+    *userdata_out = encpart->user_data;
+    encpart->user_data.data = NULL;
+
+cleanup:
+    krb5_free_priv_enc_part(context, encpart);
+    krb5_free_priv(context, privmsg);
+    zapfree(plaintext.data, plaintext.length);
+    return ret;
 }
 
 krb5_error_code KRB5_CALLCONV
-krb5_rd_priv(krb5_context context, krb5_auth_context auth_context,
-             const krb5_data *inbuf, krb5_data *outbuf,
-             krb5_replay_data *outdata)
+krb5_rd_priv(krb5_context context, krb5_auth_context authcon,
+             const krb5_data *inbuf, krb5_data *userdata_out,
+             krb5_replay_data *rdata_out)
 {
-    krb5_error_code       retval;
-    krb5_key              key;
-    krb5_replay_data      replaydata;
-
-    /* Get key */
-    if ((key = auth_context->recv_subkey) == NULL)
-        key = auth_context->key;
-
-    if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
-         (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
-        (outdata == NULL))
-        /* Need a better error */
-        return KRB5_RC_REQUIRED;
+    krb5_error_code ret;
+    krb5_key key;
+    krb5_replay_data rdata;
+    krb5_data userdata = empty_data();
+    const krb5_int32 flags = authcon->auth_context_flags;
 
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
-        (auth_context->remote_addr == NULL))
-        return KRB5_REMOTE_ADDR_REQUIRED;
+    *userdata_out = empty_data();
 
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
-        (auth_context->rcache == NULL))
+    if (((flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
+         (flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && rdata_out == NULL)
         return KRB5_RC_REQUIRED;
 
-    memset(&replaydata, 0, sizeof(replaydata));
-    retval = rd_priv_basic(context, auth_context, inbuf, key, &replaydata,
-                           outbuf);
-    if (retval)
-        return retval;
-
-    if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
-        krb5_donot_replay replay;
-
-        if ((retval = krb5_check_clockskew(context, replaydata.timestamp)))
-            goto error;
-
-        if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr,
-                                           "_priv", &replay.client)))
-            goto error;
-
-        replay.server = "";             /* XXX */
-        replay.msghash = NULL;
-        replay.cusec = replaydata.usec;
-        replay.ctime = replaydata.timestamp;
-        if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
-            free(replay.client);
-            goto error;
-        }
-        free(replay.client);
-    }
+    if ((flags & KRB5_AUTH_CONTEXT_DO_TIME) && authcon->remote_addr == NULL)
+        return KRB5_REMOTE_ADDR_REQUIRED;
 
-    if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
-        if (!k5_privsafe_check_seqnum(context, auth_context, replaydata.seq)) {
-            retval =  KRB5KRB_AP_ERR_BADORDER;
-            goto error;
+    key = (authcon->recv_subkey != NULL) ? authcon->recv_subkey : authcon->key;
+    memset(&rdata, 0, sizeof(rdata));
+    ret = read_krbpriv(context, authcon, inbuf, key, &rdata, &userdata);
+    if (ret)
+        goto cleanup;
+
+    ret = k5_privsafe_check_replay(context, authcon, authcon->remote_addr,
+                                   "_priv", &rdata, TRUE);
+    if (ret)
+        goto cleanup;
+
+    if (flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+        if (!k5_privsafe_check_seqnum(context, authcon, rdata.seq)) {
+            ret = KRB5KRB_AP_ERR_BADORDER;
+            goto cleanup;
         }
-        auth_context->remote_seq_number++;
+        authcon->remote_seq_number++;
     }
 
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
-        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
-        outdata->timestamp = replaydata.timestamp;
-        outdata->usec = replaydata.usec;
-        outdata->seq = replaydata.seq;
+    if ((flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
+        (flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
+        rdata_out->timestamp = rdata.timestamp;
+        rdata_out->usec = rdata.usec;
+        rdata_out->seq = rdata.seq;
     }
 
-    /* everything is ok - return data to the user */
-    return 0;
-
-error:;
-    free(outbuf->data);
-    outbuf->length = 0;
-    outbuf->data = NULL;
+    *userdata_out = userdata;
+    userdata = empty_data();
 
-    return retval;
+cleanup:
+    krb5_free_data_contents(context, &userdata);
+    return ret;
 }
diff --git a/src/lib/krb5/krb/rd_safe.c b/src/lib/krb5/krb/rd_safe.c
index 5d2cee2..eab7f6d 100644
--- a/src/lib/krb5/krb/rd_safe.c
+++ b/src/lib/krb5/krb/rd_safe.c
@@ -1,210 +1,174 @@
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/* lib/krb5/krb/rd_safe.c - definition of krb5_rd_safe() */
+/* lib/krb5/krb/rd_safe.c - krb5_rd_safe() */
 /*
- * Copyright 1990,1991,2007,2008 by the Massachusetts Institute of Technology.
- * All Rights Reserved.
+ * Copyright 1990,1991,2007,2008,2019 by the Massachusetts Institute of
+ * Technology.  All Rights Reserved.
  *
- * Export of this software from the United States of America may
- *   require a specific license from the United States Government.
- *   It is the responsibility of any person or organization contemplating
- *   export to obtain such a license before exporting.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission.  Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose.  It is provided "as is" without express
- * or implied warranty.
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "k5-int.h"
 #include "int-proto.h"
-#include "cleanup.h"
 #include "auth_con.h"
 
 /*
-  parses a KRB_SAFE message from inbuf, placing the integrity-protected user
-  data in *outbuf.
-
-  key specifies the key to be used for decryption of the message.
-
-  outbuf points to allocated storage which the caller should free when finished.
-
-  returns system errors, integrity errors
-*/
+ * Unmarshal a KRB-SAFE message from der_krbsafe, placing the
+ * integrity-protected user data in *userdata_out and replay data in
+ * *rdata_out.  The caller should free *userdata_out when finished.
+ */
 static krb5_error_code
-rd_safe_basic(krb5_context context, krb5_auth_context ac,
-              const krb5_data *inbuf, krb5_key key,
-              krb5_replay_data *replaydata, krb5_data *outbuf)
+read_krbsafe(krb5_context context, krb5_auth_context ac,
+             const krb5_data *der_krbsafe, krb5_key key,
+             krb5_replay_data *rdata_out, krb5_data *userdata_out)
 {
-    krb5_error_code       retval;
-    krb5_safe           * message;
-    krb5_data *safe_body = NULL;
-    krb5_checksum our_cksum, *his_cksum;
+    krb5_error_code ret;
+    krb5_safe *krbsafe;
+    krb5_data *safe_body = NULL, *der_zerosafe = NULL;
+    krb5_checksum zero_cksum, *safe_cksum;
     krb5_octet zero_octet = 0;
-    krb5_data *scratch;
     krb5_boolean valid;
     struct krb5_safe_with_body swb;
 
-    if (!krb5_is_krb_safe(inbuf))
+    *userdata_out = empty_data();
+    if (!krb5_is_krb_safe(der_krbsafe))
         return KRB5KRB_AP_ERR_MSG_TYPE;
 
-    if ((retval = decode_krb5_safe_with_body(inbuf, &message, &safe_body)))
-        return retval;
+    ret = decode_krb5_safe_with_body(der_krbsafe, &krbsafe, &safe_body);
+    if (ret)
+        return ret;
 
-    if (!krb5_c_valid_cksumtype(message->checksum->checksum_type)) {
-        retval = KRB5_PROG_SUMTYPE_NOSUPP;
+    if (!krb5_c_valid_cksumtype(krbsafe->checksum->checksum_type)) {
+        ret = KRB5_PROG_SUMTYPE_NOSUPP;
         goto cleanup;
     }
-    if (!krb5_c_is_coll_proof_cksum(message->checksum->checksum_type) ||
-        !krb5_c_is_keyed_cksum(message->checksum->checksum_type)) {
-        retval = KRB5KRB_AP_ERR_INAPP_CKSUM;
+    if (!krb5_c_is_coll_proof_cksum(krbsafe->checksum->checksum_type) ||
+        !krb5_c_is_keyed_cksum(krbsafe->checksum->checksum_type)) {
+        ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
         goto cleanup;
     }
 
-    retval = k5_privsafe_check_addrs(context, ac, message->s_address,
-                                     message->r_address);
-    if (retval)
+    ret = k5_privsafe_check_addrs(context, ac, krbsafe->s_address,
+                                  krbsafe->r_address);
+    if (ret)
         goto cleanup;
 
-    /* verify the checksum */
-    /*
-     * In order to recreate what was checksummed, we regenerate the message
-     * without checksum and then have the cryptographic subsystem verify
-     * the checksum for us.  This is because some checksum methods have
-     * a confounder encrypted as part of the checksum.
-     */
-    his_cksum = message->checksum;
-
-    our_cksum.length = 0;
-    our_cksum.checksum_type = 0;
-    our_cksum.contents = &zero_octet;
-
-    message->checksum = &our_cksum;
-
+    /* Regenerate the KRB-SAFE message without the checksum. */
+    safe_cksum = krbsafe->checksum;
+    zero_cksum.length = 0;
+    zero_cksum.checksum_type = 0;
+    zero_cksum.contents = &zero_octet;
+    krbsafe->checksum = &zero_cksum;
     swb.body = safe_body;
-    swb.safe = message;
-    retval = encode_krb5_safe_with_body(&swb, &scratch);
-    message->checksum = his_cksum;
-    if (retval)
+    swb.safe = krbsafe;
+    ret = encode_krb5_safe_with_body(&swb, &der_zerosafe);
+    krbsafe->checksum = safe_cksum;
+    if (ret)
         goto cleanup;
 
-    retval = krb5_k_verify_checksum(context, key,
-                                    KRB5_KEYUSAGE_KRB_SAFE_CKSUM,
-                                    scratch, his_cksum, &valid);
-
-    (void) memset(scratch->data, 0, scratch->length);
-    krb5_free_data(context, scratch);
-
+    /* Verify the checkum over the re-encoded message. */
+    ret = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_KRB_SAFE_CKSUM,
+                                 der_zerosafe, safe_cksum, &valid);
     if (!valid) {
-        /*
-         * Checksum over only the KRB-SAFE-BODY, like RFC 1510 says, in
-         * case someone actually implements it correctly.
-         */
-        retval = krb5_k_verify_checksum(context, key,
-                                        KRB5_KEYUSAGE_KRB_SAFE_CKSUM,
-                                        safe_body, his_cksum, &valid);
+        /* Checksum over only the KRB-SAFE-BODY as specified in RFC 1510. */
+        ret = krb5_k_verify_checksum(context, key,
+                                     KRB5_KEYUSAGE_KRB_SAFE_CKSUM,
+                                     safe_body, safe_cksum, &valid);
         if (!valid) {
-            retval = KRB5KRB_AP_ERR_MODIFIED;
+            ret = KRB5KRB_AP_ERR_MODIFIED;
             goto cleanup;
         }
     }
 
-    replaydata->timestamp = message->timestamp;
-    replaydata->usec = message->usec;
-    replaydata->seq = message->seq_number;
+    rdata_out->timestamp = krbsafe->timestamp;
+    rdata_out->usec = krbsafe->usec;
+    rdata_out->seq = krbsafe->seq_number;
 
-    *outbuf = message->user_data;
-    message->user_data.data = NULL;
-    retval = 0;
+    *userdata_out = krbsafe->user_data;
+    krbsafe->user_data.data = NULL;
 
 cleanup:
-    krb5_free_safe(context, message);
+    if (der_zerosafe != NULL) {
+        zap(der_zerosafe->data, der_zerosafe->length);
+        krb5_free_data(context, der_zerosafe);
+    }
     krb5_free_data(context, safe_body);
-    return retval;
+    krb5_free_safe(context, krbsafe);
+    return ret;
 }
 
 krb5_error_code KRB5_CALLCONV
-krb5_rd_safe(krb5_context context, krb5_auth_context auth_context,
-             const krb5_data *inbuf, krb5_data *outbuf,
-             krb5_replay_data *outdata)
+krb5_rd_safe(krb5_context context, krb5_auth_context authcon,
+             const krb5_data *inbuf, krb5_data *userdata_out,
+             krb5_replay_data *rdata_out)
 {
-    krb5_error_code       retval;
-    krb5_key              key;
-    krb5_replay_data      replaydata;
-
-    if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
-         (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
-        (outdata == NULL))
-        /* Need a better error */
+    krb5_error_code ret;
+    krb5_key key;
+    krb5_replay_data rdata;
+    krb5_data userdata = empty_data();
+    const krb5_int32 flags = authcon->auth_context_flags;
+
+    *userdata_out = empty_data();
+
+    if (((flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
+         (flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && rdata_out == NULL)
         return KRB5_RC_REQUIRED;
 
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
-        (auth_context->remote_addr == NULL))
+    if ((flags & KRB5_AUTH_CONTEXT_DO_TIME) && authcon->remote_addr == NULL)
         return KRB5_REMOTE_ADDR_REQUIRED;
 
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
-        (auth_context->rcache == NULL))
-        return KRB5_RC_REQUIRED;
+    key = (authcon->recv_subkey != NULL) ? authcon->recv_subkey : authcon->key;
+    memset(&rdata, 0, sizeof(rdata));
+    ret = read_krbsafe(context, authcon, inbuf, key, &rdata, &userdata);
+    if (ret)
+        goto cleanup;
 
-    /* Get key */
-    if ((key = auth_context->recv_subkey) == NULL)
-        key = auth_context->key;
-
-    memset(&replaydata, 0, sizeof(replaydata));
-    retval = rd_safe_basic(context, auth_context, inbuf, key, &replaydata,
-                           outbuf);
-    if (retval)
-        return retval;
-
-    if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
-        krb5_donot_replay replay;
-
-        if ((retval = krb5_check_clockskew(context, replaydata.timestamp)))
-            goto error;
-
-        if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr,
-                                           "_safe", &replay.client)))
-            goto error;
-
-        replay.server = "";             /* XXX */
-        replay.msghash = NULL;
-        replay.cusec = replaydata.usec;
-        replay.ctime = replaydata.timestamp;
-        if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
-            free(replay.client);
-            goto error;
-        }
-        free(replay.client);
-    }
+    ret = k5_privsafe_check_replay(context, authcon, authcon->remote_addr,
+                                   "_safe", &rdata, TRUE);
+    if (ret)
+        goto cleanup;
 
-    if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
-        if (!k5_privsafe_check_seqnum(context, auth_context, replaydata.seq)) {
-            retval =  KRB5KRB_AP_ERR_BADORDER;
-            goto error;
+    if (flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+        if (!k5_privsafe_check_seqnum(context, authcon, rdata.seq)) {
+            ret = KRB5KRB_AP_ERR_BADORDER;
+            goto cleanup;
         }
-        auth_context->remote_seq_number++;
+        authcon->remote_seq_number++;
     }
 
-    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
-        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
-        outdata->timestamp = replaydata.timestamp;
-        outdata->usec = replaydata.usec;
-        outdata->seq = replaydata.seq;
+    if ((flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
+        (flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
+        rdata_out->timestamp = rdata.timestamp;
+        rdata_out->usec = rdata.usec;
+        rdata_out->seq = rdata.seq;
     }
 
-    /* everything is ok - return data to the user */
-    return 0;
-
-error:
-    free(outbuf->data);
-    return retval;
+    *userdata_out = userdata;
+    userdata = empty_data();
 
+cleanup:
+    krb5_free_data_contents(context, &userdata);
+    return ret;
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.