Apply by doing:
       cd /usr/src
       patch -p0 < 022_kerberos.patch

Rebuild and install the Kerberos 5 library:
       cd lib/libkrb5
       make obj
       make depend
       make
       make install

And then rebuild and install the Kerberos 5 KDC:
       cd ../../kerberosV/libexec/kdc
       make obj
       make depend
       make
       make install

Index: kerberosV/src/kdc/config.c
===================================================================
RCS file: /cvs/src/kerberosV/src/kdc/config.c,v
retrieving revision 1.5
retrieving revision 1.5.2.1
diff -u -p -r1.5 -r1.5.2.1
--- kerberosV/src/kdc/config.c  12 May 2003 09:42:26 -0000      1.5
+++ kerberosV/src/kdc/config.c  26 May 2004 09:02:38 -0000      1.5.2.1
@@ -64,6 +64,8 @@ krb5_boolean encode_as_rep_as_tgs_rep; /
krb5_boolean check_ticket_addresses;
krb5_boolean allow_null_ticket_addresses;
krb5_boolean allow_anonymous;
+int trpolicy;
+static const char *trpolicy_str;

static struct getarg_strings addresses_str;    /* addresses to listen on */
krb5_addresses explicit_addresses;
@@ -293,9 +295,8 @@ configure(int argc, char **argv)

    get_dbinfo();

-    if(max_request_str){
+    if(max_request_str)
       max_request = parse_bytes(max_request_str, NULL);
-    }

    if(max_request == 0){
       p = krb5_config_get_string (context,
@@ -366,6 +367,23 @@ configure(int argc, char **argv)
    allow_anonymous =
       krb5_config_get_bool(context, NULL, "kdc",
                            "allow-anonymous", NULL);
+    trpolicy_str =
+       krb5_config_get_string_default(context, NULL, "always-check", "kdc",
+                                      "transited-policy", NULL);
+    if(strcasecmp(trpolicy_str, "always-check") == 0)
+       trpolicy = TRPOLICY_ALWAYS_CHECK;
+    else if(strcasecmp(trpolicy_str, "allow-per-principal") == 0)
+       trpolicy = TRPOLICY_ALLOW_PER_PRINCIPAL;
+    else if(strcasecmp(trpolicy_str, "always-honour-request") == 0)
+       trpolicy = TRPOLICY_ALWAYS_HONOUR_REQUEST;
+    else {
+       kdc_log(0, "unknown transited-policy: %s, reverting to always-check",
+               trpolicy_str);
+       trpolicy = TRPOLICY_ALWAYS_CHECK;
+    }
+
+       krb5_config_get_bool_default(context, NULL, TRUE, "kdc",
+                                    "enforce-transited-policy", NULL);
#ifdef KRB4
    if(v4_realm == NULL){
       p = krb5_config_get_string (context, NULL,
Index: kerberosV/src/kdc/kdc.8
===================================================================
RCS file: /cvs/src/kerberosV/src/kdc/kdc.8,v
retrieving revision 1.5
retrieving revision 1.5.2.1
diff -u -p -r1.5 -r1.5.2.1
--- kerberosV/src/kdc/kdc.8     11 May 2003 03:39:59 -0000      1.5
+++ kerberosV/src/kdc/kdc.8     26 May 2004 09:02:38 -0000      1.5.2.1
@@ -31,7 +31,7 @@
.\"
.\" $KTH: kdc.8,v 1.23 2003/04/06 17:48:40 lha Exp $
.\"
-.Dd August 22, 2002
+.Dd October 22, 2003
.Dt KDC 8
.Os HEIMDAL
.Sh NAME
@@ -193,6 +193,27 @@ Permit tickets with no addresses.
This option is only relevant when check-ticket-addresses is TRUE.
.It Li allow-anonymous = Va boolean
Permit anonymous tickets with no addresses.
+.It Li transited-policy = Xo
+.Li always-check \*(Ba
+.Li allow-per-principal |
+.Li always-honour-request
+.Xc
+This controls how KDC requests with the
+.Li disable-transited-check
+flag are handled. It can be one of:
+.Bl -tag -width "xxx" -offset indent
+.It Li always-check
+Always check transited encoding, this is the default.
+.It Li allow-per-principal
+Currently this is identical to
+.Li always-check .
+In a future release, it will be possible to mark a principal as able
+to handle unchecked requests.
+.It Li always-honour-request
+Always do what the client asked.
+In a future release, it will be possible to force a check per
+principal.
+.El
.It encode_as_rep_as_tgs_rep = Va boolean
Encode AS-Rep as TGS-Rep to be bug-compatible with old DCE code.
The Heimdal clients allow both.
Index: kerberosV/src/kdc/kdc_locl.h
===================================================================
RCS file: /cvs/src/kerberosV/src/kdc/kdc_locl.h,v
retrieving revision 1.5
retrieving revision 1.5.2.1
diff -u -p -r1.5 -r1.5.2.1
--- kerberosV/src/kdc/kdc_locl.h        11 May 2003 03:39:59 -0000      1.5
+++ kerberosV/src/kdc/kdc_locl.h        26 May 2004 09:02:38 -0000      1.5.2.1
@@ -62,6 +62,10 @@ extern krb5_boolean encode_as_rep_as_tgs
extern krb5_boolean check_ticket_addresses;
extern krb5_boolean allow_null_ticket_addresses;
extern krb5_boolean allow_anonymous;
+enum { TRPOLICY_ALWAYS_CHECK,
+       TRPOLICY_ALLOW_PER_PRINCIPAL,
+       TRPOLICY_ALWAYS_HONOUR_REQUEST };
+extern int trpolicy;
extern int enable_524;
extern int enable_v4_cross_realm;

Index: kerberosV/src/kdc/kerberos5.c
===================================================================
RCS file: /cvs/src/kerberosV/src/kdc/kerberos5.c,v
retrieving revision 1.1.1.4
retrieving revision 1.1.1.4.2.1
diff -u -p -r1.1.1.4 -r1.1.1.4.2.1
--- kerberosV/src/kdc/kerberos5.c       11 May 2003 02:15:33 -0000      1.1.1.4
+++ kerberosV/src/kdc/kerberos5.c       26 May 2004 09:02:38 -0000      1.1.1.4.2.1
@@ -496,8 +496,8 @@ as_rep(KDC_REQ *req,
    krb5_enctype cetype, setype;
    EncTicketPart et;
    EncKDCRepPart ek;
-    krb5_principal client_princ, server_princ;
-    char *client_name, *server_name;
+    krb5_principal client_princ = NULL, server_princ = NULL;
+    char *client_name = NULL, *server_name = NULL;
    krb5_error_code ret = 0;
    const char *e_text = NULL;
    krb5_crypto crypto;
@@ -506,27 +506,30 @@ as_rep(KDC_REQ *req,
    memset(&rep, 0, sizeof(rep));

    if(b->sname == NULL){
-       server_name = "<unknown server>";
       ret = KRB5KRB_ERR_GENERIC;
       e_text = "No server in request";
    } else{
       principalname2krb5_principal (&server_princ, *(b->sname), b->realm);
       krb5_unparse_name(context, server_princ, &server_name);
    }
+    if (ret) {
+       kdc_log(0, "AS-REQ malformed server name from %s", from);
+       goto out;
+    }

    if(b->cname == NULL){
-       client_name = "<unknown client>";
       ret = KRB5KRB_ERR_GENERIC;
       e_text = "No client in request";
    } else {
       principalname2krb5_principal (&client_princ, *(b->cname), b->realm);
       krb5_unparse_name(context, client_princ, &client_name);
    }
-    kdc_log(0, "AS-REQ %s from %s for %s",
-           client_name, from, server_name);
-
-    if(ret)
+    if (ret) {
+       kdc_log(0, "AS-REQ malformed client name from %s", from);
       goto out;
+    }
+
+    kdc_log(0, "AS-REQ %s from %s for %s", client_name, from, server_name);

    ret = db_fetch(client_princ, &client);
    if(ret){
@@ -842,13 +845,8 @@ as_rep(KDC_REQ *req,
       copy_HostAddresses(b->addresses, et.caddr);
    }

-    {
-       krb5_data empty_string;
-
-       krb5_data_zero(&empty_string);
-       et.transited.tr_type = DOMAIN_X500_COMPRESS;
-       et.transited.contents = empty_string;
-    }
+    et.transited.tr_type = DOMAIN_X500_COMPRESS;
+    krb5_data_zero(&et.transited.contents);

    copy_EncryptionKey(&et.key, &ek.key);

@@ -930,9 +928,11 @@ as_rep(KDC_REQ *req,
       ret = 0;
    }
  out2:
-    krb5_free_principal(context, client_princ);
+    if (client_princ)
+       krb5_free_principal(context, client_princ);
    free(client_name);
-    krb5_free_principal(context, server_princ);
+    if (server_princ)
+       krb5_free_principal(context, server_princ);
    free(server_name);
    if(client)
       free_ent(client);
@@ -1055,33 +1055,35 @@ check_tgs_flags(KDC_REQ_BODY *b, EncTick
}

static krb5_error_code
-fix_transited_encoding(TransitedEncoding *tr,
+fix_transited_encoding(krb5_boolean check_policy,
+                      TransitedEncoding *tr,
+                      EncTicketPart *et,
                      const char *client_realm,
                      const char *server_realm,
                      const char *tgt_realm)
{
    krb5_error_code ret = 0;
-    if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)){
-       char **realms = NULL, **tmp;
-       int num_realms = 0;
-       int i;
-       if(tr->tr_type && tr->contents.length != 0) {
-           if(tr->tr_type != DOMAIN_X500_COMPRESS){
-               kdc_log(0, "Unknown transited type: %u",
-                       tr->tr_type);
-               return KRB5KDC_ERR_TRTYPE_NOSUPP;
-           }
-           ret = krb5_domain_x500_decode(context,
-                                         tr->contents,
-                                         &realms,
-                                         &num_realms,
-                                         client_realm,
-                                         server_realm);
-           if(ret){
-               krb5_warn(context, ret, "Decoding transited encoding");
-               return ret;
-           }
-       }
+    char **realms, **tmp;
+    int num_realms;
+    int i;
+
+    if(tr->tr_type != DOMAIN_X500_COMPRESS) {
+       kdc_log(0, "Unknown transited type: %u", tr->tr_type);
+       return KRB5KDC_ERR_TRTYPE_NOSUPP;
+    }
+
+    ret = krb5_domain_x500_decode(context,
+                                 tr->contents,
+                                 &realms,
+                                 &num_realms,
+                                 client_realm,
+                                 server_realm);
+    if(ret){
+       krb5_warn(context, ret, "Decoding transited encoding");
+       return ret;
+    }
+    if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
+       /* not us, so add the previous realm to transited set */
       if (num_realms < 0 || num_realms + 1 > UINT_MAX/sizeof(*realms)) {
           ret = ERANGE;
           goto free_realms;
@@ -1098,16 +1100,46 @@ fix_transited_encoding(TransitedEncoding
           goto free_realms;
       }
       num_realms++;
-       free_TransitedEncoding(tr);
-       tr->tr_type = DOMAIN_X500_COMPRESS;
-       ret = krb5_domain_x500_encode(realms, num_realms, &tr->contents);
-       if(ret)
-           krb5_warn(context, ret, "Encoding transited encoding");
-    free_realms:
+    }
+    if(num_realms == 0) {
+       if(strcmp(client_realm, server_realm))
+           kdc_log(0, "cross-realm %s -> %s", client_realm, server_realm);
+    } else {
+       size_t l = 0;
+       char *rs;
       for(i = 0; i < num_realms; i++)
-           free(realms[i]);
-       free(realms);
+           l += strlen(realms[i]) + 2;
+       rs = malloc(l);
+       if(rs != NULL) {
+           *rs = '\0';
+           for(i = 0; i < num_realms; i++) {
+               if(i > 0)
+                   strlcat(rs, ", ", l);
+               strlcat(rs, realms[i], l);
+           }
+           kdc_log(0, "cross-realm %s -> %s via [%s]", client_realm, server_realm, rs);
+           free(rs);
+       }
    }
+    if(check_policy) {
+       ret = krb5_check_transited(context, client_realm,
+                                  server_realm,
+                                  realms, num_realms, NULL);
+       if(ret) {
+           krb5_warn(context, ret, "cross-realm %s -> %s",
+                     client_realm, server_realm);
+           goto free_realms;
+       }
+       et->flags.transited_policy_checked = 1;
+    }
+    et->transited.tr_type = DOMAIN_X500_COMPRESS;
+    ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
+    if(ret)
+       krb5_warn(context, ret, "Encoding transited encoding");
+  free_realms:
+    for(i = 0; i < num_realms; i++)
+       free(realms[i]);
+    free(realms);
    return ret;
}

@@ -1175,8 +1207,28 @@ tgs_make_reply(KDC_REQ_BODY *b,
    if(ret)
       goto out;

-    copy_TransitedEncoding(&tgt->transited, &et.transited);
-    ret = fix_transited_encoding(&et.transited,
+    /* We should check the transited encoding if:
+       1) the request doesn't ask not to be checked
+       2) globally enforcing a check
+       3) principal requires checking
+       4) we allow non-check per-principal, but principal isn't marked as allowing this
+       5) we don't globally allow this
+    */
+
+#define GLOBAL_FORCE_TRANSITED_CHECK           (trpolicy == TRPOLICY_ALWAYS_CHECK)
+#define GLOBAL_ALLOW_PER_PRINCIPAL             (trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
+#define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK   (trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
+/* these will consult the database in future release */
+#define PRINCIPAL_FORCE_TRANSITED_CHECK(P)             0
+#define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P)     0
+
+    ret = fix_transited_encoding(!f.disable_transited_check ||
+                                GLOBAL_FORCE_TRANSITED_CHECK ||
+                                PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
+                                !((GLOBAL_ALLOW_PER_PRINCIPAL &&
+                                   PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
+                                  GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
+                                &tgt->transited, &et,
                                *krb5_princ_realm(context, client_principal),
                                *krb5_princ_realm(context, server->principal),
                                *krb5_princ_realm(context, krbtgt->principal));
@@ -1276,7 +1328,7 @@ tgs_make_reply(KDC_REQ_BODY *b,
       DES3? */
    ret = encode_reply(&rep, &et, &ek, etype, adtkt ? 0 : server->kvno, ekey,
                      0, &tgt->key, e_text, reply);
-out:
+  out:
    free_TGS_REP(&rep);
    free_TransitedEncoding(&et.transited);
    if(et.starttime)
@@ -1378,13 +1430,13 @@ get_krbtgt_realm(const PrincipalName *p)
}

static Realm
-find_rpath(Realm r)
+find_rpath(Realm crealm, Realm srealm)
{
    const char *new_realm = krb5_config_get_string(context,
                                                  NULL,
-                                                  "libdefaults",
-                                                  "capath",
-                                                  r,
+                                                  "capaths",
+                                                  crealm,
+                                                  srealm,
                                                  NULL);
    return (Realm)new_realm;
}
@@ -1676,7 +1728,7 @@ tgs_rep2(KDC_REQ_BODY *b,

           if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
               if(loop++ < 2) {
-                   new_rlm = find_rpath(req_rlm);
+                   new_rlm = find_rpath(tgt->crealm, req_rlm);
                   if(new_rlm) {
                       kdc_log(5, "krbtgt for realm %s not found, trying %s",
                               req_rlm, new_rlm);
@@ -1724,6 +1776,18 @@ tgs_rep2(KDC_REQ_BODY *b,
           goto out;
       }
#endif
+
+       if(strcmp(krb5_principal_get_realm(context, sp),
+                 krb5_principal_get_comp_string(context, krbtgt->principal, 1)) != 0) {
+           char *tpn;
+           ret = krb5_unparse_name(context, krbtgt->principal, &tpn);
+           kdc_log(0, "Request with wrong krbtgt: %s", (ret == 0) ? tpn : "<unknown>");
+           if(ret == 0)
+               free(tpn);
+           ret = KRB5KRB_AP_ERR_NOT_US;
+           goto out;
+
+       }

       ret = check_flags(client, cpn, server, spn, FALSE);
       if(ret)
Index: kerberosV/src/lib/krb5/krb5-protos.h
===================================================================
RCS file: /cvs/src/kerberosV/src/lib/krb5/krb5-protos.h,v
retrieving revision 1.1.1.4
retrieving revision 1.1.1.4.2.1
diff -u -p -r1.1.1.4 -r1.1.1.4.2.1
--- kerberosV/src/lib/krb5/krb5-protos.h        11 May 2003 02:16:02 -0000      1.1.1.4
+++ kerberosV/src/lib/krb5/krb5-protos.h        26 May 2004 09:02:38 -0000      1.1.1.4.2.1
@@ -541,6 +541,15 @@ krb5_change_password (
       krb5_data */*result_string*/);

krb5_error_code
+krb5_check_transited (
+       krb5_context /*context*/,
+       krb5_const_realm /*client_realm*/,
+       krb5_const_realm /*server_realm*/,
+       krb5_realm */*realms*/,
+       int /*num_realms*/,
+       int */*bad_realm*/);
+
+krb5_error_code
krb5_check_transited_realms (
       krb5_context /*context*/,
       const char *const */*realms*/,
Index: kerberosV/src/lib/krb5/rd_req.c
===================================================================
RCS file: /cvs/src/kerberosV/src/lib/krb5/rd_req.c,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.8.1
diff -u -p -r1.1.1.3 -r1.1.1.3.8.1
--- kerberosV/src/lib/krb5/rd_req.c     6 Feb 2002 08:55:37 -0000       1.1.1.3
+++ kerberosV/src/lib/krb5/rd_req.c     26 May 2004 09:02:38 -0000      1.1.1.3.8.1
@@ -129,6 +129,32 @@ krb5_decode_ap_req(krb5_context context,
    return 0;
}

+static krb5_error_code
+check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
+{
+    char **realms;
+    int num_realms;
+    krb5_error_code ret;
+
+    if(enc->transited.tr_type != DOMAIN_X500_COMPRESS)
+       return KRB5KDC_ERR_TRTYPE_NOSUPP;
+
+    if(enc->transited.contents.length == 0)
+       return 0;
+
+    ret = krb5_domain_x500_decode(context, enc->transited.contents,
+                                 &realms, &num_realms,
+                                 enc->crealm,
+                                 ticket->realm);
+    if(ret)
+       return ret;
+    ret = krb5_check_transited(context, enc->crealm,
+                              ticket->realm,
+                              realms, num_realms, NULL);
+    free(realms);
+    return ret;
+}
+
krb5_error_code
krb5_decrypt_ticket(krb5_context context,
                   Ticket *ticket,
@@ -161,6 +187,14 @@ krb5_decrypt_ticket(krb5_context context
           krb5_clear_error_string (context);
           return KRB5KRB_AP_ERR_TKT_EXPIRED;
       }
+
+       if(!t.flags.transited_policy_checked) {
+           ret = check_transited(context, ticket, &t);
+           if(ret) {
+               free_EncTicketPart(&t);
+               return ret;
+           }
+       }
    }

    if(out)
@@ -209,29 +243,6 @@ out:
    return ret;
}

-#if 0
-static krb5_error_code
-check_transited(krb5_context context,
-               krb5_ticket *ticket)
-{
-    char **realms;
-    int num_realms;
-    krb5_error_code ret;
-
-    if(ticket->ticket.transited.tr_type != DOMAIN_X500_COMPRESS)
-       return KRB5KDC_ERR_TRTYPE_NOSUPP;
-
-    ret = krb5_domain_x500_decode(ticket->ticket.transited.contents,
-                                 &realms, &num_realms,
-                                 ticket->client->realm,
-                                 ticket->server->realm);
-    if(ret)
-       return ret;
-    ret = krb5_check_transited_realms(context, realms, num_realms, NULL);
-    free(realms);
-    return ret;
-}
-#endif

krb5_error_code
krb5_verify_ap_req(krb5_context context,
Index: kerberosV/src/lib/krb5/transited.c
===================================================================
RCS file: /cvs/src/kerberosV/src/lib/krb5/transited.c,v
retrieving revision 1.5
retrieving revision 1.5.2.1
diff -u -p -r1.5 -r1.5.2.1
--- kerberosV/src/lib/krb5/transited.c  6 Aug 2003 21:08:05 -0000       1.5
+++ kerberosV/src/lib/krb5/transited.c  26 May 2004 09:02:38 -0000      1.5.2.1
@@ -308,6 +308,12 @@ krb5_domain_x500_decode(krb5_context con
    struct tr_realm *p, **q;
    int ret;

+    if(tr.length == 0) {
+       *realms = NULL;
+       *num_realms = 0;
+       return 0;
+    }
+
    /* split string in components */
    ret = decode_realms(context, tr.data, tr.length, &r);
    if(ret)
@@ -362,6 +368,9 @@ krb5_domain_x500_encode(char **realms, i
    char *s = NULL;
    int len = 0;
    int i;
+    krb5_data_zero(encoding);
+    if (num_realms == 0)
+       return 0;
    for(i = 0; i < num_realms; i++){
       len += strlen(realms[i]);
       if(realms[i][0] == '/')
@@ -369,6 +378,8 @@ krb5_domain_x500_encode(char **realms, i
    }
    len += num_realms - 1;
    s = malloc(len + 1);
+    if (s == NULL)
+       return ENOMEM;
    *s = '\0';
    for(i = 0; i < num_realms; i++){
       if(i && i < num_realms - 1)
@@ -379,6 +390,44 @@ krb5_domain_x500_encode(char **realms, i
    }
    encoding->data = s;
    encoding->length = strlen(s);
+    return 0;
+}
+
+krb5_error_code
+krb5_check_transited(krb5_context context,
+                    krb5_const_realm client_realm,
+                    krb5_const_realm server_realm,
+                    krb5_realm *realms,
+                    int num_realms,
+                    int *bad_realm)
+{
+    char **tr_realms;
+    char **p;
+    int i;
+
+    if(num_realms == 0)
+       return 0;
+
+    tr_realms = krb5_config_get_strings(context, NULL,
+                                       "capaths",
+                                       client_realm,
+                                       server_realm,
+                                       NULL);
+    for(i = 0; i < num_realms; i++) {
+       for(p = tr_realms; p && *p; p++) {
+           if(strcmp(*p, realms[i]) == 0)
+               break;
+       }
+       if(p == NULL || *p == NULL) {
+           krb5_config_free_strings(tr_realms);
+           krb5_set_error_string (context, "no transit through realm %s",
+                                  realms[i]);
+           if(bad_realm)
+               *bad_realm = i;
+           return KRB5KRB_AP_ERR_ILL_CR_TKT;
+       }
+    }
+    krb5_config_free_strings(tr_realms);
    return 0;
}