Apply by doing: cd /usr/src patch -p0 < 009_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.4.1 diff -u -p -r1.5 -r1.5.4.1 --- kerberosV/src/kdc/config.c 12 May 2003 09:42:26 -0000 1.5 +++ kerberosV/src/kdc/config.c 26 May 2004 08:17:47 -0000 1.5.4.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.4.1 diff -u -p -r1.5 -r1.5.4.1 --- kerberosV/src/kdc/kdc.8 11 May 2003 03:39:59 -0000 1.5 +++ kerberosV/src/kdc/kdc.8 26 May 2004 08:17:47 -0000 1.5.4.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.4.1 diff -u -p -r1.5 -r1.5.4.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 08:17:47 -0000 1.5.4.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.4.1 diff -u -p -r1.1.1.4 -r1.1.1.4.4.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 08:17:47 -0000 1.1.1.4.4.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 = ""; 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 = ""; 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 : ""); + 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.4.1 diff -u -p -r1.1.1.4 -r1.1.1.4.4.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 08:17:47 -0000 1.1.1.4.4.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.10.1 diff -u -p -r1.1.1.3 -r1.1.1.3.10.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 08:17:48 -0000 1.1.1.3.10.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.4.1 diff -u -p -r1.5 -r1.5.4.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 08:17:48 -0000 1.5.4.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; }