heimdal: Match windows and return KRB5KDC_ERR_CLIENT_REVOKED when the account is...
[metze/heimdal/wip.git] / kdc / kerberos5.c
index 53680d4879cc47ce08becb80cb1bafe15a61cd7b..027f8dff2933cc8d30d757b140ad4c108b9bc3ba 100644 (file)
@@ -35,6 +35,9 @@
 
 #define MAX_TIME ((time_t)((1U << 31) - 1))
 
+#undef __attribute__
+#define __attribute__(X)
+
 void
 _kdc_fix_time(time_t **t)
 {
@@ -116,6 +119,23 @@ is_default_salt_p(const krb5_salt *default_salt, const Key *key)
     return TRUE;
 }
 
+
+krb5_boolean
+_kdc_is_anon_request(const KDC_REQ *req)
+{
+    const KDC_REQ_BODY *b = &req->req_body;
+
+    /*
+     * Versions of Heimdal from 0.9rc1 through 1.50 use bit 14 instead
+     * of 16 for request_anonymous, as indicated in the anonymous draft
+     * prior to version 11. Bit 14 is assigned to S4U2Proxy, but S4U2Proxy
+     * requests are only sent to the TGS and, in any case, would have an
+     * additional ticket present.
+     */
+    return b->kdc_options.request_anonymous ||
+          (b->kdc_options.cname_in_addl_tkt && !b->additional_tickets);
+}
+
 /*
  * return the first appropriate key of `princ' in `ret_key'.  Look for
  * all the etypes in (`etypes', `len'), stopping as soon as we find
@@ -123,11 +143,17 @@ is_default_salt_p(const krb5_salt *default_salt, const Key *key)
  */
 
 krb5_error_code
-_kdc_find_etype(krb5_context context, krb5_boolean use_strongest_session_key,
-               krb5_boolean is_preauth, hdb_entry_ex *princ,
+_kdc_find_etype(astgs_request_t r, uint32_t flags,
                krb5_enctype *etypes, unsigned len,
-               krb5_enctype *ret_enctype, Key **ret_key)
+               krb5_enctype *ret_enctype, Key **ret_key,
+               krb5_boolean *ret_default_salt)
 {
+    krb5_context context = r->context;
+    krb5_boolean use_strongest_session_key;
+    krb5_boolean is_preauth = flags & KFE_IS_PREAUTH;
+    krb5_boolean is_tgs = flags & KFE_IS_TGS;
+    hdb_entry_ex *princ;
+    krb5_principal request_princ;
     krb5_error_code ret;
     krb5_salt def_salt;
     krb5_enctype enctype = (krb5_enctype)ETYPE_NULL;
@@ -135,8 +161,21 @@ _kdc_find_etype(krb5_context context, krb5_boolean use_strongest_session_key,
     Key *key = NULL;
     int i, k;
 
+    if (flags & KFE_USE_CLIENT) {
+       princ = r->client;
+       request_princ = r->client_princ;
+    } else {
+       princ = r->server;
+       request_princ = r->server->entry.principal;
+    }
+
+    use_strongest_session_key =
+       is_preauth ? r->config->preauth_use_strongest_session_key
+            : (is_tgs ? r->config->tgt_use_strongest_session_key :
+                       r->config->svc_use_strongest_session_key);
+
     /* We'll want to avoid keys with v4 salted keys in the pre-auth case... */
-    ret = krb5_get_pw_salt(context, princ->entry.principal, &def_salt);
+    ret = krb5_get_pw_salt(context, request_princ, &def_salt);
     if (ret)
        return ret;
 
@@ -239,6 +278,8 @@ _kdc_find_etype(krb5_context context, krb5_boolean use_strongest_session_key,
            *ret_enctype = enctype;
        if (ret_key != NULL)
            *ret_key = key;
+       if (ret_default_salt != NULL)
+           *ret_default_salt = is_default_salt_p(&def_salt, key);
     }
 
     krb5_free_salt (context, def_salt);
@@ -248,22 +289,35 @@ _kdc_find_etype(krb5_context context, krb5_boolean use_strongest_session_key,
 krb5_error_code
 _kdc_make_anonymous_principalname (PrincipalName *pn)
 {
-    pn->name_type = KRB5_NT_PRINCIPAL;
-    pn->name_string.len = 1;
-    pn->name_string.val = malloc(sizeof(*pn->name_string.val));
+    pn->name_type = KRB5_NT_WELLKNOWN;
+    pn->name_string.len = 2;
+    pn->name_string.val = calloc(2, sizeof(*pn->name_string.val));
     if (pn->name_string.val == NULL)
-       return ENOMEM;
-    pn->name_string.val[0] = strdup("anonymous");
-    if (pn->name_string.val[0] == NULL) {
-       free(pn->name_string.val);
-       pn->name_string.val = NULL;
-       return ENOMEM;
-    }
+       goto failed;
+
+    pn->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
+    if (pn->name_string.val[0] == NULL)
+       goto failed;
+
+    pn->name_string.val[1] = strdup(KRB5_ANON_NAME);
+    if (pn->name_string.val[1] == NULL)
+       goto failed;
+
     return 0;
+
+failed:
+    free_PrincipalName(pn);
+
+    pn->name_type = KRB5_NT_UNKNOWN;
+    pn->name_string.len = 0;
+    pn->name_string.val = NULL;
+
+    return ENOMEM;
 }
 
 static void
-_kdc_r_log(kdc_request_t r, int level, const char *fmt, ...)
+_kdc_r_log(astgs_request_t r, int level, const char *fmt, ...)
+       __attribute__ ((__format__ (__printf__, 3, 4)))
 {
     va_list ap;
     char *s;
@@ -273,23 +327,55 @@ _kdc_r_log(kdc_request_t r, int level, const char *fmt, ...)
     va_end(ap);
 }
 
-static void
-_kdc_set_e_text(kdc_request_t r, const char *e_text)
+void
+_kdc_set_e_text(astgs_request_t r, char *fmt, ...)
+       __attribute__ ((__format__ (__printf__, 2, 3)))
 {
+    va_list ap;
+    char *e_text;
+
+    va_start(ap, fmt);
+    vasprintf(&e_text, fmt, ap);
+    va_end(ap);
+
+    if (!e_text)
+       /* not much else to do... */
+       return;
+
+    /* We should never see this */
+    if (r->e_text) {
+       kdc_log(r->context, r->config, 1, "trying to replace e-text: %s\n",
+               e_text);
+       free(e_text);
+       return;
+    }
+
     r->e_text = e_text;
-    kdc_log(r->context, r->config, 0, "%s", e_text);
+    r->e_text_buf = e_text;
+    kdc_log(r->context, r->config, 4, "%s", e_text);
 }
 
 void
-_kdc_log_timestamp(krb5_context context,
-                  krb5_kdc_configuration *config,
-                  const char *type,
+_kdc_log_timestamp(astgs_request_t r, const char *type,
                   KerberosTime authtime, KerberosTime *starttime,
                   KerberosTime endtime, KerberosTime *renew_till)
 {
+    krb5_context context = r->context;
+    krb5_kdc_configuration *config = r->config;
     char authtime_str[100], starttime_str[100],
        endtime_str[100], renewtime_str[100];
 
+    if (authtime)
+       _kdc_audit_addkv((kdc_request_t)r, 0, "auth", "%ld", (long)authtime);
+    if (starttime && *starttime)
+       _kdc_audit_addkv((kdc_request_t)r, 0, "start", "%ld",
+                        (long)*starttime);
+    if (endtime)
+       _kdc_audit_addkv((kdc_request_t)r, 0, "end", "%ld", (long)endtime);
+    if (renew_till && *renew_till)
+       _kdc_audit_addkv((kdc_request_t)r, 0, "renew", "%ld",
+                        (long)*renew_till);
+
     krb5_format_time(context, authtime,
                     authtime_str, sizeof(authtime_str), TRUE);
     if (starttime)
@@ -305,7 +391,7 @@ _kdc_log_timestamp(krb5_context context,
     else
        strlcpy(renewtime_str, "unset", sizeof(renewtime_str));
 
-    kdc_log(context, config, 5,
+    kdc_log(context, config, 4,
            "%s authtime: %s starttime: %s endtime: %s renew till: %s",
            type, authtime_str, starttime_str, endtime_str, renewtime_str);
 }
@@ -317,46 +403,40 @@ _kdc_log_timestamp(krb5_context context,
 #ifdef PKINIT
 
 static krb5_error_code
-pa_pkinit_validate(kdc_request_t r, const PA_DATA *pa)
+pa_pkinit_validate(astgs_request_t r, const PA_DATA *pa)
 {
     pk_client_params *pkp = NULL;
     char *client_cert = NULL;
     krb5_error_code ret;
 
-    ret = _kdc_pk_rd_padata(r->context, r->config, &r->req, pa, r->client, &pkp);
+    ret = _kdc_pk_rd_padata(r, pa, &pkp);
     if (ret || pkp == NULL) {
        ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
-       _kdc_r_log(r, 5, "Failed to decode PKINIT PA-DATA -- %s",
-                  r->client_name);
+       _kdc_r_log(r, 4, "Failed to decode PKINIT PA-DATA -- %s",
+                  r->cname);
        goto out;
     }
     
-    ret = _kdc_pk_check_client(r->context,
-                              r->config,
-                              r->clientdb, 
-                              r->client,
-                              pkp,
-                              &client_cert);
+    ret = _kdc_pk_check_client(r, pkp, &client_cert);
     if (ret) {
        _kdc_set_e_text(r, "PKINIT certificate not allowed to "
                        "impersonate principal");
        goto out;
     }
 
-    _kdc_r_log(r, 0, "PKINIT pre-authentication succeeded -- %s using %s",
-              r->client_name, client_cert);
+    _kdc_r_log(r, 4, "PKINIT pre-authentication succeeded -- %s using %s",
+              r->cname, client_cert);
     free(client_cert);
 
-    ret = _kdc_pk_mk_pa_reply(r->context, r->config, pkp, r->client,
-                             r->sessionetype, &r->req, &r->request,
-                             &r->reply_key, &r->session_key, &r->outpadata);
+    ret = _kdc_pk_mk_pa_reply(r, pkp);
     if (ret) {
        _kdc_set_e_text(r, "Failed to build PK-INIT reply");
        goto out;
     }
+    r->reply_kvno = 0;
 #if 0
-    ret = _kdc_add_inital_verified_cas(r->context, r->config,
-                                      pkp, &r->et);
+    ret = _kdc_add_initial_verified_cas(r->context, r->config,
+                                       pkp, &r->et);
 #endif
  out:
     if (pkp)
@@ -372,9 +452,10 @@ pa_pkinit_validate(kdc_request_t r, const PA_DATA *pa)
  */
 
 static krb5_error_code
-make_pa_enc_challange(krb5_context context, METHOD_DATA *md,
-                     krb5_crypto crypto)
+make_pa_enc_challange(astgs_request_t r, krb5_crypto crypto)
 {
+    krb5_context context = r->context;
+    METHOD_DATA *md = &r->outpadata;
     PA_ENC_TS_ENC p;
     unsigned char *buf;
     size_t buf_size;
@@ -419,10 +500,9 @@ make_pa_enc_challange(krb5_context context, METHOD_DATA *md,
 }
 
 static krb5_error_code
-pa_enc_chal_validate(kdc_request_t r, const PA_DATA *pa)
+pa_enc_chal_validate(astgs_request_t r, const PA_DATA *pa)
 {
     krb5_data pepper1, pepper2, ts_data;
-    KDC_REQ_BODY *b = &r->req.req_body;
     int invalidPassword = 0;
     EncryptedData enc_data;
     krb5_enctype aenctype;
@@ -433,20 +513,28 @@ pa_enc_chal_validate(kdc_request_t r, const PA_DATA *pa)
 
     heim_assert(r->armor_crypto != NULL, "ENC-CHAL called for non FAST");
     
-    if (_kdc_is_anon_request(b)) {
+    if (_kdc_is_anon_request(&r->req)) {
        ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
-       kdc_log(r->context, r->config, 0, "ENC-CHALL doesn't support anon");
+       kdc_log(r->context, r->config, 4, "ENC-CHALL doesn't support anon");
        return ret;
     }
 
+    if (r->client->entry.flags.locked_out) {
+       ret = KRB5KDC_ERR_CLIENT_REVOKED;
+       kdc_log(r->context, r->config, 0,
+               "Client (%s) is locked out", r->client_name);
+       return ret;
+    }
+
+
     ret = decode_EncryptedData(pa->padata_value.data,
                               pa->padata_value.length,
                               &enc_data,
                               &size);
     if (ret) {
        ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
-       _kdc_r_log(r, 5, "Failed to decode PA-DATA -- %s",
-                  r->client_name);
+       _kdc_r_log(r, 4, "Failed to decode PA-DATA -- %s",
+                  r->cname);
        return ret;
     }
 
@@ -494,9 +582,9 @@ pa_enc_chal_validate(kdc_request_t r, const PA_DATA *pa)
            ret2 = krb5_enctype_to_string(r->context, k->key.keytype, &str);
            if (ret2)
                str = NULL;
-           _kdc_r_log(r, 5, "Failed to decrypt ENC-CHAL -- %s "
+           _kdc_r_log(r, 4, "Failed to decrypt ENC-CHAL -- %s "
                       "(enctype %s) error %s",
-                      r->client_name, str ? str : "unknown enctype", msg);
+                      r->cname, str ? str : "unknown enctype", msg);
            krb5_free_error_message(r->context, msg);
            free(str);
 
@@ -511,8 +599,8 @@ pa_enc_chal_validate(kdc_request_t r, const PA_DATA *pa)
        if(ret){
            krb5_crypto_destroy(r->context, challangecrypto);
            ret = KRB5KDC_ERR_PREAUTH_FAILED;
-           _kdc_r_log(r, 5, "Failed to decode PA-ENC-TS_ENC -- %s",
-                      r->client_name);
+           _kdc_r_log(r, 4, "Failed to decode PA-ENC-TS_ENC -- %s",
+                      r->cname);
            continue;
        }
 
@@ -525,12 +613,12 @@ pa_enc_chal_validate(kdc_request_t r, const PA_DATA *pa)
                             client_time, sizeof(client_time), TRUE);
 
            ret = KRB5KRB_AP_ERR_SKEW;
-           _kdc_r_log(r, 0, "Too large time skew, "
+           _kdc_r_log(r, 4, "Too large time skew, "
                       "client time %s is out by %u > %u seconds -- %s",
                       client_time,
                       (unsigned)labs(kdc_time - p.patimestamp),
                       r->context->max_skew,
-                      r->client_name);
+                      r->cname);
 
            free_PA_ENC_TS_ENC(&p);
            goto out;
@@ -538,8 +626,7 @@ pa_enc_chal_validate(kdc_request_t r, const PA_DATA *pa)
 
        free_PA_ENC_TS_ENC(&p);
 
-       ret = make_pa_enc_challange(r->context, &r->outpadata,
-                                   challangecrypto);
+       ret = make_pa_enc_challange(r, challangecrypto);
        krb5_crypto_destroy(r->context, challangecrypto);
        if (ret)
            goto out;
@@ -550,6 +637,8 @@ pa_enc_chal_validate(kdc_request_t r, const PA_DATA *pa)
        if (ret)
            goto out;
 
+       r->reply_kvno = 0;
+
        /*
         * Success
         */
@@ -571,7 +660,7 @@ pa_enc_chal_validate(kdc_request_t r, const PA_DATA *pa)
 }
 
 static krb5_error_code
-pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa)
+pa_enc_ts_validate(astgs_request_t r, const PA_DATA *pa)
 {
     EncryptedData enc_data;
     krb5_error_code ret;
@@ -581,11 +670,12 @@ pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa)
     size_t len;
     Key *pa_key;
     char *str;
-       
-    if (_kdc_is_anon_request(&r->req.req_body)) {
-       ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
-       _kdc_set_e_text(r, "ENC-TS doesn't support anon");
-       goto out;
+
+    if (r->client->entry.flags.locked_out) {
+       ret = KRB5KDC_ERR_CLIENT_REVOKED;
+       kdc_log(r->context, r->config, 0,
+               "Client (%s) is locked out", r->client_name);
+       return ret;
     }
 
     ret = decode_EncryptedData(pa->padata_value.data,
@@ -594,8 +684,8 @@ pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa)
                               &len);
     if (ret) {
        ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
-       _kdc_r_log(r, 5, "Failed to decode PA-DATA -- %s",
-                  r->client_name);
+       _kdc_r_log(r, 4, "Failed to decode PA-DATA -- %s",
+                  r->cname);
        goto out;
     }
        
@@ -608,13 +698,13 @@ pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa)
        if(krb5_enctype_to_string(r->context, enc_data.etype, &estr))
            estr = NULL;
        if(estr == NULL)
-           _kdc_r_log(r, 5,
+           _kdc_r_log(r, 4,
                       "No client key matching pa-data (%d) -- %s",
-                      enc_data.etype, r->client_name);
+                      enc_data.etype, r->cname);
        else
-           _kdc_r_log(r, 5,
+           _kdc_r_log(r, 4,
                       "No client key matching pa-data (%s) -- %s",
-                      estr, r->client_name);
+                      estr, r->cname);
        free(estr);
        free_EncryptedData(&enc_data);
        goto out;
@@ -624,7 +714,7 @@ pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa)
     ret = krb5_crypto_init(r->context, &pa_key->key, 0, &crypto);
     if (ret) {
        const char *msg = krb5_get_error_message(r->context, ret);
-       _kdc_r_log(r, 0, "krb5_crypto_init failed: %s", msg);
+       _kdc_r_log(r, 4, "krb5_crypto_init failed: %s", msg);
        krb5_free_error_message(r->context, msg);
        free_EncryptedData(&enc_data);
        goto out;
@@ -649,9 +739,9 @@ pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa)
                                      pa_key->key.keytype, &str);
        if (ret2)
            str = NULL;
-       _kdc_r_log(r, 5, "Failed to decrypt PA-DATA -- %s "
+       _kdc_r_log(r, 4, "Failed to decrypt PA-DATA -- %s "
                   "(enctype %s) error %s",
-                  r->client_name, str ? str : "unknown enctype", msg);
+                  r->cname, str ? str : "unknown enctype", msg);
        krb5_free_error_message(r->context, msg);
        free(str);
 
@@ -676,8 +766,8 @@ pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa)
     krb5_data_free(&ts_data);
     if(ret){
        ret = KRB5KDC_ERR_PREAUTH_FAILED;
-       _kdc_r_log(r, 5, "Failed to decode PA-ENC-TS_ENC -- %s",
-                  r->client_name);
+       _kdc_r_log(r, 4, "Failed to decode PA-ENC-TS_ENC -- %s",
+                  r->cname);
        goto out;
     }
     if (labs(kdc_time - p.patimestamp) > r->context->max_skew) {
@@ -687,12 +777,12 @@ pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa)
                         client_time, sizeof(client_time), TRUE);
 
        ret = KRB5KRB_AP_ERR_SKEW;
-       _kdc_r_log(r, 0, "Too large time skew, "
+       _kdc_r_log(r, 4, "Too large time skew, "
                   "client time %s is out by %u > %u seconds -- %s",
                   client_time,
                   (unsigned)labs(kdc_time - p.patimestamp),
                   r->context->max_skew,
-                  r->client_name);
+                  r->cname);
 
        /*
         * The following is needed to make windows clients to
@@ -711,11 +801,19 @@ pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa)
     if (ret)
        return ret;
 
+    if (pa_key->mkvno != NULL) {
+       r->reply_kvno = *pa_key->mkvno;
+    } else {
+       r->reply_kvno = r->client->entry.kvno;
+    }
+
     ret = krb5_enctype_to_string(r->context, pa_key->key.keytype, &str);
     if (ret)
        str = NULL;
-    _kdc_r_log(r, 2, "ENC-TS Pre-authentication succeeded -- %s using %s",
-              r->client_name, str ? str : "unknown enctype");
+    _kdc_r_log(r, 4, "ENC-TS Pre-authentication succeeded -- %s using %s",
+              r->cname, str ? str : "unknown enctype");
+    _kdc_audit_addkv((kdc_request_t)r, 0, "pa-etype", "%d",
+                    (int)pa_key->key.keytype);
     free(str);
 
     ret = 0;
@@ -731,7 +829,7 @@ struct kdc_patypes {
     unsigned int flags;
 #define PA_ANNOUNCE    1
 #define PA_REQ_FAST    2 /* only use inside fast */
-    krb5_error_code (*validate)(kdc_request_t, const PA_DATA *pa);
+    krb5_error_code (*validate)(astgs_request_t, const PA_DATA *pa);
 };
 
 static const struct kdc_patypes pat[] = {
@@ -771,10 +869,10 @@ static const struct kdc_patypes pat[] = {
 };
 
 static void
-log_patypes(krb5_context context,
-           krb5_kdc_configuration *config,
-           METHOD_DATA *padata)
+log_patypes(astgs_request_t r, METHOD_DATA *padata)
 {
+    krb5_context context = r->context;
+    krb5_kdc_configuration *config = r->config;
     struct rk_strpool *p = NULL;
     char *str;
     size_t n, m;
@@ -791,7 +889,7 @@ log_patypes(krb5_context context,
        if (p && n + 1 < padata->len)
            p = rk_strpoolprintf(p, ", ");
        if (p == NULL) {
-           kdc_log(context, config, 0, "out of memory");
+           kdc_log(context, config, 1, "out of memory");
            return;
        }
     }
@@ -799,7 +897,9 @@ log_patypes(krb5_context context,
        p = rk_strpoolprintf(p, "none");
 
     str = rk_strpoolcollect(p);
-    kdc_log(context, config, 0, "Client sent patypes: %s", str);
+    kdc_log(context, config, 4, "Client sent patypes: %s", str);
+    _kdc_audit_addkv((kdc_request_t)r, KDC_AUDIT_EATWHITE,
+                    "client-pa", "%s", str);
     free(str);
 }
 
@@ -828,7 +928,7 @@ _kdc_encode_reply(krb5_context context,
     ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size, et, &len, ret);
     if(ret) {
        const char *msg = krb5_get_error_message(context, ret);
-       kdc_log(context, config, 0, "Failed to encode ticket: %s", msg);
+       kdc_log(context, config, 4, "Failed to encode ticket: %s", msg);
        krb5_free_error_message(context, msg);
        return ret;
     }
@@ -838,8 +938,9 @@ _kdc_encode_reply(krb5_context context,
     ret = krb5_crypto_init(context, skey, etype, &crypto);
     if (ret) {
         const char *msg = krb5_get_error_message(context, ret);
-       kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
+       kdc_log(context, config, 4, "krb5_crypto_init failed: %s", msg);
        krb5_free_error_message(context, msg);
+       free(buf);
        return ret;
     }
 
@@ -854,7 +955,7 @@ _kdc_encode_reply(krb5_context context,
     krb5_crypto_destroy(context, crypto);
     if(ret) {
        const char *msg = krb5_get_error_message(context, ret);
-       kdc_log(context, config, 0, "Failed to encrypt data: %s", msg);
+       kdc_log(context, config, 4, "Failed to encrypt data: %s", msg);
        krb5_free_error_message(context, msg);
        return ret;
     }
@@ -864,7 +965,7 @@ _kdc_encode_reply(krb5_context context,
        krb5_keyblock *strengthen_key = NULL;
        KrbFastFinished finished;
 
-       kdc_log(context, config, 0, "FAST armor protection");
+       kdc_log(context, config, 4, "FAST armor protection");
 
        memset(&finished, 0, sizeof(finished));
        krb5_data_zero(&data);
@@ -928,13 +1029,13 @@ _kdc_encode_reply(krb5_context context,
        ASN1_MALLOC_ENCODE(EncTGSRepPart, buf, buf_size, ek, &len, ret);
     if(ret) {
        const char *msg = krb5_get_error_message(context, ret);
-       kdc_log(context, config, 0, "Failed to encode KDC-REP: %s", msg);
+       kdc_log(context, config, 4, "Failed to encode KDC-REP: %s", msg);
        krb5_free_error_message(context, msg);
        return ret;
     }
     if(buf_size != len) {
        free(buf);
-       kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
+       kdc_log(context, config, 4, "Internal error in ASN.1 encoder");
        *e_text = "KDC internal error";
        return KRB5KRB_ERR_GENERIC;
     }
@@ -942,7 +1043,7 @@ _kdc_encode_reply(krb5_context context,
     if (ret) {
        const char *msg = krb5_get_error_message(context, ret);
        free(buf);
-       kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
+       kdc_log(context, config, 4, "krb5_crypto_init failed: %s", msg);
        krb5_free_error_message(context, msg);
        return ret;
     }
@@ -970,13 +1071,13 @@ _kdc_encode_reply(krb5_context context,
     krb5_crypto_destroy(context, crypto);
     if(ret) {
        const char *msg = krb5_get_error_message(context, ret);
-       kdc_log(context, config, 0, "Failed to encode KDC-REP: %s", msg);
+       kdc_log(context, config, 4, "Failed to encode KDC-REP: %s", msg);
        krb5_free_error_message(context, msg);
        return ret;
     }
     if(buf_size != len) {
        free(buf);
-       kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
+       kdc_log(context, config, 4, "Internal error in ASN.1 encoder");
        *e_text = "KDC internal error";
        return KRB5KRB_ERR_GENERIC;
     }
@@ -1018,10 +1119,13 @@ older_enctype(krb5_enctype enctype)
  */
 
 static krb5_error_code
-make_etype_info_entry(krb5_context context, ETYPE_INFO_ENTRY *ent, Key *key)
+make_etype_info_entry(krb5_context context,
+                     ETYPE_INFO_ENTRY *ent,
+                     Key *key,
+                     krb5_boolean include_salt)
 {
     ent->etype = key->key.keytype;
-    if(key->salt){
+    if (key->salt && include_salt){
 #if 0
        ALLOC(ent->salttype);
 
@@ -1030,7 +1134,7 @@ make_etype_info_entry(krb5_context context, ETYPE_INFO_ENTRY *ent, Key *key)
        else if(key->salt->type == hdb_afs3_salt)
            *ent->salttype = 2;
        else {
-           kdc_log(context, config, 0, "unknown salt-type: %d",
+           kdc_log(context, config, 4, "unknown salt-type: %d",
                    key->salt->type);
            return KRB5KRB_ERR_GENERIC;
        }
@@ -1068,7 +1172,8 @@ make_etype_info_entry(krb5_context context, ETYPE_INFO_ENTRY *ent, Key *key)
 static krb5_error_code
 get_pa_etype_info(krb5_context context,
                  krb5_kdc_configuration *config,
-                 METHOD_DATA *md, Key *ckey)
+                 METHOD_DATA *md, Key *ckey,
+                 krb5_boolean include_salt)
 {
     krb5_error_code ret = 0;
     ETYPE_INFO pa;
@@ -1081,7 +1186,7 @@ get_pa_etype_info(krb5_context context,
     if(pa.val == NULL)
        return ENOMEM;
 
-    ret = make_etype_info_entry(context, &pa.val[0], ckey);
+    ret = make_etype_info_entry(context, &pa.val[0], ckey, include_salt);
     if (ret) {
        free_ETYPE_INFO(&pa);
        return ret;
@@ -1106,13 +1211,37 @@ get_pa_etype_info(krb5_context context,
  *
  */
 
-extern int _krb5_AES_string_to_default_iterator;
+extern int _krb5_AES_SHA1_string_to_default_iterator;
+extern int _krb5_AES_SHA2_string_to_default_iterator;
 
 static krb5_error_code
-make_etype_info2_entry(ETYPE_INFO2_ENTRY *ent, Key *key)
+make_s2kparams(int value, size_t len, krb5_data **ps2kparams)
 {
+    krb5_data *s2kparams;
+    krb5_error_code ret;
+
+    ALLOC(s2kparams);
+    if (s2kparams == NULL)
+       return ENOMEM;
+    ret = krb5_data_alloc(s2kparams, len);
+    if (ret) {
+       free(s2kparams);
+       return ret;
+    }
+    _krb5_put_int(s2kparams->data, value, len);
+    *ps2kparams = s2kparams;
+    return 0;
+}
+
+static krb5_error_code
+make_etype_info2_entry(ETYPE_INFO2_ENTRY *ent,
+                      Key *key,
+                      krb5_boolean include_salt)
+{
+    krb5_error_code ret;
+
     ent->etype = key->key.keytype;
-    if(key->salt) {
+    if (key->salt && include_salt) {
        ALLOC(ent->salt);
        if (ent->salt == NULL)
            return ENOMEM;
@@ -1132,44 +1261,28 @@ make_etype_info2_entry(ETYPE_INFO2_ENTRY *ent, Key *key)
     switch (key->key.keytype) {
     case ETYPE_AES128_CTS_HMAC_SHA1_96:
     case ETYPE_AES256_CTS_HMAC_SHA1_96:
-       ALLOC(ent->s2kparams);
-       if (ent->s2kparams == NULL)
-           return ENOMEM;
-       ent->s2kparams->length = 4;
-       ent->s2kparams->data = malloc(ent->s2kparams->length);
-       if (ent->s2kparams->data == NULL) {
-           free(ent->s2kparams);
-           ent->s2kparams = NULL;
-           return ENOMEM;
-       }
-       _krb5_put_int(ent->s2kparams->data,
-                     _krb5_AES_string_to_default_iterator,
-                     ent->s2kparams->length);
+       ret = make_s2kparams(_krb5_AES_SHA1_string_to_default_iterator,
+                            4, &ent->s2kparams);
+       break;
+    case KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128:
+    case KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192:
+       ret = make_s2kparams(_krb5_AES_SHA2_string_to_default_iterator,
+                            4, &ent->s2kparams);
        break;
     case ETYPE_DES_CBC_CRC:
     case ETYPE_DES_CBC_MD4:
     case ETYPE_DES_CBC_MD5:
        /* Check if this was a AFS3 salted key */
-       if(key->salt && key->salt->type == hdb_afs3_salt){
-           ALLOC(ent->s2kparams);
-           if (ent->s2kparams == NULL)
-               return ENOMEM;
-           ent->s2kparams->length = 1;
-           ent->s2kparams->data = malloc(ent->s2kparams->length);
-           if (ent->s2kparams->data == NULL) {
-               free(ent->s2kparams);
-               ent->s2kparams = NULL;
-               return ENOMEM;
-           }
-           _krb5_put_int(ent->s2kparams->data,
-                         1,
-                         ent->s2kparams->length);
-       }
+       if(key->salt && key->salt->type == hdb_afs3_salt)
+           ret = make_s2kparams(1, 1, &ent->s2kparams);
+       else
+           ret = 0;
        break;
     default:
+       ret = 0;
        break;
     }
-    return 0;
+    return ret;
 }
 
 /*
@@ -1181,7 +1294,8 @@ make_etype_info2_entry(ETYPE_INFO2_ENTRY *ent, Key *key)
 static krb5_error_code
 get_pa_etype_info2(krb5_context context,
                   krb5_kdc_configuration *config,
-                  METHOD_DATA *md, Key *ckey)
+                  METHOD_DATA *md, Key *ckey,
+                  krb5_boolean include_salt)
 {
     krb5_error_code ret = 0;
     ETYPE_INFO2 pa;
@@ -1193,7 +1307,7 @@ get_pa_etype_info2(krb5_context context,
     if(pa.val == NULL)
        return ENOMEM;
 
-    ret = make_etype_info2_entry(&pa.val[0], ckey);
+    ret = make_etype_info2_entry(&pa.val[0], ckey, include_salt);
     if (ret) {
        free_ETYPE_INFO2(&pa);
        return ret;
@@ -1214,22 +1328,84 @@ get_pa_etype_info2(krb5_context context,
     return 0;
 }
 
+static int
+newer_enctype_present(struct KDC_REQ_BODY_etype *etype_list)
+{
+    size_t i;
+
+    for (i = 0; i < etype_list->len; i++) {
+       if (!older_enctype(etype_list->val[i]))
+           return 1;
+    }
+    return 0;
+}
+
+static krb5_error_code
+get_pa_etype_info_both(krb5_context context,
+                      krb5_kdc_configuration *config,
+                      struct KDC_REQ_BODY_etype *etype_list,
+                      METHOD_DATA *md, Key *ckey,
+                      krb5_boolean include_salt)
+{
+    krb5_error_code ret;
+
+    /*
+     * RFC4120 requires:
+     *   When the AS server is to include pre-authentication data in a
+     *   KRB-ERROR or in an AS-REP, it MUST use PA-ETYPE-INFO2, not
+     *   PA-ETYPE-INFO, if the etype field of the client's AS-REQ lists
+     *   at least one "newer" encryption type.  Otherwise (when the etype
+     *   field of the client's AS-REQ does not list any "newer" encryption
+     *   types), it MUST send both PA-ETYPE-INFO2 and PA-ETYPE-INFO (both
+     *   with an entry for each enctype).  A "newer" enctype is any enctype
+     *   first officially specified concurrently with or subsequent to the
+     *   issue of this RFC.  The enctypes DES, 3DES, or RC4 and any defined
+     *   in [RFC1510] are not "newer" enctypes.
+     *
+     * It goes on to state:
+     *   The preferred ordering of the "hint" pre-authentication data that
+     *   affect client key selection is: ETYPE-INFO2, followed by ETYPE-INFO,
+     *   followed by PW-SALT.  As noted in Section 3.1.3, a KDC MUST NOT send
+     *   ETYPE-INFO or PW-SALT when the client's AS-REQ includes at least one
+     *   "newer" etype.
+     */
+
+    ret = get_pa_etype_info2(context, config, md, ckey, include_salt);
+    if (ret)
+       return ret;
+
+    if (!newer_enctype_present(etype_list))
+       ret = get_pa_etype_info(context, config, md, ckey, include_salt);
+
+    return ret;
+}
+
 /*
  *
  */
 
-static void
-log_as_req(krb5_context context,
-          krb5_kdc_configuration *config,
-          krb5_enctype cetype,
-          krb5_enctype setype,
-          const KDC_REQ_BODY *b)
+void
+_log_astgs_req(astgs_request_t r, krb5_enctype setype)
 {
+    krb5_context context = r->context;
+    const KDC_REQ_BODY *b = &r->req.req_body;
+    krb5_enctype cetype = r->reply_key.keytype;
     krb5_error_code ret;
     struct rk_strpool *p;
+    struct rk_strpool *s = NULL;
     char *str;
+    char *cet;
+    char *set;
     size_t i;
 
+    /*
+     * we are collecting ``p'' and ``s''.  The former is a textual
+     * representation of the enctypes as strings which will be used
+     * for debugging.  The latter is a terse comma separated list of
+     * the %d's of the enctypes to emit into our audit trail to
+     * conserve space in the logs.
+     */
+
     p = rk_strpoolprintf(NULL, "%s", "Client supported enctypes: ");
 
     for (i = 0; i < b->etype.len; i++) {
@@ -1239,44 +1415,56 @@ log_as_req(krb5_context context,
            free(str);
        } else
            p = rk_strpoolprintf(p, "%d", b->etype.val[i]);
-       if (p && i + 1 < b->etype.len)
-           p = rk_strpoolprintf(p, ", ");
        if (p == NULL) {
-           kdc_log(context, config, 0, "out of memory");
+           rk_strpoolfree(s);
+           _kdc_r_log(r, 4, "out of memory");
            return;
        }
+       s = rk_strpoolprintf(s, "%d", b->etype.val[i]);
+       if (i + 1 < b->etype.len) {
+           p = rk_strpoolprintf(p, ", ");
+           s = rk_strpoolprintf(s, ",");
+       }
     }
     if (p == NULL)
        p = rk_strpoolprintf(p, "no encryption types");
 
-    {
-       char *cet;
-       char *set;
-
-       ret = krb5_enctype_to_string(context, cetype, &cet);
-       if(ret == 0) {
-           ret = krb5_enctype_to_string(context, setype, &set);
-           if (ret == 0) {
-               p = rk_strpoolprintf(p, ", using %s/%s", cet, set);
-               free(set);
-           }
-           free(cet);
+    str = rk_strpoolcollect(s);
+    if (str)
+        _kdc_audit_addkv((kdc_request_t)r, KDC_AUDIT_EATWHITE, "etypes", "%s",
+                         str);
+    free(str);
+
+    ret = krb5_enctype_to_string(context, cetype, &cet);
+    if(ret == 0) {
+       ret = krb5_enctype_to_string(context, setype, &set);
+       if (ret == 0) {
+           p = rk_strpoolprintf(p, ", using %s/%s", cet, set);
+           free(set);
        }
-       if (ret != 0)
-           p = rk_strpoolprintf(p, ", using enctypes %d/%d",
-                                cetype, setype);
+       free(cet);
     }
+    if (ret != 0)
+       p = rk_strpoolprintf(p, ", using enctypes %d/%d",
+                            cetype, setype);
 
     str = rk_strpoolcollect(p);
-    kdc_log(context, config, 0, "%s", str);
+    if (str)
+       _kdc_r_log(r, 4, "%s", str);
     free(str);
 
+    _kdc_audit_addkv((kdc_request_t)r, 0, "etype", "%d/%d", cetype, setype);
+
     {
        char fixedstr[128];
+
        unparse_flags(KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(),
                      fixedstr, sizeof(fixedstr));
-       if(*fixedstr)
-           kdc_log(context, config, 0, "Requested flags: %s", fixedstr);
+       if (*fixedstr) {
+           _kdc_r_log(r, 4, "Requested flags: %s", fixedstr);
+           _kdc_audit_addkv((kdc_request_t)r, KDC_AUDIT_EATWHITE,
+                            "flags", "%s", fixedstr);
+       }
     }
 }
 
@@ -1287,31 +1475,30 @@ log_as_req(krb5_context context,
  */
 
 krb5_error_code
-kdc_check_flags(krb5_context context,
-               krb5_kdc_configuration *config,
-               hdb_entry_ex *client_ex, const char *client_name,
-               hdb_entry_ex *server_ex, const char *server_name,
-               krb5_boolean is_as_req)
+kdc_check_flags(astgs_request_t r, krb5_boolean is_as_req)
 {
+    krb5_context context = r->context;
+    hdb_entry_ex *client_ex = r->client;
+    hdb_entry_ex *server_ex = r->server;
+
     if(client_ex != NULL) {
        hdb_entry *client = &client_ex->entry;
 
        /* check client */
        if (client->flags.locked_out) {
-           kdc_log(context, config, 0,
-                   "Client (%s) is locked out", client_name);
-           return KRB5KDC_ERR_POLICY;
+           _kdc_audit_addreason((kdc_request_t)r, "Client is locked out");
+           return KRB5KDC_ERR_CLIENT_REVOKED;
        }
 
        if (client->flags.invalid) {
-           kdc_log(context, config, 0,
-                   "Client (%s) has invalid bit set", client_name);
+           _kdc_audit_addreason((kdc_request_t)r,
+                                 "Client has invalid bit set");
            return KRB5KDC_ERR_POLICY;
        }
 
-       if(!client->flags.client){
-           kdc_log(context, config, 0,
-                   "Principal may not act as client -- %s", client_name);
+       if (!client->flags.client) {
+           _kdc_audit_addreason((kdc_request_t)r,
+                                 "Principal may not act as client");
            return KRB5KDC_ERR_POLICY;
        }
 
@@ -1319,9 +1506,8 @@ kdc_check_flags(krb5_context context,
            char starttime_str[100];
            krb5_format_time(context, *client->valid_start,
                             starttime_str, sizeof(starttime_str), TRUE);
-           kdc_log(context, config, 0,
-                   "Client not yet valid until %s -- %s",
-                   starttime_str, client_name);
+           _kdc_audit_addreason((kdc_request_t)r, "Client not yet valid "
+                                 "until %s", starttime_str);
            return KRB5KDC_ERR_CLIENT_NOTYET;
        }
 
@@ -1329,27 +1515,22 @@ kdc_check_flags(krb5_context context,
            char endtime_str[100];
            krb5_format_time(context, *client->valid_end,
                             endtime_str, sizeof(endtime_str), TRUE);
-           kdc_log(context, config, 0,
-                   "Client expired at %s -- %s",
-                   endtime_str, client_name);
-           return KRB5KDC_ERR_NAME_EXP;
+           _kdc_audit_addreason((kdc_request_t)r, "Client expired at %s",
+                                 endtime_str);
+           return  KRB5KDC_ERR_NAME_EXP;
        }
 
        if (client->flags.require_pwchange &&
-           (server_ex == NULL || !server_ex->entry.flags.change_pw)) {
-           kdc_log(context, config, 0,
-                   "Client's key must be changed -- %s", client_name);
+           (server_ex == NULL || !server_ex->entry.flags.change_pw))
            return KRB5KDC_ERR_KEY_EXPIRED;
-       }
 
        if (client->pw_end && *client->pw_end < kdc_time
            && (server_ex == NULL || !server_ex->entry.flags.change_pw)) {
            char pwend_str[100];
            krb5_format_time(context, *client->pw_end,
                             pwend_str, sizeof(pwend_str), TRUE);
-           kdc_log(context, config, 0,
-                   "Client's key has expired at %s -- %s",
-                   pwend_str, client_name);
+           _kdc_audit_addreason((kdc_request_t)r, "Client's key has expired "
+                                 "at %s", pwend_str);
            return KRB5KDC_ERR_KEY_EXPIRED;
        }
     }
@@ -1360,25 +1541,23 @@ kdc_check_flags(krb5_context context,
        hdb_entry *server = &server_ex->entry;
 
        if (server->flags.locked_out) {
-           kdc_log(context, config, 0,
-                   "Client server locked out -- %s", server_name);
+           _kdc_audit_addreason((kdc_request_t)r, "Server locked out");
            return KRB5KDC_ERR_POLICY;
        }
        if (server->flags.invalid) {
-           kdc_log(context, config, 0,
-                   "Server has invalid flag set -- %s", server_name);
+           _kdc_audit_addreason((kdc_request_t)r,
+                                 "Server has invalid flag set");
            return KRB5KDC_ERR_POLICY;
        }
-
-       if(!server->flags.server){
-           kdc_log(context, config, 0,
-                   "Principal may not act as server -- %s", server_name);
+       if (!server->flags.server) {
+           _kdc_audit_addreason((kdc_request_t)r,
+                                 "Principal may not act as server");
            return KRB5KDC_ERR_POLICY;
        }
 
-       if(!is_as_req && server->flags.initial) {
-           kdc_log(context, config, 0,
-                   "AS-REQ is required for server -- %s", server_name);
+       if (!is_as_req && server->flags.initial) {
+           _kdc_audit_addreason((kdc_request_t)r,
+                                 "AS-REQ is required for server");
            return KRB5KDC_ERR_POLICY;
        }
 
@@ -1386,9 +1565,8 @@ kdc_check_flags(krb5_context context,
            char starttime_str[100];
            krb5_format_time(context, *server->valid_start,
                             starttime_str, sizeof(starttime_str), TRUE);
-           kdc_log(context, config, 0,
-                   "Server not yet valid until %s -- %s",
-                   starttime_str, server_name);
+           _kdc_audit_addreason((kdc_request_t)r, "Server not yet valid "
+                                 "until %s", starttime_str);
            return KRB5KDC_ERR_SERVICE_NOTYET;
        }
 
@@ -1396,9 +1574,8 @@ kdc_check_flags(krb5_context context,
            char endtime_str[100];
            krb5_format_time(context, *server->valid_end,
                             endtime_str, sizeof(endtime_str), TRUE);
-           kdc_log(context, config, 0,
-                   "Server expired at %s -- %s",
-                   endtime_str, server_name);
+           _kdc_audit_addreason((kdc_request_t)r, "Server expired at %s",
+                                 endtime_str);
            return KRB5KDC_ERR_SERVICE_EXP;
        }
 
@@ -1406,9 +1583,8 @@ kdc_check_flags(krb5_context context,
            char pwend_str[100];
            krb5_format_time(context, *server->pw_end,
                             pwend_str, sizeof(pwend_str), TRUE);
-           kdc_log(context, config, 0,
-                   "Server's key has expired at -- %s",
-                   pwend_str, server_name);
+           _kdc_audit_addreason((kdc_request_t)r, "Server's key has expired "
+                                 "at %s", pwend_str);
            return KRB5KDC_ERR_KEY_EXPIRED;
        }
     }
@@ -1422,10 +1598,11 @@ kdc_check_flags(krb5_context context,
  */
 
 krb5_boolean
-_kdc_check_addresses(krb5_context context,
-                    krb5_kdc_configuration *config,
-                    HostAddresses *addresses, const struct sockaddr *from)
+_kdc_check_addresses(astgs_request_t r, HostAddresses *addresses,
+                    const struct sockaddr *from)
 {
+    krb5_context context = r->context;
+    krb5_kdc_configuration *config = r->config;
     krb5_error_code ret;
     krb5_address addr;
     krb5_boolean result;
@@ -1462,6 +1639,21 @@ _kdc_check_addresses(krb5_context context,
     return result;
 }
 
+/*
+ *
+ */
+krb5_error_code
+_kdc_check_anon_policy(astgs_request_t r)
+{
+    if (!r->config->allow_anonymous) {
+       _kdc_audit_addreason((kdc_request_t)r,
+                             "Anonymous tickets denied by local policy");
+       return KRB5KDC_ERR_POLICY;
+    }
+
+    return 0;
+}
+
 /*
  *
  */
@@ -1496,16 +1688,25 @@ send_pac_p(krb5_context context, KDC_REQ *req)
  */
 
 static krb5_error_code
-generate_pac(kdc_request_t r, Key *skey)
+generate_pac(astgs_request_t r, Key *skey)
 {
     krb5_error_code ret;
+    const krb5_keyblock *pk_reply_key = NULL;
     krb5_pac p = NULL;
     krb5_data data;
 
-    ret = _kdc_pac_generate(r->context, r->client, &p);
+    switch (r->validated_pa_type) {
+    case KRB5_PADATA_PK_AS_REQ:
+    case KRB5_PADATA_PK_AS_REQ_WIN:
+       pk_reply_key = &r->reply_key;
+       break;
+    }
+
+    ret = _kdc_pac_generate(r->context, r->client,
+                           pk_reply_key, &p);
     if (ret) {
-       _kdc_r_log(r, 0, "PAC generation failed for -- %s",
-                  r->client_name);
+       _kdc_r_log(r, 4, "PAC generation failed for -- %s",
+                  r->cname);
        return ret;
     }
     if (p == NULL)
@@ -1518,8 +1719,8 @@ generate_pac(kdc_request_t r, Key *skey)
                         &data);
     krb5_pac_free(r->context, p);
     if (ret) {
-       _kdc_r_log(r, 0, "PAC signing failed for -- %s",
-                  r->client_name);
+       _kdc_r_log(r, 4, "PAC signing failed for -- %s",
+                  r->cname);
        return ret;
     }
     
@@ -1536,18 +1737,13 @@ generate_pac(kdc_request_t r, Key *skey)
  */
 
 krb5_boolean
-_kdc_is_anonymous(krb5_context context, krb5_principal principal)
+_kdc_is_anonymous(krb5_context context, krb5_const_principal principal)
 {
-    if (principal->name.name_type != KRB5_NT_WELLKNOWN ||
-       principal->name.name_string.len != 2 ||
-       strcmp(principal->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 ||
-       strcmp(principal->name.name_string.val[1], KRB5_ANON_NAME) != 0)
-       return 0;
-    return 1;
+    return krb5_principal_is_anonymous(context, principal, KRB5_ANON_MATCH_ANY);
 }
 
 static int
-require_preauth_p(kdc_request_t r)
+require_preauth_p(astgs_request_t r)
 {
     return r->config->require_preauth
        || r->client->entry.flags.require_preauth
@@ -1560,7 +1756,7 @@ require_preauth_p(kdc_request_t r)
  */
 
 static krb5_error_code
-add_enc_pa_rep(kdc_request_t r)
+add_enc_pa_rep(astgs_request_t r)
 {
     krb5_error_code ret;
     krb5_crypto crypto;
@@ -1606,15 +1802,12 @@ add_enc_pa_rep(kdc_request_t r)
  */
 
 krb5_error_code
-_kdc_as_rep(kdc_request_t r,
-           krb5_data *reply,
-           const char *from,
-           struct sockaddr *from_addr,
-           int datagram_reply)
+_kdc_as_rep(astgs_request_t r)
 {
     krb5_context context = r->context;
     krb5_kdc_configuration *config = r->config;
     KDC_REQ *req = &r->req;
+    const char *from = r->from;
     KDC_REQ_BODY *b = NULL;
     AS_REP rep;
     KDCOptions f;
@@ -1625,6 +1818,8 @@ _kdc_as_rep(kdc_request_t r,
     int i, flags = HDB_F_FOR_AS_REQ;
     METHOD_DATA error_method;
     const PA_DATA *pa;
+    krb5_boolean is_tgs;
+    const char *msg;
 
     memset(&rep, 0, sizeof(rep));
     error_method.len = 0;
@@ -1635,7 +1830,7 @@ _kdc_as_rep(kdc_request_t r,
      */
     ret = _kdc_fast_unwrap_request(r);
     if (ret) {
-       _kdc_r_log(r, 0, "FAST unwrap request from %s failed: %d", from, ret);
+       _kdc_r_log(r, 1, "FAST unwrap request from %s failed: %d", from, ret);
        goto out;
     }
 
@@ -1645,74 +1840,61 @@ _kdc_as_rep(kdc_request_t r,
     if (f.canonicalize)
        flags |= HDB_F_CANON;
 
-    if(b->sname == NULL){
+    if (b->sname == NULL) {
        ret = KRB5KRB_ERR_GENERIC;
        _kdc_set_e_text(r, "No server in request");
-    } else{
-       ret = _krb5_principalname2krb5_principal (context,
-                                                 &r->server_princ,
-                                                 *(b->sname),
-                                                 b->realm);
-       if (ret == 0)
-           ret = krb5_unparse_name(context, r->server_princ, &r->server_name);
+       goto out;
     }
+
+    ret = _krb5_principalname2krb5_principal(context, &r->server_princ,
+                                            *(b->sname), b->realm);
+    if (!ret)
+       ret = krb5_unparse_name(context, r->server_princ, &r->sname);
     if (ret) {
-       kdc_log(context, config, 0,
-               "AS-REQ malformed server name from %s", from);
+       kdc_log(context, config, 2,
+               "AS_REQ malformed server name from %s", from);
        goto out;
     }
-    if(b->cname == NULL){
+
+    if (b->cname == NULL) {
        ret = KRB5KRB_ERR_GENERIC;
        _kdc_set_e_text(r, "No client in request");
-    } else {
-       ret = _krb5_principalname2krb5_principal (context,
-                                                 &r->client_princ,
-                                                 *(b->cname),
-                                                 b->realm);
-       if (ret)
-           goto out;
-
-       ret = krb5_unparse_name(context, r->client_princ, &r->client_name);
+       goto out;
     }
+
+    ret = _krb5_principalname2krb5_principal(context, &r->client_princ,
+                                            *(b->cname), b->realm);
+    if (!ret)
+       ret = krb5_unparse_name(context, r->client_princ, &r->cname);
     if (ret) {
-       kdc_log(context, config, 0,
+       kdc_log(context, config, 2,
                "AS-REQ malformed client name from %s", from);
        goto out;
     }
 
-    kdc_log(context, config, 0, "AS-REQ %s from %s for %s",
-           r->client_name, from, r->server_name);
+    kdc_log(context, config, 4, "AS-REQ %s from %s for %s",
+           r->cname, r->from, r->sname);
 
-    /*
-     *
-     */
+    is_tgs = krb5_principal_is_krbtgt(context, r->server_princ);
 
-    if (_kdc_is_anonymous(context, r->client_princ)) {
-       if (!_kdc_is_anon_request(b)) {
-           kdc_log(context, config, 0, "Anonymous ticket w/o anonymous flag");
-           ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
-           goto out;
-       }
-    } else if (_kdc_is_anon_request(b)) {
-       kdc_log(context, config, 0,
-               "Request for a anonymous ticket with non "
-               "anonymous client name: %s", r->client_name);
-       ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+    if (_kdc_is_anonymous(context, r->client_princ) &&
+       !_kdc_is_anon_request(req)) {
+       kdc_log(context, config, 2, "Anonymous client w/o anonymous flag");
+       ret = KRB5KDC_ERR_BADOPTION;
        goto out;
     }
 
-    /*
-     *
-     */
-
     ret = _kdc_db_fetch(context, config, r->client_princ,
                        HDB_F_GET_CLIENT | flags, NULL,
                        &r->clientdb, &r->client);
-    if(ret == HDB_ERR_NOT_FOUND_HERE) {
+    switch (ret) {
+    case 0:    /* Success */
+       break;
+    case HDB_ERR_NOT_FOUND_HERE:
        kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy",
-               r->client_name);
+               r->cname);
        goto out;
-    } else if (ret == HDB_ERR_WRONG_REALM) {
+    case HDB_ERR_WRONG_REALM: {
        char *fixed_client_name = NULL;
 
        ret = krb5_unparse_name(context, r->client->entry.principal,
@@ -1721,39 +1903,37 @@ _kdc_as_rep(kdc_request_t r,
            goto out;
        }
 
-       kdc_log(context, config, 0, "WRONG_REALM - %s -> %s",
-               r->client_name, fixed_client_name);
+       kdc_log(context, config, 4, "WRONG_REALM - %s -> %s",
+               r->cname, fixed_client_name);
        free(fixed_client_name);
 
-       ret = _kdc_fast_mk_error(context, r,
-                                &error_method,
-                                r->armor_crypto,
-                                &req->req_body,
-                                KRB5_KDC_ERR_WRONG_REALM,
-                                NULL,
-                                r->server_princ,
-                                NULL,
+       ret = _kdc_fast_mk_error(r, &error_method, r->armor_crypto,
+                                &req->req_body, KRB5_KDC_ERR_WRONG_REALM,
+                                NULL, r->server_princ, NULL,
                                 &r->client->entry.principal->realm,
-                                NULL, NULL,
-                                reply);
+                                NULL, NULL, r->reply);
        goto out;
-    } else if(ret){
-       const char *msg = krb5_get_error_message(context, ret);
-       kdc_log(context, config, 0, "UNKNOWN -- %s: %s", r->client_name, msg);
+    }
+    default:
+       msg = krb5_get_error_message(context, ret);
+       kdc_log(context, config, 4, "UNKNOWN -- %s: %s", r->cname, msg);
        krb5_free_error_message(context, msg);
        ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
        goto out;
     }
     ret = _kdc_db_fetch(context, config, r->server_princ,
-                       HDB_F_GET_SERVER|HDB_F_GET_KRBTGT | flags,
+                       HDB_F_GET_SERVER | flags | (is_tgs ? HDB_F_GET_KRBTGT : 0),
                        NULL, NULL, &r->server);
-    if(ret == HDB_ERR_NOT_FOUND_HERE) {
+    switch (ret) {
+    case 0:    /* Success */
+       break;
+    case HDB_ERR_NOT_FOUND_HERE:
        kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy",
-               r->server_name);
+               r->sname);
        goto out;
-    } else if(ret){
-       const char *msg = krb5_get_error_message(context, ret);
-       kdc_log(context, config, 0, "UNKNOWN -- %s: %s", r->server_name, msg);
+    default:
+       msg = krb5_get_error_message(context, ret);
+       kdc_log(context, config, 4, "UNKNOWN -- %s: %s", r->sname, msg);
        krb5_free_error_message(context, msg);
        ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
        goto out;
@@ -1770,17 +1950,14 @@ _kdc_as_rep(kdc_request_t r,
      * decrypt.
      */
 
-    ret = _kdc_find_etype(context,
-                         krb5_principal_is_krbtgt(context, r->server_princ) ?
-                         config->tgt_use_strongest_session_key :
-                         config->svc_use_strongest_session_key, FALSE,
-                         r->client, b->etype.val, b->etype.len, &r->sessionetype,
-                         NULL);
+    ret = _kdc_find_etype(r, (is_tgs ?  KFE_IS_TGS:0) | KFE_USE_CLIENT,
+                         b->etype.val, b->etype.len,
+                         &r->sessionetype, NULL, NULL);
     if (ret) {
-       kdc_log(context, config, 0,
+       kdc_log(context, config, 4,
                "Client (%s) from %s has no common enctypes with KDC "
                "to use for the session key",
-               r->client_name, from);
+               r->cname, from);
        goto out;
     }
 
@@ -1791,7 +1968,7 @@ _kdc_as_rep(kdc_request_t r,
     if(req->padata){
        unsigned int n;
 
-       log_patypes(context, config, req->padata);
+       log_patypes(r, req->padata);
 
        /* Check if preauth matching */
 
@@ -1802,19 +1979,38 @@ _kdc_as_rep(kdc_request_t r,
                continue;
 
            kdc_log(context, config, 5,
-                   "Looking for %s pa-data -- %s", pat[n].name, r->client_name);
+                   "Looking for %s pa-data -- %s", pat[n].name, r->cname);
            i = 0;
            pa = _kdc_find_padata(req, &i, pat[n].type);
            if (pa) {
+                _kdc_audit_addkv((kdc_request_t)r, KDC_AUDIT_VIS, "pa", "%s",
+                                 pat[n].name);
                ret = pat[n].validate(r, pa);
                if (ret != 0) {
+                   krb5_error_code  ret2;
+                   Key *ckey = NULL;
+                   krb5_boolean default_salt;
+
+                   /*
+                    * If there is a client key, send ETYPE_INFO{,2}
+                    */
+                   ret2 = _kdc_find_etype(r, KFE_IS_PREAUTH|KFE_USE_CLIENT,
+                                          b->etype.val, b->etype.len,
+                                          NULL, &ckey, &default_salt);
+                   if (ret2 == 0) {
+                       ret2 = get_pa_etype_info_both(context, config, &b->etype,
+                                                     &error_method, ckey, !default_salt);
+                       if (ret2 != 0)
+                           ret = ret2;
+                   }
                    goto out;
                }
-               kdc_log(context, config, 0,
+               kdc_log(context, config, 4,
                        "%s pre-authentication succeeded -- %s",
-                       pat[n].name, r->client_name);
+                       pat[n].name, r->cname);
                found_pa = 1;
                r->et.flags.pre_authent = 1;
+               r->validated_pa_type = pat[n].type;
            }
        }
     }
@@ -1822,6 +2018,7 @@ _kdc_as_rep(kdc_request_t r,
     if (found_pa == 0) {
        Key *ckey = NULL;
        size_t n;
+       krb5_boolean default_salt;
 
        for (n = 0; n < sizeof(pat) / sizeof(pat[0]); n++) {
            if ((pat[n].flags & PA_ANNOUNCE) == 0)
@@ -1835,31 +2032,12 @@ _kdc_as_rep(kdc_request_t r,
        /*
         * If there is a client key, send ETYPE_INFO{,2}
         */
-       ret = _kdc_find_etype(context,
-                             config->preauth_use_strongest_session_key, TRUE,
-                             r->client, b->etype.val, b->etype.len, NULL, &ckey);
+       ret = _kdc_find_etype(r, KFE_IS_PREAUTH|KFE_USE_CLIENT,
+                             b->etype.val, b->etype.len,
+                             NULL, &ckey, &default_salt);
        if (ret == 0) {
-
-           /*
-            * RFC4120 requires:
-            * - If the client only knows about old enctypes, then send
-            *   both info replies (we send 'info' first in the list).
-            * - If the client is 'modern', because it knows about 'new'
-            *   enctype types, then only send the 'info2' reply.
-            *
-            * Before we send the full list of etype-info data, we pick
-            * the client key we would have used anyway below, just pick
-            * that instead.
-            */
-
-           if (older_enctype(ckey->key.keytype)) {
-               ret = get_pa_etype_info(context, config,
-                                       &error_method, ckey);
-               if (ret)
-                   goto out;
-           }
-           ret = get_pa_etype_info2(context, config,
-                                    &error_method, ckey);
+           ret = get_pa_etype_info_both(context, config, &b->etype,
+                                        &error_method, ckey, !default_salt);
            if (ret)
                goto out;
        }
@@ -1868,7 +2046,7 @@ _kdc_as_rep(kdc_request_t r,
         * send requre preauth is its required or anon is requested,
         * anon is today only allowed via preauth mechanisms.
         */
-       if (require_preauth_p(r) || _kdc_is_anon_request(b)) {
+       if (require_preauth_p(r) || _kdc_is_anon_request(&r->req)) {
            ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
            _kdc_set_e_text(r, "Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ");
            goto out;
@@ -1883,10 +2061,12 @@ _kdc_as_rep(kdc_request_t r,
        ret = krb5_copy_keyblock_contents(r->context, &ckey->key, &r->reply_key);
        if (ret)
            goto out;
+
+       r->reply_kvno = 0;
     }
 
     if (r->clientdb->hdb_auth_status) {
-       r->clientdb->hdb_auth_status(context, r->clientdb, r->client, 
+       r->clientdb->hdb_auth_status(context, r->clientdb, r->client,
                                     HDB_AUTH_SUCCESS);
     }
 
@@ -1895,25 +2075,32 @@ _kdc_as_rep(kdc_request_t r,
      * with in a preauth mech.
      */
 
-    ret = _kdc_check_access(context, config, r->client, r->client_name,
-                           r->server, r->server_name,
-                           req, &error_method);
+    ret = _kdc_check_access(r, req, &error_method);
     if(ret)
        goto out;
 
+    if (_kdc_is_anon_request(&r->req)) {
+       ret = _kdc_check_anon_policy(r);
+       if (ret) {
+           _kdc_set_e_text(r, "Anonymous ticket requests are disabled");
+           goto out;
+       }
+
+       r->et.flags.anonymous = 1;
+    }
+
     /*
      * Select the best encryption type for the KDC with out regard to
      * the client since the client never needs to read that data.
      */
 
     ret = _kdc_get_preferred_key(context, config,
-                                r->server, r->server_name,
+                                r->server, r->sname,
                                 &setype, &skey);
     if(ret)
        goto out;
 
-    if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey
-       || (_kdc_is_anon_request(b) && !config->allow_anonymous)) {
+    if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey) {
        ret = KRB5KDC_ERR_BADOPTION;
        _kdc_set_e_text(r, "Bad KDC options");
        goto out;
@@ -1926,21 +2113,38 @@ _kdc_as_rep(kdc_request_t r,
     rep.pvno = 5;
     rep.msg_type = krb_as_rep;
 
-    if (_kdc_is_anonymous(context, r->client_princ)) {
-       Realm anon_realm=KRB5_ANON_REALM;
+    if (!config->historical_anon_realm &&
+        _kdc_is_anonymous(context, r->client_princ)) {
+       Realm anon_realm = KRB5_ANON_REALM;
        ret = copy_Realm(&anon_realm, &rep.crealm);
-    } else
+    } else if (f.canonicalize || r->client->entry.flags.force_canonicalize)
        ret = copy_Realm(&r->client->entry.principal->realm, &rep.crealm);
+    else
+       ret = copy_Realm(&r->client_princ->realm, &rep.crealm);
     if (ret)
        goto out;
-    ret = _krb5_principal2principalname(&rep.cname, r->client->entry.principal);
+    if (r->et.flags.anonymous)
+       ret = _kdc_make_anonymous_principalname(&rep.cname);
+    else if (f.canonicalize || r->client->entry.flags.force_canonicalize)
+       ret = _krb5_principal2principalname(&rep.cname, r->client->entry.principal);
+    else
+       ret = _krb5_principal2principalname(&rep.cname, r->client_princ);
     if (ret)
        goto out;
 
     rep.ticket.tkt_vno = 5;
-    copy_Realm(&r->server->entry.principal->realm, &rep.ticket.realm);
-    _krb5_principal2principalname(&rep.ticket.sname,
-                                 r->server->entry.principal);
+    if (f.canonicalize || r->server->entry.flags.force_canonicalize)
+       ret = copy_Realm(&r->server->entry.principal->realm, &rep.ticket.realm);
+    else
+       ret = copy_Realm(&r->server_princ->realm, &rep.ticket.realm);
+    if (ret)
+       goto out;
+    if (f.canonicalize || r->server->entry.flags.force_canonicalize)
+       _krb5_principal2principalname(&rep.ticket.sname,
+                                     r->server->entry.principal);
+    else
+       _krb5_principal2principalname(&rep.ticket.sname,
+                                     r->server_princ);
     /* java 1.6 expects the name to be the same type, lets allow that
      * uncomplicated name-types. */
 #define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t)
@@ -1951,11 +2155,6 @@ _kdc_as_rep(kdc_request_t r,
     r->et.flags.initial = 1;
     if(r->client->entry.flags.forwardable && r->server->entry.flags.forwardable)
        r->et.flags.forwardable = f.forwardable;
-    else if (f.forwardable) {
-       _kdc_set_e_text(r, "Ticket may not be forwardable");
-       ret = KRB5KDC_ERR_POLICY;
-       goto out;
-    }
     if(r->client->entry.flags.proxiable && r->server->entry.flags.proxiable)
        r->et.flags.proxiable = f.proxiable;
     else if (f.proxiable) {
@@ -1972,7 +2171,7 @@ _kdc_as_rep(kdc_request_t r,
     }
 
     /* check for valid set of addresses */
-    if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) {
+    if (!_kdc_check_addresses(r, b->addresses, r->addr)) {
        _kdc_set_e_text(r, "Bad address list in requested");
        ret = KRB5KRB_AP_ERR_BADADDR;
        goto out;
@@ -2036,9 +2235,6 @@ _kdc_as_rep(kdc_request_t r,
        }
     }
 
-    if (_kdc_is_anon_request(b))
-       r->et.flags.anonymous = 1;
-
     if(b->addresses){
        ALLOC(r->et.caddr);
        copy_HostAddresses(b->addresses, r->et.caddr);
@@ -2103,8 +2299,12 @@ _kdc_as_rep(kdc_request_t r,
        ALLOC(r->ek.renew_till);
        *r->ek.renew_till = *r->et.renew_till;
     }
-    copy_Realm(&rep.ticket.realm, &r->ek.srealm);
-    copy_PrincipalName(&rep.ticket.sname, &r->ek.sname);
+    ret = copy_Realm(&rep.ticket.realm, &r->ek.srealm);
+    if (ret)
+       goto out;
+    ret = copy_PrincipalName(&rep.ticket.sname, &r->ek.sname);
+    if (ret)
+       goto out;
     if(r->et.caddr){
        ALLOC(r->ek.caddr);
        copy_HostAddresses(r->et.caddr, r->ek.caddr);
@@ -2147,26 +2347,37 @@ _kdc_as_rep(kdc_request_t r,
     }
 
     /* Add the PAC */
-    if (send_pac_p(context, req)) {
+    if (send_pac_p(context, req) && !r->et.flags.anonymous) {
        generate_pac(r, skey);
     }
 
-    _kdc_log_timestamp(context, config, "AS-REQ", r->et.authtime, r->et.starttime,
-                      r->et.endtime, r->et.renew_till);
+    _kdc_log_timestamp(r, "AS-REQ", r->et.authtime,
+                      r->et.starttime, r->et.endtime,
+                      r->et.renew_till);
 
-    /* do this as the last thing since this signs the EncTicketPart */
-    ret = _kdc_add_KRB5SignedPath(context,
-                                 config,
-                                 r->server,
-                                 setype,
-                                 r->client->entry.principal,
-                                 NULL,
-                                 NULL,
-                                 &r->et);
-    if (ret)
-       goto out;
+    {
+       krb5_principal client_principal;
+
+       ret = _krb5_principalname2krb5_principal(context, &client_principal,
+                                                rep.cname, rep.crealm);
+       if (ret)
+           goto out;
+
+       /* do this as the last thing since this signs the EncTicketPart */
+       ret = _kdc_add_KRB5SignedPath(context,
+                                     config,
+                                     r->server,
+                                     setype,
+                                     client_principal,
+                                     NULL,
+                                     NULL,
+                                     &r->et);
+       krb5_free_principal(context, client_principal);
+       if (ret)
+           goto out;
+    }
 
-    log_as_req(context, config, r->reply_key.keytype, setype, b);
+    _log_astgs_req(r, setype);
 
     /*
      * We always say we support FAST/enc-pa-rep
@@ -2184,8 +2395,8 @@ _kdc_as_rep(kdc_request_t r,
 
        ret = add_enc_pa_rep(r);
        if (ret) {
-           const char *msg = krb5_get_error_message(r->context, ret);
-           _kdc_r_log(r, 0, "add_enc_pa_rep failed: %s: %d", msg, ret);
+           msg = krb5_get_error_message(r->context, ret);
+           _kdc_r_log(r, 4, "add_enc_pa_rep failed: %s: %d", msg, ret);
            krb5_free_error_message(r->context, msg);
            goto out;
        }
@@ -2197,17 +2408,18 @@ _kdc_as_rep(kdc_request_t r,
 
     ret = _kdc_encode_reply(context, config,
                            r->armor_crypto, req->req_body.nonce,
-                           &rep, &r->et, &r->ek, setype, r->server->entry.kvno,
-                           &skey->key, r->client->entry.kvno,
-                           &r->reply_key, 0, &r->e_text, reply);
+                           &rep, &r->et, &r->ek, setype,
+                           r->server->entry.kvno, &skey->key,
+                           r->reply_kvno,
+                           &r->reply_key, 0, &r->e_text, r->reply);
     if (ret)
        goto out;
 
     /*
      * Check if message too large
      */
-    if (datagram_reply && reply->length > config->max_datagram_reply_length) {
-       krb5_data_free(reply);
+    if (r->datagram_reply && r->reply->length > config->max_datagram_reply_length) {
+       krb5_data_free(r->reply);
        ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
        _kdc_set_e_text(r, "Reply packet too large");
     }
@@ -2218,21 +2430,19 @@ out:
     /*
      * In case of a non proxy error, build an error message.
      */
-    if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE && reply->length == 0) {
-       ret = _kdc_fast_mk_error(context, r,
-                                &error_method,
-                                r->armor_crypto,
-                                &req->req_body,
-                                ret, r->e_text,
-                                r->server_princ,
-                                &r->client_princ->name,
-                                &r->client_princ->realm,
-                                NULL, NULL,
-                                reply);
-       if (ret)
-           goto out2;
-    }
-out2:
+    if (ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE && r->reply->length == 0)
+       ret = _kdc_fast_mk_error(r, &error_method,
+                                r->armor_crypto,
+                                &req->req_body,
+                                ret, r->e_text,
+                                r->server_princ,
+                                r->client_princ ?
+                                    &r->client_princ->name : NULL,
+                                r->client_princ ?
+                                    &r->client_princ->realm : NULL,
+                                NULL, NULL,
+                                r->reply);
+
     free_EncTicketPart(&r->et);
     free_EncKDCRepPart(&r->ek);
     free_KDCFastState(&r->fast);
@@ -2245,18 +2455,10 @@ out2:
        krb5_free_principal(context, r->client_princ);
        r->client_princ = NULL;
     }
-    if (r->client_name) {
-       free(r->client_name);
-       r->client_name = NULL;
-    }
     if (r->server_princ){
        krb5_free_principal(context, r->server_princ);
        r->server_princ = NULL;
     }
-    if (r->server_name) {
-       free(r->server_name);
-       r->server_name = NULL;
-    }
     if (r->client)
        _kdc_free_ent(context, r->client);
     if (r->server)
@@ -2330,14 +2532,3 @@ _kdc_tkt_add_if_relevant_ad(krb5_context context,
 
     return 0;
 }
-
-krb5_boolean
-_kdc_is_anon_request(const KDC_REQ_BODY *b)
-{
-       /* some versions of heimdal use bit 14 instead of 16 for
-          request_anonymous, as indicated in the anonymous draft prior to
-          version 11. Bit 14 is assigned to S4U2Proxy, but all S4U2Proxy
-          requests will have a second ticket; don't consider those anonymous */
-       return (b->kdc_options.request_anonymous ||
-               (b->kdc_options.constrained_delegation && !b->additional_tickets));
-}