diff options
Diffstat (limited to 'source4/heimdal/lib/krb5/get_cred.c')
-rw-r--r-- | source4/heimdal/lib/krb5/get_cred.c | 346 |
1 files changed, 322 insertions, 24 deletions
diff --git a/source4/heimdal/lib/krb5/get_cred.c b/source4/heimdal/lib/krb5/get_cred.c index 1fa3f9143e..b404c30f6e 100644 --- a/source4/heimdal/lib/krb5/get_cred.c +++ b/source4/heimdal/lib/krb5/get_cred.c @@ -33,7 +33,7 @@ #include <krb5_locl.h> -RCSID("$Id: get_cred.c,v 1.109 2006/02/03 11:41:02 lha Exp $"); +RCSID("$Id: get_cred.c,v 1.112 2006/06/06 21:22:54 lha Exp $"); /* * Take the `body' and encode it into `padata' using the credentials @@ -142,6 +142,7 @@ init_tgs_req (krb5_context context, krb5_creds *in_creds, krb5_creds *krbtgt, unsigned nonce, + const METHOD_DATA *padata, krb5_keyblock **subkey, TGS_REQ *t, krb5_key_usage usage) @@ -220,12 +221,22 @@ init_tgs_req (krb5_context context, krb5_set_error_string(context, "malloc: out of memory"); goto fail; } - ALLOC_SEQ(t->padata, 1); + ALLOC_SEQ(t->padata, 1 + padata->len); if (t->padata->val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } + { + int i; + for (i = 0; i < padata->len; i++) { + ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]); + if (ret) { + krb5_set_error_string(context, "malloc: out of memory"); + goto fail; + } + } + } { krb5_auth_context ac; @@ -268,7 +279,7 @@ init_tgs_req (krb5_context context, ret = make_pa_tgs_req(context, ac, &t->req_body, - t->padata->val, + &t->padata->val[0], krbtgt, usage); if(ret) { @@ -383,8 +394,10 @@ get_cred_kdc_usage(krb5_context context, krb5_ccache id, krb5_kdc_flags flags, krb5_addresses *addresses, - krb5_creds *in_creds, + krb5_creds *in_creds, krb5_creds *krbtgt, + krb5_principal impersonate_principal, + Ticket *second_ticket, krb5_creds *out_creds, krb5_key_usage usage) { @@ -397,36 +410,91 @@ get_cred_kdc_usage(krb5_context context, unsigned nonce; krb5_keyblock *subkey = NULL; size_t len; - Ticket second_ticket; + Ticket second_ticket_data; int send_to_kdc_flags = 0; + METHOD_DATA padata; krb5_data_zero(&resp); krb5_data_zero(&enc); + padata.val = NULL; + padata.len = 0; krb5_generate_random_block(&nonce, sizeof(nonce)); nonce &= 0xffffffff; - if(flags.b.enc_tkt_in_skey){ + if(flags.b.enc_tkt_in_skey && second_ticket == NULL){ ret = decode_Ticket(in_creds->second_ticket.data, in_creds->second_ticket.length, - &second_ticket, &len); + &second_ticket_data, &len); if(ret) return ret; + second_ticket = &second_ticket_data; + } + + + if (impersonate_principal) { + krb5_crypto crypto; + PA_S4U2Self self; + krb5_data data; + void *buf; + size_t size; + + self.name = impersonate_principal->name; + self.realm = impersonate_principal->realm; + self.auth = estrdup("Kerberos"); + + ret = _krb5_s4u2self_to_checksumdata(context, &self, &data); + if (ret) { + free(self.auth); + goto out; + } + + ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto); + if (ret) { + free(self.auth); + krb5_data_free(&data); + goto out; + } + + ret = krb5_create_checksum(context, + crypto, + KRB5_KU_TGS_IMPERSONATE, + 0, + data.data, + data.length, + &self.cksum); + krb5_crypto_destroy(context, crypto); + krb5_data_free(&data); + if (ret) { + free(self.auth); + goto out; + } + + ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret); + free(self.auth); + free_Checksum(&self.cksum); + if (ret) + goto out; + if (len != size) + krb5_abortx(context, "internal asn1 error"); + + ret = krb5_padata_add(context, &padata, KRB5_PADATA_S4U2SELF, buf, len); + if (ret) + goto out; } ret = init_tgs_req (context, id, addresses, flags, - flags.b.enc_tkt_in_skey ? &second_ticket : NULL, + second_ticket, in_creds, krbtgt, nonce, + &padata, &subkey, &req, usage); - if(flags.b.enc_tkt_in_skey) - free_Ticket(&second_ticket); if (ret) goto out; @@ -475,7 +543,7 @@ again: &krbtgt->addresses, nonce, TRUE, - flags.b.request_anonymous, + TRUE /* flags.b.request_anonymous */, decrypt_tkt_with_subkey, subkey); krb5_free_kdc_rep(context, &rep); @@ -497,6 +565,9 @@ again: } out: + if (second_ticket == &second_ticket_data) + free_Ticket(&second_ticket_data); + free_METHOD_DATA(&padata); krb5_data_free(&resp); krb5_data_free(&enc); if(subkey){ @@ -514,16 +585,20 @@ get_cred_kdc(krb5_context context, krb5_addresses *addresses, krb5_creds *in_creds, krb5_creds *krbtgt, + krb5_principal impersonate_principal, + Ticket *second_ticket, krb5_creds *out_creds) { krb5_error_code ret; ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds, - krbtgt, out_creds, KRB5_KU_TGS_REQ_AUTH); + krbtgt, impersonate_principal, second_ticket, + out_creds, KRB5_KU_TGS_REQ_AUTH); if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) { krb5_clear_error_string (context); ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds, - krbtgt, out_creds, KRB5_KU_AP_REQ_AUTH); + krbtgt, impersonate_principal, second_ticket, + out_creds, KRB5_KU_AP_REQ_AUTH); } return ret; } @@ -533,6 +608,7 @@ get_cred_kdc(krb5_context context, static krb5_error_code get_cred_kdc_la(krb5_context context, krb5_ccache id, krb5_kdc_flags flags, krb5_creds *in_creds, krb5_creds *krbtgt, + krb5_principal impersonate_principal, Ticket *second_ticket, krb5_creds *out_creds) { krb5_error_code ret; @@ -543,7 +619,8 @@ get_cred_kdc_la(krb5_context context, krb5_ccache id, krb5_kdc_flags flags, if(addresses.len == 0) addrs = NULL; ret = get_cred_kdc(context, id, flags, addrs, - in_creds, krbtgt, out_creds); + in_creds, krbtgt, impersonate_principal, second_ticket, + out_creds); krb5_free_addresses(context, &addresses); return ret; } @@ -575,7 +652,7 @@ krb5_get_kdc_cred(krb5_context context, return ret; } ret = get_cred_kdc(context, id, flags, addresses, - in_creds, krbtgt, *out_creds); + in_creds, krbtgt, NULL, NULL, *out_creds); krb5_free_creds (context, krbtgt); if(ret) free(*out_creds); @@ -607,7 +684,17 @@ find_cred(krb5_context context, } tgts++; } - krb5_clear_error_string(context); + { + char *str; + ret = krb5_unparse_name(context, server, &str); + if(ret == 0) { + krb5_set_error_string(context, "Matching credential " + "(%s) not found", str); + free(str); + } else { + krb5_clear_error_string(context); + } + } return KRB5_CC_NOTFOUND; } @@ -650,6 +737,8 @@ get_cred_from_kdc_flags(krb5_context context, krb5_kdc_flags flags, krb5_ccache ccache, krb5_creds *in_creds, + krb5_principal impersonate_principal, + Ticket *second_ticket, krb5_creds **out_creds, krb5_creds ***ret_tgts) { @@ -707,10 +796,16 @@ get_cred_from_kdc_flags(krb5_context context, if (noaddr) ret = get_cred_kdc(context, ccache, flags, NULL, - in_creds, &tgts, *out_creds); + in_creds, &tgts, + impersonate_principal, + second_ticket, + *out_creds); else ret = get_cred_kdc_la(context, ccache, flags, - in_creds, &tgts, *out_creds); + in_creds, &tgts, + impersonate_principal, + second_ticket, + *out_creds); if (ret) { free (*out_creds); *out_creds = NULL; @@ -731,7 +826,7 @@ get_cred_from_kdc_flags(krb5_context context, heim_general_string tgt_inst; ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds, - &tgt, ret_tgts); + NULL, NULL, &tgt, ret_tgts); if(ret) { krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); @@ -776,10 +871,12 @@ get_cred_from_kdc_flags(krb5_context context, &noaddr); if (noaddr) ret = get_cred_kdc (context, ccache, flags, NULL, - in_creds, tgt, *out_creds); + in_creds, tgt, NULL, NULL, + *out_creds); else ret = get_cred_kdc_la(context, ccache, flags, - in_creds, tgt, *out_creds); + in_creds, tgt, NULL, NULL, + *out_creds); if (ret) { free (*out_creds); *out_creds = NULL; @@ -800,7 +897,8 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_kdc_flags f; f.i = flags; return get_cred_from_kdc_flags(context, f, ccache, - in_creds, out_creds, ret_tgts); + in_creds, NULL, NULL, + out_creds, ret_tgts); } krb5_error_code KRB5_LIB_FUNCTION @@ -879,15 +977,18 @@ krb5_get_credentials_with_flags(krb5_context context, } if(options & KRB5_GC_USER_USER) flags.b.enc_tkt_in_skey = 1; + if (flags.b.enc_tkt_in_skey) + options |= KRB5_GC_NO_STORE; + tgts = NULL; ret = get_cred_from_kdc_flags(context, flags, ccache, - in_creds, out_creds, &tgts); + in_creds, NULL, NULL, out_creds, &tgts); for(i = 0; tgts && tgts[i]; i++) { krb5_cc_store_cred(context, ccache, tgts[i]); krb5_free_creds(context, tgts[i]); } free(tgts); - if(ret == 0 && flags.b.enc_tkt_in_skey == 0) + if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0) krb5_cc_store_cred(context, ccache, *out_creds); return ret; } @@ -904,3 +1005,200 @@ krb5_get_credentials(krb5_context context, return krb5_get_credentials_with_flags(context, options, flags, ccache, in_creds, out_creds); } + +struct krb5_get_creds_opt_data { + krb5_principal self; + krb5_flags options; + krb5_enctype enctype; + Ticket *ticket; +}; + + +krb5_error_code KRB5_LIB_FUNCTION +krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt) +{ + *opt = calloc(1, sizeof(**opt)); + if (*opt == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + return 0; +} + +void KRB5_LIB_FUNCTION +krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt) +{ + if (opt->self) + krb5_free_principal(context, opt->self); + memset(opt, 0, sizeof(*opt)); + free(opt); +} + +void KRB5_LIB_FUNCTION +krb5_get_creds_opt_set_options(krb5_context context, + krb5_get_creds_opt opt, + krb5_flags options) +{ + opt->options = options; +} + +void KRB5_LIB_FUNCTION +krb5_get_creds_opt_add_options(krb5_context context, + krb5_get_creds_opt opt, + krb5_flags options) +{ + opt->options |= options; +} + +void KRB5_LIB_FUNCTION +krb5_get_creds_opt_set_enctype(krb5_context context, + krb5_get_creds_opt opt, + krb5_enctype enctype) +{ + opt->enctype = enctype; +} + +krb5_error_code KRB5_LIB_FUNCTION +krb5_get_creds_opt_set_impersonate(krb5_context context, + krb5_get_creds_opt opt, + krb5_const_principal self) +{ + if (opt->self) + krb5_free_principal(context, opt->self); + return krb5_copy_principal(context, self, &opt->self); +} + +krb5_error_code KRB5_LIB_FUNCTION +krb5_get_creds_opt_set_ticket(krb5_context context, + krb5_get_creds_opt opt, + const Ticket *ticket) +{ + if (opt->ticket) { + free_Ticket(opt->ticket); + free(opt->ticket); + opt->ticket = NULL; + } + if (ticket) { + krb5_error_code ret; + + opt->ticket = malloc(sizeof(*ticket)); + if (opt->ticket == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + ret = copy_Ticket(ticket, opt->ticket); + if (ret) { + free(opt->ticket); + opt->ticket = NULL; + krb5_set_error_string(context, "malloc: out of memory"); + return ret; + } + } + return 0; +} + + + +krb5_error_code KRB5_LIB_FUNCTION +krb5_get_creds(krb5_context context, + krb5_get_creds_opt opt, + krb5_ccache ccache, + krb5_const_principal inprinc, + krb5_creds **out_creds) +{ + krb5_kdc_flags flags; + krb5_flags options; + krb5_creds in_creds; + krb5_error_code ret; + krb5_creds **tgts; + krb5_creds *res_creds; + int i; + + memset(&in_creds, 0, sizeof(in_creds)); + in_creds.server = rk_UNCONST(inprinc); + + ret = krb5_cc_get_principal(context, ccache, &in_creds.client); + if (ret) + return ret; + + options = opt->options; + flags.i = 0; + + *out_creds = NULL; + res_creds = calloc(1, sizeof(*res_creds)); + if (res_creds == NULL) { + krb5_free_principal(context, in_creds.client); + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + + if (opt->enctype) { + in_creds.session.keytype = opt->enctype; + options |= KRB5_TC_MATCH_KEYTYPE; + } + + /* + * If we got a credential, check if credential is expired before + * returning it. + */ + ret = krb5_cc_retrieve_cred(context, + ccache, + opt->enctype ? KRB5_TC_MATCH_KEYTYPE : 0, + &in_creds, res_creds); + /* + * If we got a credential, check if credential is expired before + * returning it, but only if KRB5_GC_EXPIRED_OK is not set. + */ + if (ret == 0) { + krb5_timestamp timeret; + + /* If expired ok, don't bother checking */ + if(options & KRB5_GC_EXPIRED_OK) { + *out_creds = res_creds; + krb5_free_principal(context, in_creds.client); + return 0; + } + + krb5_timeofday(context, &timeret); + if(res_creds->times.endtime > timeret) { + *out_creds = res_creds; + krb5_free_principal(context, in_creds.client); + return 0; + } + if(options & KRB5_GC_CACHED) + krb5_cc_remove_cred(context, ccache, 0, res_creds); + + } else if(ret != KRB5_CC_END) { + free(res_creds); + krb5_free_principal(context, in_creds.client); + return ret; + } + free(res_creds); + if(options & KRB5_GC_CACHED) { + krb5_clear_error_string (context); + krb5_free_principal(context, in_creds.client); + return KRB5_CC_NOTFOUND; + } + if(options & KRB5_GC_USER_USER) { + flags.b.enc_tkt_in_skey = 1; + options |= KRB5_GC_NO_STORE; + } + if (options & KRB5_GC_FORWARDABLE) + flags.b.forwardable = 1; + if (options & KRB5_GC_NO_TRANSIT_CHECK) + flags.b.disable_transited_check = 1; + + tgts = NULL; + ret = get_cred_from_kdc_flags(context, flags, ccache, + &in_creds, opt->self, opt->ticket, + out_creds, &tgts); + krb5_free_principal(context, in_creds.client); + for(i = 0; tgts && tgts[i]; i++) { + krb5_cc_store_cred(context, ccache, tgts[i]); + krb5_free_creds(context, tgts[i]); + } + free(tgts); + if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0) + krb5_cc_store_cred(context, ccache, *out_creds); + return ret; +} |