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

Greg Hudson ghudson at mit.edu
Mon Feb 25 00:16:38 EST 2019


https://github.com/krb5/krb5/commit/44b601ec43f34a6ed5bf9a737328d2ee4c438f12
commit 44b601ec43f34a6ed5bf9a737328d2ee4c438f12
Author: Greg Hudson <ghudson at mit.edu>
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;
 }


More information about the cvs-krb5 mailing list