diff --git a/.krb5.metadata b/.krb5.metadata
new file mode 100644
index 0000000..7730c75
--- /dev/null
+++ b/.krb5.metadata
@@ -0,0 +1,4 @@
+ac6a2109eff848cf8ffac5b30e2b516ebe826a66 SOURCES/krb5-1.11.3-pdf.tar.xz
+a6c973b0658d244afdf12a39270227865ac13500 SOURCES/nss_wrapper-0.0-20130719153839Z.git6cb59864.bz2
+28a0a3d3a4a9837fb2a5124bc8aa81efb8fef1e5 SOURCES/krb5-1.11.3.tar.gz
+8437e10cbb315529a87f835ccea97ff827d7a310 SOURCES/krb5-1.11.3.tar.gz.asc
diff --git a/README.md b/README.md
deleted file mode 100644
index 0e7897f..0000000
--- a/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-The master branch has no content
- 
-Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6
- 
-If you find this file in a distro specific branch, it means that no content has been checked in yet
diff --git a/SOURCES/0000-ksu-intermediates.patch b/SOURCES/0000-ksu-intermediates.patch
new file mode 100644
index 0000000..d567116
--- /dev/null
+++ b/SOURCES/0000-ksu-intermediates.patch
@@ -0,0 +1,13 @@
+Collect changes that prevent the ksu collections changes from being directly
+pullable from master (or, as of this writing, the proposed changes for master).
+--- krb5/src/clients/ksu/main.c
++++ krb5/src/clients/ksu/main.c
+@@ -706,7 +706,7 @@ main (argc, argv)
+     }
+ 
+     if(set_env_var( "HOME", target_pwd->pw_dir)){
+-        fprintf(stderr, _("ksu: couldn't set environment variable USER\n"));
++        fprintf(stderr, _("ksu: couldn't set environment variable HOME\n"));
+         sweep_up(ksu_context, cc_target);
+         exit(1);
+     }
diff --git a/SOURCES/0001-Don-t-try-to-stat-not-on-disk-ccache-residuals.patch b/SOURCES/0001-Don-t-try-to-stat-not-on-disk-ccache-residuals.patch
new file mode 100644
index 0000000..2a6275c
--- /dev/null
+++ b/SOURCES/0001-Don-t-try-to-stat-not-on-disk-ccache-residuals.patch
@@ -0,0 +1,269 @@
+From 7e0bcb958eb5861cb30a190dcac1e6422d65299e Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@redhat.com>
+Date: Fri, 1 Nov 2013 09:48:13 -0400
+Subject: [PATCH 1/6] Don't try to stat() not-on-disk ccache residuals
+
+Don't assume that ccache residual names are filenames which we can
+stat() usefully.  Instead, use helper functions to call the library
+routines to try to read the default principal name from caches.
+---
+ src/clients/ksu/ccache.c | 88 +++++++++++++++++++++++++++++-------------------
+ src/clients/ksu/ksu.h    |  6 ++++
+ src/clients/ksu/main.c   | 17 +++++-----
+ 3 files changed, 68 insertions(+), 43 deletions(-)
+
+diff --git a/src/clients/ksu/ccache.c b/src/clients/ksu/ccache.c
+index 9916c75..7917af2 100644
+--- a/src/clients/ksu/ccache.c
++++ b/src/clients/ksu/ccache.c
+@@ -60,12 +60,10 @@ krb5_error_code krb5_ccache_copy (context, cc_def, cc_other_tag,
+ {
+     int i=0;
+     krb5_ccache  * cc_other;
+-    const char * cc_def_name;
+-    const char * cc_other_name;
++    const char * cc_other_type;
+     krb5_error_code retval=0;
+     krb5_creds ** cc_def_creds_arr = NULL;
+     krb5_creds ** cc_other_creds_arr = NULL;
+-    struct stat st_temp;
+ 
+     cc_other = (krb5_ccache *)  xcalloc(1, sizeof (krb5_ccache));
+ 
+@@ -74,10 +72,9 @@ krb5_error_code krb5_ccache_copy (context, cc_def, cc_other_tag,
+         return retval;
+     }
+ 
+-    cc_def_name = krb5_cc_get_name(context, cc_def);
+-    cc_other_name = krb5_cc_get_name(context, *cc_other);
++    cc_other_type = krb5_cc_get_type(context, *cc_other);
+ 
+-    if ( ! stat(cc_def_name, &st_temp)){
++    if (krb5_ccache_is_initialized(context, cc_def)) {
+         if((retval = krb5_get_nonexp_tkts(context,cc_def,&cc_def_creds_arr))){
+             return retval;
+         }
+@@ -86,7 +83,8 @@ krb5_error_code krb5_ccache_copy (context, cc_def, cc_other_tag,
+     *stored = krb5_find_princ_in_cred_list(context, cc_def_creds_arr,
+                                            primary_principal);
+ 
+-    if (!lstat( cc_other_name, &st_temp))
++    if (!krb5_cc_support_switch(context, cc_other_type) &&
++        krb5_ccache_name_is_initialized(context, cc_other_tag))
+         return EINVAL;
+ 
+     if (krb5_seteuid(0)||krb5_seteuid(target_uid)) {
+@@ -533,24 +531,18 @@ krb5_error_code krb5_ccache_overwrite(context, ccs, cct, primary_principal)
+     krb5_ccache cct;
+     krb5_principal primary_principal;
+ {
+-    const char * cct_name;
+-    const char * ccs_name;
+     krb5_error_code retval=0;
+     krb5_principal temp_principal;
+     krb5_creds ** ccs_creds_arr = NULL;
+     int i=0;
+-    struct stat st_temp;
+ 
+-    ccs_name = krb5_cc_get_name(context, ccs);
+-    cct_name = krb5_cc_get_name(context, cct);
+-
+-    if ( ! stat(ccs_name, &st_temp)){
++    if (krb5_ccache_is_initialized(context, ccs)) {
+         if ((retval = krb5_get_nonexp_tkts(context,  ccs, &ccs_creds_arr))){
+             return retval;
+         }
+     }
+ 
+-    if ( ! stat(cct_name, &st_temp)){
++    if (krb5_ccache_is_initialized(context, cct)) {
+         if ((retval = krb5_cc_get_principal(context, cct, &temp_principal))){
+             return retval;
+         }
+@@ -649,12 +641,10 @@ krb5_error_code krb5_ccache_copy_restricted (context, cc_def, cc_other_tag,
+ 
+     int i=0;
+     krb5_ccache  * cc_other;
+-    const char * cc_def_name;
+-    const char * cc_other_name;
++    const char * cc_other_type;
+     krb5_error_code retval=0;
+     krb5_creds ** cc_def_creds_arr = NULL;
+     krb5_creds ** cc_other_creds_arr = NULL;
+-    struct stat st_temp;
+ 
+     cc_other = (krb5_ccache *)  xcalloc(1, sizeof (krb5_ccache));
+ 
+@@ -663,19 +653,17 @@ krb5_error_code krb5_ccache_copy_restricted (context, cc_def, cc_other_tag,
+         return retval;
+     }
+ 
+-    cc_def_name = krb5_cc_get_name(context, cc_def);
+-    cc_other_name = krb5_cc_get_name(context, *cc_other);
++    cc_other_type = krb5_cc_get_type(context, *cc_other);
+ 
+-    if ( ! stat(cc_def_name, &st_temp)){
+-        if((retval = krb5_get_nonexp_tkts(context,cc_def,&cc_def_creds_arr))){
++    if (krb5_ccache_is_initialized(context, cc_def)) {
++        retval = krb5_get_nonexp_tkts(context, cc_def, &cc_def_creds_arr);
++        if (retval)
+             return retval;
+-        }
+-
+     }
+ 
+-    if (!lstat( cc_other_name, &st_temp)) {
++    if (!krb5_cc_support_switch(context, cc_other_type) &&
++        krb5_ccache_name_is_initialized(context, cc_other_tag))
+         return EINVAL;
+-    }
+ 
+     if (krb5_seteuid(0)||krb5_seteuid(target_uid)) {
+         return errno;
+@@ -723,12 +711,10 @@ krb5_error_code krb5_ccache_filter (context, cc, prst)
+     krb5_creds ** cc_creds_arr = NULL;
+     const char * cc_name;
+     krb5_boolean stored;
+-    struct stat st_temp;
+ 
+     cc_name = krb5_cc_get_name(context, cc);
+ 
+-    if ( ! stat(cc_name, &st_temp)){
+-
++    if (krb5_ccache_is_initialized(context, cc)) {
+         if (auth_debug) {
+             fprintf(stderr,"putting cache %s through a filter for -z option\n",                     cc_name);
+         }
+@@ -793,12 +779,8 @@ krb5_error_code  krb5_find_princ_in_cache (context, cc, princ, found)
+ {
+     krb5_error_code retval;
+     krb5_creds ** creds_list = NULL;
+-    const char * cc_name;
+-    struct stat st_temp;
+ 
+-    cc_name = krb5_cc_get_name(context, cc);
+-
+-    if ( ! stat(cc_name, &st_temp)){
++    if (krb5_ccache_is_initialized(context, cc)) {
+         if ((retval = krb5_get_nonexp_tkts(context, cc, &creds_list))){
+             return retval;
+         }
+@@ -807,3 +789,41 @@ krb5_error_code  krb5_find_princ_in_cache (context, cc, princ, found)
+     *found = krb5_find_princ_in_cred_list(context, creds_list, princ);
+     return 0;
+ }
++
++extern krb5_boolean
++krb5_ccache_name_is_initialized(krb5_context context, const char *cctag)
++{
++    krb5_error_code retval = 0;
++    krb5_ccache cc;
++    krb5_principal princ;
++
++    retval = krb5_cc_resolve(context, cctag, &cc);
++    if (retval)
++        return FALSE;
++
++    retval = krb5_cc_get_principal(context, cc, &princ);
++    if (retval == 0)
++        krb5_free_principal(context, princ);
++    krb5_cc_close(context, cc);
++
++    return retval == 0;
++}
++
++extern krb5_boolean
++krb5_ccache_is_initialized(krb5_context context, krb5_ccache def_cc)
++{
++    krb5_error_code retval = 0;
++    krb5_boolean result;
++    char *def_cc_name;
++
++    if (def_cc == NULL)
++        return FALSE;
++
++    retval = krb5_cc_get_full_name(context, def_cc, &def_cc_name);
++    if (retval)
++        return FALSE;
++
++    result = krb5_ccache_name_is_initialized(context, def_cc_name);
++    krb5_free_string(context, def_cc_name);
++    return result;
++}
+diff --git a/src/clients/ksu/ksu.h b/src/clients/ksu/ksu.h
+index f2c0811..2a63c21 100644
+--- a/src/clients/ksu/ksu.h
++++ b/src/clients/ksu/ksu.h
+@@ -141,6 +141,12 @@ extern krb5_error_code krb5_store_some_creds
+ (krb5_context, krb5_ccache, krb5_creds **, krb5_creds **,
+  krb5_principal, krb5_boolean *);
+ 
++extern krb5_boolean krb5_ccache_name_is_initialized
++(krb5_context, const char *);
++
++extern krb5_boolean krb5_ccache_is_initialized
++(krb5_context, krb5_ccache);
++
+ extern krb5_error_code krb5_ccache_copy_restricted
+ (krb5_context, krb5_ccache, char *, krb5_principal,
+  krb5_ccache *, krb5_boolean *, uid_t);
+diff --git a/src/clients/ksu/main.c b/src/clients/ksu/main.c
+index 233eb52..e2ca06a 100644
+--- a/src/clients/ksu/main.c
++++ b/src/clients/ksu/main.c
+@@ -112,7 +112,6 @@ main (argc, argv)
+     extern char * getpass(), *crypt();
+     int pargc;
+     char ** pargv;
+-    struct stat  st_temp;
+     krb5_boolean stored = FALSE;
+     krb5_principal  kdc_server;
+     krb5_boolean zero_password;
+@@ -265,9 +264,10 @@ main (argc, argv)
+                 if ( strchr(cc_source_tag, ':')){
+                     cc_source_tag_tmp = strchr(cc_source_tag, ':') + 1;
+ 
+-                    if( stat( cc_source_tag_tmp, &st_temp)){
++                    if (!krb5_ccache_name_is_initialized(ksu_context,
++                                                         cc_source_tag)) {
+                         com_err(prog_name, errno,
+-                                _("while looking for credentials file %s"),
++                                _("while looking for credentials cache %s"),
+                                 cc_source_tag_tmp);
+                         exit (1);
+                     }
+@@ -432,7 +432,8 @@ main (argc, argv)
+                      (long) target_uid, gen_sym());
+             cc_target_tag_tmp = strchr(cc_target_tag, ':') + 1;
+ 
+-        }while ( !stat ( cc_target_tag_tmp, &st_temp));
++        } while (krb5_ccache_name_is_initialized(ksu_context,
++                                                 cc_target_tag));
+     }
+ 
+ 
+@@ -884,8 +885,6 @@ static void sweep_up(context, cc)
+     krb5_ccache cc;
+ {
+     krb5_error_code retval;
+-    const char * cc_name;
+-    struct stat  st_temp;
+ 
+     krb5_seteuid(0);
+     if (krb5_seteuid(target_uid) < 0) {
+@@ -894,9 +893,9 @@ static void sweep_up(context, cc)
+         exit(1);
+     }
+ 
+-    cc_name = krb5_cc_get_name(context, cc);
+-    if ( ! stat(cc_name, &st_temp)){
+-        if ((retval = krb5_cc_destroy(context, cc)))
++    if (krb5_ccache_is_initialized(context, cc)) {
++        retval = krb5_cc_destroy(context, cc);
++        if (retval)
+             com_err(prog_name, retval, _("while destroying cache"));
+     }
+ }
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0002-Use-an-in-memory-cache-until-we-need-the-target-s.patch b/SOURCES/0002-Use-an-in-memory-cache-until-we-need-the-target-s.patch
new file mode 100644
index 0000000..7b900de
--- /dev/null
+++ b/SOURCES/0002-Use-an-in-memory-cache-until-we-need-the-target-s.patch
@@ -0,0 +1,321 @@
+From 60295a63fadf04f6cd6db7919aa1dad6fb4f0596 Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@dahyabhai.net>
+Date: Wed, 30 Oct 2013 21:45:35 -0400
+Subject: [PATCH 2/6] Use an in-memory cache until we need the target's
+
+Instead of copying source or obtained creds into the target cache and
+changing ownership if everything succeeds, copy them into a MEMORY:
+cache and then, if everything succeeds, create the target cache as the
+target user.
+---
+ src/clients/ksu/ksu.h  |   1 +
+ src/clients/ksu/main.c | 133 +++++++++++++++++++++++++++++--------------------
+ 2 files changed, 80 insertions(+), 54 deletions(-)
+
+diff --git a/src/clients/ksu/ksu.h b/src/clients/ksu/ksu.h
+index 2a63c21..1d102a1 100644
+--- a/src/clients/ksu/ksu.h
++++ b/src/clients/ksu/ksu.h
+@@ -45,6 +45,7 @@
+ #define KRB5_DEFAULT_TKT_LIFE 60*60*12 /* 12 hours */
+ 
+ #define KRB5_SECONDARY_CACHE "FILE:/tmp/krb5cc_"
++#define KRB5_TEMPORARY_CACHE "MEMORY:_ksu"
+ 
+ #define KRB5_LOGIN_NAME ".k5login"
+ #define KRB5_USERS_NAME ".k5users"
+diff --git a/src/clients/ksu/main.c b/src/clients/ksu/main.c
+index e2ca06a..fa86c78 100644
+--- a/src/clients/ksu/main.c
++++ b/src/clients/ksu/main.c
+@@ -86,7 +86,7 @@ main (argc, argv)
+     int statusp=0;
+     krb5_error_code retval = 0;
+     krb5_principal client = NULL;
+-    krb5_ccache cc_target = NULL;
++    krb5_ccache cc_tmp = NULL, cc_target = NULL;
+     krb5_context ksu_context;
+     char * cc_target_tag = NULL;
+     char * target_user = NULL;
+@@ -452,14 +452,15 @@ main (argc, argv)
+     }
+ 
+     /*
+-      Only when proper authentication and authorization
+-      takes place, the target user becomes the owner of the cache.
+-    */
+-
+-    /* we continue to run as source uid until
+-       the middle of the copy, when becomewe become the target user
+-       The cache is owned by the target user.*/
++     * Only after proper authentication and authorization has
++     * taken place, do we populate a cache for the target user.
++     */
+ 
++    /*
++     * We read the set of creds we want to copy from the source ccache as the
++     * source uid, become root for authentication, and then become the target
++     * user to handle authorization and creating the target user's cache.
++     */
+ 
+     /* if root ksu's to a regular user, then
+        then only the credentials for that particular user
+@@ -468,19 +469,23 @@ main (argc, argv)
+     if ((source_uid == 0) && (target_uid != 0)) {
+ 
+         if ((retval = krb5_ccache_copy_restricted(ksu_context,  cc_source,
+-                                                  cc_target_tag, client,
+-                                                  &cc_target, &stored,
+-                                                  target_uid))){
++                                                  KRB5_TEMPORARY_CACHE, client,
++                                                  &cc_tmp, &stored,
++                                                  0))){
+             com_err(prog_name, retval, _("while copying cache %s to %s"),
+-                    krb5_cc_get_name(ksu_context, cc_source), cc_target_tag);
++                    krb5_cc_get_name(ksu_context, cc_source),
++                    KRB5_TEMPORARY_CACHE);
+             exit(1);
+         }
+ 
+     } else {
+-        if ((retval = krb5_ccache_copy(ksu_context, cc_source, cc_target_tag,
+-                                       client,&cc_target, &stored, target_uid))) {
++
++        retval = krb5_ccache_copy(ksu_context, cc_source, KRB5_TEMPORARY_CACHE,
++                                  client, &cc_tmp, &stored, 0);
++        if (retval) {
+             com_err(prog_name, retval, _("while copying cache %s to %s"),
+-                    krb5_cc_get_name(ksu_context, cc_source), cc_target_tag);
++                    krb5_cc_get_name(ksu_context, cc_source),
++                    KRB5_TEMPORARY_CACHE);
+             exit(1);
+         }
+ 
+@@ -502,7 +507,7 @@ main (argc, argv)
+                                       &kdc_server))){
+                 com_err(prog_name, retval,
+                         _("while creating tgt for local realm"));
+-                sweep_up(ksu_context, cc_target);
++                sweep_up(ksu_context, cc_tmp);
+                 exit(1);
+             }
+ 
+@@ -510,13 +515,13 @@ main (argc, argv)
+                               "enter it here and are logged\n"));
+             fprintf(stderr, _("         in remotely using an unsecure "
+                               "(non-encrypted) channel.\n"));
+-            if (krb5_get_tkt_via_passwd (ksu_context, &cc_target, client,
+-                                         kdc_server, &options,
+-                                         &zero_password) == FALSE){
++            if (krb5_get_tkt_via_passwd(ksu_context, &cc_tmp, client,
++                                        kdc_server, &options,
++                                        &zero_password) == FALSE){
+ 
+                 if (zero_password == FALSE){
+                     fprintf(stderr, _("Goodbye\n"));
+-                    sweep_up(ksu_context, cc_target);
++                    sweep_up(ksu_context, cc_tmp);
+                     exit(1);
+                 }
+ 
+@@ -535,15 +540,16 @@ main (argc, argv)
+     if (source_uid && (source_uid != target_uid)) {
+         char * client_name;
+ 
+-        auth_val = krb5_auth_check(ksu_context, client, localhostname, &options,
+-                                   target_user,cc_target, &path_passwd, target_uid);
++        auth_val = krb5_auth_check(ksu_context, client, localhostname,
++                                   &options, target_user, cc_tmp,
++                                   &path_passwd, target_uid);
+ 
+         /* if Kerberos authentication failed then exit */
+         if (auth_val ==FALSE){
+             fprintf(stderr, _("Authentication failed.\n"));
+             syslog(LOG_WARNING, "'%s %s' authentication failed for %s%s",
+                    prog_name,target_user,source_user,ontty());
+-            sweep_up(ksu_context, cc_target);
++            sweep_up(ksu_context, cc_tmp);
+             exit(1);
+         }
+ 
+@@ -576,7 +582,7 @@ main (argc, argv)
+ 
+         if ((retval = krb5_unparse_name(ksu_context, client, &client_name))) {
+             com_err(prog_name, retval, _("When unparsing name"));
+-            sweep_up(ksu_context, cc_target);
++            sweep_up(ksu_context, cc_tmp);
+             exit(1);
+         }
+ 
+@@ -589,7 +595,7 @@ main (argc, argv)
+         if (krb5_seteuid(target_uid)) {
+             com_err(prog_name, errno, _("while switching to target for "
+                                         "authorization check"));
+-            sweep_up(ksu_context, cc_target);
++            sweep_up(ksu_context, cc_tmp);
+             exit(1);
+         }
+ 
+@@ -597,14 +603,14 @@ main (argc, argv)
+                                          cmd, &authorization_val, &exec_cmd))){
+             com_err(prog_name,retval, _("while checking authorization"));
+             krb5_seteuid(0); /*So we have some chance of sweeping up*/
+-            sweep_up(ksu_context, cc_target);
++            sweep_up(ksu_context, cc_tmp);
+             exit(1);
+         }
+ 
+         if (krb5_seteuid(0)) {
+             com_err(prog_name, errno, _("while switching back from target "
+                                         "after authorization check"));
+-            sweep_up(ksu_context, cc_target);
++            sweep_up(ksu_context, cc_tmp);
+             exit(1);
+         }
+         if (authorization_val == TRUE){
+@@ -646,21 +652,23 @@ main (argc, argv)
+ 
+             }
+ 
+-            sweep_up(ksu_context, cc_target);
++            sweep_up(ksu_context, cc_tmp);
+             exit(1);
+         }
+     }
+ 
+     if( some_rest_copy){
+-        if ((retval = krb5_ccache_filter(ksu_context, cc_target, client))){
++        retval = krb5_ccache_filter(ksu_context, cc_tmp, client);
++        if (retval) {
+             com_err(prog_name,retval, _("while calling cc_filter"));
+-            sweep_up(ksu_context, cc_target);
++            sweep_up(ksu_context, cc_tmp);
+             exit(1);
+         }
+     }
+ 
+     if (all_rest_copy){
+-        if ((retval = krb5_cc_initialize(ksu_context, cc_target, client))){
++        retval = krb5_cc_initialize(ksu_context, cc_tmp, client);
++        if (retval) {
+             com_err(prog_name, retval, _("while erasing target cache"));
+             exit(1);
+         }
+@@ -682,7 +690,7 @@ main (argc, argv)
+ 
+     if (!standard_shell(target_pwd->pw_shell) && source_uid) {
+         fprintf(stderr, _("ksu: permission denied (shell).\n"));
+-        sweep_up(ksu_context, cc_target);
++        sweep_up(ksu_context, cc_tmp);
+         exit(1);
+     }
+ #endif /* HAVE_GETUSERSHELL */
+@@ -692,43 +700,33 @@ main (argc, argv)
+         if(set_env_var("USER", target_pwd->pw_name)){
+             fprintf(stderr,
+                     _("ksu: couldn't set environment variable USER\n"));
+-            sweep_up(ksu_context, cc_target);
++            sweep_up(ksu_context, cc_tmp);
+             exit(1);
+         }
+     }
+ 
+     if(set_env_var( "HOME", target_pwd->pw_dir)){
+         fprintf(stderr, _("ksu: couldn't set environment variable HOME\n"));
+-        sweep_up(ksu_context, cc_target);
++        sweep_up(ksu_context, cc_tmp);
+         exit(1);
+     }
+ 
+     if(set_env_var( "SHELL", shell)){
+         fprintf(stderr, _("ksu: couldn't set environment variable SHELL\n"));
+-        sweep_up(ksu_context, cc_target);
+-        exit(1);
+-    }
+-
+-    /* set the cc env name to target */
+-
+-    if(set_env_var( KRB5_ENV_CCNAME, cc_target_tag)){
+-        fprintf(stderr, _("ksu: couldn't set environment variable %s\n"),
+-                KRB5_ENV_CCNAME);
+-        sweep_up(ksu_context, cc_target);
++        sweep_up(ksu_context, cc_tmp);
+         exit(1);
+     }
+ 
+     /* set permissions */
+     if (setgid(target_pwd->pw_gid) < 0) {
+         perror("ksu: setgid");
+-        sweep_up(ksu_context, cc_target);
++        sweep_up(ksu_context, cc_tmp);
+         exit(1);
+     }
+ 
+-
+     if (initgroups(target_user, target_pwd->pw_gid)) {
+         fprintf(stderr, _("ksu: initgroups failed.\n"));
+-        sweep_up(ksu_context, cc_target);
++        sweep_up(ksu_context, cc_tmp);
+         exit(1);
+     }
+ 
+@@ -748,22 +746,49 @@ main (argc, argv)
+      */
+     if (setluid((uid_t) pwd->pw_uid) < 0) {
+         perror("setluid");
+-        sweep_up(ksu_context, cc_target);
++        sweep_up(ksu_context, cc_tmp);
+         exit(1);
+     }
+ #endif  /* HAVE_SETLUID */
+ 
+-    if (setuid(target_pwd->pw_uid) < 0) {
++    if (seteuid(0) < 0 || seteuid(target_pwd->pw_uid) < 0) {
++        perror("ksu: seteuid");
++        sweep_up(ksu_context, cc_tmp);
++        exit(1);
++    }
++
++    retval = krb5_ccache_copy(ksu_context, cc_tmp, cc_target_tag,
++                              client, &cc_target, &stored,
++                              target_pwd->pw_uid);
++    if (retval) {
++        com_err(prog_name, retval, _("while copying cache %s to %s"),
++                krb5_cc_get_name(ksu_context, cc_tmp), cc_target_tag);
++        exit(1);
++    }
++
++    if (setuid(0) < 0 || setuid(target_pwd->pw_uid) < 0) {
+         perror("ksu: setuid");
+         sweep_up(ksu_context, cc_target);
+         exit(1);
+     }
+ 
+-    if (access( cc_target_tag_tmp, R_OK | W_OK )){
+-        com_err(prog_name, errno,
+-                _("%s does not have correct permissions for %s, %s aborted"),
+-                target_user, cc_target_tag_tmp, prog_name);
+-        exit(1);
++    /* set the cc env name to target */
++    if (stored) {
++        if (krb5_cc_get_full_name(ksu_context, cc_target,
++                                  &cc_target_tag) == 0) {
++            if (set_env_var(KRB5_ENV_CCNAME, cc_target_tag)){
++                fprintf(stderr,
++                        _("ksu: couldn't set environment variable %s\n"),
++                        KRB5_ENV_CCNAME);
++                sweep_up(ksu_context, cc_target);
++                exit(1);
++            }
++            krb5_free_string(ksu_context, cc_target_tag);
++        } else {
++            com_err(prog_name, retval, _("while reading cache name from %s"),
++                    cc_target_tag);
++            exit(1);
++        }
+     }
+ 
+     if ( cc_source)
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0003-Learn-to-destroy-the-ccache-we-re-copying-from.patch b/SOURCES/0003-Learn-to-destroy-the-ccache-we-re-copying-from.patch
new file mode 100644
index 0000000..9a55929
--- /dev/null
+++ b/SOURCES/0003-Learn-to-destroy-the-ccache-we-re-copying-from.patch
@@ -0,0 +1,95 @@
+From b92db8dabc566ba0fee4d122ce4f7fb11fe8a637 Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@redhat.com>
+Date: Thu, 31 Oct 2013 15:43:49 -0400
+Subject: [PATCH 3/6] Learn to destroy the ccache we're copying from
+
+Add a flag to krb5_ccache_copy() which will instruct it to destroy a
+source ccache after reading its contents.  Using this when we copy the
+creds from a MEMORY cache to somewhere else is necessary to avoid having
+a subsequent call to krb5_cc_cache_match() select the MEMORY cache when
+we're trying to have it search a different location by default.
+---
+ src/clients/ksu/ccache.c | 10 +++++++++-
+ src/clients/ksu/ksu.h    |  2 +-
+ src/clients/ksu/main.c   |  5 +++--
+ 3 files changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/src/clients/ksu/ccache.c b/src/clients/ksu/ccache.c
+index 7917af2..90ba2f2 100644
+--- a/src/clients/ksu/ccache.c
++++ b/src/clients/ksu/ccache.c
+@@ -47,12 +47,14 @@ void show_credential();
+ */
+ 
+ krb5_error_code krb5_ccache_copy (context, cc_def, cc_other_tag,
+-                                  primary_principal, cc_out, stored, target_uid)
++                                  primary_principal, destroy_def,
++                                  cc_out, stored, target_uid)
+ /* IN */
+     krb5_context context;
+     krb5_ccache cc_def;
+     char *cc_other_tag;
+     krb5_principal primary_principal;
++    krb5_boolean destroy_def;
+     uid_t target_uid;
+     /* OUT */
+     krb5_ccache *cc_out;
+@@ -80,6 +82,12 @@ krb5_error_code krb5_ccache_copy (context, cc_def, cc_other_tag,
+         }
+     }
+ 
++    if (destroy_def) {
++        retval = krb5_cc_destroy(context, cc_def);
++        if (retval)
++            return retval;
++    }
++
+     *stored = krb5_find_princ_in_cred_list(context, cc_def_creds_arr,
+                                            primary_principal);
+ 
+diff --git a/src/clients/ksu/ksu.h b/src/clients/ksu/ksu.h
+index 1d102a1..a889fb9 100644
+--- a/src/clients/ksu/ksu.h
++++ b/src/clients/ksu/ksu.h
+@@ -108,7 +108,7 @@ extern krb5_error_code get_best_principal
+ /* ccache.c */
+ extern krb5_error_code krb5_ccache_copy
+ (krb5_context, krb5_ccache, char *, krb5_principal,
+- krb5_ccache *, krb5_boolean *, uid_t);
++ krb5_boolean, krb5_ccache *, krb5_boolean *, uid_t);
+ 
+ extern krb5_error_code krb5_store_all_creds
+ (krb5_context, krb5_ccache, krb5_creds **, krb5_creds **);
+diff --git a/src/clients/ksu/main.c b/src/clients/ksu/main.c
+index fa86c78..7497a2b 100644
+--- a/src/clients/ksu/main.c
++++ b/src/clients/ksu/main.c
+@@ -28,6 +28,7 @@
+ 
+ #include "ksu.h"
+ #include "adm_proto.h"
++#include "../../lib/krb5/os/os-proto.h"
+ #include <sys/types.h>
+ #include <sys/wait.h>
+ #include <signal.h>
+@@ -481,7 +482,7 @@ main (argc, argv)
+     } else {
+ 
+         retval = krb5_ccache_copy(ksu_context, cc_source, KRB5_TEMPORARY_CACHE,
+-                                  client, &cc_tmp, &stored, 0);
++                                  client, FALSE, &cc_tmp, &stored, 0);
+         if (retval) {
+             com_err(prog_name, retval, _("while copying cache %s to %s"),
+                     krb5_cc_get_name(ksu_context, cc_source),
+@@ -758,7 +759,7 @@ main (argc, argv)
+     }
+ 
+     retval = krb5_ccache_copy(ksu_context, cc_tmp, cc_target_tag,
+-                              client, &cc_target, &stored,
++                              client, TRUE, &cc_target, &stored,
+                               target_pwd->pw_uid);
+     if (retval) {
+         com_err(prog_name, retval, _("while copying cache %s to %s"),
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0004-Try-to-use-the-default_ccache_name-d-as-the-target.patch b/SOURCES/0004-Try-to-use-the-default_ccache_name-d-as-the-target.patch
new file mode 100644
index 0000000..01d8430
--- /dev/null
+++ b/SOURCES/0004-Try-to-use-the-default_ccache_name-d-as-the-target.patch
@@ -0,0 +1,149 @@
+From 07f4a0b9c7d68e39a41c085e2f6323093b89ac36 Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@dahyabhai.net>
+Date: Wed, 30 Oct 2013 21:47:14 -0400
+Subject: [PATCH 4/6] Try to use the default_ccache_name'd as the target
+
+Try to use the location named by the default_ccache_name setting as the
+target cache.  If it's a collection, just create or update a subsidiary
+cache.  If it's not, then fall back to creating a new cache to try to
+avoid destroying the contents of one that might already be there.  We
+can't really detect this in advance for KEYRING: caches, though.
+---
+ src/clients/ksu/ksu.h  |  2 +-
+ src/clients/ksu/main.c | 91 ++++++++++++++++++++++++++++++++++++--------------
+ 2 files changed, 67 insertions(+), 26 deletions(-)
+
+diff --git a/src/clients/ksu/ksu.h b/src/clients/ksu/ksu.h
+index a889fb9..a195f52 100644
+--- a/src/clients/ksu/ksu.h
++++ b/src/clients/ksu/ksu.h
+@@ -44,7 +44,7 @@
+ #define KRB5_DEFAULT_OPTIONS 0
+ #define KRB5_DEFAULT_TKT_LIFE 60*60*12 /* 12 hours */
+ 
+-#define KRB5_SECONDARY_CACHE "FILE:/tmp/krb5cc_"
++#define KRB5_DEFAULT_SECONDARY_CACHE "FILE:/tmp/krb5cc_%{uid}"
+ #define KRB5_TEMPORARY_CACHE "MEMORY:_ksu"
+ 
+ #define KRB5_LOGIN_NAME ".k5login"
+diff --git a/src/clients/ksu/main.c b/src/clients/ksu/main.c
+index 7497a2b..58df6a1 100644
+--- a/src/clients/ksu/main.c
++++ b/src/clients/ksu/main.c
+@@ -90,7 +90,10 @@ main (argc, argv)
+     krb5_ccache cc_tmp = NULL, cc_target = NULL;
+     krb5_context ksu_context;
+     char * cc_target_tag = NULL;
++    char * cc_target_tag_conf;
++    krb5_boolean cc_target_switchable;
+     char * target_user = NULL;
++    char * target_user_uid_str;
+     char * source_user;
+ 
+     krb5_ccache cc_source = NULL;
+@@ -116,7 +119,6 @@ main (argc, argv)
+     krb5_boolean stored = FALSE;
+     krb5_principal  kdc_server;
+     krb5_boolean zero_password;
+-    char * dir_of_cc_target;
+ 
+     options.opt = KRB5_DEFAULT_OPTIONS;
+     options.lifetime = KRB5_DEFAULT_TKT_LIFE;
+@@ -420,31 +422,70 @@ main (argc, argv)
+     }
+ 
+     if (cc_target_tag == NULL) {
+-
+         cc_target_tag = (char *)xcalloc(KRB5_SEC_BUFFSIZE ,sizeof(char));
+-        /* make sure that the new ticket file does not already exist
+-           This is run as source_uid because it is reasonable to
+-           require the source user to have write to where the target
+-           cache will be created.*/
+-
+-        do {
+-            snprintf(cc_target_tag, KRB5_SEC_BUFFSIZE, "%s%ld.%d",
+-                     KRB5_SECONDARY_CACHE,
+-                     (long) target_uid, gen_sym());
+-            cc_target_tag_tmp = strchr(cc_target_tag, ':') + 1;
+-
+-        } while (krb5_ccache_name_is_initialized(ksu_context,
+-                                                 cc_target_tag));
+-    }
+-
+-
+-    dir_of_cc_target = get_dir_of_file(cc_target_tag_tmp);
+-
+-    if (access(dir_of_cc_target, R_OK | W_OK )){
+-        fprintf(stderr,
+-                _("%s does not have correct permissions for %s\n"),
+-                source_user, cc_target_tag);
+-        exit(1);
++        if (cc_target_tag == NULL) {
++            com_err(prog_name, retval , _("while allocating memory for the "
++                                          "target ccache name"));
++            exit(1);
++        }
++        /* Read the configured value. */
++        if (profile_get_string(ksu_context->profile, KRB5_CONF_LIBDEFAULTS,
++                               KRB5_CONF_DEFAULT_CCACHE_NAME, NULL,
++                               KRB5_DEFAULT_SECONDARY_CACHE,
++                               &cc_target_tag_conf)) {
++            com_err(prog_name, retval , _("while allocating memory for the "
++                                          "target ccache name"));
++            exit(1);
++        }
++        /* Prepend "FILE:" if a cctype wasn't specified in the config. */
++        if (strchr(cc_target_tag_conf, ':')) {
++            cc_target_tag_tmp = strdup(cc_target_tag_conf);
++        } else {
++            if (asprintf(&cc_target_tag_tmp, "FILE:%s",
++                         cc_target_tag_conf) < 0)
++                cc_target_tag_tmp = NULL;
++        }
++        profile_release_string(cc_target_tag_conf);
++        if (cc_target_tag_tmp == NULL) {
++            com_err(prog_name, retval , _("while allocating memory for the "
++                                          "target ccache name"));
++            exit(1);
++        }
++        /* Resolve parameters in the configured value for the target user. */
++        if (asprintf(&target_user_uid_str, "%lu",
++                     (unsigned long)target_uid) < 0) {
++            com_err(prog_name, retval , _("while allocating memory for the "
++                                          "target ccache name"));
++            exit(1);
++        }
++        if (k5_expand_path_tokens_extra(ksu_context,
++                                        cc_target_tag_tmp, &cc_target_tag_conf,
++                                        "euid", target_user_uid_str,
++                                        "uid", target_user_uid_str,
++                                        "USERID", target_user_uid_str,
++                                        "username", target_user,
++                                        NULL) != 0) {
++            com_err(prog_name, retval , _("while allocating memory for the "
++                                          "target ccache name"));
++            exit(1);
++        }
++        cc_target_tag_tmp[strcspn(cc_target_tag_tmp, ":")] = '\0';
++        cc_target_switchable = krb5_cc_support_switch(ksu_context,
++                                                      cc_target_tag_tmp);
++        free(cc_target_tag_tmp);
++        /* Try to avoid destroying a target ccache. */
++        if (cc_target_switchable) {
++            snprintf(cc_target_tag, KRB5_SEC_BUFFSIZE, "%s",
++                     cc_target_tag_conf);
++        } else {
++            do {
++                snprintf(cc_target_tag, KRB5_SEC_BUFFSIZE, "%s.%d",
++                         cc_target_tag_conf, gen_sym());
++            } while (krb5_ccache_name_is_initialized(ksu_context,
++                                                     cc_target_tag));
++        }
++        cc_target_tag_tmp = strchr(cc_target_tag, ':') + 1;
++        krb5_free_string(ksu_context, cc_target_tag_conf);
+     }
+ 
+     if (auth_debug){
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0005-Be-more-careful-of-target-ccache-collections.patch b/SOURCES/0005-Be-more-careful-of-target-ccache-collections.patch
new file mode 100644
index 0000000..d939cd4
--- /dev/null
+++ b/SOURCES/0005-Be-more-careful-of-target-ccache-collections.patch
@@ -0,0 +1,179 @@
+From cd193e20361624c8a0dc43634c31c6ac2d3d150d Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@dahyabhai.net>
+Date: Wed, 30 Oct 2013 21:34:27 -0400
+Subject: [PATCH 5/6] Be more careful of target ccache collections
+
+When copying credentials to a cache collection, take care to avoid
+generating multiple caches for a single client principal, but don't
+change the primary out from anyone who might already be using the
+target collection.
+---
+ src/clients/ksu/ccache.c | 62 ++++++++++++++++++++++++++++++++++++++++++------
+ src/clients/ksu/ksu.h    |  2 +-
+ src/clients/ksu/main.c   | 11 +++++++--
+ 3 files changed, 65 insertions(+), 10 deletions(-)
+
+diff --git a/src/clients/ksu/ccache.c b/src/clients/ksu/ccache.c
+index 90ba2f2..2a97893 100644
+--- a/src/clients/ksu/ccache.c
++++ b/src/clients/ksu/ccache.c
+@@ -48,7 +48,7 @@ void show_credential();
+ 
+ krb5_error_code krb5_ccache_copy (context, cc_def, cc_other_tag,
+                                   primary_principal, destroy_def,
+-                                  cc_out, stored, target_uid)
++                                  cc_out, stored, reused, target_uid)
+ /* IN */
+     krb5_context context;
+     krb5_ccache cc_def;
+@@ -59,10 +59,12 @@ krb5_error_code krb5_ccache_copy (context, cc_def, cc_other_tag,
+     /* OUT */
+     krb5_ccache *cc_out;
+     krb5_boolean *stored;
++    krb5_boolean *reused;
+ {
+     int i=0;
+     krb5_ccache  * cc_other;
+     const char * cc_other_type;
++    char * saved_cc_default_name;
+     krb5_error_code retval=0;
+     krb5_creds ** cc_def_creds_arr = NULL;
+     krb5_creds ** cc_other_creds_arr = NULL;
+@@ -99,9 +101,33 @@ krb5_error_code krb5_ccache_copy (context, cc_def, cc_other_tag,
+         return errno;
+     }
+ 
+-
+-    if ((retval = krb5_cc_initialize(context, *cc_other, primary_principal))){
+-        return retval;
++    if (krb5_cc_support_switch(context, cc_other_type)) {
++        *reused = TRUE;
++        krb5_cc_close(context, *cc_other);
++        saved_cc_default_name = strdup(krb5_cc_default_name(context));
++        krb5_cc_set_default_name(context, cc_other_tag);
++        if (krb5_cc_cache_match(context, primary_principal, cc_other) != 0) {
++            *reused = FALSE;
++            retval = krb5_cc_new_unique(context, cc_other_type, NULL,
++                                        cc_other);
++            if (retval) {
++                krb5_cc_set_default_name(context, saved_cc_default_name);
++                free(saved_cc_default_name);
++                return retval;
++            }
++        }
++        retval = krb5_cc_initialize(context, *cc_other, primary_principal);
++        krb5_cc_set_default_name(context, saved_cc_default_name);
++        free(saved_cc_default_name);
++        if (retval) {
++            return retval;
++        }
++    } else {
++        *reused = FALSE;
++        retval = krb5_cc_initialize(context, *cc_other, primary_principal);
++        if (retval) {
++            return retval;
++        }
+     }
+ 
+     retval = krb5_store_all_creds(context, * cc_other, cc_def_creds_arr,
+@@ -650,6 +676,7 @@ krb5_error_code krb5_ccache_copy_restricted (context, cc_def, cc_other_tag,
+     int i=0;
+     krb5_ccache  * cc_other;
+     const char * cc_other_type;
++    char * saved_cc_default_name;
+     krb5_error_code retval=0;
+     krb5_creds ** cc_def_creds_arr = NULL;
+     krb5_creds ** cc_other_creds_arr = NULL;
+@@ -677,9 +704,30 @@ krb5_error_code krb5_ccache_copy_restricted (context, cc_def, cc_other_tag,
+         return errno;
+     }
+ 
+-
+-    if ((retval = krb5_cc_initialize(context, *cc_other, prst))){
+-        return retval;
++    if (krb5_cc_support_switch(context, cc_other_type)) {
++        krb5_cc_close(context, *cc_other);
++        saved_cc_default_name = strdup(krb5_cc_default_name(context));
++        krb5_cc_set_default_name(context, cc_other_tag);
++        if (krb5_cc_cache_match(context, prst, cc_other) != 0) {
++            retval = krb5_cc_new_unique(context, cc_other_type, NULL,
++                                        cc_other);
++            if (retval) {
++                krb5_cc_set_default_name(context, saved_cc_default_name);
++                free(saved_cc_default_name);
++                return retval;
++            }
++        }
++        retval = krb5_cc_initialize(context, *cc_other, prst);
++        if (retval) {
++            return retval;
++        }
++        krb5_cc_set_default_name(context, saved_cc_default_name);
++        free(saved_cc_default_name);
++    } else {
++        retval = krb5_cc_initialize(context, *cc_other, prst);
++        if (retval) {
++            return retval;
++        }
+     }
+ 
+     retval = krb5_store_some_creds(context, * cc_other,
+diff --git a/src/clients/ksu/ksu.h b/src/clients/ksu/ksu.h
+index a195f52..b3ef7b9 100644
+--- a/src/clients/ksu/ksu.h
++++ b/src/clients/ksu/ksu.h
+@@ -108,7 +108,7 @@ extern krb5_error_code get_best_principal
+ /* ccache.c */
+ extern krb5_error_code krb5_ccache_copy
+ (krb5_context, krb5_ccache, char *, krb5_principal,
+- krb5_boolean, krb5_ccache *, krb5_boolean *, uid_t);
++ krb5_boolean, krb5_ccache *, krb5_boolean *, krb5_boolean *, uid_t);
+ 
+ extern krb5_error_code krb5_store_all_creds
+ (krb5_context, krb5_ccache, krb5_creds **, krb5_creds **);
+diff --git a/src/clients/ksu/main.c b/src/clients/ksu/main.c
+index 58df6a1..1c0c822 100644
+--- a/src/clients/ksu/main.c
++++ b/src/clients/ksu/main.c
+@@ -117,6 +117,7 @@ main (argc, argv)
+     int pargc;
+     char ** pargv;
+     krb5_boolean stored = FALSE;
++    krb5_boolean reused = FALSE;
+     krb5_principal  kdc_server;
+     krb5_boolean zero_password;
+ 
+@@ -523,7 +524,8 @@ main (argc, argv)
+     } else {
+ 
+         retval = krb5_ccache_copy(ksu_context, cc_source, KRB5_TEMPORARY_CACHE,
+-                                  client, FALSE, &cc_tmp, &stored, 0);
++                                  client, FALSE, &cc_tmp, &stored, &reused,
++                                  0);
+         if (retval) {
+             com_err(prog_name, retval, _("while copying cache %s to %s"),
+                     krb5_cc_get_name(ksu_context, cc_source),
+@@ -801,7 +803,7 @@ main (argc, argv)
+ 
+     retval = krb5_ccache_copy(ksu_context, cc_tmp, cc_target_tag,
+                               client, TRUE, &cc_target, &stored,
+-                              target_pwd->pw_uid);
++                              &reused, target_pwd->pw_uid);
+     if (retval) {
+         com_err(prog_name, retval, _("while copying cache %s to %s"),
+                 krb5_cc_get_name(ksu_context, cc_tmp), cc_target_tag);
+@@ -825,6 +827,11 @@ main (argc, argv)
+                 sweep_up(ksu_context, cc_target);
+                 exit(1);
+             }
++            if (reused && !keep_target_cache) {
++                print_status(_("Reusing cache %s, it will not be removed.\n"),
++                             cc_target_tag);
++                keep_target_cache = TRUE;
++            }
+             krb5_free_string(ksu_context, cc_target_tag);
+         } else {
+             com_err(prog_name, retval, _("while reading cache name from %s"),
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0006-Copy-config-entries-to-the-target-ccache.patch b/SOURCES/0006-Copy-config-entries-to-the-target-ccache.patch
new file mode 100644
index 0000000..0d931eb
--- /dev/null
+++ b/SOURCES/0006-Copy-config-entries-to-the-target-ccache.patch
@@ -0,0 +1,28 @@
+From 3eeefbd93d0fa7dee9fba9a77fffbc959b83f056 Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@dahyabhai.net>
+Date: Tue, 29 Oct 2013 16:27:20 -0400
+Subject: [PATCH 6/6] Copy config entries to the target ccache
+
+When we try to screen out expired creds while reading them from one
+ccache to eventually store in another, also keep configuration entries.
+---
+ src/clients/ksu/ccache.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/clients/ksu/ccache.c b/src/clients/ksu/ccache.c
+index 2a97893..83b5e46 100644
+--- a/src/clients/ksu/ccache.c
++++ b/src/clients/ksu/ccache.c
+@@ -269,7 +269,8 @@ krb5_error_code krb5_get_nonexp_tkts(context, cc, creds_array)
+ 
+     while (!(retval = krb5_cc_next_cred(context, cc, &cur, &creds))){
+ 
+-        if ((retval = krb5_check_exp(context, creds.times))){
++        if (!krb5_is_config_principal(context, creds.server) &&
++            (retval = krb5_check_exp(context, creds.times))){
+             if (retval != KRB5KRB_AP_ERR_TKT_EXPIRED){
+                 return retval;
+             }
+-- 
+1.8.4.2
+
diff --git a/SOURCES/_kadmind b/SOURCES/_kadmind
new file mode 100644
index 0000000..b073cfb
--- /dev/null
+++ b/SOURCES/_kadmind
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+#  Check for error conditions which the init system expects us to check and
+#  for other common errors, and exit with the expected status codes.
+#
+kadmind=/usr/sbin/kadmind
+if test -f /var/kerberos/krb5kdc/kpropd.acl ; then
+	echo $"Error. This appears to be a slave server, found kpropd.acl"
+	exit 6
+fi
+if ! test -x "$kadmind" ; then
+	exit 5
+fi
+exec "$kadmind" "$@"
diff --git a/SOURCES/_kpropd b/SOURCES/_kpropd
new file mode 100644
index 0000000..1808368
--- /dev/null
+++ b/SOURCES/_kpropd
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+#  Check for error conditions which the init system expects us to check and
+#  for other common errors, and exit with the expected status codes.
+#
+kpropd=/usr/sbin/kpropd
+if ! test -f /var/kerberos/krb5kdc/kpropd.acl ; then
+	echo $"Error. This does not appear to be a slave server, kpropd.acl not found"
+	exit 6
+fi
+if ! test -x "$kpropd" ; then
+	exit 5
+fi
+exec "$kpropd" "$@"
diff --git a/SOURCES/kadm5.acl b/SOURCES/kadm5.acl
new file mode 100644
index 0000000..dc93eb0
--- /dev/null
+++ b/SOURCES/kadm5.acl
@@ -0,0 +1 @@
+*/admin@EXAMPLE.COM	*
diff --git a/SOURCES/kadmin.service b/SOURCES/kadmin.service
new file mode 100644
index 0000000..ede159e
--- /dev/null
+++ b/SOURCES/kadmin.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Kerberos 5 Password-changing and Administration
+After=syslog.target network.target
+
+[Service]
+Type=forking
+PIDFile=/var/run/kadmind.pid
+EnvironmentFile=-/etc/sysconfig/kadmin
+ExecStart=/usr/sbin/_kadmind -P /var/run/kadmind.pid $KADMIND_ARGS
+ExecReload=/bin/kill -HUP $MAINPID
+
+[Install]
+WantedBy=multi-user.target
diff --git a/SOURCES/kadmin.sysconfig b/SOURCES/kadmin.sysconfig
new file mode 100644
index 0000000..fa72039
--- /dev/null
+++ b/SOURCES/kadmin.sysconfig
@@ -0,0 +1 @@
+KADMIND_ARGS=
diff --git a/SOURCES/kadmind.init b/SOURCES/kadmind.init
new file mode 100755
index 0000000..8915e2b
--- /dev/null
+++ b/SOURCES/kadmind.init
@@ -0,0 +1,108 @@
+#!/bin/bash
+#
+# kadmind      Start and stop the Kerberos 5 administrative server.
+#
+# chkconfig:   - 35 65
+# description: Kerberos 5 is a trusted third-party authentication system.  \
+#	       This script starts and stops the Kerberos 5 administrative \
+#              server, which should only be run on the master server for a \
+#              realm.
+# processname: kadmind
+# config: /etc/sysconfig/kadmin
+# pidfile: /var/run/kadmind.pid
+#
+
+### BEGIN INIT INFO
+# Provides: kadmin
+# Required-Start: $local_fs $network
+# Required-Stop: $local_fs $network
+# Should-Start: portreserve
+# Default-Start:
+# Default-Stop: 0 1 2 3 4 5 6
+# Short-Description: start and stop the Kerberos 5 admin server
+# Description: The kadmind service allows administrators to remotely manage \
+#              the Kerberos 5 realm database.  It should only be run on a \
+#              master KDC.
+### END INIT INFO
+
+# Get config.
+. /etc/sysconfig/network
+
+# Get config.
+[ -r /etc/sysconfig/kadmin ] && . /etc/sysconfig/kadmin
+
+# Source function library.
+. /etc/init.d/functions
+prog="Kerberos 5 Admin Server"
+kadmind=/usr/sbin/kadmind
+pidfile=/var/run/kadmind.pid
+
+RETVAL=0
+
+# Shell functions to cut down on useless shell instances.
+start() {
+  	if [ -f /var/kerberos/krb5kdc/kpropd.acl ] ; then
+	    echo $"Error. This appears to be a slave server, found kpropd.acl"
+	    exit 6
+	else
+	    [ -x $kadmind ] || exit 5
+	fi
+	echo -n $"Starting $prog: "
+	# tell portreserve to release the kerberos-adm port
+	[ -x /sbin/portrelease ] && /sbin/portrelease kerberos-adm &>/dev/null || :
+	daemon ${kadmind} ${KRB5REALM:+-r ${KRB5REALM}} -P $pidfile $KADMIND_ARGS
+	RETVAL=$?
+	echo
+	if test $RETVAL -ne 0 ; then
+	    if status -l kadmin ${kadmind} > /dev/null ; then
+		RETVAL=0
+	    fi
+	fi
+	[ $RETVAL = 0 ] && touch /var/lock/subsys/kadmin
+}
+stop() {
+	echo -n $"Stopping $prog: "
+	killproc ${kadmind}
+	RETVAL=$?
+	echo
+	[ $RETVAL = 0 ] && rm -f /var/lock/subsys/kadmin
+}
+reload() {
+	echo -n $"Reopening $prog log file: "
+	killproc ${kadmind} -HUP
+	RETVAL=$?
+	echo
+}
+
+# See how we were called.
+case "$1" in
+  start)
+	start
+	;;
+  stop)
+	stop
+	;;
+  restart)
+	stop
+	start
+	;;
+  status)
+	status -l kadmin ${kadmind}
+	RETVAL=$?
+	;;
+  reload)
+	reload
+	;;
+  condrestart)
+	if [ -f /var/lock/subsys/kadmin ] ; then
+		stop
+		start
+	fi
+	;;
+  *)
+	echo $"Usage: $0 {start|stop|status|condrestart|reload|restart}"
+	RETVAL=2
+	;;
+esac
+
+exit $RETVAL
diff --git a/SOURCES/kadmind.logrotate b/SOURCES/kadmind.logrotate
new file mode 100644
index 0000000..52a66c4
--- /dev/null
+++ b/SOURCES/kadmind.logrotate
@@ -0,0 +1,9 @@
+/var/log/kadmind.log {
+    missingok
+    notifempty
+    monthly
+    rotate 12
+    postrotate
+	/bin/kill -HUP `cat /var/run/kadmind.pid 2>/dev/null` 2> /dev/null || true
+    endscript
+}
diff --git a/SOURCES/kdc.conf b/SOURCES/kdc.conf
new file mode 100644
index 0000000..e99219a
--- /dev/null
+++ b/SOURCES/kdc.conf
@@ -0,0 +1,12 @@
+[kdcdefaults]
+ kdc_ports = 88
+ kdc_tcp_ports = 88
+
+[realms]
+ EXAMPLE.COM = {
+  #master_key_type = aes256-cts
+  acl_file = /var/kerberos/krb5kdc/kadm5.acl
+  dict_file = /usr/share/dict/words
+  admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab
+  supported_enctypes = aes256-cts:normal aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal camellia256-cts:normal camellia128-cts:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal
+ }
diff --git a/SOURCES/kerberos-adm.portreserve b/SOURCES/kerberos-adm.portreserve
new file mode 100644
index 0000000..eb6080d
--- /dev/null
+++ b/SOURCES/kerberos-adm.portreserve
@@ -0,0 +1 @@
+kerberos-adm/tcp
diff --git a/SOURCES/kprop.service b/SOURCES/kprop.service
new file mode 100644
index 0000000..959a300
--- /dev/null
+++ b/SOURCES/kprop.service
@@ -0,0 +1,10 @@
+[Unit]
+Description=Kerberos 5 Propagation
+After=syslog.target network.target
+
+[Service]
+Type=forking
+ExecStart=/usr/sbin/_kpropd -S
+
+[Install]
+WantedBy=multi-user.target
diff --git a/SOURCES/kpropd.init b/SOURCES/kpropd.init
new file mode 100755
index 0000000..b872ee3
--- /dev/null
+++ b/SOURCES/kpropd.init
@@ -0,0 +1,92 @@
+#!/bin/bash
+#
+# kpropd.init  Start and stop the Kerberos 5 propagation client.
+#
+# chkconfig:   - 35 65
+# description: Kerberos 5 is a trusted third-party authentication system.  \
+#	       This script starts and stops the service that allows this \
+#              KDC to receive updates from your master KDC.
+# processname: kpropd
+#
+
+### BEGIN INIT INFO
+# Provides: kprop
+# Required-Start: $local_fs $network
+# Required-Stop: $local_fs $network
+# Should-Start: portreserve
+# Default-Start:
+# Default-Stop: 0 1 2 3 4 5 6
+# Short-Description: start and stop the Kerberos 5 propagation client
+# Description: The kpropd service accepts database updates pushed to it from \
+#              the master KDC.  It will never be needed on a master KDC.
+### END INIT INFO
+
+# Get config.
+. /etc/sysconfig/network
+
+# Source function library.
+. /etc/init.d/functions
+
+RETVAL=0
+prog="Kerberos 5 Propagation Server"
+kpropd=/usr/sbin/kpropd
+
+# Shell functions to cut down on useless shell instances.
+start() {
+  	[ -f /var/kerberos/krb5kdc/kpropd.acl ] || exit 6
+	[ -x $kpropd ] || exit 5
+	echo -n $"Starting $prog: "
+	# tell portreserve to release the krb5_prop port
+	[ -x /sbin/portrelease ] && /sbin/portrelease krb5_prop &>/dev/null || :
+	daemon ${kpropd} -S
+	RETVAL=$?
+	echo
+	if test $RETVAL -ne 0 ; then
+	    if status -l kprop ${kpropd} > /dev/null ; then
+		RETVAL=0
+	    fi
+	fi
+	[ $RETVAL = 0 ] && touch /var/lock/subsys/kprop
+}
+stop() {
+	echo -n $"Stopping $prog: "
+	killproc ${kpropd}
+	RETVAL=$?
+	echo
+	[ $RETVAL = 0 ] && rm -f /var/lock/subsys/kprop
+}
+
+# See how we were called.
+case "$1" in
+  start)
+	start
+	;;
+  stop)
+	stop
+	;;
+  # We don't really "do" reload, so treat it as a restart.
+  restart|force-reload)
+	stop
+	start
+	;;
+  reload)
+	echo "can't reload configuration, you have to restart it"
+	RETVAL=3
+	;;
+  status)
+	status -l kprop ${kpropd}
+	RETVAL=$?
+	;;
+  condrestart)
+	if [ -f /var/lock/subsys/kprop ] ; then
+		stop
+		start
+	fi
+	;;
+  *)
+	echo $"Usage: $0 {start|stop|restart|condrestart|reload|status|force-reload}"
+	RETVAL=2
+	;;
+esac
+
+exit $RETVAL
diff --git a/SOURCES/krb5-1.10-CVE-2013-1418.patch b/SOURCES/krb5-1.10-CVE-2013-1418.patch
new file mode 100644
index 0000000..0adb104
--- /dev/null
+++ b/SOURCES/krb5-1.10-CVE-2013-1418.patch
@@ -0,0 +1,37 @@
+Minor changes to apply to 1.10.3.
+
+commit c2ccf4197f697c4ff143b8a786acdd875e70a89d
+Author: Tom Yu <tlyu@mit.edu>
+Date:   Mon Nov 4 15:49:03 2013 -0500
+
+    Multi-realm KDC null deref [CVE-2013-1418]
+    
+    If a KDC serves multiple realms, certain requests can cause
+    setup_server_realm() to dereference a null pointer, crashing the KDC.
+    
+    CVSSv2: AV:N/AC:M/Au:N/C:N/I:N/A:P/E:POC/RL:OF/RC:C
+    
+    A related but more minor vulnerability requires authentication to
+    exploit, and is only present if a third-party KDC database module can
+    dereference a null pointer under certain conditions.
+    
+    (back ported from commit 5d2d9a1abe46a2c1a8614d4672d08d9d30a5f8bf)
+    
+    ticket: 7757 (new)
+    version_fixed: 1.10.7
+    status: resolved
+
+diff --git a/src/kdc/main.c b/src/kdc/main.c
+index b56ec19..7160607 100644
+--- a/src/kdc/main.c
++++ b/src/kdc/main.c
+@@ -140,6 +140,9 @@ setup_server_realm(krb5_principal sprinc)
+     int kdc_numrealms = handle->kdc_numrealms;
+ 
+     kret = 0;
++    if (sprinc == NULL)
++        return NULL;
++
+     if (kdc_numrealms > 1) {
+         if (!(newrealm = find_realm_data(handle, sprinc->realm.data,
+                                          (krb5_ui_4) sprinc->realm.length)))
diff --git a/SOURCES/krb5-1.10-buildconf.patch b/SOURCES/krb5-1.10-buildconf.patch
new file mode 100644
index 0000000..25b05ac
--- /dev/null
+++ b/SOURCES/krb5-1.10-buildconf.patch
@@ -0,0 +1,54 @@
+Build binaries in this package as RELRO PIEs, libraries as partial RELRO,
+and install shared libraries with the execute bit set on them.  Prune out
+the -L/usr/lib* and PIE flags where they might leak out and affect
+apps which just want to link with the libraries. FIXME: needs to check and
+not just assume that the compiler supports using these flags.
+
+--- krb5/src/config/shlib.conf
++++ krb5/src/config/shlib.conf
+@@ -419,7 +419,7 @@ mips-*-netbsd*)
+ 	SHLIBEXT=.so
+ 	# Linux ld doesn't default to stuffing the SONAME field...
+ 	# Use objdump -x to examine the fields of the library
+-	LDCOMBINE='$(CC) -shared -fPIC -Wl,-h,$(LIBPREFIX)$(LIBBASE)$(SHLIBSEXT),--no-undefined'
++	LDCOMBINE='$(CC) -shared -fPIC -Wl,-h,$(LIBPREFIX)$(LIBBASE)$(SHLIBSEXT),--no-undefined -Wl,-z,relro'
+ 	# 
+ 	LDCOMBINE_TAIL='-Wl,--version-script binutils.versions && $(PERL) -w $(top_srcdir)/util/export-check.pl $(SHLIB_EXPORT_FILE) $@'
+ 	SHLIB_EXPORT_FILE_DEP=binutils.versions
+@@ -430,7 +430,8 @@
+ 	SHLIB_EXPFLAGS='$(SHLIB_RPATH_FLAGS) $(SHLIB_DIRS) $(SHLIB_EXPLIBS)'
+ 	PROFFLAGS=-pg
+ 	PROG_RPATH_FLAGS='$(RPATH_FLAG)$(PROG_RPATH)'
+-	CC_LINK_SHARED='$(CC) $(PROG_LIBPATH) $(PROG_RPATH_FLAGS) $(CFLAGS) $(LDFLAGS)'
++	CC_LINK_SHARED='$(CC) $(PROG_LIBPATH) $(PROG_RPATH_FLAGS) $(CFLAGS) -pie -Wl,-z,relro -Wl,-z,now $(LDFLAGS)'
++	INSTALL_SHLIB='${INSTALL} -m755'
+ 	CC_LINK_STATIC='$(CC) $(PROG_LIBPATH) $(CFLAGS) $(LDFLAGS)'
+ 	CXX_LINK_SHARED='$(CXX) $(PROG_LIBPATH) $(PROG_RPATH_FLAGS) $(CXXFLAGS) $(LDFLAGS)'
+ 	CXX_LINK_STATIC='$(CXX) $(PROG_LIBPATH) $(CXXFLAGS) $(LDFLAGS)'
+--- krb5/src/krb5-config.in
++++ krb5/src/krb5-config.in
+@@ -189,6 +189,13 @@ if test -n "$do_libs"; then
+ 	    -e 's#\$(PTHREAD_CFLAGS)#'"$PTHREAD_CFLAGS"'#' \
+ 	    -e 's#\$(CFLAGS)##'`
+ 
++    if test `dirname $libdir` = /usr ; then
++        lib_flags=`echo $lib_flags | sed -e "s#-L$libdir##" -e "s#$RPATH_FLAG$libdir##"`
++    fi
++    lib_flags=`echo $lib_flags | sed -e "s#-fPIE##g" -e "s#-pie##g"`
++    lib_flags=`echo $lib_flags | sed -e "s#-Wl,-z,relro##g"`
++    lib_flags=`echo $lib_flags | sed -e "s#-Wl,-z,now##g"`
++
+     if test $library = 'kdb'; then
+ 	lib_flags="$lib_flags -lkdb5 $KDB5_DB_LIB"
+ 	library=krb5
+--- krb5/src/config/pre.in
++++ krb5/src/config/pre.in
+@@ -188,7 +188,7 @@
+ INSTALL_SCRIPT=@INSTALL_PROGRAM@
+ INSTALL_DATA=@INSTALL_DATA@
+ INSTALL_SHLIB=@INSTALL_SHLIB@
+-INSTALL_SETUID=$(INSTALL) $(INSTALL_STRIP) -m 4755 -o root
++INSTALL_SETUID=$(INSTALL) $(INSTALL_STRIP) -m 4755
+ ## This is needed because autoconf will sometimes define @exec_prefix@ to be
+ ## ${prefix}.
+ prefix=@prefix@
diff --git a/SOURCES/krb5-1.10-doublelog.patch b/SOURCES/krb5-1.10-doublelog.patch
new file mode 100644
index 0000000..c20075c
--- /dev/null
+++ b/SOURCES/krb5-1.10-doublelog.patch
@@ -0,0 +1,14 @@
+Don't double-log (actually, don't process /etc/krb5.conf twice) just
+because we built with --sysconfdir=/etc.  RT#3277
+
+--- krb5/src/include/Makefile.in
++++ krb5/src/include/Makefile.in
+@@ -67,6 +67,8 @@ PROCESS_REPLACE = -e "s+@KRB5RCTMPDIR+$(
+ 		  -e "s+@GSSMODULEDIR+$(GSS_MODULE_DIR)+" \
+ 		  -e 's+@LOCALSTATEDIR+$(LOCALSTATEDIR)+' \
+ 		  -e 's+@SYSCONFDIR+$(SYSCONFDIR)+' \
++		  -e 's+:/etc/krb5.conf:/etc/krb5.conf"+:/etc/krb5.conf"+' \
++		  -e 's+"/etc/krb5.conf:/etc/krb5.conf"+"/etc/krb5.conf"+' \
+ 		  -e 's+@DYNOBJEXT+$(DYNOBJEXT)+'
+ 
+ OSCONFSRC = $(srcdir)/osconf.hin
diff --git a/SOURCES/krb5-1.10-kpasswd_tcp.patch b/SOURCES/krb5-1.10-kpasswd_tcp.patch
new file mode 100644
index 0000000..fd8da8e
--- /dev/null
+++ b/SOURCES/krb5-1.10-kpasswd_tcp.patch
@@ -0,0 +1,32 @@
+Fall back to TCP on kdc-unresolvable/unreachable errors.  We still have
+to wait for UDP to fail, so this might not be ideal.  RT #5868.
+
+--- krb5/src/lib/krb5/os/changepw.c
++++ krb5/src/lib/krb5/os/changepw.c
+@@ -270,10 +270,22 @@ change_set_password(krb5_context context
+                          &callback_info, &chpw_rep, ss2sa(&remote_addr),
+                          &addrlen, NULL, NULL, NULL);
+         if (code) {
+-            /*
+-             * Here we may want to switch to TCP on some errors.
+-             * right?
+-             */
++            /* if we're not using a stream socket, and it's an error which
++             * might reasonably be specific to a datagram "connection", try
++             * again with a stream socket */
++            if (!use_tcp) {
++                switch (code) {
++                case KRB5_KDC_UNREACH:
++                case KRB5_REALM_CANT_RESOLVE:
++                case KRB5KRB_ERR_RESPONSE_TOO_BIG:
++                /* should we do this for more result codes than these? */
++                    k5_free_serverlist (&sl);
++                    use_tcp = 1;
++                    continue;
++                default:
++                    break;
++                }
++            }
+             break;
+         }
+ 
diff --git a/SOURCES/krb5-1.10-kprop-mktemp.patch b/SOURCES/krb5-1.10-kprop-mktemp.patch
new file mode 100644
index 0000000..62178df
--- /dev/null
+++ b/SOURCES/krb5-1.10-kprop-mktemp.patch
@@ -0,0 +1,28 @@
+Use an in-memory ccache to silence a compiler warning, for RT#6414.
+
+--- krb5/src/slave/kprop.c
++++ krb5/src/slave/kprop.c
+@@ -202,9 +202,8 @@ void PRS(argc, argv)
+ void get_tickets(context)
+     krb5_context context;
+ {
+-    char   buf[BUFSIZ], *def_realm;
++    char buf[] = "MEMORY:_kproptkt", *def_realm;
+     krb5_error_code retval;
+-    static char tkstring[] = "/tmp/kproptktXXXXXX";
+     krb5_keytab keytab = NULL;
+ 
+     /*
+@@ -229,11 +228,8 @@ void get_tickets(context)
+ #endif
+ 
+     /*
+-     * Initialize cache file which we're going to be using
++     * Initialize an in-memory cache for temporary use
+      */
+-    (void) mktemp(tkstring);
+-    snprintf(buf, sizeof(buf), "FILE:%s", tkstring);
+-
+     retval = krb5_cc_resolve(context, buf, &ccache);
+     if (retval) {
+         com_err(progname, retval, _("while opening credential cache %s"), buf);
diff --git a/SOURCES/krb5-1.10-ksu-path.patch b/SOURCES/krb5-1.10-ksu-path.patch
new file mode 100644
index 0000000..48443ef
--- /dev/null
+++ b/SOURCES/krb5-1.10-ksu-path.patch
@@ -0,0 +1,12 @@
+Set the default PATH to the one set by login.
+
+--- krb5/src/clients/ksu/Makefile.in
++++ krb5/src/clients/ksu/Makefile.in
+@@ -1,6 +1,6 @@
+ mydir=clients$(S)ksu
+ BUILDTOP=$(REL)..$(S)..
+-DEFINES = -DGET_TGT_VIA_PASSWD -DPRINC_LOOK_AHEAD -DCMD_PATH='"/bin /local/bin"'
++DEFINES = -DGET_TGT_VIA_PASSWD -DPRINC_LOOK_AHEAD -DCMD_PATH='"/usr/local/sbin /usr/local/bin /sbin /bin /usr/sbin /usr/bin"'
+ DEFS=
+ 
+ PROG_LIBPATH=-L$(TOPLIBD)
diff --git a/SOURCES/krb5-1.11-alpha1-init.patch b/SOURCES/krb5-1.11-alpha1-init.patch
new file mode 100644
index 0000000..67875e6
--- /dev/null
+++ b/SOURCES/krb5-1.11-alpha1-init.patch
@@ -0,0 +1,12 @@
+If krb5_init_context() fails, context is going to be NULL anyway.
+--- krb5/src/lib/krb5/krb/t_cc_config.c
++++ krb5/src/lib/krb5/krb/t_cc_config.c
+@@ -117,7 +117,7 @@ main(int argc, char **argv)
+     int c;
+     unsigned int i;
+ 
+-    bail_on_err(context, "Error initializing Kerberos library",
++    bail_on_err(NULL, "Error initializing Kerberos library",
+                 krb5_init_context(&context));
+     bail_on_err(context, "Error getting location of default ccache",
+                 krb5_cc_default(context, &ccache));
diff --git a/SOURCES/krb5-1.11-check_transited.patch b/SOURCES/krb5-1.11-check_transited.patch
new file mode 100644
index 0000000..f3ce693
--- /dev/null
+++ b/SOURCES/krb5-1.11-check_transited.patch
@@ -0,0 +1,56 @@
+commit 0406cd81ef9d18cd505fffabba3ac78901dc797d
+Author: Greg Hudson <ghudson@mit.edu>
+Date:   Wed Sep 25 10:40:23 2013 -0400
+
+    Support authoritative KDB check_transited methods
+    
+    In kdc_check_transited_list, consult the KDB module first.  If it
+    succeeds, treat this as authoritative and do not use the core
+    transited mechanisms.  Modules can return KRB5_PLUGIN_NO_HANDLE to
+    fall back to core mechanisms.
+    
+    ticket: 7709
+
+diff --git a/src/include/kdb.h b/src/include/kdb.h
+index bc01976..69817bc 100644
+--- a/src/include/kdb.h
++++ b/src/include/kdb.h
+@@ -1261,8 +1261,9 @@ typedef struct _kdb_vftabl {
+ 
+     /*
+      * Optional: Perform a policy check on a cross-realm ticket's transited
+-     * field and return an error (other than KRB5_PLUGIN_OP_NOTSUPP) if the
+-     * check fails.
++     * field.  Return 0 if the check authoritatively succeeds,
++     * KRB5_PLUGIN_NO_HANDLE to use the core transited-checking mechanisms, or
++     * another error (other than KRB5_PLUGIN_OP_NOTSUPP) if the check fails.
+      */
+     krb5_error_code (*check_transited_realms)(krb5_context kcontext,
+                                               const krb5_data *tr_contents,
+diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
+index bc638c1..5409078 100644
+--- a/src/kdc/kdc_util.c
++++ b/src/kdc/kdc_util.c
+@@ -1573,16 +1573,14 @@ kdc_check_transited_list(kdc_realm_t *kdc_active_realm,
+ {
+     krb5_error_code             code;
+ 
+-    /* Check using krb5.conf */
+-    code = krb5_check_transited_list(kdc_context, trans, realm1, realm2);
+-    if (code)
++    /* Check against the KDB module.  Treat this answer as authoritative if the
++     * method is supported and doesn't explicitly pass control. */
++    code = krb5_db_check_transited_realms(kdc_context, trans, realm1, realm2);
++    if (code != KRB5_PLUGIN_OP_NOTSUPP && code != KRB5_PLUGIN_NO_HANDLE)
+         return code;
+ 
+-    /* Check against the KDB module. */
+-    code = krb5_db_check_transited_realms(kdc_context, trans, realm1, realm2);
+-    if (code == KRB5_PLUGIN_OP_NOTSUPP)
+-        code = 0;
+-    return code;
++    /* Check using krb5.conf [capaths] or hierarchical relationships. */
++    return krb5_check_transited_list(kdc_context, trans, realm1, realm2);
+ }
+ 
+ krb5_error_code
diff --git a/SOURCES/krb5-1.11-dirsrv-accountlock.patch b/SOURCES/krb5-1.11-dirsrv-accountlock.patch
new file mode 100644
index 0000000..12d201d
--- /dev/null
+++ b/SOURCES/krb5-1.11-dirsrv-accountlock.patch
@@ -0,0 +1,61 @@
+Treat 'nsAccountLock: true' the same as 'loginDisabled: true'.  Updated from
+original version filed as RT#5891.
+
+diff -up krb5-1.8/src/aclocal.m4.dirsrv-accountlock krb5-1.8/src/aclocal.m4
+--- krb5-1.8/src/aclocal.m4.dirsrv-accountlock	2010-03-05 11:03:09.000000000 -0500
++++ krb5-1.8/src/aclocal.m4	2010-03-05 11:03:10.000000000 -0500
+@@ -1656,6 +1656,15 @@ if test $with_ldap = yes; then
+   AC_MSG_NOTICE(enabling OpenLDAP database backend module support)
+   OPENLDAP_PLUGIN=yes
+ fi
++AC_ARG_WITH([dirsrv-account-locking],
++[  --with-dirsrv-account-locking       compile 389/Red Hat/Fedora/Netscape Directory Server database backend module],
++[case "$withval" in
++    yes | no) ;;
++    *)  AC_MSG_ERROR(Invalid option value --with-dirsrv-account-locking="$withval") ;;
++esac], with_dirsrv_account_locking=no)
++if test $with_dirsrv_account_locking = yes; then
++    AC_DEFINE(HAVE_DIRSRV_ACCOUNT_LOCKING,1,[Define if LDAP KDB interface should heed 389 DS's nsAccountLock attribute.])
++fi
+ ])dnl
+ dnl
+ dnl If libkeyutils exists (on Linux) include it and use keyring ccache
+diff -up krb5-1.8/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c.dirsrv-accountlock krb5-1.8/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
+--- krb5-1.8/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c.dirsrv-accountlock	2009-11-24 18:52:25.000000000 -0500
++++ krb5-1.8/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c	2010-03-05 11:03:10.000000000 -0500
+@@ -2101,6 +2101,22 @@ populate_krb5_db_entry(krb5_context cont
+         goto cleanup;
+     if ((st=krb5_dbe_update_tl_data(context, entry, &userinfo_tl_data)) != 0)
+         goto cleanup;
++#ifdef HAVE_DIRSRV_ACCOUNT_LOCKING
++    {
++        krb5_timestamp              expiretime=0;
++        char                        *is_login_disabled=NULL;
++
++        /* LOGIN DISABLED */
++        if ((st=krb5_ldap_get_string(ld, ent, "nsAccountLock", &is_login_disabled,
++                    &attr_present)) != 0)
++            goto cleanup;
++        if (attr_present == TRUE) {
++            if (strcasecmp(is_login_disabled, "TRUE")== 0)
++                entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
++            free (is_login_disabled);
++        }
++    }
++#endif
+ 
+     if ((st=krb5_read_tkt_policy (context, ldap_context, entry, tktpolname)) !=0)
+         goto cleanup;
+diff -up krb5-1.8/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c.dirsrv-accountlock krb5-1.8/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c
+--- krb5-1.8/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c.dirsrv-accountlock	2009-11-24 18:52:25.000000000 -0500
++++ krb5-1.8/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c	2010-03-05 11:03:10.000000000 -0500
+@@ -59,6 +59,9 @@ char     *principal_attributes[] = { "kr
+                                      "krbLastFailedAuth",
+                                      "krbLoginFailedCount",
+                                      "krbLastSuccessfulAuth",
++#ifdef HAVE_DIRSRV_ACCOUNT_LOCKING
++                                     "nsAccountLock",
++#endif
+                                      "krbLastPwdChange",
+                                      "krbLastAdminUnlock",
+                                      "krbExtraData",
diff --git a/SOURCES/krb5-1.11-gss-client-keytab.patch b/SOURCES/krb5-1.11-gss-client-keytab.patch
new file mode 100644
index 0000000..3c1e964
--- /dev/null
+++ b/SOURCES/krb5-1.11-gss-client-keytab.patch
@@ -0,0 +1,131 @@
+Originally http://fedorapeople.org/cgit/simo/public_git/krb5.git/commit/?h=gss_cs&id=a3b9bf20df1d976775ed929d8cb5f4844e03b1bf
+
+commit a3b9bf20df1d976775ed929d8cb5f4844e03b1bf
+Author: Simo Sorce <simo@redhat.com>
+Date:   Thu Mar 28 12:53:01 2013 -0400
+
+    Add support for client keytab from cred store
+    
+    The new credential store extensions added support for specifying a
+    specific ccache name and also a specific keytab to be used for accepting
+    security contexts, but did not add a way to specify a client keytab
+    to be used in conjunction with the Keytab initiation support added also
+    in 1.11
+    
+    This patch introduces a new URN named client_keytab through which a
+    specific client_keytab can be set when calling gss_acquire_cred_from()
+    and Keytab Initiation will use that keytab to initialize credentials.
+
+diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c
+index 4d499e4..8540bf3 100644
+--- a/src/lib/gssapi/krb5/acquire_cred.c
++++ b/src/lib/gssapi/krb5/acquire_cred.c
+@@ -636,6 +636,7 @@ acquire_init_cred(krb5_context context,
+                   OM_uint32 *minor_status,
+                   krb5_ccache req_ccache,
+                   gss_buffer_t password,
++                  krb5_keytab client_keytab,
+                   krb5_gss_cred_id_rec *cred)
+ {
+     krb5_error_code code;
+@@ -659,9 +660,13 @@ acquire_init_cred(krb5_context context,
+             goto error;
+     }
+ 
+-    code = krb5_kt_client_default(context, &cred->client_keytab);
+-    if (code)
+-        goto error;
++    if (client_keytab != NULL)
++        cred->client_keytab = client_keytab;
++    else {
++        code = krb5_kt_client_default(context, &cred->client_keytab);
++        if (code)
++            goto error;
++    }
+ 
+     if (password != GSS_C_NO_BUFFER) {
+         pwdata = make_data(password->value, password->length);
+@@ -719,8 +724,9 @@ static OM_uint32
+ acquire_cred_context(krb5_context context, OM_uint32 *minor_status,
+                      gss_name_t desired_name, gss_buffer_t password,
+                      OM_uint32 time_req, gss_cred_usage_t cred_usage,
+-                     krb5_ccache ccache, krb5_keytab keytab,
+-                     krb5_boolean iakerb, gss_cred_id_t *output_cred_handle,
++                     krb5_ccache ccache, krb5_keytab client_keytab,
++                     krb5_keytab keytab, krb5_boolean iakerb,
++                     gss_cred_id_t *output_cred_handle,
+                      OM_uint32 *time_rec)
+ {
+     krb5_gss_cred_id_t cred = NULL;
+@@ -787,7 +793,8 @@ acquire_cred_context(krb5_context context, OM_uint32 *minor_status,
+      * in cred->name if it wasn't set above.
+      */
+     if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
+-        ret = acquire_init_cred(context, minor_status, ccache, password, cred);
++        ret = acquire_init_cred(context, minor_status, ccache, password,
++                                client_keytab, cred);
+         if (ret != GSS_S_COMPLETE)
+             goto error_out;
+     }
+@@ -864,8 +871,8 @@ acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
+     }
+ 
+     ret = acquire_cred_context(context, minor_status, desired_name, password,
+-                               time_req, cred_usage, ccache, keytab, iakerb,
+-                               output_cred_handle, time_rec);
++                               time_req, cred_usage, ccache, NULL, keytab,
++                               iakerb, output_cred_handle, time_rec);
+ 
+ out:
+     krb5_free_context(context);
+@@ -1130,6 +1137,7 @@ krb5_gss_acquire_cred_from(OM_uint32 *minor_status,
+ {
+     krb5_context context = NULL;
+     krb5_error_code code = 0;
++    krb5_keytab client_keytab = NULL;
+     krb5_keytab keytab = NULL;
+     krb5_ccache ccache = NULL;
+     const char *value;
+@@ -1162,6 +1170,19 @@ krb5_gss_acquire_cred_from(OM_uint32 *minor_status,
+         }
+     }
+ 
++    ret = kg_value_from_cred_store(cred_store, KRB5_CS_CLI_KEYTAB_URN, &value);
++    if (GSS_ERROR(ret))
++        goto out;
++
++    if (value) {
++        code = krb5_kt_resolve(context, value, &client_keytab);
++        if (code != 0) {
++            *minor_status = code;
++            ret = GSS_S_CRED_UNAVAIL;
++            goto out;
++        }
++    }
++
+     ret = kg_value_from_cred_store(cred_store, KRB5_CS_KEYTAB_URN, &value);
+     if (GSS_ERROR(ret))
+         goto out;
+@@ -1176,8 +1197,8 @@ krb5_gss_acquire_cred_from(OM_uint32 *minor_status,
+     }
+ 
+     ret = acquire_cred_context(context, minor_status, desired_name, NULL,
+-                               time_req, cred_usage, ccache, keytab, 0,
+-                               output_cred_handle, time_rec);
++                               time_req, cred_usage, ccache, client_keytab,
++                               keytab, 0, output_cred_handle, time_rec);
+ 
+ out:
+     if (ccache != NULL)
+diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h
+index 8215b10..310ff58 100644
+--- a/src/lib/gssapi/krb5/gssapiP_krb5.h
++++ b/src/lib/gssapi/krb5/gssapiP_krb5.h
+@@ -1227,6 +1227,7 @@ data_to_gss(krb5_data *input_k5data, gss_buffer_t output_buffer)
+ 
+ /* Credential store extensions */
+ 
++#define KRB5_CS_CLI_KEYTAB_URN "client_keytab"
+ #define KRB5_CS_KEYTAB_URN "keytab"
+ #define KRB5_CS_CCACHE_URN "ccache"
+ 
diff --git a/SOURCES/krb5-1.11-gss-methods1.patch b/SOURCES/krb5-1.11-gss-methods1.patch
new file mode 100644
index 0000000..ad7b07f
--- /dev/null
+++ b/SOURCES/krb5-1.11-gss-methods1.patch
@@ -0,0 +1,38 @@
+commit ee53a887bead08ec1354de3e74659da537f87515
+Author: Simo Sorce <simo@redhat.com>
+Date:   Sat Jul 20 13:19:19 2013 -0400
+
+    Load cred store functions from GSS modules
+    
+    When the credential store feature was implement the related functions
+    were added to struct gss_config, but the initialization function that
+    dynamically loads modules was not changed to see if the plugin being
+    loaded provided such functions.
+    
+    This will allow non-builtin mechanism and interposer mechanism to
+    implement custom credential store extensions if they wish.
+    
+    ticket: 7682
+
+diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c
+index f5b8b15..70cc4ee 100644
+--- a/src/lib/gssapi/mechglue/g_initialize.c
++++ b/src/lib/gssapi/mechglue/g_initialize.c
+@@ -680,6 +680,8 @@ build_dynamicMech(void *dl, const gss_OID mech_type)
+         GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_mech_for_saslname);
+         /* RFC 5587 */
+         GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_attrs_for_mech);
++	GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_acquire_cred_from);
++	GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_store_cred_into);
+ 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_acquire_cred_with_password);
+ 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_sec_context_by_mech);
+ 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_name_by_mech);
+@@ -778,6 +780,8 @@ build_interMech(void *dl, const gss_OID mech_type)
+ 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_mech_for_saslname);
+ 	/* RFC 5587 */
+ 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_attrs_for_mech);
++	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _acquire_cred_from);
++	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _store_cred_into);
+ 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _acquire_cred_with_password);
+ 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_sec_context_by_mech);
+ 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_name_by_mech);
diff --git a/SOURCES/krb5-1.11-gss-methods2.patch b/SOURCES/krb5-1.11-gss-methods2.patch
new file mode 100644
index 0000000..6d6e620
--- /dev/null
+++ b/SOURCES/krb5-1.11-gss-methods2.patch
@@ -0,0 +1,38 @@
+commit 744d6f873393b6bbd12e1c1884738676a089fa65
+Author: Simo Sorce <simo@redhat.com>
+Date:   Sat Jul 20 13:20:43 2013 -0400
+
+    Load import/export cred functions from GSS modules
+    
+    When the import/export credential feature was implement the related
+    functions were added to struct gss_config, but the initialization
+    function that dynamically loads modules was not changed to see if
+    the plugin being loaded provided such functions.
+    
+    This will allow non-builtin mechanism and interposer mechanism to
+    implement custom import/export credential extensions if they wish.
+    
+    ticket: 7682
+
+diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c
+index 70cc4ee..48a825e 100644
+--- a/src/lib/gssapi/mechglue/g_initialize.c
++++ b/src/lib/gssapi/mechglue/g_initialize.c
+@@ -683,6 +683,8 @@ build_dynamicMech(void *dl, const gss_OID mech_type)
+ 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_acquire_cred_from);
+ 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_store_cred_into);
+ 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_acquire_cred_with_password);
++	GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_export_cred);
++	GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_import_cred);
+ 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_sec_context_by_mech);
+ 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_name_by_mech);
+ 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_cred_by_mech);
+@@ -783,6 +785,8 @@ build_interMech(void *dl, const gss_OID mech_type)
+ 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _acquire_cred_from);
+ 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _store_cred_into);
+ 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _acquire_cred_with_password);
++	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_cred);
++	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _import_cred);
+ 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_sec_context_by_mech);
+ 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_name_by_mech);
+ 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_cred_by_mech);
diff --git a/SOURCES/krb5-1.11-kpasswdtest.patch b/SOURCES/krb5-1.11-kpasswdtest.patch
new file mode 100644
index 0000000..f07b225
--- /dev/null
+++ b/SOURCES/krb5-1.11-kpasswdtest.patch
@@ -0,0 +1,10 @@
+--- krb5-1.11.3/src/kadmin/testing/proto/krb5.conf.proto
++++ krb5-1.11.3/src/kadmin/testing/proto/krb5.conf.proto
+@@ -7,6 +7,7 @@
+ 	__REALM__ = {
+ 		kdc = __KDCHOST__:1750
+ 		admin_server = __KDCHOST__:1751
++		kpasswd_server = __KDCHOST__:1752
+ 		database_module = foobar_db2_module_blah
+ 	}
+ 
diff --git a/SOURCES/krb5-1.11-pam.patch b/SOURCES/krb5-1.11-pam.patch
new file mode 100644
index 0000000..9e1d516
--- /dev/null
+++ b/SOURCES/krb5-1.11-pam.patch
@@ -0,0 +1,753 @@
+Modify ksu so that it performs account and session management on behalf of
+the target user account, mimicking the action of regular su.  The default
+service name is "ksu", because on Fedora at least the configuration used
+is determined by whether or not a login shell is being opened, and so
+this may need to vary, too.  At run-time, ksu's behavior can be reset to
+the earlier, non-PAM behavior by setting "use_pam" to false in the [ksu]
+section of /etc/krb5.conf.
+
+When enabled, ksu gains a dependency on libpam.
+
+Originally RT#5939, though it's changed since then to perform the account
+and session management before dropping privileges, and to apply on top of
+changes we're proposing for how it handles cache collections.
+
+diff -up krb5-1.8/src/aclocal.m4.pam krb5-1.8/src/aclocal.m4
+--- krb5-1.8/src/aclocal.m4.pam	2009-11-22 12:00:45.000000000 -0500
++++ krb5-1.8/src/aclocal.m4	2010-03-05 10:48:08.000000000 -0500
+@@ -1703,3 +1703,70 @@ AC_DEFUN(KRB5_AC_KEYRING_CCACHE,[
+       ]))
+ ])dnl
+ dnl
++dnl
++dnl Use PAM instead of local crypt() compare for checking local passwords,
++dnl and perform PAM account, session management, and password-changing where
++dnl appropriate.
++dnl 
++AC_DEFUN(KRB5_WITH_PAM,[
++AC_ARG_WITH(pam,[AC_HELP_STRING(--with-pam,[compile with PAM support])],
++	    withpam="$withval",withpam=auto)
++AC_ARG_WITH(pam-ksu-service,[AC_HELP_STRING(--with-ksu-service,[PAM service name for ksu ["ksu"]])],
++	    withksupamservice="$withval",withksupamservice=ksu)
++old_LIBS="$LIBS"
++if test "$withpam" != no ; then
++	AC_MSG_RESULT([checking for PAM...])
++	PAM_LIBS=
++
++	AC_CHECK_HEADERS(security/pam_appl.h)
++	if test "x$ac_cv_header_security_pam_appl_h" != xyes ; then
++		if test "$withpam" = auto ; then
++			AC_MSG_RESULT([Unable to locate security/pam_appl.h.])
++			withpam=no
++		else
++			AC_MSG_ERROR([Unable to locate security/pam_appl.h.])
++		fi
++	fi
++
++	LIBS=
++	unset ac_cv_func_pam_start
++	AC_CHECK_FUNCS(putenv pam_start)
++	if test "x$ac_cv_func_pam_start" = xno ; then
++		unset ac_cv_func_pam_start
++		AC_CHECK_LIB(dl,dlopen)
++		AC_CHECK_FUNCS(pam_start)
++		if test "x$ac_cv_func_pam_start" = xno ; then
++			AC_CHECK_LIB(pam,pam_start)
++			unset ac_cv_func_pam_start
++			unset ac_cv_func_pam_getenvlist
++			AC_CHECK_FUNCS(pam_start pam_getenvlist)
++			if test "x$ac_cv_func_pam_start" = xyes ; then
++				PAM_LIBS="$LIBS"
++			else
++				if test "$withpam" = auto ; then
++					AC_MSG_RESULT([Unable to locate libpam.])
++					withpam=no
++				else
++					AC_MSG_ERROR([Unable to locate libpam.])
++				fi
++			fi
++		fi
++	fi
++	if test "$withpam" != no ; then
++		AC_MSG_NOTICE([building with PAM support])
++		AC_DEFINE(USE_PAM,1,[Define if Kerberos-aware tools should support PAM])
++		AC_DEFINE_UNQUOTED(KSU_PAM_SERVICE,"$withksupamservice",
++				   [Define to the name of the PAM service name to be used by ksu.])
++		PAM_LIBS="$LIBS"
++		NON_PAM_MAN=".\\\" "
++		PAM_MAN=
++	else
++		PAM_MAN=".\\\" "
++		NON_PAM_MAN=
++	fi
++fi
++LIBS="$old_LIBS"
++AC_SUBST(PAM_LIBS)
++AC_SUBST(PAM_MAN)
++AC_SUBST(NON_PAM_MAN)
++])dnl
+diff -up krb5-1.8/src/clients/ksu/main.c.pam krb5-1.8/src/clients/ksu/main.c
+--- krb5-1.8/src/clients/ksu/main.c.pam	2009-11-02 22:27:56.000000000 -0500
++++ krb5-1.8/src/clients/ksu/main.c	2010-03-05 10:48:08.000000000 -0500
+@@ -26,6 +26,7 @@
+  * KSU was writen by:  Ari Medvinsky, ari@isi.edu
+  */
+ 
++#include "autoconf.h"
+ #include "ksu.h"
+ #include "adm_proto.h"
+ #include "../../lib/krb5/os/os-proto.h"
+@@ -33,6 +34,10 @@
+ #include <signal.h>
+ #include <grp.h>
+ 
++#ifdef USE_PAM
++#include "pam.h"
++#endif
++
+ /* globals */
+ char * prog_name;
+ int auth_debug =0;
+@@ -40,6 +45,7 @@ char k5login_path[MAXPATHLEN];
+ char k5users_path[MAXPATHLEN];
+ char * gb_err = NULL;
+ int quiet = 0;
++int force_fork = 0;
+ /***********/
+ 
+ #define _DEF_CSH "/bin/csh"
+@@ -586,6 +592,25 @@ main (argc, argv)
+                prog_name,target_user,client_name,
+                source_user,ontty());
+ 
++#ifdef USE_PAM
++        if (appl_pam_enabled(ksu_context, "ksu")) {
++            if (appl_pam_acct_mgmt(KSU_PAM_SERVICE, 1, target_user, NULL,
++                                   NULL, source_user,
++                                   ttyname(STDERR_FILENO)) != 0) {
++                fprintf(stderr, "Access denied for %s.\n", target_user);
++                sweep_up(ksu_context, cc_tmp);
++                exit(1);
++            }
++            if (appl_pam_requires_chauthtok()) {
++                fprintf(stderr, "Password change required for %s.\n",
++                        target_user);
++                sweep_up(ksu_context, cc_tmp);
++                exit(1);
++            }
++            force_fork++;
++        }
++#endif
++
+         /* Run authorization as target.*/
+         if (krb5_seteuid(target_uid)) {
+             com_err(prog_name, errno, _("while switching to target for "
+@@ -651,6 +676,26 @@
+             sweep_up(ksu_context, cc_tmp);
+             exit(1);
+         }
++#ifdef USE_PAM
++    } else {
++        /* we always do PAM account management, even for root */
++        if (appl_pam_enabled(ksu_context, "ksu")) {
++            if (appl_pam_acct_mgmt(KSU_PAM_SERVICE, 1, target_user, NULL,
++                                   NULL, source_user,
++                                   ttyname(STDERR_FILENO)) != 0) {
++                fprintf(stderr, "Access denied for %s.\n", target_user);
++                sweep_up(ksu_context, cc_tmp);
++                exit(1);
++            }
++            if (appl_pam_requires_chauthtok()) {
++                fprintf(stderr, "Password change required for %s.\n",
++                        target_user);
++                sweep_up(ksu_context, cc_tmp);
++                exit(1);
++            }
++            force_fork++;
++        }
++#endif
+     }
+ 
+     if( some_rest_copy){
+@@ -720,6 +745,32 @@
+         exit(1);
+     }
+ 
++#ifdef USE_PAM
++    if (appl_pam_enabled(ksu_context, "ksu")) {
++        if (appl_pam_session_open() != 0) {
++            fprintf(stderr, "Error opening session for %s.\n", target_user);
++            sweep_up(ksu_context, cc_tmp);
++            exit(1);
++        }
++#ifdef DEBUG
++        if (auth_debug){
++            printf(" Opened PAM session.\n");
++        }
++#endif
++        if (appl_pam_cred_init()) {
++            fprintf(stderr, "Error initializing credentials for %s.\n",
++                    target_user);
++            sweep_up(ksu_context, cc_tmp);
++            exit(1);
++        }
++#ifdef DEBUG
++        if (auth_debug){
++            printf(" Initialized PAM credentials.\n");
++        }
++#endif
++    }
++#endif
++
+     /* set permissions */
+     if (setgid(target_pwd->pw_gid) < 0) {
+         perror("ksu: setgid");
+@@ -792,7 +817,7 @@ main (argc, argv)
+         fprintf(stderr, "program to be execed %s\n",params[0]);
+     }
+ 
+-    if( keep_target_cache ) {
++    if( keep_target_cache && !force_fork ) {
+         execv(params[0], params);
+         com_err(prog_name, errno, _("while trying to execv %s"), params[0]);
+         sweep_up(ksu_context, cc_target);
+@@ -823,16 +875,35 @@ main (argc, argv)
+             if (ret_pid == -1) {
+                 com_err(prog_name, errno, _("while calling waitpid"));
+             }
+-            sweep_up(ksu_context, cc_target);
++            if( !keep_target_cache ) {
++                sweep_up(ksu_context, cc_target);
++            }
+             exit (statusp);
+         case -1:
+             com_err(prog_name, errno, _("while trying to fork."));
+             sweep_up(ksu_context, cc_target);
+             exit (1);
+         case 0:
++#ifdef USE_PAM
++            if (appl_pam_enabled(ksu_context, "ksu")) {
++                if (appl_pam_setenv() != 0) {
++                    fprintf(stderr, "Error setting up environment for %s.\n",
++                            target_user);
++                    exit (1);
++                }
++#ifdef DEBUG
++                if (auth_debug){
++                    printf(" Set up PAM environment.\n");
++                }
++#endif
++            }
++#endif
+             execv(params[0], params);
+             com_err(prog_name, errno, _("while trying to execv %s"),
+                     params[0]);
++            if( keep_target_cache ) {
++                sweep_up(ksu_context, cc_target);
++            }
+             exit (1);
+         }
+     }
+diff -up krb5-1.8/src/clients/ksu/Makefile.in.pam krb5-1.8/src/clients/ksu/Makefile.in
+--- krb5-1.8/src/clients/ksu/Makefile.in.pam	2009-11-22 13:13:29.000000000 -0500
++++ krb5-1.8/src/clients/ksu/Makefile.in	2010-03-05 11:55:14.000000000 -0500
+@@ -7,12 +7,14 @@
+ PROG_RPATH=$(KRB5_LIBDIR)
+ 
+ KSU_LIBS=@KSU_LIBS@
++PAM_LIBS=@PAM_LIBS@
+ 
+ SRCS = \
+ 	$(srcdir)/krb_auth_su.c \
+ 	$(srcdir)/ccache.c \
+ 	$(srcdir)/authorization.c \
+ 	$(srcdir)/main.c \
++	$(srcdir)/pam.c \
+ 	$(srcdir)/heuristic.c \
+ 	$(srcdir)/xmalloc.c \
+ 	$(srcdir)/setenv.c
+@@ -21,13 +23,17 @@ OBJS = \
+ 	ccache.o \
+ 	authorization.o \
+ 	main.o \
++	pam.o \
+ 	heuristic.o \
+ 	xmalloc.o @SETENVOBJ@
+ 
+ all:: ksu
+ 
+ ksu: $(OBJS) $(KRB5_BASE_DEPLIBS)
+-	$(CC_LINK) -o $@ $(OBJS) $(KRB5_BASE_LIBS) $(KSU_LIBS)
++	$(CC_LINK) -o $@ $(OBJS) $(KRB5_BASE_LIBS) $(KSU_LIBS) $(PAM_LIBS)
++
++pam.o: pam.c
++	$(CC) $(ALL_CFLAGS) -c $<
+ 
+ clean::
+ 	$(RM) ksu
+diff -up krb5-1.8/src/clients/ksu/pam.c.pam krb5-1.8/src/clients/ksu/pam.c
+--- krb5-1.8/src/clients/ksu/pam.c.pam	2010-03-05 10:48:08.000000000 -0500
++++ krb5-1.8/src/clients/ksu/pam.c	2010-03-05 10:48:08.000000000 -0500
+@@ -0,0 +1,389 @@
++/*
++ * src/clients/ksu/pam.c
++ *
++ * Copyright 2007,2009,2010 Red Hat, Inc.
++ *
++ * All Rights Reserved.
++ *
++ * 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.
++ *
++ *  Neither the name of Red Hat, Inc. nor the names of its contributors may be
++ *  used to endorse or promote products derived from this software without
++ *  specific prior written permission.
++ *
++ * 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 OWNER 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.
++ * 
++ * Convenience wrappers for using PAM.
++ */
++
++#include "autoconf.h"
++#ifdef USE_PAM
++#include <sys/types.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include "k5-int.h"
++#include "pam.h"
++
++#ifndef MAXPWSIZE
++#define MAXPWSIZE 128
++#endif
++
++static int appl_pam_started;
++static pid_t appl_pam_starter = -1;
++static int appl_pam_session_opened;
++static int appl_pam_creds_initialized;
++static int appl_pam_pwchange_required;
++static pam_handle_t *appl_pamh;
++static struct pam_conv appl_pam_conv;
++static char *appl_pam_user;
++struct appl_pam_non_interactive_args {
++	const char *user;
++	const char *password;
++};
++
++int
++appl_pam_enabled(krb5_context context, const char *section)
++{
++	int enabled = 1;
++	if ((context != NULL) && (context->profile != NULL)) {
++		if (profile_get_boolean(context->profile,
++					section,
++					USE_PAM_CONFIGURATION_KEYWORD,
++					NULL,
++					enabled, &enabled) != 0) {
++			enabled = 1;
++		}
++	}
++	return enabled;
++}
++
++void
++appl_pam_cleanup(void)
++{
++	if (getpid() != appl_pam_starter) {
++		return;
++	}
++#ifdef DEBUG
++	printf("Called to clean up PAM.\n");
++#endif
++	if (appl_pam_creds_initialized) {
++#ifdef DEBUG
++		printf("Deleting PAM credentials.\n");
++#endif
++		pam_setcred(appl_pamh, PAM_DELETE_CRED);
++		appl_pam_creds_initialized = 0;
++	}
++	if (appl_pam_session_opened) {
++#ifdef DEBUG
++		printf("Closing PAM session.\n");
++#endif
++		pam_close_session(appl_pamh, 0);
++		appl_pam_session_opened = 0;
++	}
++	appl_pam_pwchange_required = 0;
++	if (appl_pam_started) {
++#ifdef DEBUG
++		printf("Shutting down PAM.\n");
++#endif
++		pam_end(appl_pamh, 0);
++		appl_pam_started = 0;
++		appl_pam_starter = -1;
++		free(appl_pam_user);
++		appl_pam_user = NULL;
++	}
++}
++static int
++appl_pam_interactive_converse(int num_msg, const struct pam_message **msg,
++			      struct pam_response **presp, void *appdata_ptr)
++{
++	const struct pam_message *message;
++	struct pam_response *resp;
++	int i, code;
++	char *pwstring, pwbuf[MAXPWSIZE];
++	unsigned int pwsize;
++	resp = malloc(sizeof(struct pam_response) * num_msg);
++	if (resp == NULL) {
++		return PAM_BUF_ERR;
++	}
++	memset(resp, 0, sizeof(struct pam_response) * num_msg);
++	code = PAM_SUCCESS;
++	for (i = 0; i < num_msg; i++) {
++		message = &(msg[0][i]); /* XXX */
++		message = msg[i]; /* XXX */
++		pwstring = NULL;
++		switch (message->msg_style) {
++		case PAM_TEXT_INFO:
++		case PAM_ERROR_MSG:
++			printf("[%s]\n", message->msg ? message->msg : "");
++			fflush(stdout);
++			resp[i].resp = NULL;
++			resp[i].resp_retcode = PAM_SUCCESS;
++			break;
++		case PAM_PROMPT_ECHO_ON:
++		case PAM_PROMPT_ECHO_OFF:
++			if (message->msg_style == PAM_PROMPT_ECHO_ON) {
++				if (fgets(pwbuf, sizeof(pwbuf),
++					  stdin) != NULL) {
++					pwbuf[strcspn(pwbuf, "\r\n")] = '\0';
++					pwstring = pwbuf;
++				}
++			} else {
++				pwstring = getpass(message->msg ?
++						   message->msg :
++						   "");
++			}
++			if ((pwstring != NULL) && (pwstring[0] != '\0')) {
++				pwsize = strlen(pwstring);
++				resp[i].resp = malloc(pwsize + 1);
++				if (resp[i].resp == NULL) {
++					resp[i].resp_retcode = PAM_BUF_ERR;
++				} else {
++					memcpy(resp[i].resp, pwstring, pwsize);
++					resp[i].resp[pwsize] = '\0';
++					resp[i].resp_retcode = PAM_SUCCESS;
++				}
++			} else {
++				resp[i].resp_retcode = PAM_CONV_ERR;
++				code = PAM_CONV_ERR;
++			}
++			break;
++		default:
++			break;
++		}
++	}
++	*presp = resp;
++	return code;
++}
++static int
++appl_pam_non_interactive_converse(int num_msg,
++				  const struct pam_message **msg,
++				  struct pam_response **presp,
++				  void *appdata_ptr)
++{
++	const struct pam_message *message;
++	struct pam_response *resp;
++	int i, code;
++	unsigned int pwsize;
++	struct appl_pam_non_interactive_args *args;
++	const char *pwstring;
++	resp = malloc(sizeof(struct pam_response) * num_msg);
++	if (resp == NULL) {
++		return PAM_BUF_ERR;
++	}
++	args = appdata_ptr;
++	memset(resp, 0, sizeof(struct pam_response) * num_msg);
++	code = PAM_SUCCESS;
++	for (i = 0; i < num_msg; i++) {
++		message = &((*msg)[i]);
++		message = msg[i];
++		pwstring = NULL;
++		switch (message->msg_style) {
++		case PAM_TEXT_INFO:
++		case PAM_ERROR_MSG:
++			break;
++		case PAM_PROMPT_ECHO_ON:
++		case PAM_PROMPT_ECHO_OFF:
++			if (message->msg_style == PAM_PROMPT_ECHO_ON) {
++				/* assume "user" */
++				pwstring = args->user;
++			} else {
++				/* assume "password" */
++				pwstring = args->password;
++			}
++			if ((pwstring != NULL) && (pwstring[0] != '\0')) {
++				pwsize = strlen(pwstring);
++				resp[i].resp = malloc(pwsize + 1);
++				if (resp[i].resp == NULL) {
++					resp[i].resp_retcode = PAM_BUF_ERR;
++				} else {
++					memcpy(resp[i].resp, pwstring, pwsize);
++					resp[i].resp[pwsize] = '\0';
++					resp[i].resp_retcode = PAM_SUCCESS;
++				}
++			} else {
++				resp[i].resp_retcode = PAM_CONV_ERR;
++				code = PAM_CONV_ERR;
++			}
++			break;
++		default:
++			break;
++		}
++	}
++	*presp = resp;
++	return code;
++}
++static int
++appl_pam_start(const char *service, int interactive,
++	       const char *login_username,
++	       const char *non_interactive_password,
++	       const char *hostname,
++	       const char *ruser,
++	       const char *tty)
++{
++	static int exit_handler_registered;
++	static struct appl_pam_non_interactive_args args;
++	int ret = 0;
++	if (appl_pam_started &&
++	    (strcmp(login_username, appl_pam_user) != 0)) {
++		appl_pam_cleanup();
++		appl_pam_user = NULL;
++	}
++	if (!appl_pam_started) {
++#ifdef DEBUG
++		printf("Starting PAM up (service=\"%s\",user=\"%s\").\n",
++		       service, login_username);
++#endif
++		memset(&appl_pam_conv, 0, sizeof(appl_pam_conv));
++		appl_pam_conv.conv = interactive ?
++				     &appl_pam_interactive_converse :
++				     &appl_pam_non_interactive_converse;
++		memset(&args, 0, sizeof(args));
++		args.user = strdup(login_username);
++		args.password = non_interactive_password ?
++				strdup(non_interactive_password) :
++				NULL;
++		appl_pam_conv.appdata_ptr = &args;
++		ret = pam_start(service, login_username,
++				&appl_pam_conv, &appl_pamh);
++		if (ret == 0) {
++			if (hostname != NULL) {
++#ifdef DEBUG
++				printf("Setting PAM_RHOST to \"%s\".\n", hostname);
++#endif
++				pam_set_item(appl_pamh, PAM_RHOST, hostname);
++			}
++			if (ruser != NULL) {
++#ifdef DEBUG
++				printf("Setting PAM_RUSER to \"%s\".\n", ruser);
++#endif
++				pam_set_item(appl_pamh, PAM_RUSER, ruser);
++			}
++			if (tty != NULL) {
++#ifdef DEBUG
++				printf("Setting PAM_TTY to \"%s\".\n", tty);
++#endif
++				pam_set_item(appl_pamh, PAM_TTY, tty);
++			}
++			if (!exit_handler_registered &&
++			    (atexit(appl_pam_cleanup) != 0)) {
++				pam_end(appl_pamh, 0);
++				appl_pamh = NULL;
++				ret = -1;
++			} else {
++				appl_pam_started = 1;
++				appl_pam_starter = getpid();
++				appl_pam_user = strdup(login_username);
++				exit_handler_registered = 1;
++			}
++		}
++	}
++	return ret;
++}
++int
++appl_pam_acct_mgmt(const char *service, int interactive,
++		   const char *login_username,
++		   const char *non_interactive_password,
++		   const char *hostname,
++		   const char *ruser,
++		   const char *tty)
++{
++	int ret;
++	appl_pam_pwchange_required = 0;
++	ret = appl_pam_start(service, interactive, login_username,
++			     non_interactive_password, hostname, ruser, tty);
++	if (ret == 0) {
++#ifdef DEBUG
++		printf("Calling pam_acct_mgmt().\n");
++#endif
++		ret = pam_acct_mgmt(appl_pamh, 0);
++		switch (ret) {
++		case PAM_IGNORE:
++			ret = 0;
++			break;
++		case PAM_NEW_AUTHTOK_REQD:
++			appl_pam_pwchange_required = 1;
++			ret = 0;
++			break;
++		default:
++			break;
++		}
++	}
++	return ret;
++}
++int
++appl_pam_requires_chauthtok(void)
++{
++	return appl_pam_pwchange_required;
++}
++int
++appl_pam_session_open(void)
++{
++	int ret = 0;
++	if (appl_pam_started) {
++#ifdef DEBUG
++		printf("Opening PAM session.\n");
++#endif
++		ret = pam_open_session(appl_pamh, 0);
++		if (ret == 0) {
++			appl_pam_session_opened = 1;
++		}
++	}
++	return ret;
++}
++int
++appl_pam_setenv(void)
++{
++	int ret = 0;
++#ifdef HAVE_PAM_GETENVLIST
++#ifdef HAVE_PUTENV
++	int i;
++	char **list;
++	if (appl_pam_started) {
++		list = pam_getenvlist(appl_pamh);
++		for (i = 0; ((list != NULL) && (list[i] != NULL)); i++) {
++#ifdef DEBUG
++			printf("Setting \"%s\" in environment.\n", list[i]);
++#endif
++			putenv(list[i]);
++		}
++	}
++#endif
++#endif
++	return ret;
++}
++int
++appl_pam_cred_init(void)
++{
++	int ret = 0;
++	if (appl_pam_started) {
++#ifdef DEBUG
++		printf("Initializing PAM credentials.\n");
++#endif
++		ret = pam_setcred(appl_pamh, PAM_ESTABLISH_CRED);
++		if (ret == 0) {
++			appl_pam_creds_initialized = 1;
++		}
++	}
++	return ret;
++}
++#endif
+diff -up krb5-1.8/src/clients/ksu/pam.h.pam krb5-1.8/src/clients/ksu/pam.h
+--- krb5-1.8/src/clients/ksu/pam.h.pam	2010-03-05 10:48:08.000000000 -0500
++++ krb5-1.8/src/clients/ksu/pam.h	2010-03-05 10:48:08.000000000 -0500
+@@ -0,0 +1,57 @@
++/*
++ * src/clients/ksu/pam.h
++ *
++ * Copyright 2007,2009,2010 Red Hat, Inc.
++ *
++ * All Rights Reserved.
++ *
++ * 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.
++ *
++ *  Neither the name of Red Hat, Inc. nor the names of its contributors may be
++ *  used to endorse or promote products derived from this software without
++ *  specific prior written permission.
++ *
++ * 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 OWNER 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.
++ * 
++ * Convenience wrappers for using PAM.
++ */
++
++#include <krb5.h>
++#ifdef HAVE_SECURITY_PAM_APPL_H
++#include <security/pam_appl.h>
++#endif
++
++#define USE_PAM_CONFIGURATION_KEYWORD "use_pam"
++
++#ifdef USE_PAM
++int appl_pam_enabled(krb5_context context, const char *section);
++int appl_pam_acct_mgmt(const char *service, int interactive,
++		       const char *local_username,
++		       const char *non_interactive_password,
++		       const char *hostname,
++		       const char *ruser,
++		       const char *tty);
++int appl_pam_requires_chauthtok(void);
++int appl_pam_session_open(void);
++int appl_pam_setenv(void);
++int appl_pam_cred_init(void);
++void appl_pam_cleanup(void);
++#endif
+diff -up krb5-1.8/src/configure.in.pam krb5-1.8/src/configure.in
+--- krb5-1.8/src/configure.in.pam	2009-12-31 18:13:56.000000000 -0500
++++ krb5-1.8/src/configure.in	2010-03-05 10:48:08.000000000 -0500
+@@ -1051,6 +1051,8 @@ if test "$ac_cv_lib_socket" = "yes" -a "
+ 
+ AC_PATH_PROG(GROFF, groff)
+ 
++KRB5_WITH_PAM
++
+ # Make localedir work in autoconf 2.5x.
+ if test "${localedir+set}" != set; then
+     localedir='$(datadir)/locale'
diff --git a/SOURCES/krb5-1.11-run_user_0.patch b/SOURCES/krb5-1.11-run_user_0.patch
new file mode 100644
index 0000000..6be760a
--- /dev/null
+++ b/SOURCES/krb5-1.11-run_user_0.patch
@@ -0,0 +1,34 @@
+A hack: if we're looking at creating a ccache directory directly below
+the /run/user/0 directory, and /run/user/0 doesn't exist, try to create
+it, too.
+
+--- krb5/src/lib/krb5/ccache/cc_dir.c
++++ krb5/src/lib/krb5/ccache/cc_dir.c
+@@ -61,6 +61,8 @@
+ 
+ #include <dirent.h>
+ 
++#define ROOT_SPECIAL_DCC_PARENT "/run/user/0"
++
+ extern const krb5_cc_ops krb5_dcc_ops;
+ extern const krb5_cc_ops krb5_fcc_ops;
+ 
+@@ -239,6 +241,18 @@
+ 
+     if (stat(dirname, &st) < 0) {
+         if (errno == ENOENT) {
++            if (strncmp(dirname, ROOT_SPECIAL_DCC_PARENT "/",
++                        sizeof(ROOT_SPECIAL_DCC_PARENT)) == 0 &&
++                stat(ROOT_SPECIAL_DCC_PARENT, &st) < 0 &&
++                errno == ENOENT) {
++#ifdef USE_SELINUX
++                selabel = krb5int_push_fscreatecon_for(ROOT_SPECIAL_DCC_PARENT);
++#endif
++                status = mkdir(ROOT_SPECIAL_DCC_PARENT, S_IRWXU);
++#ifdef USE_SELINUX
++                krb5int_pop_fscreatecon(selabel);
++#endif
++            }
+ #ifdef USE_SELINUX
+             selabel = krb5int_push_fscreatecon_for(dirname);
+ #endif
diff --git a/SOURCES/krb5-1.11-selinux-label.patch b/SOURCES/krb5-1.11-selinux-label.patch
new file mode 100644
index 0000000..f832728
--- /dev/null
+++ b/SOURCES/krb5-1.11-selinux-label.patch
@@ -0,0 +1,1015 @@
+SELinux bases access to files on the domain of the requesting process,
+the operation being performed, and the context applied to the file.
+
+In many cases, applications needn't be SELinux aware to work properly,
+because SELinux can apply a default label to a file based on the label
+of the directory in which it's created.
+
+In the case of files such as /etc/krb5.keytab, however, this isn't
+sufficient, as /etc/krb5.keytab will almost always need to be given a
+label which differs from that of /etc/issue or /etc/resolv.conf.  The
+the kdb stash file needs a different label than the database for which
+it's holding a master key, even though both typically live in the same
+directory.
+
+To give the file the correct label, we can either force a "restorecon"
+call to fix a file's label after it's created, or create the file with
+the right label, as we attempt to do here.  We lean on THREEPARAMOPEN
+and define a similar macro named WRITABLEFOPEN with which we replace
+several uses of fopen().
+
+The file creation context that we're manipulating here is a process-wide
+attribute.  While for the most part, applications which need to label
+files when they're created have tended to be single-threaded, there's
+not much we can do to avoid interfering with an application that
+manipulates the creation context directly.  Right now we're mediating
+access using a library-local mutex, but that can only work for consumers
+that are part of this package -- an unsuspecting application will still
+stomp all over us.
+
+The selabel APIs for looking up the context should be thread-safe (per
+Red Hat #273081), so switching to using them instead of matchpathcon(),
+which we used earlier, is some improvement.
+
+--- krb5/src/aclocal.m4
++++ krb5/src/aclocal.m4
+@@ -103,6 +103,7 @@ AC_SUBST_FILE(libnodeps_frag)
+ dnl
+ KRB5_AC_PRAGMA_WEAK_REF
+ WITH_LDAP
++KRB5_WITH_SELINUX
+ KRB5_LIB_PARAMS
+ KRB5_AC_INITFINI
+ KRB5_AC_ENABLE_THREADS
+@@ -1791,3 +1792,51 @@ AC_SUBST(manlocalstatedir)
+ AC_SUBST(PAM_MAN)
+ AC_SUBST(NON_PAM_MAN)
+ ])dnl
++dnl
++dnl Use libselinux to set file contexts on newly-created files.
++dnl 
++AC_DEFUN(KRB5_WITH_SELINUX,[
++AC_ARG_WITH(selinux,[AC_HELP_STRING(--with-selinux,[compile with SELinux labeling support])],
++           withselinux="$withval",withselinux=auto)
++old_LIBS="$LIBS"
++if test "$withselinux" != no ; then
++       AC_MSG_RESULT([checking for libselinux...])
++       SELINUX_LIBS=
++       AC_CHECK_HEADERS(selinux/selinux.h selinux/label.h)
++       if test "x$ac_cv_header_selinux_selinux_h" != xyes ; then
++               if test "$withselinux" = auto ; then
++                       AC_MSG_RESULT([Unable to locate selinux/selinux.h.])
++                       withselinux=no
++               else
++                       AC_MSG_ERROR([Unable to locate selinux/selinux.h.])
++               fi
++       fi
++
++       LIBS=
++       unset ac_cv_func_setfscreatecon
++       AC_CHECK_FUNCS(setfscreatecon selabel_open)
++       if test "x$ac_cv_func_setfscreatecon" = xno ; then
++               AC_CHECK_LIB(selinux,setfscreatecon)
++               unset ac_cv_func_setfscreatecon
++               AC_CHECK_FUNCS(setfscreatecon selabel_open)
++               if test "x$ac_cv_func_setfscreatecon" = xyes ; then
++                       SELINUX_LIBS="$LIBS"
++               else
++                       if test "$withselinux" = auto ; then
++                               AC_MSG_RESULT([Unable to locate libselinux.])
++                               withselinux=no
++                       else
++                               AC_MSG_ERROR([Unable to locate libselinux.])
++                       fi
++               fi
++       fi
++       if test "$withselinux" != no ; then
++               AC_MSG_NOTICE([building with SELinux labeling support])
++               AC_DEFINE(USE_SELINUX,1,[Define if Kerberos-aware tools should set SELinux file contexts when creating files.])
++               SELINUX_LIBS="$LIBS"
++		EXTRA_SUPPORT_SYMS="$EXTRA_SUPPORT_SYMS krb5int_labeled_open krb5int_labeled_fopen krb5int_push_fscreatecon_for krb5int_pop_fscreatecon"
++       fi
++fi
++LIBS="$old_LIBS"
++AC_SUBST(SELINUX_LIBS)
++])dnl
+--- krb5/src/config/pre.in
++++ krb5/src/config/pre.in
+@@ -180,6 +180,7 @@ LD_UNRESOLVED_PREFIX = @LD_UNRESOLVED_PREFIX@
+ LD_SHLIBDIR_PREFIX = @LD_SHLIBDIR_PREFIX@
+ LDARGS = @LDARGS@
+ LIBS = @LIBS@
++SELINUX_LIBS=@SELINUX_LIBS@
+ 
+ INSTALL=@INSTALL@
+ INSTALL_STRIP=
+@@ -379,7 +380,7 @@ SUPPORT_LIB			= -l$(SUPPORT_LIBNAME)
+ # HESIOD_LIBS is -lhesiod...
+ HESIOD_LIBS	= @HESIOD_LIBS@
+ 
+-KRB5_BASE_LIBS	= $(KRB5_LIB) $(K5CRYPTO_LIB) $(COM_ERR_LIB) $(SUPPORT_LIB) $(GEN_LIB) $(LIBS) $(DL_LIB)
++KRB5_BASE_LIBS	= $(KRB5_LIB) $(K5CRYPTO_LIB) $(COM_ERR_LIB) $(SUPPORT_LIB) $(GEN_LIB) $(LIBS) $(SELINUX_LIBS) $(DL_LIB)
+ KDB5_LIBS	= $(KDB5_LIB) $(GSSRPC_LIBS)
+ GSS_LIBS	= $(GSS_KRB5_LIB)
+ # needs fixing if ever used on Mac OS X!
+--- krb5/src/configure.in
++++ krb5/src/configure.in
+@@ -1053,6 +1053,8 @@ fi
+ 
+ KRB5_WITH_PAM
+ 
++KRB5_WITH_SELINUX
++
+ # Make localedir work in autoconf 2.5x.
+ if test "${localedir+set}" != set; then
+     localedir='$(datadir)/locale'
+--- krb5/src/include/k5-int.h
++++ krb5/src/include/k5-int.h
+@@ -133,6 +133,7 @@ typedef unsigned char   u_char;
+ typedef UINT64_TYPE krb5_ui_8;
+ typedef INT64_TYPE krb5_int64;
+ 
++#include "k5-label.h"
+ 
+ #define DEFAULT_PWD_STRING1 "Enter password"
+ #define DEFAULT_PWD_STRING2 "Re-enter password for verification"
+--- krb5/src/include/k5-label.h
++++ krb5/src/include/k5-label.h
+@@ -0,0 +1,32 @@
++#ifndef _KRB5_LABEL_H
++#define _KRB5_LABEL_H
++
++#ifdef THREEPARAMOPEN
++#undef THREEPARAMOPEN
++#endif
++#ifdef WRITABLEFOPEN
++#undef WRITABLEFOPEN
++#endif
++
++/* Wrapper functions which help us create files and directories with the right
++ * context labels. */
++#ifdef USE_SELINUX
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <stdio.h>
++#include <unistd.h>
++FILE *krb5int_labeled_fopen(const char *path, const char *mode);
++int krb5int_labeled_creat(const char *path, mode_t mode);
++int krb5int_labeled_open(const char *path, int flags, ...);
++int krb5int_labeled_mkdir(const char *path, mode_t mode);
++int krb5int_labeled_mknod(const char *path, mode_t mode, dev_t device);
++#define THREEPARAMOPEN(x,y,z) krb5int_labeled_open(x,y,z)
++#define WRITABLEFOPEN(x,y) krb5int_labeled_fopen(x,y)
++void *krb5int_push_fscreatecon_for(const char *pathname);
++void krb5int_pop_fscreatecon(void *previous);
++#else
++#define WRITABLEFOPEN(x,y) fopen(x,y)
++#define THREEPARAMOPEN(x,y,z) open(x,y,z)
++#endif
++#endif
+--- krb5/src/include/krb5/krb5.hin
++++ krb5/src/include/krb5/krb5.hin
+@@ -87,6 +87,12 @@
+ #define THREEPARAMOPEN(x,y,z) open(x,y,z)
+ #endif
+ 
++#if KRB5_PRIVATE
++#ifndef WRITABLEFOPEN
++#define WRITABLEFOPEN(x,y) fopen(x,y)
++#endif
++#endif
++
+ #define KRB5_OLD_CRYPTO
+ 
+ #include <stdlib.h>
+--- krb5/src/kadmin/dbutil/dump.c
++++ krb5/src/kadmin/dbutil/dump.c
+@@ -376,12 +376,21 @@ create_ofile(char *ofile, char **tmpname
+ {
+     int fd = -1;
+     FILE *f;
++#ifdef USE_SELINUX
++    void *selabel;
++#endif
+ 
+     *tmpname = NULL;
+     if (asprintf(tmpname, "%s-XXXXXX", ofile) < 0)
+         goto error;
+ 
++#ifdef USE_SELINUX
++    selabel = krb5int_push_fscreatecon_for(ofile);
++#endif
+     fd = mkstemp(*tmpname);
++#ifdef USE_SELINUX
++    krb5int_pop_fscreatecon(selabel);
++#endif
+     if (fd == -1)
+         goto error;
+ 
+@@ -514,7 +514,7 @@ prep_ok_file(krb5_context context, char
+         return 0;
+     }
+ 
+-    *fd = open(file_ok, O_WRONLY | O_CREAT | O_TRUNC, 0600);
++    *fd = THREEPARAMOPEN(file_ok, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+     if (*fd == -1) {
+         com_err(progname, errno, _("while creating 'ok' file, '%s'"), file_ok);
+         exit_status++;
+--- krb5/src/krb5-config.in
++++ krb5/src/krb5-config.in
+@@ -38,6 +38,7 @@ RPATH_FLAG='@RPATH_FLAG@'
+ DEFCCNAME='@DEFCCNAME@'
+ DEFKTNAME='@DEFKTNAME@'
+ DEFCKTNAME='@DEFCKTNAME@'
++SELINUX_LIBS='@SELINUX_LIBS@'
+ 
+ LIBS='@LIBS@'
+ GEN_LIB=@GEN_LIB@
+@@ -218,7 +219,7 @@
+     fi
+ 
+     # If we ever support a flag to generate output suitable for static
+-    # linking, we would output "-lkrb5support $GEN_LIB $LIBS $DL_LIB"
++    # linking, we would output "-lkrb5support $GEN_LIB $LIBS $SELINUX_LIBS $DL_LIB"
+     # here.
+ 
+     echo $lib_flags
+--- krb5/src/lib/kadm5/logger.c
++++ krb5/src/lib/kadm5/logger.c
+@@ -425,7 +425,7 @@ krb5_klog_init(krb5_context kcontext, ch
+                      * Check for append/overwrite, then open the file.
+                      */
+                     if (cp[4] == ':' || cp[4] == '=') {
+-                        f = fopen(&cp[5], (cp[4] == ':') ? "a" : "w");
++                        f = WRITABLEFOPEN(&cp[5], (cp[4] == ':') ? "a" : "w");
+                         if (f) {
+                             set_cloexec_file(f);
+                             log_control.log_entries[i].lfu_filep = f;
+@@ -961,7 +961,7 @@ krb5_klog_reopen(krb5_context kcontext)
+              * In case the old logfile did not get moved out of the
+              * way, open for append to prevent squashing the old logs.
+              */
+-            f = fopen(log_control.log_entries[lindex].lfu_fname, "a+");
++            f = WRITABLEFOPEN(log_control.log_entries[lindex].lfu_fname, "a+");
+             if (f) {
+                 set_cloexec_file(f);
+                 log_control.log_entries[lindex].lfu_filep = f;
+--- krb5/src/lib/krb5/keytab/kt_file.c
++++ krb5/src/lib/krb5/keytab/kt_file.c
+@@ -1050,7 +1050,7 @@ krb5_ktfileint_open(krb5_context context
+ 
+     KTCHECKLOCK(id);
+     errno = 0;
+-    KTFILEP(id) = fopen(KTFILENAME(id),
++    KTFILEP(id) = WRITABLEFOPEN(KTFILENAME(id),
+                         (mode == KRB5_LOCKMODE_EXCLUSIVE) ?
+                         fopen_mode_rbplus : fopen_mode_rb);
+     if (!KTFILEP(id)) {
+@@ -1058,7 +1058,7 @@ krb5_ktfileint_open(krb5_context context
+             /* try making it first time around */
+             krb5_create_secure_file(context, KTFILENAME(id));
+             errno = 0;
+-            KTFILEP(id) = fopen(KTFILENAME(id), fopen_mode_rbplus);
++            KTFILEP(id) = WRITABLEFOPEN(KTFILENAME(id), fopen_mode_rbplus);
+             if (!KTFILEP(id))
+                 goto report_errno;
+             writevno = 1;
+--- krb5/src/plugins/kdb/db2/adb_openclose.c
++++ krb5/src/plugins/kdb/db2/adb_openclose.c
+@@ -201,7 +201,7 @@ osa_adb_init_db(osa_adb_db_t *dbp, char 
+          * POSIX systems
+          */
+         lockp->lockinfo.filename = strdup(lockfilename);
+-        if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r+")) == NULL) {
++        if ((lockp->lockinfo.lockfile = WRITABLEFOPEN(lockfilename, "r+")) == NULL) {
+             /*
+              * maybe someone took away write permission so we could only
+              * get shared locks?
+--- krb5/src/plugins/kdb/db2/libdb2/btree/bt_open.c
++++ krb5/src/plugins/kdb/db2/libdb2/btree/bt_open.c
+@@ -60,6 +60,7 @@ static char sccsid[] = "@(#)bt_open.c	8.
+ 
+ #include "k5-platform.h"	/* mkstemp? */
+ 
++#include "k5-int.h"
+ #include "db-int.h"
+ #include "btree.h"
+ 
+@@ -203,7 +204,7 @@ __bt_open(fname, flags, mode, openinfo, 
+ 			goto einval;
+ 		}
+ 
+-		if ((t->bt_fd = open(fname, flags | O_BINARY, mode)) < 0)
++		if ((t->bt_fd = THREEPARAMOPEN(fname, flags | O_BINARY, mode)) < 0)
+ 			goto err;
+ 
+ 	} else {
+--- krb5/src/plugins/kdb/db2/libdb2/hash/hash.c
++++ krb5/src/plugins/kdb/db2/libdb2/hash/hash.c
+@@ -51,6 +51,7 @@ static char sccsid[] = "@(#)hash.c	8.12 
+ #include <assert.h>
+ #endif
+ 
++#include "k5-int.h"
+ #include "db-int.h"
+ #include "hash.h"
+ #include "page.h"
+@@ -140,7 +141,7 @@ __kdb2_hash_open(file, flags, mode, info
+ 		new_table = 1;
+ 	}
+ 	if (file) {
+-		if ((hashp->fp = open(file, flags|O_BINARY, mode)) == -1)
++		if ((hashp->fp = THREEPARAMOPEN(file, flags|O_BINARY, mode)) == -1)
+ 			RETURN_ERROR(errno, error0);
+ 		(void)fcntl(hashp->fp, F_SETFD, 1);
+ 	}
+--- krb5/src/plugins/kdb/db2/libdb2/test/Makefile.in
++++ krb5/src/plugins/kdb/db2/libdb2/test/Makefile.in
+@@ -12,7 +12,8 @@ PROG_RPATH=$(KRB5_LIBDIR)
+ 
+ KRB5_RUN_ENV= @KRB5_RUN_ENV@
+ 
+-DB_LIB		= -ldb
++DB_LIB		= -ldb $(SUPPORT_DEPLIB)
++
+ DB_DEPLIB	= ../libdb$(DEPLIBEXT)
+ 
+ all::
+--- krb5/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_services.c
++++ krb5/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_services.c
+@@ -179,7 +179,7 @@ done:
+ 
+     /* set password in the file */
+     old_mode = umask(0177);
+-    pfile = fopen(file_name, "a+");
++    pfile = WRITABLEFOPEN(file_name, "a+");
+     if (pfile == NULL) {
+         com_err(me, errno, _("Failed to open file %s: %s"), file_name,
+                 strerror (errno));
+@@ -220,6 +220,9 @@ done:
+          * Delete the existing entry and add the new entry
+          */
+         FILE *newfile;
++#ifdef USE_SELINUX
++        void *selabel;
++#endif
+ 
+         mode_t omask;
+ 
+@@ -231,7 +234,13 @@ done:
+         }
+ 
+         omask = umask(077);
++#ifdef USE_SELINUX
++        selabel = krb5int_push_fscreatecon_for(file_name);
++#endif
+         newfile = fopen(tmp_file, "w");
++#ifdef USE_SELINUX
++        krb5int_pop_fscreatecon(selabel);
++#endif
+         umask (omask);
+         if (newfile == NULL) {
+             com_err(me, errno, _("Error creating file %s"), tmp_file);
+--- krb5/src/slave/kpropd.c
++++ krb5/src/slave/kpropd.c
+@@ -437,6 +437,9 @@ void doit(fd)
+     krb5_enctype etype;
+     int database_fd;
+     char host[INET6_ADDRSTRLEN+1];
++#ifdef USE_SELINUX
++    void *selabel;
++#endif
+ 
+     signal_wrapper(SIGALRM, alarm_handler);
+     alarm(params.iprop_resync_timeout);
+@@ -515,9 +518,15 @@ void doit(fd)
+         free(name);
+         exit(1);
+     }
++#ifdef USE_SELINUX
++    selabel = krb5int_push_fscreatecon_for(file);
++#endif
+     omask = umask(077);
+     lock_fd = open(temp_file_name, O_RDWR|O_CREAT, 0600);
+     (void) umask(omask);
++#ifdef USE_SELINUX
++    krb5int_pop_fscreatecon(selabel);
++#endif
+     retval = krb5_lock_file(kpropd_context, lock_fd,
+                             KRB5_LOCKMODE_EXCLUSIVE|KRB5_LOCKMODE_DONTBLOCK);
+     if (retval) {
+--- krb5/src/util/profile/prof_file.c
++++ krb5/src/util/profile/prof_file.c
+@@ -30,6 +30,7 @@
+ #endif
+ 
+ #include "k5-platform.h"
++#include "k5-label.h"
+ 
+ struct global_shared_profile_data {
+     /* This is the head of the global list of shared trees */
+@@ -418,7 +419,7 @@ static errcode_t write_data_to_file(prf_
+ 
+     errno = 0;
+ 
+-    f = fopen(new_file, "w");
++    f = WRITABLEFOPEN(new_file, "w");
+     if (!f) {
+         retval = errno;
+         if (retval == 0)
+--- krb5/src/util/support/Makefile.in
++++ krb5/src/util/support/Makefile.in
+@@ -54,6 +54,7 @@ IPC_SYMS= \
+ 
+ STLIBOBJS= \
+ 	threads.o \
++	selinux.o \
+ 	init-addrinfo.o \
+ 	plugins.o \
+ 	errors.o \
+@@ -108,7 +109,7 @@ SRCS=\
+ 
+ SHLIB_EXPDEPS =
+ # Add -lm if dumping thread stats, for sqrt.
+-SHLIB_EXPLIBS= $(LIBS) $(DL_LIB)
++SHLIB_EXPLIBS= $(LIBS) $(SELINUX_LIBS) $(DL_LIB)
+ SHLIB_DIRS=
+ SHLIB_RDIRS=$(KRB5_LIBDIR)
+ 
+--- krb5/src/util/support/selinux.c
++++ krb5/src/util/support/selinux.c
+@@ -0,0 +1,405 @@
++/*
++ * Copyright 2007,2008,2009,2011,2012 Red Hat, Inc.  All Rights Reserved.
++ *
++ * 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.
++ *
++ *  Neither the name of Red Hat, Inc. nor the names of its contributors may be
++ *  used to endorse or promote products derived from this software without
++ *  specific prior written permission.
++ *
++ * 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 OWNER 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.
++ * 
++ * File-opening wrappers for creating correctly-labeled files.  So far, we can
++ * assume that this is Linux-specific, so we make many simplifying assumptions.
++ */
++
++#include "../../include/autoconf.h"
++
++#ifdef USE_SELINUX
++
++#include <k5-label.h>
++#include <k5-platform.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <limits.h>
++#include <pthread.h>
++#include <stdarg.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include <selinux/selinux.h>
++#include <selinux/context.h>
++#ifdef HAVE_SELINUX_LABEL_H
++#include <selinux/label.h>
++#endif
++
++/* #define DEBUG 1 */
++
++/* Mutex used to serialize use of the process-global file creation context. */
++k5_mutex_t labeled_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
++
++/* Make sure we finish initializing that mutex before attempting to use it. */
++k5_once_t labeled_once = K5_ONCE_INIT;
++static void
++label_mutex_init(void)
++{
++	k5_mutex_finish_init(&labeled_mutex);
++}
++
++#ifdef HAVE_SELINUX_LABEL_H
++static struct selabel_handle *selabel_ctx;
++static time_t selabel_last_changed;
++
++MAKE_FINI_FUNCTION(cleanup_fscreatecon);
++
++static void
++cleanup_fscreatecon(void)
++{
++	if (selabel_ctx != NULL) {
++		selabel_close(selabel_ctx);
++		selabel_ctx = NULL;
++	}
++}
++#endif
++
++static security_context_t
++push_fscreatecon(const char *pathname, mode_t mode)
++{
++	security_context_t previous, configuredsc, currentsc, derivedsc;
++	context_t current, derived;
++	const char *fullpath, *currentuser;
++
++	previous = NULL;
++	if (is_selinux_enabled()) {
++		if (getfscreatecon(&previous) == 0) {
++			char *genpath;
++			genpath = NULL;
++			if (pathname[0] != '/') {
++				char *wd;
++				size_t len;
++				len = 0;
++				wd = getcwd(NULL, len);
++				if (wd == NULL) {
++					if (previous != NULL) {
++						freecon(previous);
++					}
++					return NULL;
++				}
++				len = strlen(wd) + 1 + strlen(pathname) + 1;
++				genpath = malloc(len);
++				if (genpath == NULL) {
++					free(wd);
++					if (previous != NULL) {
++						freecon(previous);
++					}
++					return NULL;
++				}
++				sprintf(genpath, "%s/%s", wd, pathname);
++				free(wd);
++				fullpath = genpath;
++			} else {
++				fullpath = pathname;
++			}
++#ifdef DEBUG
++			if (isatty(fileno(stderr))) {
++				fprintf(stderr, "Looking up context for "
++					"\"%s\"(%05o).\n", fullpath, mode);
++			}
++#endif
++			configuredsc = NULL;
++#ifdef HAVE_SELINUX_LABEL_H
++			if ((selabel_ctx != NULL) ||
++			    (selabel_last_changed == 0)) {
++				const char *cpath;
++				struct stat st;
++				int i = -1;
++				cpath = selinux_file_context_path();
++				if ((cpath == NULL) ||
++				    ((i = stat(cpath, &st)) != 0) ||
++				    (st.st_mtime != selabel_last_changed)) {
++					if (selabel_ctx != NULL) {
++						selabel_close(selabel_ctx);
++						selabel_ctx = NULL;
++					}
++					selabel_last_changed = i ?
++							       time(NULL) :
++							       st.st_mtime;
++				}
++			}
++			if (selabel_ctx == NULL) {
++				selabel_ctx = selabel_open(SELABEL_CTX_FILE,
++							   NULL, 0);
++			}
++			if (selabel_ctx != NULL) {
++				if (selabel_lookup(selabel_ctx, &configuredsc,
++						   fullpath, mode) != 0) {
++					free(genpath);
++					if (previous != NULL) {
++						freecon(previous);
++					}
++					return NULL;
++				}
++			}
++#else
++			if (matchpathcon(fullpath, mode, &configuredsc) != 0) {
++				free(genpath);
++				if (previous != NULL) {
++					freecon(previous);
++				}
++				return NULL;
++			}
++#endif
++			free(genpath);
++			if (configuredsc == NULL) {
++				if (previous != NULL) {
++					freecon(previous);
++				}
++				return NULL;
++			}
++			currentsc = NULL;
++			getcon(&currentsc);
++			if (currentsc != NULL) {
++				derived = context_new(configuredsc);
++				if (derived != NULL) {
++					current = context_new(currentsc);
++					if (current != NULL) {
++						currentuser = context_user_get(current);
++						if (currentuser != NULL) {
++							if (context_user_set(derived,
++									     currentuser) == 0) {
++								derivedsc = context_str(derived);
++								if (derivedsc != NULL) {
++									freecon(configuredsc);
++									configuredsc = strdup(derivedsc);
++								}
++							}
++						}
++						context_free(current);
++					}
++					context_free(derived);
++				}
++				freecon(currentsc);
++			}
++#ifdef DEBUG
++			if (isatty(fileno(stderr))) {
++				fprintf(stderr, "Setting file creation context "
++					"to \"%s\".\n", configuredsc);
++			}
++#endif
++			if (setfscreatecon(configuredsc) != 0) {
++				freecon(configuredsc);
++				if (previous != NULL) {
++					freecon(previous);
++				}
++				return NULL;
++			}
++			freecon(configuredsc);
++#ifdef DEBUG
++		} else {
++			if (isatty(fileno(stderr))) {
++				fprintf(stderr, "Unable to determine "
++					"current context.\n");
++			}
++#endif
++		}
++	}
++	return previous;
++}
++
++static void
++pop_fscreatecon(security_context_t previous)
++{
++	if (is_selinux_enabled()) {
++#ifdef DEBUG
++		if (isatty(fileno(stderr))) {
++			if (previous != NULL) {
++				fprintf(stderr, "Resetting file creation "
++					"context to \"%s\".\n", previous);
++			} else {
++				fprintf(stderr, "Resetting file creation "
++					"context to default.\n");
++			}
++		}
++#endif
++		setfscreatecon(previous);
++		if (previous != NULL) {
++			freecon(previous);
++		}
++	}
++}
++
++void *
++krb5int_push_fscreatecon_for(const char *pathname)
++{
++	struct stat st;
++	void *retval;
++	k5_once(&labeled_once, label_mutex_init);
++	if (k5_mutex_lock(&labeled_mutex) == 0) {
++		if (stat(pathname, &st) != 0) {
++			st.st_mode = S_IRUSR | S_IWUSR;
++		}
++		retval = push_fscreatecon(pathname, st.st_mode);
++		return retval ? retval : (void *) -1;
++	} else {
++		return NULL;
++	}
++}
++
++void
++krb5int_pop_fscreatecon(void *con)
++{
++	if (con != NULL) {
++		pop_fscreatecon((con == (void *) -1) ? NULL : con);
++		k5_mutex_unlock(&labeled_mutex);
++	}
++}
++
++FILE *
++krb5int_labeled_fopen(const char *path, const char *mode)
++{
++	FILE *fp;
++	int errno_save;
++	security_context_t ctx;
++
++	if ((strcmp(mode, "r") == 0) ||
++	    (strcmp(mode, "rb") == 0)) {
++		return fopen(path, mode);
++	}
++
++	k5_once(&labeled_once, label_mutex_init);
++	if (k5_mutex_lock(&labeled_mutex) == 0) {
++		ctx = push_fscreatecon(path, 0);
++		fp = fopen(path, mode);
++		errno_save = errno;
++		pop_fscreatecon(ctx);
++		k5_mutex_unlock(&labeled_mutex);
++		errno = errno_save;
++	} else {
++		fp = fopen(path, mode);
++	}
++
++	return fp;
++}
++
++int
++krb5int_labeled_creat(const char *path, mode_t mode)
++{
++	int fd;
++	int errno_save;
++	security_context_t ctx;
++
++	k5_once(&labeled_once, label_mutex_init);
++	if (k5_mutex_lock(&labeled_mutex) == 0) {
++		ctx = push_fscreatecon(path, 0);
++		fd = creat(path, mode);
++		errno_save = errno;
++		pop_fscreatecon(ctx);
++		k5_mutex_unlock(&labeled_mutex);
++		errno = errno_save;
++	} else {
++		fd = creat(path, mode);
++	}
++	return fd;
++}
++
++int
++krb5int_labeled_mknod(const char *path, mode_t mode, dev_t dev)
++{
++	int ret;
++	int errno_save;
++	security_context_t ctx;
++
++	k5_once(&labeled_once, label_mutex_init);
++	if (k5_mutex_lock(&labeled_mutex) == 0) {
++		ctx = push_fscreatecon(path, mode);
++		ret = mknod(path, mode, dev);
++		errno_save = errno;
++		pop_fscreatecon(ctx);
++		k5_mutex_unlock(&labeled_mutex);
++		errno = errno_save;
++	} else {
++		ret = mknod(path, mode, dev);
++	}
++	return ret;
++}
++
++int
++krb5int_labeled_mkdir(const char *path, mode_t mode)
++{
++	int ret;
++	int errno_save;
++	security_context_t ctx;
++
++	k5_once(&labeled_once, label_mutex_init);
++	if (k5_mutex_lock(&labeled_mutex) == 0) {
++		ctx = push_fscreatecon(path, S_IFDIR);
++		ret = mkdir(path, mode);
++		errno_save = errno;
++		pop_fscreatecon(ctx);
++		k5_mutex_unlock(&labeled_mutex);
++		errno = errno_save;
++	} else {
++		ret = mkdir(path, mode);
++	}
++	return ret;
++}
++
++int
++krb5int_labeled_open(const char *path, int flags, ...)
++{
++	int fd;
++	int errno_save;
++	security_context_t ctx;
++	mode_t mode;
++	va_list ap;
++
++	if ((flags & O_CREAT) == 0) {
++		return open(path, flags);
++	}
++
++	k5_once(&labeled_once, label_mutex_init);
++	if (k5_mutex_lock(&labeled_mutex) == 0) {
++		ctx = push_fscreatecon(path, 0);
++
++		va_start(ap, flags);
++		mode = va_arg(ap, mode_t);
++		fd = open(path, flags, mode);
++		va_end(ap);
++
++		errno_save = errno;
++		pop_fscreatecon(ctx);
++		k5_mutex_unlock(&labeled_mutex);
++		errno = errno_save;
++	} else {
++		va_start(ap, flags);
++		mode = va_arg(ap, mode_t);
++		fd = open(path, flags, mode);
++		errno_save = errno;
++		va_end(ap);
++		errno = errno_save;
++	}
++	return fd;
++}
++
++#endif
+--- krb5/src/lib/krb5/rcache/rc_dfl.c
++++ krb5/src/lib/krb5/rcache/rc_dfl.c
+@@ -813,6 +813,9 @@ krb5_rc_dfl_expunge_locked(krb5_context 
+     krb5_error_code retval = 0;
+     krb5_rcache tmp;
+     krb5_deltat lifespan = t->lifespan;  /* save original lifespan */
++#ifdef USE_SELINUX
++    void *selabel;
++#endif
+ 
+     if (! t->recovering) {
+         name = t->name;
+@@ -834,7 +837,17 @@ krb5_rc_dfl_expunge_locked(krb5_context 
+     retval = krb5_rc_resolve(context, tmp, 0);
+     if (retval)
+         goto cleanup;
++#ifdef USE_SELINUX
++    if (t->d.fn != NULL)
++        selabel = krb5int_push_fscreatecon_for(t->d.fn);
++    else
++        selabel = NULL;
++#endif
+     retval = krb5_rc_initialize(context, tmp, lifespan);
++#ifdef USE_SELINUX
++    if (selabel != NULL)
++        krb5int_pop_fscreatecon(selabel);
++#endif
+     if (retval)
+         goto cleanup;
+     for (q = t->a; q; q = q->na) {
+--- krb5/src/lib/krb5/ccache/cc_dir.c
++++ krb5/src/lib/krb5/ccache/cc_dir.c
+@@ -185,10 +185,19 @@ write_primary_file(const char *primary_p
+     char *newpath = NULL;
+     FILE *fp = NULL;
+     int fd = -1, status;
++#ifdef USE_SELINUX
++    void *selabel;
++#endif
+ 
+     if (asprintf(&newpath, "%s.XXXXXX", primary_path) < 0)
+         return ENOMEM;
++#ifdef USE_SELINUX
++    selabel = krb5int_push_fscreatecon_for(primary_path);
++#endif
+     fd = mkstemp(newpath);
++#ifdef USE_SELINUX
++    krb5int_pop_fscreatecon(selabel);
++#endif
+     if (fd < 0)
+         goto cleanup;
+ #ifdef HAVE_CHMOD
+@@ -223,10 +232,23 @@
+ verify_dir(krb5_context context, const char *dirname)
+ {
+     struct stat st;
++    int status;
++#ifdef USE_SELINUX
++    void *selabel;
++#endif
+ 
+     if (stat(dirname, &st) < 0) {
+-        if (errno == ENOENT && mkdir(dirname, S_IRWXU) == 0)
+-            return 0;
++        if (errno == ENOENT) {
++#ifdef USE_SELINUX
++            selabel = krb5int_push_fscreatecon_for(dirname);
++#endif
++            status = mkdir(dirname, S_IRWXU);
++#ifdef USE_SELINUX
++            krb5int_pop_fscreatecon(selabel);
++#endif
++            if (status == 0)
++                return 0;
++        }
+         krb5_set_error_message(context, KRB5_FCC_NOFILE,
+                                _("Credential cache directory %s does not "
+                                  "exist"), dirname);
+--- krb5/src/lib/krb5/os/trace.c
++++ krb5/src/lib/krb5/os/trace.c
+@@ -401,7 +401,7 @@ krb5_set_trace_filename(krb5_context con
+     fd = malloc(sizeof(*fd));
+     if (fd == NULL)
+         return ENOMEM;
+-    *fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, 0600);
++    *fd = THREEPARAMOPEN(filename, O_WRONLY|O_CREAT|O_APPEND, 0600);
+     if (*fd == -1) {
+         free(fd);
+         return errno;
+--- krb5/src/plugins/kdb/db2/kdb_db2.c
++++ krb5/src/plugins/kdb/db2/kdb_db2.c
+@@ -683,8 +683,8 @@
+     if (retval)
+         return retval;
+ 
+-    dbc->db_lf_file = open(dbc->db_lf_name, O_CREAT | O_RDWR | O_TRUNC,
+-                           0600);
++    dbc->db_lf_file = THREEPARAMOPEN(dbc->db_lf_name,
++                                     O_CREAT | O_RDWR | O_TRUNC, 0600);
+     if (dbc->db_lf_file < 0) {
+         retval = errno;
+         goto cleanup;
+--- krb5/src/plugins/kdb/db2/libdb2/recno/rec_open.c
++++ krb5/src/plugins/kdb/db2/libdb2/recno/rec_open.c
+@@ -51,6 +51,7 @@
+ #include <stdio.h>
+ #include <unistd.h>
+ 
++#include "k5-int.h"
+ #include "db-int.h"
+ #include "recno.h"
+ 
+@@ -68,7 +69,8 @@
+ 	int rfd = -1, sverrno;
+ 
+ 	/* Open the user's file -- if this fails, we're done. */
+-	if (fname != NULL && (rfd = open(fname, flags | O_BINARY, mode)) < 0)
++	if (fname != NULL &&
++            (rfd = THREEPARAMOPEN(fname, flags | O_BINARY, mode)) < 0)
+ 		return (NULL);
+ 
+ 	if (fname != NULL && fcntl(rfd, F_SETFD, 1) == -1) {
+--- krb5/src/kdc/main.c
++++ krb5/src/kdc/main.c
+@@ -905,7 +905,7 @@ write_pid_file(const char *path)
+     FILE *file;
+     unsigned long pid;
+ 
+-    file = fopen(path, "w");
++    file = WRITABLEFOPEN(path, "w");
+     if (file == NULL)
+         return errno;
+     pid = (unsigned long) getpid();
+--- krb5/src/lib/kdb/kdb_log.c
++++ krb5/src/lib/kdb/kdb_log.c
+@@ -566,7 +566,7 @@ ulog_map(krb5_context context, const cha
+             return (errno);
+         }
+ 
+-        ulogfd = open(logname, O_RDWR | O_CREAT, 0600);
++        ulogfd = THREEPARAMOPEN(logname, O_RDWR | O_CREAT, 0600);
+         if (ulogfd == -1) {
+             return (errno);
+         }
+--- krb5/src/util/gss-kernel-lib/Makefile.in
++++ krb5/src/util/gss-kernel-lib/Makefile.in
+@@ -60,6 +60,7 @@ HEADERS= \
+ 	gssapi_err_generic.h \
+ 	k5-int.h \
+ 	k5-int-pkinit.h \
++	k5-label.h \
+ 	k5-thread.h \
+ 	k5-platform.h \
+ 	k5-buf.h \
+@@ -166,10 +167,12 @@ gssapi_generic.h: $(GSS_GENERIC)/gssapi_
+ 	$(CP) $(GSS_GENERIC)/gssapi_generic.h $@
+ gssapi_err_generic.h: $(GSS_GENERIC_BUILD)/gssapi_err_generic.h
+ 	$(CP) $(GSS_GENERIC_BUILD)/gssapi_err_generic.h $@
+-k5-int.h: $(INCLUDE)/k5-int.h
++k5-int.h: $(INCLUDE)/k5-int.h k5-label.h
+ 	$(CP) $(INCLUDE)/k5-int.h $@
+ k5-int-pkinit.h: $(INCLUDE)/k5-int-pkinit.h
+ 	$(CP) $(INCLUDE)/k5-int-pkinit.h $@
++k5-label.h: $(INCLUDE)/k5-label.h
++	$(CP) $(INCLUDE)/k5-label.h $@
+ k5-thread.h: $(INCLUDE)/k5-thread.h
+ 	$(CP) $(INCLUDE)/k5-thread.h $@
+ k5-platform.h: $(INCLUDE)/k5-platform.h
diff --git a/SOURCES/krb5-1.11.2-arcfour_short.patch b/SOURCES/krb5-1.11.2-arcfour_short.patch
new file mode 100644
index 0000000..2f67ae3
--- /dev/null
+++ b/SOURCES/krb5-1.11.2-arcfour_short.patch
@@ -0,0 +1,122 @@
+Needed by tests for empty-password cases.
+
+commit 1e123231769fe640f446442cb210664d280ccbac
+Author: Greg Hudson <ghudson@mit.edu>
+Date:   Fri May 24 13:16:52 2013 -0400
+
+    Fix rc4 string-to-key on unterminated inputs
+    
+    The internal UTF-8 to UCS-2 conversion functions did not properly
+    respect their length arguments, instead assuming that the input string
+    is terminated with a zero bytes.  As a result,
+    krb5int_arcfour_string_to_key could fail on unterminated inputs.  Fix
+    the underlying support functions to read their inputs only up to the
+    specified length.
+    
+    ticket: 7643 (new)
+
+diff --git a/src/util/support/utf8_conv.c b/src/util/support/utf8_conv.c
+index d580bbc..b8bf989 100644
+--- a/src/util/support/utf8_conv.c
++++ b/src/util/support/utf8_conv.c
+@@ -78,7 +78,8 @@ k5_utf8s_to_ucs2s(krb5_ucs2 *ucs2str,
+ 
+     /* If input ptr is NULL or empty... */
+     if (utf8str == NULL || *utf8str == '\0') {
+-        *ucs2str = 0;
++        if (ucs2str != NULL)
++            *ucs2str = 0;
+ 
+         return 0;
+     }
+@@ -119,9 +120,7 @@ k5_utf8s_to_ucs2s(krb5_ucs2 *ucs2str,
+         ucs2len++;              /* Count number of wide chars stored/required */
+     }
+ 
+-    assert(ucs2len < count);
+-
+-    if (ucs2str != NULL) {
++    if (ucs2str != NULL && ucs2len < count) {
+         /* Add null terminator if there's room in the buffer. */
+         ucs2str[ucs2len] = 0;
+     }
+@@ -172,12 +171,13 @@ krb5int_utf8cs_to_ucs2s(const char *utf8s,
+         return ENOMEM;
+     }
+ 
+-    len = k5_utf8s_to_ucs2s(*ucs2s, utf8s, chars + 1, 0);
++    len = k5_utf8s_to_ucs2s(*ucs2s, utf8s, chars, 0);
+     if (len < 0) {
+         free(*ucs2s);
+         *ucs2s = NULL;
+         return EINVAL;
+     }
++    (*ucs2s)[chars] = 0;
+ 
+     if (ucs2chars != NULL) {
+         *ucs2chars = chars;
+@@ -223,21 +223,23 @@ krb5int_utf8cs_to_ucs2les(const char *utf8s,
+ {
+     ssize_t len;
+     size_t chars;
++    krb5_ucs2 *ucs2s;
+ 
+-    chars = krb5int_utf8c_chars(utf8s, utf8slen);
++    *ucs2les = NULL;
+ 
+-    *ucs2les = (unsigned char *)malloc((chars + 1) * sizeof(krb5_ucs2));
+-    if (*ucs2les == NULL) {
++    chars = krb5int_utf8c_chars(utf8s, utf8slen);
++    ucs2s = malloc((chars + 1) * sizeof(krb5_ucs2));
++    if (ucs2s == NULL)
+         return ENOMEM;
+-    }
+ 
+-    len = k5_utf8s_to_ucs2s((krb5_ucs2 *)*ucs2les, utf8s, chars + 1, 1);
++    len = k5_utf8s_to_ucs2s(ucs2s, utf8s, chars, 1);
+     if (len < 0) {
+-        free(*ucs2les);
+-        *ucs2les = NULL;
++        free(ucs2s);
+         return EINVAL;
+     }
++    ucs2s[chars] = 0;
+ 
++    *ucs2les = (unsigned char *)ucs2s;
+     if (ucs2leslen != NULL) {
+         *ucs2leslen = chars * sizeof(krb5_ucs2);
+     }
+@@ -402,13 +404,14 @@ krb5int_ucs2cs_to_utf8s(const krb5_ucs2 *ucs2s,
+         return ENOMEM;
+     }
+ 
+-    len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2s,
+-                            (size_t)len + 1, (ssize_t)ucs2slen, 0);
++    len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2s, (size_t)len,
++                            (ssize_t)ucs2slen, 0);
+     if (len < 0) {
+         free(*utf8s);
+         *utf8s = NULL;
+         return EINVAL;
+     }
++    (*utf8s)[len] = '\0';
+ 
+     if (utf8slen != NULL) {
+         *utf8slen = len;
+@@ -438,13 +441,14 @@ krb5int_ucs2lecs_to_utf8s(const unsigned char *ucs2les,
+         return ENOMEM;
+     }
+ 
+-    len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2les,
+-                            (size_t)len + 1, (ssize_t)ucs2leslen, 1);
++    len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2les, (size_t)len,
++                            (ssize_t)ucs2leslen, 1);
+     if (len < 0) {
+         free(*utf8s);
+         *utf8s = NULL;
+         return EINVAL;
+     }
++    (*utf8s)[len] = '\0';
+ 
+     if (utf8slen != NULL) {
+         *utf8slen = len;
diff --git a/SOURCES/krb5-1.11.2-empty_passwords.patch b/SOURCES/krb5-1.11.2-empty_passwords.patch
new file mode 100644
index 0000000..3941084
--- /dev/null
+++ b/SOURCES/krb5-1.11.2-empty_passwords.patch
@@ -0,0 +1,492 @@
+Should fix #960001.  Adjusted to apply to 1.11.2, which didn't yet include
+some other developments, renames, reformatting, and fewer tests.
+
+commit f3458ed803ae97b6c6c7c63baeb82b26c4943d4c
+Author: Greg Hudson <ghudson@mit.edu>
+Date:   Thu May 23 15:33:58 2013 -0400
+
+    Make empty passwords work via init_creds APIs
+    
+    In the gak_data value used by krb5_get_as_key_password, separate the
+    already-known password from the storage we might have allocated to put
+    it in, so that we no longer use an empty data buffer to determine
+    whether we know the password.  This allows empty passwords to work via
+    the API.
+    
+    Remove the kadm5 test which explicitly uses an empty password.
+    
+    Based on a patch from Stef Walter.
+    
+    ticket: 7642
+
+diff --git a/src/lib/kadm5/unit-test/api.current/init.exp b/src/lib/kadm5/unit-test/api.current/init.exp
+index b324df8..d9ae3fb 100644
+--- a/src/lib/kadm5/unit-test/api.current/init.exp
++++ b/src/lib/kadm5/unit-test/api.current/init.exp
+@@ -99,33 +99,6 @@ proc test6 {} {
+ }
+ if { $RPC } { test6 } 
+ 
+-test "init 7"
+-proc test7 {} {
+-    global test
+-
+-    send "kadm5_init admin \"\" \$KADM5_ADMIN_SERVICE null \$KADM5_STRUCT_VERSION \$KADM5_API_VERSION_3 server_handle\n"
+-
+-    expect {
+-	-re "assword\[^\r\n\]*:" { }
+-	-re "key:$" { }
+-	eof {
+-		fail "$test: eof instead of password prompt"
+-		api_exit
+-		api_start
+-		return
+-	}
+-	timeout {
+-	    fail "$test: timeout instead of password prompt"
+-	    return
+-	}
+-    }
+-    one_line_succeed_test "admin"
+-    if {! [cmd {kadm5_destroy $server_handle}]} {
+-	error_and_restart "$test: couldn't close database"
+-    }
+-}
+-if { $RPC } { test7 } 
+-
+ test "init 8"
+ 
+ proc test8 {} {
+diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
+index 59614e7..20bc689 100644
+--- a/src/lib/krb5/krb/get_in_tkt.c
++++ b/src/lib/krb5/krb/get_in_tkt.c
+@@ -493,8 +493,7 @@ krb5_init_creds_free(krb5_context context,
+     }
+     k5_response_items_free(ctx->rctx.items);
+     free(ctx->in_tkt_service);
+-    zap(ctx->password.data, ctx->password.length);
+-    krb5_free_data_contents(context, &ctx->password);
++    zapfree(ctx->gakpw.storage.data, ctx->gakpw.storage.length);
+     krb5_free_error(context, ctx->err_reply);
+     krb5_free_pa_data(context, ctx->err_padata);
+     krb5_free_cred_contents(context, &ctx->cred);
+@@ -788,7 +787,7 @@ krb5_init_creds_init(krb5_context context,
+     ctx->prompter = prompter;
+     ctx->prompter_data = data;
+     ctx->gak_fct = krb5_get_as_key_password;
+-    ctx->gak_data = &ctx->password;
++    ctx->gak_data = &ctx->gakpw;
+ 
+     ctx->request_time = 0; /* filled in later */
+     ctx->start_time = start_time;
+diff --git a/src/lib/krb5/krb/gic_pwd.c b/src/lib/krb5/krb/gic_pwd.c
+index 22db2b5..a97823f 100644
+--- a/src/lib/krb5/krb/gic_pwd.c
++++ b/src/lib/krb5/krb/gic_pwd.c
+@@ -17,22 +17,19 @@ krb5_get_as_key_password(krb5_context context,
+                          void *gak_data,
+                          k5_response_items *ritems)
+ {
+-    krb5_data *password;
++    struct gak_password *gp = gak_data;
+     krb5_error_code ret;
+     krb5_data defsalt;
+     char *clientstr;
+-    char promptstr[1024];
++    char promptstr[1024], pwbuf[1024];
++    krb5_data pw;
+     krb5_prompt prompt;
+     krb5_prompt_type prompt_type;
+     const char *rpass;
+ 
+-    password = (krb5_data *) gak_data;
+-    assert(password->length > 0);
+-
+     /* If we need to get the AS key via the responder, ask for it. */
+     if (as_key == NULL) {
+-        /* However, if we already have a password, don't ask. */
+-        if (password->data[0] != '\0')
++        if (gp->password != NULL)
+             return 0;
+ 
+         return k5_response_items_ask_question(ritems,
+@@ -55,17 +52,20 @@ krb5_get_as_key_password(krb5_context context,
+         }
+     }
+ 
+-    if (password->data[0] == '\0') {
++    if (gp->password == NULL) {
+         /* Check the responder for the password. */
+         rpass = k5_response_items_get_answer(ritems,
+                                              KRB5_RESPONDER_QUESTION_PASSWORD);
+         if (rpass != NULL) {
+-            strlcpy(password->data, rpass, password->length);
+-            password->length = strlen(password->data);
++            ret = alloc_data(&gp->storage, strlen(rpass));
++            if (ret)
++                return ret;
++            memcpy(gp->storage.data, rpass, strlen(rpass));
++            gp->password = &gp->storage;
+         }
+     }
+ 
+-    if (password->data[0] == '\0') {
++    if (gp->password == NULL) {
+         if (prompter == NULL)
+             return(EIO);
+ 
+@@ -76,9 +76,10 @@ krb5_get_as_key_password(krb5_context context,
+                  clientstr);
+         free(clientstr);
+ 
++        pw = make_data(pwbuf, sizeof(pwbuf));
+         prompt.prompt = promptstr;
+         prompt.hidden = 1;
+-        prompt.reply = password;
++        prompt.reply = &pw;
+         prompt_type = KRB5_PROMPT_TYPE_PASSWORD;
+ 
+         /* PROMPTER_INVOCATION */
+@@ -87,6 +88,12 @@ krb5_get_as_key_password(krb5_context context,
+         krb5int_set_prompt_types(context, 0);
+         if (ret)
+             return(ret);
++
++        ret = krb5int_copy_data_contents(context, &pw, &gp->storage);
++        zap(pw.data, pw.length);
++        if (ret)
++            return ret;
++        gp->password = &gp->storage;
+     }
+ 
+     if (salt == NULL) {
+@@ -98,7 +105,7 @@ krb5_get_as_key_password(krb5_context context,
+         defsalt.length = 0;
+     }
+ 
+-    ret = krb5_c_string_to_key_with_params(context, etype, password, salt,
++    ret = krb5_c_string_to_key_with_params(context, etype, gp->password, salt,
+                                            params->data?params:NULL, as_key);
+ 
+     if (defsalt.length)
+@@ -118,16 +125,11 @@ krb5_init_creds_set_password(krb5_context context,
+     if (s == NULL)
+         return ENOMEM;
+ 
+-    if (ctx->password.data != NULL) {
+-        zap(ctx->password.data, ctx->password.length);
+-        krb5_free_data_contents(context, &ctx->password);
+-    }
+-
+-    ctx->password.data = s;
+-    ctx->password.length = strlen(s);
++    zapfree(ctx->gakpw.storage.data, ctx->gakpw.storage.length);
++    ctx->gakpw.storage = string2data(s);
++    ctx->gakpw.password = &ctx->gakpw.storage;
+     ctx->gak_fct = krb5_get_as_key_password;
+-    ctx->gak_data = &ctx->password;
+-
++    ctx->gak_data = &ctx->gakpw;
+     return 0;
+ }
+ 
+@@ -257,6 +259,7 @@ krb5_get_init_creds_password(krb5_context context,
+     int tries;
+     krb5_creds chpw_creds;
+     krb5_get_init_creds_opt *chpw_opts = NULL;
++    struct gak_password gakpw;
+     krb5_data pw0, pw1;
+     char banner[1024], pw0array[1024], pw1array[1024];
+     krb5_prompt prompt[2];
+@@ -267,29 +270,18 @@ krb5_get_init_creds_password(krb5_context context,
+     use_master = 0;
+     as_reply = NULL;
+     memset(&chpw_creds, 0, sizeof(chpw_creds));
++    memset(&gakpw, 0, sizeof(gakpw));
+ 
+-    pw0.data = pw0array;
+-
+-    if (password && password[0]) {
+-        if (strlcpy(pw0.data, password, sizeof(pw0array)) >= sizeof(pw0array)) {
+-            ret = EINVAL;
+-            goto cleanup;
+-        }
+-        pw0.length = strlen(password);
+-    } else {
+-        pw0.data[0] = '\0';
+-        pw0.length = sizeof(pw0array);
++    if (password != NULL) {
++        pw0 = string2data((char *)password);
++        gakpw.password = &pw0;
+     }
+ 
+-    pw1.data = pw1array;
+-    pw1.data[0] = '\0';
+-    pw1.length = sizeof(pw1array);
+-
+     /* first try: get the requested tkt from any kdc */
+ 
+     ret = krb5int_get_init_creds(context, creds, client, prompter, data,
+                                  start_time, in_tkt_service, options,
+-                                 krb5_get_as_key_password, (void *) &pw0,
++                                 krb5_get_as_key_password, &gakpw,
+                                  &use_master, &as_reply);
+ 
+     /* check for success */
+@@ -318,7 +310,7 @@ krb5_get_init_creds_password(krb5_context context,
+         }
+         ret2 = krb5int_get_init_creds(context, creds, client, prompter, data,
+                                       start_time, in_tkt_service, options,
+-                                      krb5_get_as_key_password, (void *) &pw0,
++                                      krb5_get_as_key_password, &gakpw,
+                                       &use_master, &as_reply);
+ 
+         if (ret2 == 0) {
+@@ -365,15 +357,21 @@ krb5_get_init_creds_password(krb5_context context,
+     if ((ret = krb5int_get_init_creds(context, &chpw_creds, client,
+                                       prompter, data,
+                                       start_time, "kadmin/changepw", chpw_opts,
+-                                      krb5_get_as_key_password, (void *) &pw0,
++                                      krb5_get_as_key_password, &gakpw,
+                                       &use_master, NULL)))
+         goto cleanup;
+ 
++    pw0.data = pw0array;
++    pw0.data[0] = '\0';
++    pw0.length = sizeof(pw0array);
+     prompt[0].prompt = _("Enter new password");
+     prompt[0].hidden = 1;
+     prompt[0].reply = &pw0;
+     prompt_types[0] = KRB5_PROMPT_TYPE_NEW_PASSWORD;
+ 
++    pw1.data = pw1array;
++    pw1.data[0] = '\0';
++    pw1.length = sizeof(pw1array);
+     prompt[1].prompt = _("Enter it again");
+     prompt[1].hidden = 1;
+     prompt[1].reply = &pw1;
+@@ -460,10 +458,11 @@ krb5_get_init_creds_password(krb5_context context,
+        is final.  */
+ 
+     TRACE_GIC_PWD_CHANGED(context);
++    gakpw.password = &pw0;
+     ret = krb5int_get_init_creds(context, creds, client, prompter, data,
+                                  start_time, in_tkt_service, options,
+-                                 krb5_get_as_key_password, (void *) &pw0,
++                                 krb5_get_as_key_password, &gakpw,
+                                  &use_master, &as_reply);
+     if (ret)
+         goto cleanup;
+ 
+@@ -474,6 +473,7 @@ cleanup:
+ 
+     if (chpw_opts)
+         krb5_get_init_creds_opt_free(context, chpw_opts);
++    zapfree(gakpw.storage.data, gakpw.storage.length);
+     memset(pw0array, 0, sizeof(pw0array));
+     memset(pw1array, 0, sizeof(pw1array));
+     krb5_free_cred_contents(context, &chpw_creds);
+@@ -512,21 +512,17 @@ krb5_get_in_tkt_with_password(krb5_context context, krb5_flags options,
+                               krb5_creds *creds, krb5_kdc_rep **ret_as_reply)
+ {
+     krb5_error_code retval;
+-    krb5_data pw0;
+-    char pw0array[1024];
++    struct gak_password gakpw;
++    krb5_data pw;
+     char * server;
+     krb5_principal server_princ, client_princ;
+     int use_master = 0;
+     krb5_get_init_creds_opt *opts = NULL;
+ 
+-    pw0.data = pw0array;
+-    if (password && password[0]) {
+-        if (strlcpy(pw0.data, password, sizeof(pw0array)) >= sizeof(pw0array))
+-            return EINVAL;
+-        pw0.length = strlen(password);
+-    } else {
+-        pw0.data[0] = '\0';
+-        pw0.length = sizeof(pw0array);
++    memset(&gakpw, 0, sizeof(gakpw));
++    if (password != NULL) {
++        pw = string2data((char *)password);
++        gakpw.password = &pw;
+     }
+     retval = krb5int_populate_gic_opt(context, &opts,
+                                       options, addrs, ktypes,
+@@ -544,10 +540,11 @@ krb5_get_in_tkt_with_password(krb5_context context, krb5_flags options,
+     retval = krb5int_get_init_creds(context, creds, creds->client,
+                                     krb5_prompter_posix, NULL,
+                                     0, server, opts,
+-                                    krb5_get_as_key_password, &pw0,
++                                    krb5_get_as_key_password, &gakpw,
+                                     &use_master, ret_as_reply);
+     krb5_free_unparsed_name( context, server);
+     krb5_get_init_creds_opt_free(context, opts);
++    zapfree(gakpw.storage.data, gakpw.storage.length);
+     if (retval) {
+         return (retval);
+     }
+diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h
+index d886c7a..4dbb0e9 100644
+--- a/src/lib/krb5/krb/init_creds_ctx.h
++++ b/src/lib/krb5/krb/init_creds_ctx.h
+@@ -10,6 +10,11 @@ struct krb5_clpreauth_rock_st {
+     k5_json_value *cc_config_out;
+ };
+ 
++struct gak_password {
++    krb5_data storage;
++    const krb5_data *password;
++};
++
+ struct _krb5_init_creds_context {
+     krb5_gic_opt_ext *opte;
+     char *in_tkt_service;
+@@ -23,7 +28,7 @@ struct _krb5_init_creds_context {
+     krb5_deltat renew_life;
+     krb5_boolean complete;
+     unsigned int loopcount;
+-    krb5_data password;
++    struct gak_password gakpw;
+     krb5_error *err_reply;
+     krb5_pa_data **err_padata;
+     krb5_creds cred;
+diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
+index 2358c89..91f312e 100644
+--- a/src/tests/Makefile.in
++++ b/src/tests/Makefile.in
+@@ -28,6 +28,9 @@ kdbtest: kdbtest.o $(KDB5_DEPLIBS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+ KADMIN_OPTS= -d $(TEST_DB) -r $(TEST_REALM) -P $(TEST_MKEY)
+ KTEST_OPTS= $(KADMIN_OPTS) -p $(TEST_PREFIX) -n $(TEST_NUM) -D $(TEST_DEPTH)
+ 
++t_init_creds: t_init_creds.o $(KRB5_BASE_DEPLIBS)
++	$(CC_LINK) -o $@ t_init_creds.o $(KRB5_BASE_LIBS)
++
+ hist: hist.o $(KDB5_DEPLIBS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+ 	$(CC_LINK) -o $@ hist.o $(KDB5_LIBS) $(KADMSRV_LIBS) $(KRB5_BASE_LIBS)
+ 
+@@ -73,7 +76,7 @@ kdb_check: kdc.conf krb5.conf
+ 	$(RUN_SETUP) $(VALGRIND) ../kadmin/dbutil/kdb5_util $(KADMIN_OPTS) destroy -f
+ 	$(RM) $(TEST_DB)* stash_file
+ 
+-check-pytests:: hist
++check-pytests:: hist t_init_creds
+ 	$(RUNPYTEST) $(srcdir)/t_general.py $(PYTESTFLAGS)
+ 	$(RUNPYTEST) $(srcdir)/t_iprop.py $(PYTESTFLAGS)
+ 	$(RUNPYTEST) $(srcdir)/t_anonpkinit.py $(PYTESTFLAGS)
+diff --git a/src/tests/t_general.py b/src/tests/t_general.py
+index bb7a543..98e77a2 100755
+--- a/src/tests/t_general.py
++++ b/src/tests/t_general.py
+@@ -22,6 +22,15 @@ for realm in multipass_realms(create_host=False):
+     # Test kinit against kdb keytab
+     realm.run_as_master([kinit, "-k", "-t", "KDB:", realm.user_princ])
+ 
++# Test that we can get initial creds with an empty password via the
++# API.  We have to disable the "empty" pwqual module to create a
++# principal with an empty password.  (Regression test for #7642.)
++conf={'master':{'plugins': {'pwqual': {'disable': 'empty'}}}}
++realm = K5Realm(create_user=False, create_host=False, krb5_conf=conf)
++realm.run_kadminl('addprinc -pw "" user')
++realm.run_as_client(['./t_init_creds', 'user', ''])
++realm.stop()
++
+ realm = K5Realm(create_host=False)
+ 
+ # Create a policy and see if it survives a dump/load.
+diff --git a/src/tests/t_init_creds.c b/src/tests/t_init_creds.c
+new file mode 100644
+index 0000000..6be8340
+--- /dev/null
++++ b/src/tests/t_init_creds.c
+@@ -0,0 +1,88 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* tests/t_init_creds.c - test harness for getting initial creds */
++/*
++ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
++ * All rights reserved.
++ *
++ * 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.
++ */
++
++/*
++ * This program exercises the init_creds APIs in ways kinit doesn't.  Right now
++ * it is very simplistic, but it can be extended as needed.
++ */
++
++#include <krb5.h>
++#include <stdio.h>
++
++static krb5_context ctx;
++
++static void
++check(krb5_error_code code)
++{
++    const char *errmsg;
++
++    if (code) {
++        errmsg = krb5_get_error_message(ctx, code);
++        fprintf(stderr, "%s\n", errmsg);
++        krb5_free_error_message(ctx, errmsg);
++        exit(1);
++    }
++}
++
++int
++main(int argc, char **argv)
++{
++    const char *princstr, *password;
++    krb5_principal client;
++    krb5_init_creds_context icc;
++    krb5_creds creds;
++
++    if (argc != 3) {
++        fprintf(stderr, "Usage: t_init_creds princname password\n");
++        exit(1);
++    }
++    princstr = argv[1];
++    password = argv[2];
++
++    check(krb5_init_context(&ctx));
++    check(krb5_parse_name(ctx, princstr, &client));
++
++    /* Try once with the traditional interface. */
++    check(krb5_get_init_creds_password(ctx, &creds, client, password, NULL,
++                                       NULL, 0, NULL, NULL));
++    krb5_free_cred_contents(ctx, &creds);
++
++    /* Try again with the step interface. */
++    check(krb5_init_creds_init(ctx, client, NULL, NULL, 0, NULL, &icc));
++    check(krb5_init_creds_set_password(ctx, icc, password));
++    check(krb5_init_creds_get(ctx, icc));
++    krb5_init_creds_free(ctx, icc);
++
++    krb5_free_principal(ctx, client);
++    krb5_free_context(ctx);
++    return 0;
++}
diff --git a/SOURCES/krb5-1.11.2-keycheck.patch b/SOURCES/krb5-1.11.2-keycheck.patch
new file mode 100644
index 0000000..4b4bd08
--- /dev/null
+++ b/SOURCES/krb5-1.11.2-keycheck.patch
@@ -0,0 +1,230 @@
+From c7047421487c0e616b881c0922937bc759114233 Mon Sep 17 00:00:00 2001
+From: Nathaniel McCallum <npmccallum@redhat.com>
+Date: Fri, 3 May 2013 15:44:44 -0400
+Subject: [PATCH 1/3] Check for keys in encrypted timestamp/challenge
+
+Encrypted timestamp and encrypted challenge cannot succeed if the
+client has no long-term key matching the request enctypes, so do not
+offer them in that case.
+
+ticket: 7630
+
+NPM - This is a backport from the upstream fix. See upstream commits:
+  https://github.com/krb5/krb5/commit/e50482720a805ecd8c160e4a8f4a846e6327dca2
+  https://github.com/krb5/krb5/commit/9593d1311fa5e6e841c429653ad35a63d17c2fdd
+---
+ src/kdc/kdc_preauth_ec.c    | 23 +++++++++++++++++++++--
+ src/kdc/kdc_preauth_encts.c | 22 ++++++++++++++++++++--
+ 2 files changed, 41 insertions(+), 4 deletions(-)
+
+diff --git a/src/kdc/kdc_preauth_ec.c b/src/kdc/kdc_preauth_ec.c
+index 9d7236c..db3ad07 100644
+--- a/src/kdc/kdc_preauth_ec.c
++++ b/src/kdc/kdc_preauth_ec.c
+@@ -39,8 +39,27 @@ ec_edata(krb5_context context, krb5_kdc_req *request,
+          krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
+          krb5_kdcpreauth_edata_respond_fn respond, void *arg)
+ {
+-    krb5_keyblock *armor_key = cb->fast_armor(context, rock);
+-    (*respond)(arg, (armor_key == NULL) ? ENOENT : 0, NULL);
++    krb5_boolean have_client_keys = FALSE;
++    krb5_keyblock *armor_key;
++    krb5_key_data *kd;
++    int i;
++
++    armor_key = cb->fast_armor(context, rock);
++
++    for (i = 0; i < rock->request->nktypes; i++) {
++        if (krb5_dbe_find_enctype(context, rock->client,
++                                  rock->request->ktype[i],
++                                  -1, 0, &kd) == 0) {
++            have_client_keys = TRUE;
++            break;
++        }
++    }
++
++    /* Encrypted challenge only works with FAST, and requires a client key. */
++    if (armor_key == NULL || !have_client_keys)
++        (*respond)(arg, ENOENT, NULL);
++    else
++        (*respond)(arg, 0, NULL);
+ }
+ 
+ static void
+diff --git a/src/kdc/kdc_preauth_encts.c b/src/kdc/kdc_preauth_encts.c
+index d708061..adde6e2 100644
+--- a/src/kdc/kdc_preauth_encts.c
++++ b/src/kdc/kdc_preauth_encts.c
+@@ -34,9 +34,27 @@ enc_ts_get(krb5_context context, krb5_kdc_req *request,
+            krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
+            krb5_kdcpreauth_edata_respond_fn respond, void *arg)
+ {
+-    krb5_keyblock *armor_key = cb->fast_armor(context, rock);
++    krb5_boolean have_client_keys = FALSE;
++    krb5_keyblock *armor_key;
++    krb5_key_data *kd;
++    int i;
++
++    armor_key = cb->fast_armor(context, rock);
++
++    for (i = 0; i < rock->request->nktypes; i++) {
++        if (krb5_dbe_find_enctype(context, rock->client,
++                                  rock->request->ktype[i],
++                                  -1, 0, &kd) == 0) {
++            have_client_keys = TRUE;
++            break;
++        }
++    }
+ 
+-    (*respond)(arg, (armor_key != NULL) ? ENOENT : 0, NULL);
++    /* Encrypted timestamp must not be used with FAST, and requires a key. */
++    if (armor_key != NULL || !have_client_keys)
++        (*respond)(arg, ENOENT, NULL);
++    else
++        (*respond)(arg, 0, NULL);
+ }
+ 
+ static void
+-- 
+1.8.2.1
+
+
+From 4d19790527e2c9d52bb05abd6048a63a1a8c222c Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson@mit.edu>
+Date: Mon, 29 Apr 2013 14:55:31 -0400
+Subject: [PATCH 2/3] Don't send empty etype info from KDC
+
+RFC 4120 prohibits empty ETYPE-INFO2 sequences (though not ETYPE-INFO
+sequences), and our client errors out if it sees an empty sequence of
+either.
+
+ticket: 7630
+---
+ src/kdc/kdc_preauth.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
+index 42a37a8..5c3b615 100644
+--- a/src/kdc/kdc_preauth.c
++++ b/src/kdc/kdc_preauth.c
+@@ -1404,6 +1404,11 @@ etype_info_helper(krb5_context context, krb5_kdc_req *request,
+             seen_des++;
+         }
+     }
++
++    /* If the list is empty, don't send it at all. */
++    if (i == 0)
++        goto cleanup;
++
+     if (etype_info2)
+         retval = encode_krb5_etype_info2(entry, &scratch);
+     else
+-- 
+1.8.2.1
+
+
+From a04cf2e89f4101eb6c2ec44ef1948976fe5fe9d3 Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson@mit.edu>
+Date: Thu, 2 May 2013 16:15:32 -0400
+Subject: [PATCH 3/3] Make AS requests work with no client key
+
+If we cannot find a client key while preparing an AS reply, give
+preauth mechanisms a chance to replace the reply key before erroring
+out.
+
+ticket: 7630
+---
+ src/kdc/do_as_req.c   | 36 ++++++++++++++++++++----------------
+ src/kdc/kdc_preauth.c |  6 ++++++
+ 2 files changed, 26 insertions(+), 16 deletions(-)
+
+diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
+index 79da300..cb91803 100644
+--- a/src/kdc/do_as_req.c
++++ b/src/kdc/do_as_req.c
+@@ -195,23 +195,18 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
+                                    useenctype, -1, 0, &client_key))
+             break;
+     }
+-    if (!(client_key)) {
+-        /* Cannot find an appropriate key */
+-        state->status = "CANT_FIND_CLIENT_KEY";
+-        errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
+-        goto egress;
+-    }
+-    state->rock.client_key = client_key;
+ 
+-    /* convert client.key_data into a real key */
+-    if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL,
+-                                             client_key,
+-                                             &state->client_keyblock,
+-                                             NULL))) {
+-        state->status = "DECRYPT_CLIENT_KEY";
+-        goto egress;
++    if (client_key != NULL) {
++        /* Decrypt the client key data entry to get the real reply key. */
++        errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL, client_key,
++                                            &state->client_keyblock, NULL);
++        if (errcode) {
++            state->status = "DECRYPT_CLIENT_KEY";
++            goto egress;
++        }
++        state->client_keyblock.enctype = useenctype;
++        state->rock.client_key = client_key;
+     }
+-    state->client_keyblock.enctype = useenctype;
+ 
+     /* Start assembling the response */
+     state->reply.msg_type = KRB5_AS_REP;
+@@ -248,6 +243,14 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
+         goto egress;
+     }
+ 
++    /* If we didn't find a client long-term key and no preauth mechanism
++     * replaced the reply key, error out now. */
++    if (state->client_keyblock.enctype == ENCTYPE_NULL) {
++        state->status = "CANT_FIND_CLIENT_KEY";
++        errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
++        goto egress;
++    }
++
+     errcode = handle_authdata(kdc_context,
+                               state->c_flags,
+                               state->client,
+@@ -306,7 +309,8 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
+                                   &state->reply_encpart, 0,
+                                   as_encrypting_key,
+                                   &state->reply, &response);
+-    state->reply.enc_part.kvno = client_key->key_data_kvno;
++    if (client_key != NULL)
++        state->reply.enc_part.kvno = client_key->key_data_kvno;
+     if (errcode) {
+         state->status = "ENCODE_KDC_REP";
+         goto egress;
+diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
+index 5c3b615..5d12346 100644
+--- a/src/kdc/kdc_preauth.c
++++ b/src/kdc/kdc_preauth.c
+@@ -1473,6 +1473,9 @@ etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
+     krb5_etype_info_entry **entry = NULL;
+     krb5_data *scratch = NULL;
+ 
++    if (client_key == NULL)
++        return 0;
++
+     /*
+      * Skip PA-ETYPE-INFO completely if AS-REQ lists any "newer"
+      * enctypes.
+@@ -1576,6 +1579,9 @@ return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
+     krb5_key_data *     client_key = rock->client_key;
+     int i;
+ 
++    if (client_key == NULL)
++        return 0;
++
+     for (i = 0; i < request->nktypes; i++) {
+         if (enctype_requires_etype_info_2(request->ktype[i]))
+             return 0;
+-- 
+1.8.2.1
+
diff --git a/SOURCES/krb5-1.11.2-otp.patch b/SOURCES/krb5-1.11.2-otp.patch
new file mode 100644
index 0000000..eab913e
--- /dev/null
+++ b/SOURCES/krb5-1.11.2-otp.patch
@@ -0,0 +1,5163 @@
+From 9a0bb5ada335017c5da35cb41333632ae5577a93 Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson@mit.edu>
+Date: Tue, 15 Jan 2013 11:11:27 -0500
+Subject: [PATCH 1/4] Add internal KDC_DIR macro
+
+Define KDC_DIR in osconf.hin and use it for paths within the KDC
+directory.
+---
+ src/include/osconf.hin | 21 +++++++++++----------
+ 1 file changed, 11 insertions(+), 10 deletions(-)
+
+diff --git a/src/include/osconf.hin b/src/include/osconf.hin
+index c3a33c2..90ab86d 100644
+--- a/src/include/osconf.hin
++++ b/src/include/osconf.hin
+@@ -58,14 +58,15 @@
+ #define DEFAULT_PLUGIN_BASE_DIR "@LIBDIR/krb5/plugins"
+ #define PLUGIN_EXT              "@DYNOBJEXT"
+ 
+-#define DEFAULT_KDB_FILE        "@LOCALSTATEDIR/krb5kdc/principal"
+-#define DEFAULT_KEYFILE_STUB    "@LOCALSTATEDIR/krb5kdc/.k5."
+-#define KRB5_DEFAULT_ADMIN_ACL  "@LOCALSTATEDIR/krb5kdc/krb5_adm.acl"
++#define KDC_DIR                 "@LOCALSTATEDIR/krb5kdc"
++#define DEFAULT_KDB_FILE        KDC_DIR "/principal"
++#define DEFAULT_KEYFILE_STUB    KDC_DIR "/.k5."
++#define KRB5_DEFAULT_ADMIN_ACL  KDC_DIR "/krb5_adm.acl"
+ /* Used by old admin server */
+-#define DEFAULT_ADMIN_ACL       "@LOCALSTATEDIR/krb5kdc/kadm_old.acl"
++#define DEFAULT_ADMIN_ACL       KDC_DIR "/kadm_old.acl"
+ 
+ /* Location of KDC profile */
+-#define DEFAULT_KDC_PROFILE     "@LOCALSTATEDIR/krb5kdc/kdc.conf"
++#define DEFAULT_KDC_PROFILE     KDC_DIR "/kdc.conf"
+ #define KDC_PROFILE_ENV         "KRB5_KDC_PROFILE"
+ 
+ #if TARGET_OS_MAC
+@@ -93,8 +94,8 @@
+ /*
+  * Defaults for the KADM5 admin system.
+  */
+-#define DEFAULT_KADM5_KEYTAB    "@LOCALSTATEDIR/krb5kdc/kadm5.keytab"
+-#define DEFAULT_KADM5_ACL_FILE  "@LOCALSTATEDIR/krb5kdc/kadm5.acl"
++#define DEFAULT_KADM5_KEYTAB    KDC_DIR "/kadm5.keytab"
++#define DEFAULT_KADM5_ACL_FILE  KDC_DIR "/kadm5.acl"
+ #define DEFAULT_KADM5_PORT      749 /* assigned by IANA */
+ 
+ #define KRB5_DEFAULT_SUPPORTED_ENCTYPES                 \
+@@ -116,12 +117,12 @@
+  * krb5 slave support follows
+  */
+ 
+-#define KPROP_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/slave_datatrans"
+-#define KPROPD_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/from_master"
++#define KPROP_DEFAULT_FILE KDC_DIR "/slave_datatrans"
++#define KPROPD_DEFAULT_FILE KDC_DIR "/from_master"
+ #define KPROPD_DEFAULT_KDB5_UTIL "@SBINDIR/kdb5_util"
+ #define KPROPD_DEFAULT_KPROP "@SBINDIR/kprop"
+ #define KPROPD_DEFAULT_KRB_DB DEFAULT_KDB_FILE
+-#define KPROPD_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kpropd.acl"
++#define KPROPD_ACL_FILE KDC_DIR "/kpropd.acl"
+ 
+ /*
+  * GSS mechglue
+-- 
+1.8.2.1
+
+
+From 4ba748915908a45564d2e8a098cf107e39de3315 Mon Sep 17 00:00:00 2001
+From: Nathaniel McCallum <npmccallum@redhat.com>
+Date: Tue, 9 Apr 2013 11:17:04 -0400
+Subject: [PATCH 2/4] add k5memdup()
+
+---
+ src/include/k5-int.h | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/src/include/k5-int.h b/src/include/k5-int.h
+index 75e6783..7b5ab2c 100644
+--- a/src/include/k5-int.h
++++ b/src/include/k5-int.h
+@@ -2600,6 +2600,17 @@ k5alloc(size_t len, krb5_error_code *code)
+     return ptr;
+ }
+ 
++/* Return a copy of the len bytes of memory at in; set *code to 0 or ENOMEM. */
++static inline void *
++k5memdup(const void *in, size_t len, krb5_error_code *code)
++{
++    void *ptr = k5alloc(len, code);
++
++    if (ptr != NULL)
++        memcpy(ptr, in, len);
++    return ptr;
++}
++
+ krb5_error_code KRB5_CALLCONV
+ krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
+                               krb5_ccache ccache,
+-- 
+1.8.2.1
+
+
+From f98e3f2569996d84c968852b50f76173203e568f Mon Sep 17 00:00:00 2001
+From: Nathaniel McCallum <npmccallum@redhat.com>
+Date: Thu, 4 Apr 2013 13:39:21 -0400
+Subject: [PATCH 3/4] add libkrad
+
+---
+ src/configure.in             |   2 +-
+ src/include/Makefile.in      |   1 +
+ src/include/krad.h           | 267 ++++++++++++++++++++++
+ src/lib/Makefile.in          |   2 +-
+ src/lib/krad/Makefile.in     |  74 ++++++
+ src/lib/krad/attr.c          | 317 ++++++++++++++++++++++++++
+ src/lib/krad/attrset.c       | 244 ++++++++++++++++++++
+ src/lib/krad/client.c        | 335 ++++++++++++++++++++++++++++
+ src/lib/krad/code.c          | 111 +++++++++
+ src/lib/krad/deps            | 156 +++++++++++++
+ src/lib/krad/internal.h      | 155 +++++++++++++
+ src/lib/krad/libkrad.exports |  23 ++
+ src/lib/krad/packet.c        | 470 +++++++++++++++++++++++++++++++++++++++
+ src/lib/krad/remote.c        | 519 +++++++++++++++++++++++++++++++++++++++++++
+ src/lib/krad/t_attr.c        |  89 ++++++++
+ src/lib/krad/t_attrset.c     |  98 ++++++++
+ src/lib/krad/t_client.c      | 126 +++++++++++
+ src/lib/krad/t_code.c        |  54 +++++
+ src/lib/krad/t_daemon.h      |  92 ++++++++
+ src/lib/krad/t_daemon.py     |  76 +++++++
+ src/lib/krad/t_packet.c      | 194 ++++++++++++++++
+ src/lib/krad/t_remote.c      | 170 ++++++++++++++
+ src/lib/krad/t_test.c        |  50 +++++
+ src/lib/krad/t_test.h        |  60 +++++
+ 24 files changed, 3683 insertions(+), 2 deletions(-)
+ create mode 100644 src/include/krad.h
+ create mode 100644 src/lib/krad/Makefile.in
+ create mode 100644 src/lib/krad/attr.c
+ create mode 100644 src/lib/krad/attrset.c
+ create mode 100644 src/lib/krad/client.c
+ create mode 100644 src/lib/krad/code.c
+ create mode 100644 src/lib/krad/deps
+ create mode 100644 src/lib/krad/internal.h
+ create mode 100644 src/lib/krad/libkrad.exports
+ create mode 100644 src/lib/krad/packet.c
+ create mode 100644 src/lib/krad/remote.c
+ create mode 100644 src/lib/krad/t_attr.c
+ create mode 100644 src/lib/krad/t_attrset.c
+ create mode 100644 src/lib/krad/t_client.c
+ create mode 100644 src/lib/krad/t_code.c
+ create mode 100644 src/lib/krad/t_daemon.h
+ create mode 100644 src/lib/krad/t_daemon.py
+ create mode 100644 src/lib/krad/t_packet.c
+ create mode 100644 src/lib/krad/t_remote.c
+ create mode 100644 src/lib/krad/t_test.c
+ create mode 100644 src/lib/krad/t_test.h
+
+diff --git a/src/configure.in b/src/configure.in
+index faf93a1..d8676e5 100644
+--- a/src/configure.in
++++ b/src/configure.in
+@@ -1318,7 +1318,7 @@ dnl	lib/krb5/ccache/ccapi
+ 	lib/rpc lib/rpc/unit-test
+ 
+ 	lib/kadm5 lib/kadm5/clnt lib/kadm5/srv lib/kadm5/unit-test
+-
++	lib/krad
+ 	lib/apputils
+ 
+ dnl	ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
+diff --git a/src/include/Makefile.in b/src/include/Makefile.in
+index c69b809..869b04b 100644
+--- a/src/include/Makefile.in
++++ b/src/include/Makefile.in
+@@ -145,5 +145,6 @@ install-headers-unix install:: krb5/krb5.h profile.h
+ 	$(INSTALL_DATA) $(srcdir)/krb5/kadm5_hook_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)kadm5_hook_plugin.h
+ 	$(INSTALL_DATA) profile.h $(DESTDIR)$(KRB5_INCDIR)$(S)profile.h
+ 	$(INSTALL_DATA) $(srcdir)/gssapi.h $(DESTDIR)$(KRB5_INCDIR)$(S)gssapi.h
++	$(INSTALL_DATA) $(srcdir)/krad.h $(DESTDIR)$(KRB5_INCDIR)/krad.h
+ 
+ depend:: krb5/krb5.h $(BUILT_HEADERS)
+diff --git a/src/include/krad.h b/src/include/krad.h
+new file mode 100644
+index 0000000..e6d5766
+--- /dev/null
++++ b/src/include/krad.h
+@@ -0,0 +1,267 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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.
++ */
++
++/*
++ * This API is not considered as stable as the main krb5 API.
++ *
++ * - We may make arbitrary incompatible changes between feature releases
++ *   (e.g. from 1.12 to 1.13).
++ * - We will make some effort to avoid making incompatible changes for
++ *   bugfix releases, but will make them if necessary.
++ */
++
++#ifndef KRAD_H_
++#define KRAD_H_
++
++#include <krb5.h>
++#include <verto.h>
++#include <stddef.h>
++#include <stdio.h>
++
++#define KRAD_PACKET_SIZE_MAX 4096
++
++#define KRAD_SERVICE_TYPE_LOGIN 1
++#define KRAD_SERVICE_TYPE_FRAMED 2
++#define KRAD_SERVICE_TYPE_CALLBACK_LOGIN 3
++#define KRAD_SERVICE_TYPE_CALLBACK_FRAMED 4
++#define KRAD_SERVICE_TYPE_OUTBOUND 5
++#define KRAD_SERVICE_TYPE_ADMINISTRATIVE 6
++#define KRAD_SERVICE_TYPE_NAS_PROMPT 7
++#define KRAD_SERVICE_TYPE_AUTHENTICATE_ONLY 8
++#define KRAD_SERVICE_TYPE_CALLBACK_NAS_PROMPT 9
++#define KRAD_SERVICE_TYPE_CALL_CHECK 10
++#define KRAD_SERVICE_TYPE_CALLBACK_ADMINISTRATIVE 11
++
++typedef struct krad_attrset_st krad_attrset;
++typedef struct krad_packet_st krad_packet;
++typedef struct krad_client_st krad_client;
++typedef unsigned char krad_code;
++typedef unsigned char krad_attr;
++
++/* Called when a response is received or the request times out. */
++typedef void
++(*krad_cb)(krb5_error_code retval, const krad_packet *request,
++           const krad_packet *response, void *data);
++
++/*
++ * Called to iterate over a set of requests.  Either the callback will be
++ * called until it returns NULL, or it will be called with cancel = TRUE to
++ * terminate in the middle of an iteration.
++ */
++typedef const krad_packet *
++(*krad_packet_iter_cb)(void *data, krb5_boolean cancel);
++
++/*
++ * Code
++ */
++
++/* Convert a code name to its number. Only works for codes defined
++ * by RFC 2875 or 2882. Returns 0 if the name was not found. */
++krad_code
++krad_code_name2num(const char *name);
++
++/* Convert a code number to its name. Only works for attributes defined
++ * by RFC 2865 or 2882. Returns NULL if the name was not found. */
++const char *
++krad_code_num2name(krad_code code);
++
++/*
++ * Attribute
++ */
++
++/* Convert an attribute name to its number. Only works for attributes defined
++ * by RFC 2865. Returns 0 if the name was not found. */
++krad_attr
++krad_attr_name2num(const char *name);
++
++/* Convert an attribute number to its name. Only works for attributes defined
++ * by RFC 2865. Returns NULL if the name was not found. */
++const char *
++krad_attr_num2name(krad_attr type);
++
++/*
++ * Attribute set
++ */
++
++/* Create a new attribute set. */
++krb5_error_code
++krad_attrset_new(krb5_context ctx, krad_attrset **set);
++
++/* Create a deep copy of an attribute set. */
++krb5_error_code
++krad_attrset_copy(const krad_attrset *set, krad_attrset **copy);
++
++/* Free an attribute set. */
++void
++krad_attrset_free(krad_attrset *set);
++
++/* Add an attribute to a set. */
++krb5_error_code
++krad_attrset_add(krad_attrset *set, krad_attr type, const krb5_data *data);
++
++/* Add a four-octet unsigned number attribute to the given set. */
++krb5_error_code
++krad_attrset_add_number(krad_attrset *set, krad_attr type, krb5_ui_4 num);
++
++/* Delete the specified attribute. */
++void
++krad_attrset_del(krad_attrset *set, krad_attr type, size_t indx);
++
++/* Get the specified attribute. */
++const krb5_data *
++krad_attrset_get(const krad_attrset *set, krad_attr type, size_t indx);
++
++/*
++ * Packet
++ */
++
++/* Determine the bytes needed from the socket to get the whole packet.  Don't
++ * cache the return value as it can change! Returns -1 on EBADMSG. */
++ssize_t
++krad_packet_bytes_needed(const krb5_data *buffer);
++
++/* Free a packet. */
++void
++krad_packet_free(krad_packet *pkt);
++
++/*
++ * Create a new request packet.
++ *
++ * This function takes the attributes specified in set and converts them into a
++ * radius packet. The packet will have a randomized id. If cb is not NULL, it
++ * will be called passing data as the argument to iterate over a set of
++ * outstanding requests. In this case, the id will be both random and unique
++ * across the set of requests.
++ */
++krb5_error_code
++krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
++                        const krad_attrset *set, krad_packet_iter_cb cb,
++                        void *data, krad_packet **request);
++
++/*
++ * Create a new response packet.
++ *
++ * This function is similar to krad_packet_new_requst() except that it crafts a
++ * packet in response to a request packet. This new packet will borrow values
++ * from the request such as the id and the authenticator.
++ */
++krb5_error_code
++krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
++                         const krad_attrset *set, const krad_packet *request,
++                         krad_packet **response);
++
++/*
++ * Decode a request radius packet from krb5_data.
++ *
++ * The resulting decoded packet will be a request packet stored in *reqpkt.
++ *
++ * If cb is NULL, *duppkt will always be NULL.
++ *
++ * If cb is not NULL, it will be called (with the data argument) to iterate
++ * over a set of requests currently being processed. In this case, if the
++ * packet is a duplicate of an already received request, the original request
++ * will be set in *duppkt.
++ */
++krb5_error_code
++krad_packet_decode_request(krb5_context ctx, const char *secret,
++                           const krb5_data *buffer, krad_packet_iter_cb cb,
++                           void *data, const krad_packet **duppkt,
++                           krad_packet **reqpkt);
++
++/*
++ * Decode a response radius packet from krb5_data.
++ *
++ * The resulting decoded packet will be a response packet stored in *rsppkt.
++ *
++ * If cb is NULL, *reqpkt will always be NULL.
++ *
++ * If cb is not NULL, it will be called (with the data argument) to iterate
++ * over a set of requests awaiting responses. In this case, if the response
++ * packet matches one of these requests, the original request will be set in
++ * *reqpkt.
++ */
++krb5_error_code
++krad_packet_decode_response(krb5_context ctx, const char *secret,
++                            const krb5_data *buffer, krad_packet_iter_cb cb,
++                            void *data, const krad_packet **reqpkt,
++                            krad_packet **rsppkt);
++
++/* Encode packet. */
++const krb5_data *
++krad_packet_encode(const krad_packet *pkt);
++
++/* Get the code for the given packet. */
++krad_code
++krad_packet_get_code(const krad_packet *pkt);
++
++/* Get the specified attribute. */
++const krb5_data *
++krad_packet_get_attr(const krad_packet *pkt, krad_attr type, size_t indx);
++
++/*
++ * Client
++ */
++
++/* Create a new client. */
++krb5_error_code
++krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **client);
++
++/* Free the client. */
++void
++krad_client_free(krad_client *client);
++
++/*
++ * Send a request to a radius server.
++ *
++ * The remote host may be specified by one of the following formats:
++ *  - /path/to/unix.socket
++ *  - IPv4
++ *  - IPv4:port
++ *  - IPv4:service
++ *  - [IPv6]
++ *  - [IPv6]:port
++ *  - [IPv6]:service
++ *  - hostname
++ *  - hostname:port
++ *  - hostname:service
++ *
++ * The timeout parameter (milliseconds) is the total timeout across all remote
++ * hosts (when DNS returns multiple entries) and all retries.
++ *
++ * The cb function will be called with the data argument when either a response
++ * is received or the request times out on all possible remote hosts.
++ *
++ * If the remote host is a unix domain socket, retries is ignored due to
++ * guaranteed delivery.
++ */
++krb5_error_code
++krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,
++                 const char *remote, const char *secret, int timeout,
++                 size_t retries, krad_cb cb, void *data);
++
++#endif /* KRAD_H_ */
+diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in
+index 485db40..4dde514 100644
+--- a/src/lib/Makefile.in
++++ b/src/lib/Makefile.in
+@@ -1,5 +1,5 @@
+ mydir=lib
+-SUBDIRS=crypto krb5 gssapi rpc kdb kadm5 apputils
++SUBDIRS=crypto krb5 gssapi rpc kdb kadm5 apputils krad
+ WINSUBDIRS=crypto krb5 gssapi
+ BUILDTOP=$(REL)..
+ 
+diff --git a/src/lib/krad/Makefile.in b/src/lib/krad/Makefile.in
+new file mode 100644
+index 0000000..75431a0
+--- /dev/null
++++ b/src/lib/krad/Makefile.in
+@@ -0,0 +1,74 @@
++mydir=lib$(S)krad
++BUILDTOP=$(REL)..$(S)..
++RELDIR=krad
++
++RUN_SETUP=@KRB5_RUN_ENV@
++PROG_LIBPATH=-L$(TOPLIBD)
++PROG_RPATH=$(KRB5_LIBDIR)
++
++SHLIB_EXPLIBS=$(KRB5_BASE_LIBS) $(VERTO_LIBS)
++SHLIB_EXPDEPLIBS=$(KRB5_BASE_DEPLIBS) $(VERTO_DEPLIB)
++SHLIB_DIRS=-L$(TOPLIBD)
++SHLIB_RDIRS=$(KRB5_LIBDIR)
++
++LIBBASE=krad
++LIBMAJOR=0
++LIBMINOR=0
++
++STLIBOBJS=attr.o attrset.o client.o code.o packet.o remote.o
++LIBOBJS=$(OUTPRE)attr.$(OBJEXT) \
++	$(OUTPRE)attrset.$(OBJEXT) \
++	$(OUTPRE)client.$(OBJEXT) \
++	$(OUTPRE)code.$(OBJEXT) \
++	$(OUTPRE)packet.$(OBJEXT) \
++	$(OUTPRE)remote.$(OBJEXT)
++SRCS=attr.c attrset.c client.c code.c packet.c remote.c \
++	t_attr.c t_attrset.c t_client.c t_code.c t_packet.c t_remote.c t_test.c
++
++STOBJLISTS=OBJS.ST
++
++all-unix:: all-liblinks
++install-unix:: install-libs
++
++clean-unix:: clean-liblinks clean-libs clean-libobjs
++
++check-unix:: t_attr t_attrset t_code t_packet t_remote t_client
++	$(RUN_SETUP) $(VALGRIND) ./t_attr
++	$(RUN_SETUP) $(VALGRIND) ./t_attrset
++	$(RUN_SETUP) $(VALGRIND) ./t_code
++	$(RUN_SETUP) $(VALGRIND) ./t_packet $(PYTHON) $(srcdir)/t_daemon.py
++	$(RUN_SETUP) $(VALGRIND) ./t_remote $(PYTHON) $(srcdir)/t_daemon.py
++	$(RUN_SETUP) $(VALGRIND) ./t_client $(PYTHON) $(srcdir)/t_daemon.py
++
++TESTDEPS=t_test.o $(KRB5_BASE_DEPLIBS)
++TESTLIBS=t_test.o $(KRB5_BASE_LIBS)
++
++T_ATTR_OBJS=attr.o t_attr.o
++t_attr: $(T_ATTR_OBJS) $(TESTDEPS)
++	$(CC_LINK) -o $@ $(T_ATTR_OBJS) $(TESTLIBS)
++
++T_ATTRSET_OBJS=attr.o attrset.o t_attrset.o
++t_attrset: $(T_ATTRSET_OBJS) $(TESTDEPS)
++	$(CC_LINK) -o $@ $(T_ATTRSET_OBJS) $(TESTLIBS)
++
++T_CODE_OBJS=code.o t_code.o
++t_code: $(T_CODE_OBJS) $(TESTDEPS)
++	$(CC_LINK) -o $@ $(T_CODE_OBJS) $(TESTLIBS)
++
++T_PACKET_OBJS=attr.o attrset.o code.o packet.o t_packet.o
++t_packet: $(T_PACKET_OBJS) $(TESTDEPS)
++	$(CC_LINK) -o $@ $(T_PACKET_OBJS) $(TESTLIBS)
++
++T_REMOTE_OBJS=attr.o attrset.o code.o packet.o remote.o t_remote.o
++t_remote: $(T_REMOTE_OBJS) $(TESTDEPS) $(VERTO_DEPLIB)
++	$(CC_LINK) -o $@ $(T_REMOTE_OBJS) $(TESTLIBS) $(VERTO_LIBS)
++
++T_CLIENT_OBJS=attr.o attrset.o code.o packet.o remote.o client.o t_client.o
++t_client: $(T_CLIENT_OBJS) $(TESTDEPS) $(VERTO_DEPLIB)
++	$(CC_LINK) -o $@ $(T_CLIENT_OBJS) $(TESTLIBS) $(VERTO_LIBS)
++
++clean-unix:: clean-libobjs
++	$(RM) *.o t_attr t_attrset t_code t_packet t_remote t_client
++
++@lib_frag@
++@libobj_frag@
+diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c
+new file mode 100644
+index 0000000..9c13d9d
+--- /dev/null
++++ b/src/lib/krad/attr.c
+@@ -0,0 +1,317 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/attr.c - RADIUS attribute functions for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "internal.h"
++
++#include <string.h>
++
++/* RFC 2865 */
++#define BLOCKSIZE 16
++
++typedef krb5_error_code
++(*attribute_transform_fn)(krb5_context ctx, const char *secret,
++                          const unsigned char *auth, const krb5_data *in,
++                          unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++
++typedef struct {
++    const char *name;
++    unsigned char minval;
++    unsigned char maxval;
++    attribute_transform_fn encode;
++    attribute_transform_fn decode;
++} attribute_record;
++
++static krb5_error_code
++user_password_encode(krb5_context ctx, const char *secret,
++                     const unsigned char *auth, const krb5_data *in,
++                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++
++static krb5_error_code
++user_password_decode(krb5_context ctx, const char *secret,
++                     const unsigned char *auth, const krb5_data *in,
++                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++
++static const attribute_record attributes[UCHAR_MAX] = {
++    {"User-Name", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"User-Password", 1, 128, user_password_encode, user_password_decode},
++    {"CHAP-Password", 17, 17, NULL, NULL},
++    {"NAS-IP-Address", 4, 4, NULL, NULL},
++    {"NAS-Port", 4, 4, NULL, NULL},
++    {"Service-Type", 4, 4, NULL, NULL},
++    {"Framed-Protocol", 4, 4, NULL, NULL},
++    {"Framed-IP-Address", 4, 4, NULL, NULL},
++    {"Framed-IP-Netmask", 4, 4, NULL, NULL},
++    {"Framed-Routing", 4, 4, NULL, NULL},
++    {"Filter-Id", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"Framed-MTU", 4, 4, NULL, NULL},
++    {"Framed-Compression", 4, 4, NULL, NULL},
++    {"Login-IP-Host", 4, 4, NULL, NULL},
++    {"Login-Service", 4, 4, NULL, NULL},
++    {"Login-TCP-Port", 4, 4, NULL, NULL},
++    {NULL, 0, 0, NULL, NULL}, /* Unassigned */
++    {"Reply-Message", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"Callback-Number", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"Callback-Id", 1, MAX_ATTRSIZE, NULL, NULL},
++    {NULL, 0, 0, NULL, NULL}, /* Unassigned */
++    {"Framed-Route", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"Framed-IPX-Network", 4, 4, NULL, NULL},
++    {"State", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"Class", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"Vendor-Specific", 5, MAX_ATTRSIZE, NULL, NULL},
++    {"Session-Timeout", 4, 4, NULL, NULL},
++    {"Idle-Timeout", 4, 4, NULL, NULL},
++    {"Termination-Action", 4, 4, NULL, NULL},
++    {"Called-Station-Id", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"Calling-Station-Id", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"NAS-Identifier", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"Proxy-State", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"Login-LAT-Service", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"Login-LAT-Node", 1, MAX_ATTRSIZE, NULL, NULL},
++    {"Login-LAT-Group", 32, 32, NULL, NULL},
++    {"Framed-AppleTalk-Link", 4, 4, NULL, NULL},
++    {"Framed-AppleTalk-Network", 4, 4, NULL, NULL},
++    {"Framed-AppleTalk-Zone", 1, MAX_ATTRSIZE, NULL, NULL},
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++    {"CHAP-Challenge", 5, MAX_ATTRSIZE, NULL, NULL},
++    {"NAS-Port-Type", 4, 4, NULL, NULL},
++    {"Port-Limit", 4, 4, NULL, NULL},
++    {"Login-LAT-Port", 1, MAX_ATTRSIZE, NULL, NULL},
++};
++
++/* Encode User-Password attribute. */
++static krb5_error_code
++user_password_encode(krb5_context ctx, const char *secret,
++                     const unsigned char *auth, const krb5_data *in,
++                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
++{
++    const unsigned char *indx;
++    krb5_error_code retval;
++    unsigned int seclen;
++    krb5_checksum sum;
++    size_t blck, len, i;
++    krb5_data tmp;
++
++    /* Copy the input buffer to the (zero-padded) output buffer. */
++    len = (in->length + BLOCKSIZE - 1) / BLOCKSIZE * BLOCKSIZE;
++    if (len > MAX_ATTRSIZE)
++        return ENOBUFS;
++    memset(outbuf, 0, len);
++    memcpy(outbuf, in->data, in->length);
++
++    /* Create our temporary space for processing each block. */
++    seclen = strlen(secret);
++    retval = alloc_data(&tmp, seclen + BLOCKSIZE);
++    if (retval != 0)
++        return retval;
++
++    memcpy(tmp.data, secret, seclen);
++    for (blck = 0, indx = auth; blck * BLOCKSIZE < len; blck++) {
++        memcpy(tmp.data + seclen, indx, BLOCKSIZE);
++
++        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
++                                      &sum);
++        if (retval != 0) {
++            zap(tmp.data, tmp.length);
++            zap(outbuf, len);
++            krb5_free_data_contents(ctx, &tmp);
++            return retval;
++        }
++
++        for (i = 0; i < BLOCKSIZE; i++)
++            outbuf[blck * BLOCKSIZE + i] ^= sum.contents[i];
++        krb5_free_checksum_contents(ctx, &sum);
++
++        indx = &outbuf[blck * BLOCKSIZE];
++    }
++
++    zap(tmp.data, tmp.length);
++    krb5_free_data_contents(ctx, &tmp);
++    *outlen = len;
++    return 0;
++}
++
++/* Decode User-Password attribute. */
++static krb5_error_code
++user_password_decode(krb5_context ctx, const char *secret,
++                     const unsigned char *auth, const krb5_data *in,
++                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
++{
++    const unsigned char *indx;
++    krb5_error_code retval;
++    unsigned int seclen;
++    krb5_checksum sum;
++    ssize_t blck, i;
++    krb5_data tmp;
++
++    if (in->length % BLOCKSIZE != 0)
++        return EINVAL;
++    if (in->length > MAX_ATTRSIZE)
++        return ENOBUFS;
++
++    /* Create our temporary space for processing each block. */
++    seclen = strlen(secret);
++    retval = alloc_data(&tmp, seclen + BLOCKSIZE);
++    if (retval != 0)
++        return retval;
++
++    memcpy(tmp.data, secret, seclen);
++    for (blck = 0, indx = auth; blck * BLOCKSIZE < in->length; blck++) {
++        memcpy(tmp.data + seclen, indx, BLOCKSIZE);
++
++        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
++                                      &tmp, &sum);
++        if (retval != 0) {
++            zap(tmp.data, tmp.length);
++            zap(outbuf, in->length);
++            krb5_free_data_contents(ctx, &tmp);
++            return retval;
++        }
++
++        for (i = 0; i < BLOCKSIZE; i++) {
++            outbuf[blck * BLOCKSIZE + i] = in->data[blck * BLOCKSIZE + i] ^
++                sum.contents[i];
++        }
++        krb5_free_checksum_contents(ctx, &sum);
++
++        indx = (const unsigned char *)&in->data[blck * BLOCKSIZE];
++    }
++
++    /* Strip off trailing NULL bytes. */
++    *outlen = in->length;
++    while (*outlen > 0 && outbuf[*outlen - 1] == '\0')
++        (*outlen)--;
++
++    krb5_free_data_contents(ctx, &tmp);
++    return 0;
++}
++
++krb5_error_code
++kr_attr_valid(krad_attr type, const krb5_data *data)
++{
++    const attribute_record *ar;
++
++    if (type == 0)
++        return EINVAL;
++
++    ar = &attributes[type - 1];
++    return (data->length >= ar->minval && data->length <= ar->maxval) ? 0 :
++        EMSGSIZE;
++}
++
++krb5_error_code
++kr_attr_encode(krb5_context ctx, const char *secret,
++               const unsigned char *auth, krad_attr type,
++               const krb5_data *in, unsigned char outbuf[MAX_ATTRSIZE],
++               size_t *outlen)
++{
++    krb5_error_code retval;
++
++    retval = kr_attr_valid(type, in);
++    if (retval != 0)
++        return retval;
++
++    if (attributes[type - 1].encode == NULL) {
++        if (in->length > MAX_ATTRSIZE)
++            return ENOBUFS;
++
++        *outlen = in->length;
++        memcpy(outbuf, in->data, in->length);
++        return 0;
++    }
++
++    return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen);
++}
++
++krb5_error_code
++kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
++               krad_attr type, const krb5_data *in,
++               unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
++{
++    krb5_error_code retval;
++
++    retval = kr_attr_valid(type, in);
++    if (retval != 0)
++        return retval;
++
++    if (attributes[type - 1].encode == NULL) {
++        if (in->length > MAX_ATTRSIZE)
++            return ENOBUFS;
++
++        *outlen = in->length;
++        memcpy(outbuf, in->data, in->length);
++        return 0;
++    }
++
++    return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen);
++}
++
++krad_attr
++krad_attr_name2num(const char *name)
++{
++    unsigned char i;
++
++    for (i = 0; i < UCHAR_MAX; i++) {
++        if (attributes[i].name == NULL)
++            continue;
++
++        if (strcmp(attributes[i].name, name) == 0)
++            return i + 1;
++    }
++
++    return 0;
++}
++
++const char *
++krad_attr_num2name(krad_attr type)
++{
++    if (type == 0)
++        return NULL;
++
++    return attributes[type - 1].name;
++}
+diff --git a/src/lib/krad/attrset.c b/src/lib/krad/attrset.c
+new file mode 100644
+index 0000000..fbd0621
+--- /dev/null
++++ b/src/lib/krad/attrset.c
+@@ -0,0 +1,244 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/attrset.c - RADIUS attribute set functions for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 <k5-queue.h>
++#include "internal.h"
++
++#include <string.h>
++
++TAILQ_HEAD(attr_head, attr_st);
++
++typedef struct attr_st attr;
++struct attr_st {
++    TAILQ_ENTRY(attr_st) list;
++    krad_attr type;
++    krb5_data attr;
++    char buffer[MAX_ATTRSIZE];
++};
++
++struct krad_attrset_st {
++    krb5_context ctx;
++    struct attr_head list;
++};
++
++krb5_error_code
++krad_attrset_new(krb5_context ctx, krad_attrset **set)
++{
++    krad_attrset *tmp;
++
++    tmp = calloc(1, sizeof(krad_attrset));
++    if (tmp == NULL)
++        return ENOMEM;
++    tmp->ctx = ctx;
++    TAILQ_INIT(&tmp->list);
++
++    *set = tmp;
++    return 0;
++}
++
++void
++krad_attrset_free(krad_attrset *set)
++{
++    attr *a;
++
++    if (set == NULL)
++        return;
++
++    while (!TAILQ_EMPTY(&set->list)) {
++        a = TAILQ_FIRST(&set->list);
++        TAILQ_REMOVE(&set->list, a, list);
++        zap(a->buffer, sizeof(a->buffer));
++        free(a);
++    }
++
++    free(set);
++}
++
++krb5_error_code
++krad_attrset_add(krad_attrset *set, krad_attr type, const krb5_data *data)
++{
++    krb5_error_code retval;
++    attr *tmp;
++
++    retval = kr_attr_valid(type, data);
++    if (retval != 0)
++        return retval;
++
++    tmp = calloc(1, sizeof(attr));
++    if (tmp == NULL)
++        return ENOMEM;
++
++    tmp->type = type;
++    tmp->attr = make_data(tmp->buffer, data->length);
++    memcpy(tmp->attr.data, data->data, data->length);
++
++    TAILQ_INSERT_TAIL(&set->list, tmp, list);
++    return 0;
++}
++
++krb5_error_code
++krad_attrset_add_number(krad_attrset *set, krad_attr type, krb5_ui_4 num)
++{
++    krb5_data data;
++
++    num = htonl(num);
++    data = make_data(&num, sizeof(num));
++    return krad_attrset_add(set, type, &data);
++}
++
++void
++krad_attrset_del(krad_attrset *set, krad_attr type, size_t indx)
++{
++    attr *a;
++
++    TAILQ_FOREACH(a, &set->list, list) {
++        if (a->type == type && indx-- == 0) {
++            TAILQ_REMOVE(&set->list, a, list);
++            zap(a->buffer, sizeof(a->buffer));
++            free(a);
++            return;
++        }
++    }
++}
++
++const krb5_data *
++krad_attrset_get(const krad_attrset *set, krad_attr type, size_t indx)
++{
++    attr *a;
++
++    TAILQ_FOREACH(a, &set->list, list) {
++        if (a->type == type && indx-- == 0)
++            return &a->attr;
++    }
++
++    return NULL;
++}
++
++krb5_error_code
++krad_attrset_copy(const krad_attrset *set, krad_attrset **copy)
++{
++    krb5_error_code retval;
++    krad_attrset *tmp;
++    attr *a;
++
++    retval = krad_attrset_new(set->ctx, &tmp);
++    if (retval != 0)
++        return retval;
++
++    TAILQ_FOREACH(a, &set->list, list) {
++        retval = krad_attrset_add(tmp, a->type, &a->attr);
++        if (retval != 0) {
++            krad_attrset_free(tmp);
++            return retval;
++        }
++    }
++
++    *copy = tmp;
++    return 0;
++}
++
++krb5_error_code
++kr_attrset_encode(const krad_attrset *set, const char *secret,
++                  const unsigned char *auth,
++                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen)
++{
++    unsigned char buffer[MAX_ATTRSIZE];
++    krb5_error_code retval;
++    size_t i = 0, attrlen;
++    attr *a;
++
++    if (set == NULL) {
++        *outlen = 0;
++        return 0;
++    }
++
++    TAILQ_FOREACH(a, &set->list, list) {
++        retval = kr_attr_encode(set->ctx, secret, auth, a->type, &a->attr,
++                                buffer, &attrlen);
++        if (retval != 0)
++            return retval;
++
++        if (i + attrlen + 2 > MAX_ATTRSETSIZE)
++            return EMSGSIZE;
++
++        outbuf[i++] = a->type;
++        outbuf[i++] = attrlen + 2;
++        memcpy(&outbuf[i], buffer, attrlen);
++        i += attrlen;
++    }
++
++    *outlen = i;
++    return 0;
++}
++
++krb5_error_code
++kr_attrset_decode(krb5_context ctx, const krb5_data *in, const char *secret,
++                  const unsigned char *auth, krad_attrset **set_out)
++{
++    unsigned char buffer[MAX_ATTRSIZE];
++    krb5_data tmp;
++    krb5_error_code retval;
++    krad_attr type;
++    krad_attrset *set;
++    size_t i, len;
++
++    *set_out = NULL;
++
++    retval = krad_attrset_new(ctx, &set);
++    if (retval != 0)
++        return retval;
++
++    for (i = 0; i + 2 < in->length; ) {
++        type = in->data[i++];
++        tmp = make_data(&in->data[i + 1], in->data[i] - 2);
++        i += tmp.length + 1;
++
++        retval = (in->length < i) ? EBADMSG : 0;
++        if (retval != 0)
++            goto cleanup;
++
++        retval = kr_attr_decode(ctx, secret, auth, type, &tmp, buffer, &len);
++        if (retval != 0)
++            goto cleanup;
++
++        tmp = make_data(buffer, len);
++        retval = krad_attrset_add(set, type, &tmp);
++        if (retval != 0)
++            goto cleanup;
++    }
++
++    *set_out = set;
++    set = NULL;
++
++cleanup:
++    zap(buffer, sizeof(buffer));
++    krad_attrset_free(set);
++    return retval;
++}
+diff --git a/src/lib/krad/client.c b/src/lib/krad/client.c
+new file mode 100644
+index 0000000..0c37680
+--- /dev/null
++++ b/src/lib/krad/client.c
+@@ -0,0 +1,335 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/client.c - Client request code for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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-queue.h>
++#include "internal.h"
++
++#include <string.h>
++#include <sys/un.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <limits.h>
++
++LIST_HEAD(server_head, server_st);
++
++typedef struct remote_state_st remote_state;
++typedef struct request_st request;
++typedef struct server_st server;
++
++struct remote_state_st {
++    const krad_packet *packet;
++    krad_remote *remote;
++};
++
++struct request_st {
++    krad_client *rc;
++
++    krad_code code;
++    krad_attrset *attrs;
++    int timeout;
++    size_t retries;
++    krad_cb cb;
++    void *data;
++
++    remote_state *remotes;
++    ssize_t current;
++    ssize_t count;
++};
++
++struct server_st {
++    krad_remote *serv;
++    time_t last;
++    LIST_ENTRY(server_st) list;
++};
++
++struct krad_client_st {
++    krb5_context kctx;
++    verto_ctx *vctx;
++    struct server_head servers;
++};
++
++/* Return either a pre-existing server that matches the address info and the
++ * secret, or create a new one. */
++static krb5_error_code
++get_server(krad_client *rc, const struct addrinfo *ai, const char *secret,
++           krad_remote **out)
++{
++    krb5_error_code retval;
++    time_t currtime;
++    server *srv;
++
++    if (time(&currtime) == (time_t)-1)
++        return errno;
++
++    LIST_FOREACH(srv, &rc->servers, list) {
++        if (kr_remote_equals(srv->serv, ai, secret)) {
++            srv->last = currtime;
++            *out = srv->serv;
++            return 0;
++        }
++    }
++
++    srv = calloc(1, sizeof(server));
++    if (srv == NULL)
++        return ENOMEM;
++    srv->last = currtime;
++
++    retval = kr_remote_new(rc->kctx, rc->vctx, ai, secret, &srv->serv);
++    if (retval != 0) {
++        free(srv);
++        return retval;
++    }
++
++    LIST_INSERT_HEAD(&rc->servers, srv, list);
++    *out = srv->serv;
++    return 0;
++}
++
++/* Free a request. */
++static void
++request_free(request *req)
++{
++    krad_attrset_free(req->attrs);
++    free(req->remotes);
++    free(req);
++}
++
++/* Create a request. */
++static krb5_error_code
++request_new(krad_client *rc, krad_code code, const krad_attrset *attrs,
++            const struct addrinfo *ai, const char *secret, int timeout,
++            size_t retries, krad_cb cb, void *data, request **req)
++{
++    const struct addrinfo *tmp;
++    krb5_error_code retval;
++    request *rqst;
++    size_t i;
++
++    if (ai == NULL)
++        return EINVAL;
++
++    rqst = calloc(1, sizeof(request));
++    if (rqst == NULL)
++        return ENOMEM;
++
++    for (tmp = ai; tmp != NULL; tmp = tmp->ai_next)
++        rqst->count++;
++
++    rqst->rc = rc;
++    rqst->code = code;
++    rqst->cb = cb;
++    rqst->data = data;
++    rqst->timeout = timeout / rqst->count;
++    rqst->retries = retries;
++
++    retval = krad_attrset_copy(attrs, &rqst->attrs);
++    if (retval != 0) {
++        request_free(rqst);
++        return retval;
++    }
++
++    rqst->remotes = calloc(rqst->count + 1, sizeof(remote_state));
++    if (rqst->remotes == NULL) {
++        request_free(rqst);
++        return ENOMEM;
++    }
++
++    i = 0;
++    for (tmp = ai; tmp != NULL; tmp = tmp->ai_next) {
++        retval = get_server(rc, tmp, secret, &rqst->remotes[i++].remote);
++        if (retval != 0) {
++            request_free(rqst);
++            return retval;
++        }
++    }
++
++    *req = rqst;
++    return 0;
++}
++
++/* Close remotes that haven't been used in a while. */
++static void
++age(struct server_head *head, time_t currtime)
++{
++    server *srv, *tmp;
++
++    LIST_FOREACH_SAFE(srv, head, list, tmp) {
++        if (currtime == (time_t)-1 || currtime - srv->last > 60 * 60) {
++            LIST_REMOVE(srv, list);
++            kr_remote_free(srv->serv);
++            free(srv);
++        }
++    }
++}
++
++/* Handle a response from a server (or related errors). */
++static void
++on_response(krb5_error_code retval, const krad_packet *reqp,
++            const krad_packet *rspp, void *data)
++{
++    request *req = data;
++    time_t currtime;
++    size_t i;
++
++    /* Do nothing if we are already completed. */
++    if (req->count < 0)
++        return;
++
++    /* If we have timed out and have more remotes to try, do so. */
++    if (retval == ETIMEDOUT && req->remotes[++req->current].remote != NULL) {
++        retval = kr_remote_send(req->remotes[req->current].remote, req->code,
++                                req->attrs, on_response, req, req->timeout,
++                                req->retries,
++                                &req->remotes[req->current].packet);
++        if (retval == 0)
++            return;
++    }
++
++    /* Mark the request as complete. */
++    req->count = -1;
++
++    /* Inform the callback. */
++    req->cb(retval, reqp, rspp, req->data);
++
++    /* Cancel the outstanding packets. */
++    for (i = 0; req->remotes[i].remote != NULL; i++)
++        kr_remote_cancel(req->remotes[i].remote, req->remotes[i].packet);
++
++    /* Age out servers that haven't been used in a while. */
++    if (time(&currtime) != (time_t)-1)
++        age(&req->rc->servers, currtime);
++
++    request_free(req);
++}
++
++krb5_error_code
++krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **out)
++{
++    krad_client *tmp;
++
++    tmp = calloc(1, sizeof(krad_client));
++    if (tmp == NULL)
++        return ENOMEM;
++
++    tmp->kctx = kctx;
++    tmp->vctx = vctx;
++
++    *out = tmp;
++    return 0;
++}
++
++void
++krad_client_free(krad_client *rc)
++{
++    if (rc == NULL)
++        return;
++
++    age(&rc->servers, -1);
++    free(rc);
++}
++
++static krb5_error_code
++resolve_remote(const char *remote, struct addrinfo **ai)
++{
++    const char *svc = "radius";
++    krb5_error_code retval;
++    struct addrinfo hints;
++    char *sep, *srv;
++
++    /* Isolate the port number if it exists. */
++    srv = strdup(remote);
++    if (srv == NULL)
++        return ENOMEM;
++
++    if (srv[0] == '[') {
++        /* IPv6 */
++        sep = strrchr(srv, ']');
++        if (sep != NULL && sep[1] == ':') {
++            sep[1] = '\0';
++            svc = &sep[2];
++        }
++    } else {
++        /* IPv4 or DNS */
++        sep = strrchr(srv, ':');
++        if (sep != NULL && sep[1] != '\0') {
++            sep[0] = '\0';
++            svc = &sep[1];
++        }
++    }
++
++    /* Perform the lookup. */
++    memset(&hints, 0, sizeof(hints));
++    hints.ai_socktype = SOCK_DGRAM;
++    retval = gai_error_code(getaddrinfo(srv, svc, &hints, ai));
++    free(srv);
++    return retval;
++}
++
++krb5_error_code
++krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,
++                 const char *remote, const char *secret, int timeout,
++                 size_t retries, krad_cb cb, void *data)
++{
++    struct addrinfo usock, *ai = NULL;
++    krb5_error_code retval;
++    struct sockaddr_un ua;
++    request *req;
++
++    if (remote[0] == '/') {
++        ua.sun_family = AF_UNIX;
++        snprintf(ua.sun_path, sizeof(ua.sun_path), "%s", remote);
++        memset(&usock, 0, sizeof(usock));
++        usock.ai_family = AF_UNIX;
++        usock.ai_socktype = SOCK_STREAM;
++        usock.ai_addr = (struct sockaddr *)&ua;
++        usock.ai_addrlen = sizeof(ua);
++
++        retval = request_new(rc, code, attrs, &usock, secret, timeout, retries,
++                             cb, data, &req);
++    } else {
++        retval = resolve_remote(remote, &ai);
++        if (retval == 0) {
++            retval = request_new(rc, code, attrs, ai, secret, timeout, retries,
++                                 cb, data, &req);
++            freeaddrinfo(ai);
++        }
++    }
++    if (retval != 0)
++        return retval;
++
++    retval = kr_remote_send(req->remotes[req->current].remote, req->code,
++                            req->attrs, on_response, req, req->timeout,
++                            req->retries, &req->remotes[req->current].packet);
++    if (retval != 0) {
++        request_free(req);
++        return retval;
++    }
++
++    return 0;
++}
+diff --git a/src/lib/krad/code.c b/src/lib/krad/code.c
+new file mode 100644
+index 0000000..16871bb
+--- /dev/null
++++ b/src/lib/krad/code.c
+@@ -0,0 +1,111 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/code.c - RADIUS code name table for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "internal.h"
++
++#include <string.h>
++
++static const char *codes[UCHAR_MAX] = {
++    "Access-Request",
++    "Access-Accept",
++    "Access-Reject",
++    "Accounting-Request",
++    "Accounting-Response",
++    "Accounting-Status",
++    "Password-Request",
++    "Password-Ack",
++    "Password-Reject",
++    "Accounting-Message",
++    "Access-Challenge",
++    "Status-Server",
++    "Status-Client",
++    NULL,
++    NULL,
++    NULL,
++    NULL,
++    NULL,
++    NULL,
++    NULL,
++    "Resource-Free-Request",
++    "Resource-Free-Response",
++    "Resource-Query-Request",
++    "Resource-Query-Response",
++    "Alternate-Resource-Reclaim-Request",
++    "NAS-Reboot-Request",
++    "NAS-Reboot-Response",
++    NULL,
++    "Next-Passcode",
++    "New-Pin",
++    "Terminate-Session",
++    "Password-Expired",
++    "Event-Request",
++    "Event-Response",
++    NULL,
++    NULL,
++    NULL,
++    NULL,
++    NULL,
++    "Disconnect-Request",
++    "Disconnect-Ack",
++    "Disconnect-Nak",
++    "Change-Filters-Request",
++    "Change-Filters-Ack",
++    "Change-Filters-Nak",
++    NULL,
++    NULL,
++    NULL,
++    NULL,
++    "IP-Address-Allocate",
++    "IP-Address-Release",
++};
++
++krad_code
++krad_code_name2num(const char *name)
++{
++    unsigned char i;
++
++    for (i = 0; i < UCHAR_MAX; i++) {
++        if (codes[i] == NULL)
++            continue;
++
++        if (strcmp(codes[i], name) == 0)
++            return ++i;
++    }
++
++    return 0;
++}
++
++const char *
++krad_code_num2name(krad_code code)
++{
++    if (code == 0)
++        return NULL;
++
++    return codes[code - 1];
++}
+diff --git a/src/lib/krad/deps b/src/lib/krad/deps
+new file mode 100644
+index 0000000..8171f94
+--- /dev/null
++++ b/src/lib/krad/deps
+@@ -0,0 +1,156 @@
++#
++# Generated makefile dependencies follow.
++#
++attr.so attr.po $(OUTPRE)attr.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_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/krad.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 attr.c internal.h
++attrset.so attrset.po $(OUTPRE)attrset.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_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-queue.h \
++  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++  $(top_srcdir)/include/krad.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 \
++  attrset.c internal.h
++client.so client.po $(OUTPRE)client.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_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-queue.h \
++  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++  $(top_srcdir)/include/krad.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 \
++  client.c internal.h
++code.so code.po $(OUTPRE)code.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_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/krad.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 code.c internal.h
++packet.so packet.po $(OUTPRE)packet.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_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/krad.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 internal.h packet.c
++remote.so remote.po $(OUTPRE)remote.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_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-queue.h \
++  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++  $(top_srcdir)/include/krad.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 \
++  internal.h remote.c
++t_attr.so t_attr.po $(OUTPRE)t_attr.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_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/krad.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 internal.h t_attr.c \
++  t_test.h
++t_attrset.so t_attrset.po $(OUTPRE)t_attrset.$(OBJEXT): \
++  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
++  $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
++  $(COM_ERR_DEPS) $(VERTO_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/krad.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 \
++  internal.h t_attrset.c t_test.h
++t_client.so t_client.po $(OUTPRE)t_client.$(OBJEXT): \
++  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
++  $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
++  $(COM_ERR_DEPS) $(VERTO_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/krad.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 \
++  internal.h t_client.c t_daemon.h t_test.h
++t_code.so t_code.po $(OUTPRE)t_code.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_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/krad.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 internal.h t_code.c \
++  t_test.h
++t_packet.so t_packet.po $(OUTPRE)t_packet.$(OBJEXT): \
++  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
++  $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
++  $(COM_ERR_DEPS) $(VERTO_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/krad.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 \
++  internal.h t_daemon.h t_packet.c t_test.h
++t_remote.so t_remote.po $(OUTPRE)t_remote.$(OBJEXT): \
++  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
++  $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
++  $(COM_ERR_DEPS) $(VERTO_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/krad.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 \
++  internal.h t_daemon.h t_remote.c t_test.h
++t_test.so t_test.po $(OUTPRE)t_test.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_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/krad.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 internal.h t_test.c \
++  t_test.h
+diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
+new file mode 100644
+index 0000000..996a893
+--- /dev/null
++++ b/src/lib/krad/internal.h
+@@ -0,0 +1,155 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/internal.h - Internal declarations for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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.
++ */
++
++#ifndef INTERNAL_H_
++#define INTERNAL_H_
++
++#include <k5-int.h>
++#include "krad.h"
++
++#include <errno.h>
++
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netdb.h>
++
++#ifndef UCHAR_MAX
++#define UCHAR_MAX 255
++#endif
++
++/* RFC 2865 */
++#define MAX_ATTRSIZE (UCHAR_MAX - 2)
++#define MAX_ATTRSETSIZE (KRAD_PACKET_SIZE_MAX - 20)
++
++typedef struct krad_remote_st krad_remote;
++
++/* Validate constraints of an attribute. */
++krb5_error_code
++kr_attr_valid(krad_attr type, const krb5_data *data);
++
++/* Encode an attribute. */
++krb5_error_code
++kr_attr_encode(krb5_context ctx, const char *secret, const unsigned char *auth,
++               krad_attr type, const krb5_data *in,
++               unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++
++/* Decode an attribute. */
++krb5_error_code
++kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
++               krad_attr type, const krb5_data *in,
++               unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++
++/* Encode the attributes into the buffer. */
++krb5_error_code
++kr_attrset_encode(const krad_attrset *set, const char *secret,
++                  const unsigned char *auth,
++                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen);
++
++/* Decode attributes from a buffer. */
++krb5_error_code
++kr_attrset_decode(krb5_context ctx, const krb5_data *in, const char *secret,
++                  const unsigned char *auth, krad_attrset **set);
++
++/* Create a new remote object which manages a socket and the state of
++ * outstanding requests. */
++krb5_error_code
++kr_remote_new(krb5_context kctx, verto_ctx *vctx, const struct addrinfo *info,
++              const char *secret, krad_remote **rr);
++
++/* Free a remote object. */
++void
++kr_remote_free(krad_remote *rr);
++
++/*
++ * Send the packet to the remote. The cb will be called when a response is
++ * received, the request times out, the request is canceled or an error occurs.
++ *
++ * The timeout parameter is the total timeout across all retries in
++ * milliseconds.
++ *
++ * If the cb is called with a retval of ETIMEDOUT it indicates that the alloted
++ * time has elapsed. However, in the case of a timeout, we continue to listen
++ * for the packet until krad_remote_cancel() is called or a response is
++ * received. This means that cb will always be called twice in the event of a
++ * timeout. This permits you to pursue other remotes while still listening for
++ * a response from the first one.
++ */
++krb5_error_code
++kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
++               krad_cb cb, void *data, int timeout, size_t retries,
++               const krad_packet **pkt);
++
++/* Remove packet from the queue of requests awaiting responses. */
++void
++kr_remote_cancel(krad_remote *rr, const krad_packet *pkt);
++
++/* Determine if this remote object refers to the remote resource identified
++ * by the addrinfo struct and the secret. */
++krb5_boolean
++kr_remote_equals(const krad_remote *rr, const struct addrinfo *info,
++                 const char *secret);
++
++/* Adapted from lib/krb5/os/sendto_kdc.c. */
++static inline krb5_error_code
++gai_error_code(int err)
++{
++    switch (err) {
++    case 0:
++        return 0;
++    case EAI_BADFLAGS:
++    case EAI_FAMILY:
++    case EAI_SOCKTYPE:
++    case EAI_SERVICE:
++#ifdef EAI_ADDRFAMILY
++    case EAI_ADDRFAMILY:
++#endif
++        return EINVAL;
++    case EAI_AGAIN:
++        return EAGAIN;
++    case EAI_MEMORY:
++        return ENOMEM;
++#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
++    case EAI_NODATA:
++#endif
++    case EAI_NONAME:
++        return EADDRNOTAVAIL;
++#ifdef EAI_OVERFLOW
++    case EAI_OVERFLOW:
++        return EOVERFLOW;
++#endif
++#ifdef EAI_SYSTEM
++    case EAI_SYSTEM:
++        return errno;
++#endif
++    default:
++        return EINVAL;
++    }
++}
++
++#endif /* INTERNAL_H_ */
+diff --git a/src/lib/krad/libkrad.exports b/src/lib/krad/libkrad.exports
+new file mode 100644
+index 0000000..fe3f159
+--- /dev/null
++++ b/src/lib/krad/libkrad.exports
+@@ -0,0 +1,23 @@
++krad_code_name2num
++krad_code_num2name
++krad_attr_name2num
++krad_attr_num2name
++krad_attrset_new
++krad_attrset_copy
++krad_attrset_free
++krad_attrset_add
++krad_attrset_add_number
++krad_attrset_del
++krad_attrset_get
++krad_packet_bytes_needed
++krad_packet_free
++krad_packet_new_request
++krad_packet_new_response
++krad_packet_decode_request
++krad_packet_decode_response
++krad_packet_encode
++krad_packet_get_code
++krad_packet_get_attr
++krad_client_new
++krad_client_free
++krad_client_send
+diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c
+new file mode 100644
+index 0000000..6e6b27e
+--- /dev/null
++++ b/src/lib/krad/packet.c
+@@ -0,0 +1,470 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/packet.c - Packet functions for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "internal.h"
++
++#include <string.h>
++
++#include <arpa/inet.h>
++
++typedef unsigned char uchar;
++
++/* RFC 2865 */
++#define OFFSET_CODE 0
++#define OFFSET_ID 1
++#define OFFSET_LENGTH 2
++#define OFFSET_AUTH 4
++#define OFFSET_ATTR 20
++#define AUTH_FIELD_SIZE (OFFSET_ATTR - OFFSET_AUTH)
++
++#define offset(d, o) (&(d)->data[o])
++#define pkt_code_get(p) (*(krad_code *)offset(&(p)->pkt, OFFSET_CODE))
++#define pkt_code_set(p, v) (*(krad_code *)offset(&(p)->pkt, OFFSET_CODE)) = v
++#define pkt_id_get(p) (*(uchar *)offset(&(p)->pkt, OFFSET_ID))
++#define pkt_id_set(p, v) (*(uchar *)offset(&(p)->pkt, OFFSET_ID)) = v
++#define pkt_len_get(p)  load_16_be(offset(&(p)->pkt, OFFSET_LENGTH))
++#define pkt_len_set(p, v)  store_16_be(v, offset(&(p)->pkt, OFFSET_LENGTH))
++#define pkt_auth(p) ((uchar *)offset(&(p)->pkt, OFFSET_AUTH))
++#define pkt_attr(p) ((unsigned char *)offset(&(p)->pkt, OFFSET_ATTR))
++
++struct krad_packet_st {
++    char buffer[KRAD_PACKET_SIZE_MAX];
++    krad_attrset *attrset;
++    krb5_data pkt;
++};
++
++typedef struct {
++    uchar x[(UCHAR_MAX + 1) / 8];
++} idmap;
++
++/* Ensure the map is empty. */
++static inline void
++idmap_init(idmap *map)
++{
++    memset(map, 0, sizeof(*map));
++}
++
++/* Set an id as already allocated. */
++static inline void
++idmap_set(idmap *map, uchar id)
++{
++    map->x[id / 8] |= 1 << (id % 8);
++}
++
++/* Determine whether or not an id is used. */
++static inline krb5_boolean
++idmap_isset(const idmap *map, uchar id)
++{
++    return (map->x[id / 8] & (1 << (id % 8))) != 0;
++}
++
++/* Find an unused id starting the search at the value specified in id.
++ * NOTE: For optimal security, the initial value of id should be random. */
++static inline krb5_error_code
++idmap_find(const idmap *map, uchar *id)
++{
++    krb5_int16 i;
++
++    for (i = *id; i >= 0 && i <= UCHAR_MAX; *id % 2 == 0 ? i++ : i--) {
++        if (!idmap_isset(map, i))
++            goto success;
++    }
++
++    for (i = *id; i >= 0 && i <= UCHAR_MAX; *id % 2 == 1 ? i++ : i--) {
++        if (!idmap_isset(map, i))
++            goto success;
++    }
++
++    return ERANGE;
++
++success:
++    *id = i;
++    return 0;
++}
++
++/* Generate size bytes of random data into the buffer. */
++static inline krb5_error_code
++randomize(krb5_context ctx, void *buffer, unsigned int size)
++{
++    krb5_data rdata = make_data(buffer, size);
++    return krb5_c_random_make_octets(ctx, &rdata);
++}
++
++/* Generate a radius packet id. */
++static krb5_error_code
++id_generate(krb5_context ctx, krad_packet_iter_cb cb, void *data, uchar *id)
++{
++    krb5_error_code retval;
++    const krad_packet *tmp;
++    idmap used;
++    uchar i;
++
++    retval = randomize(ctx, &i, sizeof(i));
++    if (retval != 0) {
++        if (cb != NULL)
++            (*cb)(data, TRUE);
++        return retval;
++    }
++
++    if (cb != NULL) {
++        idmap_init(&used);
++        for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE))
++            idmap_set(&used, tmp->pkt.data[1]);
++
++        retval = idmap_find(&used, &i);
++        if (retval != 0)
++            return retval;
++    }
++
++    *id = i;
++    return 0;
++}
++
++/* Generate a random authenticator field. */
++static krb5_error_code
++auth_generate_random(krb5_context ctx, uchar *rauth)
++{
++    krb5_ui_4 trunctime;
++    time_t currtime;
++
++    /* Get the least-significant four bytes of the current time. */
++    currtime = time(NULL);
++    if (currtime == (time_t)-1)
++        return errno;
++    trunctime = (krb5_ui_4)currtime;
++    memcpy(rauth, &trunctime, sizeof(trunctime));
++
++    /* Randomize the rest of the buffer. */
++    return randomize(ctx, rauth + sizeof(trunctime),
++                     AUTH_FIELD_SIZE - sizeof(trunctime));
++}
++
++/* Generate a response authenticator field. */
++static krb5_error_code
++auth_generate_response(krb5_context ctx, const char *secret,
++                       const krad_packet *response, const uchar *auth,
++                       uchar *rauth)
++{
++    krb5_error_code retval;
++    krb5_checksum hash;
++    krb5_data data;
++
++    /* Allocate the temporary buffer. */
++    retval = alloc_data(&data, response->pkt.length + strlen(secret));
++    if (retval != 0)
++        return retval;
++
++    /* Encoded RADIUS packet with the request's
++     * authenticator and the secret at the end. */
++    memcpy(data.data, response->pkt.data, response->pkt.length);
++    memcpy(data.data + OFFSET_AUTH, auth, AUTH_FIELD_SIZE);
++    memcpy(data.data + response->pkt.length, secret, strlen(secret));
++
++    /* Hash it. */
++    retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
++                                  &hash);
++    free(data.data);
++    if (retval != 0)
++        return retval;
++
++    memcpy(rauth, hash.contents, AUTH_FIELD_SIZE);
++    krb5_free_checksum_contents(ctx, &hash);
++    return 0;
++}
++
++/* Create a new packet. */
++static krad_packet *
++packet_new()
++{
++    krad_packet *pkt;
++
++    pkt = calloc(1, sizeof(krad_packet));
++    if (pkt == NULL)
++        return NULL;
++    pkt->pkt = make_data(pkt->buffer, sizeof(pkt->buffer));
++
++    return pkt;
++}
++
++/* Set the attrset object by decoding the packet. */
++static krb5_error_code
++packet_set_attrset(krb5_context ctx, const char *secret, krad_packet *pkt)
++{
++    krb5_data tmp;
++
++    tmp = make_data(pkt_attr(pkt), pkt->pkt.length - OFFSET_ATTR);
++    return kr_attrset_decode(ctx, &tmp, secret, pkt_auth(pkt), &pkt->attrset);
++}
++
++ssize_t
++krad_packet_bytes_needed(const krb5_data *buffer)
++{
++    size_t len;
++
++    if (buffer->length < OFFSET_AUTH)
++        return OFFSET_AUTH - buffer->length;
++
++    len = load_16_be(offset(buffer, OFFSET_LENGTH));
++    if (len > KRAD_PACKET_SIZE_MAX)
++        return -1;
++
++    return buffer->length > len ? 0 : len - buffer->length;
++}
++
++void
++krad_packet_free(krad_packet *pkt)
++{
++    if (pkt)
++        krad_attrset_free(pkt->attrset);
++    free(pkt);
++}
++
++/* Create a new request packet. */
++krb5_error_code
++krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
++                        const krad_attrset *set, krad_packet_iter_cb cb,
++                        void *data, krad_packet **request)
++{
++    krb5_error_code retval;
++    krad_packet *pkt;
++    uchar id;
++    size_t attrset_len;
++
++    pkt = packet_new();
++    if (pkt == NULL) {
++        if (cb != NULL)
++            (*cb)(data, TRUE);
++        return ENOMEM;
++    }
++
++    /* Generate the ID. */
++    retval = id_generate(ctx, cb, data, &id);
++    if (retval != 0)
++        goto error;
++    pkt_id_set(pkt, id);
++
++    /* Generate the authenticator. */
++    retval = auth_generate_random(ctx, pkt_auth(pkt));
++    if (retval != 0)
++        goto error;
++
++    /* Encode the attributes. */
++    retval = kr_attrset_encode(set, secret, pkt_auth(pkt), pkt_attr(pkt),
++                               &attrset_len);
++    if (retval != 0)
++        goto error;
++
++    /* Set the code, ID and length. */
++    pkt->pkt.length = attrset_len + OFFSET_ATTR;
++    pkt_code_set(pkt, code);
++    pkt_len_set(pkt, pkt->pkt.length);
++
++    /* Copy the attrset for future use. */
++    retval = packet_set_attrset(ctx, secret, pkt);
++    if (retval != 0)
++        goto error;
++
++    *request = pkt;
++    return 0;
++
++error:
++    free(pkt);
++    return retval;
++}
++
++/* Create a new request packet. */
++krb5_error_code
++krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
++                         const krad_attrset *set, const krad_packet *request,
++                         krad_packet **response)
++{
++    krb5_error_code retval;
++    krad_packet *pkt;
++    size_t attrset_len;
++
++    pkt = packet_new();
++    if (pkt == NULL)
++        return ENOMEM;
++
++    /* Encode the attributes. */
++    retval = kr_attrset_encode(set, secret, pkt_auth(request), pkt_attr(pkt),
++                               &attrset_len);
++    if (retval != 0)
++        goto error;
++
++    /* Set the code, ID and length. */
++    pkt->pkt.length = attrset_len + OFFSET_ATTR;
++    pkt_code_set(pkt, code);
++    pkt_id_set(pkt, pkt_id_get(request));
++    pkt_len_set(pkt, pkt->pkt.length);
++
++    /* Generate the authenticator. */
++    retval = auth_generate_response(ctx, secret, pkt, pkt_auth(request),
++                                    pkt_auth(pkt));
++    if (retval != 0)
++        goto error;
++
++    /* Copy the attrset for future use. */
++    retval = packet_set_attrset(ctx, secret, pkt);
++    if (retval != 0)
++        goto error;
++
++    *response = pkt;
++    return 0;
++
++error:
++    free(pkt);
++    return retval;
++}
++
++/* Decode a packet. */
++static krb5_error_code
++decode_packet(krb5_context ctx, const char *secret, const krb5_data *buffer,
++              krad_packet **pkt)
++{
++    krb5_error_code retval;
++    krad_packet *tmp;
++    krb5_ui_2 len;
++
++    tmp = packet_new();
++    if (tmp == NULL) {
++        retval = ENOMEM;
++        goto error;
++    }
++
++    /* Ensure a proper message length. */
++    retval = buffer->length < OFFSET_ATTR ? EMSGSIZE : 0;
++    if (retval != 0)
++        goto error;
++    len = load_16_be(offset(buffer, OFFSET_LENGTH));
++    retval = len < OFFSET_ATTR ? EBADMSG : 0;
++    if (retval != 0)
++        goto error;
++    retval = (len > buffer->length || len > tmp->pkt.length) ? EBADMSG : 0;
++    if (retval != 0)
++        goto error;
++
++    /* Copy over the buffer. */
++    tmp->pkt.length = len;
++    memcpy(tmp->pkt.data, buffer->data, len);
++
++    /* Parse the packet to ensure it is well-formed. */
++    retval = packet_set_attrset(ctx, secret, tmp);
++    if (retval != 0)
++        goto error;
++
++    *pkt = tmp;
++    return 0;
++
++error:
++    krad_packet_free(tmp);
++    return retval;
++}
++
++krb5_error_code
++krad_packet_decode_request(krb5_context ctx, const char *secret,
++                           const krb5_data *buffer, krad_packet_iter_cb cb,
++                           void *data, const krad_packet **duppkt,
++                           krad_packet **reqpkt)
++{
++    const krad_packet *tmp = NULL;
++    krb5_error_code retval;
++
++    retval = decode_packet(ctx, secret, buffer, reqpkt);
++    if (cb != NULL && retval == 0) {
++        for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
++            if (pkt_id_get(*reqpkt) == pkt_id_get(tmp))
++                break;
++        }
++    }
++
++    if (cb != NULL && (retval != 0 || tmp != NULL))
++        (*cb)(data, TRUE);
++
++    *duppkt = tmp;
++    return retval;
++}
++
++krb5_error_code
++krad_packet_decode_response(krb5_context ctx, const char *secret,
++                            const krb5_data *buffer, krad_packet_iter_cb cb,
++                            void *data, const krad_packet **reqpkt,
++                            krad_packet **rsppkt)
++{
++    uchar auth[AUTH_FIELD_SIZE];
++    const krad_packet *tmp = NULL;
++    krb5_error_code retval;
++
++    retval = decode_packet(ctx, secret, buffer, rsppkt);
++    if (cb != NULL && retval == 0) {
++        for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
++            if (pkt_id_get(*rsppkt) != pkt_id_get(tmp))
++                continue;
++
++            /* Response */
++            retval = auth_generate_response(ctx, secret, *rsppkt,
++                                            pkt_auth(tmp), auth);
++            if (retval != 0) {
++                krad_packet_free(*rsppkt);
++                break;
++            }
++
++            /* If the authenticator matches, then the response is valid. */
++            if (memcmp(pkt_auth(*rsppkt), auth, sizeof(auth)) == 0)
++                break;
++        }
++    }
++
++    if (cb != NULL && (retval != 0 || tmp != NULL))
++        (*cb)(data, TRUE);
++
++    *reqpkt = tmp;
++    return retval;
++}
++
++const krb5_data *
++krad_packet_encode(const krad_packet *pkt)
++{
++    return &pkt->pkt;
++}
++
++krad_code
++krad_packet_get_code(const krad_packet *pkt)
++{
++    if (pkt == NULL)
++        return 0;
++
++    return pkt_code_get(pkt);
++}
++
++const krb5_data *
++krad_packet_get_attr(const krad_packet *pkt, krad_attr type, size_t indx)
++{
++    return krad_attrset_get(pkt->attrset, type, indx);
++}
+diff --git a/src/lib/krad/remote.c b/src/lib/krad/remote.c
+new file mode 100644
+index 0000000..74d9865
+--- /dev/null
++++ b/src/lib/krad/remote.c
+@@ -0,0 +1,519 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/remote.c - Protocol code for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 <k5-queue.h>
++#include "internal.h"
++
++#include <string.h>
++#include <unistd.h>
++
++#include <sys/un.h>
++
++#define FLAGS_READ (VERTO_EV_FLAG_PERSIST | VERTO_EV_FLAG_IO_CLOSE_FD | \
++                    VERTO_EV_FLAG_IO_ERROR | VERTO_EV_FLAG_IO_READ)
++#define FLAGS_WRITE (FLAGS_READ | VERTO_EV_FLAG_IO_WRITE)
++
++TAILQ_HEAD(request_head, request_st);
++
++typedef struct request_st request;
++struct request_st {
++    TAILQ_ENTRY(request_st) list;
++    krad_remote *rr;
++    krad_packet *request;
++    krad_cb cb;
++    void *data;
++    verto_ev *timer;
++    int timeout;
++    size_t retries;
++    size_t sent;
++};
++
++struct krad_remote_st {
++    krb5_context kctx;
++    verto_ctx *vctx;
++    verto_ev *io;
++    char *secret;
++    struct addrinfo *info;
++    struct request_head list;
++    char buffer_[KRAD_PACKET_SIZE_MAX];
++    krb5_data buffer;
++};
++
++static void
++on_io(verto_ctx *ctx, verto_ev *ev);
++
++/* Iterate over the set of outstanding packets. */
++static const krad_packet *
++iterator(request **out)
++{
++    request *tmp = *out;
++
++    if (tmp == NULL)
++        return NULL;
++
++    *out = TAILQ_NEXT(tmp, list);
++    return tmp->request;
++}
++
++/* Create a new request. */
++static krb5_error_code
++request_new(krad_remote *rr, krad_packet *rqst, int timeout, size_t retries,
++            krad_cb cb, void *data, request **out)
++{
++    request *tmp;
++
++    tmp = calloc(1, sizeof(request));
++    if (tmp == NULL)
++        return ENOMEM;
++
++    tmp->rr = rr;
++    tmp->request = rqst;
++    tmp->cb = cb;
++    tmp->data = data;
++    tmp->timeout = timeout;
++    tmp->retries = retries;
++
++    *out = tmp;
++    return 0;
++}
++
++/* Finish a request, calling the callback and freeing it. */
++static inline void
++request_finish(request *req, krb5_error_code retval,
++               const krad_packet *response)
++{
++    if (retval != ETIMEDOUT)
++        TAILQ_REMOVE(&req->rr->list, req, list);
++
++    req->cb(retval, req->request, response, req->data);
++
++    if (retval != ETIMEDOUT) {
++        krad_packet_free(req->request);
++        verto_del(req->timer);
++        free(req);
++    }
++}
++
++/* Handle when packets receive no response within their alloted time. */
++static void
++on_timeout(verto_ctx *ctx, verto_ev *ev)
++{
++    request *req = verto_get_private(ev);
++
++    req->timer = NULL;          /* Void the timer event. */
++
++    /* If we have more retries to perform, resend the packet. */
++    if (req->retries-- > 1) {
++        req->sent = 0;
++        verto_set_flags(req->rr->io, FLAGS_WRITE);
++        return;
++    }
++
++    request_finish(req, ETIMEDOUT, NULL);
++}
++
++/* Connect to the remote host. */
++static krb5_error_code
++remote_connect(krad_remote *rr)
++{
++    int i, sock = -1;
++    verto_ev *ev;
++
++    sock = socket(rr->info->ai_family, rr->info->ai_socktype,
++                  rr->info->ai_protocol);
++    if (sock < 0)
++        return errno;
++
++    i = connect(sock, rr->info->ai_addr, rr->info->ai_addrlen);
++    if (i < 0) {
++        i = errno;
++        close(sock);
++        return i;
++    }
++
++    ev = verto_add_io(rr->vctx, FLAGS_READ, on_io, sock);
++    if (ev == NULL) {
++        close(sock);
++        return ENOMEM;
++    }
++
++    rr->io = ev;
++    verto_set_private(rr->io, rr, NULL);
++    return 0;
++}
++
++/* Disconnect and reconnect to the remote host. */
++static krb5_error_code
++remote_reconnect(krad_remote *rr, int errnum)
++{
++    krb5_error_code retval;
++    const krb5_data *tmp;
++    request *r;
++
++    verto_del(rr->io);
++    rr->io = NULL;
++    retval = remote_connect(rr);
++    if (retval != 0)
++        return retval;
++
++    TAILQ_FOREACH(r, &rr->list, list) {
++        tmp = krad_packet_encode(r->request);
++
++        if (r->sent == tmp->length) {
++            /* Error out sent requests. */
++            request_finish(r, errnum, NULL);
++        } else {
++            /* Reset partially sent requests. */
++            r->sent = 0;
++        }
++    }
++
++    return 0;
++}
++
++/* Close the connection and call the callbacks of all oustanding requests. */
++static void
++remote_shutdown(krad_remote *rr, int errnum)
++{
++    verto_del(rr->io);
++    rr->io = NULL;
++    while (!TAILQ_EMPTY(&rr->list))
++        request_finish(TAILQ_FIRST(&rr->list), errnum, NULL);
++}
++
++/* Write data to the socket. */
++static void
++on_io_write(krad_remote *rr)
++{
++    const krb5_data *tmp;
++    request *r;
++    int i;
++
++    TAILQ_FOREACH(r, &rr->list, list) {
++        tmp = krad_packet_encode(r->request);
++
++        /* If the packet has already been sent, do nothing. */
++        if (r->sent == tmp->length)
++            continue;
++
++        /* Send the packet. */
++        i = sendto(verto_get_fd(rr->io), tmp->data + r->sent,
++                   tmp->length - r->sent, 0, NULL, 0);
++        if (i < 0) {
++            /* Should we try again? */
++            if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS ||
++                errno == EINTR)
++                return;
++
++            /* In this case, we need to re-connect. */
++            i = remote_reconnect(rr, errno);
++            if (i == 0)
++                return;
++
++            /* Do a full reset. */
++            remote_shutdown(rr, i);
++            return;
++        }
++
++        /* SOCK_STREAM permits partial writes. */
++        if (rr->info->ai_socktype == SOCK_STREAM)
++            r->sent += i;
++        else if (i == (int)tmp->length)
++            r->sent = i;
++
++        /* If the packet was completely sent, set a timeout. */
++        if (r->sent == tmp->length) {
++            verto_del(r->timer);
++            r->timer = verto_add_timeout(rr->vctx, VERTO_EV_FLAG_NONE,
++                                         on_timeout, r->timeout);
++            if (r->timer == NULL)
++                request_finish(r, ENOMEM, NULL);
++            else
++                verto_set_private(r->timer, r, NULL);
++        }
++
++        return;
++    }
++
++    verto_set_flags(rr->io, FLAGS_READ);
++}
++
++/* Read data from the socket. */
++static void
++on_io_read(krad_remote *rr)
++{
++    const krad_packet *req = NULL;
++    krad_packet *rsp = NULL;
++    krb5_error_code retval;
++    ssize_t pktlen;
++    request *tmp, *r;
++    int i;
++
++    pktlen = sizeof(rr->buffer_);
++    if (rr->info->ai_socktype == SOCK_STREAM) {
++        pktlen = krad_packet_bytes_needed(&rr->buffer);
++        if (pktlen < 0) {
++            retval = remote_reconnect(rr, EBADMSG);
++            if (retval != 0)
++                remote_shutdown(rr, retval);
++            return;
++        }
++    }
++
++    /* Read the packet. */
++    i = recv(verto_get_fd(rr->io), rr->buffer.data + rr->buffer.length,
++             pktlen - rr->buffer.length, 0);
++    if (i < 0) {
++        /* Should we try again? */
++        if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
++            return;
++
++        if (errno == ECONNREFUSED || errno == ECONNRESET ||
++            errno == ENOTCONN) {
++            /*
++             * When doing UDP against a local socket, the kernel will notify
++             * when the daemon closes. But not against remote sockets. We want
++             * to treat them both the same. Returning here will cause an
++             * eventual timeout.
++             */
++            if (rr->info->ai_socktype != SOCK_STREAM)
++                return;
++        }
++
++        /* In this case, we need to re-connect. */
++        i = remote_reconnect(rr, errno);
++        if (i == 0)
++           return;
++
++        /* Do a full reset. */
++        remote_shutdown(rr, i);
++        return;
++    }
++
++    /* If we have a partial read or just the header, try again. */
++    rr->buffer.length += i;
++    pktlen = krad_packet_bytes_needed(&rr->buffer);
++    if (rr->info->ai_socktype == SOCK_STREAM && pktlen > 0)
++        return;
++
++    /* Decode the packet. */
++    tmp = TAILQ_FIRST(&rr->list);
++    retval = krad_packet_decode_response(rr->kctx, rr->secret, &rr->buffer,
++                                         (krad_packet_iter_cb)iterator, &tmp,
++                                         &req, &rsp);
++    rr->buffer.length = 0;
++    if (retval != 0)
++        return;
++
++    /* Match the response with an outstanding request. */
++    if (req != NULL) {
++        TAILQ_FOREACH(r, &rr->list, list) {
++            if (r->request == req &&
++                r->sent == krad_packet_encode(req)->length) {
++                request_finish(r, 0, rsp);
++                break;
++            }
++        }
++    }
++
++    krad_packet_free(rsp);
++}
++
++/* Handle when IO is ready on the socket. */
++static void
++on_io(verto_ctx *ctx, verto_ev *ev)
++{
++    krad_remote *rr;
++
++    rr = verto_get_private(ev);
++
++    if (verto_get_fd_state(ev) & VERTO_EV_FLAG_IO_WRITE)
++        on_io_write(rr);
++    else
++        on_io_read(rr);
++}
++
++krb5_error_code
++kr_remote_new(krb5_context kctx, verto_ctx *vctx, const struct addrinfo *info,
++              const char *secret, krad_remote **rr)
++{
++    krb5_error_code retval = ENOMEM;
++    krad_remote *tmp = NULL;
++
++    tmp = calloc(1, sizeof(krad_remote));
++    if (tmp == NULL)
++        goto error;
++    tmp->kctx = kctx;
++    tmp->vctx = vctx;
++    tmp->buffer = make_data(tmp->buffer_, 0);
++    TAILQ_INIT(&tmp->list);
++
++    tmp->secret = strdup(secret);
++    if (tmp->secret == NULL)
++        goto error;
++
++    tmp->info = k5memdup(info, sizeof(*info), &retval);
++    if (tmp->info == NULL)
++        goto error;
++
++    tmp->info->ai_addr = k5memdup(info->ai_addr, info->ai_addrlen, &retval);
++    if (tmp->info == NULL)
++        goto error;
++    tmp->info->ai_next = NULL;
++    tmp->info->ai_canonname = NULL;
++
++    retval = remote_connect(tmp);
++    if (retval != 0)
++        goto error;
++
++    *rr = tmp;
++    return 0;
++
++error:
++    kr_remote_free(tmp);
++    return retval;
++}
++
++void
++kr_remote_free(krad_remote *rr)
++{
++    if (rr == NULL)
++        return;
++
++    while (!TAILQ_EMPTY(&rr->list))
++        request_finish(TAILQ_FIRST(&rr->list), ECANCELED, NULL);
++
++    free(rr->secret);
++    if (rr->info != NULL)
++        free(rr->info->ai_addr);
++    free(rr->info);
++    verto_del(rr->io);
++    free(rr);
++}
++
++krb5_error_code
++kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
++               krad_cb cb, void *data, int timeout, size_t retries,
++               const krad_packet **pkt)
++{
++    krad_packet *tmp = NULL;
++    krb5_error_code retval;
++    request *r;
++
++    r = TAILQ_FIRST(&rr->list);
++    retval = krad_packet_new_request(rr->kctx, rr->secret, code, attrs,
++                                     (krad_packet_iter_cb)iterator, &r, &tmp);
++    if (retval != 0)
++        goto error;
++
++    TAILQ_FOREACH(r, &rr->list, list) {
++        if (r->request == tmp) {
++            retval = EALREADY;
++            goto error;
++        }
++    }
++
++    if (rr->io == NULL) {
++        retval = remote_connect(rr);
++        if (retval != 0)
++            goto error;
++    }
++
++    if (rr->info->ai_socktype == SOCK_STREAM)
++        retries = 0;
++    timeout = timeout / (retries + 1);
++    retval = request_new(rr, tmp, timeout, retries, cb, data, &r);
++    if (retval != 0)
++        goto error;
++
++    if ((verto_get_flags(rr->io) & VERTO_EV_FLAG_IO_WRITE) == 0)
++        verto_set_flags(rr->io, FLAGS_WRITE);
++
++    TAILQ_INSERT_TAIL(&rr->list, r, list);
++    if (pkt != NULL)
++        *pkt = tmp;
++    return 0;
++
++error:
++    krad_packet_free(tmp);
++    return retval;
++}
++
++void
++kr_remote_cancel(krad_remote *rr, const krad_packet *pkt)
++{
++    request *r;
++
++    TAILQ_FOREACH(r, &rr->list, list) {
++        if (r->request == pkt) {
++            request_finish(r, ECANCELED, NULL);
++            return;
++        }
++    }
++}
++
++krb5_boolean
++kr_remote_equals(const krad_remote *rr, const struct addrinfo *info,
++                 const char *secret)
++{
++    struct sockaddr_un *a, *b;
++
++    if (strcmp(rr->secret, secret) != 0)
++        return FALSE;
++
++    if (info->ai_addrlen != rr->info->ai_addrlen)
++        return FALSE;
++
++    if (info->ai_family != rr->info->ai_family)
++        return FALSE;
++
++    if (info->ai_socktype != rr->info->ai_socktype)
++        return FALSE;
++
++    if (info->ai_protocol != rr->info->ai_protocol)
++        return FALSE;
++
++    if (info->ai_flags != rr->info->ai_flags)
++        return FALSE;
++
++    if (memcmp(rr->info->ai_addr, info->ai_addr, info->ai_addrlen) != 0) {
++        /* AF_UNIX fails the memcmp() test due to uninitialized bytes after the
++         * socket name. */
++        if (info->ai_family != AF_UNIX)
++            return FALSE;
++
++        a = (struct sockaddr_un *)info->ai_addr;
++        b = (struct sockaddr_un *)rr->info->ai_addr;
++        if (strncmp(a->sun_path, b->sun_path, sizeof(a->sun_path)) != 0)
++            return FALSE;
++    }
++
++    return TRUE;
++}
+diff --git a/src/lib/krad/t_attr.c b/src/lib/krad/t_attr.c
+new file mode 100644
+index 0000000..e80d77b
+--- /dev/null
++++ b/src/lib/krad/t_attr.c
+@@ -0,0 +1,89 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_attr.c - Attribute test program */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "t_test.h"
++
++const static char encoded[] = {
++    0xba, 0xfc, 0xed, 0x50, 0xe1, 0xeb, 0xa6, 0xc3,
++    0xc1, 0x75, 0x20, 0xe9, 0x10, 0xce, 0xc2, 0xcb
++};
++
++const static unsigned char auth[] = {
++    0xac, 0x9d, 0xc1, 0x62, 0x08, 0xc4, 0xc7, 0x8b,
++    0xa1, 0x2f, 0x25, 0x0a, 0xc4, 0x1d, 0x36, 0x41
++};
++
++int
++main()
++{
++    unsigned char outbuf[MAX_ATTRSETSIZE];
++    const char *decoded = "accept";
++    const char *secret = "foo";
++    krb5_error_code retval;
++    krb5_context ctx;
++    const char *tmp;
++    krb5_data in;
++    size_t len;
++
++    noerror(krb5_init_context(&ctx));
++
++    /* Make sure User-Name is 1. */
++    insist(krad_attr_name2num("User-Name") == 1);
++
++    /* Make sure 2 is User-Password. */
++    tmp = krad_attr_num2name(2);
++    insist(tmp != NULL);
++    insist(strcmp(tmp, "User-Password") == 0);
++
++    /* Test decoding. */
++    in = make_data((void *)encoded, sizeof(encoded));
++    noerror(kr_attr_decode(ctx, secret, auth,
++                           krad_attr_name2num("User-Password"),
++                           &in, outbuf, &len));
++    insist(len == strlen(decoded));
++    insist(memcmp(outbuf, decoded, len) == 0);
++
++    /* Test encoding. */
++    in = string2data((char *)decoded);
++    retval = kr_attr_encode(ctx, secret, auth,
++                            krad_attr_name2num("User-Password"),
++                            &in, outbuf, &len);
++    insist(retval == 0);
++    insist(len == sizeof(encoded));
++    insist(memcmp(outbuf, encoded, len) == 0);
++
++    /* Test constraint. */
++    in.length = 100;
++    insist(kr_attr_valid(krad_attr_name2num("User-Password"), &in) == 0);
++    in.length = 200;
++    insist(kr_attr_valid(krad_attr_name2num("User-Password"), &in) != 0);
++
++    krb5_free_context(ctx);
++    return 0;
++}
+diff --git a/src/lib/krad/t_attrset.c b/src/lib/krad/t_attrset.c
+new file mode 100644
+index 0000000..afae5e4
+--- /dev/null
++++ b/src/lib/krad/t_attrset.c
+@@ -0,0 +1,98 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_attrset.c - Attribute set test program */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "t_test.h"
++
++const static unsigned char auth[] = {
++    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++};
++
++const static char encpass[] = {
++    0x58, 0x8d, 0xff, 0xda, 0x37, 0xf9, 0xe4, 0xca,
++    0x19, 0xae, 0x49, 0xb7, 0x16, 0x6d, 0x58, 0x27
++};
++
++int
++main()
++{
++    unsigned char buffer[KRAD_PACKET_SIZE_MAX], encoded[MAX_ATTRSETSIZE];
++    const char *username = "testUser", *password = "accept";
++    const krb5_data *tmpp;
++    krad_attrset *set;
++    krb5_context ctx;
++    size_t len = 0, encode_len;
++    krb5_data tmp;
++
++    noerror(krb5_init_context(&ctx));
++    noerror(krad_attrset_new(ctx, &set));
++
++    /* Add username. */
++    tmp = string2data((char *)username);
++    noerror(krad_attrset_add(set, krad_attr_name2num("User-Name"), &tmp));
++
++    /* Add password. */
++    tmp = string2data((char *)password);
++    noerror(krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp));
++
++    /* Encode attrset. */
++    noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len));
++    krad_attrset_free(set);
++
++    /* Manually encode User-Name. */
++    encoded[len + 0] = krad_attr_name2num("User-Name");
++    encoded[len + 1] = strlen(username) + 2;
++    memcpy(encoded + len + 2, username, strlen(username));
++    len += encoded[len + 1];
++
++    /* Manually encode User-Password. */
++    encoded[len + 0] = krad_attr_name2num("User-Password");
++    encoded[len + 1] = sizeof(encpass) + 2;
++    memcpy(encoded + len + 2, encpass, sizeof(encpass));
++    len += encoded[len + 1];
++
++    /* Compare output. */
++    insist(len == encode_len);
++    insist(memcmp(encoded, buffer, len) == 0);
++
++    /* Decode output. */
++    tmp = make_data(buffer, len);
++    noerror(kr_attrset_decode(ctx, &tmp, "foo", auth, &set));
++
++    /* Test getting an attribute. */
++    tmp = string2data((char *)username);
++    tmpp = krad_attrset_get(set, krad_attr_name2num("User-Name"), 0);
++    insist(tmpp != NULL);
++    insist(tmpp->length == tmp.length);
++    insist(strncmp(tmpp->data, tmp.data, tmp.length) == 0);
++
++    krad_attrset_free(set);
++    krb5_free_context(ctx);
++    return 0;
++}
+diff --git a/src/lib/krad/t_client.c b/src/lib/krad/t_client.c
+new file mode 100644
+index 0000000..3d0fda9
+--- /dev/null
++++ b/src/lib/krad/t_client.c
+@@ -0,0 +1,126 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_client.c - Client request test program */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "t_daemon.h"
++
++#define EVENT_COUNT 4
++
++static struct
++{
++    int count;
++    struct event events[EVENT_COUNT];
++} record;
++
++static verto_ctx *vctx;
++
++static void
++callback(krb5_error_code retval, const krad_packet *request,
++         const krad_packet *response, void *data)
++{
++    struct event *evt;
++
++    evt = &record.events[record.count++];
++    evt->error = retval != 0;
++    if (evt->error)
++        evt->result.retval = retval;
++    else
++        evt->result.code = krad_packet_get_code(response);
++    verto_break(vctx);
++}
++
++int
++main(int argc, const char **argv)
++{
++    krad_attrset *attrs;
++    krad_client *rc;
++    krb5_context kctx;
++    krb5_data tmp;
++
++    if (!daemon_start(argc, argv)) {
++        fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
++        return 0;
++    }
++
++    noerror(krb5_init_context(&kctx));
++    vctx = verto_new(NULL, VERTO_EV_TYPE_IO | VERTO_EV_TYPE_TIMEOUT);
++    insist(vctx != NULL);
++    noerror(krad_client_new(kctx, vctx, &rc));
++
++    tmp = string2data("testUser");
++    noerror(krad_attrset_new(kctx, &attrs));
++    noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Name"), &tmp));
++
++    /* Test accept. */
++    tmp = string2data("accept");
++    noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Password"),
++                             &tmp));
++    noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
++                             "localhost", "foo", 1000, 3, callback, NULL));
++    verto_run(vctx);
++
++    /* Test reject. */
++    tmp = string2data("reject");
++    krad_attrset_del(attrs, krad_attr_name2num("User-Password"), 0);
++    noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Password"),
++                             &tmp));
++    noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
++                             "localhost", "foo", 1000, 3, callback, NULL));
++    verto_run(vctx);
++
++    /* Test timeout. */
++    daemon_stop();
++    noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
++                             "localhost", "foo", 1000, 3, callback, NULL));
++    verto_run(vctx);
++
++    /* Test outstanding packet freeing. */
++    noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
++                             "localhost", "foo", 1000, 3, callback, NULL));
++    krad_client_free(rc);
++    rc = NULL;
++
++    /* Verify the results. */
++    insist(record.count == EVENT_COUNT);
++    insist(record.events[0].error == FALSE);
++    insist(record.events[0].result.code ==
++           krad_code_name2num("Access-Accept"));
++    insist(record.events[1].error == FALSE);
++    insist(record.events[1].result.code ==
++           krad_code_name2num("Access-Reject"));
++    insist(record.events[2].error == TRUE);
++    insist(record.events[2].result.retval == ETIMEDOUT);
++    insist(record.events[3].error == TRUE);
++    insist(record.events[3].result.retval == ECANCELED);
++
++    krad_attrset_free(attrs);
++    krad_client_free(rc);
++    verto_free(vctx);
++    krb5_free_context(kctx);
++    return 0;
++}
+diff --git a/src/lib/krad/t_code.c b/src/lib/krad/t_code.c
+new file mode 100644
+index 0000000..b245a7e
+--- /dev/null
++++ b/src/lib/krad/t_code.c
+@@ -0,0 +1,54 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_code.c - RADIUS code table test program */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "t_test.h"
++
++int
++main()
++{
++    const char *tmp;
++
++    insist(krad_code_name2num("Access-Request") == 1);
++    insist(krad_code_name2num("Access-Accept") == 2);
++    insist(krad_code_name2num("Access-Reject") == 3);
++
++    tmp = krad_code_num2name(1);
++    insist(tmp != NULL);
++    insist(strcmp(tmp, "Access-Request") == 0);
++
++    tmp = krad_code_num2name(2);
++    insist(tmp != NULL);
++    insist(strcmp(tmp, "Access-Accept") == 0);
++
++    tmp = krad_code_num2name(3);
++    insist(tmp != NULL);
++    insist(strcmp(tmp, "Access-Reject") == 0);
++
++    return 0;
++}
+diff --git a/src/lib/krad/t_daemon.h b/src/lib/krad/t_daemon.h
+new file mode 100644
+index 0000000..7c345a6
+--- /dev/null
++++ b/src/lib/krad/t_daemon.h
+@@ -0,0 +1,92 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_daemon.h - Daemonization helper for RADIUS test programs */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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.
++ */
++
++#ifndef T_DAEMON_H_
++#define T_DAEMON_H_
++
++#include "t_test.h"
++#include <libgen.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++
++static pid_t daemon_pid;
++
++static void
++daemon_stop(void)
++{
++    if (daemon_pid == 0)
++        return;
++    kill(daemon_pid, SIGTERM);
++    waitpid(daemon_pid, NULL, 0);
++    daemon_pid = 0;
++}
++
++static krb5_boolean
++daemon_start(int argc, const char **argv)
++{
++    sigset_t set;
++    int sig;
++
++    if (argc != 3 || argv == NULL)
++        return FALSE;
++
++    if (daemon_pid != 0)
++        return TRUE;
++
++    if (sigemptyset(&set) != 0)
++        return FALSE;
++
++    if (sigaddset(&set, SIGUSR1) != 0)
++        return FALSE;
++
++    if (sigaddset(&set, SIGCHLD) != 0)
++        return FALSE;
++
++    if (sigprocmask(SIG_BLOCK, &set, NULL) != 0)
++        return FALSE;
++
++    daemon_pid = fork();
++    if (daemon_pid == 0) {
++        close(STDOUT_FILENO);
++        open("/dev/null", O_WRONLY);
++        exit(execlp(argv[1], argv[1], argv[2], NULL));
++    }
++
++    if (sigwait(&set, &sig) != 0 || sig == SIGCHLD) {
++        daemon_stop();
++        daemon_pid = 0;
++        return FALSE;
++    }
++
++    atexit(daemon_stop);
++    return TRUE;
++}
++
++#endif /* T_DAEMON_H_ */
+diff --git a/src/lib/krad/t_daemon.py b/src/lib/krad/t_daemon.py
+new file mode 100644
+index 0000000..d62bc29
+--- /dev/null
++++ b/src/lib/krad/t_daemon.py
+@@ -0,0 +1,76 @@
++#!/usr/bin/python
++#
++# Copyright 2013 Red Hat, Inc.  All rights reserved.
++#
++# Redistribution and use in source and binary forms, with or without
++# modification, are permitted provided that the following conditions are met:
++#
++#    1. Redistributions of source code must retain the above copyright
++#       notice, this list of conditions and the following disclaimer.
++#
++#    2. 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 OWNER
++# 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.
++
++import StringIO
++import os
++import sys
++import signal
++
++try:
++    from pyrad import dictionary, packet, server
++except ImportError:
++    sys.stdout.write("pyrad not found!\n")
++    sys.exit(0)
++
++# We could use a dictionary file, but since we need
++# such few attributes, we'll just include them here
++DICTIONARY = """
++ATTRIBUTE\tUser-Name\t1\tstring
++ATTRIBUTE\tUser-Password\t2\toctets
++ATTRIBUTE\tNAS-Identifier\t32\tstring
++"""
++
++class TestServer(server.Server):
++    def _HandleAuthPacket(self, pkt):
++        server.Server._HandleAuthPacket(self, pkt)
++
++        passwd = []
++  
++        print "Request: "
++        for key in pkt.keys():
++            if key == "User-Password":
++                passwd = map(pkt.PwDecrypt, pkt[key])
++                print "\t%s\t%s" % (key, passwd)
++            else:
++                print "\t%s\t%s" % (key, pkt[key])
++  
++        reply = self.CreateReplyPacket(pkt)
++        if passwd == ['accept']:
++            reply.code = packet.AccessAccept
++            print "Response: %s" % "Access-Accept"
++        else:
++            reply.code = packet.AccessReject
++            print "Response: %s" % "Access-Reject"
++        print
++        self.SendReplyPacket(pkt.fd, reply)
++
++srv = TestServer(addresses=["localhost"],
++                 hosts={"127.0.0.1":
++                        server.RemoteHost("127.0.0.1", "foo", "localhost")},
++                 dict=dictionary.Dictionary(StringIO.StringIO(DICTIONARY)))
++os.kill(os.getppid(), signal.SIGUSR1)
++srv.Run()
+diff --git a/src/lib/krad/t_packet.c b/src/lib/krad/t_packet.c
+new file mode 100644
+index 0000000..0a92e9c
+--- /dev/null
++++ b/src/lib/krad/t_packet.c
+@@ -0,0 +1,194 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_packet.c - RADIUS packet test program */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "t_daemon.h"
++
++#define ACCEPT_PACKET 0
++#define REJECT_PACKET 1
++
++static krad_packet *packets[3];
++
++static const krad_packet *
++iterator(void *data, krb5_boolean cancel)
++{
++    krad_packet *tmp;
++    int *i = data;
++
++    if (cancel || packets[*i] == NULL)
++        return NULL;
++
++    tmp = packets[*i];
++    *i += 1;
++    return tmp;
++}
++
++static krb5_error_code
++make_packet(krb5_context ctx, const krb5_data *username,
++            const krb5_data *password, krad_packet **pkt)
++{
++    krad_attrset *set = NULL;
++    krad_packet *tmp = NULL;
++    krb5_error_code retval;
++    const krb5_data *data;
++    int i = 0;
++
++    retval = krad_attrset_new(ctx, &set);
++    if (retval != 0)
++        goto out;
++
++    retval = krad_attrset_add(set, krad_attr_name2num("User-Name"), username);
++    if (retval != 0)
++        goto out;
++
++    retval = krad_attrset_add(set, krad_attr_name2num("User-Password"),
++                              password);
++    if (retval != 0)
++        goto out;
++
++    retval = krad_packet_new_request(ctx, "foo",
++                                     krad_code_name2num("Access-Request"),
++                                     set, iterator, &i, &tmp);
++    if (retval != 0)
++        goto out;
++
++    data = krad_packet_get_attr(tmp, krad_attr_name2num("User-Name"), 0);
++    if (data == NULL) {
++        retval = ENOENT;
++        goto out;
++    }
++
++    if (data->length != username->length ||
++        memcmp(data->data, username->data, data->length) != 0) {
++        retval = EINVAL;
++        goto out;
++    }
++
++    *pkt = tmp;
++    tmp = NULL;
++
++out:
++    krad_attrset_free(set);
++    krad_packet_free(tmp);
++    return retval;
++}
++
++static krb5_error_code
++do_auth(krb5_context ctx, struct addrinfo *ai, const char *secret,
++        const krad_packet *rqst, krb5_boolean *auth)
++{
++    const krad_packet *req = NULL;
++    char tmp[KRAD_PACKET_SIZE_MAX];
++    const krb5_data *request;
++    krad_packet *rsp = NULL;
++    krb5_error_code retval;
++    krb5_data response;
++    int sock = -1, i;
++
++    response = make_data(tmp, sizeof(tmp));
++
++    sock = socket(ai->ai_family, ai->ai_socktype, 0);
++    if (sock < 0) {
++        retval = errno;
++        goto out;
++    }
++
++    request = krad_packet_encode(rqst);
++    if (sendto(sock, request->data, request->length, 0, ai->ai_addr,
++               ai->ai_addrlen) < 0) {
++        retval = errno;
++        goto out;
++    }
++
++    i = recv(sock, response.data, sizeof(tmp), 0);
++    if (i < 0) {
++        retval = errno;
++        goto out;
++    }
++    response.length = i;
++
++    i = 0;
++    retval = krad_packet_decode_response(ctx, secret, &response, iterator, &i,
++                                         &req, &rsp);
++    if (retval != 0)
++        goto out;
++
++    if (req != rqst) {
++        retval = EBADMSG;
++        goto out;
++    }
++
++    *auth = krad_packet_get_code(rsp) == krad_code_name2num("Access-Accept");
++
++out:
++    krad_packet_free(rsp);
++    if (sock >= 0)
++        close(sock);
++    return retval;
++}
++
++int
++main(int argc, const char **argv)
++{
++    struct addrinfo *ai = NULL, hints;
++    krb5_data username, password;
++    krb5_boolean auth = FALSE;
++    krb5_context ctx;
++
++    username = string2data("testUser");
++
++    if (!daemon_start(argc, argv)) {
++        fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
++        return 0;
++    }
++
++    noerror(krb5_init_context(&ctx));
++
++    password = string2data("accept");
++    noerror(make_packet(ctx, &username, &password, &packets[ACCEPT_PACKET]));
++
++    password = string2data("reject");
++    noerror(make_packet(ctx, &username, &password, &packets[REJECT_PACKET]));
++
++    memset(&hints, 0, sizeof(hints));
++    hints.ai_family = AF_INET;
++    hints.ai_socktype = SOCK_DGRAM;
++    noerror(gai_error_code(getaddrinfo("127.0.0.1", "radius", &hints, &ai)));
++
++    noerror(do_auth(ctx, ai, "foo", packets[ACCEPT_PACKET], &auth));
++    insist(auth == TRUE);
++
++    noerror(do_auth(ctx, ai, "foo", packets[REJECT_PACKET], &auth));
++    insist(auth == FALSE);
++
++    krad_packet_free(packets[ACCEPT_PACKET]);
++    krad_packet_free(packets[REJECT_PACKET]);
++    krb5_free_context(ctx);
++    freeaddrinfo(ai);
++    return 0;
++}
+diff --git a/src/lib/krad/t_remote.c b/src/lib/krad/t_remote.c
+new file mode 100644
+index 0000000..a521ecb
+--- /dev/null
++++ b/src/lib/krad/t_remote.c
+@@ -0,0 +1,170 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_remote.c - Protocol test program */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "t_daemon.h"
++
++#define EVENT_COUNT 6
++
++static struct
++{
++    int count;
++    struct event events[EVENT_COUNT];
++} record;
++
++static krad_attrset *set;
++static krad_remote *rr;
++static verto_ctx *vctx;
++
++static void
++callback(krb5_error_code retval, const krad_packet *request,
++         const krad_packet *response, void *data)
++{
++    struct event *evt;
++
++    evt = &record.events[record.count++];
++    evt->error = retval != 0;
++    if (evt->error)
++        evt->result.retval = retval;
++    else
++        evt->result.code = krad_packet_get_code(response);
++    verto_break(vctx);
++}
++
++static void
++remote_new(krb5_context kctx, krad_remote **remote)
++{
++    struct addrinfo *ai = NULL, hints;
++
++    memset(&hints, 0, sizeof(hints));
++    hints.ai_family = AF_INET;
++    hints.ai_socktype = SOCK_DGRAM;
++    noerror(gai_error_code(getaddrinfo("127.0.0.1", "radius", &hints, &ai)));
++
++    noerror(kr_remote_new(kctx, vctx, ai, "foo", remote));
++    insist(kr_remote_equals(*remote, ai, "foo"));
++    freeaddrinfo(ai);
++}
++
++static krb5_error_code
++do_auth(const char *password, const krad_packet **pkt)
++{
++    const krad_packet *tmppkt;
++    krb5_error_code retval;
++    krb5_data tmp = string2data((char *)password);
++
++    retval = krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp);
++    if (retval != 0)
++        return retval;
++
++    retval = kr_remote_send(rr, krad_code_name2num("Access-Request"), set,
++                            callback, NULL, 1000, 3, &tmppkt);
++    krad_attrset_del(set, krad_attr_name2num("User-Password"), 0);
++    if (retval != 0)
++        return retval;
++
++    if (pkt != NULL)
++        *pkt = tmppkt;
++    return 0;
++}
++
++static void
++test_timeout(verto_ctx *ctx, verto_ev *ev)
++{
++    static const krad_packet *pkt;
++
++    noerror(do_auth("accept", &pkt));
++    kr_remote_cancel(rr, pkt);
++}
++
++int
++main(int argc, const char **argv)
++{
++    krb5_context kctx = NULL;
++    krb5_data tmp;
++
++    if (!daemon_start(argc, argv)) {
++        fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
++        return 0;
++    }
++
++    /* Initialize. */
++    noerror(krb5_init_context(&kctx));
++    vctx = verto_new(NULL, VERTO_EV_TYPE_IO | VERTO_EV_TYPE_TIMEOUT);
++    insist(vctx != NULL);
++    remote_new(kctx, &rr);
++
++    /* Create attribute set. */
++    noerror(krad_attrset_new(kctx, &set));
++    tmp = string2data("testUser");
++    noerror(krad_attrset_add(set, krad_attr_name2num("User-Name"), &tmp));
++
++    /* Send accept packet. */
++    noerror(do_auth("accept", NULL));
++    verto_run(vctx);
++
++    /* Send reject packet. */
++    noerror(do_auth("reject", NULL));
++    verto_run(vctx);
++
++    /* Send canceled packet. */
++    insist(verto_add_timeout(vctx, VERTO_EV_FLAG_NONE, test_timeout, 0) !=
++           NULL);
++    verto_run(vctx);
++
++    /* Test timeout. */
++    daemon_stop();
++    noerror(do_auth("accept", NULL));
++    verto_run(vctx);
++
++    /* Test outstanding packet freeing. */
++    noerror(do_auth("accept", NULL));
++    kr_remote_free(rr);
++    krad_attrset_free(set);
++
++    /* Verify the results. */
++    insist(record.count == EVENT_COUNT);
++    insist(record.events[0].error == FALSE);
++    insist(record.events[0].result.code ==
++           krad_code_name2num("Access-Accept"));
++    insist(record.events[1].error == FALSE);
++    insist(record.events[1].result.code ==
++           krad_code_name2num("Access-Reject"));
++    insist(record.events[2].error == TRUE);
++    insist(record.events[2].result.retval == ECANCELED);
++    insist(record.events[3].error == TRUE);
++    insist(record.events[3].result.retval == ETIMEDOUT);
++    insist(record.events[4].error == TRUE);
++    insist(record.events[4].result.retval == ECANCELED);
++    insist(record.events[5].error == TRUE);
++    insist(record.events[5].result.retval == ECANCELED);
++
++    verto_free(vctx);
++    krb5_free_context(kctx);
++    return 0;
++}
+diff --git a/src/lib/krad/t_test.c b/src/lib/krad/t_test.c
+new file mode 100644
+index 0000000..152bc77
+--- /dev/null
++++ b/src/lib/krad/t_test.c
+@@ -0,0 +1,50 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_test.c - Utility functions for libkrad tests */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "t_test.h"
++
++void
++noerror_impl(const char *file, int line, const char *cmd, int retval)
++{
++    if (retval == 0)
++        return;
++
++    fprintf(stderr, "%s:%d: %s:\n\t%s\n", file, line, strerror(retval), cmd);
++    exit(1);
++}
++
++void
++insist_impl(const char *file, int line, const char *cmd, krb5_boolean result)
++{
++    if (result)
++        return;
++
++    fprintf(stderr, "%s:%d: insist failed:\n\t%s\n", file, line, cmd);
++    exit(1);
++}
+diff --git a/src/lib/krad/t_test.h b/src/lib/krad/t_test.h
+new file mode 100644
+index 0000000..f44742f
+--- /dev/null
++++ b/src/lib/krad/t_test.h
+@@ -0,0 +1,60 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_test.h - Shared declarations for libkrad test programs */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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.
++ */
++
++#ifndef T_TEST_H_
++#define T_TEST_H_
++
++#include "internal.h"
++
++#include <sys/wait.h>
++#include <signal.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++#define insist(x) insist_impl(__FILE__, __LINE__, #x, x)
++#define noerror(x) noerror_impl(__FILE__, __LINE__, #x, x)
++
++struct event {
++    krb5_boolean error;
++    union
++    {
++        krb5_error_code retval;
++        krad_code code;
++    } result;
++};
++
++void
++noerror_impl(const char *file, int line, const char *cmd, int retval);
++
++void
++insist_impl(const char *file, int line, const char *cmd, krb5_boolean result);
++
++#endif /* T_TEST_H_ */
+-- 
+1.8.2.1
+
+
+From 01c7bae6c9a962ffd38f9d5f70fdde7b9e6eb929 Mon Sep 17 00:00:00 2001
+From: Nathaniel McCallum <npmccallum@redhat.com>
+Date: Tue, 9 Apr 2013 12:24:47 -0400
+Subject: [PATCH 4/4] add otp plugin
+
+---
+ src/Makefile.in                     |   1 +
+ src/configure.in                    |   1 +
+ src/kdc/kdc_preauth.c               |   2 +
+ src/plugins/preauth/otp/Makefile.in |  39 +++
+ src/plugins/preauth/otp/deps        |  26 ++
+ src/plugins/preauth/otp/main.c      | 379 ++++++++++++++++++++++++
+ src/plugins/preauth/otp/otp.exports |   1 +
+ src/plugins/preauth/otp/otp_state.c | 560 ++++++++++++++++++++++++++++++++++++
+ src/plugins/preauth/otp/otp_state.h |  59 ++++
+ 9 files changed, 1068 insertions(+)
+ create mode 100644 src/plugins/preauth/otp/Makefile.in
+ create mode 100644 src/plugins/preauth/otp/deps
+ create mode 100644 src/plugins/preauth/otp/main.c
+ create mode 100644 src/plugins/preauth/otp/otp.exports
+ create mode 100644 src/plugins/preauth/otp/otp_state.c
+ create mode 100644 src/plugins/preauth/otp/otp_state.h
+
+diff --git a/src/Makefile.in b/src/Makefile.in
+index 2c65831..0b9d355 100644
+--- a/src/Makefile.in
++++ b/src/Makefile.in
+@@ -12,6 +12,7 @@ SUBDIRS=util include lib \
+ 	plugins/kadm5_hook/test \
+ 	plugins/kdb/db2 \
+ 	@ldap_plugin_dir@ \
++	plugins/preauth/otp \
+ 	plugins/preauth/pkinit \
+ 	kdc kadmin slave clients appl tests \
+ 	config-files man doc @po@
+diff --git a/src/configure.in b/src/configure.in
+index d8676e5..520e0c8 100644
+--- a/src/configure.in
++++ b/src/configure.in
+@@ -1337,6 +1337,7 @@ dnl	ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
+ 	plugins/kdb/db2/libdb2/test
+ 	plugins/kdb/hdb
+ 	plugins/preauth/cksum_body
++	plugins/preauth/otp
+ 	plugins/preauth/securid_sam2
+ 	plugins/preauth/wpse
+ 	plugins/authdata/greet
+diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
+index 42a37a8..afbf1f6 100644
+--- a/src/kdc/kdc_preauth.c
++++ b/src/kdc/kdc_preauth.c
+@@ -238,6 +238,8 @@ get_plugin_vtables(krb5_context context,
+     /* Auto-register encrypted challenge and (if possible) pkinit. */
+     k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "pkinit",
+                            "preauth");
++    k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "otp",
++                           "preauth");
+     k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH,
+                        "encrypted_challenge",
+                        kdcpreauth_encrypted_challenge_initvt);
+diff --git a/src/plugins/preauth/otp/Makefile.in b/src/plugins/preauth/otp/Makefile.in
+new file mode 100644
+index 0000000..43bd2ba
+--- /dev/null
++++ b/src/plugins/preauth/otp/Makefile.in
+@@ -0,0 +1,39 @@
++mydir=plugins$(S)preauth$(S)otp
++BUILDTOP=$(REL)..$(S)..$(S)..
++KRB5_CONFIG_SETUP = KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf ; export KRB5_CONFIG ;
++PROG_LIBPATH=-L$(TOPLIBD)
++PROG_RPATH=$(KRB5_LIBDIR)
++MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
++DEFS=@DEFS@
++
++LIBBASE=otp
++LIBMAJOR=0
++LIBMINOR=0
++SO_EXT=.so
++RELDIR=../plugins/preauth/otp
++
++SHLIB_EXPDEPS = $(VERTO_DEPLIBS) $(KRB5_BASE_DEPLIBS) \
++	$(TOPLIBD)/libkrad$(SHLIBEXT)
++
++SHLIB_EXPLIBS= -lkrad $(VERTO_LIBS) $(KRB5_BASE_LIBS)
++
++SHLIB_DIRS=-L$(TOPLIBD)
++SHLIB_RDIRS=$(KRB5_LIBDIR)
++STOBJLISTS=OBJS.ST
++STLIBOBJS = \
++	otp_state.o \
++	main.o
++
++SRCS = \
++	$(srcdir)/otp_state.c \
++	$(srcdir)/main.c
++
++all-unix:: all-liblinks
++install-unix:: install-libs
++clean-unix:: clean-liblinks clean-libs clean-libobjs
++
++clean::
++	$(RM) lib$(LIBBASE)$(SO_EXT)
++
++@libnover_frag@
++@libobj_frag@
+diff --git a/src/plugins/preauth/otp/deps b/src/plugins/preauth/otp/deps
+new file mode 100644
+index 0000000..3352126
+--- /dev/null
++++ b/src/plugins/preauth/otp/deps
+@@ -0,0 +1,26 @@
++# 
++# Generated makefile dependencies follow.
++#
++otp_state.so otp_state.po $(OUTPRE)otp_state.$(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-json.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 $(top_srcdir)/include/krb5/preauth_plugin.h \
++  $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
++  otp_state.c otp_state.h
++main.so main.po $(OUTPRE)main.$(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-json.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 \
++  $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
++  $(top_srcdir)/include/socket-utils.h main.c otp_state.h
+diff --git a/src/plugins/preauth/otp/main.c b/src/plugins/preauth/otp/main.c
+new file mode 100644
+index 0000000..2f7470e
+--- /dev/null
++++ b/src/plugins/preauth/otp/main.c
+@@ -0,0 +1,379 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* plugins/preauth/otp/main.c - OTP kdcpreauth module definition */
++/*
++ * Copyright 2011 NORDUnet A/S.  All rights reserved.
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "k5-json.h"
++#include <krb5/preauth_plugin.h>
++#include "otp_state.h"
++
++#include <errno.h>
++#include <ctype.h>
++
++static krb5_preauthtype otp_pa_type_list[] =
++  { KRB5_PADATA_OTP_REQUEST, 0 };
++
++struct request_state {
++    krb5_kdcpreauth_verify_respond_fn respond;
++    void *arg;
++};
++
++static krb5_error_code
++decrypt_encdata(krb5_context context, krb5_keyblock *armor_key,
++                krb5_pa_otp_req *req, krb5_data *out)
++{
++    krb5_error_code retval;
++    krb5_data plaintext;
++
++    if (req == NULL)
++        return EINVAL;
++
++    retval = alloc_data(&plaintext, req->enc_data.ciphertext.length);
++    if (retval)
++        return retval;
++
++    retval = krb5_c_decrypt(context, armor_key, KRB5_KEYUSAGE_PA_OTP_REQUEST,
++                            NULL, &req->enc_data, &plaintext);
++    if (retval != 0) {
++        com_err("otp", retval, "Unable to decrypt encData in PA-OTP-REQUEST");
++        free(plaintext.data);
++        return retval;
++    }
++
++    *out = plaintext;
++    return 0;
++}
++
++static krb5_error_code
++nonce_verify(krb5_context ctx, krb5_keyblock *armor_key,
++             const krb5_data *nonce)
++{
++    krb5_error_code retval;
++    krb5_timestamp ts;
++    krb5_data *er = NULL;
++
++    if (armor_key == NULL || nonce->data == NULL) {
++        retval = EINVAL;
++        goto out;
++    }
++
++    /* Decode the PA-OTP-ENC-REQUEST structure. */
++    retval = decode_krb5_pa_otp_enc_req(nonce, &er);
++    if (retval != 0)
++        goto out;
++
++    /* Make sure the nonce is exactly the same size as the one generated. */
++    if (er->length != armor_key->length + sizeof(krb5_timestamp))
++        goto out;
++
++    /* Check to make sure the timestamp at the beginning is still valid. */
++    ts = load_32_be(er->data);
++    retval = krb5_check_clockskew(ctx, ts);
++
++out:
++    krb5_free_data(ctx, er);
++    return retval;
++}
++
++static krb5_error_code
++timestamp_verify(krb5_context ctx, const krb5_data *nonce)
++{
++    krb5_error_code retval = EINVAL;
++    krb5_pa_enc_ts *et = NULL;
++
++    if (nonce->data == NULL)
++        goto out;
++
++    /* Decode the PA-ENC-TS-ENC structure. */
++    retval = decode_krb5_pa_enc_ts(nonce, &et);
++    if (retval != 0)
++        goto out;
++
++    /* Check the clockskew. */
++    retval = krb5_check_clockskew(ctx, et->patimestamp);
++
++out:
++    krb5_free_pa_enc_ts(ctx, et);
++    return retval;
++}
++
++static krb5_error_code
++nonce_generate(krb5_context ctx, unsigned int length, krb5_data *nonce_out)
++{
++    krb5_data nonce;
++    krb5_error_code retval;
++    krb5_timestamp now;
++
++    retval = krb5_timeofday(ctx, &now);
++    if (retval != 0)
++        return retval;
++
++    retval = alloc_data(&nonce, sizeof(now) + length);
++    if (retval != 0)
++        return retval;
++
++    retval = krb5_c_random_make_octets(ctx, &nonce);
++    if (retval != 0) {
++        free(nonce.data);
++        return retval;
++    }
++
++    store_32_be(now, nonce.data);
++    *nonce_out = nonce;
++    return 0;
++}
++
++static void
++on_response(void *data, krb5_error_code retval, otp_response response)
++{
++    struct request_state rs = *(struct request_state *)data;
++
++    free(data);
++
++    if (retval == 0 && response != otp_response_success)
++        retval = KRB5_PREAUTH_FAILED;
++
++    rs.respond(rs.arg, retval, NULL, NULL, NULL);
++}
++
++static krb5_error_code
++otp_init(krb5_context context, krb5_kdcpreauth_moddata *moddata_out,
++         const char **realmnames)
++{
++    krb5_error_code retval;
++    otp_state *state;
++
++    retval = otp_state_new(context, &state);
++    if (retval)
++        return retval;
++    *moddata_out = (krb5_kdcpreauth_moddata)state;
++    return 0;
++}
++
++static void
++otp_fini(krb5_context context, krb5_kdcpreauth_moddata moddata)
++{
++    otp_state_free((otp_state *)moddata);
++}
++
++static int
++otp_flags(krb5_context context, krb5_preauthtype pa_type)
++{
++    return PA_REPLACES_KEY;
++}
++
++static void
++otp_edata(krb5_context context, krb5_kdc_req *request,
++          krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
++          krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
++          krb5_kdcpreauth_edata_respond_fn respond, void *arg)
++{
++    krb5_otp_tokeninfo ti, *tis[2] = { &ti, NULL };
++    krb5_keyblock *armor_key = NULL;
++    krb5_pa_otp_challenge chl;
++    krb5_pa_data *pa = NULL;
++    krb5_error_code retval;
++    krb5_data *encoding;
++    char *config;
++
++    /* Determine if otp is enabled for the user. */
++    retval = cb->get_string(context, rock, "otp", &config);
++    if (retval != 0 || config == NULL)
++        goto out;
++    cb->free_string(context, rock, config);
++
++    /* Get the armor key.  This indicates the length of random data to use in
++     * the nonce. */
++    armor_key = cb->fast_armor(context, rock);
++    if (armor_key == NULL) {
++        retval = ENOENT;
++        goto out;
++    }
++
++    /* Build the (mostly empty) challenge. */
++    memset(&ti, 0, sizeof(ti));
++    memset(&chl, 0, sizeof(chl));
++    chl.tokeninfo = tis;
++    ti.format = -1;
++    ti.length = -1;
++    ti.iteration_count = -1;
++
++    /* Generate the nonce. */
++    retval = nonce_generate(context, armor_key->length, &chl.nonce);
++    if (retval != 0)
++        goto out;
++
++    /* Build the output pa-data. */
++    retval = encode_krb5_pa_otp_challenge(&chl, &encoding);
++    if (retval != 0)
++        goto out;
++    pa = k5alloc(sizeof(krb5_pa_data), &retval);
++    if (pa == NULL) {
++        krb5_free_data(context, encoding);
++        goto out;
++    }
++    pa->pa_type = KRB5_PADATA_OTP_CHALLENGE;
++    pa->contents = (krb5_octet *)encoding->data;
++    pa->length = encoding->length;
++    free(encoding);
++
++out:
++    (*respond)(arg, retval, pa);
++}
++
++static void
++otp_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
++           krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa,
++           krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
++           krb5_kdcpreauth_moddata moddata,
++           krb5_kdcpreauth_verify_respond_fn respond, void *arg)
++{
++    krb5_keyblock *armor_key = NULL;
++    krb5_pa_otp_req *req = NULL;
++    struct request_state *rs;
++    krb5_error_code retval;
++    krb5_data d, plaintext;
++    char *config;
++
++    enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
++
++    /* Get the FAST armor key. */
++    armor_key = cb->fast_armor(context, rock);
++    if (armor_key == NULL) {
++        retval = KRB5KDC_ERR_PREAUTH_FAILED;
++        com_err("otp", retval, "No armor key found when verifying padata");
++        goto error;
++    }
++
++    /* Decode the request. */
++    d = make_data(pa->contents, pa->length);
++    retval = decode_krb5_pa_otp_req(&d, &req);
++    if (retval != 0) {
++        com_err("otp", retval, "Unable to decode OTP request");
++        goto error;
++    }
++
++    /* Decrypt the nonce from the request. */
++    retval = decrypt_encdata(context, armor_key, req, &plaintext);
++    if (retval != 0) {
++        com_err("otp", retval, "Unable to decrypt nonce");
++        goto error;
++    }
++
++    /* Verify the nonce or timestamp. */
++    retval = nonce_verify(context, armor_key, &plaintext);
++    if (retval != 0)
++        retval = timestamp_verify(context, &plaintext);
++    krb5_free_data_contents(context, &plaintext);
++    if (retval != 0) {
++        com_err("otp", retval, "Unable to verify nonce or timestamp");
++        goto error;
++    }
++
++    /* Create the request state. */
++    rs = k5alloc(sizeof(struct request_state), &retval);
++    if (rs == NULL)
++        goto error;
++    rs->arg = arg;
++    rs->respond = respond;
++
++    /* Get the principal's OTP configuration string. */
++    retval = cb->get_string(context, rock, "otp", &config);
++    if (config == NULL)
++        retval = KRB5_PREAUTH_FAILED;
++    if (retval != 0) {
++        free(rs);
++        goto error;
++    }
++
++    /* Send the request. */
++    otp_state_verify((otp_state *)moddata, cb->event_context(context, rock),
++                     request->client, config, req, on_response, rs);
++    cb->free_string(context, rock, config);
++
++    k5_free_pa_otp_req(context, req);
++    return;
++
++error:
++    k5_free_pa_otp_req(context, req);
++    (*respond)(arg, retval, NULL, NULL, NULL);
++}
++
++static krb5_error_code
++otp_return_padata(krb5_context context, krb5_pa_data *padata,
++                  krb5_data *req_pkt, krb5_kdc_req *request,
++                  krb5_kdc_rep *reply, krb5_keyblock *encrypting_key,
++                  krb5_pa_data **send_pa_out, krb5_kdcpreauth_callbacks cb,
++                  krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata,
++                  krb5_kdcpreauth_modreq modreq)
++{
++    krb5_keyblock *armor_key = NULL;
++
++    if (padata->length == 0)
++        return 0;
++
++    /* Get the armor key. */
++    armor_key = cb->fast_armor(context, rock);
++    if (!armor_key) {
++      com_err("otp", ENOENT, "No armor key found when returning padata");
++      return ENOENT;
++    }
++
++    /* Replace the reply key with the FAST armor key. */
++    krb5_free_keyblock_contents(context, encrypting_key);
++    return krb5_copy_keyblock_contents(context, armor_key, encrypting_key);
++}
++
++krb5_error_code
++kdcpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
++                      krb5_plugin_vtable vtable);
++
++krb5_error_code
++kdcpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
++                      krb5_plugin_vtable vtable)
++{
++    krb5_kdcpreauth_vtable vt;
++
++    if (maj_ver != 1)
++        return KRB5_PLUGIN_VER_NOTSUPP;
++
++    vt = (krb5_kdcpreauth_vtable)vtable;
++    vt->name = "otp";
++    vt->pa_type_list = otp_pa_type_list;
++    vt->init = otp_init;
++    vt->fini = otp_fini;
++    vt->flags = otp_flags;
++    vt->edata = otp_edata;
++    vt->verify = otp_verify;
++    vt->return_padata = otp_return_padata;
++
++    com_err("otp", 0, "Loaded");
++
++    return 0;
++}
+diff --git a/src/plugins/preauth/otp/otp.exports b/src/plugins/preauth/otp/otp.exports
+new file mode 100644
+index 0000000..26aa19d
+--- /dev/null
++++ b/src/plugins/preauth/otp/otp.exports
+@@ -0,0 +1 @@
++kdcpreauth_otp_initvt
+diff --git a/src/plugins/preauth/otp/otp_state.c b/src/plugins/preauth/otp/otp_state.c
+new file mode 100644
+index 0000000..95f88e0
+--- /dev/null
++++ b/src/plugins/preauth/otp/otp_state.c
+@@ -0,0 +1,560 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* plugins/preauth/otp/otp_state.c - Verify OTP token values using RADIUS */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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 "otp_state.h"
++
++#include <krad.h>
++#include <k5-json.h>
++
++#include <ctype.h>
++
++#ifndef HOST_NAME_MAX
++/* SUSv2 */
++#define HOST_NAME_MAX 255
++#endif
++
++#define DEFAULT_TYPE_NAME "DEFAULT"
++#define DEFAULT_SOCKET_FMT KDC_DIR "/%s.socket"
++#define DEFAULT_TIMEOUT 5
++#define DEFAULT_RETRIES 3
++
++typedef struct token_type_st {
++    char *name;
++    char *server;
++    char *secret;
++    int timeout;
++    size_t retries;
++    krb5_boolean strip_realm;
++} token_type;
++
++typedef struct token_st {
++    const token_type *type;
++    krb5_data username;
++} token;
++
++typedef struct request_st {
++    otp_state *state;
++    token *tokens;
++    ssize_t index;
++    otp_cb cb;
++    void *data;
++    krad_attrset *attrs;
++} request;
++
++struct otp_state_st {
++    krb5_context ctx;
++    token_type *types;
++    krad_client *radius;
++    krad_attrset *attrs;
++};
++
++static void request_send(request *req);
++
++/* Free the contents of a single token type. */
++static void
++token_type_free(token_type *type)
++{
++    if (type == NULL)
++        return;
++
++    free(type->name);
++    free(type->server);
++    free(type->secret);
++}
++
++/* Construct the internal default token type. */
++static krb5_error_code
++token_type_default(token_type *out)
++{
++    char *name = NULL, *server = NULL, *secret = NULL;
++
++    memset(out, 0, sizeof(*out));
++
++    name = strdup(DEFAULT_TYPE_NAME);
++    if (name == NULL)
++        goto oom;
++    if (asprintf(&server, DEFAULT_SOCKET_FMT, name) < 0)
++        goto oom;
++    secret = strdup("");
++    if (secret == NULL)
++        goto oom;
++
++    out->name = name;
++    out->server = server;
++    out->secret = secret;
++    out->timeout = DEFAULT_TIMEOUT * 1000;
++    out->retries = DEFAULT_RETRIES;
++    out->strip_realm = FALSE;
++    return 0;
++
++oom:
++    free(name);
++    free(server);
++    free(secret);
++    return ENOMEM;
++}
++
++/* Decode a single token type from the profile. */
++static krb5_error_code
++token_type_decode(profile_t profile, const char *name, token_type *out)
++{
++    krb5_error_code retval;
++    char *server = NULL, *name_copy = NULL, *secret = NULL;
++    const char *default_secret;
++    int strip_realm, timeout, retries;
++
++    memset(out, 0, sizeof(*out));
++
++    /* Set the name. */
++    name_copy = strdup(name);
++    if (name_copy == NULL)
++        return ENOMEM;
++
++    /* Set strip_realm. */
++    retval = profile_get_boolean(profile, "otp", name, "strip_realm", TRUE,
++                                 &strip_realm);
++    if (retval != 0)
++        goto cleanup;
++
++    /* Set the server. */
++    retval = profile_get_string(profile, "otp", name, "server", NULL, &server);
++    if (retval != 0)
++        goto cleanup;
++    if (server == NULL && asprintf(&server, DEFAULT_SOCKET_FMT, name) < 0) {
++        retval = ENOMEM;
++        goto cleanup;
++    }
++
++    /* Get the secret (optional for Unix-domain sockets). */
++    default_secret = (*server == '/') ? "" : NULL;
++    retval = profile_get_string(profile, "otp", name, "secret", default_secret,
++                                &secret);
++    if (retval != 0)
++        goto cleanup;
++    if (secret == NULL) {
++        com_err("otp", EINVAL, "Secret not specified in token type '%s'",
++		name);
++        retval = EINVAL;
++        goto cleanup;
++    }
++
++    /* Get the timeout (profile value in seconds, result in milliseconds). */
++    retval = profile_get_integer(profile, "otp", name, "timeout",
++                                 DEFAULT_TIMEOUT, &timeout);
++    if (retval != 0)
++        goto cleanup;
++    timeout *= 1000;
++
++    /* Get the number of retries. */
++    retval = profile_get_integer(profile, "otp", name, "retries",
++                                 DEFAULT_RETRIES, &retries);
++    if (retval != 0)
++        goto cleanup;
++
++    out->name = name_copy;
++    out->server = server;
++    out->secret = secret;
++    out->timeout = timeout;
++    out->retries = retries;
++    out->strip_realm = strip_realm;
++    name_copy = server = secret = NULL;
++
++cleanup:
++    free(name_copy);
++    free(server);
++    free(secret);
++    return retval;
++}
++
++/* Free an array of token types. */
++static void
++token_types_free(token_type *types)
++{
++    size_t i;
++
++    if (types == NULL)
++        return;
++
++    for (i = 0; types[i].server != NULL; i++)
++        token_type_free(&types[i]);
++
++    free(types);
++}
++
++/* Decode an array of token types from the profile. */
++static krb5_error_code
++token_types_decode(profile_t profile, token_type **out)
++{
++    const char *hier[2] = { "otp", NULL };
++    token_type *types = NULL;
++    char **names = NULL;
++    krb5_error_code retval;
++    size_t i, pos;
++    krb5_boolean have_default = FALSE;
++
++    retval = profile_get_subsection_names(profile, hier, &names);
++    if (retval != 0)
++        return retval;
++
++    /* Check if any of the profile subsections overrides the default. */
++    for (i = 0; names[i] != NULL; i++) {
++        if (strcmp(names[i], DEFAULT_TYPE_NAME) == 0)
++            have_default = TRUE;
++    }
++
++    /* Leave space for the default (possibly) and the terminator. */
++    types = k5alloc((i + 2) * sizeof(token_type), &retval);
++    if (types == NULL)
++        goto cleanup;
++
++    /* If no default has been specified, use our internal default. */
++    pos = 0;
++    if (!have_default) {
++        retval = token_type_default(&types[pos++]);
++        if (retval != 0)
++            goto cleanup;
++    }
++
++    /* Decode each profile section into a token type element. */
++    for (i = 0; names[i] != NULL; i++) {
++        retval = token_type_decode(profile, names[i], &types[pos++]);
++        if (retval != 0)
++            goto cleanup;
++    }
++
++    *out = types;
++    types = NULL;
++
++cleanup:
++    profile_free_list(names);
++    token_types_free(types);
++    return retval;
++}
++
++/* Free the contents of a single token. */
++static void
++token_free_contents(token *t)
++{
++    if (t != NULL)
++        free(t->username.data);
++}
++
++/* Decode a single token from a JSON token object. */
++static krb5_error_code
++token_decode(krb5_context ctx, krb5_const_principal princ,
++             const token_type *types, k5_json_object obj, token *out)
++{
++    const char *typename = DEFAULT_TYPE_NAME;
++    const token_type *type = NULL;
++    char *username = NULL;
++    krb5_error_code retval;
++    k5_json_value val;
++    size_t i;
++    int flags;
++
++    memset(out, 0, sizeof(*out));
++
++    /* Find the token type. */
++    val = k5_json_object_get(obj, "type");
++    if (val != NULL && k5_json_get_tid(val) == K5_JSON_TID_STRING)
++        typename = k5_json_string_utf8(val);
++    for (i = 0; types[i].server != NULL; i++) {
++        if (strcmp(typename, types[i].name) == 0)
++            type = &types[i];
++    }
++    if (type == NULL)
++        return EINVAL;
++
++    /* Get the username, either from obj or from unparsing the principal. */
++    val = k5_json_object_get(obj, "username");
++    if (val != NULL && k5_json_get_tid(val) == K5_JSON_TID_STRING) {
++        username = strdup(k5_json_string_utf8(val));
++        if (username == NULL)
++            return ENOMEM;
++    } else {
++        flags = type->strip_realm ? KRB5_PRINCIPAL_UNPARSE_NO_REALM : 0;
++        retval = krb5_unparse_name_flags(ctx, princ, flags, &username);
++        if (retval != 0)
++            return retval;
++    }
++
++    out->type = type;
++    out->username = string2data(username);
++    return 0;
++}
++
++/* Free an array of tokens. */
++static void
++tokens_free(token *tokens)
++{
++    size_t i;
++
++    if (tokens == NULL)
++        return;
++
++    for (i = 0; tokens[i].type != NULL; i++)
++        token_free_contents(&tokens[i]);
++
++    free(tokens);
++}
++
++/* Decode an array of tokens from the configuration string. */
++static krb5_error_code
++tokens_decode(krb5_context ctx, krb5_const_principal princ,
++              const token_type *types, const char *config, token **out)
++{
++    krb5_error_code retval;
++    k5_json_value arr, obj;
++    token *tokens;
++    ssize_t len, i, j;
++
++    if (config == NULL)
++        config = "[{}]";
++
++    arr = k5_json_decode(config);
++    if (arr == NULL)
++        return ENOMEM;
++
++    if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY ||
++        (len = k5_json_array_length(arr)) == 0) {
++        k5_json_release(arr);
++
++        arr = k5_json_decode("[{}]");
++        if (arr == NULL)
++            return ENOMEM;
++
++        if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY) {
++            k5_json_release(arr);
++            return ENOMEM;
++        }
++
++        len = k5_json_array_length(arr);
++    }
++
++    tokens = calloc(len + 1, sizeof(token));
++    if (tokens == NULL) {
++        k5_json_release(arr);
++        return ENOMEM;
++    }
++
++    for (i = 0, j = 0; i < len; i++) {
++        obj = k5_json_array_get(arr, i);
++        if (k5_json_get_tid(obj) != K5_JSON_TID_OBJECT)
++            continue;
++
++        retval = token_decode(ctx, princ, types, obj, &tokens[j++]);
++        if (retval != 0) {
++            k5_json_release(arr);
++            while (--j > 0)
++                token_free_contents(&tokens[j]);
++            free(tokens);
++            return retval;
++        }
++    }
++
++    k5_json_release(arr);
++    *out = tokens;
++    return 0;
++}
++
++static void
++request_free(request *req)
++{
++    if (req == NULL)
++        return;
++
++    krad_attrset_free(req->attrs);
++    tokens_free(req->tokens);
++    free(req);
++}
++
++krb5_error_code
++otp_state_new(krb5_context ctx, otp_state **out)
++{
++    char hostname[HOST_NAME_MAX + 1];
++    krb5_error_code retval;
++    profile_t profile;
++    krb5_data hndata;
++    otp_state *self;
++
++    retval = gethostname(hostname, sizeof(hostname));
++    if (retval != 0)
++        return retval;
++
++    self = calloc(1, sizeof(otp_state));
++    if (self == NULL)
++        return ENOMEM;
++
++    retval = krb5_get_profile(ctx, &profile);
++    if (retval != 0)
++        goto error;
++
++    retval = token_types_decode(profile, &self->types);
++    profile_abandon(profile);
++    if (retval != 0)
++        goto error;
++
++    retval = krad_attrset_new(ctx, &self->attrs);
++    if (retval != 0)
++        goto error;
++
++    hndata = make_data(hostname, strlen(hostname));
++    retval = krad_attrset_add(self->attrs,
++                              krad_attr_name2num("NAS-Identifier"), &hndata);
++    if (retval != 0)
++        goto error;
++
++    retval = krad_attrset_add_number(self->attrs,
++                                     krad_attr_name2num("Service-Type"),
++                                     KRAD_SERVICE_TYPE_AUTHENTICATE_ONLY);
++    if (retval != 0)
++        goto error;
++
++    self->ctx = ctx;
++    *out = self;
++    return 0;
++
++error:
++    otp_state_free(self);
++    return retval;
++}
++
++void
++otp_state_free(otp_state *self)
++{
++    if (self == NULL)
++        return;
++
++    krad_attrset_free(self->attrs);
++    token_types_free(self->types);
++    free(self);
++}
++
++static void
++callback(krb5_error_code retval, const krad_packet *rqst,
++         const krad_packet *resp, void *data)
++{
++    request *req = data;
++
++    req->index++;
++
++    if (retval != 0)
++        goto error;
++
++    /* If we received an accept packet, success! */
++    if (krad_packet_get_code(resp) ==
++        krad_code_name2num("Access-Accept")) {
++        req->cb(req->data, retval, otp_response_success);
++        request_free(req);
++        return;
++    }
++
++    /* If we have no more tokens to try, failure! */
++    if (req->tokens[req->index].type == NULL)
++        goto error;
++
++    /* Try the next token. */
++    request_send(req);
++
++error:
++    req->cb(req->data, retval, otp_response_fail);
++    request_free(req);
++}
++
++static void
++request_send(request *req)
++{
++    krb5_error_code retval;
++    token *tok = &req->tokens[req->index];
++    const token_type *t = tok->type;
++
++    retval = krad_attrset_add(req->attrs, krad_attr_name2num("User-Name"),
++                              &tok->username);
++    if (retval != 0)
++        goto error;
++
++    retval = krad_client_send(req->state->radius,
++                              krad_code_name2num("Access-Request"), req->attrs,
++                              t->server, t->secret, t->timeout, t->retries,
++                              callback, req);
++    krad_attrset_del(req->attrs, krad_attr_name2num("User-Name"), 0);
++    if (retval != 0)
++        goto error;
++
++    return;
++
++error:
++    req->cb(req->data, retval, otp_response_fail);
++    request_free(req);
++}
++
++void
++otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ,
++                 const char *config, const krb5_pa_otp_req *req,
++                 otp_cb cb, void *data)
++{
++    krb5_error_code retval;
++    request *rqst = NULL;
++
++    if (state->radius == NULL) {
++        retval = krad_client_new(state->ctx, ctx, &state->radius);
++        if (retval != 0)
++            goto error;
++    }
++
++    rqst = calloc(1, sizeof(request));
++    if (rqst == NULL) {
++        (*cb)(data, ENOMEM, otp_response_fail);
++        return;
++    }
++    rqst->state = state;
++    rqst->data = data;
++    rqst->cb = cb;
++
++    retval = krad_attrset_copy(state->attrs, &rqst->attrs);
++    if (retval != 0)
++        goto error;
++
++    retval = krad_attrset_add(rqst->attrs, krad_attr_name2num("User-Password"),
++                              &req->otp_value);
++    if (retval != 0)
++        goto error;
++
++    retval = tokens_decode(state->ctx, princ, state->types, config,
++                           &rqst->tokens);
++    if (retval != 0)
++        goto error;
++
++    request_send(rqst);
++    return;
++
++error:
++    (*cb)(data, retval, otp_response_fail);
++    request_free(rqst);
++}
+diff --git a/src/plugins/preauth/otp/otp_state.h b/src/plugins/preauth/otp/otp_state.h
+new file mode 100644
+index 0000000..4247d0b
+--- /dev/null
++++ b/src/plugins/preauth/otp/otp_state.h
+@@ -0,0 +1,59 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* plugins/preauth/otp/otp_state.h - Internal declarations for OTP module */
++/*
++ * Copyright 2013 Red Hat, Inc.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ *    1. Redistributions of source code must retain the above copyright
++ *       notice, this list of conditions and the following disclaimer.
++ *
++ *    2. 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 OWNER
++ * 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.
++ */
++
++#ifndef OTP_H_
++#define OTP_H_
++
++#include <k5-int.h>
++#include <verto.h>
++
++#include <com_err.h>
++
++typedef enum otp_response {
++    otp_response_fail = 0,
++    otp_response_success
++    /* Other values reserved for responses like next token or new pin. */
++} otp_response;
++
++typedef struct otp_state_st otp_state;
++typedef void
++(*otp_cb)(void *data, krb5_error_code retval, otp_response response);
++
++krb5_error_code
++otp_state_new(krb5_context ctx, otp_state **self);
++
++void
++otp_state_free(otp_state *self);
++
++void
++otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ,
++                 const char *config, const krb5_pa_otp_req *request,
++                 otp_cb cb, void *data);
++
++#endif /* OTP_H_ */
+-- 
+1.8.2.1
+
diff --git a/SOURCES/krb5-1.11.2-skew1.patch b/SOURCES/krb5-1.11.2-skew1.patch
new file mode 100644
index 0000000..c7dc919
--- /dev/null
+++ b/SOURCES/krb5-1.11.2-skew1.patch
@@ -0,0 +1,115 @@
+>From 8f6d12bae1a0f1d274593c4a06dfa5948aa61418 Mon Sep 17 00:00:00 2001
+From: Stef Walter <stefw@redhat.com>
+Date: Thu, 23 May 2013 08:38:20 +0200
+Subject: [PATCH 1/2] krb5: Refator duplicate code for setting the AS REQ nonce
+
+---
+ src/lib/krb5/krb/get_in_tkt.c | 64 +++++++++++++++++++++++--------------------
+ 1 file changed, 35 insertions(+), 29 deletions(-)
+
+diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
+index 828b0fb..1058112 100644
+--- a/src/lib/krb5/krb/get_in_tkt.c
++++ b/src/lib/krb5/krb/get_in_tkt.c
+@@ -650,6 +650,34 @@ cleanup:
+     return code;
+ }
+ 
++static krb5_error_code
++update_req_before_encoding(krb5_context context, krb5_init_creds_context ctx)
++{
++    krb5_error_code code = 0;
++    unsigned char random_buf[4];
++    krb5_data random_data;
++
++    /*
++     * RFC 6113 requires a new nonce for the inner request on each try. It's
++     * permitted to change the nonce even for non-FAST as well.
++     */
++    random_data.length = 4;
++    random_data.data = (char *)random_buf;
++    code = krb5_c_random_make_octets(context, &random_data);
++    if (code != 0)
++        goto cleanup;
++
++    /*
++     * See RT ticket 3196 at MIT.  If we set the high bit, we may have
++     * compatibility problems with Heimdal, because we (incorrectly) encode
++     * this value as signed.
++     */
++    ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
++
++cleanup:
++    return code;
++}
++
+ /**
+  * Throw away any state related to specific realm either at the beginning of a
+  * request, or when a realm changes, or when we start to use FAST after
+@@ -664,8 +692,6 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
+                         krb5_pa_data **padata)
+ {
+     krb5_error_code code = 0;
+-    unsigned char random_buf[4];
+-    krb5_data random_data;
+     krb5_timestamp from;
+ 
+     if (ctx->preauth_to_use) {
+@@ -693,18 +719,10 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
+             goto cleanup;
+     }
+ 
+-    /* Set the request nonce. */
+-    random_data.length = 4;
+-    random_data.data = (char *)random_buf;
+-    code = krb5_c_random_make_octets(context, &random_data);
+-    if (code !=0)
++    code = update_req_before_encoding(context, ctx);
++    if (code != 0)
+         goto cleanup;
+-    /*
+-     * See RT ticket 3196 at MIT.  If we set the high bit, we may have
+-     * compatibility problems with Heimdal, because we (incorrectly) encode
+-     * this value as signed.
+-     */
+-    ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
++
+     krb5_free_principal(context, ctx->request->server);
+     ctx->request->server = NULL;
+ 
+@@ -1188,28 +1206,16 @@ init_creds_step_request(krb5_context context,
+ {
+     krb5_error_code code;
+     krb5_boolean got_real;
+-    char random_buf[4];
+-    krb5_data random_data;
+ 
+     if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
+         code = KRB5_GET_IN_TKT_LOOP;
+         goto cleanup;
+     }
+-    /*
+-     * RFC 6113 requires a new nonce for the inner request on each try. It's
+-     * permitted to change the nonce even for non-FAST so we do here.
+-     */
+-    random_data.length = 4;
+-    random_data.data = (char *)random_buf;
+-    code = krb5_c_random_make_octets(context, &random_data);
+-    if (code !=0)
++
++    code = update_req_before_encoding(context, ctx);
++    if (code != 0)
+         goto cleanup;
+-    /*
+-     * See RT ticket 3196 at MIT.  If we set the high bit, we may have
+-     * compatibility problems with Heimdal, because we (incorrectly) encode
+-     * this value as signed.
+-     */
+-    ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
++
+     krb5_free_data(context, ctx->inner_request_body);
+     ctx->inner_request_body = NULL;
+     code = encode_krb5_kdc_req_body(ctx->request, &ctx->inner_request_body);
+-- 
+1.8.1.4
+
diff --git a/SOURCES/krb5-1.11.2-skew2.patch b/SOURCES/krb5-1.11.2-skew2.patch
new file mode 100644
index 0000000..d5063bd
--- /dev/null
+++ b/SOURCES/krb5-1.11.2-skew2.patch
@@ -0,0 +1,144 @@
+>From 51ab359d7cc6643cfd4fac28def2e1c756553201 Mon Sep 17 00:00:00 2001
+From: Stef Walter <stefw@redhat.com>
+Date: Thu, 23 May 2013 08:44:43 +0200
+Subject: [PATCH 2/2] krb5: Fix ticket start and end time to respect skew
+
+Since the kerberos protocol uses timestamp rather than duration deltas
+for its starttime, endtime, and renewtime KDC AS REQ fields, we have
+to calculate these with respect to the offsets we know about received
+from the server.
+
+Leverage the unauthenticated server time we received during preauth when
+calculating these these timestamps from the duration deltas we use
+in our krb5 api and tools.
+
+In order to do this we have to update certain fields of the AS REQ
+each time we encode it for sending to the KDC.
+---
+ src/lib/krb5/krb/get_in_tkt.c | 44 +++++++++++++++++++++++--------------------
+ src/lib/krb5/krb/int-proto.h  |  5 +++++
+ src/lib/krb5/krb/preauth2.c   |  8 ++++++++
+ 3 files changed, 37 insertions(+), 20 deletions(-)
+
+diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
+index 1058112..694c9b0b 100644
+--- a/src/lib/krb5/krb/get_in_tkt.c
++++ b/src/lib/krb5/krb/get_in_tkt.c
+@@ -656,6 +656,8 @@ update_req_before_encoding(krb5_context context, krb5_init_creds_context ctx)
+     krb5_error_code code = 0;
+     unsigned char random_buf[4];
+     krb5_data random_data;
++    krb5_timestamp from;
++    krb5_int32 unused;
+ 
+     /*
+      * RFC 6113 requires a new nonce for the inner request on each try. It's
+@@ -674,6 +676,28 @@ update_req_before_encoding(krb5_context context, krb5_init_creds_context ctx)
+      */
+     ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
+ 
++    code = k5_preauth_get_time(context, &ctx->preauth_rock, TRUE, &ctx->request_time, &unused);
++    if (code != 0)
++        goto cleanup;
++
++    /* Omit request start time in the common case.  MIT and Heimdal KDCs will
++     * ignore it for non-postdated tickets anyway. */
++    from = krb5int_addint32(ctx->request_time, ctx->start_time);
++    if (ctx->start_time != 0)
++        ctx->request->from = from;
++    ctx->request->till = krb5int_addint32(from, ctx->tkt_life);
++
++    if (ctx->renew_life > 0) {
++        ctx->request->rtime =
++            krb5int_addint32(from, ctx->renew_life);
++        if (ctx->request->rtime < ctx->request->till) {
++            /* don't ask for a smaller renewable time than the lifetime */
++            ctx->request->rtime = ctx->request->till;
++        }
++        ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
++    } else
++        ctx->request->rtime = 0;
++
+ cleanup:
+     return code;
+ }
+@@ -692,7 +716,6 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
+                         krb5_pa_data **padata)
+ {
+     krb5_error_code code = 0;
+-    krb5_timestamp from;
+ 
+     if (ctx->preauth_to_use) {
+         krb5_free_pa_data(context, ctx->preauth_to_use);
+@@ -732,8 +755,6 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
+     if (code != 0)
+         goto cleanup;
+ 
+-    ctx->request_time = time(NULL);
+-
+     code = krb5int_fast_as_armor(context, ctx->fast_state,
+                                  ctx->opte, ctx->request);
+     if (code != 0)
+@@ -747,23 +768,6 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
+     /* give the preauth plugins a chance to prep the request body */
+     krb5_preauth_prepare_request(context, ctx->opte, ctx->request);
+ 
+-    /* Omit request start time in the common case.  MIT and Heimdal KDCs will
+-     * ignore it for non-postdated tickets anyway. */
+-    from = krb5int_addint32(ctx->request_time, ctx->start_time);
+-    if (ctx->start_time != 0)
+-        ctx->request->from = from;
+-    ctx->request->till = krb5int_addint32(from, ctx->tkt_life);
+-
+-    if (ctx->renew_life > 0) {
+-        ctx->request->rtime =
+-            krb5int_addint32(from, ctx->renew_life);
+-        if (ctx->request->rtime < ctx->request->till) {
+-            /* don't ask for a smaller renewable time than the lifetime */
+-            ctx->request->rtime = ctx->request->till;
+-        }
+-        ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
+-    } else
+-        ctx->request->rtime = 0;
+     code = krb5int_fast_prep_req_body(context, ctx->fast_state,
+                                       ctx->request,
+                                       &ctx->outer_request_body);
+diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
+index 3326154..83a47c0 100644
+--- a/src/lib/krb5/krb/int-proto.h
++++ b/src/lib/krb5/krb/int-proto.h
+@@ -142,6 +142,11 @@ krb5_preauth_supply_preauth_data(krb5_context context,
+                                  const char *value);
+ 
+ krb5_error_code
++k5_preauth_get_time(krb5_context context, krb5_clpreauth_rock rock,
++                    krb5_boolean allow_unauth_time, krb5_timestamp *time_out,
++                    krb5_int32 *usec_out);
++
++krb5_error_code
+ clpreauth_encrypted_challenge_initvt(krb5_context context, int maj_ver,
+                                      int min_ver, krb5_plugin_vtable vtable);
+ 
+diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c
+index 747611e..167f611 100644
+--- a/src/lib/krb5/krb/preauth2.c
++++ b/src/lib/krb5/krb/preauth2.c
+@@ -397,6 +397,15 @@ get_preauth_time(krb5_context context, krb5_clpreauth_rock rock,
+                  krb5_boolean allow_unauth_time, krb5_timestamp *time_out,
+                  krb5_int32 *usec_out)
+ {
++    return k5_preauth_get_time(context, rock, allow_unauth_time,
++                               time_out, usec_out);
++}
++ 
++krb5_error_code
++k5_preauth_get_time(krb5_context context, krb5_clpreauth_rock rock,
++                    krb5_boolean allow_unauth_time, krb5_timestamp *time_out,
++                    krb5_int32 *usec_out)
++{
+     if (rock->pa_offset_state != NO_OFFSET &&
+         (allow_unauth_time || rock->pa_offset_state == AUTH_OFFSET) &&
+         (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME)) {
+-- 
+1.8.1.4
+
diff --git a/SOURCES/krb5-1.11.3-gss-ccache-import.patch b/SOURCES/krb5-1.11.3-gss-ccache-import.patch
new file mode 100644
index 0000000..2bfd927
--- /dev/null
+++ b/SOURCES/krb5-1.11.3-gss-ccache-import.patch
@@ -0,0 +1,131 @@
+Tweaked for 1.11.3.
+
+commit 48dd01f29b893a958a64dcf6eb0b734e8463425b
+Author: Greg Hudson <ghudson@mit.edu>
+Date:   Mon Oct 7 09:51:56 2013 -0400
+
+    Fix GSSAPI krb5 cred ccache import
+    
+    json_to_ccache was incorrectly indexing the JSON array when restoring
+    a memory ccache.  Fix it.
+    
+    Add test coverage for a multi-cred ccache by exporting/importing the
+    synthesized S4U2Proxy delegated cred in t_s4u2proxy_krb5.c; move
+    export_import_cred from t_export_cred.c to common.c to facilitate
+    this.  Make a note in t_export_cred.py that this case is covered in
+    t_s4u.py.
+    
+    ticket: 7706
+    target_version: 1.11.4
+
+diff --git a/src/lib/gssapi/krb5/import_cred.c b/src/lib/gssapi/krb5/import_cred.c
+index 973b9d0..f0a0373 100644
+--- a/src/lib/gssapi/krb5/import_cred.c
++++ b/src/lib/gssapi/krb5/import_cred.c
+@@ -486,7 +486,7 @@ json_to_ccache(krb5_context context, k5_json_value v, krb5_ccache *ccache_out,
+ 
+     /* Add remaining array entries to the ccache as credentials. */
+     for (i = 1; i < len; i++) {
+-        if (json_to_creds(context, k5_json_array_get(array, 1), &creds))
++        if (json_to_creds(context, k5_json_array_get(array, i), &creds))
+             goto invalid;
+         ret = krb5_cc_store_cred(context, ccache, &creds);
+         krb5_free_cred_contents(context, &creds);
+diff --git a/src/tests/gssapi/common.c b/src/tests/gssapi/common.c
+index 19a781a..231f44a 100644
+--- a/src/tests/gssapi/common.c
++++ b/src/tests/gssapi/common.c
+@@ -149,6 +149,20 @@ establish_contexts(gss_OID imech, gss_cred_id_t icred, gss_cred_id_t acred,
+ }
+ 
+ void
++export_import_cred(gss_cred_id_t *cred)
++{
++    OM_uint32 major, minor;
++    gss_buffer_desc buf;
++
++    major = gss_export_cred(&minor, *cred, &buf);
++    check_gsserr("gss_export_cred", major, minor);
++    (void)gss_release_cred(&minor, cred);
++    major = gss_import_cred(&minor, &buf, cred);
++    check_gsserr("gss_import_cred", major, minor);
++    (void)gss_release_buffer(&minor, &buf);
++}
++
++void
+ display_canon_name(const char *tag, gss_name_t name, gss_OID mech)
+ {
+     gss_name_t canon;
+diff --git a/src/tests/gssapi/common.h b/src/tests/gssapi/common.h
+index 54c0d36..ae11b51 100644
+--- a/src/tests/gssapi/common.h
++++ b/src/tests/gssapi/common.h
+@@ -62,6 +62,10 @@ void establish_contexts(gss_OID imech, gss_cred_id_t icred,
+  * 'p:principalname', or 'h:host@service' (or just 'h:service'). */
+ gss_name_t import_name(const char *str);
+ 
++/* Export *cred to a token, then release *cred and replace it by re-importing
++ * the token. */
++void export_import_cred(gss_cred_id_t *cred);
++
+ /* Display name as canonicalized to mech, preceded by tag. */
+ void display_canon_name(const char *tag, gss_name_t name, gss_OID mech);
+ 
+diff --git a/src/tests/gssapi/t_export_cred.c b/src/tests/gssapi/t_export_cred.c
+index 5214cd5..4d7c028 100644
+--- a/src/tests/gssapi/t_export_cred.c
++++ b/src/tests/gssapi/t_export_cred.c
+@@ -37,22 +37,6 @@ usage(void)
+     exit(1);
+ }
+ 
+-/* Export *cred to a token, then release *cred and replace it by re-importing
+- * the token. */
+-static void
+-export_import_cred(gss_cred_id_t *cred)
+-{
+-    OM_uint32 major, minor;
+-    gss_buffer_desc buf;
+-
+-    major = gss_export_cred(&minor, *cred, &buf);
+-    check_gsserr("gss_export_cred", major, minor);
+-    (void)gss_release_cred(&minor, cred);
+-    major = gss_import_cred(&minor, &buf, cred);
+-    check_gsserr("gss_import_cred", major, minor);
+-    (void)gss_release_buffer(&minor, &buf);
+-}
+-
+ int
+ main(int argc, char *argv[])
+ {
+diff --git a/src/tests/gssapi/t_export_cred.py b/src/tests/gssapi/t_export_cred.py
+index 53dd13c..6988359 100644
+--- a/src/tests/gssapi/t_export_cred.py
++++ b/src/tests/gssapi/t_export_cred.py
+@@ -1,7 +1,10 @@
+ #!/usr/bin/python
+ from k5test import *
+ 
+-# Test gss_export_cred and gss_import_cred.
++# Test gss_export_cred and gss_import_cred for initiator creds,
++# acceptor creds, and traditional delegated creds.  t_s4u.py tests
++# exporting and importing a synthesized S4U2Proxy delegated
++# credential.
+ 
+ # Make up a filename to hold user's initial credentials.
+ def ccache_savefile(realm):
+diff --git a/src/tests/gssapi/t_s4u2proxy_krb5.c b/src/tests/gssapi/t_s4u2proxy_krb5.c
+index 3ad1086..483d915 100644
+--- a/src/tests/gssapi/t_s4u2proxy_krb5.c
++++ b/src/tests/gssapi/t_s4u2proxy_krb5.c
+@@ -117,6 +117,10 @@ main(int argc, char *argv[])
+         goto cleanup;
+     }
+ 
++    /* Take the opportunity to test cred export/import on the synthesized
++     * S4U2Proxy delegated cred. */
++    export_import_cred(&deleg_cred);
++
+     /* Store the delegated credentials. */
+     ret = krb5_cc_resolve(context, storage_ccname, &storage_ccache);
+     check_k5err(context, "krb5_cc_resolve", ret);
diff --git a/SOURCES/krb5-1.11.3-prompter1.patch b/SOURCES/krb5-1.11.3-prompter1.patch
new file mode 100644
index 0000000..e8d393d
--- /dev/null
+++ b/SOURCES/krb5-1.11.3-prompter1.patch
@@ -0,0 +1,91 @@
+commit a8eec52a13ba108b8855aef8cf9dafeb37811d2e
+Author: Nalin Dahyabhai <nalin@redhat.com>
+Date:   Fri Mar 15 12:05:56 2013 -0400
+
+    Add PEM password prompter callback in PKINIT
+    
+    Supply a callack to PEM_read_bio_PrivateKey() using the prompter to
+    request a password for encrypted PEM data.  Otherwise OpenSSL will use
+    the controlling terminal.
+    
+    [ghudson@mit.edu: minor style cleanup, commit message]
+    
+    ticket: 7590
+
+diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+index 6dbda9b..7186ce8 100644
+--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
++++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+@@ -656,11 +656,50 @@ cleanup:
+     return retval;
+ }
+ 
++struct get_key_cb_data {
++    krb5_context context;
++    pkinit_identity_crypto_context id_cryptoctx;
++    char *filename;
++};
++
++static int
++get_key_cb(char *buf, int size, int rwflag, void *userdata)
++{
++    struct get_key_cb_data *data = userdata;
++    pkinit_identity_crypto_context id_cryptoctx;
++    krb5_data rdat;
++    krb5_prompt kprompt;
++    krb5_prompt_type prompt_type;
++    krb5_error_code retval;
++    char *prompt;
++
++    if (asprintf(&prompt, "%s %s", _("Pass phrase for"), data->filename) < 0)
++        return -1;
++    rdat.data = buf;
++    rdat.length = size;
++    kprompt.prompt = prompt;
++    kprompt.hidden = 1;
++    kprompt.reply = &rdat;
++    prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
++
++    /* PROMPTER_INVOCATION */
++    k5int_set_prompt_types(data->context, &prompt_type);
++    id_cryptoctx = data->id_cryptoctx;
++    retval = data->id_cryptoctx->prompter(data->context,
++                                          id_cryptoctx->prompter_data, NULL,
++                                          NULL, 1, &kprompt);
++    k5int_set_prompt_types(data->context, 0);
++    free(prompt);
++    return retval ? -1 : (int)rdat.length;
++}
++
+ static krb5_error_code
+-get_key(char *filename, EVP_PKEY **retkey)
++get_key(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
++        char *filename, EVP_PKEY **retkey)
+ {
+     EVP_PKEY *pkey = NULL;
+     BIO *tmp = NULL;
++    struct get_key_cb_data cb_data;
+     int code;
+     krb5_error_code retval;
+ 
+@@ -676,7 +715,10 @@ get_key(char *filename, EVP_PKEY **retkey)
+         retval = errno;
+         goto cleanup;
+     }
+-    pkey = (EVP_PKEY *) PEM_read_bio_PrivateKey(tmp, NULL, NULL, NULL);
++    cb_data.context = context;
++    cb_data.id_cryptoctx = id_cryptoctx;
++    cb_data.filename = filename;
++    pkey = PEM_read_bio_PrivateKey(tmp, NULL, get_key_cb, &cb_data);
+     if (pkey == NULL) {
+         retval = EIO;
+         pkiDebug("failed to read private key from %s\n", filename);
+@@ -4333,7 +4375,7 @@ pkinit_load_fs_cert_and_key(krb5_context context,
+         pkiDebug("failed to load user's certificate from '%s'\n", certname);
+         goto cleanup;
+     }
+-    retval = get_key(keyname, &y);
++    retval = get_key(context, id_cryptoctx, keyname, &y);
+     if (retval != 0 || y == NULL) {
+         pkiDebug("failed to load user's private key from '%s'\n", keyname);
+         goto cleanup;
diff --git a/SOURCES/krb5-1.11.3-prompter2.patch b/SOURCES/krb5-1.11.3-prompter2.patch
new file mode 100644
index 0000000..015284d
--- /dev/null
+++ b/SOURCES/krb5-1.11.3-prompter2.patch
@@ -0,0 +1,55 @@
+Don't call a prompter function if it's NULL, as it can be, depending on
+which code path we were called from.  Part of the larger responder retrofit
+coming in 1.12 (RT#7680).
+
+--- krb5-1.11.3/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
++++ krb5-1.11.3/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+@@ -673,6 +673,8 @@ get_key_cb(char *buf, int size, int rwfl
+     krb5_error_code retval;
+     char *prompt;
+ 
++    if (data->id_cryptoctx->prompter == NULL)
++        return -1;
+     if (asprintf(&prompt, "%s %s", _("Pass phrase for"), data->filename) < 0)
+         return -1;
+     rdat.data = buf;
+@@ -3739,10 +3741,15 @@ pkinit_login(krb5_context context,
+         prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
+ 
+         /* PROMPTER_INVOCATION */
+-        k5int_set_prompt_types(context, &prompt_type);
+-        r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,
+-                                      NULL, NULL, 1, &kprompt);
+-        k5int_set_prompt_types(context, 0);
++        if (id_cryptoctx->prompter == NULL) {
++            r = KRB5_LIBOS_CANTREADPWD;
++            rdat.data = NULL;
++        } else {
++            k5int_set_prompt_types(context, &prompt_type);
++            r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,
++                                          NULL, NULL, 1, &kprompt);
++            k5int_set_prompt_types(context, 0);
++        }
+         free(prompt);
+     }
+ 
+@@ -4307,10 +4314,15 @@ pkinit_get_certs_pkcs12(krb5_context con
+         prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
+ 
+         /* PROMPTER_INVOCATION */
+-        k5int_set_prompt_types(context, &prompt_type);
+-        r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,
+-                                      NULL, NULL, 1, &kprompt);
+-        k5int_set_prompt_types(context, 0);
++        if (*id_cryptoctx->prompter == NULL) {
++            retval = KRB5_LIBOS_CANTREADPWD;
++            goto cleanup;
++        } else {
++            k5int_set_prompt_types(context, &prompt_type);
++            r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,
++                                          NULL, NULL, 1, &kprompt);
++            k5int_set_prompt_types(context, 0);
++        }
+ 
+         ret = PKCS12_parse(p12, rdat.data, &y, &x, NULL);
+         if (ret == 0) {
diff --git a/SOURCES/krb5-1.11.3-skew3.patch b/SOURCES/krb5-1.11.3-skew3.patch
new file mode 100644
index 0000000..0fe0b28
--- /dev/null
+++ b/SOURCES/krb5-1.11.3-skew3.patch
@@ -0,0 +1,28 @@
+commit 3b1b31a57cd932eda928932e67f5f2857929f429
+Author: Greg Hudson <ghudson@mit.edu>
+Date:   Sun Jun 2 15:36:40 2013 -0400
+
+    Fix spurious clock skew caused by preauth delay
+    
+    Commit 37b0e55e21926c7875b7176e24e13005920915a6 (#7063) prevented
+    clock skew caused by preauth delay by recording the time of the
+    initial request.  However, it failed to take into account delay
+    between requests due to prompting during preauthentication.  Fix this
+    by recording the request time for each request.
+    
+    ticket: 7656 (new)
+
+diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
+index ff455d3..0dd497e 100644
+--- a/src/lib/krb5/krb/get_in_tkt.c
++++ b/src/lib/krb5/krb/get_in_tkt.c
+@@ -1256,6 +1256,9 @@ init_creds_step_request(krb5_context context,
+         }
+     }
+ 
++    /* Remember when we sent this request (after any preauth delay). */
++    ctx->request_time = time(NULL);
++
+     if (ctx->encoded_previous_request != NULL) {
+         krb5_free_data(context, ctx->encoded_previous_request);
+         ctx->encoded_previous_request = NULL;
diff --git a/SOURCES/krb5-1.3.1-dns.patch b/SOURCES/krb5-1.3.1-dns.patch
new file mode 100644
index 0000000..5d27689
--- /dev/null
+++ b/SOURCES/krb5-1.3.1-dns.patch
@@ -0,0 +1,12 @@
+We want to be able to use --with-netlib and --enable-dns at the same time.
+RT#2022
+--- krb5-1.3.1/src/aclocal.m4	2003-11-24 11:17:30.000000000 -0500
++++ krb5-1.3.1/src/aclocal.m4	2003-11-24 11:18:45.000000000 -0500
+@@ -647,6 +647,7 @@
+ 	LIBS="$LIBS $withval"
+ 	AC_MSG_RESULT("netlib will use \'$withval\'")
+   fi
++  KRB5_AC_ENABLE_DNS
+ ],dnl
+ [AC_LIBRARY_NET]
+ )])dnl
diff --git a/SOURCES/krb5-1.3.4-send-pr-tempfile.patch b/SOURCES/krb5-1.3.4-send-pr-tempfile.patch
new file mode 100644
index 0000000..a9ffa31
--- /dev/null
+++ b/SOURCES/krb5-1.3.4-send-pr-tempfile.patch
@@ -0,0 +1,41 @@
+Use mktemp to create our temporary files instead of basing them on our PID.
+Only portable if you assume the presence of a mktemp helper.
+diff -ur krb5-1.3.4/src/util/send-pr/send-pr.sh krb5-1.3.4/src/util/send-pr/send-pr.sh
+--- krb5-1.3.4/src/util/send-pr/send-pr.sh	1997-03-20 01:13:56.000000000 +0100
++++ krb5-1.3.4/src/util/send-pr/send-pr.sh	2004-09-20 11:28:56.000000000 +0200
+@@ -96,9 +96,9 @@
+   fi
+ fi
+ 
+-TEMP=$TMPDIR/p$$
+-BAD=$TMPDIR/pbad$$
+-REF=$TMPDIR/pf$$
++TEMP=`mktemp "$TMPDIR"/p.XXXXXX` || exit 1
++BAD=`mktemp "$TMPDIR"/pbad.XXXXXX` || exit 1
++REF=`mktemp "$TMPDIR"/pf.XXXXXX` || exit 1
+ 
+ # find a user name
+ if [ "$LOGNAME" = "" ]; then
+@@ -122,9 +122,10 @@
+ else
+   # Must use temp file due to incompatibilities in quoting behavior
+   # and to protect shell metacharacters in the expansion of $LOGNAME
+-  $PASSWD | grep "^$LOGNAME:" | awk -F: '{print $5}' | sed -e 's/,.*//' > $TEMP
+-  ORIGINATOR="`cat $TEMP`"
+-  rm -f $TEMP
++  TEMP2=`mktemp "$TMPDIR"/plogname.XXXXXX` || exit 1
++  $PASSWD | grep "^$LOGNAME:" | awk -F: '{print $5}' | sed -e 's/,.*//' > $TEMP2
++  ORIGINATOR="`cat $TEMP2`"
++  rm -f $TEMP2
+ fi
+ 
+ if [ -n "$ORGANIZATION" ]; then
+@@ -280,7 +281,7 @@
+ # Catch some signals. ($xs kludge needed by Sun /bin/sh)
+ xs=0
+ trap 'rm -f $REF $TEMP; exit $xs' 0
+-trap 'echo "$COMMAND: Aborting ..."; rm -f $REF $TEMP; xs=1; exit' 1 2 3 13 15
++trap 'echo "$COMMAND: Aborting ..."; rm -f "$REF" "$BAD" "$TEMP"; xs=1; exit' 1 2 3 13 15
+ 
+ # If they told us to use a specific file, then do so.
+ if [ -n "$IN_FILE" ]; then
diff --git a/SOURCES/krb5-1.7-ktany.patch b/SOURCES/krb5-1.7-ktany.patch
new file mode 100644
index 0000000..902f328
--- /dev/null
+++ b/SOURCES/krb5-1.7-ktany.patch
@@ -0,0 +1,351 @@
+Adds an "ANY" keytab type which is a list of other keytab locations to search
+when searching for a specific entry.  When iterated through, it only presents
+the contents of the first keytab.
+
+diff -up /dev/null krb5-1.7/src/lib/krb5/keytab/kt_any.c
+--- /dev/null	2009-06-04 10:34:55.169007373 -0400
++++ krb5-1.7/src/lib/krb5/keytab/kt_any.c	2009-06-04 13:54:36.000000000 -0400
+@@ -0,0 +1,292 @@
++/*
++ * lib/krb5/keytab/kt_any.c
++ *
++ * Copyright 1998, 1999 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.
++ * 
++ * 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.  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.
++ * 
++ *
++ * krb5_kta_ops
++ */
++
++#include "k5-int.h"
++
++typedef struct _krb5_ktany_data {
++    char *name;
++    krb5_keytab *choices;
++    int nchoices;
++} krb5_ktany_data;
++
++typedef struct _krb5_ktany_cursor_data {
++    int which;
++    krb5_kt_cursor cursor;
++} krb5_ktany_cursor_data;
++
++static krb5_error_code krb5_ktany_resolve
++	          (krb5_context,
++		   const char *,
++		   krb5_keytab *);
++static krb5_error_code krb5_ktany_get_name
++	          (krb5_context context,
++		   krb5_keytab id,
++		   char *name,
++		   unsigned int len);
++static krb5_error_code krb5_ktany_close
++	          (krb5_context context,
++		   krb5_keytab id);
++static krb5_error_code krb5_ktany_get_entry
++	          (krb5_context context,
++		   krb5_keytab id,
++		   krb5_const_principal principal,
++		   krb5_kvno kvno,
++		   krb5_enctype enctype,
++		   krb5_keytab_entry *entry);
++static krb5_error_code krb5_ktany_start_seq_get
++	          (krb5_context context,
++		   krb5_keytab id,
++		   krb5_kt_cursor *cursorp);
++static krb5_error_code krb5_ktany_next_entry
++	          (krb5_context context,
++		   krb5_keytab id,
++		   krb5_keytab_entry *entry,
++		   krb5_kt_cursor *cursor);
++static krb5_error_code krb5_ktany_end_seq_get
++	          (krb5_context context,
++		   krb5_keytab id,
++		   krb5_kt_cursor *cursor);
++static void cleanup
++	          (krb5_context context,
++		   krb5_ktany_data *data,
++		   int nchoices);
++
++struct _krb5_kt_ops krb5_kta_ops = {
++    0,
++    "ANY", 	/* Prefix -- this string should not appear anywhere else! */
++    krb5_ktany_resolve,
++    krb5_ktany_get_name,
++    krb5_ktany_close,
++    krb5_ktany_get_entry,
++    krb5_ktany_start_seq_get,
++    krb5_ktany_next_entry,
++    krb5_ktany_end_seq_get,
++    NULL,
++    NULL,
++    NULL,
++};
++
++static krb5_error_code
++krb5_ktany_resolve(context, name, id)
++    krb5_context context;
++    const char *name;
++    krb5_keytab *id;
++{
++    const char *p, *q;
++    char *copy;
++    krb5_error_code kerror;
++    krb5_ktany_data *data;
++    int i;
++
++    /* Allocate space for our data and remember a copy of the name. */
++    if ((data = (krb5_ktany_data *)malloc(sizeof(krb5_ktany_data))) == NULL)
++	return(ENOMEM);
++    if ((data->name = (char *)malloc(strlen(name) + 1)) == NULL) {
++	krb5_xfree(data);
++	return(ENOMEM);
++    }
++    strcpy(data->name, name);
++
++    /* Count the number of choices and allocate memory for them. */
++    data->nchoices = 1;
++    for (p = name; (q = strchr(p, ',')) != NULL; p = q + 1)
++	data->nchoices++;
++    if ((data->choices = (krb5_keytab *)
++	 malloc(data->nchoices * sizeof(krb5_keytab))) == NULL) {
++	krb5_xfree(data->name);
++	krb5_xfree(data);
++	return(ENOMEM);
++    }
++
++    /* Resolve each of the choices. */
++    i = 0;
++    for (p = name; (q = strchr(p, ',')) != NULL; p = q + 1) {
++	/* Make a copy of the choice name so we can terminate it. */
++	if ((copy = (char *)malloc(q - p + 1)) == NULL) {
++	    cleanup(context, data, i);
++	    return(ENOMEM);
++	}
++	memcpy(copy, p, q - p);
++	copy[q - p] = 0;
++
++	/* Try resolving the choice name. */
++	kerror = krb5_kt_resolve(context, copy, &data->choices[i]);
++	krb5_xfree(copy);
++	if (kerror) {
++	    cleanup(context, data, i);
++	    return(kerror);
++	}
++	i++;
++    }
++    if ((kerror = krb5_kt_resolve(context, p, &data->choices[i]))) {
++	cleanup(context, data, i);
++	return(kerror);
++    }
++
++    /* Allocate and fill in an ID for the caller. */
++    if ((*id = (krb5_keytab)malloc(sizeof(**id))) == NULL) {
++	cleanup(context, data, i);
++	return(ENOMEM);
++    }
++    (*id)->ops = &krb5_kta_ops;
++    (*id)->data = (krb5_pointer)data;
++    (*id)->magic = KV5M_KEYTAB;
++
++    return(0);
++}
++
++static krb5_error_code
++krb5_ktany_get_name(context, id, name, len)
++    krb5_context context;
++    krb5_keytab id;
++    char *name;
++    unsigned int len;
++{
++    krb5_ktany_data *data = (krb5_ktany_data *)id->data;
++
++    if (len < strlen(data->name) + 1)
++	return(KRB5_KT_NAME_TOOLONG);
++    strcpy(name, data->name);
++    return(0);
++}
++
++static krb5_error_code
++krb5_ktany_close(context, id)
++    krb5_context context;
++    krb5_keytab id;
++{
++    krb5_ktany_data *data = (krb5_ktany_data *)id->data;
++
++    cleanup(context, data, data->nchoices);
++    id->ops = 0;
++    krb5_xfree(id);
++    return(0);
++}
++
++static krb5_error_code
++krb5_ktany_get_entry(context, id, principal, kvno, enctype, entry)
++    krb5_context context;
++    krb5_keytab id;
++    krb5_const_principal principal;
++    krb5_kvno kvno;
++    krb5_enctype enctype;
++    krb5_keytab_entry *entry;
++{
++    krb5_ktany_data *data = (krb5_ktany_data *)id->data;
++    krb5_error_code kerror = KRB5_KT_NOTFOUND;
++    int i;
++
++    for (i = 0; i < data->nchoices; i++) {
++	if ((kerror = krb5_kt_get_entry(context, data->choices[i], principal,
++					kvno, enctype, entry)) != ENOENT)
++	    return kerror;
++    }
++    return kerror;
++}
++
++static krb5_error_code
++krb5_ktany_start_seq_get(context, id, cursorp)
++    krb5_context context;
++    krb5_keytab id;
++    krb5_kt_cursor *cursorp;
++{
++    krb5_ktany_data *data = (krb5_ktany_data *)id->data;
++    krb5_ktany_cursor_data *cdata;
++    krb5_error_code kerror = ENOENT;
++    int i;
++
++    if ((cdata = (krb5_ktany_cursor_data *)
++	 malloc(sizeof(krb5_ktany_cursor_data))) == NULL)
++	return(ENOMEM);
++
++    /* Find a choice which can handle the serialization request. */
++    for (i = 0; i < data->nchoices; i++) {
++	if ((kerror = krb5_kt_start_seq_get(context, data->choices[i],
++					    &cdata->cursor)) == 0)
++	    break;
++	else if (kerror != ENOENT) {
++	    krb5_xfree(cdata);
++	    return(kerror);
++	}
++    }
++
++    if (i == data->nchoices) {
++	/* Everyone returned ENOENT, so no go. */
++	krb5_xfree(cdata);
++	return(kerror);
++    }
++
++    cdata->which = i;
++    *cursorp = (krb5_kt_cursor)cdata;
++    return(0);
++}
++
++static krb5_error_code
++krb5_ktany_next_entry(context, id, entry, cursor)
++    krb5_context context;
++    krb5_keytab id;
++    krb5_keytab_entry *entry;
++    krb5_kt_cursor *cursor;
++{
++    krb5_ktany_data *data = (krb5_ktany_data *)id->data;
++    krb5_ktany_cursor_data *cdata = (krb5_ktany_cursor_data *)*cursor;
++    krb5_keytab choice_id;
++
++    choice_id = data->choices[cdata->which];
++    return(krb5_kt_next_entry(context, choice_id, entry, &cdata->cursor));
++}
++
++static krb5_error_code
++krb5_ktany_end_seq_get(context, id, cursor)
++    krb5_context context;
++    krb5_keytab id;
++    krb5_kt_cursor *cursor;
++{
++    krb5_ktany_data *data = (krb5_ktany_data *)id->data;
++    krb5_ktany_cursor_data *cdata = (krb5_ktany_cursor_data *)*cursor;
++    krb5_keytab choice_id;
++    krb5_error_code kerror;
++
++    choice_id = data->choices[cdata->which];
++    kerror = krb5_kt_end_seq_get(context, choice_id, &cdata->cursor);
++    krb5_xfree(cdata);
++    return(kerror);
++}
++
++static void
++cleanup(context, data, nchoices)
++    krb5_context context;
++    krb5_ktany_data *data;
++    int nchoices;
++{
++    int i;
++
++    krb5_xfree(data->name);
++    for (i = 0; i < nchoices; i++)
++	krb5_kt_close(context, data->choices[i]);
++    krb5_xfree(data->choices);
++    krb5_xfree(data);
++}
+diff -up krb5-1.7/src/lib/krb5/keytab/ktbase.c krb5-1.7/src/lib/krb5/keytab/ktbase.c
+--- krb5-1.7/src/lib/krb5/keytab/ktbase.c	2009-02-18 13:18:56.000000000 -0500
++++ krb5-1.7/src/lib/krb5/keytab/ktbase.c	2009-06-04 13:54:36.000000000 -0400
+@@ -59,14 +59,19 @@ extern const krb5_kt_ops krb5_ktf_ops;
+ extern const krb5_kt_ops krb5_ktf_writable_ops;
+ extern const krb5_kt_ops krb5_kts_ops;
+ extern const krb5_kt_ops krb5_mkt_ops;
++extern const krb5_kt_ops krb5_kta_ops;
+ 
+ struct krb5_kt_typelist {
+     const krb5_kt_ops *ops;
+     const struct krb5_kt_typelist *next;
+ };
++static struct krb5_kt_typelist krb5_kt_typelist_any = {
++    &krb5_kta_ops,
++    NULL
++};
+ const static struct krb5_kt_typelist krb5_kt_typelist_srvtab = {
+     &krb5_kts_ops,
+-    NULL
++    &krb5_kt_typelist_any
+ };
+ const static struct krb5_kt_typelist krb5_kt_typelist_memory = {
+     &krb5_mkt_ops,
+diff -up krb5-1.7/src/lib/krb5/keytab/Makefile.in krb5-1.7/src/lib/krb5/keytab/Makefile.in
+--- krb5-1.7/src/lib/krb5/keytab/Makefile.in	2009-01-05 15:27:53.000000000 -0500
++++ krb5-1.7/src/lib/krb5/keytab/Makefile.in	2009-06-04 13:54:36.000000000 -0400
+@@ -19,6 +19,7 @@ STLIBOBJS= \
+ 	ktfr_entry.o	\
+ 	ktremove.o	\
+ 	ktfns.o		\
++	kt_any.o	\
+ 	kt_file.o	\
+ 	kt_memory.o	\
+ 	kt_srvtab.o	\
+@@ -31,6 +32,7 @@ OBJS=	\
+ 	$(OUTPRE)ktfr_entry.$(OBJEXT)	\
+ 	$(OUTPRE)ktremove.$(OBJEXT)	\
+ 	$(OUTPRE)ktfns.$(OBJEXT)	\
++	$(OUTPRE)kt_any.$(OBJEXT)	\
+ 	$(OUTPRE)kt_file.$(OBJEXT)	\
+ 	$(OUTPRE)kt_memory.$(OBJEXT)	\
+ 	$(OUTPRE)kt_srvtab.$(OBJEXT)	\
+@@ -43,6 +45,7 @@ SRCS=	\
+ 	$(srcdir)/ktfr_entry.c	\
+ 	$(srcdir)/ktremove.c	\
+ 	$(srcdir)/ktfns.c	\
++	$(srcdir)/kt_any.c	\
+ 	$(srcdir)/kt_file.c	\
+ 	$(srcdir)/kt_memory.c	\
+ 	$(srcdir)/kt_srvtab.c	\
diff --git a/SOURCES/krb5-1.8-api.patch b/SOURCES/krb5-1.8-api.patch
new file mode 100644
index 0000000..9cc9cd2
--- /dev/null
+++ b/SOURCES/krb5-1.8-api.patch
@@ -0,0 +1,30 @@
+Reference docs don't define what happens if you call krb5_realm_compare() with
+malformed krb5_principal structures.  Define a behavior which keeps it from
+crashing if applications don't check ahead of time.
+
+diff -up krb5-1.8/src/lib/krb5/krb/princ_comp.c.api krb5-1.8/src/lib/krb5/krb/princ_comp.c
+--- krb5-1.8/src/lib/krb5/krb/princ_comp.c.api	2009-10-30 20:48:38.000000000 -0400
++++ krb5-1.8/src/lib/krb5/krb/princ_comp.c	2010-03-05 11:00:55.000000000 -0500
+@@ -41,6 +41,12 @@ realm_compare_flags(krb5_context context
+     const krb5_data *realm1 = krb5_princ_realm(context, princ1);
+     const krb5_data *realm2 = krb5_princ_realm(context, princ2);
+ 
++    if ((princ1 == NULL) || (princ2 == NULL))
++        return FALSE;
++
++    if ((realm1 == NULL) || (realm2 == NULL))
++        return FALSE;
++
+     if (realm1->length != realm2->length)
+         return FALSE;
+ 
+@@ -92,6 +98,9 @@ krb5_principal_compare_flags(krb5_contex
+     krb5_principal upn2 = NULL;
+     krb5_boolean ret = FALSE;
+ 
++    if ((princ1 == NULL) || (princ2 == NULL))
++        return FALSE;
++
+     if (flags & KRB5_PRINCIPAL_COMPARE_ENTERPRISE) {
+         /* Treat UPNs as if they were real principals */
+         if (krb5_princ_type(context, princ1) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
diff --git a/SOURCES/krb5-1.9-debuginfo.patch b/SOURCES/krb5-1.9-debuginfo.patch
new file mode 100644
index 0000000..ae81f7c
--- /dev/null
+++ b/SOURCES/krb5-1.9-debuginfo.patch
@@ -0,0 +1,26 @@
+We want to keep these y.tab.c files around because the debuginfo points to
+them.  It would be more elegant at the end to use symbolic links, but that
+could mess up people working in the tree on other things.
+
+--- src/kadmin/cli/Makefile.in
++++ src/kadmin/cli/Makefile.in
+@@ -43,3 +43,8 @@ clean-unix::
+ # CC_LINK is not meant for compilation and this use may break in the future.
+ datetest: getdate.c
+ 	$(CC_LINK) $(ALL_CFLAGS) -DTEST -o datetest getdate.c
++
++%.c: %.y
++	$(RM) y.tab.c $@
++	$(YACC.y) $< 
++	$(CP) y.tab.c $@
+--- src/plugins/kdb/ldap/ldap_util/Makefile.in
++++ src/plugins/kdb/ldap/ldap_util/Makefile.in
+@@ -22,7 +22,7 @@ $(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KR
+ getdate.c: $(GETDATE)
+ 	$(RM) getdate.c y.tab.c
+ 	$(YACC) $(GETDATE)
+-	$(MV) y.tab.c getdate.c
++	$(CP) y.tab.c getdate.c
+ 
+ install::
+ 	$(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
diff --git a/SOURCES/krb5-aarch64.patch b/SOURCES/krb5-aarch64.patch
new file mode 100644
index 0000000..fd8649e
--- /dev/null
+++ b/SOURCES/krb5-aarch64.patch
@@ -0,0 +1,411 @@
+diff -urN krb5-1.11.1/src/config/config.guess krb5-1.11.1-aarch64/src/config/config.guess
+--- krb5-1.11.1/src/config/config.guess	2013-02-21 17:23:19.000000000 -0600
++++ krb5-1.11.1-aarch64/src/config/config.guess	2013-03-08 01:18:32.742187563 -0600
+@@ -2,9 +2,9 @@
+ # Attempt to guess a canonical system name.
+ #   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ #   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+-#   2011 Free Software Foundation, Inc.
++#   2011, 2012 Free Software Foundation, Inc.
+ 
+-timestamp='2011-08-20'
++timestamp='2012-09-25'
+ 
+ # This file is free software; you can redistribute it and/or modify it
+ # under the terms of the GNU General Public License as published by
+@@ -17,9 +17,7 @@
+ # General Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License
+-# along with this program; if not, write to the Free Software
+-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+-# 02110-1301, USA.
++# along with this program; if not, see <http://www.gnu.org/licenses/>.
+ #
+ # As a special exception to the GNU General Public License, if you
+ # distribute this file as part of a program that contains a
+@@ -57,8 +55,8 @@
+ 
+ Originally written by Per Bothner.
+ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free
+-Software Foundation, Inc.
++2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
++Free Software Foundation, Inc.
+ 
+ This is free software; see the source for copying conditions.  There is NO
+ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+@@ -145,7 +143,7 @@
+ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+     *:NetBSD:*:*)
+ 	# NetBSD (nbsd) targets should (where applicable) match one or
+-	# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
++	# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+ 	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+ 	# switched to ELF, *-*-netbsd* would select the old
+ 	# object file format.  This provides both forward
+@@ -202,6 +200,10 @@
+ 	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ 	echo "${machine}-${os}${release}"
+ 	exit ;;
++    *:Bitrig:*:*)
++	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
++	echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
++	exit ;;
+     *:OpenBSD:*:*)
+ 	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ 	echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+@@ -304,7 +306,7 @@
+     arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ 	echo arm-acorn-riscix${UNAME_RELEASE}
+ 	exit ;;
+-    arm:riscos:*:*|arm:RISCOS:*:*)
++    arm*:riscos:*:*|arm*:RISCOS:*:*)
+ 	echo arm-unknown-riscos
+ 	exit ;;
+     SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+@@ -803,9 +805,15 @@
+     i*:CYGWIN*:*)
+ 	echo ${UNAME_MACHINE}-pc-cygwin
+ 	exit ;;
++    *:MINGW64*:*)
++	echo ${UNAME_MACHINE}-pc-mingw64
++	exit ;;
+     *:MINGW*:*)
+ 	echo ${UNAME_MACHINE}-pc-mingw32
+ 	exit ;;
++    i*:MSYS*:*)
++	echo ${UNAME_MACHINE}-pc-msys
++	exit ;;
+     i*:windows32*:*)
+ 	# uname -m includes "-pc" on this system.
+ 	echo ${UNAME_MACHINE}-mingw32
+@@ -860,6 +868,13 @@
+     i*86:Minix:*:*)
+ 	echo ${UNAME_MACHINE}-pc-minix
+ 	exit ;;
++    aarch64:Linux:*:*)
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
++	exit ;;
++    aarch64_be:Linux:*:*)
++	UNAME_MACHINE=aarch64_be
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
++	exit ;;
+     alpha:Linux:*:*)
+ 	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ 	  EV5)   UNAME_MACHINE=alphaev5 ;;
+@@ -894,13 +909,16 @@
+ 	echo ${UNAME_MACHINE}-unknown-linux-gnu
+ 	exit ;;
+     cris:Linux:*:*)
+-	echo cris-axis-linux-gnu
++	echo ${UNAME_MACHINE}-axis-linux-gnu
+ 	exit ;;
+     crisv32:Linux:*:*)
+-	echo crisv32-axis-linux-gnu
++	echo ${UNAME_MACHINE}-axis-linux-gnu
+ 	exit ;;
+     frv:Linux:*:*)
+-	echo frv-unknown-linux-gnu
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
++	exit ;;
++    hexagon:Linux:*:*)
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
+ 	exit ;;
+     i*86:Linux:*:*)
+ 	LIBC=gnu
+@@ -942,7 +960,7 @@
+ 	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ 	;;
+     or32:Linux:*:*)
+-	echo or32-unknown-linux-gnu
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
+ 	exit ;;
+     padre:Linux:*:*)
+ 	echo sparc-unknown-linux-gnu
+@@ -983,7 +1001,7 @@
+ 	echo ${UNAME_MACHINE}-dec-linux-gnu
+ 	exit ;;
+     x86_64:Linux:*:*)
+-	echo x86_64-unknown-linux-gnu
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
+ 	exit ;;
+     xtensa*:Linux:*:*)
+ 	echo ${UNAME_MACHINE}-unknown-linux-gnu
+@@ -1190,6 +1208,9 @@
+     BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
+ 	echo i586-pc-haiku
+ 	exit ;;
++    x86_64:Haiku:*:*)
++	echo x86_64-unknown-haiku
++	exit ;;
+     SX-4:SUPER-UX:*:*)
+ 	echo sx4-nec-superux${UNAME_RELEASE}
+ 	exit ;;
+@@ -1245,7 +1266,7 @@
+     NEO-?:NONSTOP_KERNEL:*:*)
+ 	echo neo-tandem-nsk${UNAME_RELEASE}
+ 	exit ;;
+-    NSE-?:NONSTOP_KERNEL:*:*)
++    NSE-*:NONSTOP_KERNEL:*:*)
+ 	echo nse-tandem-nsk${UNAME_RELEASE}
+ 	exit ;;
+     NSR-?:NONSTOP_KERNEL:*:*)
+@@ -1314,11 +1335,11 @@
+     i*86:AROS:*:*)
+ 	echo ${UNAME_MACHINE}-pc-aros
+ 	exit ;;
++    x86_64:VMkernel:*:*)
++	echo ${UNAME_MACHINE}-unknown-esx
++	exit ;;
+ esac
+ 
+-#echo '(No uname command or uname output not recognized.)' 1>&2
+-#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+-
+ eval $set_cc_for_build
+ cat >$dummy.c <<EOF
+ #ifdef _SEQUENT_
+diff -urN krb5-1.11.1/src/config/config.sub krb5-1.11.1-aarch64/src/config/config.sub
+--- krb5-1.11.1/src/config/config.sub	2013-02-21 17:23:19.000000000 -0600
++++ krb5-1.11.1-aarch64/src/config/config.sub	2013-03-08 01:18:32.751186528 -0600
+@@ -2,9 +2,9 @@
+ # Configuration validation subroutine script.
+ #   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ #   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+-#   2011 Free Software Foundation, Inc.
++#   2011, 2012 Free Software Foundation, Inc.
+ 
+-timestamp='2011-08-23'
++timestamp='2012-10-10'
+ 
+ # This file is (in principle) common to ALL GNU software.
+ # The presence of a machine in this file suggests that SOME GNU software
+@@ -21,9 +21,7 @@
+ # GNU General Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License
+-# along with this program; if not, write to the Free Software
+-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+-# 02110-1301, USA.
++# along with this program; if not, see <http://www.gnu.org/licenses/>.
+ #
+ # As a special exception to the GNU General Public License, if you
+ # distribute this file as part of a program that contains a
+@@ -76,8 +74,8 @@
+ GNU config.sub ($timestamp)
+ 
+ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free
+-Software Foundation, Inc.
++2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
++Free Software Foundation, Inc.
+ 
+ This is free software; see the source for copying conditions.  There is NO
+ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+@@ -125,13 +123,17 @@
+ maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+ case $maybe_os in
+   nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+-  linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
++  linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+   knetbsd*-gnu* | netbsd*-gnu* | \
+   kopensolaris*-gnu* | \
+   storm-chaos* | os2-emx* | rtmk-nova*)
+     os=-$maybe_os
+     basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+     ;;
++  android-linux)
++    os=-linux-android
++    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
++    ;;
+   *)
+     basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+     if [ $basic_machine != $1 ]
+@@ -154,7 +156,7 @@
+ 	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ 	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ 	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+-	-apple | -axis | -knuth | -cray | -microblaze)
++	-apple | -axis | -knuth | -cray | -microblaze*)
+ 		os=
+ 		basic_machine=$1
+ 		;;
+@@ -223,6 +225,12 @@
+ 	-isc*)
+ 		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ 		;;
++	-lynx*178)
++		os=-lynxos178
++		;;
++	-lynx*5)
++		os=-lynxos5
++		;;
+ 	-lynx*)
+ 		os=-lynxos
+ 		;;
+@@ -247,6 +255,7 @@
+ 	# Some are omitted here because they have special meanings below.
+ 	1750a | 580 \
+ 	| a29k \
++	| aarch64 | aarch64_be \
+ 	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ 	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ 	| am33_2.0 \
+@@ -255,14 +264,16 @@
+ 	| bfin \
+ 	| c4x | clipper \
+ 	| d10v | d30v | dlx | dsp16xx \
++	| epiphany \
+ 	| fido | fr30 | frv \
+ 	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
++	| hexagon \
+ 	| i370 | i860 | i960 | ia64 \
+ 	| ip2k | iq2000 \
+ 	| le32 | le64 \
+ 	| lm32 \
+ 	| m32c | m32r | m32rle | m68000 | m68k | m88k \
+-	| maxq | mb | microblaze | mcore | mep | metag \
++	| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
+ 	| mips | mipsbe | mipseb | mipsel | mipsle \
+ 	| mips16 \
+ 	| mips64 | mips64el \
+@@ -293,7 +304,7 @@
+ 	| pdp10 | pdp11 | pj | pjl \
+ 	| powerpc | powerpc64 | powerpc64le | powerpcle \
+ 	| pyramid \
+-	| rx \
++	| rl78 | rx \
+ 	| score \
+ 	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ 	| sh64 | sh64le \
+@@ -317,8 +328,7 @@
+ 	c6x)
+ 		basic_machine=tic6x-unknown
+ 		;;
+-	m6811 | m68hc11 | m6812 | m68hc12 | picochip)
+-		# Motorola 68HC11/12.
++	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
+ 		basic_machine=$basic_machine-unknown
+ 		os=-none
+ 		;;
+@@ -331,7 +341,10 @@
+ 	strongarm | thumb | xscale)
+ 		basic_machine=arm-unknown
+ 		;;
+-
++	xgate)
++		basic_machine=$basic_machine-unknown
++		os=-none
++		;;
+ 	xscaleeb)
+ 		basic_machine=armeb-unknown
+ 		;;
+@@ -354,6 +367,7 @@
+ 	# Recognize the basic CPU types with company name.
+ 	580-* \
+ 	| a29k-* \
++	| aarch64-* | aarch64_be-* \
+ 	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ 	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ 	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+@@ -368,13 +382,15 @@
+ 	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+ 	| h8300-* | h8500-* \
+ 	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
++	| hexagon-* \
+ 	| i*86-* | i860-* | i960-* | ia64-* \
+ 	| ip2k-* | iq2000-* \
+ 	| le32-* | le64-* \
+ 	| lm32-* \
+ 	| m32c-* | m32r-* | m32rle-* \
+ 	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+-	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
++	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
++	| microblaze-* | microblazeel-* \
+ 	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ 	| mips16-* \
+ 	| mips64-* | mips64el-* \
+@@ -404,7 +420,7 @@
+ 	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ 	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
+ 	| pyramid-* \
+-	| romp-* | rs6000-* | rx-* \
++	| rl78-* | romp-* | rs6000-* | rx-* \
+ 	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ 	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ 	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+@@ -716,7 +732,6 @@
+ 	i370-ibm* | ibm*)
+ 		basic_machine=i370-ibm
+ 		;;
+-# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+ 	i*86v32)
+ 		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ 		os=-sysv32
+@@ -774,9 +789,13 @@
+ 		basic_machine=ns32k-utek
+ 		os=-sysv
+ 		;;
+-	microblaze)
++	microblaze*)
+ 		basic_machine=microblaze-xilinx
+ 		;;
++	mingw64)
++		basic_machine=x86_64-pc
++		os=-mingw64
++		;;
+ 	mingw32)
+ 		basic_machine=i386-pc
+ 		os=-mingw32
+@@ -813,6 +832,10 @@
+ 	ms1-*)
+ 		basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+ 		;;
++	msys)
++		basic_machine=i386-pc
++		os=-msys
++		;;
+ 	mvs)
+ 		basic_machine=i370-ibm
+ 		os=-mvs
+@@ -1334,15 +1357,15 @@
+ 	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ 	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ 	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+-	      | -openbsd* | -solidbsd* \
++	      | -bitrig* | -openbsd* | -solidbsd* \
+ 	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ 	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ 	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ 	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ 	      | -chorusos* | -chorusrdb* | -cegcc* \
+-	      | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+-	      | -mingw32* | -linux-gnu* | -linux-android* \
+-	      | -linux-newlib* | -linux-uclibc* \
++	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
++	      | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
++	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \
+ 	      | -uxpv* | -beos* | -mpeix* | -udk* \
+ 	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ 	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+@@ -1525,6 +1548,9 @@
+ 	c4x-* | tic4x-*)
+ 		os=-coff
+ 		;;
++	hexagon-*)
++		os=-elf
++		;;
+ 	tic54x-*)
+ 		os=-coff
+ 		;;
+@@ -1552,9 +1578,6 @@
+ 		;;
+ 	m68000-sun)
+ 		os=-sunos3
+-		# This also exists in the configure program, but was not the
+-		# default.
+-		# os=-sunos4
+ 		;;
+ 	m68*-cisco)
+ 		os=-aout
diff --git a/SOURCES/krb5-cccol-primary.patch b/SOURCES/krb5-cccol-primary.patch
new file mode 100644
index 0000000..f64b1f0
--- /dev/null
+++ b/SOURCES/krb5-cccol-primary.patch
@@ -0,0 +1,85 @@
+commit b874882dc93e5ece4f7218617ed7942656985471
+Author: Greg Hudson <ghudson@mit.edu>
+Date:   Mon Apr 22 17:00:35 2013 -0400
+
+    Include default DIR::file ccache in collection
+    
+    If the context's default ccache name is a subsidiary file of a
+    directory collection, include that single cache in the cursor walk
+    over the DIR type.
+    
+    ticket: 7172
+
+diff --git a/src/lib/krb5/ccache/cc_dir.c b/src/lib/krb5/ccache/cc_dir.c
+index cee21ac..b8231ed 100644
+--- a/src/lib/krb5/ccache/cc_dir.c
++++ b/src/lib/krb5/ccache/cc_dir.c
+@@ -266,6 +266,28 @@ get_context_default_dir(krb5_context context, char **dirname_out)
+     return 0;
+ }
+ 
++/*
++ * If the default ccache name for context is a subsidiary file in a directory
++ * collection, set *subsidiary_out to the residual value.  Otherwise set
++ * *subsidiary_out to NULL.
++ */
++static krb5_error_code
++get_context_subsidiary_file(krb5_context context, char **subsidiary_out)
++{
++    const char *defname;
++    char *residual;
++
++    *subsidiary_out = NULL;
++    defname = krb5_cc_default_name(context);
++    if (defname == NULL || strncmp(defname, "DIR::", 5) != 0)
++        return 0;
++    residual = strdup(defname + 4);
++    if (residual == NULL)
++        return ENOMEM;
++    *subsidiary_out = residual;
++    return 0;
++}
++
+ static const char * KRB5_CALLCONV
+ dcc_get_name(krb5_context context, krb5_ccache cache)
+ {
+@@ -562,6 +584,18 @@ dcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor_out)
+ 
+     *cursor_out = NULL;
+ 
++    /* If the default cache is a subsidiary file, make a cursor with the
++     * specified file as the primary but with no directory collection. */
++    ret = get_context_subsidiary_file(context, &primary);
++    if (ret)
++        goto cleanup;
++    if (primary != NULL) {
++        ret = make_cursor(NULL, primary, NULL, cursor_out);
++        if (ret)
++            free(primary);
++        return ret;
++    }
++
+     /* Open the directory for the context's default cache. */
+     ret = get_context_default_dir(context, &dirname);
+     if (ret || dirname == NULL)
+@@ -607,16 +641,17 @@ dcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor,
+     struct stat sb;
+ 
+     *cache_out = NULL;
+-    if (data->dir == NULL)      /* Empty cursor */
+-        return 0;
+ 
+-    /* Return the primary cache if we haven't yet. */
++    /* Return the primary or specified subsidiary cache if we haven't yet. */
+     if (data->first) {
+         data->first = FALSE;
+         if (data->primary != NULL && stat(data->primary + 1, &sb) == 0)
+             return dcc_resolve(context, cache_out, data->primary);
+     }
+ 
++    if (data->dir == NULL)      /* No directory collection */
++        return 0;
++
+     /* Look for the next filename of the correct form, without repeating the
+      * primary cache. */
+     while ((ent = readdir(data->dir)) != NULL) {
diff --git a/SOURCES/krb5-kvno-230379.patch b/SOURCES/krb5-kvno-230379.patch
new file mode 100644
index 0000000..ea9b69f
--- /dev/null
+++ b/SOURCES/krb5-kvno-230379.patch
@@ -0,0 +1,53 @@
+From patch attached to http://krbdev.mit.edu/rt/Ticket/Display.html?id=3349,
+at http://krbdev.mit.edu/rt/Ticket/Attachment/23851/13214/kvno.diff, adjusted
+as needed to apply to 1.10.  FIXME: I'd like to better handle cases where we
+have a new key with the right version stored later in the keytab file.
+Currently, we're setting up to overlook that possibility.
+
+Note that this only affects the path taken when krb5_rd_rep() is passed a
+server principal name, as without a server principal name it already tries
+all of the keys it finds in the keytab, regardless of version numbers.
+
+Index: krb5/src/kadmin/ktutil/ktutil.c
+===================================================================
+--- krb5/src/kadmin/ktutil/ktutil.c	(revision 3367)
++++ krb5/src/kadmin/ktutil/ktutil.c	(working copy)
+@@ -155,7 +155,7 @@
+     char *princ = NULL;
+     char *enctype = NULL;
+     krb5_kvno kvno = 0;
+-    int use_pass = 0, use_key = 0, i;
++    int use_pass = 0, use_key = 0, use_kvno = 0, i;
+ 
+     for (i = 1; i < argc; i++) {
+         if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-p", 2)) {
+@@ -164,6 +164,7 @@
+         }
+         if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-k", 2)) {
+             kvno = (krb5_kvno) atoi(argv[++i]);
++            use_kvno++;
+             continue;
+         }
+         if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-e", 2)) {
+@@ -180,7 +181,7 @@
+         }
+     }
+ 
+-    if (argc != 8 || !(princ && kvno && enctype) || (use_pass+use_key != 1)) {
++    if (argc != 8 || !(princ && use_kvno && enctype) || (use_pass+use_key != 1)) {
+         fprintf(stderr, _("usage: %s (-key | -password) -p principal "
+                           "-k kvno -e enctype\n"), argv[0]);
+         return;
+Index: krb5/src/lib/krb5/keytab/kt_file.c
+===================================================================
+--- krb5/src/lib/krb5/keytab/kt_file.c	(revision 3367)
++++ krb5/src/lib/krb5/keytab/kt_file.c	(working copy)
+@@ -349,7 +349,7 @@
+                higher than that.  Short-term workaround: only compare
+                the low 8 bits.  */
+ 
+-            if (new_entry.vno == (kvno & 0xff)) {
++            if (new_entry.vno == (kvno & 0xff) || new_entry.vno == IGNORE_VNO) {
+                 krb5_kt_free_entry(context, &cur_entry);
+                 cur_entry = new_entry;
+                 break;
diff --git a/SOURCES/krb5-master-init_referral.patch b/SOURCES/krb5-master-init_referral.patch
new file mode 100644
index 0000000..ae3d3b8
--- /dev/null
+++ b/SOURCES/krb5-master-init_referral.patch
@@ -0,0 +1,47 @@
+commit a12a5ddb9b932061bad7b83df058c7c6e2e4b044
+Author: Greg Hudson <ghudson@mit.edu>
+Date:   Thu May 30 11:39:54 2013 -0400
+
+    Properly handle use_master in k5_init_creds_get
+    
+    If we make multiple requests in an initial creds exchange, the
+    krb5_sendto_kdc call in k5_init_creds_get may flip the use_master
+    value from 0 to 1 if it detects that the response was from a master
+    KDC.  Don't turn this into a requirement for future requests during
+    the same exchange, or we may have trouble following AS referrals.
+    Reported by Sumit Bose.
+    
+    ticket: 7650
+
+diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
+index 20bc689..ff455d3 100644
+--- a/src/lib/krb5/krb/get_in_tkt.c
++++ b/src/lib/krb5/krb/get_in_tkt.c
+@@ -521,7 +521,7 @@ k5_init_creds_get(krb5_context context, krb5_init_creds_context ctx,
+     krb5_data reply;
+     krb5_data realm;
+     unsigned int flags = 0;
+-    int tcp_only = 0;
++    int tcp_only = 0, master = *use_master;
+ 
+     request.length = 0;
+     request.data = NULL;
+@@ -545,8 +545,9 @@ k5_init_creds_get(krb5_context context, krb5_init_creds_context ctx,
+ 
+         krb5_free_data_contents(context, &reply);
+ 
++        master = *use_master;
+         code = krb5_sendto_kdc(context, &request, &realm,
+-                               &reply, use_master, tcp_only);
++                               &reply, &master, tcp_only);
+         if (code != 0)
+             break;
+ 
+@@ -558,6 +559,7 @@ k5_init_creds_get(krb5_context context, krb5_init_creds_context ctx,
+     krb5_free_data_contents(context, &reply);
+     krb5_free_data_contents(context, &realm);
+ 
++    *use_master = master;
+     return code;
+ }
+ 
diff --git a/SOURCES/krb5-master-kinit-cccol.patch b/SOURCES/krb5-master-kinit-cccol.patch
new file mode 100644
index 0000000..58542fd
--- /dev/null
+++ b/SOURCES/krb5-master-kinit-cccol.patch
@@ -0,0 +1,314 @@
+commit d7b94742daae85329067b126d0a4bc5b2ea7e4a0
+Author: Greg Hudson <ghudson@mit.edu>
+Date:   Thu Sep 26 05:38:46 2013 -0400
+
+    Improve kinit output credential cache selection
+    
+    If kinit chooses a client principal based on anything other than the
+    current default ccache's principal name, apply collection rules if
+    possible.  When applying collection rules, if we don't find an
+    existing cache for the client principal, use the default cache if it
+    is uninitialized, instead of creating a new one.
+    
+    ticket: 7689
+
+diff --git a/src/clients/kinit/kinit.c b/src/clients/kinit/kinit.c
+index 5ceede8..d9033ec 100644
+--- a/src/clients/kinit/kinit.c
++++ b/src/clients/kinit/kinit.c
+@@ -466,9 +466,12 @@ k5_begin(opts, k5)
+     struct k5_data* k5;
+ {
+     krb5_error_code code = 0;
++    int success = 0;
+     int flags = opts->enterprise ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0;
+-    krb5_ccache defcache;
+-    const char *deftype;
++    krb5_ccache defcache = NULL;
++    krb5_principal defcache_princ = NULL, princ;
++    const char *deftype = NULL;
++    char *defrealm, *name;
+ 
+     code = krb5_init_context(&k5->ctx);
+     if (code) {
+@@ -477,73 +480,153 @@ k5_begin(opts, k5)
+     }
+     errctx = k5->ctx;
+ 
+-    /* Parse specified principal name now if we got one. */
+-    if (opts->principal_name) {
+-        if ((code = krb5_parse_name_flags(k5->ctx, opts->principal_name,
+-                                          flags, &k5->me))) {
+-            com_err(progname, code, _("when parsing name %s"),
+-                    opts->principal_name);
+-            return 0;
+-        }
+-    }
+-
+     if (opts->k5_out_cache_name) {
+         code = krb5_cc_resolve(k5->ctx, opts->k5_out_cache_name, &k5->out_cc);
+         if (code != 0) {
+             com_err(progname, code, _("resolving ccache %s"),
+                     opts->k5_out_cache_name);
+-            return 0;
++            goto cleanup;
+         }
+         if (opts->verbose) {
+             fprintf(stderr, _("Using specified cache: %s\n"),
+                     opts->k5_out_cache_name);
+         }
+     } else {
+-        if ((code = krb5_cc_default(k5->ctx, &defcache))) {
++        /* Resolve the default ccache and get its type and default principal
++         * (if it is initialized). */
++        code = krb5_cc_default(k5->ctx, &defcache);
++        if (code) {
+             com_err(progname, code, _("while getting default ccache"));
+-            return 0;
++            goto cleanup;
+         }
+         deftype = krb5_cc_get_type(k5->ctx, defcache);
+-        if (k5->me != NULL && krb5_cc_support_switch(k5->ctx, deftype)) {
+-            /* Use an existing cache for the specified principal if we can. */
+-            code = krb5_cc_cache_match(k5->ctx, k5->me, &k5->out_cc);
+-            if (code != 0 && code != KRB5_CC_NOTFOUND) {
+-                com_err(progname, code, _("while searching for ccache for %s"),
+-                        opts->principal_name);
+-                krb5_cc_close(k5->ctx, defcache);
+-                return 0;
++        if (krb5_cc_get_principal(k5->ctx, defcache, &defcache_princ) != 0)
++            defcache_princ = NULL;
++    }
++
++    /* Choose a client principal name. */
++    if (opts->principal_name != NULL) {
++        /* Use the specified principal name. */
++        code = krb5_parse_name_flags(k5->ctx, opts->principal_name, flags,
++                                     &k5->me);
++        if (code) {
++            com_err(progname, code, _("when parsing name %s"),
++                    opts->principal_name);
++            goto cleanup;
++        }
++    } else if (opts->anonymous) {
++        /* Use the anonymous principal for the local realm. */
++        code = krb5_get_default_realm(k5->ctx, &defrealm);
++        if (code) {
++            com_err(progname, code, _("while getting default realm"));
++            goto cleanup;
++        }
++        code = krb5_build_principal_ext(k5->ctx, &k5->me,
++                                        strlen(defrealm), defrealm,
++                                        strlen(KRB5_WELLKNOWN_NAMESTR),
++                                        KRB5_WELLKNOWN_NAMESTR,
++                                        strlen(KRB5_ANONYMOUS_PRINCSTR),
++                                        KRB5_ANONYMOUS_PRINCSTR,
++                                        0);
++        krb5_free_default_realm(k5->ctx, defrealm);
++        if (code) {
++            com_err(progname, code, _("while building principal"));
++            goto cleanup;
++        }
++    } else if (opts->action == INIT_KT) {
++        /* Use the default host/service name. */
++        code = krb5_sname_to_principal(k5->ctx, NULL, NULL, KRB5_NT_SRV_HST,
++                                       &k5->me);
++        if (code) {
++            com_err(progname, code,
++                    _("when creating default server principal name"));
++            goto cleanup;
++        }
++        if (k5->me->realm.data[0] == 0) {
++            code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
++            if (code == 0) {
++                com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
++                        _("(principal %s)"), k5->name);
++            } else {
++                com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
++                        _("for local services"));
+             }
+-            if (code == KRB5_CC_NOTFOUND) {
+-                code = krb5_cc_new_unique(k5->ctx, deftype, NULL, &k5->out_cc);
+-                if (code) {
+-                    com_err(progname, code, _("while generating new ccache"));
+-                    krb5_cc_close(k5->ctx, defcache);
+-                    return 0;
+-                }
+-                if (opts->verbose) {
+-                    fprintf(stderr, _("Using new cache: %s\n"),
+-                            krb5_cc_get_name(k5->ctx, k5->out_cc));
+-                }
+-            } else if (opts->verbose) {
++            goto cleanup;
++        }
++    } else if (k5->out_cc != NULL) {
++        /* If the output ccache is initialized, use its principal. */
++        if (krb5_cc_get_principal(k5->ctx, k5->out_cc, &princ) == 0)
++            k5->me = princ;
++    } else if (defcache_princ != NULL) {
++        /* Use the default cache's principal, and use the default cache as the
++         * output cache. */
++        k5->out_cc = defcache;
++        defcache = NULL;
++        k5->me = defcache_princ;
++        defcache_princ = NULL;
++    }
++
++    /* If we still haven't chosen, use the local username. */
++    if (k5->me == NULL) {
++        name = get_name_from_os();
++        if (name == NULL) {
++            fprintf(stderr, _("Unable to identify user\n"));
++            goto cleanup;
++        }
++        code = krb5_parse_name_flags(k5->ctx, name, flags, &k5->me);
++        if (code) {
++            com_err(progname, code, _("when parsing name %s"),
++                    name);
++            goto cleanup;
++        }
++    }
++
++    if (k5->out_cc == NULL && krb5_cc_support_switch(k5->ctx, deftype)) {
++        /* Use an existing cache for the client principal if we can. */
++        code = krb5_cc_cache_match(k5->ctx, k5->me, &k5->out_cc);
++        if (code != 0 && code != KRB5_CC_NOTFOUND) {
++            com_err(progname, code, _("while searching for ccache for %s"),
++                    opts->principal_name);
++            goto cleanup;
++        }
++        if (code == 0) {
++            if (opts->verbose) {
+                 fprintf(stderr, _("Using existing cache: %s\n"),
+                         krb5_cc_get_name(k5->ctx, k5->out_cc));
+             }
+-            krb5_cc_close(k5->ctx, defcache);
+             k5->switch_to_cache = 1;
+-        } else {
+-            k5->out_cc = defcache;
++        } else if (defcache_princ != NULL) {
++            /* Create a new cache to avoid overwriting the initialized default
++             * cache. */
++            code = krb5_cc_new_unique(k5->ctx, deftype, NULL, &k5->out_cc);
++            if (code) {
++                com_err(progname, code, _("while generating new ccache"));
++                goto cleanup;
++            }
+             if (opts->verbose) {
+-                fprintf(stderr, _("Using default cache: %s\n"),
++                fprintf(stderr, _("Using new cache: %s\n"),
+                         krb5_cc_get_name(k5->ctx, k5->out_cc));
+             }
++            k5->switch_to_cache = 1;
++        }
++    }
++
++    /* Use the default cache if we haven't picked one yet. */
++    if (k5->out_cc == NULL) {
++        k5->out_cc = defcache;
++        defcache = NULL;
++        if (opts->verbose) {
++            fprintf(stderr, _("Using default cache: %s\n"),
++                    krb5_cc_get_name(k5->ctx, k5->out_cc));
+         }
+     }
++
+     if (opts->k5_in_cache_name) {
+         code = krb5_cc_resolve(k5->ctx, opts->k5_in_cache_name, &k5->in_cc);
+         if (code != 0) {
+             com_err(progname, code, _("resolving ccache %s"),
+                     opts->k5_in_cache_name);
+-            return 0;
++            goto cleanup;
+         }
+         if (opts->verbose) {
+             fprintf(stderr, _("Using specified input cache: %s\n"),
+@@ -551,80 +634,24 @@ k5_begin(opts, k5)
+         }
+     }
+ 
+-    if (!k5->me) {
+-        /* No principal name specified */
+-        if (opts->anonymous) {
+-            char *defrealm;
+-            code = krb5_get_default_realm(k5->ctx, &defrealm);
+-            if (code) {
+-                com_err(progname, code, _("while getting default realm"));
+-                return 0;
+-            }
+-            code = krb5_build_principal_ext(k5->ctx, &k5->me,
+-                                            strlen(defrealm), defrealm,
+-                                            strlen(KRB5_WELLKNOWN_NAMESTR),
+-                                            KRB5_WELLKNOWN_NAMESTR,
+-                                            strlen(KRB5_ANONYMOUS_PRINCSTR),
+-                                            KRB5_ANONYMOUS_PRINCSTR,
+-                                            0);
+-            krb5_free_default_realm(k5->ctx, defrealm);
+-            if (code) {
+-                com_err(progname, code, _("while building principal"));
+-                return 0;
+-            }
+-        } else {
+-            if (opts->action == INIT_KT) {
+-                /* Use the default host/service name */
+-                code = krb5_sname_to_principal(k5->ctx, NULL, NULL,
+-                                               KRB5_NT_SRV_HST, &k5->me);
+-                if (code) {
+-                    com_err(progname, code,
+-                            _("when creating default server principal name"));
+-                    return 0;
+-                }
+-                if (k5->me->realm.data[0] == 0) {
+-                    code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
+-                    if (code == 0) {
+-                        com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
+-                                _("(principal %s)"), k5->name);
+-                    } else {
+-                        com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
+-                                _("for local services"));
+-                    }
+-                    return 0;
+-                }
+-            } else {
+-                /* Get default principal from cache if one exists */
+-                code = krb5_cc_get_principal(k5->ctx, k5->out_cc,
+-                                             &k5->me);
+-                if (code) {
+-                    char *name = get_name_from_os();
+-                    if (!name) {
+-                        fprintf(stderr, _("Unable to identify user\n"));
+-                        return 0;
+-                    }
+-                    if ((code = krb5_parse_name_flags(k5->ctx, name,
+-                                                      flags, &k5->me))) {
+-                        com_err(progname, code, _("when parsing name %s"),
+-                                name);
+-                        return 0;
+-                    }
+-                }
+-            }
+-        }
+-    }
+ 
+     code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
+     if (code) {
+         com_err(progname, code, _("when unparsing name"));
+-        return 0;
++        goto cleanup;
+     }
+     if (opts->verbose)
+         fprintf(stderr, _("Using principal: %s\n"), k5->name);
+ 
+     opts->principal_name = k5->name;
+ 
+-    return 1;
++    success = 1;
++
++cleanup:
++    if (defcache != NULL)
++        krb5_cc_close(k5->ctx, defcache);
++    krb5_free_principal(k5->ctx, defcache_princ);
++    return success;
+ }
+ 
+ static void
diff --git a/SOURCES/krb5-master-test_gss_no_udp.patch b/SOURCES/krb5-master-test_gss_no_udp.patch
new file mode 100644
index 0000000..866647d
--- /dev/null
+++ b/SOURCES/krb5-master-test_gss_no_udp.patch
@@ -0,0 +1,41 @@
+commit 11bd102c0e3793204111f712e5bd4bf54f2d9573
+Author: Greg Hudson <ghudson@mit.edu>
+Date:   Wed May 1 14:40:31 2013 -0400
+
+    Disable UDP pass of gssrpc tests on all platforms
+    
+    The AUTH_GSSAPI flavor of rpc authentication uses IP address channel
+    bindings.  These are broken over UDP, because svcudp_recv() fails to
+    get the destination address of incoming packets (it tries to use the
+    recvmsg() msg_name field to get the destination IP address, which
+    instead gets the source address; see ticket #5540).
+    
+    There is no simple or comprehensive way to fix this; using IP_PKTINFO
+    is a fair amount of code and only works on some platforms.  It's also
+    not very important--nobody should be using AUTH_GSSAPI except perhaps
+    for compatibility with really old kadmin, and kadmin only runs over
+    TCP.  Since the gssrpc tests are closely wedded to AUTH_GSSAPI, the
+    simplest fix is to only run the TCP pass.
+
+diff --git a/src/configure.in b/src/configure.in
+index 0c8111b..42a5fd5 100644
+--- a/src/configure.in
++++ b/src/configure.in
+@@ -984,16 +984,7 @@ extern void endrpcent();],
+ AC_MSG_RESULT($k5_cv_type_endrpcent)
+ AC_DEFINE_UNQUOTED(ENDRPCENT_TYPE, $k5_cv_type_endrpcent, [Define as return type of endrpcent])
+ K5_GEN_FILE(include/gssrpc/types.h:include/gssrpc/types.hin)
+-changequote(<<, >>)
+-case "$krb5_cv_host" in
+-*-*-solaris2.[012345]*)
+-	PASS=tcp
+-	;;
+-*)
+-	PASS="tcp udp"
+-	;;
+-esac
+-changequote([, ])
++PASS=tcp
+ AC_SUBST(PASS)
+ 
+ # for pkinit
diff --git a/SOURCES/krb5-master-test_no_pmap.patch b/SOURCES/krb5-master-test_no_pmap.patch
new file mode 100644
index 0000000..bc6afed
--- /dev/null
+++ b/SOURCES/krb5-master-test_no_pmap.patch
@@ -0,0 +1,244 @@
+commit 5454da3bcaa383f5b47984283f11f010d3d2b73e
+Author: Greg Hudson <ghudson@mit.edu>
+Date:   Wed May 1 13:07:36 2013 -0400
+
+    Don't use portmapper in RPC tests
+    
+    On many Linux systems, due to what is arguably a bug in rpcbind, the
+    portmapper doesn't allow service registration from non-root processes.
+    This causes the RPC tests to be frequently skipped.  Modify the tests
+    so that they don't need the portmapper, by grabbing the port number
+    from the server process and passing it to the client.
+
+diff --git a/doc/build/doing_build.rst b/doc/build/doing_build.rst
+index bc438c8..3c686cc 100644
+--- a/doc/build/doing_build.rst
++++ b/doc/build/doing_build.rst
+@@ -149,9 +149,6 @@ However, there are several prerequisites that must be satisfied first:
+   **-**\ **-disable-rpath**, which renders the build tree less suitable for
+   installation, but allows testing without interference from
+   previously installed libraries.
+-* In order to test the RPC layer, the local system has to be running
+-  the portmap daemon and it has to be listening to the regular network
+-  interface (not just localhost).
+ 
+ There are additional regression tests available, which are not run
+ by ``make check``.  These tests require manual setup and teardown of
+diff --git a/src/lib/rpc/unit-test/client.c b/src/lib/rpc/unit-test/client.c
+index a70cf38..6ab4534 100644
+--- a/src/lib/rpc/unit-test/client.c
++++ b/src/lib/rpc/unit-test/client.c
+@@ -7,12 +7,15 @@
+ 
+ #include <stdio.h>
+ #include <string.h>
++#include <netdb.h>
++#include <sys/socket.h>
+ #include "autoconf.h"
+ #ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+ #endif
+ #include <gssrpc/rpc.h>
+ #include <gssapi/gssapi.h>
++#include <gssapi/gssapi_krb5.h>
+ #include <gssrpc/rpc.h>
+ #include <gssrpc/auth_gssapi.h>
+ #include "rpc_test.h"
+@@ -51,17 +54,19 @@ main(argc, argv)
+    int argc;
+    char **argv;
+ {
+-     char        *host, *target, *echo_arg, **echo_resp, buf[BIG_BUF];
+-     char	 *prot;
++     char        *host, *port, *target, *echo_arg, **echo_resp, buf[BIG_BUF];
+      CLIENT      *clnt;
+      AUTH	 *tmp_auth;
+      struct rpc_err e;
+-     int i, auth_once;
++     int i, auth_once, sock, use_tcp;
+      unsigned int count;
+      extern int optind;
+      extern char *optarg;
+      extern int svc_debug_gssapi, misc_debug_gssapi, auth_debug_gssapi;
+      int c;
++     struct sockaddr_in sin;
++     struct hostent *h;
++     struct timeval tv;
+ 
+      extern int krb5_gss_dbg_client_expcreds;
+      krb5_gss_dbg_client_expcreds = 1;
+@@ -69,7 +74,7 @@ main(argc, argv)
+      whoami = argv[0];
+      count = 1026;
+      auth_once = 0;
+-     prot = NULL;
++     use_tcp = -1;
+ 
+      while ((c = getopt(argc, argv, "a:m:os:tu")) != -1) {
+ 	  switch (c) {
+@@ -86,39 +91,60 @@ main(argc, argv)
+ 	       svc_debug_gssapi = atoi(optarg);
+ 	       break;
+ 	  case 't':
+-	       prot = "tcp";
++	       use_tcp = 1;
+ 	       break;
+ 	  case 'u':
+-	       prot = "udp";
++	       use_tcp = 0;
+ 	       break;
+ 	  case '?':
+ 	       usage();
+ 	       break;
+ 	  }
+      }
+-     if (prot == NULL)
++     if (use_tcp == -1)
+ 	  usage();
+ 
+      argv += optind;
+      argc -= optind;
+ 
+      switch (argc) {
+-     case 3:
+-	  count = atoi(argv[2]);
++     case 4:
++	  count = atoi(argv[3]);
+ 	  if (count > BIG_BUF-1) {
+ 	    fprintf(stderr, "Test count cannot exceed %d.\n", BIG_BUF-1);
+ 	    usage();
+ 	  }
+-     case 2:
++     case 3:
+ 	  host = argv[0];
+-	  target = argv[1];
++	  port = argv[1];
++	  target = argv[2];
+ 	  break;
+      default:
+ 	  usage();
+      }
+ 
++     /* get server address */
++     h = gethostbyname(host);
++     if (h == NULL) {
++	 fprintf(stderr, "Can't resolve hostname %s\n", host);
++	 exit(1);
++     }
++     memset(&sin, 0, sizeof(sin));
++     sin.sin_family = h->h_addrtype;
++     sin.sin_port = ntohs(atoi(port));
++     memmove(&sin.sin_addr, h->h_addr, sizeof(sin.sin_addr));
++
+      /* client handle to rstat */
+-     clnt = clnt_create(host, RPC_TEST_PROG, RPC_TEST_VERS_1, prot);
++     sock = RPC_ANYSOCK;
++     if (use_tcp) {
++	 clnt = clnttcp_create(&sin, RPC_TEST_PROG, RPC_TEST_VERS_1, &sock, 0,
++			       0);
++     } else {
++	 tv.tv_sec = 5;
++	 tv.tv_usec = 0;
++	 clnt = clntudp_create(&sin, RPC_TEST_PROG, RPC_TEST_VERS_1, tv,
++			       &sock);
++     }
+      if (clnt == NULL) {
+ 	  clnt_pcreateerror(whoami);
+ 	  exit(1);
+diff --git a/src/lib/rpc/unit-test/config/unix.exp b/src/lib/rpc/unit-test/config/unix.exp
+index f02116e..ba57b70 100644
+--- a/src/lib/rpc/unit-test/config/unix.exp
++++ b/src/lib/rpc/unit-test/config/unix.exp
+@@ -112,10 +112,6 @@ proc rpc_test_exit {} {
+ 	global server_started
+ 	global kill
+ 
+-        if { [info exists server_started] && $server_started == 0 } { 
+-	    return 
+-	}
+-
+  	if {[catch {
+ 		expect {
+ 			-i $server_id
+@@ -138,6 +134,7 @@ proc rpc_test_start { } {
+ 	global server_id
+ 	global server_pid
+ 	global server_started
++	global server_port
+ 	global env
+ 
+ 	if [info exists server_pid] { rpc_test_exit }
+@@ -148,25 +145,17 @@ proc rpc_test_start { } {
+ 	set server_pid [spawn $SERVER $PROT]
+ 	set server_id $spawn_id
+         set server_started 1
++	set server_port -1
+ 
+ 	unset env(KRB5_KTNAME)
+ 
+ 	set timeout 30
+ 
+ 	expect {
++		-re "port: (\[0-9\]*)\r\n" {
++			set server_port $expect_out(1,string)
++		}
+ 		"running" { }
+-	        "Cannot register service" {
+-		        send_error "Server cannot register with portmap/rpcbind!!\n"
+-		        note "+++"
+-		        note "+++ These tests require the ability to register with portmap/rpcbind"
+-		        note "+++ Either the server is not running or it does not"
+-		        note "+++  allow registration using a loopback connection"
+-		        note "+++"
+-		        verbose $expect_out(buffer) 1
+-		        set server_started 0
+-		        unsupported "Server registration"
+-		        return
+-	        }
+ 		eof { 
+ 			send_error "server exited!"
+ 			verbose $expect_out(buffer) 1
+diff --git a/src/lib/rpc/unit-test/lib/helpers.exp b/src/lib/rpc/unit-test/lib/helpers.exp
+index 963fff4..a1b0783 100644
+--- a/src/lib/rpc/unit-test/lib/helpers.exp
++++ b/src/lib/rpc/unit-test/lib/helpers.exp
+@@ -170,7 +170,7 @@ proc flush_server {} {
+ 
+ proc start_client {testname ccname user password lifetime count
+ 		  {target ""}} {
+-	global env CLIENT PROT hostname spawn_id verbose
++	global env CLIENT PROT hostname server_port spawn_id verbose
+ 
+ 	if {$target == ""} {
+ 		set target "server@$hostname"
+@@ -180,9 +180,9 @@ proc start_client {testname ccname user password lifetime count
+ 	kinit $user $password $lifetime
+ 
+ 	if {$verbose > 0} {
+-		spawn $CLIENT -a 1 -s 1 -m 1 $PROT $hostname $target $count
++		spawn $CLIENT -a 1 -s 1 -m 1 $PROT $hostname $server_port $target $count
+ 	} else {
+-		spawn $CLIENT $PROT $hostname $target $count
++		spawn $CLIENT $PROT $hostname $server_port $target $count
+ 	}
+ 
+ 	verbose "$testname: client $ccname started"
+diff --git a/src/lib/rpc/unit-test/server.c b/src/lib/rpc/unit-test/server.c
+index c2cb30c..7451558 100644
+--- a/src/lib/rpc/unit-test/server.c
++++ b/src/lib/rpc/unit-test/server.c
+@@ -114,12 +114,13 @@ main(int argc, char **argv)
+ 	  exit(1);
+      }
+      if (!svc_register(transp, RPC_TEST_PROG, RPC_TEST_VERS_1,
+-		       rpc_test_prog_1_svc, prot)) {
++		       rpc_test_prog_1_svc, 0)) {
+ 	  fprintf(stderr,
+ 		  "unable to register (RPC_TEST_PROG, RPC_TEST_VERS_1, %s).",
+ 		  prot == IPPROTO_TCP ? "tcp" : "udp");
+ 	  exit(1);
+      }
++     printf("port: %d\n", (int)transp->xp_port);
+ 
+      if (svcauth_gssapi_set_names(names, 0) == FALSE) {
+ 	  fprintf(stderr, "unable to set gssapi names\n");
diff --git a/SOURCES/krb5.conf b/SOURCES/krb5.conf
new file mode 100644
index 0000000..471c4a1
--- /dev/null
+++ b/SOURCES/krb5.conf
@@ -0,0 +1,22 @@
+[logging]
+ default = FILE:/var/log/krb5libs.log
+ kdc = FILE:/var/log/krb5kdc.log
+ admin_server = FILE:/var/log/kadmind.log
+
+[libdefaults]
+ dns_lookup_realm = false
+ ticket_lifetime = 24h
+ renew_lifetime = 7d
+ forwardable = true
+ rdns = false
+# default_realm = EXAMPLE.COM
+
+[realms]
+# EXAMPLE.COM = {
+#  kdc = kerberos.example.com
+#  admin_server = kerberos.example.com
+# }
+
+[domain_realm]
+# .example.com = EXAMPLE.COM
+# example.com = EXAMPLE.COM
diff --git a/SOURCES/krb5_prop.portreserve b/SOURCES/krb5_prop.portreserve
new file mode 100644
index 0000000..54eeff2
--- /dev/null
+++ b/SOURCES/krb5_prop.portreserve
@@ -0,0 +1 @@
+krb5_prop/tcp
diff --git a/SOURCES/krb5kdc.init b/SOURCES/krb5kdc.init
new file mode 100755
index 0000000..3462ca6
--- /dev/null
+++ b/SOURCES/krb5kdc.init
@@ -0,0 +1,102 @@
+#!/bin/bash
+#
+# krb5kdc      Start and stop the Kerberos 5 servers.
+#
+# chkconfig:   - 35 65
+# description: Kerberos 5 is a trusted third-party authentication system.  \
+#	       This script starts and stops the server that Kerberos 5 \
+#	       clients need to connect to in order to obtain credentials.
+# processname: krb5kdc
+# config: /etc/sysconfig/krb5kdc
+# pidfile: /var/run/krb5kdc.pid
+#
+
+### BEGIN INIT INFO
+# Provides: krb5kdc
+# Required-Start: $local_fs $network
+# Required-Stop: $local_fs $network
+# Should-Start: portreserve
+# Default-Start:
+# Default-Stop: 0 1 2 3 4 5 6
+# Short-Description: start and stop the Kerberos 5 KDC
+# Description: The krb5kdc is the Kerberos 5 key distribution center, which \
+#              issues credentials to Kerberos 5 clients.
+### END INIT INFO
+
+# Get config.
+. /etc/sysconfig/network
+
+# Get config.
+[ -r /etc/sysconfig/krb5kdc ] && . /etc/sysconfig/krb5kdc
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+RETVAL=0
+prog="Kerberos 5 KDC"
+krb5kdc=/usr/sbin/krb5kdc
+pidfile=/var/run/krb5kdc.pid
+PATH=/usr/lib64/krb5:/usr/lib/krb5:"$PATH"
+
+# Shell functions to cut down on useless shell instances.
+start() {
+	[ -x $krb5kdc ] || exit 5
+	echo -n $"Starting $prog: "
+	# tell portreserve to release the kerberos-iv port
+	[ -x /sbin/portrelease ] && /sbin/portrelease kerberos-iv &>/dev/null || :
+	daemon ${krb5kdc} ${KRB5REALM:+-r ${KRB5REALM}} -P $pidfile $KRB5KDC_ARGS
+	RETVAL=$?
+	echo
+	if test $RETVAL -ne 0 ; then
+	    if status ${krb5kdc} > /dev/null ; then
+		RETVAL=0
+	    fi
+	fi
+	[ $RETVAL = 0 ] && touch /var/lock/subsys/krb5kdc
+}
+stop() {
+	echo -n $"Stopping $prog: "
+	killproc ${krb5kdc}
+	RETVAL=$?
+	echo
+	[ $RETVAL = 0 ] && rm -f /var/lock/subsys/krb5kdc
+}
+reload() {
+	echo -n $"Reopening $prog log file: "
+	killproc ${krb5kdc} -HUP
+        RETVAL=$?
+	echo
+}
+
+# See how we were called.
+case "$1" in
+  start)
+	start
+	;;
+  stop)
+	stop
+	;;
+  restart)
+	stop
+	start
+	;;
+  reload)
+        reload
+        ;;
+  status)
+	status ${krb5kdc}
+        RETVAL=$?
+	;;
+  condrestart)
+	if [ -f /var/lock/subsys/krb5kdc ] ; then
+		stop
+		start
+	fi
+	;;
+  *)
+	echo $"Usage: $0 {start|stop|status|reload|restart|condrestart}"
+	RETVAL=2
+	;;
+esac
+
+exit $RETVAL
diff --git a/SOURCES/krb5kdc.logrotate b/SOURCES/krb5kdc.logrotate
new file mode 100644
index 0000000..1100ed3
--- /dev/null
+++ b/SOURCES/krb5kdc.logrotate
@@ -0,0 +1,9 @@
+/var/log/krb5kdc.log {
+    missingok
+    notifempty
+    monthly
+    rotate 12
+    postrotate
+	/bin/kill -HUP `cat /var/run/krb5kdc.pid 2>/dev/null` 2> /dev/null || true
+    endscript
+}
diff --git a/SOURCES/krb5kdc.service b/SOURCES/krb5kdc.service
new file mode 100644
index 0000000..bc49204
--- /dev/null
+++ b/SOURCES/krb5kdc.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Kerberos 5 KDC
+After=syslog.target network.target
+
+[Service]
+Type=forking
+PIDFile=/var/run/krb5kdc.pid
+EnvironmentFile=-/etc/sysconfig/krb5kdc
+ExecStart=/usr/sbin/krb5kdc -P /var/run/krb5kdc.pid $KRB5KDC_ARGS
+ExecReload=/bin/kill -HUP $MAINPID
+
+[Install]
+WantedBy=multi-user.target
diff --git a/SOURCES/krb5kdc.sysconfig b/SOURCES/krb5kdc.sysconfig
new file mode 100644
index 0000000..791216d
--- /dev/null
+++ b/SOURCES/krb5kdc.sysconfig
@@ -0,0 +1 @@
+KRB5KDC_ARGS=
diff --git a/SOURCES/ksu.pamd b/SOURCES/ksu.pamd
new file mode 100644
index 0000000..66f5b2c
--- /dev/null
+++ b/SOURCES/ksu.pamd
@@ -0,0 +1,4 @@
+#%PAM-1.0
+auth    include  su
+account include  su
+session include  su
diff --git a/SOURCES/noport.c b/SOURCES/noport.c
new file mode 100644
index 0000000..c7a0c01
--- /dev/null
+++ b/SOURCES/noport.c
@@ -0,0 +1,107 @@
+#define _GNU_SOURCE
+#include <sys/socket.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+
+static int
+port_is_okay(unsigned short port)
+{
+	char *p, *q;
+	long l;
+
+	p = getenv("NOPORT");
+	while ((p != NULL) && (*p != '\0')) {
+		l = strtol(p, &q, 10);
+		if ((q == NULL) || (q == p)) {
+			break;
+		}
+		if ((*q == '\0') || (*q == ',')) {
+			if (port == l) {
+				errno = ECONNREFUSED;
+				return -1;
+			}
+		}
+		p = q;
+		p += strspn(p, ",");
+	}
+	return 0;
+}
+
+int
+connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
+{
+	unsigned short port;
+	static int (*next_connect)(int, const struct sockaddr *, socklen_t);
+
+	if (next_connect == NULL) {
+		next_connect = dlsym(RTLD_NEXT, "connect");
+		if (next_connect == NULL) {
+			errno = ENOSYS;
+			return -1;
+		}
+	}
+
+	if (getenv("NOPORT") == NULL) {
+		return next_connect(sockfd, addr, addrlen);
+	}
+
+	switch (addr->sa_family) {
+	case AF_INET:
+		port = ntohs(((struct sockaddr_in *)addr)->sin_port);
+		if (port_is_okay(port) != 0) {
+			return -1;
+		}
+		break;
+	case AF_INET6:
+		port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
+		if (port_is_okay(port) != 0) {
+			return -1;
+		}
+		break;
+	default:
+		break;
+	}
+	return next_connect(sockfd, addr, addrlen);
+}
+
+ssize_t
+sendto(int sockfd, const void *buf, size_t len, int flags,
+       const struct sockaddr *dest_addr, socklen_t addrlen)
+{
+	unsigned short port;
+	static int (*next_sendto)(int, const void *, size_t, int,
+				  const struct sockaddr *, socklen_t);
+
+	if (next_sendto == NULL) {
+		next_sendto = dlsym(RTLD_NEXT, "sendto");
+		if (next_sendto == NULL) {
+			errno = ENOSYS;
+			return -1;
+		}
+	}
+
+	if (getenv("NOPORT") == NULL) {
+		return next_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
+	}
+
+	switch (dest_addr->sa_family) {
+	case AF_INET:
+		port = ntohs(((struct sockaddr_in *)dest_addr)->sin_port);
+		if (port_is_okay(port) != 0) {
+			return -1;
+		}
+		break;
+	case AF_INET6:
+		port = ntohs(((struct sockaddr_in6 *)dest_addr)->sin6_port);
+		if (port_is_okay(port) != 0) {
+			return -1;
+		}
+		break;
+	default:
+		break;
+	}
+	return next_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
+}
diff --git a/SOURCES/persistent_keyring.patch b/SOURCES/persistent_keyring.patch
new file mode 100644
index 0000000..863774c
--- /dev/null
+++ b/SOURCES/persistent_keyring.patch
@@ -0,0 +1,3321 @@
+Pared down from the git commits, with a local copy of k5memdup0() added in
+to cc_keyring, and a wrapper 'run' in to k5test.py.
+
+diff --git a/src/aclocal.m4 b/src/aclocal.m4
+index 2c17e46..7be77c2 100644
+--- a/src/aclocal.m4
++++ b/src/aclocal.m4
+@@ -89,6 +89,7 @@ KRB5_AC_INITFINI
+ KRB5_AC_ENABLE_THREADS
+ KRB5_AC_FIND_DLOPEN
+ KRB5_AC_KEYRING_CCACHE
++KRB5_AC_PERSISTENT_KEYRING
+ ])dnl
+ 
+ dnl Maintainer mode, akin to what automake provides, 'cept we don't
+@@ -1664,3 +1659,12 @@ AC_DEFUN(KRB5_AC_KEYRING_CCACHE,[
+       ]))
+ ])dnl
+ dnl
++dnl If libkeyutils supports persistent keyrings, use them
++AC_DEFUN(KRB5_AC_PERSISTENT_KEYRING,[
++  AC_CHECK_HEADERS([keyutils.h],
++    AC_CHECK_LIB(keyutils, keyctl_get_persistent,
++      [AC_DEFINE(HAVE_PERSISTENT_KEYRING, 1,
++                 [Define if persistent keyrings are supported])
++      ]))
++])dnl
++dnl
+diff --git a/src/lib/krb5/error_tables/k5e1_err.et b/src/lib/krb5/error_tables/k5e1_err.et
+index 98374ed..071b7f2 100644
+--- a/src/lib/krb5/error_tables/k5e1_err.et
++++ b/src/lib/krb5/error_tables/k5e1_err.et
+@@ -35,4 +35,7 @@ error_code KRB5_PLUGIN_BAD_MODULE_SPEC, "Invalid module specifier"
+ error_code KRB5_PLUGIN_NAME_NOTFOUND, "Plugin module name not found"
+ error_code KRB5KDC_ERR_DISCARD, "The KDC should discard this request"
+ error_code KRB5_DCC_CANNOT_CREATE, "Can't create new subsidiary cache"
++error_code KRB5_KCC_INVALID_ANCHOR, "Invalid keyring anchor name"
++error_code KRB5_KCC_UNKNOWN_VERSION, "Unknown keyring collection version"
++error_code KRB5_KCC_INVALID_UID, "Invalid UID in persistent keyring name"
+ end
+diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c
+index fd1bcec..795ccd6 100644
+--- a/src/lib/krb5/ccache/cc_keyring.c
++++ b/src/lib/krb5/ccache/cc_keyring.c
+@@ -56,17 +56,42 @@
+  */
+ 
+ /*
+- * Implementation of a credentials cache stored in the Linux keyring facility
++ * This file implements a collection-enabled credential cache type where the
++ * credentials are stored in the Linux keyring facility.
+  *
+- * Some assumptions:
++ * A residual of this type can have three forms:
++ *    anchor:collection:subsidiary
++ *    anchor:collection
++ *    collection
+  *
+- *      - A credentials cache "file" == a keyring with separate keys
+- *        for the information in the ccache (see below)
+- *      - A credentials cache keyring will contain only keys,
+- *        not other keyrings
+- *      - Each Kerberos ticket will have its own key within the ccache keyring
+- *      - The principal information for the ccache is stored in a
+- *        special key, which is not counted in the 'numkeys' count
++ * The anchor name is "process", "thread", or "legacy" and determines where we
++ * search for keyring collections.  In the third form, the anchor name is
++ * presumed to be "legacy".  The anchor keyring for legacy caches is the
++ * session keyring.
++ *
++ * If the subsidiary name is present, the residual identifies a single cache
++ * within a collection.  Otherwise, the residual identifies the collection
++ * itself.  When a residual identifying a collection is resolved, the
++ * collection's primary key is looked up (or initialized, using the collection
++ * name as the subsidiary name), and the resulting cache's name will use the
++ * first name form and will identify the primary cache.
++ *
++ * Keyring collections are named "_krb_<collection>" and are linked from the
++ * anchor keyring.  The keys within a keyring collection are links to cache
++ * keyrings, plus a link to one user key named "krb_ccache:primary" which
++ * contains a serialized representation of the collection version (currently 1)
++ * and the primary name of the collection.
++ *
++ * Cache keyrings contain one user key per credential which contains a
++ * serialized representation of the credential.  There is also one user key
++ * named "__krb5_princ__" which contains a serialized representation of the
++ * cache's default principal.
++ *
++ * If the anchor name is "legacy", then the initial primary cache (the one
++ * named with the collection name) is also linked to the session keyring, and
++ * we look for a cache in that location when initializing the collection.  This
++ * extra link allows that cache to be visible to old versions of the KEYRING
++ * cache type, and allows us to see caches created by that code.
+  */
+ 
+ #include "cc-int.h"
+@@ -101,7 +126,20 @@ debug_print(char *fmt, ...)
+ #endif
+ 
+ /*
+- * We always use "user" key type
++ * We try to use the big_key key type for credentials except in legacy caches.
++ * We fall back to the user key type if the kernel does not support big_key.
++ * If the library doesn't support keyctl_get_persistent(), we don't even try
++ * big_key since the two features were added at the same time.
++ */
++#ifdef HAVE_PERSISTENT_KEYRING
++#define KRCC_CRED_KEY_TYPE "big_key"
++#else
++#define KRCC_CRED_KEY_TYPE "user"
++#endif
++
++/*
++ * We use the "user" key type for collection primary names, for cache principal
++ * names, and for credentials in legacy caches.
+  */
+ #define KRCC_KEY_TYPE_USER "user"
+ 
+@@ -117,20 +155,6 @@ debug_print(char *fmt, ...)
+ #define KRCC_SPEC_PRINC_KEYNAME "__krb5_princ__"
+ 
+ /*
+- * XXX The following two really belong in some external
+- * header since outside programs will need to use these
+- * same names.
+- */
+-/*
+- * Special name for key to communicate key serial numbers
+- * This is used by the Linux gssd process to pass the
+- * user's keyring values it gets in an upcall.
+- * The format of the contents should be
+- *    <session_key>:<process_key>:<thread_key>
+- */
+-#define KRCC_SPEC_IDS_KEYNAME "_gssd_keyring_ids_"
+-
+-/*
+  * Special name for the key to communicate the name(s)
+  * of credentials caches to be used for requests.
+  * This should currently contain a single name, but
+@@ -139,26 +163,55 @@ debug_print(char *fmt, ...)
+  */
+ #define KRCC_SPEC_CCACHE_SET_KEYNAME "__krb5_cc_set__"
+ 
++/*
++ * This name identifies the key containing the name of the current primary
++ * cache within a collection.
++ */
++#define KRCC_COLLECTION_PRIMARY "krb_ccache:primary"
++
++/*
++ * If the library context does not specify a keyring collection, unique ccaches
++ * will be created within this collection.
++ */
++#define KRCC_DEFAULT_UNIQUE_COLLECTION "session:__krb5_unique__"
++
++/*
++ * Collection keyring names begin with this prefix.  We use a prefix so that a
++ * cache keyring with the collection name itself can be linked directly into
++ * the anchor, for legacy session keyring compatibility.
++ */
++#define KRCC_CCCOL_PREFIX "_krb_"
++
++/*
++ * For the "persistent" anchor type, we look up or create this fixed keyring
++ * name within the per-UID persistent keyring.
++ */
++#define KRCC_PERSISTENT_KEYRING_NAME "_krb"
++
++/*
++ * Keyring name prefix and length of random name part
++ */
++#define KRCC_NAME_PREFIX "krb_ccache_"
++#define KRCC_NAME_RAND_CHARS 8
++
++#define KRCC_COLLECTION_VERSION 1
++
++#define KRCC_PERSISTENT_ANCHOR "persistent"
++#define KRCC_PROCESS_ANCHOR "process"
++#define KRCC_THREAD_ANCHOR "thread"
++#define KRCC_SESSION_ANCHOR "session"
++#define KRCC_USER_ANCHOR "user"
++#define KRCC_LEGACY_ANCHOR "legacy"
++
+ #define KRB5_OK 0
+ 
+ /* Hopefully big enough to hold a serialized credential */
+-#define GUESS_CRED_SIZE 4096
+-
+-#define ALLOC(NUM,TYPE)                         \
+-    (((NUM) <= (((size_t)0-1)/ sizeof(TYPE)))   \
+-     ? (TYPE *) calloc((NUM), sizeof(TYPE))     \
+-     : (errno = ENOMEM,(TYPE *) 0))
++#define MAX_CRED_SIZE (1024*1024)
+ 
+ #define CHECK_N_GO(ret, errdest) if (ret != KRB5_OK) goto errdest
+ #define CHECK(ret) if (ret != KRB5_OK) goto errout
+ #define CHECK_OUT(ret) if (ret != KRB5_OK) return ret
+ 
+-typedef struct krb5_krcc_ring_ids {
+-    key_serial_t        session;
+-    key_serial_t        process;
+-    key_serial_t        thread;
+-} krb5_krcc_ring_ids_t;
+-
+ typedef struct _krb5_krcc_cursor
+ {
+     int     numkeys;
+@@ -169,7 +222,7 @@ typedef struct _krb5_krcc_cursor
+ 
+ /*
+  * This represents a credentials cache "file"
+- * where ring_id is the keyring serial number for
++ * where cache_id is the keyring serial number for
+  * this credentials cache "file".  Each key
+  * in the keyring contains a separate key.
+  */
+@@ -177,12 +230,11 @@ typedef struct _krb5_krcc_data
+ {
+     char   *name;               /* Name for this credentials cache */
+     k5_cc_mutex lock;           /* synchronization */
+-    key_serial_t parent_id;     /* parent keyring of this ccache keyring */
+-    key_serial_t ring_id;       /* keyring representing ccache */
++    key_serial_t collection_id; /* collection containing this cache keyring */
++    key_serial_t cache_id;      /* keyring representing ccache */
+     key_serial_t princ_id;      /* key holding principal info */
+-    int     numkeys;            /* # of keys in this ring
+-                                 * (does NOT include principal info) */
+     krb5_timestamp changetime;
++    krb5_boolean is_legacy_type;
+ } krb5_krcc_data;
+ 
+ /* Passed internally to assure we don't go past the bounds of our buffer */
+@@ -190,6 +242,7 @@ typedef struct _krb5_krcc_buffer_cursor
+ {
+     char   *bpp;
+     char   *endp;
++    size_t  size;               /* For dry-run length calculation */
+ } krb5_krcc_bc;
+ 
+ /* Global mutex */
+@@ -258,6 +311,29 @@ static krb5_error_code KRB5_CALLCONV krb5_krcc_lock
+ static krb5_error_code KRB5_CALLCONV krb5_krcc_unlock
+ (krb5_context context, krb5_ccache id);
+ 
++static krb5_error_code KRB5_CALLCONV krb5_krcc_ptcursor_new
++(krb5_context context, krb5_cc_ptcursor *cursor_out);
++
++static krb5_error_code KRB5_CALLCONV krb5_krcc_ptcursor_next
++(krb5_context context, krb5_cc_ptcursor cursor, krb5_ccache *cache_out);
++
++static krb5_error_code KRB5_CALLCONV krb5_krcc_ptcursor_free
++(krb5_context context, krb5_cc_ptcursor *cursor);
++
++static krb5_error_code KRB5_CALLCONV krb5_krcc_switch_to
++(krb5_context context, krb5_ccache cache);
++
++/* Like k5memdup, but add a final null byte. */
++static inline void *
++k5memdup0(const void *in, size_t len, krb5_error_code *code)
++{
++    void *ptr = k5alloc(len + 1, code);
++
++    if (ptr != NULL && len > 0)
++        memcpy(ptr, in, len);
++    return ptr;
++}
++
+ /*
+  * Internal utility functions
+  */
+@@ -266,8 +331,9 @@ static krb5_error_code krb5_krcc_clearcache
+ (krb5_context context, krb5_ccache id);
+ 
+ static krb5_error_code krb5_krcc_new_data
+-(const char *, key_serial_t ring, key_serial_t parent_ring,
+- krb5_krcc_data **);
++(const char *anchor_name, const char *collection_name,
++ const char *subsidiary_name, key_serial_t cache_id,
++ key_serial_t collection_id, krb5_krcc_data **datapp);
+ 
+ static krb5_error_code krb5_krcc_save_principal
+ (krb5_context context, krb5_ccache id, krb5_principal princ);
+@@ -275,100 +341,480 @@ static krb5_error_code krb5_krcc_save_principal
+ static krb5_error_code krb5_krcc_retrieve_principal
+ (krb5_context context, krb5_ccache id, krb5_principal * princ);
+ 
+-static int krb5_krcc_get_ring_ids(krb5_krcc_ring_ids_t *p);
+-
+ /* Routines to parse a key from a keyring into a cred structure */
+ static krb5_error_code krb5_krcc_parse
+-(krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len,
+- krb5_krcc_bc * bc);
++(krb5_context, krb5_pointer buf, unsigned int len, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_parse_cred
+-(krb5_context context, krb5_ccache id, krb5_creds * creds,
+- char *payload, int psize);
++(krb5_context context, krb5_creds * creds, char *payload, int psize);
+ static krb5_error_code krb5_krcc_parse_principal
+-(krb5_context context, krb5_ccache id, krb5_principal * princ,
+- krb5_krcc_bc * bc);
++(krb5_context context, krb5_principal * princ, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_parse_keyblock
+-(krb5_context context, krb5_ccache id, krb5_keyblock * keyblock,
+- krb5_krcc_bc * bc);
++(krb5_context context, krb5_keyblock * keyblock, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_parse_times
+-(krb5_context context, krb5_ccache id, krb5_ticket_times * t,
+- krb5_krcc_bc * bc);
++(krb5_context context, krb5_ticket_times * t, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_parse_krb5data
+-(krb5_context context, krb5_ccache id, krb5_data * data,
+- krb5_krcc_bc * bc);
++(krb5_context context, krb5_data * data, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_parse_int32
+-(krb5_context context, krb5_ccache id, krb5_int32 * i, krb5_krcc_bc * bc);
++(krb5_context context, krb5_int32 * i, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_parse_octet
+-(krb5_context context, krb5_ccache id, krb5_octet * octet,
+- krb5_krcc_bc * bc);
++(krb5_context context, krb5_octet * octet, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_parse_addrs
+-(krb5_context context, krb5_ccache id, krb5_address *** a,
+- krb5_krcc_bc * bc);
++(krb5_context context, krb5_address *** a, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_parse_addr
+-(krb5_context context, krb5_ccache id, krb5_address * a,
+- krb5_krcc_bc * bc);
++(krb5_context context, krb5_address * a, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_parse_authdata
+-(krb5_context context, krb5_ccache id, krb5_authdata *** ad,
+- krb5_krcc_bc * bc);
++(krb5_context context, krb5_authdata *** ad, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_parse_authdatum
+-(krb5_context context, krb5_ccache id, krb5_authdata * ad,
+- krb5_krcc_bc * bc);
++(krb5_context context, krb5_authdata * ad, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_parse_ui_2
+-(krb5_context, krb5_ccache id, krb5_ui_2 * i, krb5_krcc_bc * bc);
++(krb5_context, krb5_ui_2 * i, krb5_krcc_bc * bc);
+ 
+ /* Routines to unparse a cred structure into keyring key */
+ static krb5_error_code krb5_krcc_unparse
+-(krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len,
+- krb5_krcc_bc * bc);
+-static krb5_error_code krb5_krcc_unparse_cred
+-(krb5_context context, krb5_ccache id, krb5_creds * creds,
++(krb5_context, krb5_pointer buf, unsigned int len, krb5_krcc_bc * bc);
++static krb5_error_code krb5_krcc_unparse_cred_alloc
++(krb5_context context, krb5_creds * creds,
+  char **datapp, unsigned int *lenptr);
++static krb5_error_code krb5_krcc_unparse_cred
++(krb5_context context, krb5_creds * creds, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_principal
+-(krb5_context, krb5_ccache id, krb5_principal princ, krb5_krcc_bc * bc);
++(krb5_context, krb5_principal princ, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_keyblock
+-(krb5_context, krb5_ccache id, krb5_keyblock * keyblock,
+- krb5_krcc_bc * bc);
++(krb5_context, krb5_keyblock * keyblock, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_times
+-(krb5_context, krb5_ccache id, krb5_ticket_times * t, krb5_krcc_bc * bc);
++(krb5_context, krb5_ticket_times * t, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_krb5data
+-(krb5_context, krb5_ccache id, krb5_data * data, krb5_krcc_bc * bc);
++(krb5_context, krb5_data * data, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_int32
+-(krb5_context, krb5_ccache id, krb5_int32 i, krb5_krcc_bc * bc);
++(krb5_context, krb5_int32 i, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_octet
+-(krb5_context, krb5_ccache id, krb5_int32 i, krb5_krcc_bc * bc);
++(krb5_context, krb5_int32 i, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_addrs
+-(krb5_context, krb5_ccache, krb5_address ** a, krb5_krcc_bc * bc);
++(krb5_context, krb5_address ** a, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_addr
+-(krb5_context, krb5_ccache, krb5_address * a, krb5_krcc_bc * bc);
++(krb5_context, krb5_address * a, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_authdata
+-(krb5_context, krb5_ccache, krb5_authdata ** ad, krb5_krcc_bc * bc);
++(krb5_context, krb5_authdata ** ad, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_authdatum
+-(krb5_context, krb5_ccache, krb5_authdata * ad, krb5_krcc_bc * bc);
++(krb5_context, krb5_authdata * ad, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_ui_4
+-(krb5_context, krb5_ccache id, krb5_ui_4 i, krb5_krcc_bc * bc);
++(krb5_context, krb5_ui_4 i, krb5_krcc_bc * bc);
+ static krb5_error_code krb5_krcc_unparse_ui_2
+-(krb5_context, krb5_ccache id, krb5_int32 i, krb5_krcc_bc * bc);
++(krb5_context, krb5_int32 i, krb5_krcc_bc * bc);
+ static void krb5_krcc_update_change_time
+ (krb5_krcc_data *);
+ 
++static krb5_error_code
++krb5_krcc_parse_index(krb5_context context, krb5_int32 *version,
++                      char **primary, void *payload, int psize);
++static krb5_error_code
++krb5_krcc_unparse_index(krb5_context context, krb5_int32 version,
++                        const char *primary, void **datapp, int *lenptr);
++
+ /* Note the following is a stub function for Linux */
+ extern krb5_error_code krb5_change_cache(void);
+ 
+ /*
+- * Determine how many keys exist in a ccache keyring.
+- * Subtracts out the "hidden" key holding the principal information.
++ * GET_PERSISTENT(uid) acquires the persistent keyring for uid, or falls back
++ * to the user keyring if uid matches the current effective uid.
++ */
++
++static key_serial_t
++get_persistent_fallback(uid_t uid)
++{
++    return (uid == geteuid()) ? KEY_SPEC_USER_KEYRING : -1;
++}
++
++#ifdef HAVE_PERSISTENT_KEYRING
++#define GET_PERSISTENT get_persistent_real
++static key_serial_t
++get_persistent_real(uid_t uid)
++{
++    key_serial_t key;
++
++    key = keyctl_get_persistent(uid, KEY_SPEC_PROCESS_KEYRING);
++    return (key == -1 && errno == ENOTSUP) ? get_persistent_fallback(uid) :
++        key;
++}
++#else
++#define GET_PERSISTENT get_persistent_fallback
++#endif
++
++/*
++ * Find or create a keyring within parent with the given name.  If possess is
++ * nonzero, also make sure the key is linked from possess.  This is necessary
++ * to ensure that we have possession rights on the key when the parent is the
++ * user or persistent keyring.
++ */
++static krb5_error_code
++find_or_create_keyring(key_serial_t parent, key_serial_t possess,
++                       const char *name, key_serial_t *key_out)
++{
++    key_serial_t key;
++
++    *key_out = -1;
++    key = keyctl_search(parent, KRCC_KEY_TYPE_KEYRING, name, possess);
++    if (key == -1) {
++        if (possess != 0) {
++            key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, possess);
++            if (key == -1)
++                return errno;
++            if (keyctl_link(key, parent) == -1)
++                return errno;
++        } else {
++            key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, parent);
++            if (key == -1)
++                return errno;
++        }
++    }
++    *key_out = key;
++    return 0;
++}
++
++/* Parse a residual name into an anchor name, a collection name, and possibly a
++ * subsidiary name. */
++static krb5_error_code
++parse_residual(const char *residual, char **anchor_name_out,
++               char **collection_name_out, char **subsidiary_name_out)
++{
++    krb5_error_code ret;
++    char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
++    const char *sep;
++
++    *anchor_name_out = 0;
++    *collection_name_out = NULL;
++    *subsidiary_name_out = NULL;
++
++    /* Parse out the anchor name.  Use the legacy anchor if not present. */
++    sep = strchr(residual, ':');
++    if (sep == NULL) {
++        anchor_name = strdup(KRCC_LEGACY_ANCHOR);
++        if (anchor_name == NULL)
++            goto oom;
++    } else {
++        anchor_name = k5memdup0(residual, sep - residual, &ret);
++        if (anchor_name == NULL)
++            goto oom;
++        residual = sep + 1;
++    }
++
++    /* Parse out the collection and subsidiary name. */
++    sep = strchr(residual, ':');
++    if (sep == NULL) {
++        collection_name = strdup(residual);
++        if (collection_name == NULL)
++            goto oom;
++        subsidiary_name = NULL;
++    } else {
++        collection_name = k5memdup0(residual, sep - residual, &ret);
++        if (collection_name == NULL)
++            goto oom;
++        subsidiary_name = strdup(sep + 1);
++        if (subsidiary_name == NULL)
++            goto oom;
++    }
++
++    *anchor_name_out = anchor_name;
++    *collection_name_out = collection_name;
++    *subsidiary_name_out = subsidiary_name;
++    return 0;
++
++oom:
++    free(anchor_name);
++    free(collection_name);
++    free(subsidiary_name);
++    return ENOMEM;
++}
++
++/*
++ * Return true if residual identifies a subsidiary cache which should be linked
++ * into the anchor so it can be visible to old code.  This is the case if the
++ * residual has the legacy anchor and the subsidiary name matches the
++ * collection name.
++ */
++static krb5_boolean
++is_legacy_cache_name(const char *residual)
++{
++    const char *sep, *aname, *cname, *sname;
++    size_t alen, clen, legacy_len = sizeof(KRCC_LEGACY_ANCHOR) - 1;
++
++    /* Get pointers to the anchor, collection, and subsidiary names. */
++    aname = residual;
++    sep = strchr(residual, ':');
++    if (sep == NULL)
++        return FALSE;
++    alen = sep - aname;
++    cname = sep + 1;
++    sep = strchr(cname, ':');
++    if (sep == NULL)
++        return FALSE;
++    clen = sep - cname;
++    sname = sep + 1;
++
++    return alen == legacy_len && clen == strlen(sname) &&
++        strncmp(aname, KRCC_LEGACY_ANCHOR, alen) == 0 &&
++        strncmp(cname, sname, clen) == 0;
++}
++
++/* If the default cache name for context is a KEYRING cache, parse its residual
++ * string.  Otherwise set all outputs to NULL. */
++static krb5_error_code
++get_default(krb5_context context, char **anchor_name_out,
++            char **collection_name_out, char **subsidiary_name_out)
++{
++    const char *defname;
++
++    *anchor_name_out = *collection_name_out = *subsidiary_name_out = NULL;
++    defname = krb5_cc_default_name(context);
++    if (defname == NULL || strncmp(defname, "KEYRING:", 8) != 0)
++        return 0;
++    return parse_residual(defname + 8, anchor_name_out, collection_name_out,
++                          subsidiary_name_out);
++}
++
++/* Create a residual identifying a subsidiary cache. */
++static krb5_error_code
++make_subsidiary_residual(const char *anchor_name, const char *collection_name,
++                         const char *subsidiary_name, char **residual_out)
++{
++    if (asprintf(residual_out, "%s:%s:%s", anchor_name, collection_name,
++                 subsidiary_name) < 0) {
++        *residual_out = NULL;
++        return ENOMEM;
++    }
++    return 0;
++}
++
++/* Retrieve or create a keyring for collection_name within the anchor, and set
++ * *collection_id_out to its serial number. */
++static krb5_error_code
++get_collection(const char *anchor_name, const char *collection_name,
++               key_serial_t *collection_id_out)
++{
++    krb5_error_code ret;
++    key_serial_t persistent_id, anchor_id, possess_id = 0;
++    char *ckname;
++    long uidnum;
++
++    *collection_id_out = 0;
++
++    if (strcmp(anchor_name, KRCC_PERSISTENT_ANCHOR) == 0) {
++        /*
++         * The collection name is a uid (or empty for the current effective
++         * uid), and we look up a fixed keyring name within the persistent
++         * keyring for that uid.  We link it to the process keyring to ensure
++         * that we have possession rights on the collection key.
++         */
++        if (*collection_name != '\0') {
++            errno = 0;
++            uidnum = strtol(collection_name, NULL, 10);
++            if (errno)
++                return KRB5_KCC_INVALID_UID;
++        } else {
++            uidnum = geteuid();
++        }
++        persistent_id = GET_PERSISTENT(uidnum);
++        if (persistent_id == -1)
++            return KRB5_KCC_INVALID_UID;
++        return find_or_create_keyring(persistent_id, KEY_SPEC_PROCESS_KEYRING,
++                                      KRCC_PERSISTENT_KEYRING_NAME,
++                                      collection_id_out);
++    }
++
++    if (strcmp(anchor_name, KRCC_PROCESS_ANCHOR) == 0) {
++        anchor_id = KEY_SPEC_PROCESS_KEYRING;
++    } else if (strcmp(anchor_name, KRCC_THREAD_ANCHOR) == 0) {
++        anchor_id = KEY_SPEC_THREAD_KEYRING;
++    } else if (strcmp(anchor_name, KRCC_SESSION_ANCHOR) == 0) {
++        anchor_id = KEY_SPEC_SESSION_KEYRING;
++    } else if (strcmp(anchor_name, KRCC_USER_ANCHOR) == 0) {
++        /* The user keyring does not confer possession, so we need to link the
++         * collection to the process keyring to maintain possession rights. */
++        anchor_id = KEY_SPEC_USER_KEYRING;
++        possess_id = KEY_SPEC_PROCESS_KEYRING;
++    } else if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
++        anchor_id = KEY_SPEC_SESSION_KEYRING;
++    } else {
++        return KRB5_KCC_INVALID_ANCHOR;
++    }
++
++    /* Look up the collection keyring name within the anchor keyring. */
++    if (asprintf(&ckname, "%s%s", KRCC_CCCOL_PREFIX, collection_name) == -1)
++        return ENOMEM;
++    ret = find_or_create_keyring(anchor_id, possess_id, ckname,
++                                 collection_id_out);
++    free(ckname);
++    return ret;
++}
++
++/* Store subsidiary_name into the primary index key for collection_id. */
++static krb5_error_code
++set_primary_name(krb5_context context, key_serial_t collection_id,
++                 const char *subsidiary_name)
++{
++    krb5_error_code ret;
++    key_serial_t key;
++    void *payload = NULL;
++    int payloadlen;
++
++    ret = krb5_krcc_unparse_index(context, KRCC_COLLECTION_VERSION,
++                                  subsidiary_name, &payload, &payloadlen);
++    if (ret)
++        return ret;
++    key = add_key(KRCC_KEY_TYPE_USER, KRCC_COLLECTION_PRIMARY,
++                  payload, payloadlen, collection_id);
++    free(payload);
++    return (key == -1) ? errno : 0;
++}
++
++/*
++ * Get or initialize the primary name within collection_id and set
++ * *subsidiary_out to its value.  If initializing a legacy collection, look
++ * for a legacy cache and add it to the collection.
++ */
++static krb5_error_code
++get_primary_name(krb5_context context, const char *anchor_name,
++                 const char *collection_name, key_serial_t collection_id,
++                 char **subsidiary_out)
++{
++    krb5_error_code ret;
++    key_serial_t primary_id, legacy;
++    void *payload = NULL;
++    int payloadlen;
++    krb5_int32 version;
++    char *subsidiary_name = NULL;
++
++    *subsidiary_out = NULL;
++
++    primary_id = keyctl_search(collection_id, KRCC_KEY_TYPE_USER,
++                               KRCC_COLLECTION_PRIMARY, 0);
++    if (primary_id == -1) {
++        /* Initialize the primary key using the collection name.  We can't name
++         * a key with the empty string, so map that to an arbitrary string. */
++        subsidiary_name = strdup((*collection_name == '\0') ? "tkt" :
++                                 collection_name);
++        if (subsidiary_name == NULL) {
++            ret = ENOMEM;
++            goto cleanup;
++        }
++        ret = set_primary_name(context, collection_id, subsidiary_name);
++        if (ret)
++            goto cleanup;
++
++        if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
++            /* Look for a cache created by old code.  If we find one, add it to
++             * the collection. */
++            legacy = keyctl_search(KEY_SPEC_SESSION_KEYRING,
++                                   KRCC_KEY_TYPE_KEYRING, subsidiary_name, 0);
++            if (legacy != -1 && keyctl_link(legacy, collection_id) == -1) {
++                ret = errno;
++                goto cleanup;
++            }
++        }
++    } else {
++        /* Read, parse, and free the primary key's payload. */
++        payloadlen = keyctl_read_alloc(primary_id, &payload);
++        if (payloadlen == -1) {
++            ret = errno;
++            goto cleanup;
++        }
++        ret = krb5_krcc_parse_index(context, &version, &subsidiary_name,
++                                    payload, payloadlen);
++        if (ret)
++            goto cleanup;
++
++        if (version != KRCC_COLLECTION_VERSION) {
++            ret = KRB5_KCC_UNKNOWN_VERSION;
++            goto cleanup;
++        }
++    }
++
++    *subsidiary_out = subsidiary_name;
++    subsidiary_name = NULL;
++
++cleanup:
++    free(payload);
++    free(subsidiary_name);
++    return ret;
++}
++
++/*
++ * Create a keyring with a unique random name within collection_id.  Set
++ * *subsidiary to its name and *cache_id_out to its key serial number.
+  */
+-static int KRB5_CALLCONV
+-krb5_krcc_getkeycount(key_serial_t cred_ring)
++static krb5_error_code
++unique_keyring(krb5_context context, key_serial_t collection_id,
++               char **subsidiary_out, key_serial_t *cache_id_out)
+ {
+-    int res, nkeys;
++    key_serial_t key;
++    krb5_error_code ret;
++    char uniquename[sizeof(KRCC_NAME_PREFIX) + KRCC_NAME_RAND_CHARS];
++    int prefixlen = sizeof(KRCC_NAME_PREFIX) - 1;
++    int tries;
++
++    *subsidiary_out = NULL;
++    *cache_id_out = 0;
++
++    memcpy(uniquename, KRCC_NAME_PREFIX, sizeof(KRCC_NAME_PREFIX));
++    k5_cc_mutex_lock(context, &krb5int_krcc_mutex);
++
++    /* Loop until we successfully create a new ccache keyring with
++     * a unique name, or we get an error. Limit to 100 tries. */
++    tries = 100;
++    while (tries-- > 0) {
++        ret = krb5int_random_string(context, uniquename + prefixlen,
++                                    KRCC_NAME_RAND_CHARS);
++        if (ret)
++            goto cleanup;
++
++        key = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING, uniquename,
++                            0);
++        if (key < 0) {
++            /* Name does not already exist.  Create it to reserve the name. */
++            key = add_key(KRCC_KEY_TYPE_KEYRING, uniquename, NULL, 0,
++                          collection_id);
++            if (key < 0) {
++                ret = errno;
++                goto cleanup;
++            }
++            break;
++        }
++    }
+ 
+-    res = keyctl_read(cred_ring, NULL, 0);
+-    if (res > 0)
+-        nkeys = (res / sizeof(key_serial_t)) - 1;
+-    else
+-        nkeys = 0;
+-    return(nkeys);
++    if (tries <= 0) {
++        ret = KRB5_CC_BADNAME;
++        goto cleanup;
++    }
++
++    *subsidiary_out = strdup(uniquename);
++    if (*subsidiary_out == NULL) {
++        ret = ENOMEM;
++        goto cleanup;
++    }
++    *cache_id_out = key;
++    ret = KRB5_OK;
++cleanup:
++    k5_cc_mutex_unlock(context, &krb5int_krcc_mutex);
++    return ret;
++}
++
++static krb5_error_code
++add_cred_key(const char *name, const void *payload, size_t plen,
++             key_serial_t cache_id, krb5_boolean legacy_type)
++{
++    key_serial_t key;
++
++    if (!legacy_type) {
++        /* Try the preferred cred key type; fall back if no kernel support. */
++        key = add_key(KRCC_CRED_KEY_TYPE, name, payload, plen, cache_id);
++        if (key != -1)
++            return 0;
++        else if (errno != EINVAL && errno != ENODEV)
++            return errno;
++    }
++    /* Use the user key type. */
++    key = add_key(KRCC_KEY_TYPE_USER, name, payload, plen, cache_id);
++    return (key == -1) ? errno : 0;
+ }
+ 
+ /*
+@@ -388,24 +834,40 @@ static krb5_error_code KRB5_CALLCONV
+ krb5_krcc_initialize(krb5_context context, krb5_ccache id,
+                      krb5_principal princ)
+ {
++    krb5_krcc_data *data = (krb5_krcc_data *)id->data;
+     krb5_error_code kret;
++    const char *cache_name, *p;
+ 
+     DEBUG_PRINT(("krb5_krcc_initialize: entered\n"));
+ 
+-    kret = k5_cc_mutex_lock(context, &((krb5_krcc_data *) id->data)->lock);
+-    if (kret)
+-        return kret;
++    k5_cc_mutex_lock(context, &data->lock);
+ 
+     kret = krb5_krcc_clearcache(context, id);
+     if (kret != KRB5_OK)
+         goto out;
+ 
++    if (!data->cache_id) {
++        /* The key didn't exist at resolve time.  Check again and create the
++         * key if it still isn't there. */
++        p = strrchr(data->name, ':');
++        cache_name = (p != NULL) ? p + 1 : data->name;
++        kret = find_or_create_keyring(data->collection_id, 0, cache_name,
++                                      &data->cache_id);
++        if (kret)
++            goto out;
++    }
++
++    /* If this is the legacy cache in a legacy session collection, link it
++     * directly to the session keyring so that old code can see it. */
++    if (is_legacy_cache_name(data->name))
++        (void)keyctl_link(data->cache_id, KEY_SPEC_SESSION_KEYRING);
++
+     kret = krb5_krcc_save_principal(context, id, princ);
+     if (kret == KRB5_OK)
+         krb5_change_cache();
+ 
+ out:
+-    k5_cc_mutex_unlock(context, &((krb5_krcc_data *) id->data)->lock);
++    k5_cc_mutex_unlock(context, &data->lock);
+     return kret;
+ }
+ 
+@@ -460,14 +922,14 @@ krb5_krcc_clearcache(krb5_context context, krb5_ccache id)
+ 
+     d = (krb5_krcc_data *) id->data;
+ 
+-    DEBUG_PRINT(("krb5_krcc_clearcache: ring_id %d, princ_id %d, "
+-                 "numkeys is %d\n", d->ring_id, d->princ_id, d->numkeys));
++    DEBUG_PRINT(("krb5_krcc_clearcache: cache_id %d, princ_id %d\n",
++                 d->cache_id, d->princ_id));
+ 
+-    res = keyctl_clear(d->ring_id);
+-    if (res != 0) {
+-        return errno;
++    if (d->cache_id) {
++        res = keyctl_clear(d->cache_id);
++        if (res != 0)
++            return errno;
+     }
+-    d->numkeys = 0;
+     d->princ_id = 0;
+     krb5_krcc_update_change_time(d);
+ 
+@@ -484,7 +946,7 @@ krb5_krcc_clearcache(krb5_context context, krb5_ccache id)
+ static krb5_error_code KRB5_CALLCONV
+ krb5_krcc_destroy(krb5_context context, krb5_ccache id)
+ {
+-    krb5_error_code kret;
++    krb5_error_code kret = 0;
+     krb5_krcc_data *d;
+     int     res;
+ 
+@@ -492,30 +954,67 @@ krb5_krcc_destroy(krb5_context context, krb5_ccache id)
+ 
+     d = (krb5_krcc_data *) id->data;
+ 
+-    kret = k5_cc_mutex_lock(context, &d->lock);
+-    if (kret)
+-        return kret;
++    k5_cc_mutex_lock(context, &d->lock);
+ 
+     krb5_krcc_clearcache(context, id);
+-    free(d->name);
+-    res = keyctl_unlink(d->ring_id, d->parent_id);
+-    if (res < 0) {
+-        kret = errno;
+-        DEBUG_PRINT(("krb5_krcc_destroy: unlinking key %d from ring %d: %s",
+-                     d->ring_id, d->parent_id, error_message(errno)));
+-        goto cleanup;
++    if (d->cache_id) {
++        res = keyctl_unlink(d->cache_id, d->collection_id);
++        if (res < 0) {
++            kret = errno;
++            DEBUG_PRINT(("unlinking key %d from ring %d: %s",
++                         d->cache_id, d->collection_id, error_message(errno)));
++        }
++        /* If this is a legacy cache, unlink it from the session anchor. */
++        if (is_legacy_cache_name(d->name))
++            (void)keyctl_unlink(d->cache_id, KEY_SPEC_SESSION_KEYRING);
+     }
+-cleanup:
++
+     k5_cc_mutex_unlock(context, &d->lock);
+     k5_cc_mutex_destroy(&d->lock);
++    free(d->name);
+     free(d);
+     free(id);
+ 
+     krb5_change_cache();
+ 
+-    return KRB5_OK;
++    return kret;
+ }
+ 
++/* Create a cache handle for a cache ID. */
++static krb5_error_code
++make_cache(key_serial_t collection_id, key_serial_t cache_id,
++           const char *anchor_name, const char *collection_name,
++           const char *subsidiary_name, krb5_ccache *cache_out)
++{
++    krb5_error_code ret;
++    krb5_ccache ccache = NULL;
++    krb5_krcc_data *d;
++    key_serial_t pkey = 0;
++
++    /* Determine the key containing principal information, if present. */
++    pkey = keyctl_search(cache_id, KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME,
++                         0);
++    if (pkey < 0)
++        pkey = 0;
++
++    ccache = malloc(sizeof(struct _krb5_ccache));
++    if (!ccache)
++        return ENOMEM;
++
++    ret = krb5_krcc_new_data(anchor_name, collection_name, subsidiary_name,
++                             cache_id, collection_id, &d);
++    if (ret) {
++        free(ccache);
++        return ret;
++    }
++
++    d->princ_id = pkey;
++    ccache->ops = &krb5_krcc_ops;
++    ccache->data = d;
++    ccache->magic = KV5M_CCACHE;
++    *cache_out = ccache;
++    return 0;
++}
+ 
+ /*
+  * Requires:
+@@ -538,101 +1037,42 @@ cleanup:
+  */
+ 
+ static krb5_error_code KRB5_CALLCONV
+-krb5_krcc_resolve(krb5_context context, krb5_ccache * id, const char *full_residual)
++krb5_krcc_resolve(krb5_context context, krb5_ccache *id, const char *residual)
+ {
+-    krb5_ccache lid;
+-    krb5_error_code kret;
+-    krb5_krcc_data *d;
+-    key_serial_t key;
+-    key_serial_t pkey = 0;
+-    int     nkeys = 0;
+-    int     res;
+-    krb5_krcc_ring_ids_t ids;
+-    key_serial_t ring_id;
+-    const char *residual;
++    krb5_error_code ret;
++    key_serial_t collection_id, cache_id;
++    char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
+ 
+-    DEBUG_PRINT(("krb5_krcc_resolve: entered with name '%s'\n",
+-                 full_residual));
++    ret = parse_residual(residual, &anchor_name, &collection_name,
++                         &subsidiary_name);
++    if (ret)
++        goto cleanup;
++    ret = get_collection(anchor_name, collection_name, &collection_id);
++    if (ret)
++        goto cleanup;
+ 
+-    res = krb5_krcc_get_ring_ids(&ids);
+-    if (res) {
+-        kret = EINVAL;
+-        DEBUG_PRINT(("krb5_krcc_resolve: Error getting ring id values!\n"));
+-        return kret;
++    if (subsidiary_name == NULL) {
++        /* Retrieve or initialize the primary name for the collection. */
++        ret = get_primary_name(context, anchor_name, collection_name,
++                               collection_id, &subsidiary_name);
++        if (ret)
++            goto cleanup;
+     }
+ 
+-    if (strncmp(full_residual, "thread:", 7) == 0) {
+-        residual = full_residual + 7;
+-        ring_id = ids.thread;
+-    } else if (strncmp(full_residual, "process:", 8) == 0) {
+-        residual = full_residual + 8;
+-        ring_id = ids.process;
+-    } else {
+-        residual = full_residual;
+-        ring_id = ids.session;
+-    }
++    /* Look up the cache keyring ID, if the cache is already initialized. */
++    cache_id = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING,
++                             subsidiary_name, 0);
++    if (cache_id < 0)
++        cache_id = 0;
+ 
+-    DEBUG_PRINT(("krb5_krcc_resolve: searching ring %d for residual '%s'\n",
+-                 ring_id, residual));
++    ret = make_cache(collection_id, cache_id, anchor_name, collection_name,
++                     subsidiary_name, id);
+ 
+-    /*
+-     * Use keyctl_search instead of request_key. If we're supposed
+-     * to be looking for a process ccache, we shouldn't find a
+-     * thread ccache.
+-     * XXX But should we look in the session ring if we don't find it
+-     * in the process ring?  Same goes for thread.  Should we look in
+-     * the process and session rings if not found in the thread ring?
+-     *
+-     */
+-    key = keyctl_search(ring_id, KRCC_KEY_TYPE_KEYRING, residual, 0);
+-    if (key < 0) {
+-        key = add_key(KRCC_KEY_TYPE_KEYRING, residual, NULL, 0, ring_id);
+-        if (key < 0) {
+-            kret = errno;
+-            DEBUG_PRINT(("krb5_krcc_resolve: Error adding new "
+-                         "keyring '%s': %s\n", residual, strerror(errno)));
+-            return kret;
+-        }
+-        DEBUG_PRINT(("krb5_krcc_resolve: new keyring '%s', "
+-                     "key %d, added to keyring %d\n",
+-                     residual, key, ring_id));
+-    } else {
+-        DEBUG_PRINT(("krb5_krcc_resolve: found existing "
+-                     "key %d, with name '%s' in keyring %d\n",
+-                     key, residual, ring_id));
+-        /* Determine key containing principal information */
+-        pkey = keyctl_search(key, KRCC_KEY_TYPE_USER,
+-                             KRCC_SPEC_PRINC_KEYNAME, 0);
+-        if (pkey < 0) {
+-            DEBUG_PRINT(("krb5_krcc_resolve: Error locating principal "
+-                         "info for existing ccache in ring %d: %s\n",
+-                         key, strerror(errno)));
+-            pkey = 0;
+-        }
+-        /* Determine how many keys exist */
+-        nkeys = krb5_krcc_getkeycount(key);
+-    }
+-
+-    lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
+-    if (lid == NULL)
+-        return KRB5_CC_NOMEM;
+-
+-
+-    kret = krb5_krcc_new_data(residual, key, ring_id, &d);
+-    if (kret) {
+-        free(lid);
+-        return kret;
+-    }
+-
+-    DEBUG_PRINT(("krb5_krcc_resolve: ring_id %d, princ_id %d, "
+-                 "nkeys %d\n", key, pkey, nkeys));
+-    d->princ_id = pkey;
+-    d->numkeys = nkeys;
+-    lid->ops = &krb5_krcc_ops;
+-    lid->data = d;
+-    lid->magic = KV5M_CCACHE;
+-    *id = lid;
+-    return KRB5_OK;
++cleanup:
++    free(anchor_name);
++    free(collection_name);
++    free(subsidiary_name);
++    return ret;
+ }
+ 
+ /*
+@@ -653,47 +1093,37 @@ krb5_krcc_start_seq_get(krb5_context context, krb5_ccache id,
+                         krb5_cc_cursor * cursor)
+ {
+     krb5_krcc_cursor krcursor;
+-    krb5_error_code kret;
+     krb5_krcc_data *d;
+-    unsigned int size;
+-    int     res;
++    void *keys;
++    long size;
+ 
+     DEBUG_PRINT(("krb5_krcc_start_seq_get: entered\n"));
+ 
+     d = id->data;
+-    kret = k5_cc_mutex_lock(context, &d->lock);
+-    if (kret)
+-        return kret;
+-
+-    /*
+-     * Determine how many keys currently exist and update numkeys.
+-     * We cannot depend on the current value of numkeys because
+-     * the ccache may have been updated elsewhere
+-     */
+-    d->numkeys = krb5_krcc_getkeycount(d->ring_id);
++    k5_cc_mutex_lock(context, &d->lock);
+ 
+-    size = sizeof(*krcursor) + ((d->numkeys + 1) * sizeof(key_serial_t));
+-
+-    krcursor = (krb5_krcc_cursor) malloc(size);
+-    if (krcursor == NULL) {
++    if (!d->cache_id) {
+         k5_cc_mutex_unlock(context, &d->lock);
+-        return KRB5_CC_NOMEM;
++        return KRB5_FCC_NOFILE;
+     }
+ 
+-    krcursor->keys = (key_serial_t *) ((char *) krcursor + sizeof(*krcursor));
+-    res = keyctl_read(d->ring_id, (char *) krcursor->keys,
+-                      ((d->numkeys + 1) * sizeof(key_serial_t)));
+-    if (res < 0 || res > ((d->numkeys + 1) * sizeof(key_serial_t))) {
+-        DEBUG_PRINT(("Read %d bytes from keyring, numkeys %d: %s\n",
+-                     res, d->numkeys, strerror(errno)));
+-        free(krcursor);
++    size = keyctl_read_alloc(d->cache_id, &keys);
++    if (size == -1) {
++        DEBUG_PRINT(("Error getting from keyring: %s\n", strerror(errno)));
+         k5_cc_mutex_unlock(context, &d->lock);
+         return KRB5_CC_IO;
+     }
+ 
+-    krcursor->numkeys = d->numkeys;
+-    krcursor->currkey = 0;
++    krcursor = calloc(1, sizeof(*krcursor));
++    if (krcursor == NULL) {
++        free(keys);
++        k5_cc_mutex_unlock(context, &d->lock);
++        return KRB5_CC_NOMEM;
++    }
++
+     krcursor->princ_id = d->princ_id;
++    krcursor->numkeys = size / sizeof(key_serial_t);
++    krcursor->keys = keys;
+ 
+     k5_cc_mutex_unlock(context, &d->lock);
+     *cursor = (krb5_cc_cursor) krcursor;
+@@ -741,14 +1171,14 @@ krb5_krcc_next_cred(krb5_context context, krb5_ccache id,
+     memset(creds, 0, sizeof(krb5_creds));
+ 
+     /* If we're pointing past the end of the keys array, there are no more */
+-    if (krcursor->currkey > krcursor->numkeys)
++    if (krcursor->currkey >= krcursor->numkeys)
+         return KRB5_CC_END;
+ 
+     /* If we're pointing at the entry with the principal, skip it */
+     if (krcursor->keys[krcursor->currkey] == krcursor->princ_id) {
+         krcursor->currkey++;
+         /* Check if we have now reached the end */
+-        if (krcursor->currkey > krcursor->numkeys)
++        if (krcursor->currkey >= krcursor->numkeys)
+             return KRB5_CC_END;
+     }
+ 
+@@ -763,7 +1193,7 @@ krb5_krcc_next_cred(krb5_context context, krb5_ccache id,
+     }
+     krcursor->currkey++;
+ 
+-    kret = krb5_krcc_parse_cred(context, id, creds, payload, psize);
++    kret = krb5_krcc_parse_cred(context, creds, payload, psize);
+ 
+ freepayload:
+     if (payload) free(payload);
+@@ -787,19 +1217,24 @@ static krb5_error_code KRB5_CALLCONV
+ krb5_krcc_end_seq_get(krb5_context context, krb5_ccache id,
+                       krb5_cc_cursor * cursor)
+ {
++    krb5_krcc_cursor krcursor = (krb5_krcc_cursor)*cursor;
+     DEBUG_PRINT(("krb5_krcc_end_seq_get: entered\n"));
+ 
+-    free(*cursor);
+-    *cursor = 0L;
++    if (krcursor != NULL) {
++        free(krcursor->keys);
++        free(krcursor);
++    }
++    *cursor = NULL;
+     return KRB5_OK;
+ }
+ 
+ /* Utility routine: Creates the back-end data for a keyring cache.
+ 
+    Call with the global list lock held.  */
+-static  krb5_error_code
+-krb5_krcc_new_data(const char *name, key_serial_t ring,
+-                   key_serial_t parent_ring, krb5_krcc_data ** datapp)
++static krb5_error_code
++krb5_krcc_new_data(const char *anchor_name, const char *collection_name,
++                   const char *subsidiary_name, key_serial_t cache_id,
++                   key_serial_t collection_id, krb5_krcc_data **datapp)
+ {
+     krb5_error_code kret;
+     krb5_krcc_data *d;
+@@ -814,17 +1249,18 @@ krb5_krcc_new_data(const char *name, key_serial_t ring,
+         return kret;
+     }
+ 
+-    d->name = strdup(name);
+-    if (d->name == NULL) {
++    kret = make_subsidiary_residual(anchor_name, collection_name,
++                                    subsidiary_name, &d->name);
++    if (kret) {
+         k5_cc_mutex_destroy(&d->lock);
+         free(d);
+-        return KRB5_CC_NOMEM;
++        return kret;
+     }
+     d->princ_id = 0;
+-    d->ring_id = ring;
+-    d->parent_id = parent_ring;
+-    d->numkeys = 0;
++    d->cache_id = cache_id;
++    d->collection_id = collection_id;
+     d->changetime = 0;
++    d->is_legacy_type = (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0);
+     krb5_krcc_update_change_time(d);
+ 
+     *datapp = d;
+@@ -846,82 +1282,73 @@ krb5_krcc_new_data(const char *name, key_serial_t ring,
+ static krb5_error_code KRB5_CALLCONV
+ krb5_krcc_generate_new(krb5_context context, krb5_ccache * id)
+ {
+-    krb5_ccache lid;
+-    char uniquename[8];
++    krb5_ccache lid = NULL;
+     krb5_error_code kret;
++    char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
++    char *new_subsidiary_name = NULL, *new_residual = NULL;
+     krb5_krcc_data *d;
+-    key_serial_t ring_id = KEY_SPEC_SESSION_KEYRING;
+-    key_serial_t key;
++    key_serial_t collection_id;
++    key_serial_t cache_id = 0;
+ 
+     DEBUG_PRINT(("krb5_krcc_generate_new: entered\n"));
+ 
++    /* Determine the collection in which we will create the cache.*/
++    kret = get_default(context, &anchor_name, &collection_name,
++                       &subsidiary_name);
++    if (kret)
++        return kret;
++    if (anchor_name == NULL) {
++        kret = parse_residual(KRCC_DEFAULT_UNIQUE_COLLECTION, &anchor_name,
++                              &collection_name, &subsidiary_name);
++        if (kret)
++            return kret;
++    }
++    if (subsidiary_name != NULL) {
++        krb5_set_error_message(context, KRB5_DCC_CANNOT_CREATE,
++                               _("Can't create new subsidiary cache because "
++                                 "default cache is already a subsdiary"));
++        kret = KRB5_DCC_CANNOT_CREATE;
++        goto cleanup;
++    }
++
+     /* Allocate memory */
+     lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
+-    if (lid == NULL)
+-        return KRB5_CC_NOMEM;
++    if (lid == NULL) {
++        kret = ENOMEM;
++        goto cleanup;
++    }
+ 
+     lid->ops = &krb5_krcc_ops;
+ 
+-    kret = k5_cc_mutex_lock(context, &krb5int_krcc_mutex);
+-    if (kret) {
+-        free(lid);
+-        return kret;
+-    }
+-
+-/* XXX These values are platform-specific and should not be here! */
+-/* XXX There is a bug in FC5 where these are not included in errno.h  */
+-#ifndef ENOKEY
+-#define ENOKEY          126     /* Required key not available */
+-#endif
+-#ifndef EKEYEXPIRED
+-#define EKEYEXPIRED     127     /* Key has expired */
+-#endif
+-#ifndef EKEYREVOKED
+-#define EKEYREVOKED     128     /* Key has been revoked */
+-#endif
+-#ifndef EKEYREJECTED
+-#define EKEYREJECTED    129     /* Key was rejected by service */
+-#endif
++    /* Make a unique keyring within the chosen collection. */
++    kret = get_collection(anchor_name, collection_name, &collection_id);
++    if (kret)
++        goto cleanup;
++    kret = unique_keyring(context, collection_id, &new_subsidiary_name,
++                          &cache_id);
++    if (kret)
++        goto cleanup;
+ 
+-    /*
+-     * Loop until we successfully create a new ccache keyring with
+-     * a unique name, or we get an error.
+-     */
+-    while (1) {
+-        kret = krb5int_random_string(context, uniquename, sizeof(uniquename));
+-        if (kret) {
+-            k5_cc_mutex_unlock(context, &krb5int_krcc_mutex);
+-            free(lid);
+-            return kret;
+-        }
++    kret = krb5_krcc_new_data(anchor_name, collection_name,
++                              new_subsidiary_name, cache_id, collection_id,
++                              &d);
++    if (kret)
++        goto cleanup;
+ 
+-        DEBUG_PRINT(("krb5_krcc_generate_new: searching for name '%s'\n",
+-                     uniquename));
+-        key = keyctl_search(ring_id, KRCC_KEY_TYPE_KEYRING, uniquename, 0);
+-        /*XXX*/ DEBUG_PRINT(("krb5_krcc_generate_new: after searching for '%s', key = %d, errno = %d\n", uniquename, key, errno));
+-        if (key < 0 && errno == ENOKEY) {
+-            /* name does not already exist, create it to reserve the name */
+-            key = add_key(KRCC_KEY_TYPE_KEYRING, uniquename, NULL, 0, ring_id);
+-            if (key < 0) {
+-                kret = errno;
+-                DEBUG_PRINT(("krb5_krcc_generate_new: '%s' trying to "
+-                             "create '%s'\n", strerror(errno), uniquename));
+-                k5_cc_mutex_unlock(context, &krb5int_krcc_mutex);
+-                return kret;
+-            }
+-            break;
+-        }
+-    }
++    lid->data = d;
++    krb5_change_cache();
+ 
+-    kret = krb5_krcc_new_data(uniquename, key, ring_id, &d);
+-    k5_cc_mutex_unlock(context, &krb5int_krcc_mutex);
++cleanup:
++    free(anchor_name);
++    free(collection_name);
++    free(subsidiary_name);
++    free(new_subsidiary_name);
++    free(new_residual);
+     if (kret) {
+         free(lid);
+         return kret;
+     }
+-    lid->data = d;
+     *id = lid;
+-    krb5_change_cache();
+     return KRB5_OK;
+ }
+ 
+@@ -1023,14 +1450,16 @@ krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds)
+     krb5_krcc_data *d = (krb5_krcc_data *) id->data;
+     char   *payload = NULL;
+     unsigned int payloadlen;
+-    key_serial_t newkey;
+     char   *keyname = NULL;
+ 
+     DEBUG_PRINT(("krb5_krcc_store: entered\n"));
+ 
+-    kret = k5_cc_mutex_lock(context, &d->lock);
+-    if (kret)
+-        return kret;
++    k5_cc_mutex_lock(context, &d->lock);
++
++    if (!d->cache_id) {
++        k5_cc_mutex_unlock(context, &d->lock);
++        return KRB5_FCC_NOFILE;
++    }
+ 
+     /* Get the service principal name and use it as the key name */
+     kret = krb5_unparse_name(context, creds->server, &keyname);
+@@ -1040,24 +1469,19 @@ krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds)
+     }
+ 
+     /* Serialize credential into memory */
+-    kret = krb5_krcc_unparse_cred(context, id, creds, &payload, &payloadlen);
++    kret = krb5_krcc_unparse_cred_alloc(context, creds, &payload, &payloadlen);
+     if (kret != KRB5_OK)
+         goto errout;
+ 
+     /* Add new key (credentials) into keyring */
+     DEBUG_PRINT(("krb5_krcc_store: adding new key '%s' to keyring %d\n",
+-                 keyname, d->ring_id));
+-    newkey = add_key(KRCC_KEY_TYPE_USER, keyname, payload,
+-                     payloadlen, d->ring_id);
+-    if (newkey < 0) {
+-        kret = errno;
+-        DEBUG_PRINT(("Error adding user key '%s': %s\n",
+-                     keyname, strerror(kret)));
+-    } else {
+-        d->numkeys++;
+-        kret = KRB5_OK;
+-        krb5_krcc_update_change_time(d);
+-    }
++                 keyname, d->cache_id));
++    kret = add_cred_key(keyname, payload, payloadlen, d->cache_id,
++                        d->is_legacy_type);
++    if (kret)
++        goto errout;
++
++    krb5_krcc_update_change_time(d);
+ 
+ errout:
+     if (keyname)
+@@ -1073,36 +1497,30 @@ static krb5_error_code KRB5_CALLCONV
+ krb5_krcc_last_change_time(krb5_context context, krb5_ccache id,
+                            krb5_timestamp *change_time)
+ {
+-    krb5_error_code ret = 0;
+     krb5_krcc_data *data = (krb5_krcc_data *) id->data;
+ 
+-    *change_time = 0;
+-
+-    ret = k5_cc_mutex_lock(context, &data->lock);
+-    if (!ret) {
+-        *change_time = data->changetime;
+-        k5_cc_mutex_unlock(context, &data->lock);
+-    }
+-
+-    return ret;
++    k5_cc_mutex_lock(context, &data->lock);
++    *change_time = data->changetime;
++    k5_cc_mutex_unlock(context, &data->lock);
++    return 0;
+ }
+ 
+ static krb5_error_code KRB5_CALLCONV
+ krb5_krcc_lock(krb5_context context, krb5_ccache id)
+ {
+-    krb5_error_code ret = 0;
+     krb5_krcc_data *data = (krb5_krcc_data *) id->data;
+-    ret = k5_cc_mutex_lock(context, &data->lock);
+-    return ret;
++
++    k5_cc_mutex_lock(context, &data->lock);
++    return 0;
+ }
+ 
+ static krb5_error_code KRB5_CALLCONV
+ krb5_krcc_unlock(krb5_context context, krb5_ccache id)
+ {
+-    krb5_error_code ret = 0;
+     krb5_krcc_data *data = (krb5_krcc_data *) id->data;
+-    ret = k5_cc_mutex_unlock(context, &data->lock);
+-    return ret;
++
++    k5_cc_mutex_unlock(context, &data->lock);
++    return 0;
+ }
+ 
+ 
+@@ -1112,7 +1530,7 @@ krb5_krcc_save_principal(krb5_context context, krb5_ccache id,
+ {
+     krb5_krcc_data *d;
+     krb5_error_code kret;
+-    char   *payload;
++    char *payload = NULL;
+     key_serial_t newkey;
+     unsigned int payloadsize;
+     krb5_krcc_bc bc;
+@@ -1121,14 +1539,19 @@ krb5_krcc_save_principal(krb5_context context, krb5_ccache id,
+ 
+     d = (krb5_krcc_data *) id->data;
+ 
+-    payload = malloc(GUESS_CRED_SIZE);
++    /* Do a dry run first to calculate the size. */
++    bc.bpp = bc.endp = NULL;
++    bc.size = 0;
++    kret = krb5_krcc_unparse_principal(context, princ, &bc);
++    CHECK_N_GO(kret, errout);
++
++    /* Allocate a buffer and serialize for real. */
++    payload = malloc(bc.size);
+     if (payload == NULL)
+         return KRB5_CC_NOMEM;
+-
+     bc.bpp = payload;
+-    bc.endp = payload + GUESS_CRED_SIZE;
+-
+-    kret = krb5_krcc_unparse_principal(context, id, princ, &bc);
++    bc.endp = payload + bc.size;
++    kret = krb5_krcc_unparse_principal(context, princ, &bc);
+     CHECK_N_GO(kret, errout);
+ 
+     /* Add new key into keyring */
+@@ -1140,14 +1563,14 @@ krb5_krcc_save_principal(krb5_context context, krb5_ccache id,
+         rc = krb5_unparse_name(context, princ, &princname);
+         DEBUG_PRINT(("krb5_krcc_save_principal: adding new key '%s' "
+                      "to keyring %d for principal '%s'\n",
+-                     KRCC_SPEC_PRINC_KEYNAME, d->ring_id,
++                     KRCC_SPEC_PRINC_KEYNAME, d->cache_id,
+                      rc ? "<unknown>" : princname));
+         if (rc == 0)
+             krb5_free_unparsed_name(context, princname);
+     }
+ #endif
+     newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME, payload,
+-                     payloadsize, d->ring_id);
++                     payloadsize, d->cache_id);
+     if (newkey < 0) {
+         kret = errno;
+         DEBUG_PRINT(("Error adding principal key: %s\n", strerror(kret)));
+@@ -1172,11 +1595,9 @@ krb5_krcc_retrieve_principal(krb5_context context, krb5_ccache id,
+     int     psize;
+     krb5_krcc_bc bc;
+ 
+-    kret = k5_cc_mutex_lock(context, &d->lock);
+-    if (kret)
+-        return kret;
++    k5_cc_mutex_lock(context, &d->lock);
+ 
+-    if (!d->princ_id) {
++    if (!d->cache_id || !d->princ_id) {
+         princ = 0L;
+         kret = KRB5_FCC_NOFILE;
+         goto errout;
+@@ -1191,7 +1612,7 @@ krb5_krcc_retrieve_principal(krb5_context context, krb5_ccache id,
+     }
+     bc.bpp = payload;
+     bc.endp = (char *)payload + psize;
+-    kret = krb5_krcc_parse_principal(context, id, princ, &bc);
++    kret = krb5_krcc_parse_principal(context, princ, &bc);
+ 
+ errout:
+     if (payload)
+@@ -1200,57 +1621,195 @@ errout:
+     return kret;
+ }
+ 
+-static int
+-krb5_krcc_get_ring_ids(krb5_krcc_ring_ids_t *p)
+-{
+-    key_serial_t ids_key;
+-    char ids_buf[128];
+-    key_serial_t session, process, thread;
+-    long val;
++struct krcc_ptcursor_data {
++    key_serial_t collection_id;
++    char *anchor_name;
++    char *collection_name;
++    char *subsidiary_name;
++    char *primary_name;
++    krb5_boolean first;
++    long num_keys;
++    long next_key;
++    key_serial_t *keys;
++};
+ 
+-    DEBUG_PRINT(("krb5_krcc_get_ring_ids: entered\n"));
++static krb5_error_code KRB5_CALLCONV
++krb5_krcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor_out)
++{
++    struct krcc_ptcursor_data *data;
++    krb5_cc_ptcursor cursor;
++    krb5_error_code ret;
++    long size;
++
++    *cursor_out = NULL;
++
++    cursor = k5alloc(sizeof(struct krb5_cc_ptcursor_s), &ret);
++    if (cursor == NULL)
++        return ENOMEM;
++    data = k5alloc(sizeof(struct krcc_ptcursor_data), &ret);
++    if (data == NULL)
++        goto error;
++    cursor->ops = &krb5_krcc_ops;
++    cursor->data = data;
++    data->first = TRUE;
++
++    ret = get_default(context, &data->anchor_name, &data->collection_name,
++                      &data->subsidiary_name);
++    if (ret)
++        goto error;
++
++    /* If there is no default collection, return an empty cursor. */
++    if (data->anchor_name == NULL) {
++        *cursor_out = cursor;
++        return 0;
++    }
+ 
+-    if (!p)
+-        return EINVAL;
++    ret = get_collection(data->anchor_name, data->collection_name,
++                         &data->collection_id);
++    if (ret)
++        goto error;
++
++    if (data->subsidiary_name == NULL) {
++        ret = get_primary_name(context, data->anchor_name,
++                               data->collection_name, data->collection_id,
++                               &data->primary_name);
++        if (ret)
++            goto error;
++
++        size = keyctl_read_alloc(data->collection_id, (void **)&data->keys);
++        if (size == -1) {
++            ret = errno;
++            goto error;
++        }
++        data->num_keys = size / sizeof(key_serial_t);
++    }
+ 
+-    /* Use the defaults in case we find no ids key */
+-    p->session = KEY_SPEC_SESSION_KEYRING;
+-    p->process = KEY_SPEC_PROCESS_KEYRING;
+-    p->thread = KEY_SPEC_THREAD_KEYRING;
++    *cursor_out = cursor;
++    return 0;
+ 
+-    /*
+-     * Note that in the "normal" case, this will not be found.
+-     * The Linux gssd creates this key while creating a
+-     * context to communicate the user's key serial numbers.
+-     */
+-    ids_key = request_key(KRCC_KEY_TYPE_USER, KRCC_SPEC_IDS_KEYNAME, NULL, 0);
+-    if (ids_key < 0)
+-        goto out;
++error:
++    krb5_krcc_ptcursor_free(context, &cursor);
++    return ret;
++}
+ 
+-    DEBUG_PRINT(("krb5_krcc_get_ring_ids: processing '%s' key %d\n",
+-                 KRCC_SPEC_IDS_KEYNAME, ids_key));
+-    /*
+-     * Read and parse the ids file
+-     */
+-    memset(ids_buf, '\0', sizeof(ids_buf));
+-    val = keyctl_read(ids_key, ids_buf, sizeof(ids_buf));
+-    if (val > sizeof(ids_buf))
+-        goto out;
++static krb5_error_code KRB5_CALLCONV
++krb5_krcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor,
++                        krb5_ccache *cache_out)
++{
++    krb5_error_code ret;
++    struct krcc_ptcursor_data *data;
++    key_serial_t key, cache_id = 0;
++    const char *first_name, *keytype, *sep, *subsidiary_name;
++    size_t keytypelen;
++    char *description = NULL;
++
++    *cache_out = NULL;
++
++    data = cursor->data;
++
++    /* No keyring available */
++    if (data->collection_id == 0)
++        return 0;
++
++    if (data->first) {
++        /* Look for the primary cache for a collection cursor, or the
++         * subsidiary cache for a subsidiary cursor. */
++        data->first = FALSE;
++        first_name = (data->primary_name != NULL) ? data->primary_name :
++            data->subsidiary_name;
++        cache_id = keyctl_search(data->collection_id, KRCC_KEY_TYPE_KEYRING,
++                                 first_name, 0);
++        if (cache_id != -1) {
++            return make_cache(data->collection_id, cache_id, data->anchor_name,
++                              data->collection_name, first_name, cache_out);
++        }
++    }
+ 
+-    val = sscanf(ids_buf, "%d:%d:%d", &session, &process, &thread);
+-    if (val != 3)
+-        goto out;
++    /* A subsidiary cursor yields at most the first cache. */
++    if (data->subsidiary_name != NULL)
++        return 0;
++
++    keytype = KRCC_KEY_TYPE_KEYRING ";";
++    keytypelen = strlen(keytype);
++
++    for (; data->next_key < data->num_keys; data->next_key++) {
++        /* Free any previously retrieved key description. */
++        free(description);
++        description = NULL;
++
++        /*
++         * Get the key description, which should have the form:
++         *   typename;UID;GID;permissions;description
++         */
++        key = data->keys[data->next_key];
++        if (keyctl_describe_alloc(key, &description) < 0)
++            continue;
++        sep = strrchr(description, ';');
++        if (sep == NULL)
++            continue;
++        subsidiary_name = sep + 1;
++
++        /* Skip this key if it isn't a keyring. */
++        if (strncmp(description, keytype, keytypelen) != 0)
++            continue;
++
++        /* Don't repeat the primary cache. */
++        if (strcmp(subsidiary_name, data->primary_name) == 0)
++            continue;
++
++        /* We found a valid key */
++        data->next_key++;
++        ret = make_cache(data->collection_id, key, data->anchor_name,
++                         data->collection_name, subsidiary_name, cache_out);
++        free(description);
++        return ret;
++    }
+ 
+-    p->session = session;
+-    p->process = process;
+-    p->thread = thread;
++    free(description);
++    return 0;
++}
+ 
+-out:
+-    DEBUG_PRINT(("krb5_krcc_get_ring_ids: returning %d:%d:%d\n",
+-                 p->session, p->process, p->thread));
++static krb5_error_code KRB5_CALLCONV
++krb5_krcc_ptcursor_free(krb5_context context, krb5_cc_ptcursor *cursor)
++{
++    struct krcc_ptcursor_data *data = (*cursor)->data;
++
++    if (data != NULL) {
++        free(data->anchor_name);
++        free(data->collection_name);
++        free(data->subsidiary_name);
++        free(data->primary_name);
++        free(data->keys);
++        free(data);
++    }
++    free(*cursor);
++    *cursor = NULL;
+     return 0;
+ }
+ 
++static krb5_error_code KRB5_CALLCONV
++krb5_krcc_switch_to(krb5_context context, krb5_ccache cache)
++{
++    krb5_krcc_data *data = cache->data;
++    krb5_error_code ret;
++    char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
++    key_serial_t collection_id;
++
++    ret = parse_residual(data->name, &anchor_name, &collection_name,
++                         &subsidiary_name);
++    if (ret)
++        goto cleanup;
++    ret = get_collection(anchor_name, collection_name, &collection_id);
++    if (ret)
++        goto cleanup;
++    ret = set_primary_name(context, collection_id, subsidiary_name);
++cleanup:
++    free(anchor_name);
++    free(collection_name);
++    free(subsidiary_name);
++    return ret;
++}
++
+ /*
+  * ===============================================================
+  * INTERNAL functions to parse a credential from a key payload
+@@ -1271,8 +1830,8 @@ out:
+  * KRB5_CC_END - there were not len bytes available
+  */
+ static  krb5_error_code
+-krb5_krcc_parse(krb5_context context, krb5_ccache id, krb5_pointer buf,
+-                unsigned int len, krb5_krcc_bc * bc)
++krb5_krcc_parse(krb5_context context, krb5_pointer buf, unsigned int len,
++                krb5_krcc_bc * bc)
+ {
+     DEBUG_PRINT(("krb5_krcc_parse: entered\n"));
+ 
+@@ -1290,8 +1849,8 @@ krb5_krcc_parse(krb5_context context, krb5_ccache id, krb5_pointer buf,
+  * and parse it into a credential structure.
+  */
+ static  krb5_error_code
+-krb5_krcc_parse_cred(krb5_context context, krb5_ccache id, krb5_creds * creds,
+-                     char *payload, int psize)
++krb5_krcc_parse_cred(krb5_context context, krb5_creds * creds, char *payload,
++                     int psize)
+ {
+     krb5_error_code kret;
+     krb5_octet octet;
+@@ -1301,36 +1860,36 @@ krb5_krcc_parse_cred(krb5_context context, krb5_ccache id, krb5_creds * creds,
+     /* Parse the pieces of the credential */
+     bc.bpp = payload;
+     bc.endp = bc.bpp + psize;
+-    kret = krb5_krcc_parse_principal(context, id, &creds->client, &bc);
++    kret = krb5_krcc_parse_principal(context, &creds->client, &bc);
+     CHECK_N_GO(kret, out);
+ 
+-    kret = krb5_krcc_parse_principal(context, id, &creds->server, &bc);
++    kret = krb5_krcc_parse_principal(context, &creds->server, &bc);
+     CHECK_N_GO(kret, cleanclient);
+ 
+-    kret = krb5_krcc_parse_keyblock(context, id, &creds->keyblock, &bc);
++    kret = krb5_krcc_parse_keyblock(context, &creds->keyblock, &bc);
+     CHECK_N_GO(kret, cleanserver);
+ 
+-    kret = krb5_krcc_parse_times(context, id, &creds->times, &bc);
++    kret = krb5_krcc_parse_times(context, &creds->times, &bc);
+     CHECK_N_GO(kret, cleanserver);
+ 
+-    kret = krb5_krcc_parse_octet(context, id, &octet, &bc);
++    kret = krb5_krcc_parse_octet(context, &octet, &bc);
+     CHECK_N_GO(kret, cleanserver);
+     creds->is_skey = octet;
+ 
+-    kret = krb5_krcc_parse_int32(context, id, &int32, &bc);
++    kret = krb5_krcc_parse_int32(context, &int32, &bc);
+     CHECK_N_GO(kret, cleanserver);
+     creds->ticket_flags = int32;
+ 
+-    kret = krb5_krcc_parse_addrs(context, id, &creds->addresses, &bc);
++    kret = krb5_krcc_parse_addrs(context, &creds->addresses, &bc);
+     CHECK_N_GO(kret, cleanblock);
+ 
+-    kret = krb5_krcc_parse_authdata(context, id, &creds->authdata, &bc);
++    kret = krb5_krcc_parse_authdata(context, &creds->authdata, &bc);
+     CHECK_N_GO(kret, cleanaddrs);
+ 
+-    kret = krb5_krcc_parse_krb5data(context, id, &creds->ticket, &bc);
++    kret = krb5_krcc_parse_krb5data(context, &creds->ticket, &bc);
+     CHECK_N_GO(kret, cleanauthdata);
+ 
+-    kret = krb5_krcc_parse_krb5data(context, id, &creds->second_ticket, &bc);
++    kret = krb5_krcc_parse_krb5data(context, &creds->second_ticket, &bc);
+     CHECK_N_GO(kret, cleanticket);
+ 
+     kret = KRB5_OK;
+@@ -1355,8 +1914,8 @@ out:
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_parse_principal(krb5_context context, krb5_ccache id,
+-                          krb5_principal * princ, krb5_krcc_bc * bc)
++krb5_krcc_parse_principal(krb5_context context, krb5_principal * princ,
++                          krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+     register krb5_principal tmpprinc;
+@@ -1364,12 +1923,12 @@ krb5_krcc_parse_principal(krb5_context context, krb5_ccache id,
+     int     i;
+ 
+     /* Read principal type */
+-    kret = krb5_krcc_parse_int32(context, id, &type, bc);
++    kret = krb5_krcc_parse_int32(context, &type, bc);
+     if (kret != KRB5_OK)
+         return kret;
+ 
+     /* Read the number of components */
+-    kret = krb5_krcc_parse_int32(context, id, &length, bc);
++    kret = krb5_krcc_parse_int32(context, &length, bc);
+     if (kret != KRB5_OK)
+         return kret;
+ 
+@@ -1380,12 +1939,7 @@ krb5_krcc_parse_principal(krb5_context context, krb5_ccache id,
+     if (tmpprinc == NULL)
+         return KRB5_CC_NOMEM;
+     if (length) {
+-        size_t  msize = length;
+-        if (msize != length) {
+-            free(tmpprinc);
+-            return KRB5_CC_NOMEM;
+-        }
+-        tmpprinc->data = ALLOC(msize, krb5_data);
++        tmpprinc->data = calloc(length, sizeof(krb5_data));
+         if (tmpprinc->data == 0) {
+             free(tmpprinc);
+             return KRB5_CC_NOMEM;
+@@ -1396,15 +1950,12 @@ krb5_krcc_parse_principal(krb5_context context, krb5_ccache id,
+     tmpprinc->length = length;
+     tmpprinc->type = type;
+ 
+-    kret = krb5_krcc_parse_krb5data(context, id,
+-                                    krb5_princ_realm(context, tmpprinc), bc);
++    kret = krb5_krcc_parse_krb5data(context, &tmpprinc->realm, bc);
+     i = 0;
+     CHECK(kret);
+ 
+     for (i = 0; i < length; i++) {
+-        kret = krb5_krcc_parse_krb5data(context, id,
+-                                        krb5_princ_component(context, tmpprinc,
+-                                                             i), bc);
++        kret = krb5_krcc_parse_krb5data(context, &tmpprinc->data[i], bc);
+         CHECK(kret);
+     }
+     *princ = tmpprinc;
+@@ -1412,16 +1963,16 @@ krb5_krcc_parse_principal(krb5_context context, krb5_ccache id,
+ 
+ errout:
+     while (--i >= 0)
+-        free(krb5_princ_component(context, tmpprinc, i)->data);
+-    free(krb5_princ_realm(context, tmpprinc)->data);
++        free(tmpprinc->data[i].data);
++    free(tmpprinc->realm.data);
+     free(tmpprinc->data);
+     free(tmpprinc);
+     return kret;
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_parse_keyblock(krb5_context context, krb5_ccache id,
+-                         krb5_keyblock * keyblock, krb5_krcc_bc * bc)
++krb5_krcc_parse_keyblock(krb5_context context, krb5_keyblock * keyblock,
++                         krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+     krb5_ui_2 ui2;
+@@ -1430,26 +1981,22 @@ krb5_krcc_parse_keyblock(krb5_context context, krb5_ccache id,
+     keyblock->magic = KV5M_KEYBLOCK;
+     keyblock->contents = 0;
+ 
+-    kret = krb5_krcc_parse_ui_2(context, id, &ui2, bc);
++    kret = krb5_krcc_parse_ui_2(context, &ui2, bc);
+     CHECK(kret);
+     keyblock->enctype = ui2;
+ 
+-    kret = krb5_krcc_parse_int32(context, id, &int32, bc);
++    kret = krb5_krcc_parse_int32(context, &int32, bc);
+     CHECK(kret);
+     if (int32 < 0)
+         return KRB5_CC_NOMEM;
+     keyblock->length = int32;
+-    /* Overflow check.  */
+-    if (keyblock->length != int32)
+-        return KRB5_CC_NOMEM;
+     if (keyblock->length == 0)
+         return KRB5_OK;
+-    keyblock->contents = ALLOC(keyblock->length, krb5_octet);
++    keyblock->contents = malloc(keyblock->length);
+     if (keyblock->contents == NULL)
+         return KRB5_CC_NOMEM;
+ 
+-    kret = krb5_krcc_parse(context, id, keyblock->contents,
+-                           keyblock->length, bc);
++    kret = krb5_krcc_parse(context, keyblock->contents, keyblock->length, bc);
+     CHECK(kret);
+ 
+     return KRB5_OK;
+@@ -1460,25 +2007,25 @@ errout:
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_parse_times(krb5_context context, krb5_ccache id,
+-                      krb5_ticket_times * t, krb5_krcc_bc * bc)
++krb5_krcc_parse_times(krb5_context context, krb5_ticket_times * t,
++                      krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+     krb5_int32 i;
+ 
+-    kret = krb5_krcc_parse_int32(context, id, &i, bc);
++    kret = krb5_krcc_parse_int32(context, &i, bc);
+     CHECK(kret);
+     t->authtime = i;
+ 
+-    kret = krb5_krcc_parse_int32(context, id, &i, bc);
++    kret = krb5_krcc_parse_int32(context, &i, bc);
+     CHECK(kret);
+     t->starttime = i;
+ 
+-    kret = krb5_krcc_parse_int32(context, id, &i, bc);
++    kret = krb5_krcc_parse_int32(context, &i, bc);
+     CHECK(kret);
+     t->endtime = i;
+ 
+-    kret = krb5_krcc_parse_int32(context, id, &i, bc);
++    kret = krb5_krcc_parse_int32(context, &i, bc);
+     CHECK(kret);
+     t->renew_till = i;
+ 
+@@ -1488,8 +2035,8 @@ errout:
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_parse_krb5data(krb5_context context, krb5_ccache id,
+-                         krb5_data * data, krb5_krcc_bc * bc)
++krb5_krcc_parse_krb5data(krb5_context context, krb5_data * data,
++                         krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+     krb5_int32 len;
+@@ -1497,12 +2044,12 @@ krb5_krcc_parse_krb5data(krb5_context context, krb5_ccache id,
+     data->magic = KV5M_DATA;
+     data->data = 0;
+ 
+-    kret = krb5_krcc_parse_int32(context, id, &len, bc);
++    kret = krb5_krcc_parse_int32(context, &len, bc);
+     CHECK(kret);
+     if (len < 0)
+         return KRB5_CC_NOMEM;
+     data->length = len;
+-    if (data->length != len || data->length + 1 == 0)
++    if (data->length + 1 == 0)
+         return KRB5_CC_NOMEM;
+ 
+     if (data->length == 0) {
+@@ -1514,8 +2061,7 @@ krb5_krcc_parse_krb5data(krb5_context context, krb5_ccache id,
+     if (data->data == NULL)
+         return KRB5_CC_NOMEM;
+ 
+-    kret = krb5_krcc_parse(context, id, data->data, (unsigned) data->length,
+-                           bc);
++    kret = krb5_krcc_parse(context, data->data, (unsigned) data->length, bc);
+     CHECK(kret);
+ 
+     data->data[data->length] = 0;       /* Null terminate, just in case.... */
+@@ -1527,13 +2073,12 @@ errout:
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_parse_int32(krb5_context context, krb5_ccache id, krb5_int32 * i,
+-                      krb5_krcc_bc * bc)
++krb5_krcc_parse_int32(krb5_context context, krb5_int32 * i, krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+     unsigned char buf[4];
+ 
+-    kret = krb5_krcc_parse(context, id, buf, 4, bc);
++    kret = krb5_krcc_parse(context, buf, 4, bc);
+     if (kret)
+         return kret;
+     *i = load_32_be(buf);
+@@ -1541,15 +2086,14 @@ krb5_krcc_parse_int32(krb5_context context, krb5_ccache id, krb5_int32 * i,
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_parse_octet(krb5_context context, krb5_ccache id, krb5_octet * i,
+-                      krb5_krcc_bc * bc)
++krb5_krcc_parse_octet(krb5_context context, krb5_octet * i, krb5_krcc_bc * bc)
+ {
+-    return krb5_krcc_parse(context, id, (krb5_pointer) i, 1, bc);
++    return krb5_krcc_parse(context, (krb5_pointer) i, 1, bc);
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_parse_addrs(krb5_context context, krb5_ccache id,
+-                      krb5_address *** addrs, krb5_krcc_bc * bc)
++krb5_krcc_parse_addrs(krb5_context context, krb5_address *** addrs,
++                      krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+     krb5_int32 length;
+@@ -1559,18 +2103,17 @@ krb5_krcc_parse_addrs(krb5_context context, krb5_ccache id,
+     *addrs = 0;
+ 
+     /* Read the number of components */
+-    kret = krb5_krcc_parse_int32(context, id, &length, bc);
++    kret = krb5_krcc_parse_int32(context, &length, bc);
+     CHECK(kret);
+ 
+     /*
+      * Make *addrs able to hold length pointers to krb5_address structs
+      * Add one extra for a null-terminated list
+      */
+-    msize = length;
+-    msize += 1;
+-    if (msize == 0 || msize - 1 != length || length < 0)
++    msize = (size_t)length + 1;
++    if (msize == 0 || length < 0)
+         return KRB5_CC_NOMEM;
+-    *addrs = ALLOC(msize, krb5_address *);
++    *addrs = calloc(msize, sizeof(krb5_address *));
+     if (*addrs == NULL)
+         return KRB5_CC_NOMEM;
+ 
+@@ -1580,7 +2123,7 @@ krb5_krcc_parse_addrs(krb5_context context, krb5_ccache id,
+             krb5_free_addresses(context, *addrs);
+             return KRB5_CC_NOMEM;
+         }
+-        kret = krb5_krcc_parse_addr(context, id, (*addrs)[i], bc);
++        kret = krb5_krcc_parse_addr(context, (*addrs)[i], bc);
+         CHECK(kret);
+     }
+ 
+@@ -1592,7 +2135,7 @@ errout:
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_parse_addr(krb5_context context, krb5_ccache id, krb5_address * addr,
++krb5_krcc_parse_addr(krb5_context context, krb5_address * addr,
+                      krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+@@ -1602,22 +2145,15 @@ krb5_krcc_parse_addr(krb5_context context, krb5_ccache id, krb5_address * addr,
+     addr->magic = KV5M_ADDRESS;
+     addr->contents = 0;
+ 
+-    kret = krb5_krcc_parse_ui_2(context, id, &ui2, bc);
++    kret = krb5_krcc_parse_ui_2(context, &ui2, bc);
+     CHECK(kret);
+     addr->addrtype = ui2;
+ 
+-    kret = krb5_krcc_parse_int32(context, id, &int32, bc);
++    kret = krb5_krcc_parse_int32(context, &int32, bc);
+     CHECK(kret);
+     if ((int32 & VALID_INT_BITS) != int32)      /* Overflow int??? */
+         return KRB5_CC_NOMEM;
+     addr->length = int32;
+-    /*
+-     * Length field is "unsigned int", which may be smaller
+-     * than 32 bits.
+-     */
+-    if (addr->length != int32)
+-        return KRB5_CC_NOMEM;   /* XXX */
+-
+     if (addr->length == 0)
+         return KRB5_OK;
+ 
+@@ -1625,7 +2161,7 @@ krb5_krcc_parse_addr(krb5_context context, krb5_ccache id, krb5_address * addr,
+     if (addr->contents == NULL)
+         return KRB5_CC_NOMEM;
+ 
+-    kret = krb5_krcc_parse(context, id, addr->contents, addr->length, bc);
++    kret = krb5_krcc_parse(context, addr->contents, addr->length, bc);
+     CHECK(kret);
+ 
+     return KRB5_OK;
+@@ -1636,8 +2172,8 @@ errout:
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_parse_authdata(krb5_context context, krb5_ccache id,
+-                         krb5_authdata *** a, krb5_krcc_bc * bc)
++krb5_krcc_parse_authdata(krb5_context context, krb5_authdata *** a,
++                         krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+     krb5_int32 length;
+@@ -1647,7 +2183,7 @@ krb5_krcc_parse_authdata(krb5_context context, krb5_ccache id,
+     *a = 0;
+ 
+     /* Read the number of components */
+-    kret = krb5_krcc_parse_int32(context, id, &length, bc);
++    kret = krb5_krcc_parse_int32(context, &length, bc);
+     CHECK(kret);
+ 
+     if (length == 0)
+@@ -1657,11 +2193,10 @@ krb5_krcc_parse_authdata(krb5_context context, krb5_ccache id,
+      * Make *a able to hold length pointers to krb5_authdata structs
+      * Add one extra for a null-terminated list
+      */
+-    msize = length;
+-    msize += 1;
+-    if (msize == 0 || msize - 1 != length || length < 0)
++    msize = (size_t)length + 1;
++    if (msize == 0 || length < 0)
+         return KRB5_CC_NOMEM;
+-    *a = ALLOC(msize, krb5_authdata *);
++    *a = calloc(msize, sizeof(krb5_authdata *));
+     if (*a == NULL)
+         return KRB5_CC_NOMEM;
+ 
+@@ -1672,7 +2207,7 @@ krb5_krcc_parse_authdata(krb5_context context, krb5_ccache id,
+             *a = NULL;
+             return KRB5_CC_NOMEM;
+         }
+-        kret = krb5_krcc_parse_authdatum(context, id, (*a)[i], bc);
++        kret = krb5_krcc_parse_authdatum(context, (*a)[i], bc);
+         CHECK(kret);
+     }
+ 
+@@ -1686,8 +2221,8 @@ errout:
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_parse_authdatum(krb5_context context, krb5_ccache id,
+-                          krb5_authdata * a, krb5_krcc_bc * bc)
++krb5_krcc_parse_authdatum(krb5_context context, krb5_authdata * a,
++                          krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+     krb5_int32 int32;
+@@ -1696,21 +2231,14 @@ krb5_krcc_parse_authdatum(krb5_context context, krb5_ccache id,
+     a->magic = KV5M_AUTHDATA;
+     a->contents = NULL;
+ 
+-    kret = krb5_krcc_parse_ui_2(context, id, &ui2, bc);
++    kret = krb5_krcc_parse_ui_2(context, &ui2, bc);
+     CHECK(kret);
+     a->ad_type = (krb5_authdatatype) ui2;
+-    kret = krb5_krcc_parse_int32(context, id, &int32, bc);
++    kret = krb5_krcc_parse_int32(context, &int32, bc);
+     CHECK(kret);
+     if ((int32 & VALID_INT_BITS) != int32)      /* Overflow int??? */
+         return KRB5_CC_NOMEM;
+     a->length = int32;
+-    /*
+-     * Value could have gotten truncated if int is
+-     * smaller than 32 bits.
+-     */
+-    if (a->length != int32)
+-        return KRB5_CC_NOMEM;   /* XXX */
+-
+     if (a->length == 0)
+         return KRB5_OK;
+ 
+@@ -1718,7 +2246,7 @@ krb5_krcc_parse_authdatum(krb5_context context, krb5_ccache id,
+     if (a->contents == NULL)
+         return KRB5_CC_NOMEM;
+ 
+-    kret = krb5_krcc_parse(context, id, a->contents, a->length, bc);
++    kret = krb5_krcc_parse(context, a->contents, a->length, bc);
+     CHECK(kret);
+ 
+     return KRB5_OK;
+@@ -1730,13 +2258,12 @@ errout:
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_parse_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 * i,
+-                     krb5_krcc_bc * bc)
++krb5_krcc_parse_ui_2(krb5_context context, krb5_ui_2 * i, krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+     unsigned char buf[2];
+ 
+-    kret = krb5_krcc_parse(context, id, buf, 2, bc);
++    kret = krb5_krcc_parse(context, buf, 2, bc);
+     if (kret)
+         return kret;
+     *i = load_16_be(buf);
+@@ -1756,9 +2283,15 @@ krb5_krcc_parse_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 * i,
+  * system errors
+  */
+ static  krb5_error_code
+-krb5_krcc_unparse(krb5_context context, krb5_ccache id, krb5_pointer buf,
+-                  unsigned int len, krb5_krcc_bc * bc)
++krb5_krcc_unparse(krb5_context context, krb5_pointer buf, unsigned int len,
++                  krb5_krcc_bc * bc)
+ {
++    if (bc->bpp == NULL) {
++        /* This is a dry run; just increase size and return. */
++        bc->size += len;
++        return KRB5_OK;
++    }
++
+     if (bc->bpp + len > bc->endp)
+         return KRB5_CC_WRITE;
+ 
+@@ -1769,29 +2302,26 @@ krb5_krcc_unparse(krb5_context context, krb5_ccache id, krb5_pointer buf,
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_principal(krb5_context context, krb5_ccache id,
+-                            krb5_principal princ, krb5_krcc_bc * bc)
++krb5_krcc_unparse_principal(krb5_context context, krb5_principal princ,
++                            krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+     krb5_int32 i, length, tmp, type;
+ 
+-    type = krb5_princ_type(context, princ);
+-    tmp = length = krb5_princ_size(context, princ);
++    type = princ->type;
++    tmp = length = princ->length;
+ 
+-    kret = krb5_krcc_unparse_int32(context, id, type, bc);
++    kret = krb5_krcc_unparse_int32(context, type, bc);
+     CHECK_OUT(kret);
+ 
+-    kret = krb5_krcc_unparse_int32(context, id, tmp, bc);
++    kret = krb5_krcc_unparse_int32(context, tmp, bc);
+     CHECK_OUT(kret);
+ 
+-    kret = krb5_krcc_unparse_krb5data(context, id,
+-                                      krb5_princ_realm(context, princ), bc);
++    kret = krb5_krcc_unparse_krb5data(context, &princ->realm, bc);
+     CHECK_OUT(kret);
+ 
+     for (i = 0; i < length; i++) {
+-        kret = krb5_krcc_unparse_krb5data(context, id,
+-                                          krb5_princ_component(context, princ,
+-                                                               i), bc);
++        kret = krb5_krcc_unparse_krb5data(context, &princ->data[i], bc);
+         CHECK_OUT(kret);
+     }
+ 
+@@ -1799,67 +2329,65 @@ krb5_krcc_unparse_principal(krb5_context context, krb5_ccache id,
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_keyblock(krb5_context context, krb5_ccache id,
+-                           krb5_keyblock * keyblock, krb5_krcc_bc * bc)
++krb5_krcc_unparse_keyblock(krb5_context context, krb5_keyblock * keyblock,
++                           krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+ 
+-    kret = krb5_krcc_unparse_ui_2(context, id, keyblock->enctype, bc);
++    kret = krb5_krcc_unparse_ui_2(context, keyblock->enctype, bc);
+     CHECK_OUT(kret);
+-    kret = krb5_krcc_unparse_ui_4(context, id, keyblock->length, bc);
++    kret = krb5_krcc_unparse_ui_4(context, keyblock->length, bc);
+     CHECK_OUT(kret);
+-    return krb5_krcc_unparse(context, id, (char *) keyblock->contents,
++    return krb5_krcc_unparse(context, (char *) keyblock->contents,
+                              keyblock->length, bc);
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_times(krb5_context context, krb5_ccache id,
+-                        krb5_ticket_times * t, krb5_krcc_bc * bc)
++krb5_krcc_unparse_times(krb5_context context, krb5_ticket_times * t,
++                        krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+ 
+-    kret = krb5_krcc_unparse_int32(context, id, t->authtime, bc);
++    kret = krb5_krcc_unparse_int32(context, t->authtime, bc);
+     CHECK_OUT(kret);
+-    kret = krb5_krcc_unparse_int32(context, id, t->starttime, bc);
++    kret = krb5_krcc_unparse_int32(context, t->starttime, bc);
+     CHECK_OUT(kret);
+-    kret = krb5_krcc_unparse_int32(context, id, t->endtime, bc);
++    kret = krb5_krcc_unparse_int32(context, t->endtime, bc);
+     CHECK_OUT(kret);
+-    kret = krb5_krcc_unparse_int32(context, id, t->renew_till, bc);
++    kret = krb5_krcc_unparse_int32(context, t->renew_till, bc);
+     CHECK_OUT(kret);
+     return 0;
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_krb5data(krb5_context context, krb5_ccache id,
+-                           krb5_data * data, krb5_krcc_bc * bc)
++krb5_krcc_unparse_krb5data(krb5_context context, krb5_data * data,
++                           krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+ 
+-    kret = krb5_krcc_unparse_ui_4(context, id, data->length, bc);
++    kret = krb5_krcc_unparse_ui_4(context, data->length, bc);
+     CHECK_OUT(kret);
+-    return krb5_krcc_unparse(context, id, data->data, data->length, bc);
++    return krb5_krcc_unparse(context, data->data, data->length, bc);
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_int32(krb5_context context, krb5_ccache id, krb5_int32 i,
+-                        krb5_krcc_bc * bc)
++krb5_krcc_unparse_int32(krb5_context context, krb5_int32 i, krb5_krcc_bc * bc)
+ {
+-    return krb5_krcc_unparse_ui_4(context, id, (krb5_ui_4) i, bc);
++    return krb5_krcc_unparse_ui_4(context, (krb5_ui_4) i, bc);
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_octet(krb5_context context, krb5_ccache id, krb5_int32 i,
+-                        krb5_krcc_bc * bc)
++krb5_krcc_unparse_octet(krb5_context context, krb5_int32 i, krb5_krcc_bc * bc)
+ {
+     krb5_octet ibuf;
+ 
+     ibuf = (krb5_octet) i;
+-    return krb5_krcc_unparse(context, id, (char *) &ibuf, 1, bc);
++    return krb5_krcc_unparse(context, (char *) &ibuf, 1, bc);
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_addrs(krb5_context context, krb5_ccache id,
+-                        krb5_address ** addrs, krb5_krcc_bc * bc)
++krb5_krcc_unparse_addrs(krb5_context context, krb5_address ** addrs,
++                        krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+     krb5_address **temp;
+@@ -1872,10 +2400,10 @@ krb5_krcc_unparse_addrs(krb5_context context, krb5_ccache id,
+             length += 1;
+     }
+ 
+-    kret = krb5_krcc_unparse_int32(context, id, length, bc);
++    kret = krb5_krcc_unparse_int32(context, length, bc);
+     CHECK_OUT(kret);
+     for (i = 0; i < length; i++) {
+-        kret = krb5_krcc_unparse_addr(context, id, addrs[i], bc);
++        kret = krb5_krcc_unparse_addr(context, addrs[i], bc);
+         CHECK_OUT(kret);
+     }
+ 
+@@ -1883,21 +2411,21 @@ krb5_krcc_unparse_addrs(krb5_context context, krb5_ccache id,
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_addr(krb5_context context, krb5_ccache id,
+-                       krb5_address * addr, krb5_krcc_bc * bc)
++krb5_krcc_unparse_addr(krb5_context context, krb5_address * addr,
++                       krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+ 
+-    kret = krb5_krcc_unparse_ui_2(context, id, addr->addrtype, bc);
++    kret = krb5_krcc_unparse_ui_2(context, addr->addrtype, bc);
+     CHECK_OUT(kret);
+-    kret = krb5_krcc_unparse_ui_4(context, id, addr->length, bc);
++    kret = krb5_krcc_unparse_ui_4(context, addr->length, bc);
+     CHECK_OUT(kret);
+-    return krb5_krcc_unparse(context, id, (char *) addr->contents,
++    return krb5_krcc_unparse(context, (char *) addr->contents,
+                              addr->length, bc);
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_authdata(krb5_context context, krb5_ccache id,
++krb5_krcc_unparse_authdata(krb5_context context,
+                            krb5_authdata ** a, krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+@@ -1909,47 +2437,45 @@ krb5_krcc_unparse_authdata(krb5_context context, krb5_ccache id,
+             length++;
+     }
+ 
+-    kret = krb5_krcc_unparse_int32(context, id, length, bc);
++    kret = krb5_krcc_unparse_int32(context, length, bc);
+     CHECK_OUT(kret);
+     for (i = 0; i < length; i++) {
+-        kret = krb5_krcc_unparse_authdatum(context, id, a[i], bc);
++        kret = krb5_krcc_unparse_authdatum(context, a[i], bc);
+         CHECK_OUT(kret);
+     }
+     return KRB5_OK;
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_authdatum(krb5_context context, krb5_ccache id,
+-                            krb5_authdata * a, krb5_krcc_bc * bc)
++krb5_krcc_unparse_authdatum(krb5_context context, krb5_authdata * a,
++                            krb5_krcc_bc * bc)
+ {
+     krb5_error_code kret;
+ 
+-    kret = krb5_krcc_unparse_ui_2(context, id, a->ad_type, bc);
++    kret = krb5_krcc_unparse_ui_2(context, a->ad_type, bc);
+     CHECK_OUT(kret);
+-    kret = krb5_krcc_unparse_ui_4(context, id, a->length, bc);
++    kret = krb5_krcc_unparse_ui_4(context, a->length, bc);
+     CHECK_OUT(kret);
+-    return krb5_krcc_unparse(context, id, (krb5_pointer) a->contents,
++    return krb5_krcc_unparse(context, (krb5_pointer) a->contents,
+                              a->length, bc);
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_ui_4(krb5_context context, krb5_ccache id, krb5_ui_4 i,
+-                       krb5_krcc_bc * bc)
++krb5_krcc_unparse_ui_4(krb5_context context, krb5_ui_4 i, krb5_krcc_bc * bc)
+ {
+     unsigned char buf[4];
+ 
+     store_32_be(i, buf);
+-    return krb5_krcc_unparse(context, id, buf, 4, bc);
++    return krb5_krcc_unparse(context, buf, 4, bc);
+ }
+ 
+ static  krb5_error_code
+-krb5_krcc_unparse_ui_2(krb5_context context, krb5_ccache id, krb5_int32 i,
+-                       krb5_krcc_bc * bc)
++krb5_krcc_unparse_ui_2(krb5_context context, krb5_int32 i, krb5_krcc_bc * bc)
+ {
+     unsigned char buf[2];
+ 
+     store_16_be(i, buf);
+-    return krb5_krcc_unparse(context, id, buf, 2, bc);
++    return krb5_krcc_unparse(context, buf, 2, bc);
+ }
+ 
+ /*
+@@ -1965,11 +2491,55 @@ krb5_krcc_unparse_ui_2(krb5_context context, krb5_ccache id, krb5_int32 i,
+  * Caller is responsible for freeing returned buffer.
+  */
+ static  krb5_error_code
+-krb5_krcc_unparse_cred(krb5_context context, krb5_ccache id,
+-                       krb5_creds * creds, char **datapp, unsigned int *lenptr)
++krb5_krcc_unparse_cred(krb5_context context, krb5_creds * creds,
++                       krb5_krcc_bc *bc)
+ {
+     krb5_error_code kret;
+-    char   *buf;
++
++    kret = krb5_krcc_unparse_principal(context, creds->client, bc);
++    CHECK_OUT(kret);
++
++    kret = krb5_krcc_unparse_principal(context, creds->server, bc);
++    CHECK_OUT(kret);
++
++    kret = krb5_krcc_unparse_keyblock(context, &creds->keyblock, bc);
++    CHECK_OUT(kret);
++
++    kret = krb5_krcc_unparse_times(context, &creds->times, bc);
++    CHECK_OUT(kret);
++
++    kret = krb5_krcc_unparse_octet(context, (krb5_int32) creds->is_skey, bc);
++    CHECK_OUT(kret);
++
++    kret = krb5_krcc_unparse_int32(context, creds->ticket_flags, bc);
++    CHECK_OUT(kret);
++
++    kret = krb5_krcc_unparse_addrs(context, creds->addresses, bc);
++    CHECK_OUT(kret);
++
++    kret = krb5_krcc_unparse_authdata(context, creds->authdata, bc);
++    CHECK_OUT(kret);
++
++    kret = krb5_krcc_unparse_krb5data(context, &creds->ticket, bc);
++    CHECK_OUT(kret);
++    CHECK(kret);
++
++    kret = krb5_krcc_unparse_krb5data(context, &creds->second_ticket, bc);
++    CHECK_OUT(kret);
++
++    /* Success! */
++    kret = KRB5_OK;
++
++errout:
++    return kret;
++}
++
++static  krb5_error_code
++krb5_krcc_unparse_cred_alloc(krb5_context context, krb5_creds * creds,
++                             char **datapp, unsigned int *lenptr)
++{
++    krb5_error_code kret;
++    char *buf = NULL;
+     krb5_krcc_bc bc;
+ 
+     if (!creds || !datapp || !lenptr)
+@@ -1978,43 +2548,102 @@ krb5_krcc_unparse_cred(krb5_context context, krb5_ccache id,
+     *datapp = NULL;
+     *lenptr = 0;
+ 
+-    buf = malloc(GUESS_CRED_SIZE);
++    /* Do a dry run first to calculate the size. */
++    bc.bpp = bc.endp = NULL;
++    bc.size = 0;
++    kret = krb5_krcc_unparse_cred(context, creds, &bc);
++    CHECK(kret);
++    if (bc.size > MAX_CRED_SIZE)
++        return KRB5_CC_WRITE;
++
++    /* Allocate a buffer and unparse for real. */
++    buf = malloc(bc.size);
+     if (buf == NULL)
+         return KRB5_CC_NOMEM;
+-
+     bc.bpp = buf;
+-    bc.endp = buf + GUESS_CRED_SIZE;
++    bc.endp = buf + bc.size;
++    kret = krb5_krcc_unparse_cred(context, creds, &bc);
++    CHECK(kret);
+ 
+-    kret = krb5_krcc_unparse_principal(context, id, creds->client, &bc);
+-    CHECK_N_GO(kret, errout);
++    /* Success! */
++    *datapp = buf;
++    *lenptr = bc.bpp - buf;
++    buf = NULL;
++    kret = KRB5_OK;
+ 
+-    kret = krb5_krcc_unparse_principal(context, id, creds->server, &bc);
+-    CHECK_N_GO(kret, errout);
++errout:
++    free(buf);
++    return kret;
++}
+ 
+-    kret = krb5_krcc_unparse_keyblock(context, id, &creds->keyblock, &bc);
+-    CHECK_N_GO(kret, errout);
++static krb5_error_code
++krb5_krcc_parse_index(krb5_context context, krb5_int32 *version,
++                      char **primary, void *payload, int psize)
++{
++    krb5_error_code kret;
++    krb5_krcc_bc bc;
++    krb5_data data;
+ 
+-    kret = krb5_krcc_unparse_times(context, id, &creds->times, &bc);
+-    CHECK_N_GO(kret, errout);
++    bc.bpp = payload;
++    bc.endp = bc.bpp + psize;
+ 
+-    kret = krb5_krcc_unparse_octet(context, id, (krb5_int32) creds->is_skey,
+-                                   &bc);
+-    CHECK_N_GO(kret, errout);
++    kret = krb5_krcc_parse_int32(context, version, &bc);
++    CHECK_OUT(kret);
+ 
+-    kret = krb5_krcc_unparse_int32(context, id, creds->ticket_flags, &bc);
+-    CHECK_N_GO(kret, errout);
++    kret = krb5_krcc_parse_krb5data(context, &data, &bc);
++    CHECK_OUT(kret);
+ 
+-    kret = krb5_krcc_unparse_addrs(context, id, creds->addresses, &bc);
+-    CHECK_N_GO(kret, errout);
++    *primary = (char *)data.data;
++    return KRB5_OK;
++}
+ 
+-    kret = krb5_krcc_unparse_authdata(context, id, creds->authdata, &bc);
+-    CHECK_N_GO(kret, errout);
++static krb5_error_code
++krb5_krcc_unparse_index_internal(krb5_context context, krb5_int32 version,
++                                 const char *primary, krb5_krcc_bc *bc)
++{
++    krb5_error_code kret;
++    krb5_data data;
+ 
+-    kret = krb5_krcc_unparse_krb5data(context, id, &creds->ticket, &bc);
+-    CHECK_N_GO(kret, errout);
++    data.length = strlen(primary) + 1;
++    data.data = (void *)primary;
+ 
+-    kret = krb5_krcc_unparse_krb5data(context, id, &creds->second_ticket, &bc);
+-    CHECK_N_GO(kret, errout);
++    kret = krb5_krcc_unparse_int32(context, version, bc);
++    CHECK_OUT(kret);
++
++    kret = krb5_krcc_unparse_krb5data(context, &data, bc);
++    CHECK_OUT(kret);
++
++    return KRB5_OK;
++}
++
++static krb5_error_code
++krb5_krcc_unparse_index(krb5_context context, krb5_int32 version,
++                        const char *primary, void **datapp, int *lenptr)
++{
++    krb5_error_code kret;
++    krb5_krcc_bc bc;
++    char *buf;
++
++    if (!primary || !datapp || !lenptr)
++        return EINVAL;
++
++    *datapp = NULL;
++    *lenptr = 0;
++
++    /* Do a dry run first to calculate the size. */
++    bc.bpp = bc.endp = NULL;
++    bc.size = 0;
++    kret = krb5_krcc_unparse_index_internal(context, version, primary, &bc);
++    CHECK_OUT(kret);
++
++    buf = malloc(bc.size);
++    if (buf == NULL)
++        return ENOMEM;
++
++    bc.bpp = buf;
++    bc.endp = buf + bc.size;
++    kret = krb5_krcc_unparse_index_internal(context, version, primary, &bc);
++    CHECK(kret);
+ 
+     /* Success! */
+     *datapp = buf;
+@@ -2022,6 +2651,8 @@ krb5_krcc_unparse_cred(krb5_context context, krb5_ccache id,
+     kret = KRB5_OK;
+ 
+ errout:
++    if (kret)
++        free(buf);
+     return kret;
+ }
+ 
+@@ -2065,15 +2696,15 @@ const krb5_cc_ops krb5_krcc_ops = {
+     krb5_krcc_remove_cred,
+     krb5_krcc_set_flags,
+     krb5_krcc_get_flags,        /* added after 1.4 release */
+-    NULL,
+-    NULL,
+-    NULL,
++    krb5_krcc_ptcursor_new,
++    krb5_krcc_ptcursor_next,
++    krb5_krcc_ptcursor_free,
+     NULL, /* move */
+     krb5_krcc_last_change_time, /* lastchange */
+     NULL, /* wasdefault */
+     krb5_krcc_lock,
+     krb5_krcc_unlock,
+-    NULL, /* switch_to */
++    krb5_krcc_switch_to,
+ };
+ 
+ #else /* !USE_KEYRING_CCACHE */
+diff --git a/src/lib/krb5/ccache/t_cc.c b/src/lib/krb5/ccache/t_cc.c
+index e14ae7f..6069cab 100644
+--- a/src/lib/krb5/ccache/t_cc.c
++++ b/src/lib/krb5/ccache/t_cc.c
+@@ -25,6 +25,7 @@
+  */
+ 
+ #include "k5-int.h"
++#include "cc-int.h"
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include "autoconf.h"
+@@ -331,14 +332,14 @@ check_registered(krb5_context context, const char *prefix)
+     if(kret != KRB5_OK) {
+         if(kret == KRB5_CC_UNKNOWN_TYPE)
+             return 0;
+-        com_err("Checking on credential type", kret,prefix);
++        com_err("Checking on credential type", kret, "%s", prefix);
+         fflush(stderr);
+         return 0;
+     }
+ 
+     kret = krb5_cc_close(context, id);
+     if(kret != KRB5_OK) {
+-        com_err("Checking on credential type - closing", kret,prefix);
++        com_err("Checking on credential type - closing", kret, "%s", prefix);
+         fflush(stderr);
+     }
+ 
+@@ -425,8 +426,8 @@ main(void)
+     test_misc(context);
+     do_test(context, "");
+ 
+-    if(check_registered(context, "KEYRING:"))
+-        do_test(context, "KEYRING:");
++    if (check_registered(context, "KEYRING:process:"))
++        do_test(context, "KEYRING:process:");
+     else
+         printf("Skiping KEYRING: test - unregistered type\n");
+ 
+diff --git a/src/lib/krb5/ccache/t_cccol.c b/src/lib/krb5/ccache/t_cccol.c
+new file mode 100644
+index 0000000..444806e
+--- /dev/null
++++ b/src/lib/krb5/ccache/t_cccol.c
+@@ -0,0 +1,363 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krb5/ccache/t_cccol.py - Test ccache collection via API */
++/*
++ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
++ * All rights reserved.
++ *
++ * 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 <krb5.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <assert.h>
++
++static krb5_context ctx;
++
++/* Check that code is 0.  Display an error message first if it is not. */
++static void
++check(krb5_error_code code)
++{
++    const char *errmsg;
++
++    if (code != 0) {
++        errmsg = krb5_get_error_message(ctx, code);
++        fprintf(stderr, "%s\n", errmsg);
++        krb5_free_error_message(ctx, errmsg);
++    }
++    assert(code == 0);
++}
++
++/* Construct a list of the names of each credential cache in the collection. */
++static void
++get_collection_names(char ***list_out, size_t *count_out)
++{
++    krb5_cccol_cursor cursor;
++    krb5_ccache cache;
++    char **list = NULL;
++    size_t count = 0;
++    char *name;
++
++    check(krb5_cccol_cursor_new(ctx, &cursor));
++    while (1) {
++        check(krb5_cccol_cursor_next(ctx, cursor, &cache));
++        if (cache == NULL)
++            break;
++        check(krb5_cc_get_full_name(ctx, cache, &name));
++        krb5_cc_close(ctx, cache);
++        list = realloc(list, (count + 1) * sizeof(*list));
++        assert(list != NULL);
++        list[count++] = name;
++    }
++    krb5_cccol_cursor_free(ctx, &cursor);
++    *list_out = list;
++    *count_out = count;
++}
++
++/* Return true if list contains name. */
++static krb5_boolean
++in_list(char **list, size_t count, const char *name)
++{
++    size_t i;
++
++    for (i = 0; i < count; i++) {
++        if (strcmp(list[i], name) == 0)
++            return TRUE;
++    }
++    return FALSE;
++}
++
++/* Release the memory for a list of credential cache names. */
++static void
++free_list(char **list, size_t count)
++{
++    size_t i;
++
++    for (i = 0; i < count; i++)
++        krb5_free_string(ctx, list[i]);
++    free(list);
++}
++
++/*
++ * Check that the cache names within the current collection begin with first
++ * (unless first is NULL), that the other elements match the remaining
++ * arguments in some order.  others must be the number of additional cache
++ * names.
++ */
++static void
++check_collection(const char *first, size_t others, ...)
++{
++    va_list ap;
++    char **list;
++    size_t count, i;
++    const char *name;
++
++    get_collection_names(&list, &count);
++    if (first != NULL) {
++        assert(strcmp(first, list[0]) == 0);
++        assert(count == others + 1);
++    } else {
++        assert(count == others);
++    }
++    va_start(ap, others);
++    for (i = 0; i < others; i++) {
++        name = va_arg(ap, const char *);
++        assert(in_list(list, count, name));
++    }
++    va_end(ap);
++    free_list(list, count);
++}
++
++/* Check that the name of cache matches expected_name. */
++static void
++check_name(krb5_ccache cache, const char *expected_name)
++{
++    char *name;
++
++    check(krb5_cc_get_full_name(ctx, cache, &name));
++    assert(strcmp(name, expected_name) == 0);
++    krb5_free_string(ctx, name);
++}
++
++/* Check that when collection_name is resolved, the resulting cache's name
++ * matches expected_name. */
++static void
++check_primary_name(const char *collection_name, const char *expected_name)
++{
++    krb5_ccache cache;
++
++    check(krb5_cc_resolve(ctx, collection_name, &cache));
++    check_name(cache, expected_name);
++    krb5_cc_close(ctx, cache);
++}
++
++/* Check that when name is resolved, the resulting cache's principal matches
++ * expected_princ, or has no principal if expected_princ is NULL. */
++static void
++check_princ(const char *name, krb5_principal expected_princ)
++{
++    krb5_ccache cache;
++    krb5_principal princ;
++
++    check(krb5_cc_resolve(ctx, name, &cache));
++    if (expected_princ != NULL) {
++        check(krb5_cc_get_principal(ctx, cache, &princ));
++        assert(krb5_principal_compare(ctx, princ, expected_princ));
++        krb5_free_principal(ctx, princ);
++    } else {
++        assert(krb5_cc_get_principal(ctx, cache, &princ) != 0);
++    }
++    krb5_cc_close(ctx, cache);
++}
++
++/* Check that krb5_cc_cache_match on princ returns a cache whose name matches
++ * expected_name, or that the match fails if expected_name is NULL. */
++static void
++check_match(krb5_principal princ, const char *expected_name)
++{
++    krb5_ccache cache;
++
++    if (expected_name != NULL) {
++        check(krb5_cc_cache_match(ctx, princ, &cache));
++        check_name(cache, expected_name);
++        krb5_cc_close(ctx, cache);
++    } else {
++        assert(krb5_cc_cache_match(ctx, princ, &cache) != 0);
++    }
++}
++
++int
++main(int argc, char **argv)
++{
++    krb5_ccache ccinitial, ccu1, ccu2;
++    krb5_principal princ1, princ2, princ3;
++    const char *collection_name, *typename;
++    char *initial_primary_name, *unique1_name, *unique2_name;
++
++    /*
++     * Get the collection name from the command line.  This is a ccache name
++     * with collection semantics, like DIR:/path/to/directory.  This test
++     * program assumes that the collection is empty to start with.
++     */
++    assert(argc == 2);
++    collection_name = argv[1];
++
++    /*
++     * Set the default ccache for the context to be the collection name, so the
++     * library can find the collection.
++     */
++    check(krb5_init_context(&ctx));
++    check(krb5_cc_set_default_name(ctx, collection_name));
++
++    /*
++     * Resolve the collection name.  Since the collection is empty, this should
++     * generate a subsidiary name of an uninitialized cache.  Getting the name
++     * of the resulting cache should give us the subsidiary name, not the
++     * collection name.  This resulting subsidiary name should be consistent if
++     * we resolve the collection name again, and the collection should still be
++     * empty since we haven't initialized the cache.
++     */
++    check(krb5_cc_resolve(ctx, collection_name, &ccinitial));
++    check(krb5_cc_get_full_name(ctx, ccinitial, &initial_primary_name));
++    assert(strcmp(initial_primary_name, collection_name) != 0);
++    check_primary_name(collection_name, initial_primary_name);
++    check_collection(NULL, 0);
++    check_princ(collection_name, NULL);
++    check_princ(initial_primary_name, NULL);
++
++    /*
++     * Before initializing the primary ccache, generate and initialize two
++     * unique caches of the collection's type.  Check that the cache names
++     * resolve to the generated caches and appear in the collection.  (They
++     * might appear before being initialized; that's not currently considered
++     * important).  The primary cache for the collection should remain as the
++     * unitialized cache from the previous step.
++     */
++    typename = krb5_cc_get_type(ctx, ccinitial);
++    check(krb5_cc_new_unique(ctx, typename, NULL, &ccu1));
++    check(krb5_cc_get_full_name(ctx, ccu1, &unique1_name));
++    check(krb5_parse_name(ctx, "princ1@X", &princ1));
++    check(krb5_cc_initialize(ctx, ccu1, princ1));
++    check_princ(unique1_name, princ1);
++    check_match(princ1, unique1_name);
++    check_collection(NULL, 1, unique1_name);
++    check(krb5_cc_new_unique(ctx, typename, NULL, &ccu2));
++    check(krb5_cc_get_full_name(ctx, ccu2, &unique2_name));
++    check(krb5_parse_name(ctx, "princ2@X", &princ2));
++    check(krb5_cc_initialize(ctx, ccu2, princ2));
++    check_princ(unique2_name, princ2);
++    check_match(princ1, unique1_name);
++    check_match(princ2, unique2_name);
++    check_collection(NULL, 2, unique1_name, unique2_name);
++    assert(strcmp(unique1_name, initial_primary_name) != 0);
++    assert(strcmp(unique1_name, collection_name) != 0);
++    assert(strcmp(unique2_name, initial_primary_name) != 0);
++    assert(strcmp(unique2_name, collection_name) != 0);
++    assert(strcmp(unique2_name, unique1_name) != 0);
++    check_primary_name(collection_name, initial_primary_name);
++
++    /*
++     * Initialize the initial primary cache.  Make sure it didn't change names,
++     * that the previously retrieved name and the collection name both resolve
++     * to the initialized cache, and that it now appears first in the
++     * collection.
++     */
++    check(krb5_parse_name(ctx, "princ3@X", &princ3));
++    check(krb5_cc_initialize(ctx, ccinitial, princ3));
++    check_name(ccinitial, initial_primary_name);
++    check_princ(initial_primary_name, princ3);
++    check_princ(collection_name, princ3);
++    check_match(princ3, initial_primary_name);
++    check_collection(initial_primary_name, 2, unique1_name, unique2_name);
++
++    /*
++     * Switch the primary cache to each cache we have open.  One each switch,
++     * check the primary name, check that the collection resolves to the
++     * expected cache, and check that the new primary name appears first in the
++     * collection.
++     */
++    check(krb5_cc_switch(ctx, ccu1));
++    check_primary_name(collection_name, unique1_name);
++    check_princ(collection_name, princ1);
++    check_collection(unique1_name, 2, initial_primary_name, unique2_name);
++    check(krb5_cc_switch(ctx, ccu2));
++    check_primary_name(collection_name, unique2_name);
++    check_princ(collection_name, princ2);
++    check_collection(unique2_name, 2, initial_primary_name, unique1_name);
++    check(krb5_cc_switch(ctx, ccinitial));
++    check_primary_name(collection_name, initial_primary_name);
++    check_princ(collection_name, princ3);
++    check_collection(initial_primary_name, 2, unique1_name, unique2_name);
++
++    /*
++     * Temporarily set the context default ccache to a subsidiary name, and
++     * check that iterating over the collection yields that subsidiary cache
++     * and no others.
++     */
++    check(krb5_cc_set_default_name(ctx, unique1_name));
++    check_collection(unique1_name, 0);
++    check(krb5_cc_set_default_name(ctx, collection_name));
++
++    /*
++     * Destroy the primary cache.  Make sure this causes both the initial
++     * primary name and the collection name to resolve to an uninitialized
++     * cache.  Make sure the primary name doesn't change and doesn't appear in
++     * the collection any more.
++     */
++    check(krb5_cc_destroy(ctx, ccinitial));
++    check_princ(initial_primary_name, NULL);
++    check_princ(collection_name, NULL);
++    check_primary_name(collection_name, initial_primary_name);
++    check_match(princ1, unique1_name);
++    check_match(princ2, unique2_name);
++    check_match(princ3, NULL);
++    check_collection(NULL, 2, unique1_name, unique2_name);
++
++    /*
++     * Switch to the first unique cache after destroying the primary cache.
++     * Check that the collection name resolves to this cache and that the new
++     * primary name appears first in the collection.
++     */
++    check(krb5_cc_switch(ctx, ccu1));
++    check_primary_name(collection_name, unique1_name);
++    check_princ(collection_name, princ1);
++    check_collection(unique1_name, 1, unique2_name);
++
++    /*
++     * Destroy the second unique cache (which is not the current primary),
++     * check that it is on longer initialized, and check that it no longer
++     * appears in the collection.  Check that destroying the non-primary cache
++     * doesn't affect the primary name.
++     */
++    check(krb5_cc_destroy(ctx, ccu2));
++    check_princ(unique2_name, NULL);
++    check_match(princ2, NULL);
++    check_collection(unique1_name, 0);
++    check_primary_name(collection_name, unique1_name);
++    check_match(princ1, unique1_name);
++    check_princ(collection_name, princ1);
++
++    /*
++     * Destroy the first unique cache.  Check that the collection is empty and
++     * still has the same primary name.
++     */
++    check(krb5_cc_destroy(ctx, ccu1));
++    check_princ(unique1_name, NULL);
++    check_princ(collection_name, NULL);
++    check_primary_name(collection_name, unique1_name);
++    check_match(princ1, NULL);
++    check_collection(NULL, 0);
++
++    krb5_free_string(ctx, initial_primary_name);
++    krb5_free_string(ctx, unique1_name);
++    krb5_free_string(ctx, unique2_name);
++    krb5_free_principal(ctx, princ1);
++    krb5_free_principal(ctx, princ2);
++    krb5_free_principal(ctx, princ3);
++    krb5_free_context(ctx);
++    return 0;
++}
+diff --git a/src/lib/krb5/ccache/t_cccol.py b/src/lib/krb5/ccache/t_cccol.py
+index 8c459dd..e762625 100644
+--- a/src/lib/krb5/ccache/t_cccol.py
++++ b/src/lib/krb5/ccache/t_cccol.py
+@@ -1,6 +1,46 @@
+ #!/usr/bin/python
+ from k5test import *
+ 
++realm = K5Realm(create_kdb=False)
++
++keyctl = which('keyctl')
++out = realm.run([klist, '-c', 'KEYRING:process:abcd'], expected_code=1)
++test_keyring = (keyctl is not None and
++                'Unknown credential cache type' not in out)
++
++# Run the collection test program against each collection-enabled type.
++realm.run(['./t_cccol', 'DIR:' + os.path.join(realm.testdir, 'cc')])
++if test_keyring:
++    # Use the test directory as the collection name to avoid colliding
++    # with other build trees.
++    cname = realm.testdir
++
++    # Remove any keys left behind by previous failed test runs.
++    realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
++    realm.run(['keyctl', 'purge', 'keyring', cname])
++    out = realm.run(['keyctl', 'list', '@u'])
++    if ('keyring: _krb_' + cname + '\n') in out:
++        id = realm.run(['keyctl', 'search', '@u', 'keyring', '_krb_' + cname])
++        realm.run(['keyctl', 'unlink', id.strip(), '@u'])
++
++    # Run test program over each subtype, cleaning up as we go.  Don't
++    # test the persistent subtype, since it supports only one
++    # collection and might be in actual use.
++    realm.run(['./t_cccol', 'KEYRING:' + cname])
++    realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
++    realm.run(['./t_cccol', 'KEYRING:legacy:' + cname])
++    realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
++    realm.run(['./t_cccol', 'KEYRING:session:' + cname])
++    realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
++    realm.run(['./t_cccol', 'KEYRING:user:' + cname])
++    id = realm.run(['keyctl', 'search', '@u', 'keyring', '_krb_' + cname])
++    realm.run(['keyctl', 'unlink', id.strip(), '@u'])
++    realm.run(['./t_cccol', 'KEYRING:process:abcd'])
++    realm.run(['./t_cccol', 'KEYRING:thread:abcd'])
++
++realm.stop()
++
++# Test cursor semantics using real ccaches.
+ realm = K5Realm(create_host=False)
+ 
+ realm.addprinc('alice', password('alice'))
+@@ -11,12 +51,25 @@ dccname = 'DIR:%s' % ccdir
+ duser = 'DIR::%s/tkt1' % ccdir
+ dalice = 'DIR::%s/tkt2' % ccdir
+ dbob = 'DIR::%s/tkt3' % ccdir
++dnoent = 'DIR::%s/noent' % ccdir
+ realm.kinit('user', password('user'), flags=['-c', duser])
+ realm.kinit('alice', password('alice'), flags=['-c', dalice])
+ realm.kinit('bob', password('bob'), flags=['-c', dbob])
+ 
++if test_keyring:
++    cname = realm.testdir
++    realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
++    krccname = 'KEYRING:session:' + cname
++    kruser = '%s:tkt1' % krccname
++    kralice = '%s:tkt2' % krccname
++    krbob = '%s:tkt3' % krccname
++    krnoent = '%s:noent' % krccname
++    realm.kinit('user', password('user'), flags=['-c', kruser])
++    realm.kinit('alice', password('alice'), flags=['-c', kralice])
++    realm.kinit('bob', password('bob'), flags=['-c', krbob])
++
+ def cursor_test(testname, args, expected):
+-    outlines = realm.run_as_client(['./t_cccursor'] + args).splitlines()
++    outlines = realm.run(['./t_cccursor'] + args).splitlines()
+     outlines.sort()
+     expected.sort()
+     if outlines != expected:
+@@ -30,21 +83,33 @@ cursor_test('file-default2', [realm.ccache], [fccname])
+ cursor_test('file-default3', [fccname], [fccname])
+ 
+ cursor_test('dir', [dccname], [duser, dalice, dbob])
++cursor_test('dir-subsidiary', [duser], [duser])
++cursor_test('dir-nofile', [dnoent], [])
++
++if test_keyring:
++    cursor_test('keyring', [krccname], [kruser, kralice, krbob])
++    cursor_test('keyring-subsidiary', [kruser], [kruser])
++    cursor_test('keyring-noent', [krnoent], [])
+ 
+ mfoo = 'MEMORY:foo'
+ mbar = 'MEMORY:bar'
+ cursor_test('filemem', [fccname, mfoo, mbar], [fccname, mfoo, mbar])
+ cursor_test('dirmem', [dccname, mfoo], [duser, dalice, dbob, mfoo])
++if test_keyring:
++    cursor_test('keyringmem', [krccname, mfoo], [kruser, kralice, krbob, mfoo])
+ 
+ # Test krb5_cccol_have_content.
+-realm.run_as_client(['./t_cccursor', dccname, 'CONTENT'])
+-realm.run_as_client(['./t_cccursor', fccname, 'CONTENT'])
+-realm.run_as_client(['./t_cccursor', realm.ccache, 'CONTENT'])
+-realm.run_as_client(['./t_cccursor', mfoo, 'CONTENT'], expected_code=1)
++realm.run(['./t_cccursor', dccname, 'CONTENT'])
++realm.run(['./t_cccursor', fccname, 'CONTENT'])
++realm.run(['./t_cccursor', realm.ccache, 'CONTENT'])
++realm.run(['./t_cccursor', mfoo, 'CONTENT'], expected_code=1)
++if test_keyring:
++    realm.run(['./t_cccursor', krccname, 'CONTENT'])
++    realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
+ 
+ # Make sure FILE doesn't yield a nonexistent default cache.
+-realm.run_as_client([kdestroy])
++realm.run([kdestroy])
+ cursor_test('noexist', [], [])
+-realm.run_as_client(['./t_cccursor', fccname, 'CONTENT'], expected_code=1)
++realm.run(['./t_cccursor', fccname, 'CONTENT'], expected_code=1)
+ 
+ success('Renewing credentials')
+diff --git a/src/util/k5test.py b/src/util/k5test.py
+index 3400154..aead832 100644
+--- a/src/util/k5test.py
++++ b/src/util/k5test.py
+@@ -142,6 +133,9 @@ Scripts may use the following functions and variables:
+   added newline) in testlog, and write it to stdout if running
+   verbosely.
+ 
++* which(progname): Return the location of progname in the executable
++  path, or None if it is not found.
++
+ * password(name): Return a weakly random password based on name.  The
+   password will be consistent across calls with the same name.
+ 
+@@ -388,6 +374,16 @@ def output(msg, force_verbose=False):
+         sys.stdout.write(msg)
+ 
+ 
++# Return the location of progname in the executable path, or None if
++# it is not found.
++def which(progname):
++    for dir in os.environ["PATH"].split(os.pathsep):
++        path = os.path.join(dir, progname)
++        if os.access(path, os.X_OK):
++            return path
++    return None
++
++
+ def password(name):
+     """Choose a weakly random password from name, consistent across calls."""
+     return name + str(os.getpid())
+@@ -880,6 +880,11 @@ class K5Realm(object):
+         env['KPROP_PORT'] = str(self.portbase + 3)
+         return env
+ 
++    def run(self, args, env=None, **keywords):
++        if env is None:
++            env = self.env_client
++        return _run_cmd(args, env, **keywords)
++
+     def run_as_client(self, args, **keywords):
+         return _run_cmd(args, self.env_client, **keywords)
+ 
+diff --git a/src/lib/krb5/ccache/Makefile.in b/src/lib/krb5/ccache/Makefile.in
+index f64226b..ad53e65 100644
+--- a/src/lib/krb5/ccache/Makefile.in
++++ b/src/lib/krb5/ccache/Makefile.in
+@@ -71,6 +66,7 @@ SRCS=	$(srcdir)/ccbase.c \
+ 
+ EXTRADEPSRCS= \
+ 	$(srcdir)/t_cc.c \
++	$(srcdir)/t_cccol.c \
+ 	$(srcdir)/t_cccursor.c
+ 
+ ##DOS##OBJS=$(OBJS) $(OUTPRE)ccfns.$(OBJEXT)
+@@ -108,6 +104,10 @@ T_CC_OBJS=t_cc.o
+ t_cc: $(T_CC_OBJS) $(KRB5_BASE_DEPLIBS)
+ 	$(CC_LINK) -o t_cc $(T_CC_OBJS) $(KRB5_BASE_LIBS)
+ 
++T_CCCOL_OBJS = t_cccol.o
++t_cccol: $(T_CCCOL_OBJS) $(KRB5_BASE_DEPLIBS)
++	$(CC_LINK) -o $@ $(T_CCCOL_OBJS) $(KRB5_BASE_LIBS)
++
+ T_CCCURSOR_OBJS = t_cccursor.o
+ t_cccursor: $(T_CCCURSOR_OBJS) $(KRB5_BASE_DEPLIBS)
+ 	$(CC_LINK) -o $@ $(T_CCCURSOR_OBJS) $(KRB5_BASE_LIBS)
+@@ -116,11 +116,11 @@ check-unix:: t_cc
+ 	KRB5_CONFIG=$(srcdir)/t_krb5.conf ; export KRB5_CONFIG ;\
+ 	$(RUN_SETUP) $(VALGRIND) ./t_cc
+ 
+-check-pytests:: t_cccursor
++check-pytests:: t_cccursor t_cccol
+ 	$(RUNPYTEST) $(srcdir)/t_cccol.py $(PYTESTFLAGS)
+ 
+ clean-unix::
+-	$(RM) t_cc t_cc.o t_cccursor t_cccursor.o
++	$(RM) t_cc t_cc.o t_cccursor t_cccursor.o t_cccol t_cccol.o
+ 
+ ##WIN32## $(OUTPRE)cc_mslsa.$(OBJEXT): cc_mslsa.c $(top_srcdir)/include/k5-int.h $(BUILDTOP)/include/krb5/osconf.h $(BUILDTOP)/include/krb5/autoconf.h $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS)
+ 
diff --git a/SPECS/krb5.spec b/SPECS/krb5.spec
new file mode 100644
index 0000000..8588d3c
--- /dev/null
+++ b/SPECS/krb5.spec
@@ -0,0 +1,3142 @@
+%global WITH_LDAP 1
+%global WITH_DIRSRV 1
+%if 0%{?fedora} >= 17 || 0%{?rhel} > 6
+# These next two *will* change.
+%global WITH_OPENSSL 1
+%global WITH_NSS 0
+%global WITH_SYSVERTO 1
+%else
+%global WITH_OPENSSL 1
+%global WITH_NSS 0
+%global WITH_SYSVERTO 0
+%endif
+# The "move everything to /usr" feature landed in Fedora 17, but we didn't
+# catch up until the Fedora 18 development cycle, at which point we found
+# that some packages were hard-coding paths.
+%if 0%{?fedora} > 17 || 0%{?rhel} > 6
+%global separate_usr 0
+%else
+%global separate_usr 1
+%endif
+# Systemd landed in Fedora 15, but this package was cut over for Fedora 16.
+%if 0%{?fedora} >= 16 || 0%{?rhel} > 6
+%global WITH_SYSTEMD 1
+%else
+%global WITH_SYSTEMD 0
+%endif
+# Set this so that find-lang.sh will recognize the .po files.
+%global gettext_domain mit-krb5
+# Guess where the -libs subpackage's docs are going to go.
+%define libsdocdir %{?_pkgdocdir:%(echo %{_pkgdocdir} | sed -e s,krb5,krb5-libs,g)}%{!?_pkgdocdir:%{_docdir}/%{name}-libs-%{version}}
+# Figure out where the default ccache lives and how we set it.
+%if 0%{?fedora} > 18 && 0%{?fedora} < 20
+%global compile_default_ccache_name 1
+%global compiled_default_ccache_name DIR:/run/user/%%{uid}/krb5cc
+%endif
+%if 0%{?fedora} >= 20 || 0%{?rhel} > 6
+%global configure_default_ccache_name 1
+%global configured_default_ccache_name KEYRING:persistent:%%{uid}
+%endif
+
+Summary: The Kerberos network authentication system
+Name: krb5
+Version: 1.11.3
+Release: 31%{?dist}
+# Maybe we should explode from the now-available-to-everybody tarball instead?
+# http://web.mit.edu/kerberos/dist/krb5/1.11/krb5-1.11.3-signed.tar
+Source0: krb5-%{version}.tar.gz
+Source1: krb5-%{version}.tar.gz.asc
+# Use a dummy krb5-%{version}-pdf.tar.xz the first time through, then
+#  tar cvJf $RPM_SOURCE_DIR/krb5-%%{version}-pdf.tar.xz build-pdf/*.pdf
+# after the build phase finishes.
+Source3: krb5-%{version}-pdf.tar.xz
+Source2: kprop.service
+Source4: kadmin.service
+Source5: krb5kdc.service
+Source6: krb5.conf
+Source7: _kpropd
+Source8: _kadmind
+Source10: kdc.conf
+Source11: kadm5.acl
+Source19: krb5kdc.sysconfig
+Source20: kadmin.sysconfig
+Source29: ksu.pamd
+Source31: kerberos-adm.portreserve
+Source32: krb5_prop.portreserve
+Source33: krb5kdc.logrotate
+Source34: kadmind.logrotate
+Source36: kpropd.init
+Source37: kadmind.init
+Source38: krb5kdc.init
+
+BuildRequires: cmake
+# Carry this locally until it's available in a packaged form.
+Source100: nss_wrapper-0.0-20130719153839Z.git6cb59864.bz2
+Source101: noport.c
+
+Patch6: krb5-1.10-ksu-path.patch
+Patch12: krb5-1.7-ktany.patch
+Patch16: krb5-1.10-buildconf.patch
+Patch23: krb5-1.3.1-dns.patch
+Patch29: krb5-1.10-kprop-mktemp.patch
+Patch30: krb5-1.3.4-send-pr-tempfile.patch
+Patch39: krb5-1.8-api.patch
+Patch56: krb5-1.10-doublelog.patch
+Patch59: krb5-1.10-kpasswd_tcp.patch
+Patch60: krb5-1.11-pam.patch
+Patch63: krb5-1.11-selinux-label.patch
+Patch71: krb5-1.11-dirsrv-accountlock.patch
+Patch86: krb5-1.9-debuginfo.patch
+Patch105: krb5-kvno-230379.patch
+Patch113: krb5-1.11-alpha1-init.patch
+Patch116: http://ausil.fedorapeople.org/aarch64/krb5/krb5-aarch64.patch
+Patch117: krb5-1.11-gss-client-keytab.patch
+Patch121: krb5-cccol-primary.patch
+Patch123: krb5-1.11.2-empty_passwords.patch
+Patch124: krb5-1.11.2-arcfour_short.patch
+Patch125: krb5-1.11.2-skew1.patch
+Patch126: krb5-1.11.2-skew2.patch
+Patch127: krb5-master-test_gss_no_udp.patch
+Patch128: krb5-master-test_no_pmap.patch 
+Patch129: krb5-1.11-run_user_0.patch
+Patch130: krb5-master-init_referral.patch
+Patch131: krb5-1.11.3-skew3.patch
+Patch132: krb5-1.11-gss-methods1.patch
+Patch133: krb5-1.11-gss-methods2.patch 
+Patch134: krb5-1.11-kpasswdtest.patch
+Patch135: krb5-1.11-check_transited.patch
+Patch136: krb5-1.11.3-prompter1.patch
+Patch137: krb5-1.11.3-prompter2.patch
+Patch138: krb5-1.11.3-gss-ccache-import.patch
+Patch139: krb5-1.10-CVE-2013-1418.patch
+
+# Patches for otp plugin backport
+Patch201: krb5-1.11.2-keycheck.patch
+Patch202: krb5-1.11.2-otp.patch
+
+# Patches for kernel-persistent-keyring support (backport)
+Patch301: persistent_keyring.patch
+Patch302: krb5-master-kinit-cccol.patch
+
+Patch400: 0000-ksu-intermediates.patch
+Patch401: 0001-Don-t-try-to-stat-not-on-disk-ccache-residuals.patch 
+Patch402: 0002-Use-an-in-memory-cache-until-we-need-the-target-s.patch 
+Patch403: 0003-Learn-to-destroy-the-ccache-we-re-copying-from.patch 
+Patch404: 0004-Try-to-use-the-default_ccache_name-d-as-the-target.patch 
+Patch405: 0005-Be-more-careful-of-target-ccache-collections.patch 
+Patch406: 0006-Copy-config-entries-to-the-target-ccache.patch 
+
+License: MIT
+URL: http://web.mit.edu/kerberos/www/
+Group: System Environment/Libraries
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRequires: autoconf, bison, flex, gawk, gettext, sed
+%if 0%{?fedora} >= 12 || 0%{?rhel} >= 6
+BuildRequires: libcom_err-devel, libss-devel
+%endif
+BuildRequires: gzip, ncurses-devel, tar
+BuildRequires: python-sphinx
+# The texlive package got a lot more complicated here.
+%if 0%{?fedora} > 17 || 0%{?rhel} > 6
+# Taken from \usepackage directives produced by sphinx:
+BuildRequires: tex(babel.sty)
+BuildRequires: tex(bookmark.sty)
+BuildRequires: tex(fancybox.sty)
+BuildRequires: tex(fncychap.sty)
+BuildRequires: tex(fontenc.sty)
+BuildRequires: tex(framed.sty)
+BuildRequires: tex(hyperref.sty)
+BuildRequires: tex(ifthen.sty)
+BuildRequires: tex(inputenc.sty)
+BuildRequires: tex(longtable.sty)
+BuildRequires: tex(multirow.sty)
+BuildRequires: tex(times.sty)
+BuildRequires: tex(titlesec.sty)
+BuildRequires: tex(threeparttable.sty)
+BuildRequires: tex(wrapfig.sty)
+BuildRequires: tex(report.cls)
+%else
+BuildRequires: texlive-texmf, texlive-texmf-latex
+%endif
+# Typical fonts, and the commands which we need to have present.
+BuildRequires: texlive, texlive-latex, texlive-texmf-fonts
+BuildRequires: /usr/bin/pdflatex /usr/bin/makeindex
+BuildRequires: keyutils, keyutils-libs-devel >= 1.5.8
+BuildRequires: libselinux-devel
+BuildRequires: pam-devel
+%if %{WITH_SYSTEMD}
+BuildRequires: systemd-units
+%endif
+# For the test framework.
+BuildRequires: perl, dejagnu, tcl-devel
+BuildRequires: net-tools, rpcbind
+%if 0%{?fedora} >= 13 || 0%{?rhel} > 6
+BuildRequires: hostname
+BuildRequires: iproute
+%endif
+
+%if %{WITH_LDAP}
+BuildRequires: openldap-devel
+%endif
+%if %{WITH_OPENSSL} || %{WITH_NSS}
+BuildRequires: openssl-devel >= 0.9.8
+%endif
+%if %{WITH_NSS}
+BuildRequires: nss-devel >= 3.13
+%endif
+%if %{WITH_SYSVERTO}
+BuildRequires: libverto-devel
+%endif
+
+%description
+Kerberos V5 is a trusted-third-party network authentication system,
+which can improve your network's security by eliminating the insecure
+practice of sending passwords over the network in unencrypted form.
+
+%package devel
+Summary: Development files needed to compile Kerberos 5 programs
+Group: Development/Libraries
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+%if 0%{?fedora} >= 12 || 0%{?rhel} >= 6
+Requires: libcom_err-devel
+%endif
+Requires: keyutils-libs-devel, libselinux-devel
+Requires: libverto-devel
+
+%description devel
+Kerberos is a network authentication system. The krb5-devel package
+contains the header files and libraries needed for compiling Kerberos
+5 programs. If you want to develop Kerberos-aware programs, you need
+to install this package.
+
+%package libs
+Summary: The shared libraries used by Kerberos 5
+Group: System Environment/Libraries
+%if 0%{?rhel} == 6
+# Some of the older libsmbclient builds here incorrectly called
+# krb5_locate_kdc(), which was mistakenly exported in 1.9.
+Conflicts: libsmbclient < 3.5.10-124
+%endif
+Requires: coreutils, gawk, grep, sed
+Requires: keyutils-libs >= 1.5.8
+
+%description libs
+Kerberos is a network authentication system. The krb5-libs package
+contains the shared libraries needed by Kerberos 5. If you are using
+Kerberos, you need to install this package.
+
+%package server
+Group: System Environment/Daemons
+Summary: The KDC and related programs for Kerberos 5
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires(post): chkconfig
+%if %{WITH_SYSTEMD}
+Requires(post): systemd-sysv
+Requires(post): systemd-units
+Requires(preun): systemd-units
+Requires(postun): systemd-units
+%else
+Requires(preun): chkconfig
+# portreserve is used by init scripts for kadmind, kpropd, and krb5kdc
+Requires: portreserve
+%endif
+Requires(post): initscripts
+Requires(postun): initscripts
+# we need 'status -l' to work, and that option was added in 8.99
+Requires: initscripts >= 8.99-1
+# used by the triggers
+Requires: chkconfig
+# we drop files in its directory, but we don't want to own that directory
+Requires: logrotate
+Requires(preun): initscripts
+# mktemp is used by krb5-send-pr
+Requires: coreutils
+# we specify /usr/share/dict/words as the default dict_file in kdc.conf
+Requires: /usr/share/dict/words
+%if %{WITH_SYSVERTO}
+# for run-time, and for parts of the test suite
+BuildRequires: libverto-module-base
+Requires: libverto-module-base
+%endif
+
+%description server
+Kerberos is a network authentication system. The krb5-server package
+contains the programs that must be installed on a Kerberos 5 key
+distribution center (KDC).  If you are installing a Kerberos 5 KDC,
+you need to install this package (in other words, most people should
+NOT install this package).
+
+%package server-ldap
+Group: System Environment/Daemons
+Summary: The LDAP storage plugin for the Kerberos 5 KDC
+Requires: %{name}-server%{?_isa} = %{version}-%{release}
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+
+%description server-ldap
+Kerberos is a network authentication system. The krb5-server package
+contains the programs that must be installed on a Kerberos 5 key
+distribution center (KDC).  If you are installing a Kerberos 5 KDC,
+and you wish to use a directory server to store the data for your
+realm, you need to install this package.
+
+%package workstation
+Summary: Kerberos 5 programs for use on workstations
+Group: System Environment/Base
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+# mktemp is used by krb5-send-pr
+Requires: coreutils
+
+%description workstation
+Kerberos is a network authentication system. The krb5-workstation
+package contains the basic Kerberos programs (kinit, klist, kdestroy,
+kpasswd). If your network uses Kerberos, this package should be
+installed on every workstation.
+
+%if 0%{?fedora} >= 17 || 0%{?rhel} > 6
+%package pkinit
+%else
+%package pkinit-openssl
+%endif
+Summary: The PKINIT module for Kerberos 5
+Group: System Environment/Libraries
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+%if 0%{?fedora} >= 17 || 0%{?rhel} >= 6
+Obsoletes: krb5-pkinit-openssl < %{version}-%{release}
+Provides: krb5-pkinit-openssl = %{version}-%{release}
+%endif
+
+%if 0%{?fedora} >= 17 || 0%{?rhel} > 6
+%description pkinit
+%else
+%description pkinit-openssl
+%endif
+Kerberos is a network authentication system. The krb5-pkinit
+package contains the PKINIT plugin, which allows clients
+to obtain initial credentials from a KDC using a private key and a
+certificate.
+
+%prep
+%setup -q -n %{name}-%{version} -a 3 -a 100
+ln -s NOTICE LICENSE
+
+%patch301 -p1 -b .persistent-keyring
+%patch302 -p1 -b .kinit-cccol
+
+%patch400 -p1 -b .intermediates
+%patch401 -p1 -b .Don-t-try-to-stat-not-on-disk-ccache-residuals
+%patch402 -p1 -b .Use-an-in-memory-cache-until-we-need-the-target-s
+%patch403 -p1 -b .Learn-to-destroy-the-ccache-we-re-copying-from
+%patch404 -p1 -b .Try-to-use-the-default_ccache_name-d-as-the-target
+%patch405 -p1 -b .Be-more-careful-of-target-ccache-collections
+%patch406 -p1 -b .Copy-config-entries-to-the-target-ccache
+
+%patch60 -p1 -b .pam
+
+%patch63 -p1 -b .selinux-label
+
+%patch6  -p1 -b .ksu-path
+%patch12 -p1 -b .ktany
+%patch16 -p1 -b .buildconf %{?_rawbuild}
+%patch23 -p1 -b .dns %{?_rawbuild}
+%patch29 -p1 -b .kprop-mktemp
+%patch30 -p1 -b .send-pr-tempfile
+%patch39 -p1 -b .api
+%patch56 -p1 -b .doublelog
+%patch59 -p1 -b .kpasswd_tcp
+%patch71 -p1 -b .dirsrv-accountlock %{?_rawbuild}
+%patch86 -p0 -b .debuginfo
+%patch105 -p1 -b .kvno
+%patch113 -p1 -b .init
+%patch116 -p1 -b .aarch64
+%patch117 -p1 -b .gss-client-keytab
+%patch121 -p1 -b .cccol-primary
+%patch123 -p1 -b .empty_passwords
+%patch124 -p1 -b .arcfour_short
+%patch125 -p1 -b .skew1
+%patch126 -p1 -b .skew2
+%patch127 -p1 -b .test_gss_no_udp
+%patch128 -p1 -b .test_no_pmap
+
+# Apply when the hard-wired or configured default location is
+# DIR:/run/user/%%{uid}/krb5cc.
+%patch129 -p1 -b .run_user_0
+
+%patch130 -p1 -b .init_referral
+%patch131 -p1 -b .skew3
+%patch132 -p1 -b .gss-methods1
+%patch133 -p1 -b .gss-methods2
+%patch134 -p1 -b .kpasswdtest
+%patch135 -p1 -b .check_transited
+%patch136 -p1 -b .prompter1
+%patch137 -p1 -b .prompter2
+%patch138 -p1 -b .gss-ccache-import
+%patch139 -p1 -b .CVE-2013-1418
+
+%patch201 -p1 -b .keycheck
+%patch202 -p1 -b .otp
+
+# Take the execute bit off of documentation.
+chmod -x doc/krb5-protocol/*.txt
+
+# Generate an FDS-compatible LDIF file.
+inldif=src/plugins/kdb/ldap/libkdb_ldap/kerberos.ldif
+cat > 60kerberos.ldif << EOF
+# This is a variation on kerberos.ldif which 389 Directory Server will like.
+dn: cn=schema
+EOF
+egrep -iv '(^$|^dn:|^changetype:|^add:)' $inldif | \
+sed -r 's,^		,                ,g' | \
+sed -r 's,^	,        ,g' >> 60kerberos.ldif
+touch -r $inldif 60kerberos.ldif
+
+# Rebuild the configure scripts.
+pushd src
+#autoheader
+#autoconf
+./util/reconf --verbose
+popd
+
+# Create build space for the test wrapper.
+mkdir -p nss_wrapper/build
+
+# Mess with some of the default ports that we use for testing, so that multiple
+# builds going on the same host don't step on each other.
+cfg="src/kadmin/testing/proto/kdc.conf.proto \
+     src/kadmin/testing/proto/krb5.conf.proto \
+     src/lib/kadm5/unit-test/api.current/init-v2.exp \
+     src/util/k5test.py \
+     src/tests/kdc_realm/input_conf/*.conf \
+     src/tests/mk_migr/ldap_backend/input_conf/*.conf \
+     src/tests/mk_migr/db2_backend/input_conf/*.conf"
+LONG_BIT=`getconf LONG_BIT`
+PORT=`expr 61000 + $LONG_BIT - 48`
+sed -i -e s,61000,`expr "$PORT" + 0`,g $cfg
+PORT=`expr 1750 + $LONG_BIT - 48`
+sed -i -e s,1750,`expr "$PORT" + 0`,g $cfg
+sed -i -e s,1751,`expr "$PORT" + 1`,g $cfg
+sed -i -e s,1752,`expr "$PORT" + 2`,g $cfg
+PORT=`expr 8888 + $LONG_BIT - 48`
+sed -i -e s,8888,`expr "$PORT" - 0`,g $cfg
+sed -i -e s,8887,`expr "$PORT" - 1`,g $cfg
+sed -i -e s,8886,`expr "$PORT" - 2`,g $cfg
+PORT=`expr 7777 + $LONG_BIT - 48`
+sed -i -e s,7777,`expr "$PORT" + 0`,g $cfg
+sed -i -e s,7778,`expr "$PORT" + 1`,g $cfg
+
+%build
+# Go ahead and supply tcl info, because configure doesn't know how to find it.
+. %{_libdir}/tclConfig.sh
+pushd src
+# Keep the old default if the package is built against older releases.
+%if 0%{?compile_default_ccache_name}
+DEFCCNAME=%{compiled_default_ccache_name}; export DEFCCNAME
+%endif
+# Work out the CFLAGS and CPPFLAGS which we intend to use.
+INCLUDES=-I%{_includedir}/et
+CFLAGS="`echo $RPM_OPT_FLAGS $DEFINES $INCLUDES -fPIC -fno-strict-aliasing -fstack-protector-all`"
+CPPFLAGS="`echo $DEFINES $INCLUDES`"
+%configure \
+	CC="%{__cc}" \
+	CFLAGS="$CFLAGS" \
+	CPPFLAGS="$CPPFLAGS" \
+%if 0%{?fedora} >= 7 || 0%{?rhel} >= 6
+	SS_LIB="-lss" \
+%else
+	SS_LIB="-lss -lncurses" \
+%endif
+	--enable-shared \
+	--localstatedir=%{_var}/kerberos \
+	--disable-rpath \
+	--without-krb5-config \
+	--with-system-et \
+	--with-system-ss \
+	--with-netlib=-lresolv \
+	--with-tcl \
+	--enable-dns-for-realm \
+%if %{WITH_LDAP}
+	--with-ldap \
+%if %{WITH_DIRSRV}
+	--with-dirsrv-account-locking \
+%endif
+%endif
+%if %{WITH_OPENSSL} || %{WITH_NSS}
+	--enable-pkinit \
+%else
+	--disable-pkinit \
+%endif
+%if %{WITH_OPENSSL}
+	--with-pkinit-crypto-impl=openssl \
+%endif
+%if %{WITH_NSS}
+	--with-crypto-impl=nss \
+%endif
+%if %{WITH_SYSVERTO}
+	--with-system-verto \
+%else
+	--without-system-verto \
+%endif
+	--with-pam \
+	--with-selinux
+# Now build it.
+make
+popd
+
+# Build the docs.
+make -C src/doc paths.py version.py
+cp src/doc/paths.py doc/
+mkdir -p build-man build-html build-pdf
+sphinx-build -a -b man   -t pathsubs doc build-man
+sphinx-build -a -b html  -t pathsubs doc build-html
+rm -fr build-html/_sources
+sphinx-build -a -b latex -t pathsubs doc build-pdf
+# Build the PDFs if we didn't have pre-built ones.
+for pdf in admin appdev basic build plugindev user ; do
+	test -s build-pdf/$pdf.pdf || make -C build-pdf
+done
+
+# Build the test wrapper.
+pushd nss_wrapper/build
+cmake ..
+make
+popd
+
+# We need to cut off any access to locally-running nameservers, too.
+%{__cc} -fPIC -shared -o noport.so -Wall -Wextra $RPM_SOURCE_DIR/noport.c
+
+%check
+# Alright, this much is still a work in progress.
+%if %{?__isa_bits:%{__isa_bits}}%{!?__isa_bits:32} == 64
+if hostname | grep -q build ; then
+	sleep 600
+fi
+%endif
+
+# Set things up to use the test wrappers.
+NSS_WRAPPER_HOSTNAME=test.example.com ; export NSS_WRAPPER_HOSTNAME
+NSS_WRAPPER_HOSTS="`pwd`/nss_wrapper/fakehosts" ; export NSS_WRAPPER_HOSTS
+echo 127.0.0.1 $NSS_WRAPPER_HOSTNAME $NSS_WRAPPER_HOSTNAME >"$NSS_WRAPPER_HOSTS"
+NOPORT=53,111; export NOPORT
+LD_PRELOAD=`pwd`/noport.so:`pwd`/nss_wrapper/build/src/libnss_wrapper.so ; export LD_PRELOAD
+
+# Run the test suite. We can't actually run the whole thing in the build
+# system, but we can at least run more than we used to.  The build system may
+# give us a revoked session keyring, so run affected tests with a new one.
+make -C src runenv.py
+: make -C src check TMPDIR=%{_tmppath}
+keyctl session - make -C src/lib check TMPDIR=%{_tmppath} OFFLINE=yes
+make -C src/kdc check TMPDIR=%{_tmppath}
+keyctl session - make -C src/appl check TMPDIR=%{_tmppath}
+make -C src/clients check TMPDIR=%{_tmppath}
+keyctl session - make -C src/util check TMPDIR=%{_tmppath}
+
+%install
+[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
+
+# Sample KDC config files (bundled kdc.conf and kadm5.acl).
+mkdir -p $RPM_BUILD_ROOT%{_var}/kerberos/krb5kdc
+install -pm 600 %{SOURCE10} $RPM_BUILD_ROOT%{_var}/kerberos/krb5kdc/
+install -pm 600 %{SOURCE11} $RPM_BUILD_ROOT%{_var}/kerberos/krb5kdc/
+
+# Where per-user keytabs live by default.
+mkdir -p $RPM_BUILD_ROOT%{_var}/kerberos/krb5/user
+
+# Default configuration file for everything.
+mkdir -p $RPM_BUILD_ROOT/etc
+install -pm 644 %{SOURCE6} $RPM_BUILD_ROOT/etc/krb5.conf
+
+# Parent of configuration file for list of loadable GSS mechs ("mechs").  This
+# location is not relative to sysconfdir, but is hard-coded in g_initialize.c.
+mkdir -m 755 -p $RPM_BUILD_ROOT/etc/gss
+
+# If the default configuration needs to start specifying a default cache
+# location, add it now, then fixup the timestamp so that it looks the same.
+%if 0%{?configure_default_ccache_name}
+DEFCCNAME="%{configured_default_ccache_name}"; export DEFCCNAME
+awk '{print}
+     /^# default_realm/{print " default_ccache_name =", ENVIRON["DEFCCNAME"]}' \
+     %{SOURCE6} > $RPM_BUILD_ROOT/etc/krb5.conf
+touch -r %{SOURCE6} $RPM_BUILD_ROOT/etc/krb5.conf
+grep default_ccache_name $RPM_BUILD_ROOT/etc/krb5.conf
+%endif
+
+# Server init scripts (krb5kdc,kadmind,kpropd) and their sysconfig files.
+%if %{WITH_SYSTEMD}
+mkdir -p $RPM_BUILD_ROOT%{_unitdir}
+for unit in \
+	%{SOURCE5}\
+	%{SOURCE4} \
+	%{SOURCE2} ; do
+	# In the past, the init script was supposed to be named after the
+	# service that the started daemon provided.  Changing their names
+	# is an upgrade-time problem I'm in no hurry to deal with.
+	install -pm 644 ${unit} $RPM_BUILD_ROOT%{_unitdir}
+done
+mkdir -p $RPM_BUILD_ROOT%{_sbindir}
+for wrapper in \
+	%{SOURCE7} \
+	%{SOURCE8} ; do
+	install -pm 755 ${wrapper} $RPM_BUILD_ROOT%{_sbindir}/
+done
+%else
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+for init in \
+	%{SOURCE36}\
+	%{SOURCE37} \
+	%{SOURCE38} ; do
+	# In the past, the init script was supposed to be named after the
+	# service that the started daemon provided.  Changing their names
+	# is an upgrade-time problem I'm in no hurry to deal with.
+	service=`basename ${init} .init`
+	install -pm 755 ${init} \
+	$RPM_BUILD_ROOT/etc/rc.d/init.d/${service%d}
+done
+# portreserve configuration files.
+mkdir -p $RPM_BUILD_ROOT/etc/portreserve
+for portreserve in \
+	%{SOURCE31} \
+	%{SOURCE32} ; do
+	install -pm 644 ${portreserve} \
+	$RPM_BUILD_ROOT/etc/portreserve/`basename ${portreserve} .portreserve`
+done
+%endif
+
+mkdir -p $RPM_BUILD_ROOT/etc/sysconfig
+for sysconfig in \
+	%{SOURCE19}\
+	%{SOURCE20} ; do
+	install -pm 644 ${sysconfig} \
+	$RPM_BUILD_ROOT/etc/sysconfig/`basename ${sysconfig} .sysconfig`
+done
+
+# logrotate configuration files
+mkdir -p $RPM_BUILD_ROOT/etc/logrotate.d/
+for logrotate in \
+	%{SOURCE33} \
+	%{SOURCE34} ; do
+	install -pm 644 ${logrotate} \
+	$RPM_BUILD_ROOT/etc/logrotate.d/`basename ${logrotate} .logrotate`
+done
+
+# PAM configuration files.
+mkdir -p $RPM_BUILD_ROOT/etc/pam.d/
+for pam in \
+	%{SOURCE29} ; do
+	install -pm 644 ${pam} \
+	$RPM_BUILD_ROOT/etc/pam.d/`basename ${pam} .pamd`
+done
+
+# Plug-in directories.
+install -pdm 755 $RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/preauth
+install -pdm 755 $RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/kdb
+install -pdm 755 $RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/authdata
+
+# The rest of the binaries, headers, libraries, and docs.
+make -C src DESTDIR=$RPM_BUILD_ROOT EXAMPLEDIR=%{libsdocdir}/examples install
+
+# Munge krb5-config yet again.  This is totally wrong for 64-bit, but chunks
+# of the buildconf patch already conspire to strip out /usr/<anything> from the
+# list of link flags, and it helps prevent file conflicts on multilib systems.
+sed -r -i -e 's|^libdir=/usr/lib(64)?$|libdir=/usr/lib|g' $RPM_BUILD_ROOT%{_bindir}/krb5-config
+
+%if %{separate_usr}
+# Move specific libraries from %%{_libdir} to /%%{_lib}, and fixup the symlinks.
+touch $RPM_BUILD_ROOT/rootfile
+rellibdir=..
+while ! test -r $RPM_BUILD_ROOT/%{_libdir}/${rellibdir}/rootfile ; do
+	rellibdir=../${rellibdir}
+done
+rm -f $RPM_BUILD_ROOT/rootfile
+mkdir -p $RPM_BUILD_ROOT/%{_lib}
+for library in libgssapi_krb5 libgssrpc libk5crypto libkrb5 libkrb5support ; do
+	mv $RPM_BUILD_ROOT/%{_libdir}/${library}.so.* $RPM_BUILD_ROOT/%{_lib}/
+	pushd $RPM_BUILD_ROOT/%{_libdir}
+	ln -fs ${rellibdir}/%{_lib}/${library}.so.*.* ${library}.so
+	popd
+done
+%endif
+
+# Install processed man pages.
+for section in 1 5 8 ; do
+	install -m 644 build-man/*.${section} \
+		       $RPM_BUILD_ROOT/%{_mandir}/man${section}/
+done
+
+%find_lang %{gettext_domain}
+
+%clean
+[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
+
+%post libs -p /sbin/ldconfig
+
+%if 0%{?configure_default_ccache_name}
+%triggerun libs -- krb5-libs < 1.11.3-16
+# Triggered roughly on the version where this logic was introduced.
+# Try to add a default_ccache_name to /etc/krb5.conf, removing the previous
+# default which we configured, if we find it.
+DEFCCNAME="%{configured_default_ccache_name}"; export DEFCCNAME
+tmpfile=`mktemp /etc/krb5.conf.XXXXXX`
+if test -z "$tmpfile" ; then
+	# Give up.
+	exit 0
+fi
+# Remove the default value we previously set.  Be very exact about it.
+if grep -q default_ccache_name /etc/krb5.conf ; then
+	sed -r '/^ default_ccache_name = DIR:\/run\/user\/%%\{uid\}\/krb5cc$/d' /etc/krb5.conf > "$tmpfile"
+	if test -s "$tmpfile" ; then
+		if touch -r /etc/krb5.conf "$tmpfile" ; then
+			cat "$tmpfile" > /etc/krb5.conf
+			touch -r "$tmpfile" /etc/krb5.conf
+		fi
+	fi
+fi
+# Add the new default value, unless there's one set.  Don't be too particular
+# about it.
+if ! grep -q default_ccache_name /etc/krb5.conf ; then
+	awk '
+	/^\[.*\]$/ {
+		if (libdefaults) {
+			print " default_ccache_name =", ENVIRON["DEFCCNAME"]
+			print ""
+		}
+		libdefaults=0;
+	}
+	/^\[libdefaults\]$/ { libdefaults=1; }
+	{ print }' /etc/krb5.conf > "$tmpfile"
+	if test -s "$tmpfile" ; then
+		if touch -r /etc/krb5.conf "$tmpfile" ; then
+			cat "$tmpfile" > /etc/krb5.conf
+			touch -r "$tmpfile" /etc/krb5.conf
+		fi
+	fi
+fi
+if test -n "$tmpfile" ; then
+	rm -f "$tmpfile"
+fi
+%endif
+
+%postun libs -p /sbin/ldconfig
+
+%post server-ldap -p /sbin/ldconfig
+
+%postun server-ldap -p /sbin/ldconfig
+
+%post server
+# Remove the init script for older servers.
+[ -x /etc/rc.d/init.d/krb5server ] && /sbin/chkconfig --del krb5server
+%if %{WITH_SYSTEMD}
+if [ $1 -eq 1 ] ; then 
+    # Initial installation 
+    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
+fi
+%else
+# Install the new ones.
+/sbin/chkconfig --add krb5kdc
+/sbin/chkconfig --add kadmin
+/sbin/chkconfig --add kprop
+%endif
+exit 0
+
+%preun server
+if [ "$1" -eq "0" ] ; then
+%if %{WITH_SYSTEMD}
+	/bin/systemctl --no-reload disable krb5kdc.service > /dev/null 2>&1 || :
+	/bin/systemctl --no-reload disable kadmin.service > /dev/null 2>&1 || :
+	/bin/systemctl --no-reload disable kprop.service > /dev/null 2>&1 || :
+	/bin/systemctl stop krb5kdc.service > /dev/null 2>&1 || :
+	/bin/systemctl stop kadmin.service > /dev/null 2>&1 || :
+	/bin/systemctl stop kprop.service > /dev/null 2>&1 || :
+%else
+	/sbin/chkconfig --del krb5kdc
+	/sbin/chkconfig --del kadmin
+	/sbin/chkconfig --del kprop
+	/sbin/service krb5kdc stop > /dev/null 2>&1 || :
+	/sbin/service kadmin stop > /dev/null 2>&1 || :
+	/sbin/service kprop stop > /dev/null 2>&1 || :
+%endif
+fi
+exit 0
+
+%postun server
+%if %{WITH_SYSTEMD}
+/bin/systemctl daemon-reload >/dev/null 2>&1 || :
+if [ "$1" -ge 1 ] ; then
+	/bin/systemctl try-restart krb5kdc.service >/dev/null 2>&1 || :
+	/bin/systemctl try-restart kadmin.service >/dev/null 2>&1 || :
+	/bin/systemctl try-restart kprop.service >/dev/null 2>&1 || :
+fi
+%else
+if [ "$1" -ge 1 ] ; then
+	/sbin/service krb5kdc condrestart > /dev/null 2>&1 || :
+	/sbin/service kadmin condrestart > /dev/null 2>&1 || :
+	/sbin/service kprop condrestart > /dev/null 2>&1 || :
+fi
+%endif
+exit 0
+
+%if %{WITH_SYSTEMD}
+%triggerun server -- krb5-server < 1.9.1-13
+# Save the current service runlevel info
+# User must manually run 
+#  systemd-sysv-convert --apply krb5kdc
+#  systemd-sysv-convert --apply kadmin
+#  systemd-sysv-convert --apply kprop
+# to migrate them to systemd targets
+/usr/bin/systemd-sysv-convert --save krb5kdc >/dev/null 2>&1 ||:
+/usr/bin/systemd-sysv-convert --save kadmin >/dev/null 2>&1 ||:
+/usr/bin/systemd-sysv-convert --save kprop >/dev/null 2>&1 ||:
+
+# Run these because the SysV package being removed won't do them
+/sbin/chkconfig --del krb5kdc >/dev/null 2>&1 || :
+/sbin/chkconfig --del kadmin >/dev/null 2>&1 || :
+/sbin/chkconfig --del kprop >/dev/null 2>&1 || :
+/bin/systemctl try-restart krb5kdc.service >/dev/null 2>&1 || :
+/bin/systemctl try-restart kadmin.service >/dev/null 2>&1 || :
+/bin/systemctl try-restart kprop.service >/dev/null 2>&1 || :
+%endif
+
+%triggerun server -- krb5-server < 1.6.3-100
+if [ "$2" -eq "0" ] ; then
+	/sbin/install-info --delete %{_infodir}/krb425.info.gz %{_infodir}/dir
+	/sbin/service krb524 stop > /dev/null 2>&1 || :
+	/sbin/chkconfig --del krb524 > /dev/null 2>&1 || :
+fi
+exit 0
+
+%files workstation
+%defattr(-,root,root,-)
+%doc src/config-files/services.append
+%doc build-html/*
+%doc build-pdf/user.pdf build-pdf/basic.pdf
+%attr(0755,root,root) %doc src/config-files/convert-config-files
+
+# Clients of the KDC, including tools you're likely to need if you're running
+# app servers other than those built from this source package.
+%{_bindir}/kdestroy
+%{_mandir}/man1/kdestroy.1*
+%{_bindir}/kinit
+%{_mandir}/man1/kinit.1*
+%{_bindir}/klist
+%{_mandir}/man1/klist.1*
+%{_bindir}/kpasswd
+%{_mandir}/man1/kpasswd.1*
+%{_bindir}/kswitch
+%{_mandir}/man1/kswitch.1*
+
+%{_bindir}/kvno
+%{_mandir}/man1/kvno.1*
+%{_bindir}/kadmin
+%{_mandir}/man1/kadmin.1*
+%{_bindir}/k5srvutil
+%{_mandir}/man1/k5srvutil.1*
+%{_bindir}/ktutil
+%{_mandir}/man1/ktutil.1*
+
+# Doesn't really fit anywhere else.
+%attr(4755,root,root) %{_bindir}/ksu
+%{_mandir}/man1/ksu.1*
+%config(noreplace) /etc/pam.d/ksu
+
+# Problem-reporting tool.
+%{_sbindir}/krb5-send-pr
+%dir %{_datadir}/gnats
+%{_datadir}/gnats/mit
+%{_mandir}/man1/krb5-send-pr.1*
+
+%files server
+%defattr(-,root,root,-)
+%docdir %{_mandir}
+%doc build-pdf/admin.pdf build-pdf/build.pdf
+%if %{WITH_SYSTEMD}
+%{_unitdir}/krb5kdc.service
+%{_unitdir}/kadmin.service
+%{_unitdir}/kprop.service
+%else
+/etc/rc.d/init.d/krb5kdc
+/etc/rc.d/init.d/kadmin
+/etc/rc.d/init.d/kprop
+%config(noreplace) /etc/portreserve/kerberos-adm
+%config(noreplace) /etc/portreserve/krb5_prop
+%endif
+%config(noreplace) /etc/sysconfig/krb5kdc
+%config(noreplace) /etc/sysconfig/kadmin
+%config(noreplace) /etc/logrotate.d/krb5kdc
+%config(noreplace) /etc/logrotate.d/kadmind
+
+%dir %{_var}/kerberos
+%dir %{_var}/kerberos/krb5kdc
+%config(noreplace) %{_var}/kerberos/krb5kdc/kdc.conf
+%config(noreplace) %{_var}/kerberos/krb5kdc/kadm5.acl
+
+%dir %{_libdir}/krb5
+%dir %{_libdir}/krb5/plugins
+%dir %{_libdir}/krb5/plugins/kdb
+%dir %{_libdir}/krb5/plugins/preauth
+%dir %{_libdir}/krb5/plugins/authdata
+%{_libdir}/krb5/plugins/preauth/otp.so
+
+
+# Problem-reporting tool.
+%{_sbindir}/krb5-send-pr
+%dir %{_datadir}/gnats
+%{_datadir}/gnats/mit
+%{_mandir}/man1/krb5-send-pr.1*
+
+# KDC binaries and configuration.
+%{_mandir}/man5/kadm5.acl.5*
+%{_mandir}/man5/kdc.conf.5*
+%{_sbindir}/kadmin.local
+%{_mandir}/man8/kadmin.local.8*
+%{_sbindir}/kadmind
+%{_sbindir}/_kadmind
+%{_mandir}/man8/kadmind.8*
+%{_sbindir}/kdb5_util
+%{_mandir}/man8/kdb5_util.8*
+%{_sbindir}/kprop
+%{_mandir}/man8/kprop.8*
+%{_sbindir}/kpropd
+%{_sbindir}/_kpropd
+%{_mandir}/man8/kpropd.8*
+%{_sbindir}/kproplog
+%{_mandir}/man8/kproplog.8*
+%{_sbindir}/krb5kdc
+%{_mandir}/man8/krb5kdc.8*
+
+# This is here for people who want to test their server, and also 
+# included in devel package for similar reasons.
+%{_bindir}/sclient
+%{_mandir}/man1/sclient.1*
+%{_sbindir}/sserver
+%{_mandir}/man8/sserver.8*
+
+%if %{WITH_LDAP}
+%files server-ldap
+%defattr(-,root,root,-)
+%docdir %{_mandir}
+%doc src/plugins/kdb/ldap/libkdb_ldap/kerberos.ldif
+%doc src/plugins/kdb/ldap/libkdb_ldap/kerberos.schema
+%doc 60kerberos.ldif
+%dir %{_libdir}/krb5
+%dir %{_libdir}/krb5/plugins
+%dir %{_libdir}/krb5/plugins/kdb
+%{_libdir}/krb5/plugins/kdb/kldap.so
+%{_libdir}/libkdb_ldap.so
+%{_libdir}/libkdb_ldap.so.*
+%{_mandir}/man8/kdb5_ldap_util.8.gz
+%{_sbindir}/kdb5_ldap_util
+%endif
+
+%files libs -f %{gettext_domain}.lang
+%defattr(-,root,root,-)
+%doc README NOTICE LICENSE
+%docdir %{_mandir}
+# This is a hard-coded, not-dependent-on-the-configure-script path.
+%dir /etc/gss
+%verify(not md5 size mtime) %config(noreplace) /etc/krb5.conf
+/%{_mandir}/man5/.k5identity.5*
+/%{_mandir}/man5/.k5login.5*
+/%{_mandir}/man5/k5identity.5*
+/%{_mandir}/man5/k5login.5*
+/%{_mandir}/man5/krb5.conf.5*
+%if %{separate_usr}
+/%{_lib}/libgssapi_krb5.so.*
+/%{_lib}/libgssrpc.so.*
+/%{_lib}/libk5crypto.so.*
+%else
+%{_libdir}/libgssapi_krb5.so.*
+%{_libdir}/libgssrpc.so.*
+%{_libdir}/libk5crypto.so.*
+%endif
+%{_libdir}/libkadm5clnt_mit.so.*
+%{_libdir}/libkadm5srv_mit.so.*
+%{_libdir}/libkdb5.so.*
+%{_libdir}/libkrad.so.*
+%if %{separate_usr}
+/%{_lib}/libkrb5.so.*
+/%{_lib}/libkrb5support.so.*
+%else
+%{_libdir}/libkrb5.so.*
+%{_libdir}/libkrb5support.so.*
+%endif
+%dir %{_libdir}/krb5
+%dir %{_libdir}/krb5/plugins
+%dir %{_libdir}/krb5/plugins/*
+%{_libdir}/krb5/plugins/kdb/db2.so
+%dir %{_var}/kerberos
+%dir %{_var}/kerberos/krb5
+%dir %{_var}/kerberos/krb5/user
+%if ! %{WITH_SYSVERTO}
+%{_libdir}/libverto-k5ev.so
+%{_libdir}/libverto-k5ev.so.*
+# These really shouldn't be here, but until we have a system copy of libverto,
+# don't force people who are using libverto to install the KDC just to get the
+# shared library.  Not that there are any development headers, but anyway.
+%{_libdir}/libverto.so
+%{_libdir}/libverto.so.*
+%endif
+
+%if 0%{?fedora} >= 17 || 0%{?rhel} > 6
+%files pkinit
+%else
+%files pkinit-openssl
+%endif
+%defattr(-,root,root,-)
+%dir %{_libdir}/krb5
+%dir %{_libdir}/krb5/plugins
+%dir %{_libdir}/krb5/plugins/preauth
+%{_libdir}/krb5/plugins/preauth/pkinit.so
+
+%files devel
+%defattr(-,root,root,-)
+%docdir %{_mandir}
+%doc doc/krb5-protocol
+%doc build-pdf/appdev.pdf build-pdf/plugindev.pdf
+
+%{_includedir}/*
+%{_libdir}/libgssapi_krb5.so
+%{_libdir}/libgssrpc.so
+%{_libdir}/libk5crypto.so
+%{_libdir}/libkadm5clnt.so
+%{_libdir}/libkadm5clnt_mit.so
+%{_libdir}/libkadm5srv.so
+%{_libdir}/libkadm5srv_mit.so
+%{_libdir}/libkdb5.so
+%{_libdir}/libkrad.so
+%{_libdir}/libkrb5.so
+%{_libdir}/libkrb5support.so
+
+%{_bindir}/krb5-config
+%{_bindir}/sclient
+%{_mandir}/man1/krb5-config.1*
+%{_mandir}/man1/sclient.1*
+%{_mandir}/man8/sserver.8*
+%{_sbindir}/sserver
+
+# Protocol test clients.
+%{_bindir}/sim_client
+%{_bindir}/gss-client
+%{_bindir}/uuclient
+
+# Protocol test servers.
+%{_sbindir}/sim_server
+%{_sbindir}/gss-server
+%{_sbindir}/uuserver
+
+%changelog
+* Fri Nov  8 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-31
+- revise proposed changes for ksu to handle dropping privileges and cases
+  where we didn't pick up creds from the source ccache
+
+* Tue Nov  5 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-30
+- add currently-proposed changes to teach ksu about credential cache
+  collections and the default_ccache_name setting (#1015559)
+
+* Tue Nov  5 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-29
+- incorporate upstream patch for remote crash of KDCs which serve multiple
+  realms simultaneously (#1026981, RT#7756, CVE-2013-1418)
+
+* Tue Nov  5 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-28
+- drop patch to add additional access() checks to ksu - they add to breakage
+  when non-FILE: caches are in use (#1015559), shouldn't be resulting in any
+  benefit, and clash with proposed changes to fix its cache handling
+
+* Tue Oct 22 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-27
+- add some minimal description to the top of the wrapper scripts we use
+  when starting krb5kdc and kadmind to describe why they exist (tooling)
+
+* Wed Oct 16 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-26
+- create and own /etc/gss (#1019937)
+
+* Tue Oct 15 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-25
+- pull up fix for importing previously-exported credential caches in the
+  gssapi library (RT# 7706, #1019420)
+
+* Mon Oct 14 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-24
+- backport the callback to use the libkrb5 prompter when we can't load PEM
+  files for PKINIT (RT#7590, includes part of #965721/#1016690)
+- extract the rest of the fix #965721/#1016690 from the changes for RT#7680
+
+* Mon Oct 14 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-23
+- fix trigger scriptlet's invocation of sed (#1016945)
+
+* Fri Oct  4 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-22
+- rebuild with keyutils 1.5.8 (part of #1012043)
+
+* Wed Oct  2 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-21
+- switch to the version of persistent-keyring that was just merged to
+  master (RT#7711), along with related changes to kinit (RT#7689)
+- go back to setting default_ccache_name to a KEYRING type
+
+* Mon Sep 30 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-20
+- pull up fix for not calling a kdb plugin's check-transited-path
+  method before calling the library's default version, which only knows
+  how to read what's in the configuration file (RT#7709, #1013664)
+
+* Thu Sep 26 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-19
+- configure --without-krb5-config so that we don't pull in the old default
+  ccache name when we want to stop setting a default ccache name at configure-
+  time
+
+* Wed Sep 25 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-18
+- fix broken dependency on awk (should be gawk, rdieter)
+
+* Wed Sep 25 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-17
+- add missing dependency on newer keyutils-libs (#1012034)
+
+* Tue Sep 24 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-16
+- back out setting default_ccache_name to the new default for now, resetting
+  it to the old default while the kernel/keyutils bits get sorted (sgallagh)
+
+* Mon Sep 23 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-15
+- add explicit build-time dependency on a version of keyutils that's new
+  enough to include keyctl_get_persistent() (more of #991148)
+
+* Thu Sep 19 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-14
+- incorporate Simo's updated backport of his updated persistent-keyring changes
+  (more of #991148)
+
+* Fri Sep 13 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-13
+- don't break during %%check when the session keyring is revoked
+
+* Fri Sep 13 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-12
+- pull the newer F21 defaults back to F20 (sgallagh)
+
+* Mon Sep  9 2013 Nalin Dahyabhai <nalin@redhat.com>
+- only apply the patch to autocreate /run/user/0 when we're hard-wiring the
+  default ccache location to be under it; otherwise it's unnecessary
+
+* Mon Sep  9 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.3-11
+- don't let comments intended for one scriptlet become part of the "script"
+  that gets passed to ldconfig as part of another one (Mattias Ellert, #1005675)
+
+* Fri Sep  6 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.3-10
+- incorporate Simo's backport of his persistent-keyring changes (#991148)
+- restore build-time default DEFCCNAME on Fedora 21 and later and EL, and
+  instead set default_ccache_name in the default krb5.conf's [libdefaults]
+  section (#991148)
+- on releases where we expect krb5.conf to be configured with a
+  default_ccache_name, add it whenever we upgrade from an older version of
+  the package that wouldn't have included it in its default configuration
+  file (#991148)
+
+* Fri Aug 23 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.3-9
+- take another stab at accounting for UnversionedDocdirs for the -libs
+  subpackage (spotted by ssorce)
+- switch to just the snapshot of nss_wrapper we were using, since we
+  no longer need to carry anything that isn't in the cwrap.org repository
+  (ssorce)
+
+* Thu Aug 15 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.3-8
+- drop a patch we weren't not applying (build tooling)
+- wrap kadmind and kpropd in scripts which check for the presence/absence
+  of files which dictate particular exit codes before exec'ing the actual
+  binaries, instead of trying to use ConditionPathExists in the unit files
+  to accomplish that, so that we exit with failure properly when what we
+  expect isn't actually in effect on the system (#800343)
+
+* Mon Jul 29 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.3-7
+- attempt to account for UnversionedDocdirs for the -libs subpackage
+
+* Fri Jul 26 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.3-6
+- tweak configuration files used during tests to try to reduce the number
+  of conflicts encountered when builds for multiple arches land on the same
+  builder
+
+* Mon Jul 22 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.3-5
+- pull up changes to allow GSSAPI modules to provide more functions
+  (RT#7682, #986564/#986565)
+
+* Fri Jul 19 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.3-4
+- use (a bundled, for now, copy of) nss_wrapper to let us run some of the
+  self-tests at build-time in more places than we could previously (#978756)
+- cover inconsistencies in whether or not there's a local caching nameserver
+  that's willing to answer when the build environment doesn't have a
+  resolver configuration, so that nss_wrapper's faking of the local
+  hostname can be complete
+
+* Mon Jul  1 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.3-3
+- specify dependencies on the same arch of krb5-libs by using the %%{?_isa}
+  suffix, to avoid dragging 32-bit libraries onto 64-bit systems (#980155)
+
+* Thu Jun 13 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.3-2
+- special-case /run/user/0, attempting to create it when resolving a
+  directory cache below it fails due to ENOENT and we find that it doesn't
+  already exist, either, before attempting to create the directory cache
+  (maybe helping, maybe just making things more confusing for #961235)
+
+* Tue Jun  4 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.3-1
+- update to 1.11.3
+  - drop patch for RT#7605, fixed in this release
+  - drop patch for CVE-2002-2443, fixed in this release
+  - drop patch for RT#7369, fixed in this release
+- pull upstream fix for breaking t_skew.py by adding the patch for #961221
+
+* Fri May 31 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.2-10
+- respin with updated version of patch for RT#7650 (#969331)
+
+* Thu May 30 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.2-9
+- don't forget to set the SELinux label when creating the directory for
+  a DIR: ccache
+- pull in proposed fix for attempts to get initial creds, which end up
+  following referrals, incorrectly trying to always use master KDCs if
+  they talked to a master at any point (should fix RT#7650)
+
+* Thu May 30 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.2-8
+- pull in patches from master to not test GSSRPC-over-UDP and to not
+  depend on the portmapper, which are areas where our build systems
+  often give us trouble, too
+
+* Tue May 28 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.2-7
+- backport fix for not being able to verify the list of transited realms
+  in GSS acceptors (RT#7639, #959685)
+- backport fix for not being able to pass an empty password to the
+  get-init-creds APIs and have them actually use it (RT#7642, #960001)
+- add backported proposed fix to use the unauthenticated server time
+  as the basis for computing the requested credential expiration times,
+  rather than the client's idea of the current time, which could be
+  significantly incorrect (#961221)
+
+* Tue May 21 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.2-6
+- pull in upstream fix to start treating a KRB5CCNAME value that begins
+  with DIR:: the same as it would a DIR: value with just one ccache file
+  in it (RT#7172, #965574)
+
+* Mon May 13 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.2-5
+- pull up fix for UDP ping-pong flaw in kpasswd service (CVE-2002-2443,
+  #962531,#962534)
+
+* Mon Apr 29 2013 Nathaniel McCallum <npmccallum@redhat.com> 1.11.2-4
+- Update otp patches
+- Merge otp patches into a single patch
+- Add keycheck patch
+
+* Tue Apr 23 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.2-3
+- pull the changing of the compiled-in default ccache location to
+  DIR:/run/user/%%{uid}/krb5cc back into F19, in line with SSSD and
+  the most recent pam_krb5 build
+
+* Wed Apr 17 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.2-2
+- correct some configuration file paths which the KDC_DIR patch missed
+
+* Mon Apr 15 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.2-1
+- update to 1.11.2
+  - drop pulled in patch for RT#7586, included in this release
+  - drop pulled in patch for RT#7592, included in this release
+- pull in fix for keeping track of the message type when parsing FAST requests
+  in the KDC (RT#7605, #951843) (also #951965)
+
+* Fri Apr 12 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.1-9
+- move the compiled-in default ccache location from the previous default of
+  FILE:/tmp/krb5cc_%%{uid} to DIR:/run/user/%%{uid}/krb5cc (part of #949588)
+
+* Tue Apr 09 2013 Nathaniel McCallum <npmccallum@redhat.com> - 1.11.1-8
+- Update otp backport patches (libk5radius => libkrad)
+
+* Wed Apr  3 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.1-7
+- when testing the RPC library, treat denials from the local portmapper the
+  same as a portmapper-not-running situation, to allow other library tests
+  to be run while building the package
+
+* Thu Mar 28 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.1-6
+- create and own /var/kerberos/krb5/user instead of /var/kerberos/kdc/user,
+  since that's what the libraries actually look for
+- add buildrequires on nss-myhostname, in an attempt to get more of the tests
+  to run properly during builds
+- pull in Simo's patch to recognize "client_keytab" as a key type which can
+  be passed in to gss_acquire_cred_from() (RT#7598)
+
+* Tue Mar 26 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.1-5
+- pull up Simo's patch to mark the correct mechanism on imported GSSAPI
+  contexts (RT#7592)
+- go back to using reconf to run autoconf and autoheader (part of #925640)
+- add temporary patch to use newer config.guess/config.sub (more of #925640)
+
+* Mon Mar 18 2013 Nalin Dahyabhai <nalin@redhat.com>
+- fix a version comparison to expect newer texlive build requirements when
+  %%{_rhel} > 6 rather than when it's > 7
+
+* Mon Mar 11 2013 Nathaniel McCallum <npmccallum@redhat.com> 1.11.1-4
+- Add libverto-devel requires for krb5-devel
+- Add otp support
+
+* Thu Feb 28 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.1-3
+- fix a memory leak when acquiring credentials using a keytab (RT#7586, #911110)
+
+* Wed Feb 27 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.1-2
+- prebuild PDF docs to reduce multilib differences (internal tooling, #884065)
+- drop the kerberos-iv portreserve file, and drop the rest on systemd systems
+- escape uses of macros in comments (more of #884065)
+
+* Mon Feb 25 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.1-1
+- update to 1.11.1
+  - drop patch for noticing negative timeouts being passed to the poll()
+    wrapper in the client transmit functions
+
+* Fri Feb  8 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11-2
+- set "rdns = false" in the default krb5.conf (#908323,#908324)
+
+* Tue Dec 18 2012 Nalin Dahyabhai <nalin@redhat.com> 1.11-1
+- update to 1.11 release
+
+* Thu Dec 13 2012 Nalin Dahyabhai <nalin@redhat.com> 1.11-0.beta2.0
+- update to 1.11 beta 2
+
+* Thu Dec 13 2012 Nalin Dahyabhai <nalin@redhat.com>
+- when building with our bundled copy of libverto, package it in with -libs
+  rather than with -server (#886049)
+
+* Wed Nov 21 2012 Nalin Dahyabhai <nalin@redhat.com> 1.11-0.beta1.0
+- update to 1.11 beta 1
+
+* Fri Nov 16 2012 Nalin Dahyabhai <nalin@redhat.com> 1.11-0.alpha1.1
+- handle releases where texlive packaging wasn't yet as complicated as it
+  is in Fedora 18
+- fix an uninitialized-variable error building one of the test programs
+
+* Fri Nov 16 2012 Nalin Dahyabhai <nalin@redhat.com> 1.11-0.alpha1.0
+- move the rather large pile of html and pdf docs to -workstation, so
+  that just having something that links to the libraries won't drag
+  them onto a system, and we avoid having to sort out hard-coded paths
+  that include %%{_libdir} showing up in docs in multilib packages
+- actually create %%{_var}/kerberos/kdc/user, so that it can be packaged
+- correct the list of packaged man pages
+- don't dummy up required tex stylesheets, require them
+- require pdflatex and makeindex
+
+* Thu Nov 15 2012 Nalin Dahyabhai <nalin@redhat.com>
+- update to 1.11 alpha 1
+  - drop backported patch for RT #7406
+  - drop backported patch for RT #7407
+  - drop backported patch for RT #7408
+  - the new docs system generates PDFs, so stop including them as sources
+  - drop backported patch to allow deltat.y to build with the usual
+    warning flags and the current gcc
+  - drop backported fix for disabling use of a replay cache when verifying
+    initial credentials
+  - drop backported fix for teaching PKINIT clients which trust the KDC's
+    certificate directly to verify signed-data messages that are signed with
+    the KDC's certificate, when the blobs don't include a copy of the KDC's
+    certificate
+  - drop backported patches to make keytab-based authentication attempts
+    work better when the client tells the KDC that it supports a particular
+    cipher, but doesn't have a key for it in the keytab
+  - drop backported fix for avoiding spurious clock skew when a TGT is
+    decrypted long after the KDC sent it to the client which decrypts it
+  - move the cross-referenced HTML docs into the -libs package to avoid
+    broken internal links
+  - drop patches to fixup paths in man pages, shouldn't be needed any more
+
+* Wed Oct 17 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.3-7
+- tag a couple of other patches which we still need to be applied during
+  %%{?_rawbuild} builds (zmraz)
+
+* Tue Sep 25 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.3-6
+- actually pull up the patch for RT#7063, and not some other ticket (#773496)
+
+* Mon Sep 10 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.3-5
+- add patch based on one from Filip Krska to not call poll() with a negative
+  timeout when the caller's intent is for us to just stop calling it (#838548)
+
+* Fri Sep  7 2012 Nalin Dahyabhai <nalin@redhat.com>
+- on EL6, conflict with libsmbclient before 3.5.10-124, which is when it
+  stopped linking with a symbol which we no longer export (#771687)
+- pull up patch for RT#7063, in which not noticing a prompt for a long
+  time throws the client library's idea of the time difference between it
+  and the KDC really far out of whack (#773496)
+- add a backport of more patches to set the client's list of supported enctypes
+  when using a keytab to be the list of types of keys in the keytab, plus the
+  list of other types the client supports but for which it doesn't have keys,
+  in that order, so that KDCs have a better chance of being able to issue
+  tickets with session keys of types that the client can use (#837855)
+
+* Thu Sep  6 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.3-4
+- cut down the number of times we load SELinux labeling configuration from
+  a minimum of two times to actually one (more of #845125)
+
+* Thu Aug 30 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.3-3
+- backport patch to disable replay detection in krb5_verify_init_creds()
+  while reading the AP-REQ that's generated in the same function (RT#7229)
+
+* Thu Aug 30 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.3-2
+- undo rename from krb5-pkinit-openssl to krb5-pkinit on EL6
+- version the Obsoletes: on the krb5-pkinit-openssl to krb5-pkinit rename
+- reintroduce the init scripts for non-systemd releases
+- forward-port %%{?_rawbuild} annotations from EL6 packaging
+
+* Thu Aug  9 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.3-1
+- update to 1.10.3, rolling in the fixes from MITKRB5-SA-2012-001
+
+* Thu Aug  2 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.2-7
+- selinux: hang on to the list of selinux contexts, freeing and reloading
+  it only when the file we read it from is modified, freeing it when the
+  shared library is being unloaded (#845125)
+
+* Thu Aug  2 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.2-6
+- go back to not messing with library file paths on Fedora 17: it breaks
+  file path dependencies in other packages, and since Fedora 17 is already
+  released, breaking that is our fault
+
+* Tue Jul 31 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.2-5
+- add upstream patch to fix freeing an uninitialized pointer and dereferencing
+  another uninitialized pointer in the KDC (MITKRB5-SA-2012-001, CVE-2012-1014
+  and CVE-2012-1015, #844779 and #844777)
+- fix a thinko in whether or not we mess around with devel .so symlinks on
+  systems without a separate /usr (sbose)
+
+* Fri Jul 27 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.10.2-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Fri Jun 22 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.2-3
+- backport a fix to allow a PKINIT client to handle SignedData from a KDC
+  that's signed with a certificate that isn't in the SignedData, but which
+  is available as an anchor or intermediate on the client (RT#7183)
+
+* Tue Jun  5 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.2-2
+- back out this labeling change (dwalsh):
+  - when building the new label for a file we're about to create, also mix
+    in the current range, in addition to the current user
+
+* Fri Jun  1 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.2-1
+- update to 1.10.2
+  - when building the new label for a file we're about to create, also mix
+    in the current range, in addition to the current user
+  - also package the PDF format admin, user, and install guides
+  - drop some PDFs that no longer get built right
+- add a backport of Stef's patch to set the client's list of supported
+  enctypes to match the types of keys that we have when we are using a
+  keytab to try to get initial credentials, so that a KDC won't send us
+  an AS reply that we can't encrypt (RT#2131, #748528)
+- don't shuffle around any shared libraries on releases with no-separate-/usr,
+  since /usr/lib is the same place as /lib
+- add explicit buildrequires: on 'hostname', for the tests, on systems where
+  it's in its own package, and require net-tools, which used to provide the
+  command, everywhere
+
+* Mon May  7 2012 Nalin Dahyabhai <nalin@redhat.com>
+- skip the setfscreatecon() if fopen() is passed "rb" as the open mode (part
+  of #819115)
+
+* Tue May  1 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.1-3
+- have -server require /usr/share/dict/words, which we set as the default
+  dict_file in kdc.conf (#817089)
+
+* Tue Mar 20 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.1-2
+- change back dns_lookup_kdc to the default setting (Stef Walter, #805318)
+- comment out example.com examples in default krb5.conf (Stef Walter, #805320)
+
+* Fri Mar  9 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10.1-1
+- update to 1.10.1
+  - drop the KDC crash fix
+  - drop the KDC lookaside cache fix
+  - drop the fix for kadmind RPC ACLs (CVE-2012-1012)
+
+* Wed Mar  7 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10-5
+- when removing -workstation, remove our files from the info index while
+  the file is still there, in %%preun, rather than %%postun, and use the
+  compressed file's name (#801035)
+
+* Tue Feb 21 2012 Nathaniel McCallum <nathaniel@natemccallum.com> - 1.10-4
+- Fix string RPC ACLs (RT#7093); CVE-2012-1012
+
+* Tue Jan 31 2012 Nathaniel McCallum <nathaniel@natemccallum.com> - 1.10-3
+- Add upstream lookaside cache behavior fix (RT#7082)
+
+* Mon Jan 30 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10-2
+- add patch to accept keytab entries with vno==0 as matches when we're
+  searching for an entry with a specific name/kvno (#230382/#782211,RT#3349)
+
+* Mon Jan 30 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10-1
+- update to 1.10 final
+
+* Thu Jan 26 2012 Nathaniel McCallum <nathaniel@natemccallum.com> - 1.10-0.beta1.2
+- Add upstream crashfix patch (RT#7081)
+
+* Thu Jan 12 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10-0.beta1.1
+- update to beta 1
+
+* Wed Jan 11 2012 Peter Robinson <pbrobinson@gmail.com>
+- mktemp was long obsoleted by coreutils
+
+* Wed Jan  4 2012 Nalin Dahyabhai <nalin@redhat.com> 1.10-0.alpha2.2
+- modify the deltat grammar to also tell gcc (4.7) to suppress
+  "maybe-uninitialized" warnings in addition to the "uninitialized" warnings
+  it's already being told to suppress (RT#7080)
+
+* Tue Dec 20 2011 Nalin Dahyabhai <nalin@redhat.com> 1.10-0.alpha2.1
+- update to alpha 2
+- drop a couple of patches which were integrated for alpha 2
+
+* Tue Dec 13 2011 Nalin Dahyabhai <nalin@redhat.com> 1.10-0.alpha1.3
+- pull in patch for RT#7046: tag a ccache containing credentials obtained via
+  S4U2Proxy with the principal name of the proxying principal (part of #761317)
+  so that the default principal name can be set to that of the client for which
+  it is proxying, which results in the ccache looking more normal to consumers
+  of the ccache that don't care that there's proxying going on
+- pull in patch for RT#7047: allow tickets obtained via S4U2Proxy to be cached
+  (more of #761317)
+- pull in patch for RT#7048: allow PAC verification to only bother trying to
+  verify the signature with keys that it's given (still more of #761317)
+
+* Tue Dec  6 2011 Nalin Dahyabhai <nalin@redhat.com> 1.10-0.alpha1.2
+- apply upstream patch to fix a null pointer dereference when processing
+  TGS requests (CVE-2011-1530, #753748)
+
+* Wed Nov 30 2011 Nalin Dahyabhai <nalin@redhat.com> 1.10-0.alpha1.1
+- correct a bug in the fix for #754001 so that the file creation context is
+  consistently reset
+
+* Tue Nov 15 2011 Nalin Dahyabhai <nalin@redhat.com> 1.10-0.alpha1.0
+- update to 1.10 alpha 1
+- on newer releases where we can assume NSS >= 3.13, configure PKINIT to build
+  using NSS
+- on newer releases where we build PKINIT using NSS, configure libk5crypto to
+  build using NSS
+- rename krb5-pkinit-openssl to krb5-pkinit on newer releases where we're
+  expecting to build PKINIT using NSS instead
+- during %%check, run check in the library and kdc subdirectories, which
+  should be able to run inside of the build system without issue
+
+* Wed Oct 26 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.9.1-19
+- Rebuilt for glibc bug#747377
+
+* Tue Oct 18 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-18
+- apply upstream patch to fix a null pointer dereference with the LDAP kdb
+  backend (CVE-2011-1527, #744125), an assertion failure with multiple kdb
+  backends (CVE-2011-1528), and a null pointer dereference with multiple kdb
+  backends (CVE-2011-1529) (#737711)
+
+* Thu Oct 13 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-17
+- pull in patch from trunk to rename krb5int_pac_sign() to krb5_pac_sign() and
+  make it public (#745533)
+
+* Fri Oct  7 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-16
+- kadmin.service: fix #723723 again
+- kadmin.service,krb5kdc.service: remove optional use of $KRB5REALM in command
+  lines, because systemd parsing doesn't handle alternate value shell variable
+  syntax
+- kprop.service: add missing Type=forking so that systemd doesn't assume simple
+- kprop.service: expect the ACL configuration to be there, not absent
+- handle a harder-to-trigger assertion failure that starts cropping up when we
+  exit the transmit loop on time (#739853)
+
+* Sun Oct  2 2011 Tom Callaway <spot@fedoraproject.org> 1.9.1-15
+- hardcode pid file as option in krb5kdc.service
+
+* Fri Sep 30 2011 Tom Callaway <spot@fedoraproject.org> 1.9.1-14
+- fix pid path in krb5kdc.service
+
+* Mon Sep 19 2011 Tom Callaway <spot@fedoraproject.org> 1.9.1-13
+- convert to systemd
+
+* Tue Sep  6 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-12
+- pull in upstream patch for RT#6952, confusion following referrals for
+  cross-realm auth (#734341)
+- pull in build-time deps for the tests
+
+* Thu Sep  1 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-11
+- switch to the upstream patch for #727829
+
+* Wed Aug 31 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-10
+- handle an assertion failure that starts cropping up when the patch for
+  using poll (#701446) meets servers that aren't running KDCs or against
+  which the connection fails for other reasons (#727829, #734172)
+
+* Mon Aug  8 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-9
+- override the default build rules to not delete temporary y.tab.c files,
+  so that they can be packaged, allowing debuginfo files which point to them
+  do so usefully (#729044)
+
+* Fri Jul 22 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-8
+- build shared libraries with partial RELRO support (#723995)
+- filter out potentially multiple instances of -Wl,-z,relro from krb5-config
+  output, now that it's in the buildroot's default LDFLAGS
+- pull in a patch to fix losing track of the replay cache FD, from SVN by
+  way of Kevin Coffman
+
+* Wed Jul 20 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-7
+- kadmind.init: drop the attempt to detect no-database-present errors (#723723),
+  which is too fragile in cases where the database has been manually moved or
+  is accessed through another kdb plugin
+
+* Tue Jul 19 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-6
+- backport fixes to teach libkrb5 to use descriptors higher than FD_SETSIZE
+  to talk to a KDC by using poll() if it's detected at compile-time (#701446,
+  RT#6905)
+
+* Thu Jun 23 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-5
+- pull a fix from SVN to try to avoid triggering a PTR lookup in getaddrinfo()
+  during krb5_sname_to_principal(), and to let getaddrinfo() decide whether or
+  not to ask for an IPv6 address based on the set of configured interfaces
+  (#717378, RT#6922)
+- pull a fix from SVN to use AI_ADDRCONFIG more often (RT#6923)
+
+* Mon Jun 20 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-4
+- apply upstream patch by way of Burt Holzman to fall back to a non-referral
+  method in cases where we might be derailed by a KDC that rejects the
+  canonicalize option (for example, those from the RHEL 2.1 or 3 era) (#715074)
+
+* Tue Jun 14 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-3
+- pull a fix from SVN to get libgssrpc clients (e.g. kadmin) authenticating
+  using the old protocol over IPv4 again (RT#6920)
+
+* Tue Jun 14 2011 Nalin Dahyabhai <nalin@redhat.com>
+- incorporate a fix to teach the file labeling bits about when replay caches
+  are expunged (#576093)
+
+* Thu May 26 2011 Nalin Dahyabhai <nalin@redhat.com>
+- switch to the upstream patch for #707145
+
+* Wed May 25 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-2
+- klist: don't trip over referral entries when invoked with -s (#707145,
+  RT#6915)
+
+* Fri May  6 2011 Nalin Dahyabhai <nalin@redhat.com>
+- fixup URL in a comment
+- when built with NSS, require 3.12.10 rather than 3.12.9
+
+* Thu May  5 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9.1-1
+- update to 1.9.1:
+  - drop no-longer-needed patches for CVE-2010-4022, CVE-2011-0281,
+    CVE-2011-0282, CVE-2011-0283, CVE-2011-0284, CVE-2011-0285
+
+* Wed Apr 13 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9-9
+- kadmind: add upstream patch to fix free() on an invalid pointer (#696343,
+  MITKRB5-SA-2011-004, CVE-2011-0285)
+
+* Mon Apr  4 2011 Nalin Dahyabhai <nalin@redhat.com>
+- don't discard the error code from an error message received in response
+  to a change-password request (#658871, RT#6893)
+
+* Fri Apr  1 2011 Nalin Dahyabhai <nalin@redhat.com>
+- override INSTALL_SETUID at build-time so that ksu is installed into
+  the buildroot with the right permissions (part of #225974)
+
+* Fri Mar 18 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9-8
+- backport change from SVN to fix a computed-value-not-used warning in
+  kpropd (#684065)
+
+* Tue Mar 15 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9-7
+- turn off NSS as the backend for libk5crypto for now to work around its
+  DES string2key not working (#679012)
+- add revised upstream patch to fix double-free in KDC while returning
+  typed-data with errors (MITKRB5-SA-2011-003, CVE-2011-0284, #674325)
+
+* Thu Feb 17 2011 Nalin Dahyabhai <nalin@redhat.com>
+- throw in a not-applied-by-default patch to try to make pkinit debugging
+  into a run-time boolean option named "pkinit_debug"
+
+* Wed Feb 16 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9-6
+- turn on NSS as the backend for libk5crypto, adding nss-devel as a build
+  dependency when that switch is flipped
+
+* Wed Feb  9 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9-5
+- krb5kdc init script: prototype some changes to do a quick spot-check
+  of the TGS and kadmind keys and warn if there aren't any non-weak keys
+  on file for them (to flush out parts of #651466)
+
+* Tue Feb  8 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9-4
+- add upstream patches to fix standalone kpropd exiting if the per-client
+  child process exits with an error (MITKRB5-SA-2011-001), a hang or crash
+  in the KDC when using the LDAP kdb backend, and an uninitialized pointer
+  use in the KDC (MITKRB5-SA-2011-002) (CVE-2010-4022, #664009,
+  CVE-2011-0281, #668719, CVE-2011-0282, #668726, CVE-2011-0283, #676126)
+
+* Mon Feb 07 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.9-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Mon Feb  7 2011 Nalin Dahyabhai <nalin@redhat.com>
+- fix a compile error in the SELinux labeling patch when -DDEBUG is used (Sumit
+  Bose)
+
+* Tue Feb  1 2011 Nalin Dahyabhai <nalin@redhat.com>
+- properly advertise that the kpropd init script now supports force-reload
+  (Zbysek Mraz, #630587)
+
+* Wed Jan 26 2011 Nalin Dahyabhai <nalin@redhat.com> 1.9-2
+- pkinit: when verifying signed data, use the CMS APIs for better
+  interoperability (#636985, RT#6851)
+
+* Wed Dec 22 2010 Nalin Dahyabhai <nalin@redhat.com> 1.9-1
+- update to 1.9 final
+
+* Mon Dec 20 2010 Nalin Dahyabhai <nalin@redhat.com> 1.9-0.beta3.1
+- fix link flags and permissions on shared libraries (ausil)
+
+* Thu Dec 16 2010 Nalin Dahyabhai <nalin@redhat.com> 1.9-0.beta3.0
+- update to 1.9 beta 3
+
+* Mon Dec  6 2010 Nalin Dahyabhai <nalin@redhat.com> 1.9-0.beta2.0
+- update to 1.9 beta 2
+
+* Tue Nov  9 2010 Nalin Dahyabhai <nalin@redhat.com> 1.9-0.beta1.1
+- drop not-needed-since-1.8 build dependency on rsh (ssorce)
+
+* Fri Nov  5 2010 Nalin Dahyabhai <nalin@redhat.com> 1.9-0.beta1.0
+- start moving to 1.9 with beta 1
+  - drop patches for RT#5755, RT#6762, RT#6774, RT#6775
+  - drop no-longer-needed backport patch for #539423
+  - drop no-longer-needed patch for CVE-2010-1322
+- if WITH_NSS is set, built with --with-crypto-impl=nss (requires NSS 3.12.9)
+
+* Tue Oct  5 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.3-8
+- incorporate upstream patch to fix uninitialized pointer crash in the KDC's
+  authorization data handling (CVE-2010-1322, #636335)
+
+* Mon Oct  4 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.3-7
+- rebuild
+
+* Mon Oct  4 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.3-6
+- pull down patches from trunk to implement k5login_authoritative and
+  k5login_directory settings for krb5.conf (#539423)
+
+* Wed Sep 29 2010 jkeating - 1.8.3-5
+- Rebuilt for gcc bug 634757
+
+* Wed Sep 15 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.3-4
+- fix reading of keyUsage extensions when attempting to select pkinit client
+  certs (part of #629022, RT#6775)
+- fix selection of pkinit client certs when one or more don't include a
+  subjectAltName extension (part of #629022, RT#6774)
+
+* Fri Sep  3 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.3-3
+- build with -fstack-protector-all instead of the default -fstack-protector,
+  so that we add checking to more functions (i.e., all of them) (#629950)
+- also link binaries with -Wl,-z,relro,-z,now (part of #629950)
+
+* Tue Aug 24 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.3-2
+- fix a logic bug in computing key expiration times (RT#6762, #627022)
+
+* Wed Aug  4 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.3-1
+- update to 1.8.3
+  - drop backports of fixes for gss context expiration and error table
+    registration/deregistration mismatch
+  - drop patch for upstream #6750
+
+* Wed Jul  7 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.2-3
+- tell krb5kdc and kadmind to create pid files, since they can
+- add logrotate configuration files for krb5kdc and kadmind (#462658)
+- fix parsing of the pidfile option in the KDC (upstream #6750)
+
+* Mon Jun 21 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.2-2
+- libgssapi: pull in patch from svn to stop returning context-expired errors
+  when the ticket which was used to set up the context expires (#605366,
+  upstream #6739)
+
+* Mon Jun 21 2010 Nalin Dahyabhai <nalin@redhat.com>
+- pull up fix for upstream #6745, in which the gssapi library would add the
+  wrong error table but subsequently attempt to unload the right one
+
+* Thu Jun 10 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.2-1
+- update to 1.8.2
+  - drop patches for CVE-2010-1320, CVE-2010-1321
+
+* Tue Jun  1 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.1-7
+- rebuild
+
+* Thu May 27 2010 Nalin Dahyabhai <nalin@redhat.com>
+- ksu: move session management calls to before we drop privileges, like
+  su does (#596887), and don't skip the PAM account check for root or the
+  same user (more of #540769)
+
+* Mon May 24 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.1-6
+- make krb5-server-ldap also depend on the same version-release of krb5-libs,
+  as the other subpackages do, if only to make it clearer than it is when we
+  just do it through krb5-server
+- drop explicit linking with libtinfo for applications that use libss, now
+  that readline itself links with libtinfo (as of readline-5.2-3, since
+  fedora 7 or so)
+- go back to building without strict aliasing (compiler warnings in gssrpc)
+
+* Tue May 18 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.1-5
+- add patch to correct GSSAPI library null pointer dereference which could be
+  triggered by malformed client requests (CVE-2010-1321, #582466)
+
+* Tue May  4 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.1-4
+- fix output of kprop's init script's "status" and "reload" commands (#588222)
+
+* Tue Apr 20 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.1-3
+- incorporate patch to fix double-free in the KDC (CVE-2010-1320, #581922)
+
+* Wed Apr 14 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.1-2
+- fix a typo in kerberos.ldif
+
+* Fri Apr  9 2010 Nalin Dahyabhai <nalin@redhat.com> 1.8.1-1
+- update to 1.8.1
+  - no longer need patches for #555875, #561174, #563431, RT#6661, CVE-2010-0628
+- replace buildrequires on tetex-latex with one on texlive-latex, which is
+  the package that provides it now
+
+* Thu Apr  8 2010 Nalin Dahyabhai <nalin@redhat.com>
+- kdc.conf: no more need to suggest a v4 mode, or listening on the v4 port
+
+* Thu Apr  8 2010 Nalin Dahyabhai <nalin@redhat.com>
+- drop patch to suppress key expiration warnings sent from the KDC in
+  the last-req field, as the KDC is expected to just be configured to either
+  send them or not as a particular key approaches expiration (#556495)
+
+* Tue Mar 23 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.8-5
+- add upstream fix for denial-of-service in SPNEGO (CVE-2010-0628, #576325)
+- kdc.conf: no more need to suggest keeping keys with v4-compatible salting
+
+* Fri Mar 19 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.8-4
+- remove the krb5-appl bits (the -workstation-clients and -workstation-servers
+  subpackages) now that krb5-appl is its own package
+- replace our patch for #563431 (kpasswd doesn't fall back to guessing your
+  principal name using your user name if you don't have a ccache) with the
+  one upstream uses
+
+* Fri Mar 12 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.8-3
+- add documentation for the ticket_lifetime option (#561174)
+
+* Mon Mar  8 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.8-2
+- pull up patch to get the client libraries to correctly perform password
+  changes over IPv6 (Sumit Bose, RT#6661)
+
+* Fri Mar  5 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.8-1
+- update to 1.8
+  - temporarily bundling the krb5-appl package (split upstream as of 1.8)
+    until its package review is complete
+  - profile.d scriptlets are now only needed by -workstation-clients
+  - adjust paths in init scripts
+  - drop upstreamed fix for KDC denial of service (CVE-2010-0283)
+  - drop patch to check the user's password correctly using crypt(), which
+    isn't a code path we hit when we're using PAM
+
+* Wed Mar  3 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7.1-6
+- fix a null pointer dereference and crash introduced in our PAM patch that
+  would happen if ftpd was given the name of a user who wasn't known to the
+  local system, limited to being triggerable by gssapi-authenticated clients by
+  the default xinetd config (Olivier Fourdan, #569472)
+
+* Tue Mar  2 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7.1-5
+- fix a regression (not labeling a kdb database lock file correctly, #569902)
+
+* Thu Feb 25 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7.1-4
+- move the package changelog to the end to match the usual style (jdennis)
+- scrub out references to $RPM_SOURCE_DIR (jdennis)
+- include a symlink to the readme with the name LICENSE so that people can
+  find it more easily (jdennis)
+
+* Wed Feb 17 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7.1-3
+- pull up the change to make kpasswd's behavior better match the docs
+  when there's no ccache (#563431)
+
+* Tue Feb 16 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7.1-2
+- apply patch from upstream to fix KDC denial of service (CVE-2010-0283,
+  #566002)
+
+* Wed Feb  3 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7.1-1
+- update to 1.7.1
+  - don't trip AD lockout on wrong password (#542687, #554351)
+  - incorporates fixes for CVE-2009-4212 and CVE-2009-3295
+  - fixes gss_krb5_copy_ccache() when SPNEGO is used
+- move sim_client/sim_server, gss-client/gss-server, uuclient/uuserver to
+  the devel subpackage, better lining up with the expected krb5/krb5-appl
+  split in 1.8
+- drop kvno,kadmin,k5srvutil,ktutil from -workstation-servers, as it already
+  depends on -workstation which also includes them
+
+* Mon Jan 25 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7-23
+- tighten up default permissions on kdc.conf and kadm5.acl (#558343)
+
+* Fri Jan 22 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7-22
+- use portreserve correctly -- portrelease takes the basename of the file
+  whose entries should be released, so we need three files, not one
+
+* Mon Jan 18 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7-21
+- suppress warnings of impending password expiration if expiration is more than
+  seven days away when the KDC reports it via the last-req field, just as we
+  already do when it reports expiration via the key-expiration field (#556495)
+- link with libtinfo rather than libncurses, when we can, in future RHEL
+
+* Fri Jan 15 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7-20
+- krb5_get_init_creds_password: check opte->flags instead of options->flags
+  when checking whether or not we get to use the prompter callback (#555875)
+
+* Thu Jan 14 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7-19
+- use portreserve to make sure the KDC can always bind to the kerberos-iv
+  port, kpropd can always bind to the krb5_prop port, and that kadmind can
+  always bind to the kerberos-adm port (#555279)
+- correct inadvertent use of macros in the changelog (rpmlint)
+
+* Tue Jan 12 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7-18
+- add upstream patch for integer underflow during AES and RC4 decryption
+  (CVE-2009-4212), via Tom Yu (#545015)
+
+* Wed Jan  6 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7-17
+- put the conditional back for the -devel subpackage
+- back down to the earlier version of the patch for #551764; the backported
+  alternate version was incomplete
+
+* Tue Jan  5 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7-16
+- use %%global instead of %%define
+- pull up proposed patch for creating previously-not-there lock files for
+  kdb databases when 'kdb5_util' is called to 'load' (#551764)
+
+* Mon Jan  4 2010 Dennis Gregorovic <dgregor@redhat.com>
+- fix conditional for future RHEL
+
+* Mon Jan  4 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7-15
+- add upstream patch for KDC crash during referral processing (CVE-2009-3295),
+  via Tom Yu (#545002)
+
+* Mon Dec 21 2009 Nalin Dahyabhai <nalin@redhat.com> - 1.7-14
+- refresh patch for #542868 from trunk
+
+* Thu Dec 10 2009 Nalin Dahyabhai <nalin@redhat.com>
+- move man pages that live in the -libs subpackage into the regular
+  %%{_mandir} tree where they'll still be found if that package is the
+  only one installed (#529319)
+
+* Wed Dec  9 2009 Nalin Dahyabhai <nalin@redhat.com> - 1.7-13
+- and put it back in
+
+* Tue Dec  8 2009 Nalin Dahyabhai <nalin@redhat.com>
+- back that last change out
+
+* Tue Dec  8 2009 Nalin Dahyabhai <nalin@redhat.com> - 1.7-12
+- try to make gss_krb5_copy_ccache() work correctly for spnego (#542868)
+
+* Fri Dec  4 2009 Nalin Dahyabhai <nalin@redhat.com>
+- make krb5-config suppress CFLAGS output when called with --libs (#544391)
+
+* Thu Dec  3 2009 Nalin Dahyabhai <nalin@redhat.com> - 1.7-11
+- ksu: move account management checks to before we drop privileges, like
+  su does (#540769)
+- selinux: set the user part of file creation contexts to match the current
+  context instead of what we looked up
+- configure with --enable-dns-for-realm instead of --enable-dns, which isn't
+  recognized any more
+
+* Fri Nov 20 2009 Nalin Dahyabhai <nalin@redhat.com> - 1.7-10
+- move /etc/pam.d/ksu from krb5-workstation-servers to krb5-workstation,
+  where it's actually needed (#538703)
+
+* Fri Oct 23 2009 Nalin Dahyabhai <nalin@redhat.com> - 1.7-9
+- add some conditional logic to simplify building on older Fedora releases
+
+* Tue Oct 13 2009 Nalin Dahyabhai <nalin@redhat.com>
+- don't forget the README
+
+* Mon Sep 14 2009 Nalin Dahyabhai <nalin@redhat.com> - 1.7-8
+- specify the location of the subsystem lock when using the status() function
+  in the kadmind and kpropd init scripts, so that we get the right error when
+  we're dead but have a lock file - requires initscripts 8.99 (#521772)
+
+* Tue Sep  8 2009 Nalin Dahyabhai <nalin@redhat.com>
+- if the init script fails to start krb5kdc/kadmind/kpropd because it's already
+  running (according to status()), return 0 (part of #521772)
+
+* Mon Aug 24 2009 Nalin Dahyabhai <nalin@redhat.com> - 1.7-7
+- work around a compile problem with new openssl
+
+* Fri Aug 21 2009 Tomas Mraz <tmraz@redhat.com> - 1.7-6
+- rebuilt with new openssl
+
+* Fri Jul 24 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.7-5
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
+
+* Tue Jul  7 2009 Nalin Dahyabhai <nalin@redhat.com> 1.7-5
+- rebuild to pick up the current forms of various patches
+
+* Mon Jul  6 2009 Nalin Dahyabhai <nalin@redhat.com>
+- simplify the man pages patch by only preprocessing the files we care about
+  and moving shared configure.in logic into a shared function
+- catch the case of ftpd printing file sizes using %%i, when they might be
+  bigger than an int now
+
+* Tue Jun 30 2009 Nalin Dahyabhai <nalin@redhat.com> 1.7-4
+- try to merge and clean up all the large file support for ftp and rcp
+  - ftpd no longer prints a negative length when sending a large file
+    from a 32-bit host
+
+* Tue Jun 30 2009 Nalin Dahyabhai <nalin@redhat.com>
+- pam_rhosts_auth.so's been gone, use pam_rhosts.so instead
+
+* Mon Jun 29 2009 Nalin Dahyabhai <nalin@redhat.com> 1.7-3
+- switch buildrequires: and requires: on e2fsprogs-devel into
+  buildrequires: and requires: on libss-devel, libcom_err-devel, per
+  sandeen on fedora-devel-list
+
+* Fri Jun 26 2009 Nalin Dahyabhai <nalin@redhat.com>
+- fix a type mismatch in krb5_copy_error_message()
+- ftp: fix some odd use of strlen()
+- selinux labeling: use selabel_open() family of functions rather than
+  matchpathcon(), bail on it if attempting to get the mutex lock fails
+
+* Tue Jun 16 2009 Nalin Dahyabhai <nalin@redhat.com>
+- compile with %%{?_smp_mflags} (Steve Grubb)
+- drop the bit where we munge part of the error table header, as it's not
+  needed any more
+
+* Fri Jun  5 2009 Nalin Dahyabhai <nalin@redhat.com> 1.7-2
+- add and own %%{_libdir}/krb5/plugins/authdata
+
+* Thu Jun  4 2009 Nalin Dahyabhai <nalin@redhat.com> 1.7-1
+- update to 1.7
+  - no need to work around build issues with ASN1BUF_OMIT_INLINE_FUNCS
+  - configure recognizes --enable/--disable-pkinit now
+  - configure can take --disable-rpath now
+  - no more libdes425, krb524d, krb425.info
+  - kadmin/k5srvutil/ktutil are user commands now
+  - new kproplog
+  - FAST encrypted-challenge plugin is new
+- drop static build logic
+- drop pam_krb5-specific configuration from the default krb5.conf
+- drop only-use-v5 flags being passed to various things started by xinetd
+- put %%{krb5prefix}/sbin in everyone's path, too (#504525)
+
+* Tue May 19 2009 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-106
+- add an auth stack to ksu's PAM configuration so that pam_setcred() calls
+  won't just fail
+
+* Mon May 11 2009 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-105
+- make PAM support for ksu also set PAM_RUSER
+
+* Thu Apr 23 2009 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-104
+- extend PAM support to ksu: perform account and session management for the
+  target user
+- pull up and merge James Leddy's changes to also set PAM_RHOST in PAM-aware
+  network-facing services
+
+* Tue Apr 21 2009 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-103
+- fix a typo in a ksu error message (Marek Mahut)
+- "rev" works the way the test suite expects now, so don't disable tests
+  that use it
+
+* Mon Apr 20 2009 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-102
+- add LSB-style init script info
+
+* Fri Apr 17 2009 Nalin Dahyabhai <nalin@redhat.com>
+- explicitly run the pdf generation script using sh (part of #225974)
+
+* Tue Apr  7 2009 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-101
+- add patches for read overflow and null pointer dereference in the
+  implementation of the SPNEGO mechanism (CVE-2009-0844, CVE-2009-0845)
+- add patch for attempt to free uninitialized pointer in libkrb5
+  (CVE-2009-0846)
+- add patch to fix length validation bug in libkrb5 (CVE-2009-0847)
+- put the krb5-user .info file into just -workstation and not also
+  -workstation-clients
+
+* Mon Apr  6 2009 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-100
+- turn off krb4 support (it won't be part of the 1.7 release, but do it now)
+- use triggeruns to properly shut down and disable krb524d when -server and
+  -workstation-servers gets upgraded, because it's gone now
+- move the libraries to /%%{_lib}, but leave --libdir alone so that plugins
+  get installed and are searched for in the same locations (#473333)
+- clean up buildprereq/prereqs, explicit mktemp requires, and add the
+  ldconfig for the -server-ldap subpackage (part of #225974)
+- escape possible macros in the changelog (part of #225974)
+- fixup summary texts (part of #225974)
+- take the execute bit off of the protocol docs (part of #225974)
+- unflag init scripts as configuration files (part of #225974)
+- make the kpropd init script treat 'reload' as 'restart' (part of #225974)
+
+* Tue Mar 17 2009 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-19
+- libgssapi_krb5: backport fix for some errors which can occur when
+  we fail to set up the server half of a context (CVE-2009-0845)
+
+* Wed Feb 25 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.6.3-18
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild
+
+* Fri Jan 16 2009 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-17
+- rebuild
+
+* Thu Sep  4 2008 Nalin Dahyabhai <nalin@redhat.com>
+- if we successfully change the user's password during an attempt to get
+  initial credentials, but then fail to get initial creds from a non-master
+  using the new password, retry against the master (#432334)
+
+* Tue Aug  5 2008 Tom "spot" Callaway <tcallawa@redhat.com> 1.6.3-16
+- fix license tag
+
+* Wed Jul 16 2008 Nalin Dahyabhai <nalin@redhat.com>
+- clear fuzz out of patches, dropping a man page patch which is no longer
+  necessary
+- quote %%{__cc} where needed because it includes whitespace now
+- define ASN1BUF_OMIT_INLINE_FUNCS at compile-time (for now) to keep building
+
+* Fri Jul 11 2008 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-15
+- build with -fno-strict-aliasing, which is needed because the library
+  triggers these warnings
+- don't forget to label principal database lock files
+- fix the labeling patch so that it doesn't break bootstrapping
+
+* Sat Jun 14 2008 Tom "spot" Callaway <tcallawa@redhat.com> 1.6.3-14
+- generate src/include/krb5/krb5.h before building
+- fix conditional for sparcv9
+
+* Wed Apr 16 2008 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-13
+- ftp: use the correct local filename during mget when the 'case' option is
+  enabled (#442713)
+
+* Fri Apr  4 2008 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-12
+- stop exporting kadmin keys to a keytab file when kadmind starts -- the
+  daemon's been able to use the database directly for a long long time now
+- belatedly add aes128,aes256 to the default set of supported key types
+
+* Tue Apr  1 2008 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-11
+- libgssapi_krb5: properly export the acceptor subkey when creating a lucid
+  context (Kevin Coffman, via the nfs4 mailing list)
+
+* Tue Mar 18 2008 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-10
+- add fixes from MITKRB5-SA-2008-001 for use of null or dangling pointer
+  when v4 compatibility is enabled on the KDC (CVE-2008-0062, CVE-2008-0063,
+  #432620, #432621)
+- add fixes from MITKRB5-SA-2008-002 for array out-of-bounds accesses when
+  high-numbered descriptors are used (CVE-2008-0947, #433596)
+- add backport bug fix for an attempt to free non-heap memory in
+  libgssapi_krb5 (CVE-2007-5901, #415321)
+- add backport bug fix for a double-free in out-of-memory situations in
+  libgssapi_krb5 (CVE-2007-5971, #415351)
+
+* Tue Mar 18 2008 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-9
+- rework file labeling patch to not depend on fragile preprocessor trickery,
+  in another attempt at fixing #428355 and friends
+
+* Tue Feb 26 2008 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-8
+- ftp: add patch to fix "runique on" case when globbing fixes applied
+- stop adding a redundant but harmless call to initialize the gssapi internals
+
+* Mon Feb 25 2008 Nalin Dahyabhai <nalin@redhat.com>
+- add patch to suppress double-processing of /etc/krb5.conf when we build
+  with --sysconfdir=/etc, thereby suppressing double-logging (#231147)
+
+* Mon Feb 25 2008 Nalin Dahyabhai <nalin@redhat.com>
+- remove a patch, to fix problems with interfaces which are "up" but which
+  have no address assigned, which conflicted with a different fix for the same
+  problem in 1.5 (#200979)
+
+* Mon Feb 25 2008 Nalin Dahyabhai <nalin@redhat.com>
+- ftp: don't lose track of a descriptor on passive get when the server fails to
+  open a file
+
+* Mon Feb 25 2008 Nalin Dahyabhai <nalin@redhat.com>
+- in login, allow PAM to interact with the user when they've been strongly
+  authenticated
+- in login, signal PAM when we're changing an expired password that it's an
+  expired password, so that when cracklib flags a password as being weak it's
+  treated as an error even if we're running as root
+
+* Mon Feb 18 2008 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-7
+- drop netdb patch
+- kdb_ldap: add patch to treat 'nsAccountLock: true' as an indication that
+  the DISALLOW_ALL_TIX flag is set on an entry, for better interop with Fedora,
+  Netscape, Red Hat Directory Server (Simo Sorce)
+
+* Wed Feb 13 2008 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-6
+- patch to avoid depending on <netdb.h> to define NI_MAXHOST and NI_MAXSERV
+
+* Tue Feb 12 2008 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-5
+- enable patch for key-expiration reporting
+- enable patch to make kpasswd fall back to TCP if UDP fails (#251206)
+- enable patch to make kpasswd use the right sequence number on retransmit
+- enable patch to allow mech-specific creds delegated under spnego to be found
+  when searching for creds
+
+* Wed Jan  2 2008 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-4
+- some init script cleanups
+  - drop unquoted check and silent exit for "$NETWORKING" (#426852, #242502)
+  - krb524: don't barf on missing database if it looks like we're using kldap,
+    same as for kadmin
+  - return non-zero status for missing files which cause startup to
+    fail (#242502)
+
+* Tue Dec 18 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-3
+- allocate space for the nul-terminator in the local pathname when looking up
+  a file context, and properly free a previous context (Jose Plans, #426085)
+
+* Wed Dec  5 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-2
+- rebuild
+
+* Tue Oct 23 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.3-1
+- update to 1.6.3, dropping now-integrated patches for CVE-2007-3999
+  and CVE-2007-4000 (the new pkinit module is built conditionally and goes
+  into the -pkinit-openssl package, at least for now, to make a buildreq
+  loop with openssl avoidable)
+
+* Wed Oct 17 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.2-10
+- make proper use of pam_loginuid and pam_selinux in rshd and ftpd
+
+* Fri Oct 12 2007 Nalin Dahyabhai <nalin@redhat.com>
+- make krb5.conf %%verify(not md5 size mtime) in addition to
+  %%config(noreplace), like /etc/nsswitch.conf (#329811)
+
+* Mon Oct  1 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.2-9
+- apply the fix for CVE-2007-4000 instead of the experimental patch for
+  setting ok-as-delegate flags
+
+* Tue Sep 11 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.2-8
+- move the db2 kdb plugin from -server to -libs, because a multilib libkdb
+  might need it
+
+* Tue Sep 11 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.2-7
+- also perform PAM session and credential management when ftpd accepts a
+  client using strong authentication, missed earlier
+- also label kadmind log files and files created by the db2 plugin
+
+* Thu Sep  6 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.2-6
+- incorporate updated fix for CVE-2007-3999 (CVE-2007-4743)
+- fix incorrect call to "test" in the kadmin init script (#252322,#287291)
+
+* Tue Sep  4 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.2-5
+- incorporate fixes for MITKRB5-SA-2007-006 (CVE-2007-3999, CVE-2007-4000)
+
+* Sat Aug 25 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.2-4
+- cover more cases in labeling files on creation
+- add missing gawk build dependency
+
+* Thu Aug 23 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.2-3
+- rebuild
+
+* Thu Jul 26 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.2-2
+- kdc.conf: default to listening for TCP clients, too (#248415)
+
+* Thu Jul 19 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.2-1
+- update to 1.6.2
+- add "buildrequires: texinfo-tex" to get texi2pdf
+
+* Wed Jun 27 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.1-8
+- incorporate fixes for MITKRB5-SA-2007-004 (CVE-2007-2442,CVE-2007-2443)
+  and MITKRB5-SA-2007-005 (CVE-2007-2798)
+
+* Mon Jun 25 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.1-7
+- reintroduce missing %%postun for the non-split_workstation case
+
+* Mon Jun 25 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.1-6
+- rebuild
+
+* Mon Jun 25 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.1-5.1
+- rebuild
+
+* Sun Jun 24 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.1-5
+- add missing pam-devel build requirement, force selinux-or-fail build
+
+* Sun Jun 24 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.1-4
+- rebuild
+
+* Sun Jun 24 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.1-3
+- label all files at creation-time according to the SELinux policy (#228157)
+
+* Fri Jun 22 2007 Nalin Dahyabhai <nalin@redhat.com>
+- perform PAM account / session management in krshd (#182195,#195922)
+- perform PAM authentication and account / session management in ftpd
+- perform PAM authentication, account / session management, and password-
+  changing in login.krb5 (#182195,#195922)
+
+* Fri Jun 22 2007 Nalin Dahyabhai <nalin@redhat.com>
+- preprocess kerberos.ldif into a format FDS will like better, and include
+  that as a doc file as well
+
+* Fri Jun 22 2007 Nalin Dahyabhai <nalin@redhat.com>
+- switch man pages to being generated with the right paths in them
+- drop old, incomplete SELinux patch
+- add patch from Greg Hudson to make srvtab routines report missing-file errors
+  at same point that keytab routines do (#241805)
+
+* Thu May 24 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.1-2
+- pull patch from svn to undo unintentional chattiness in ftp
+- pull patch from svn to handle NULL krb5_get_init_creds_opt structures
+  better in a couple of places where they're expected
+
+* Wed May 23 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6.1-1
+- update to 1.6.1
+  - drop no-longer-needed patches for CVE-2007-0956,CVE-2007-0957,CVE-2007-1216
+  - drop patch for sendto bug in 1.6, fixed in 1.6.1
+
+* Fri May 18 2007 Nalin Dahyabhai <nalin@redhat.com>
+- kadmind.init: don't fail outright if the default principal database
+  isn't there if it looks like we might be using the kldap plugin
+- kadmind.init: attempt to extract the key for the host-specific kadmin
+  service when we try to create the keytab
+
+* Wed May 16 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6-6
+- omit dependent libraries from the krb5-config --libs output, as using
+  shared libraries (no more static libraries) makes them unnecessary and
+  they're not part of the libkrb5 interface (patch by Rex Dieter, #240220)
+  (strips out libkeyutils, libresolv, libdl)
+
+* Fri May  4 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6-5
+- pull in keyutils as a build requirement to get the "KEYRING:" ccache type,
+  because we've merged
+
+* Fri May  4 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6-4
+- fix an uninitialized length value which could cause a crash when parsing
+  key data coming from a directory server
+- correct a typo in the krb5.conf man page ("ldap_server"->"ldap_servers")
+
+* Fri Apr 13 2007 Nalin Dahyabhai <nalin@redhat.com>
+- move the default acl_file, dict_file, and admin_keytab settings to
+  the part of the default/example kdc.conf where they'll actually have
+  an effect (#236417)
+
+* Thu Apr  5 2007 Nalin Dahyabhai <nalin@redhat.com> 1.5-24
+- merge security fixes from RHSA-2007:0095
+
+* Tue Apr  3 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6-3
+- add patch to correct unauthorized access via krb5-aware telnet
+  daemon (#229782, CVE-2007-0956)
+- add patch to fix buffer overflow in krb5kdc and kadmind
+  (#231528, CVE-2007-0957)
+- add patch to fix double-free in kadmind (#231537, CVE-2007-1216)
+
+* Thu Mar 22 2007 Nalin Dahyabhai <nalin@redhat.com>
+- back out buildrequires: keyutils-libs-devel for now
+
+* Thu Mar 22 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6-2
+- add buildrequires: on keyutils-libs-devel to enable use of keyring ccaches,
+  dragging keyutils-libs in as a dependency
+
+* Mon Mar 19 2007 Nalin Dahyabhai <nalin@redhat.com> 1.5-23
+- fix bug ID in changelog
+
+* Thu Mar 15 2007 Nalin Dahyabhai <nalin@redhat.com> 1.5-22
+
+* Thu Mar 15 2007 Nalin Dahyabhai <nalin@redhat.com> 1.5-21
+- add preliminary patch to fix buffer overflow in krb5kdc and kadmind
+  (#231528, CVE-2007-0957)
+- add preliminary patch to fix double-free in kadmind (#231537, CVE-2007-1216)
+
+* Wed Feb 28 2007 Nalin Dahyabhai <nalin@redhat.com>
+- add patch to build semi-useful static libraries, but don't apply it unless
+  we need them
+
+* Tue Feb 27 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.5-20
+- temporarily back out %%post changes, fix for #143289 for security update
+- add preliminary patch to correct unauthorized access via krb5-aware telnet
+
+* Mon Feb 19 2007 Nalin Dahyabhai <nalin@redhat.com>
+- make profile.d scriptlets mode 644 instead of 755 (part of #225974)
+
+* Tue Jan 30 2007 Nalin Dahyabhai <nalin@redhat.com> 1.6-1
+- clean up quoting of command-line arguments passed to the krsh/krlogin
+  wrapper scripts
+
+* Mon Jan 22 2007 Nalin Dahyabhai <nalin@redhat.com>
+- initial update to 1.6, pre-package-reorg
+- move workstation daemons to a new subpackage (#81836, #216356, #217301), and
+  make the new subpackage require xinetd (#211885)
+
+* Mon Jan 22 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.5-18
+- make use of install-info more failsafe (Ville Skyttä, #223704)
+- preserve timestamps on shell scriptlets at %%install-time
+
+* Tue Jan 16 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.5-17
+- move to using pregenerated PDF docs to cure multilib conflicts (#222721)
+
+* Fri Jan 12 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.5-16
+- update backport of the preauth module interface (part of #194654)
+
+* Tue Jan  9 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.5-14
+- apply fixes from Tom Yu for MITKRB5-SA-2006-002 (CVE-2006-6143) (#218456)
+- apply fixes from Tom Yu for MITKRB5-SA-2006-003 (CVE-2006-6144) (#218456)
+
+* Wed Dec 20 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-12
+- update backport of the preauth module interface
+
+* Mon Oct 30 2006 Nalin Dahyabhai <nalin@redhat.com>
+- update backport of the preauth module interface
+- add proposed patches 4566, 4567
+- add proposed edata reporting interface for KDC
+- add temporary placeholder for module global context fixes
+
+* Mon Oct 23 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-11
+- don't bail from the KDC init script if there's no database, it may be in
+  a different location than the default (fenlason)
+- remove the [kdc] section from the default krb5.conf -- doesn't seem to have
+  been applicable for a while
+
+* Wed Oct 18 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-10
+- rename krb5.sh and krb5.csh so that they don't overlap (#210623)
+- way-late application of added error info in kadmind.init (#65853)
+ 
+* Wed Oct 18 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-9.pal_18695
+- add backport of in-development preauth module interface (#208643)
+
+* Mon Oct  9 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-9
+- provide docs in PDF format instead of as tex source (Enrico Scholz, #209943)
+
+* Wed Oct  4 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-8
+- add missing shebang headers to krsh and krlogin wrapper scripts (#209238)
+
+* Wed Sep  6 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-7
+- set SS_LIB at configure-time so that libss-using apps get working readline
+  support (#197044)
+
+* Fri Aug 18 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-6
+- switch to the updated patch for MITKRB-SA-2006-001
+
+* Tue Aug  8 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-5
+- apply patch to address MITKRB-SA-2006-001 (CVE-2006-3084)
+
+* Mon Aug  7 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-4
+- ensure that the gssapi library's been initialized before walking the
+  internal mechanism list in gss_release_oid(), needed if called from
+  gss_release_name() right after a gss_import_name() (#198092)
+
+* Tue Jul 25 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-3
+- rebuild
+
+* Tue Jul 25 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-2
+- pull up latest revision of patch to reduce lockups in rsh/rshd
+
+* Mon Jul 17 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.5-1.2
+- rebuild
+
+* Wed Jul 12 2006 Jesse Keating <jkeating@redhat.com> - 1.5-1.1
+- rebuild
+
+* Thu Jul  6 2006 Nalin Dahyabhai <nalin@redhat.com> 1.5-1
+- build
+
+* Wed Jul  5 2006 Nalin Dahyabhai <nalin@redhat.com> 1.5-0
+- update to 1.5
+
+* Fri Jun 23 2006 Nalin Dahyabhai <nalin@redhat.com> 1.4.3-9
+- mark profile.d config files noreplace (Laurent Rineau, #196447)
+
+* Thu Jun  8 2006 Nalin Dahyabhai <nalin@redhat.com> 1.4.3-8
+- add buildprereq for autoconf
+
+* Mon May 22 2006 Nalin Dahyabhai <nalin@redhat.com> 1.4.3-7
+- further munge krb5-config so that 'libdir=/usr/lib' is given even on 64-bit
+  architectures, to avoid multilib conflicts; other changes will conspire to
+  strip out the -L flag which uses this, so it should be harmless (#192692)
+
+* Fri Apr 28 2006 Nalin Dahyabhai <nalin@redhat.com> 1.4.3-6
+- adjust the patch which removes the use of rpath to also produce a
+  krb5-config which is okay in multilib environments (#190118)
+- make the name-of-the-tempfile comment which compile_et adds to error code
+  headers always list the same file to avoid conflicts on multilib installations
+- strip SIZEOF_LONG out of krb5.h so that it doesn't conflict on multilib boxes
+- strip GSS_SIZEOF_LONG out of gssapi.h so that it doesn't conflict on mulitlib
+  boxes
+
+* Fri Apr 14 2006 Stepan Kasal <skasal@redhat.com> 1.4.3-5
+- Fix formatting typo in kinit.1 (krb5-kinit-man-typo.patch)
+
+* Fri Feb 10 2006 Jesse Keating <jkeating@redhat.com> 1.4.3-4.1
+- bump again for double-long bug on ppc(64)
+
+* Mon Feb  6 2006 Nalin Dahyabhai <nalin@redhat.com> 1.4.3-4
+- give a little bit more information to the user when kinit gets the catch-all
+  I/O error (#180175)
+
+* Thu Jan 19 2006 Nalin Dahyabhai <nalin@redhat.com> 1.4.3-3
+- rebuild properly when pthread_mutexattr_setrobust_np() is defined but not
+  declared, such as with recent glibc when _GNU_SOURCE isn't being used
+
+* Thu Jan 19 2006 Matthias Clasen <mclasen@redhat.com> 1.4.3-2
+- Use full paths in krb5.sh to avoid path lookups
+
+* Fri Dec 09 2005 Jesse Keating <jkeating@redhat.com>
+- rebuilt
+
+* Thu Dec  1 2005 Nalin Dahyabhai <nalin@redhat.com>
+- login: don't truncate passwords before passing them into crypt(), in
+  case they're significant (#149476)
+
+* Thu Nov 17 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4.3-1
+- update to 1.4.3
+- make ksu setuid again (#137934, others)
+
+* Tue Sep 13 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4.2-4
+- mark %%{krb5prefix}/man so that files which are packaged within it are
+  flagged as %%doc (#168163)
+
+* Tue Sep  6 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4.2-3
+- add an xinetd configuration file for encryption-only telnetd, parallelling
+  the kshell/ekshell pair (#167535)
+
+* Wed Aug 31 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4.2-2
+- change the default configured encryption type for KDC databases to the
+  compiled-in default of des3-hmac-sha1 (#57847)
+
+* Thu Aug 11 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4.2-1
+- update to 1.4.2, incorporating the fixes for MIT-KRB5-SA-2005-002 and
+  MIT-KRB5-SA-2005-003
+
+* Wed Jun 29 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4.1-6
+- rebuild
+
+* Wed Jun 29 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4.1-5
+- fix telnet client environment variable disclosure the same way NetKit's
+  telnet client did (CAN-2005-0488) (#159305)
+- keep apps which call krb5_principal_compare() or krb5_realm_compare() with
+  malformed or NULL principal structures from crashing outright (Thomas Biege)
+  (#161475)
+
+* Tue Jun 28 2005 Nalin Dahyabhai <nalin@redhat.com>
+- apply fixes from draft of MIT-KRB5-SA-2005-002 (CAN-2005-1174,CAN-2005-1175)
+  (#157104)
+- apply fixes from draft of MIT-KRB5-SA-2005-003 (CAN-2005-1689) (#159755)
+
+* Fri Jun 24 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4.1-4
+- fix double-close in keytab handling
+- add port of fixes for CAN-2004-0175 to krb5-aware rcp (#151612)
+
+* Fri May 13 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4.1-3
+- prevent spurious EBADF in krshd when stdin is closed by the client while
+  the command is running (#151111)
+
+* Fri May 13 2005 Martin Stransky <stransky@redhat.com> 1.4.1-2
+- add deadlock patch, removed old patch
+
+* Fri May  6 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4.1-1
+- update to 1.4.1, incorporating fixes for CAN-2005-0468 and CAN-2005-0469
+- when starting the KDC or kadmind, if KRB5REALM is set via the /etc/sysconfig
+  file for the service, pass it as an argument for the -r flag
+
+* Wed Mar 23 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4-3
+- drop krshd patch for now
+
+* Thu Mar 17 2005 Nalin Dahyabhai <nalin@redhat.com>
+- add draft fix from Tom Yu for slc_add_reply() buffer overflow (CAN-2005-0469)
+- add draft fix from Tom Yu for env_opt_add() buffer overflow (CAN-2005-0468)
+
+* Wed Mar 16 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4-2
+- don't include <term.h> into the telnet client when we're not using curses
+
+* Thu Feb 24 2005 Nalin Dahyabhai <nalin@redhat.com> 1.4-1
+- update to 1.4
+  - v1.4 kadmin client requires a v1.4 kadmind on the server, or use the "-O"
+    flag to specify that it should communicate with the server using the older
+    protocol
+  - new libkrb5support library
+  - v5passwdd and kadmind4 are gone
+  - versioned symbols
+- pick up $KRB5KDC_ARGS from /etc/sysconfig/krb5kdc, if it exists, and pass
+  it on to krb5kdc
+- pick up $KADMIND_ARGS from /etc/sysconfig/kadmin, if it exists, and pass
+  it on to kadmind
+- pick up $KRB524D_ARGS from /etc/sysconfig/krb524, if it exists, and pass
+  it on to krb524d *instead of* "-m"
+- set "forwardable" in [libdefaults] in the default krb5.conf to match the
+  default setting which we supply for pam_krb5
+- set a default of 24h for "ticket_lifetime" in [libdefaults], reflecting the
+  compiled-in default
+
+* Mon Dec 20 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.6-3
+- rebuild
+
+* Mon Dec 20 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.6-2
+- rebuild
+
+* Mon Dec 20 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.6-1
+- update to 1.3.6, which includes the previous fix
+
+* Mon Dec 20 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.5-8
+- apply fix from Tom Yu for MITKRB5-SA-2004-004 (CAN-2004-1189)
+
+* Fri Dec 17 2004 Martin Stransky <stransky@redhat.com> 1.3.5-7
+- fix deadlock during file transfer via rsync/krsh
+- thanks goes to James Antill for hint
+
+* Fri Nov 26 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.5-6
+- rebuild
+
+* Mon Nov 22 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.5-3
+- fix predictable-tempfile-name bug in krb5-send-pr (CAN-2004-0971, #140036)
+
+* Tue Nov 16 2004 Nalin Dahyabhai <nalin@redhat.com>
+- silence compiler warning in kprop by using an in-memory ccache with a fixed
+  name instead of an on-disk ccache with a name generated by tmpnam()
+
+* Tue Nov 16 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.5-2
+- fix globbing patch port mode (#139075)
+
+* Mon Nov  1 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.5-1
+- fix segfault in telnet due to incorrect checking of gethostbyname_r result
+  codes (#129059)
+
+* Fri Oct 15 2004 Nalin Dahyabhai <nalin@redhat.com>
+- remove rc4-hmac:norealm and rc4-hmac:onlyrealm from the default list of
+  supported keytypes in kdc.conf -- they produce exactly the same keys as
+  rc4-hmac:normal because rc4 string-to-key ignores salts
+- nuke kdcrotate -- there are better ways to balance the load on KDCs, and
+  the SELinux policy for it would have been scary-looking
+- update to 1.3.5, mainly to include MITKRB5SA 2004-002 and 2004-003
+
+* Tue Aug 31 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.4-7
+- rebuild
+
+* Tue Aug 24 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.4-6
+- rebuild
+
+* Tue Aug 24 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.4-5
+- incorporate revised fixes from Tom Yu for CAN-2004-0642, CAN-2004-0644,
+  CAN-2004-0772
+
+* Mon Aug 23 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.4-4
+- rebuild
+
+* Mon Aug 23 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.4-3
+- incorporate fixes from Tom Yu for CAN-2004-0642, CAN-2004-0772
+  (MITKRB5-SA-2004-002, #130732)
+- incorporate fixes from Tom Yu for CAN-2004-0644 (MITKRB5-SA-2004-003, #130732)
+
+* Tue Jul 27 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.4-2
+- fix indexing error in server sorting patch (#127336)
+
+* Tue Jun 15 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Mon Jun 14 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.4-0.1
+- update to 1.3.4 final
+
+* Mon Jun  7 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.4-0
+- update to 1.3.4 beta1
+- remove MITKRB5-SA-2004-001, included in 1.3.4
+
+* Mon Jun  7 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.3-8
+- rebuild
+
+* Fri Jun  4 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.3-7
+- rebuild
+
+* Fri Jun  4 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.3-6
+- apply updated patch from MITKRB5-SA-2004-001 (revision 2004-06-02)
+
+* Tue Jun  1 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.3-5
+- rebuild
+
+* Tue Jun  1 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.3-4
+- apply patch from MITKRB5-SA-2004-001 (#125001)
+
+* Wed May 12 2004 Thomas Woerner <twoerner@redhat.com> 1.3.3-3
+- removed rpath
+
+* Thu Apr 15 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.3-2
+- re-enable large file support, fell out in 1.3-1
+- patch rcp to use long long and %%lld format specifiers when reporting file
+  sizes on large files
+
+* Tue Apr 13 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.3-1
+- update to 1.3.3
+
+* Wed Mar 10 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.2-1
+- update to 1.3.2
+
+* Mon Mar  8 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.1-12
+- rebuild
+
+* Tue Mar 02 2004 Elliot Lee <sopwith@redhat.com> 1.3.1-11.1
+- rebuilt
+
+* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com> 1.3.1-11
+- rebuilt
+
+* Mon Feb  9 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.1-10
+- catch krb4 send_to_kdc cases in kdc preference patch
+
+* Mon Feb  2 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.1-9
+- remove patch to set TERM in klogind which, combined with the upstream fix in
+  1.3.1, actually produces the bug now (#114762)
+
+* Mon Jan 19 2004 Nalin Dahyabhai <nalin@redhat.com> 1.3.1-8
+- when iterating over lists of interfaces which are "up" from getifaddrs(),
+  skip over those which have no address (#113347)
+
+* Mon Jan 12 2004 Nalin Dahyabhai <nalin@redhat.com>
+- prefer the kdc which last replied to a request when sending requests to kdcs
+
+* Mon Nov 24 2003 Nalin Dahyabhai <nalin@redhat.com> 1.3.1-7
+- fix combination of --with-netlib and --enable-dns (#82176)
+
+* Tue Nov 18 2003 Nalin Dahyabhai <nalin@redhat.com>
+- remove libdefault ticket_lifetime option from the default krb5.conf, it is
+  ignored by libkrb5
+
+* Thu Sep 25 2003 Nalin Dahyabhai <nalin@redhat.com> 1.3.1-6
+- fix bug in patch to make rlogind start login with a clean environment a la
+  netkit rlogin, spotted and fixed by Scott McClung
+
+* Tue Sep 23 2003 Nalin Dahyabhai <nalin@redhat.com> 1.3.1-5
+- include profile.d scriptlets in krb5-devel so that krb5-config will be in
+  the path if krb5-workstation isn't installed, reported by Kir Kolyshkin
+
+* Mon Sep  8 2003 Nalin Dahyabhai <nalin@redhat.com>
+- add more etypes (arcfour) to the default enctype list in kdc.conf
+- don't apply previous patch, refused upstream
+
+* Fri Sep  5 2003 Nalin Dahyabhai <nalin@redhat.com> 1.3.1-4
+- fix 32/64-bit bug storing and retrieving the issue_date in v4 credentials
+
+* Wed Sep 3 2003 Dan Walsh <dwalsh@redhat.com> 1.3.1-3
+- Don't check for write access on /etc/krb5.conf if SELinux
+
+* Tue Aug 26 2003 Nalin Dahyabhai <nalin@redhat.com> 1.3.1-2
+- fixup some int/pointer varargs wackiness
+
+* Tue Aug  5 2003 Nalin Dahyabhai <nalin@redhat.com> 1.3.1-1
+- rebuild
+
+* Mon Aug  4 2003 Nalin Dahyabhai <nalin@redhat.com> 1.3.1-0
+- update to 1.3.1
+
+* Thu Jul 24 2003 Nalin Dahyabhai <nalin@redhat.com> 1.3-2
+- pull fix for non-compliant encoding of salt field in etype-info2 preauth
+  data from 1.3.1 beta 1, until 1.3.1 is released.
+
+* Mon Jul 21 2003 Nalin Dahyabhai <nalin@redhat.com> 1.3-1
+- update to 1.3
+
+* Mon Jul  7 2003 Nalin Dahyabhai <nalin@redhat.com> 1.2.8-4
+- correctly use stdargs
+
+* Wed Jun 18 2003 Nalin Dahyabhai <nalin@redhat.com> 1.3-0.beta.4
+- test update to 1.3 beta 4
+- ditch statglue build option
+- krb5-devel requires e2fsprogs-devel, which now provides libss and libcom_err
+
+* Wed Jun 04 2003 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Wed May 21 2003 Jeremy Katz <katzj@redhat.com> 1.2.8-2
+- gcc 3.3 doesn't implement varargs.h, include stdarg.h instead
+
+* Wed Apr  9 2003 Nalin Dahyabhai <nalin@redhat.com> 1.2.8-1
+- update to 1.2.8
+
+* Mon Mar 31 2003 Nalin Dahyabhai <nalin@redhat.com> 1.2.7-14
+- fix double-free of enc_part2 in krb524d
+
+* Fri Mar 21 2003 Nalin Dahyabhai <nalin@redhat.com> 1.2.7-13
+- update to latest patch kit for MITKRB5-SA-2003-004
+
+* Wed Mar 19 2003 Nalin Dahyabhai <nalin@redhat.com> 1.2.7-12
+- add patch included in MITKRB5-SA-2003-003 (CAN-2003-0028)
+
+* Mon Mar 17 2003 Nalin Dahyabhai <nalin@redhat.com> 1.2.7-11
+- add patches from patchkit from MITKRB5-SA-2003-004 (CAN-2003-0138 and
+  CAN-2003-0139)
+
+* Thu Mar  6 2003 Nalin Dahyabhai <nalin@redhat.com> 1.2.7-10
+- rebuild
+
+* Thu Mar  6 2003 Nalin Dahyabhai <nalin@redhat.com> 1.2.7-9
+- fix buffer underrun in unparsing certain principals (CAN-2003-0082)
+
+* Tue Feb  4 2003 Nalin Dahyabhai <nalin@redhat.com> 1.2.7-8
+- add patch to document the reject-bad-transited option in kdc.conf
+
+* Mon Feb  3 2003 Nalin Dahyabhai <nalin@redhat.com>
+- add patch to fix server-side crashes when principals have no
+  components (CAN-2003-0072)
+
+* Thu Jan 23 2003 Nalin Dahyabhai <nalin@redhat.com> 1.2.7-7
+- add patch from Mark Cox for exploitable bugs in ftp client
+
+* Wed Jan 22 2003 Tim Powers <timp@redhat.com>
+- rebuilt
+
+* Wed Jan 15 2003 Nalin Dahyabhai <nalin@redhat.com> 1.2.7-5
+- use PICFLAGS when building code from the ktany patch
+
+* Thu Jan  9 2003 Bill Nottingham <notting@redhat.com> 1.2.7-4
+- debloat
+
+* Tue Jan  7 2003 Jeremy Katz <katzj@redhat.com> 1.2.7-3
+- include .so.* symlinks as well as .so.*.*
+
+* Mon Dec  9 2002 Jakub Jelinek <jakub@redhat.com> 1.2.7-2
+- always #include <errno.h> to access errno, never do it directly
+- enable LFS on a bunch of other 32-bit arches
+
+* Wed Dec  4 2002 Nalin Dahyabhai <nalin@redhat.com>
+- increase the maximum name length allowed by kuserok() to the higher value
+  used in development versions
+
+* Mon Dec  2 2002 Nalin Dahyabhai <nalin@redhat.com>
+- install src/krb524/README as README.krb524 in the -servers package,
+  includes information about converting for AFS principals
+
+* Fri Nov 15 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.7-1
+- update to 1.2.7
+- disable use of tcl
+
+* Mon Nov 11 2002 Nalin Dahyabhai <nalin@redhat.com>
+- update to 1.2.7-beta2 (internal only, not for release), dropping dnsparse
+  and kadmind4 fixes
+
+* Wed Oct 23 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.6-5
+- add patch for buffer overflow in kadmind4 (not used by default)
+
+* Fri Oct 11 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.6-4
+- drop a hunk from the dnsparse patch which is actually redundant (thanks to
+  Tom Yu)
+
+* Wed Oct  9 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.6-3
+- patch to handle truncated dns responses
+
+* Mon Oct  7 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.6-2
+- remove hashless key types from the default kdc.conf, they're not supposed to
+  be there, noted by Sam Hartman on krbdev
+
+* Fri Sep 27 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.6-1
+- update to 1.2.6
+
+* Fri Sep 13 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.5-7
+- use %%{_lib} for the sake of multilib systems
+
+* Fri Aug  2 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.5-6
+- add patch from Tom Yu for exploitable bugs in rpc code used in kadmind
+
+* Tue Jul 23 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.5-5
+- fix bug in krb5.csh which would cause the path check to always succeed
+
+* Fri Jul 19 2002 Jakub Jelinek <jakub@redhat.com> 1.2.5-4
+- build even libdb.a with -fPIC and $RPM_OPT_FLAGS.
+
+* Fri Jun 21 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Sun May 26 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Wed May  1 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.5-1
+- update to 1.2.5
+- disable statglue
+
+* Fri Mar  1 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.4-1
+- update to 1.2.4
+
+* Wed Feb 20 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.3-5
+- rebuild in new environment
+- reenable statglue
+
+* Sat Jan 26 2002 Florian La Roche <Florian.LaRoche@redhat.de>
+- prereq chkconfig for the server subpackage
+
+* Wed Jan 16 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.3-3
+- build without -g3, which gives us large static libraries in -devel
+
+* Tue Jan 15 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.3-2
+- reintroduce ld.so.conf munging in the -libs %%post
+
+* Thu Jan 10 2002 Nalin Dahyabhai <nalin@redhat.com> 1.2.3-1
+- rename the krb5 package back to krb5-libs; the previous rename caused
+  something of an uproar
+- update to 1.2.3, which includes the FTP and telnetd fixes
+- configure without --enable-dns-for-kdc --enable-dns-for-realm, which now set
+  the default behavior instead of enabling the feature (the feature is enabled
+  by --enable-dns, which we still use)
+- reenable optimizations on Alpha
+- support more encryption types in the default kdc.conf (heads-up from post
+  to comp.protocols.kerberos by Jason Heiss)
+
+* Fri Aug  3 2001 Nalin Dahyabhai <nalin@redhat.com> 1.2.2-14
+- rename the krb5-libs package to krb5 (naming a subpackage -libs when there
+  is no main package is silly)
+- move defaults for PAM to the appdefaults section of krb5.conf -- this is
+  the area where the krb5_appdefault_* functions look for settings)
+- disable statglue (warning: breaks binary compatibility with previous
+  packages, but has to be broken at some point to work correctly with
+  unpatched versions built with newer versions of glibc)
+
+* Fri Aug  3 2001 Nalin Dahyabhai <nalin@redhat.com> 1.2.2-13
+- bump release number and rebuild
+
+* Wed Aug  1 2001 Nalin Dahyabhai <nalin@redhat.com>
+- add patch to fix telnetd vulnerability
+
+* Fri Jul 20 2001 Nalin Dahyabhai <nalin@redhat.com>
+- tweak statglue.c to fix stat/stat64 aliasing problems
+- be cleaner in use of gcc to build shlibs
+
+* Wed Jul 11 2001 Nalin Dahyabhai <nalin@redhat.com>
+- use gcc to build shared libraries
+
+* Wed Jun 27 2001 Nalin Dahyabhai <nalin@redhat.com>
+- add patch to support "ANY" keytab type (i.e.,
+  "default_keytab_name = ANY:FILE:/etc/krb5.keytab,SRVTAB:/etc/srvtab"
+  patch from Gerald Britton, #42551)
+- build with -D_FILE_OFFSET_BITS=64 to get large file I/O in ftpd (#30697)
+- patch ftpd to use long long and %%lld format specifiers to support the SIZE
+  command on large files (also #30697)
+- don't use LOG_AUTH as an option value when calling openlog() in ksu (#45965)
+- implement reload in krb5kdc and kadmind init scripts (#41911)
+- lose the krb5server init script (not using it any more)
+
+* Sun Jun 24 2001 Elliot Lee <sopwith@redhat.com>
+- Bump release + rebuild.
+
+* Tue May 29 2001 Nalin Dahyabhai <nalin@redhat.com>
+- pass some structures by address instead of on the stack in krb5kdc
+
+* Tue May 22 2001 Nalin Dahyabhai <nalin@redhat.com>
+- rebuild in new environment
+
+* Thu Apr 26 2001 Nalin Dahyabhai <nalin@redhat.com>
+- add patch from Tom Yu to fix ftpd overflows (#37731)
+
+* Wed Apr 18 2001 Than Ngo <than@redhat.com>
+- disable optimizations on the alpha again
+
+* Fri Mar 30 2001 Nalin Dahyabhai <nalin@redhat.com>
+- add in glue code to make sure that libkrb5 continues to provide a
+  weak copy of stat()
+
+* Thu Mar 15 2001 Nalin Dahyabhai <nalin@redhat.com>
+- build alpha with -O0 for now
+
+* Thu Mar  8 2001 Nalin Dahyabhai <nalin@redhat.com>
+- fix the kpropd init script
+
+* Mon Mar  5 2001 Nalin Dahyabhai <nalin@redhat.com>
+- update to 1.2.2, which fixes some bugs relating to empty ETYPE-INFO
+- re-enable optimization on Alpha
+
+* Thu Feb  8 2001 Nalin Dahyabhai <nalin@redhat.com>
+- build alpha with -O0 for now
+- own %%{_var}/kerberos
+
+* Tue Feb  6 2001 Nalin Dahyabhai <nalin@redhat.com>
+- own the directories which are created for each package (#26342)
+
+* Tue Jan 23 2001 Nalin Dahyabhai <nalin@redhat.com>
+- gettextize init scripts
+
+* Fri Jan 19 2001 Nalin Dahyabhai <nalin@redhat.com>
+- add some comments to the ksu patches for the curious
+- re-enable optimization on alphas
+
+* Mon Jan 15 2001 Nalin Dahyabhai <nalin@redhat.com>
+- fix krb5-send-pr (#18932) and move it from -server to -workstation
+- buildprereq libtermcap-devel
+- temporariliy disable optimization on alphas
+- gettextize init scripts
+
+* Tue Dec  5 2000 Nalin Dahyabhai <nalin@redhat.com>
+- force -fPIC
+
+* Fri Dec  1 2000 Nalin Dahyabhai <nalin@redhat.com>
+- rebuild in new environment
+
+* Tue Oct 31 2000 Nalin Dahyabhai <nalin@redhat.com>
+- add bison as a BuildPrereq (#20091)
+
+* Mon Oct 30 2000 Nalin Dahyabhai <nalin@redhat.com>
+- change /usr/dict/words to /usr/share/dict/words in default kdc.conf (#20000)
+
+* Thu Oct  5 2000 Nalin Dahyabhai <nalin@redhat.com>
+- apply kpasswd bug fixes from David Wragg
+
+* Wed Oct  4 2000 Nalin Dahyabhai <nalin@redhat.com>
+- make krb5-libs obsolete the old krb5-configs package (#18351)
+- don't quit from the kpropd init script if there's no principal database so
+  that you can propagate the first time without running kpropd manually
+- don't complain if /etc/ld.so.conf doesn't exist in the -libs %%post
+
+* Tue Sep 12 2000 Nalin Dahyabhai <nalin@redhat.com>
+- fix credential forwarding problem in klogind (goof in KRB5CCNAME handling)
+  (#11588)
+- fix heap corruption bug in FTP client (#14301)
+
+* Wed Aug 16 2000 Nalin Dahyabhai <nalin@redhat.com>
+- fix summaries and descriptions
+- switched the default transfer protocol from PORT to PASV as proposed on
+  bugzilla (#16134), and to match the regular ftp package's behavior
+
+* Wed Jul 19 2000 Jeff Johnson <jbj@redhat.com>
+- rebuild to compress man pages.
+
+* Sat Jul 15 2000 Bill Nottingham <notting@redhat.com>
+- move initscript back
+
+* Fri Jul 14 2000 Nalin Dahyabhai <nalin@redhat.com>
+- disable servers by default to keep linuxconf from thinking they need to be
+  started when they don't
+
+* Thu Jul 13 2000 Prospector <bugzilla@redhat.com>
+- automatic rebuild
+
+* Mon Jul 10 2000 Nalin Dahyabhai <nalin@redhat.com>
+- change cleanup code in post to not tickle chkconfig
+- add grep as a Prereq: for -libs
+
+* Thu Jul  6 2000 Nalin Dahyabhai <nalin@redhat.com>
+- move condrestarts to postun
+- make xinetd configs noreplace
+- add descriptions to xinetd configs
+- add /etc/init.d as a prereq for the -server package
+- patch to properly truncate $TERM in krlogind
+
+* Fri Jun 30 2000 Nalin Dahyabhai <nalin@redhat.com>
+- update to 1.2.1
+- back out Tom Yu's patch, which is a big chunk of the 1.2 -> 1.2.1 update
+- start using the official source tarball instead of its contents
+
+* Thu Jun 29 2000 Nalin Dahyabhai <nalin@redhat.com>
+- Tom Yu's patch to fix compatibility between 1.2 kadmin and 1.1.1 kadmind
+- pull out 6.2 options in the spec file (sonames changing in 1.2 means it's not
+  compatible with other stuff in 6.2, so no need)
+
+* Wed Jun 28 2000 Nalin Dahyabhai <nalin@redhat.com>
+- tweak graceful start/stop logic in post and preun
+
+* Mon Jun 26 2000 Nalin Dahyabhai <nalin@redhat.com>
+- update to the 1.2 release
+- ditch a lot of our patches which went upstream
+- enable use of DNS to look up things at build-time
+- disable use of DNS to look up things at run-time in default krb5.conf
+- change ownership of the convert-config-files script to root.root
+- compress PS docs
+- fix some typos in the kinit man page
+- run condrestart in server post, and shut down in preun
+
+* Mon Jun 19 2000 Nalin Dahyabhai <nalin@redhat.com>
+- only remove old krb5server init script links if the init script is there
+
+* Sat Jun 17 2000 Nalin Dahyabhai <nalin@redhat.com>
+- disable kshell and eklogin by default
+
+* Thu Jun 15 2000 Nalin Dahyabhai <nalin@redhat.com>
+- patch mkdir/rmdir problem in ftpcmd.y
+- add condrestart option to init script
+- split the server init script into three pieces and add one for kpropd
+
+* Wed Jun 14 2000 Nalin Dahyabhai <nalin@redhat.com>
+- make sure workstation servers are all disabled by default
+- clean up krb5server init script
+
+* Fri Jun  9 2000 Nalin Dahyabhai <nalin@redhat.com>
+- apply second set of buffer overflow fixes from Tom Yu
+- fix from Dirk Husung for a bug in buffer cleanups in the test suite
+- work around possibly broken rev binary in running test suite
+- move default realm configs from /var/kerberos to %%{_var}/kerberos
+
+* Tue Jun  6 2000 Nalin Dahyabhai <nalin@redhat.com>
+- make ksu and v4rcp owned by root
+
+* Sat Jun  3 2000 Nalin Dahyabhai <nalin@redhat.com>
+- use %%{_infodir} to better comply with FHS
+- move .so files to -devel subpackage
+- tweak xinetd config files (bugs #11833, #11835, #11836, #11840)
+- fix package descriptions again
+
+* Wed May 24 2000 Nalin Dahyabhai <nalin@redhat.com>
+- change a LINE_MAX to 1024, fix from Ken Raeburn
+- add fix for login vulnerability in case anyone rebuilds without krb4 compat
+- add tweaks for byte-swapping macros in krb.h, also from Ken
+- add xinetd config files
+- make rsh and rlogin quieter
+- build with debug to fix credential forwarding
+- add rsh as a build-time req because the configure scripts look for it to
+  determine paths
+
+* Wed May 17 2000 Nalin Dahyabhai <nalin@redhat.com>
+- fix config_subpackage logic
+
+* Tue May 16 2000 Nalin Dahyabhai <nalin@redhat.com>
+- remove setuid bit on v4rcp and ksu in case the checks previously added
+  don't close all of the problems in ksu
+- apply patches from Jeffrey Schiller to fix overruns Chris Evans found
+- reintroduce configs subpackage for use in the errata
+- add PreReq: sh-utils
+
+* Mon May 15 2000 Nalin Dahyabhai <nalin@redhat.com>
+- fix double-free in the kdc (patch merged into MIT tree)
+- include convert-config-files script as a documentation file
+
+* Wed May 03 2000 Nalin Dahyabhai <nalin@redhat.com>
+- patch ksu man page because the -C option never works
+- add access() checks and disable debug mode in ksu
+- modify default ksu build arguments to specify more directories in CMD_PATH
+  and to use getusershell()
+
+* Wed May 03 2000 Bill Nottingham <notting@redhat.com>
+- fix configure stuff for ia64
+
+* Mon Apr 10 2000 Nalin Dahyabhai <nalin@redhat.com>
+- add LDCOMBINE=-lc to configure invocation to use libc versioning (bug #10653)
+- change Requires: for/in subpackages to include %%{version}
+
+* Wed Apr 05 2000 Nalin Dahyabhai <nalin@redhat.com>
+- add man pages for kerberos(1), kvno(1), .k5login(5)
+- add kvno to -workstation
+
+* Mon Apr 03 2000 Nalin Dahyabhai <nalin@redhat.com>
+- Merge krb5-configs back into krb5-libs.  The krb5.conf file is marked as
+  a %%config file anyway.
+- Make krb5.conf a noreplace config file.
+
+* Thu Mar 30 2000 Nalin Dahyabhai <nalin@redhat.com>
+- Make klogind pass a clean environment to children, like NetKit's rlogind does.
+
+* Wed Mar 08 2000 Nalin Dahyabhai <nalin@redhat.com>
+- Don't enable the server by default.
+- Compress info pages.
+- Add defaults for the PAM module to krb5.conf
+
+* Mon Mar 06 2000 Nalin Dahyabhai <nalin@redhat.com>
+- Correct copyright: it's exportable now, provided the proper paperwork is
+  filed with the government.
+
+* Fri Mar 03 2000 Nalin Dahyabhai <nalin@redhat.com>
+- apply Mike Friedman's patch to fix format string problems
+- don't strip off argv[0] when invoking regular rsh/rlogin
+
+* Thu Mar 02 2000 Nalin Dahyabhai <nalin@redhat.com>
+- run kadmin.local correctly at startup
+
+* Mon Feb 28 2000 Nalin Dahyabhai <nalin@redhat.com>
+- pass absolute path to kadm5.keytab if/when extracting keys at startup
+
+* Sat Feb 19 2000 Nalin Dahyabhai <nalin@redhat.com>
+- fix info page insertions
+
+* Wed Feb  9 2000 Nalin Dahyabhai <nalin@redhat.com>
+- tweak server init script to automatically extract kadm5 keys if
+  /var/kerberos/krb5kdc/kadm5.keytab doesn't exist yet
+- adjust package descriptions
+
+* Thu Feb  3 2000 Nalin Dahyabhai <nalin@redhat.com>
+- fix for potentially gzipped man pages
+
+* Fri Jan 21 2000 Nalin Dahyabhai <nalin@redhat.com>
+- fix comments in krb5-configs
+
+* Fri Jan  7 2000 Nalin Dahyabhai <nalin@redhat.com>
+- move /usr/kerberos/bin to end of PATH
+
+* Tue Dec 28 1999 Nalin Dahyabhai <nalin@redhat.com>
+- install kadmin header files
+
+* Tue Dec 21 1999 Nalin Dahyabhai <nalin@redhat.com>
+- patch around TIOCGTLC defined on alpha and remove warnings from libpty.h
+- add installation of info docs
+- remove krb4 compat patch because it doesn't fix workstation-side servers
+
+* Mon Dec 20 1999 Nalin Dahyabhai <nalin@redhat.com>
+- remove hesiod dependency at build-time
+
+* Sun Dec 19 1999 Nalin Dahyabhai <nsdahya1@eos.ncsu.edu>
+- rebuild on 1.1.1
+
+* Thu Oct  7 1999 Nalin Dahyabhai <nsdahya1@eos.ncsu.edu>
+- clean up init script for server, verify that it works [jlkatz]
+- clean up rotation script so that rc likes it better
+- add clean stanza
+
+* Mon Oct  4 1999 Nalin Dahyabhai <nsdahya1@eos.ncsu.edu>
+- backed out ncurses and makeshlib patches
+- update for krb5-1.1
+- add KDC rotation to rc.boot, based on ideas from Michael's C version
+
+* Mon Sep 27 1999 Nalin Dahyabhai <nsdahya1@eos.ncsu.edu>
+- added -lncurses to telnet and telnetd makefiles
+
+* Mon Jul  5 1999 Nalin Dahyabhai <nsdahya1@eos.ncsu.edu>
+- added krb5.csh and krb5.sh to /etc/profile.d
+
+* Tue Jun 22 1999 Nalin Dahyabhai <nsdahya1@eos.ncsu.edu>
+- broke out configuration files
+
+* Mon Jun 14 1999 Nalin Dahyabhai <nsdahya1@eos.ncsu.edu>
+- fixed server package so that it works now
+
+* Sat May 15 1999 Nalin Dahyabhai <nsdahya1@eos.ncsu.edu>
+- started changelog (previous package from zedz.net)
+- updated existing 1.0.5 RPM from Eos Linux to krb5 1.0.6
+- added --force to makeinfo commands to skip errors during build