diff options
Diffstat (limited to 'source4/heimdal/kdc/kerberos5.c')
-rw-r--r-- | source4/heimdal/kdc/kerberos5.c | 198 |
1 files changed, 149 insertions, 49 deletions
diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c index bb0fda89e7..e34938447a 100644 --- a/source4/heimdal/kdc/kerberos5.c +++ b/source4/heimdal/kdc/kerberos5.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,7 +33,7 @@ #include "kdc_locl.h" -RCSID("$Id: kerberos5.c,v 1.231 2007/01/04 13:27:27 lha Exp $"); +RCSID("$Id: kerberos5.c 21040 2007-06-10 06:20:59Z lha $"); #define MAX_TIME ((time_t)((1U << 31) - 1)) @@ -70,9 +70,12 @@ set_salt_padata (METHOD_DATA *md, Salt *salt) } } -PA_DATA* -_kdc_find_padata(KDC_REQ *req, int *start, int type) +const PA_DATA* +_kdc_find_padata(const KDC_REQ *req, int *start, int type) { + if (req->padata == NULL) + return NULL; + while(*start < req->padata->len){ (*start)++; if(req->padata->val[*start - 1].padata_type == type) @@ -431,7 +434,8 @@ get_pa_etype_info(krb5_context context, ret = krb5_unparse_name(context, client->principal, &name); if (ret) name = rk_UNCONST("<unparse_name failed>"); - kdc_log(context, config, 0, "internal error in get_pa_etype_info(%s): %d != %d", + kdc_log(context, config, 0, + "internal error in get_pa_etype_info(%s): %d != %d", name, n, pa.len); if (ret == 0) free(name); @@ -689,11 +693,11 @@ log_as_req(krb5_context context, } { - char _str[128]; + char fixedstr[128]; unparse_flags(KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(), - _str, sizeof(_str)); - if(*_str) - kdc_log(context, config, 2, "Requested flags: %s", _str); + fixedstr, sizeof(fixedstr)); + if(*fixedstr) + kdc_log(context, config, 2, "Requested flags: %s", fixedstr); } } @@ -870,7 +874,7 @@ send_pac_p(krb5_context context, KDC_REQ *req) { krb5_error_code ret; PA_PAC_REQUEST pacreq; - PA_DATA *pa; + const PA_DATA *pa; int i = 0; pa = _kdc_find_padata(req, &i, KRB5_PADATA_PA_PAC_REQUEST); @@ -909,32 +913,37 @@ _kdc_as_rep(krb5_context context, KDCOptions f = b->kdc_options; hdb_entry_ex *client = NULL, *server = NULL; krb5_enctype cetype, setype, sessionetype; + krb5_data e_data; EncTicketPart et; EncKDCRepPart ek; 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_data e_data; krb5_crypto crypto; Key *ckey, *skey; EncryptionKey *reply_key; + int flags = 0; #ifdef PKINIT pk_client_params *pkp = NULL; #endif memset(&rep, 0, sizeof(rep)); - memset(&e_data, 0, sizeof(e_data)); + krb5_data_zero(&e_data); + + if (f.canonicalize) + flags |= HDB_F_CANON; if(b->sname == NULL){ ret = KRB5KRB_ERR_GENERIC; e_text = "No server in request"; } else{ - _krb5_principalname2krb5_principal (context, - &server_princ, - *(b->sname), - b->realm); - ret = krb5_unparse_name(context, server_princ, &server_name); + ret = _krb5_principalname2krb5_principal (context, + &server_princ, + *(b->sname), + b->realm); + if (ret == 0) + ret = krb5_unparse_name(context, server_princ, &server_name); } if (ret) { kdc_log(context, config, 0, @@ -946,10 +955,26 @@ _kdc_as_rep(krb5_context context, ret = KRB5KRB_ERR_GENERIC; e_text = "No client in request"; } else { - _krb5_principalname2krb5_principal (context, - &client_princ, - *(b->cname), - b->realm); + + if (b->cname->name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { + if (b->cname->name_string.len != 1) { + kdc_log(context, config, 0, + "AS-REQ malformed canon request from %s", from); + ret = KRB5_PARSE_MALFORMED; + goto out; + } + ret = krb5_parse_name(context, b->cname->name_string.val[0], + &client_princ); + if (ret) + goto out; + } else { + ret = _krb5_principalname2krb5_principal (context, + &client_princ, + *(b->cname), + b->realm); + if (ret) + goto out; + } ret = krb5_unparse_name(context, client_princ, &client_name); } if (ret) { @@ -962,7 +987,7 @@ _kdc_as_rep(krb5_context context, client_name, from, server_name); ret = _kdc_db_fetch(context, config, client_princ, - HDB_F_GET_CLIENT, NULL, &client); + HDB_F_GET_CLIENT | flags, NULL, &client); if(ret){ kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, krb5_get_err_text(context, ret)); @@ -996,7 +1021,7 @@ _kdc_as_rep(krb5_context context, if(req->padata){ int i; - PA_DATA *pa; + const PA_DATA *pa; int found_pa = 0; log_patypes(context, config, req->padata); @@ -1041,7 +1066,7 @@ _kdc_as_rep(krb5_context context, kdc_log(context, config, 0, "%s", e_text); pkp = NULL; - goto ts_enc; + goto out; } found_pa = 1; et.flags.pre_authent = 1; @@ -1169,6 +1194,8 @@ _kdc_as_rep(krb5_context context, (unsigned)abs(kdc_time - p.patimestamp), context->max_skew, client_name); +#if 1 + /* This code is from samba, needs testing */ /* * the following is needed to make windows clients * to retry using the timestamp in the error message @@ -1177,6 +1204,9 @@ _kdc_as_rep(krb5_context context, * is present... */ e_text = NULL; +#else + e_text = "Too large time skew"; +#endif goto out; } et.flags.pre_authent = 1; @@ -1227,6 +1257,12 @@ _kdc_as_rep(krb5_context context, pa->padata_type = KRB5_PADATA_PK_AS_REQ; pa->padata_value.length = 0; pa->padata_value.data = NULL; + + ret = realloc_method_data(&method_data); + pa = &method_data.val[method_data.len-1]; + pa->padata_type = KRB5_PADATA_PK_AS_REQ_WIN; + pa->padata_value.length = 0; + pa->padata_value.data = NULL; #endif /* @@ -1253,12 +1289,12 @@ _kdc_as_rep(krb5_context context, e_data.data = buf; e_data.length = len; e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ", + ret = KRB5KDC_ERR_PREAUTH_REQUIRED; kdc_log(context, config, 0, "No preauth found, returning PREAUTH-REQUIRED -- %s", client_name); - goto out; } @@ -1283,45 +1319,57 @@ _kdc_as_rep(krb5_context context, if(ret) goto out; + /* + * Select a session enctype from the list of the crypto systems + * supported enctype, is supported by the client and is one of the + * enctype of the enctype of the krbtgt. + * + * The later is used as a hint what enctype all KDC are supporting + * to make sure a newer version of KDC wont generate a session + * enctype that and older version of a KDC in the same realm can't + * decrypt. + * + * But if the KDC admin is paranoid and doesn't want to have "no + * the best" enctypes on the krbtgt, lets save the best pick from + * the client list and hope that that will work for any other + * KDCs. + */ { const krb5_enctype *p; - int i, j, y; + krb5_enctype clientbest = ETYPE_NULL; + int i, j; p = krb5_kerberos_enctypes(context); sessionetype = ETYPE_NULL; for (i = 0; p[i] != ETYPE_NULL && sessionetype == ETYPE_NULL; i++) { - /* check it's valid */ if (krb5_enctype_valid(context, p[i]) != 0) continue; - /* check if the client supports it */ for (j = 0; j < b->etype.len && sessionetype == ETYPE_NULL; j++) { - if (p[i] == b->etype.val[j]) { - /* - * if the server (krbtgt) has explicit etypes, - * check if it also supports it - */ - if (server->entry.etypes) { - for (y = 0; y < server->entry.etypes->len; y++) { - if (p[i] == server->entry.etypes->val[y]) { - sessionetype = p[i]; - break; - } - } - } else { - sessionetype = p[i]; - break; - } - } + Key *dummy; + /* check with client */ + if (p[i] != b->etype.val[j]) + continue; + /* save best of union of { client, crypto system } */ + if (clientbest == ETYPE_NULL) + clientbest = p[i]; + /* check with krbtgt */ + ret = hdb_enctype2key(context, &server->entry, p[i], &dummy); + if (ret) + continue; + sessionetype = p[i]; } } - if (sessionetype == ETYPE_NULL) { - kdc_log(context, config, 0, + /* if krbtgt had no shared keys with client, pick clients best */ + if (clientbest != ETYPE_NULL && sessionetype == ETYPE_NULL) { + sessionetype = clientbest; + } else if (sessionetype == ETYPE_NULL) { + kdc_log(context, config, 0, "Client (%s) from %s has no common enctypes with KDC" - "to use for the session key", - client_name, from); + "to use for the session key", + client_name, from); goto out; } } @@ -1534,6 +1582,58 @@ _kdc_as_rep(krb5_context context, set_salt_padata (rep.padata, ckey->salt); + /* Add signing of alias referral */ + if (f.canonicalize) { + PA_ClientCanonicalized canon; + krb5_data data; + PA_DATA pa; + krb5_crypto crypto; + size_t len; + + memset(&canon, 0, sizeof(canon)); + + canon.names.requested_name = *b->cname; + canon.names.real_name = client->entry.principal->name; + + ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length, + &canon.names, &len, ret); + if (ret) + goto out; + if (data.length != len) + krb5_abortx(context, "internal asn.1 error"); + + /* sign using "returned session key" */ + ret = krb5_crypto_init(context, &et.key, 0, &crypto); + if (ret) { + free(data.data); + goto out; + } + + ret = krb5_create_checksum(context, crypto, + KRB5_KU_CANONICALIZED_NAMES, 0, + data.data, data.length, + &canon.canon_checksum); + free(data.data); + krb5_crypto_destroy(context, crypto); + if (ret) + goto out; + + ASN1_MALLOC_ENCODE(PA_ClientCanonicalized, data.data, data.length, + &canon, &len, ret); + free_Checksum(&canon.canon_checksum); + if (ret) + goto out; + if (data.length != len) + krb5_abortx(context, "internal asn.1 error"); + + pa.padata_type = KRB5_PADATA_CLIENT_CANONICALIZED; + pa.padata_value = data; + ret = add_METHOD_DATA(rep.padata, &pa); + free(data.data); + if (ret) + goto out; + } + if (rep.padata->len == 0) { free(rep.padata); rep.padata = NULL; |