diff options
Diffstat (limited to 'lib/krb5/get_cred.c')
| -rw-r--r-- | lib/krb5/get_cred.c | 963 | 
1 files changed, 596 insertions, 367 deletions
| diff --git a/lib/krb5/get_cred.c b/lib/krb5/get_cred.c index ce0ec6d29283..e3bb23a2e9d7 100644 --- a/lib/krb5/get_cred.c +++ b/lib/krb5/get_cred.c @@ -1,39 +1,45 @@  /* - * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan - * (Royal Institute of Technology, Stockholm, Sweden).  - * All rights reserved.  + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved.   * - * Redistribution and use in source and binary forms, with or without  - * modification, are permitted provided that the following conditions  - * are met:  + * Portions Copyright (c) 2009 Apple Inc. All rights reserved.   * - * 1. Redistributions of source code must retain the above copyright  - *    notice, this list of conditions and the following disclaimer.  + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met:   * - * 2. Redistributions in binary form must reproduce the above copyright  - *    notice, this list of conditions and the following disclaimer in the  - *    documentation and/or other materials provided with the distribution.  + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer.   * - * 3. Neither the name of the Institute nor the names of its contributors  - *    may be used to endorse or promote products derived from this software  - *    without specific prior written permission.  + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution.   * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND  - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  - * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE  - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS  - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)  - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  - * SUCH DAMAGE.  + * 3. Neither the name of the Institute nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE.   */ -#include <krb5_locl.h> +#include "krb5_locl.h" +#include <assert.h> -RCSID("$Id: get_cred.c 21668 2007-07-22 11:28:05Z lha $"); +static krb5_error_code +get_cred_kdc_capath(krb5_context, krb5_kdc_flags, +		    krb5_ccache, krb5_creds *, krb5_principal, +		    Ticket *, krb5_creds **, krb5_creds ***);  /*   * Take the `body' and encode it into `padata' using the credentials @@ -41,16 +47,15 @@ RCSID("$Id: get_cred.c 21668 2007-07-22 11:28:05Z lha $");   */  static krb5_error_code -make_pa_tgs_req(krb5_context context,  +make_pa_tgs_req(krb5_context context,  		krb5_auth_context ac,  		KDC_REQ_BODY *body,  		PA_DATA *padata, -		krb5_creds *creds, -		krb5_key_usage usage) +		krb5_creds *creds)  {      u_char *buf;      size_t buf_size; -    size_t len; +    size_t len = 0;      krb5_data in_data;      krb5_error_code ret; @@ -65,8 +70,7 @@ make_pa_tgs_req(krb5_context context,      ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds,  				&padata->padata_value,  				KRB5_KU_TGS_REQ_AUTH_CKSUM, -				usage -				/* KRB5_KU_TGS_REQ_AUTH */); +				KRB5_KU_TGS_REQ_AUTH);   out:      free (buf);      if(ret) @@ -83,10 +87,10 @@ static krb5_error_code  set_auth_data (krb5_context context,  	       KDC_REQ_BODY *req_body,  	       krb5_authdata *authdata, -	       krb5_keyblock *key) +	       krb5_keyblock *subkey)  {      if(authdata->len) { -	size_t len, buf_size; +	size_t len = 0, buf_size;  	unsigned char *buf;  	krb5_crypto crypto;  	krb5_error_code ret; @@ -101,20 +105,20 @@ set_auth_data (krb5_context context,  	ALLOC(req_body->enc_authorization_data, 1);  	if (req_body->enc_authorization_data == NULL) {  	    free (buf); -	    krb5_set_error_string(context, "malloc: out of memory"); +	    krb5_set_error_message(context, ENOMEM, +				   N_("malloc: out of memory", ""));  	    return ENOMEM;  	} -	ret = krb5_crypto_init(context, key, 0, &crypto); +	ret = krb5_crypto_init(context, subkey, 0, &crypto);  	if (ret) {  	    free (buf);  	    free (req_body->enc_authorization_data);  	    req_body->enc_authorization_data = NULL;  	    return ret;  	} -	krb5_encrypt_EncryptedData(context,  +	krb5_encrypt_EncryptedData(context,  				   crypto, -				   KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,  -				   /* KRB5_KU_TGS_REQ_AUTH_DAT_SESSION? */ +				   KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,  				   buf,  				   len,  				   0, @@ -125,7 +129,7 @@ set_auth_data (krb5_context context,  	req_body->enc_authorization_data = NULL;      }      return 0; -}     +}  /*   * Create a tgs-req in `t' with `addresses', `flags', `second_ticket' @@ -144,9 +148,9 @@ init_tgs_req (krb5_context context,  	      unsigned nonce,  	      const METHOD_DATA *padata,  	      krb5_keyblock **subkey, -	      TGS_REQ *t, -	      krb5_key_usage usage) +	      TGS_REQ *t)  { +    krb5_auth_context ac = NULL;      krb5_error_code ret = 0;      memset(t, 0, sizeof(*t)); @@ -156,15 +160,17 @@ init_tgs_req (krb5_context context,  	ALLOC_SEQ(&t->req_body.etype, 1);  	if(t->req_body.etype.val == NULL) {  	    ret = ENOMEM; -	    krb5_set_error_string(context, "malloc: out of memory"); +	    krb5_set_error_message(context, ret, +				   N_("malloc: out of memory", ""));  	    goto fail;  	}  	t->req_body.etype.val[0] = in_creds->session.keytype;      } else { -	ret = krb5_init_etype(context,  -			      &t->req_body.etype.len,  -			      &t->req_body.etype.val,  -			      NULL); +	ret = _krb5_init_etype(context, +			       KRB5_PDU_TGS_REQUEST, +			       &t->req_body.etype.len, +			       &t->req_body.etype.val, +			       NULL);      }      if (ret)  	goto fail; @@ -176,7 +182,7 @@ init_tgs_req (krb5_context context,      ALLOC(t->req_body.sname, 1);      if (t->req_body.sname == NULL) {  	ret = ENOMEM; -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));  	goto fail;      } @@ -192,107 +198,83 @@ init_tgs_req (krb5_context context,      ALLOC(t->req_body.till, 1);      if(t->req_body.till == NULL){  	ret = ENOMEM; -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));  	goto fail;      }      *t->req_body.till = in_creds->times.endtime; -     +      t->req_body.nonce = nonce;      if(second_ticket){  	ALLOC(t->req_body.additional_tickets, 1);  	if (t->req_body.additional_tickets == NULL) {  	    ret = ENOMEM; -	    krb5_set_error_string(context, "malloc: out of memory"); +	    krb5_set_error_message(context, ret, +				   N_("malloc: out of memory", ""));  	    goto fail;  	}  	ALLOC_SEQ(t->req_body.additional_tickets, 1);  	if (t->req_body.additional_tickets->val == NULL) {  	    ret = ENOMEM; -	    krb5_set_error_string(context, "malloc: out of memory"); +	    krb5_set_error_message(context, ret, +				   N_("malloc: out of memory", ""));  	    goto fail;  	} -	ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);  +	ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);  	if (ret)  	    goto fail;      }      ALLOC(t->padata, 1);      if (t->padata == NULL) {  	ret = ENOMEM; -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));  	goto fail;      }      ALLOC_SEQ(t->padata, 1 + padata->len);      if (t->padata->val == NULL) {  	ret = ENOMEM; -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));  	goto fail;      }      { -	int i; +	size_t 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"); +		krb5_set_error_message(context, ret, +				       N_("malloc: out of memory", ""));  		goto fail;  	    }  	}      } -    { -	krb5_auth_context ac; -	krb5_keyblock *key = NULL; +    ret = krb5_auth_con_init(context, &ac); +    if(ret) +	goto fail; -	ret = krb5_auth_con_init(context, &ac); -	if(ret) -	    goto fail; +    ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session); +    if (ret) +	goto fail; -	if (krb5_config_get_bool_default(context, NULL, FALSE, -					 "realms", -					 krbtgt->server->realm, -					 "tgs_require_subkey", -					 NULL)) -	{ -	    ret = krb5_generate_subkey (context, &krbtgt->session, &key); -	    if (ret) { -		krb5_auth_con_free (context, ac); -		goto fail; -	    } +    ret = set_auth_data (context, &t->req_body, &in_creds->authdata, +			 ac->local_subkey); +    if (ret) +	goto fail; -	    ret = krb5_auth_con_setlocalsubkey(context, ac, key); -	    if (ret) { -		if (key) -		    krb5_free_keyblock (context, key); -		krb5_auth_con_free (context, ac); -		goto fail; -	    } -	} +    ret = make_pa_tgs_req(context, +			  ac, +			  &t->req_body, +			  &t->padata->val[0], +			  krbtgt); +    if(ret) +	goto fail; -	ret = set_auth_data (context, &t->req_body, &in_creds->authdata, -			     key ? key : &krbtgt->session); -	if (ret) { -	    if (key) -		krb5_free_keyblock (context, key); -	    krb5_auth_con_free (context, ac); -	    goto fail; -	} +    ret = krb5_auth_con_getlocalsubkey(context, ac, subkey); +    if (ret) +	goto fail; -	ret = make_pa_tgs_req(context, -			      ac, -			      &t->req_body,  -			      &t->padata->val[0], -			      krbtgt, -			      usage); -	if(ret) { -	    if (key) -		krb5_free_keyblock (context, key); -	    krb5_auth_con_free(context, ac); -	    goto fail; -	} -	*subkey = key; -	 -	krb5_auth_con_free(context, ac); -    }  fail: +    if (ac) +	krb5_auth_con_free(context, ac);      if (ret) {  	t->req_body.addresses = NULL;  	free_TGS_REQ (t); @@ -315,7 +297,7 @@ _krb5_get_krbtgt(krb5_context context,      if (ret)  	return ret; -    ret = krb5_make_principal(context,  +    ret = krb5_make_principal(context,  			      &tmp_cred.server,  			      realm,  			      KRB5_TGS_NAME, @@ -338,29 +320,27 @@ _krb5_get_krbtgt(krb5_context context,  }  /* DCE compatible decrypt proc */ -static krb5_error_code +static krb5_error_code KRB5_CALLCONV  decrypt_tkt_with_subkey (krb5_context context,  			 krb5_keyblock *key,  			 krb5_key_usage usage, -			 krb5_const_pointer subkey, +			 krb5_const_pointer skey,  			 krb5_kdc_rep *dec_rep)  { -    krb5_error_code ret; +    const krb5_keyblock *subkey = skey; +    krb5_error_code ret = 0;      krb5_data data;      size_t size;      krb5_crypto crypto; -     -    ret = krb5_crypto_init(context, key, 0, &crypto); -    if (ret) -	return ret; -    ret = krb5_decrypt_EncryptedData (context, -				      crypto, -				      usage, -				      &dec_rep->kdc_rep.enc_part, -				      &data); -    krb5_crypto_destroy(context, crypto); -    if(ret && subkey){ -	/* DCE compat -- try to decrypt with subkey */ + +    assert(usage == 0); + +    krb5_data_zero(&data); + +    /* +     * start out with trying with subkey if we have one +     */ +    if (subkey) {  	ret = krb5_crypto_init(context, subkey, 0, &crypto);  	if (ret)  	    return ret; @@ -369,37 +349,59 @@ decrypt_tkt_with_subkey (krb5_context context,  					  KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,  					  &dec_rep->kdc_rep.enc_part,  					  &data); +	/* +	 * If the is Windows 2000 DC, we need to retry with key usage +	 * 8 when doing ARCFOUR. +	 */ +	if (ret && subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) { +	    ret = krb5_decrypt_EncryptedData(context, +					     crypto, +					     8, +					     &dec_rep->kdc_rep.enc_part, +					     &data); +	} +	krb5_crypto_destroy(context, crypto); +    } +    if (subkey == NULL || ret) { +	ret = krb5_crypto_init(context, key, 0, &crypto); +	if (ret) +	    return ret; +	ret = krb5_decrypt_EncryptedData (context, +					  crypto, +					  KRB5_KU_TGS_REP_ENC_PART_SESSION, +					  &dec_rep->kdc_rep.enc_part, +					  &data);  	krb5_crypto_destroy(context, crypto);      }      if (ret)  	return ret; -     -    ret = krb5_decode_EncASRepPart(context, -				   data.data, + +    ret = decode_EncASRepPart(data.data, +			      data.length, +			      &dec_rep->enc_part, +			      &size); +    if (ret) +	ret = decode_EncTGSRepPart(data.data,  				   data.length, -				   &dec_rep->enc_part,  +				   &dec_rep->enc_part,  				   &size);      if (ret) -	ret = krb5_decode_EncTGSRepPart(context, -					data.data, -					data.length, -					&dec_rep->enc_part,  -					&size); +      krb5_set_error_message(context, ret, +			     N_("Failed to decode encpart in ticket", ""));      krb5_data_free (&data);      return ret;  }  static krb5_error_code -get_cred_kdc_usage(krb5_context context,  -		   krb5_ccache id,  -		   krb5_kdc_flags flags, -		   krb5_addresses *addresses,  -		   krb5_creds *in_creds, -		   krb5_creds *krbtgt, -		   krb5_principal impersonate_principal, -		   Ticket *second_ticket, -		   krb5_creds *out_creds, -		   krb5_key_usage usage) +get_cred_kdc(krb5_context context, +	     krb5_ccache id, +	     krb5_kdc_flags flags, +	     krb5_addresses *addresses, +	     krb5_creds *in_creds, +	     krb5_creds *krbtgt, +	     krb5_principal impersonate_principal, +	     Ticket *second_ticket, +	     krb5_creds *out_creds)  {      TGS_REQ req;      krb5_data enc; @@ -409,10 +411,10 @@ get_cred_kdc_usage(krb5_context context,      krb5_error_code ret;      unsigned nonce;      krb5_keyblock *subkey = NULL; -    size_t len; +    size_t len = 0;      Ticket second_ticket_data;      METHOD_DATA padata; -     +      krb5_data_zero(&resp);      krb5_data_zero(&enc);      padata.val = NULL; @@ -420,10 +422,10 @@ get_cred_kdc_usage(krb5_context context,      krb5_generate_random_block(&nonce, sizeof(nonce));      nonce &= 0xffffffff; -     +      if(flags.b.enc_tkt_in_skey && second_ticket == NULL){ -	ret = decode_Ticket(in_creds->second_ticket.data,  -			    in_creds->second_ticket.length,  +	ret = decode_Ticket(in_creds->second_ticket.data, +			    in_creds->second_ticket.length,  			    &second_ticket_data, &len);  	if(ret)  	    return ret; @@ -436,12 +438,12 @@ get_cred_kdc_usage(krb5_context context,  	PA_S4U2Self self;  	krb5_data data;  	void *buf; -	size_t size; +	size_t size = 0;  	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); @@ -460,7 +462,7 @@ get_cred_kdc_usage(krb5_context context,  				   KRB5_KU_OTHER_CKSUM,  				   0,  				   data.data, -				   data.length,  +				   data.length,  				   &self.cksum);  	krb5_crypto_destroy(context, crypto);  	krb5_data_free(&data); @@ -476,8 +478,8 @@ get_cred_kdc_usage(krb5_context context,  	    goto out;  	if (len != size)  	    krb5_abortx(context, "internal asn1 error"); -	 -	ret = krb5_padata_add(context, &padata, KRB5_PADATA_S4U2SELF, buf, len); + +	ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);  	if (ret)  	    goto out;      } @@ -491,14 +493,13 @@ get_cred_kdc_usage(krb5_context context,  			krbtgt,  			nonce,  			&padata, -			&subkey,  -			&req, -			usage); +			&subkey, +			&req);      if (ret)  	goto out;      ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret); -    if (ret)  +    if (ret)  	goto out;      if(enc.length != len)  	krb5_abortx(context, "internal error in ASN.1 encoder"); @@ -526,42 +527,48 @@ get_cred_kdc_usage(krb5_context context,  	goto out;      memset(&rep, 0, sizeof(rep)); -    if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0){ -	ret = krb5_copy_principal(context,  -				  in_creds->client,  +    if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) { +	unsigned eflags = 0; + +	ret = krb5_copy_principal(context, +				  in_creds->client,  				  &out_creds->client);  	if(ret) -	    goto out; -	ret = krb5_copy_principal(context,  -				  in_creds->server,  +	    goto out2; +	ret = krb5_copy_principal(context, +				  in_creds->server,  				  &out_creds->server);  	if(ret) -	    goto out; +	    goto out2;  	/* this should go someplace else */  	out_creds->times.endtime = in_creds->times.endtime; +	/* XXX should do better testing */ +	if (flags.b.constrained_delegation || impersonate_principal) +	    eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; +  	ret = _krb5_extract_ticket(context,  				   &rep,  				   out_creds,  				   &krbtgt->session,  				   NULL, -				   KRB5_KU_TGS_REP_ENC_PART_SESSION, +				   0,  				   &krbtgt->addresses,  				   nonce, -				   EXTRACT_TICKET_ALLOW_CNAME_MISMATCH| -				   EXTRACT_TICKET_ALLOW_SERVER_MISMATCH, +				   eflags,  				   decrypt_tkt_with_subkey,  				   subkey); +    out2:  	krb5_free_kdc_rep(context, &rep);      } else if(krb5_rd_error(context, &resp, &error) == 0) {  	ret = krb5_error_from_rd_error(context, &error, in_creds);  	krb5_free_error_contents(context, &error); -    } else if(resp.data && ((char*)resp.data)[0] == 4) { +    } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {  	ret = KRB5KRB_AP_ERR_V4_REPLY; -	krb5_clear_error_string(context); +	krb5_clear_error_message(context);      } else {  	ret = KRB5KRB_AP_ERR_MSG_TYPE; -	krb5_clear_error_string(context); +	krb5_clear_error_message(context);      }  out: @@ -570,62 +577,58 @@ out:      free_METHOD_DATA(&padata);      krb5_data_free(&resp);      krb5_data_free(&enc); -    if(subkey){ -	krb5_free_keyblock_contents(context, subkey); -	free(subkey); -    } +    if(subkey) +	krb5_free_keyblock(context, subkey);      return ret; -     +  } +/* + * same as above, just get local addresses first if the krbtgt have + * them and the realm is not addressless + */ +  static krb5_error_code -get_cred_kdc(krb5_context context,  -	     krb5_ccache id,  -	     krb5_kdc_flags flags, -	     krb5_addresses *addresses,  -	     krb5_creds *in_creds,  -	     krb5_creds *krbtgt, -	     krb5_principal impersonate_principal, -	     Ticket *second_ticket, -	     krb5_creds *out_creds) +get_cred_kdc_address(krb5_context context, +		     krb5_ccache id, +		     krb5_kdc_flags flags, +		     krb5_addresses *addrs, +		     krb5_creds *in_creds, +		     krb5_creds *krbtgt, +		     krb5_principal impersonate_principal, +		     Ticket *second_ticket, +		     krb5_creds *out_creds)  {      krb5_error_code ret; +    krb5_addresses addresses = { 0, NULL }; -    ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds, -			     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, impersonate_principal, second_ticket, -				 out_creds, KRB5_KU_AP_REQ_AUTH); -    } -    return ret; -} +    /* +     * Inherit the address-ness of the krbtgt if the address is not +     * specified. +     */ + +    if (addrs == NULL && krbtgt->addresses.len != 0) { +	krb5_boolean noaddr; -/* same as above, just get local addresses first */ +	krb5_appdefault_boolean(context, NULL, krbtgt->server->realm, +				"no-addresses", FALSE, &noaddr); -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; -    krb5_addresses addresses, *addrs = &addresses; -     -    krb5_get_all_client_addrs(context, &addresses); -    /* XXX this sucks. */ -    if(addresses.len == 0) -	addrs = NULL; -    ret = get_cred_kdc(context, id, flags, addrs,  -		       in_creds, krbtgt, impersonate_principal, second_ticket, -		       out_creds); +	if (!noaddr) { +	    krb5_get_all_client_addrs(context, &addresses); +	    /* XXX this sucks. */ +	    addrs = &addresses; +	    if(addresses.len == 0) +		addrs = NULL; +	} +    } +    ret = get_cred_kdc(context, id, flags, addrs, in_creds, +		       krbtgt, impersonate_principal, +		       second_ticket, out_creds);      krb5_free_addresses(context, &addresses);      return ret;  } -krb5_error_code KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  krb5_get_kdc_cred(krb5_context context,  		  krb5_ccache id,  		  krb5_kdc_flags flags, @@ -640,7 +643,8 @@ krb5_get_kdc_cred(krb5_context context,      *out_creds = calloc(1, sizeof(**out_creds));      if(*out_creds == NULL) { -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM;      }      ret = _krb5_get_krbtgt (context, @@ -649,29 +653,34 @@ krb5_get_kdc_cred(krb5_context context,  			    &krbtgt);      if(ret) {  	free(*out_creds); +	*out_creds = NULL;  	return ret;      } -    ret = get_cred_kdc(context, id, flags, addresses,  +    ret = get_cred_kdc(context, id, flags, addresses,  		       in_creds, krbtgt, NULL, NULL, *out_creds);      krb5_free_creds (context, krbtgt); -    if(ret) +    if(ret) {  	free(*out_creds); +	*out_creds = NULL; +    }      return ret;  } -static void -not_found(krb5_context context, krb5_const_principal p) +static int +not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)  {      krb5_error_code ret;      char *str;      ret = krb5_unparse_name(context, p, &str);      if(ret) { -	krb5_clear_error_string(context); -	return; +	krb5_clear_error_message(context); +	return code;      } -    krb5_set_error_string(context, "Matching credential (%s) not found", str); +    krb5_set_error_message(context, code, +			   N_("Matching credential (%s) not found", ""), str);      free(str); +    return code;  }  static krb5_error_code @@ -686,24 +695,23 @@ find_cred(krb5_context context,      krb5_cc_clear_mcred(&mcreds);      mcreds.server = server; -    ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,  +    ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,  				&mcreds, out_creds);      if(ret == 0)  	return 0;      while(tgts && *tgts){ -	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,  +	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,  			      &mcreds, *tgts)){  	    ret = krb5_copy_creds_contents(context, *tgts, out_creds);  	    return ret;  	}  	tgts++;      } -    not_found(context, server); -    return KRB5_CC_NOTFOUND; +    return not_found(context, server, KRB5_CC_NOTFOUND);  }  static krb5_error_code -add_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt) +add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)  {      int i;      krb5_error_code ret; @@ -712,7 +720,8 @@ add_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt)      for(i = 0; tmp && tmp[i]; i++); /* XXX */      tmp = realloc(tmp, (i+2)*sizeof(*tmp));      if(tmp == NULL) { -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM;      }      *tgts = tmp; @@ -721,34 +730,21 @@ add_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt)      return ret;  } -/* -get_cred(server) -	creds = cc_get_cred(server) -	if(creds) return creds -	tgt = cc_get_cred(krbtgt/server_realm@any_realm) -	if(tgt) -		return get_cred_tgt(server, tgt) -	if(client_realm == server_realm) -		return NULL -	tgt = get_cred(krbtgt/server_realm@client_realm) -	while(tgt_inst != server_realm) -		tgt = get_cred(krbtgt/server_realm@tgt_inst) -	return get_cred_tgt(server, tgt) -	*/ -  static krb5_error_code -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) +get_cred_kdc_capath_worker(krb5_context context, +                           krb5_kdc_flags flags, +                           krb5_ccache ccache, +                           krb5_creds *in_creds, +                           krb5_const_realm try_realm, +                           krb5_principal impersonate_principal, +                           Ticket *second_ticket, +                           krb5_creds **out_creds, +                           krb5_creds ***ret_tgts)  {      krb5_error_code ret;      krb5_creds *tgt, tmp_creds; -    krb5_const_realm client_realm, server_realm, try_realm; +    krb5_const_realm client_realm, server_realm; +    int ok_as_delegate = 1;      *out_creds = NULL; @@ -759,24 +755,11 @@ get_cred_from_kdc_flags(krb5_context context,      if(ret)  	return ret; -    try_realm = krb5_config_get_string(context, NULL, "capaths",  -				       client_realm, server_realm, NULL); -     -#if 1 -    /* XXX remove in future release */ -    if(try_realm == NULL) -	try_realm = krb5_config_get_string(context, NULL, "libdefaults",  -					   "capath", server_realm, NULL); -#endif - -    if (try_realm == NULL) -	try_realm = client_realm; -      ret = krb5_make_principal(context,  			      &tmp_creds.server,  			      try_realm,  			      KRB5_TGS_NAME, -			      server_realm,  +			      server_realm,  			      NULL);      if(ret){  	krb5_free_principal(context, tmp_creds.client); @@ -784,36 +767,30 @@ get_cred_from_kdc_flags(krb5_context context,      }      {  	krb5_creds tgts; -	/* XXX try krb5_cc_retrieve_cred first? */ -	ret = find_cred(context, ccache, tmp_creds.server,  + +	ret = find_cred(context, ccache, tmp_creds.server,  			*ret_tgts, &tgts);  	if(ret == 0){ +	    /* only allow implicit ok_as_delegate if the realm is the clients realm */ +	    if (strcmp(try_realm, client_realm) != 0 || strcmp(try_realm, server_realm) != 0) +		ok_as_delegate = tgts.flags.b.ok_as_delegate; +  	    *out_creds = calloc(1, sizeof(**out_creds));  	    if(*out_creds == NULL) { -		krb5_set_error_string(context, "malloc: out of memory");  		ret = ENOMEM; +		krb5_set_error_message(context, ret, +				       N_("malloc: out of memory", ""));  	    } else { -		krb5_boolean noaddr; - -		krb5_appdefault_boolean(context, NULL, tgts.server->realm, -					"no-addresses", FALSE, &noaddr); - -		if (noaddr) -		    ret = get_cred_kdc(context, ccache, flags, NULL, -				       in_creds, &tgts, -				       impersonate_principal,  -				       second_ticket, -				       *out_creds); -		else -		    ret = get_cred_kdc_la(context, ccache, flags,  -					  in_creds, &tgts,  -					  impersonate_principal,  -					  second_ticket, -					  *out_creds); +		ret = get_cred_kdc_address(context, ccache, flags, NULL, +					   in_creds, &tgts, +					   impersonate_principal, +					   second_ticket, +					   *out_creds);  		if (ret) {  		    free (*out_creds);  		    *out_creds = NULL; -		} +		} else if (ok_as_delegate == 0) +		    (*out_creds)->flags.b.ok_as_delegate = 0;  	    }  	    krb5_free_cred_contents(context, &tgts);  	    krb5_free_principal(context, tmp_creds.server); @@ -821,22 +798,30 @@ get_cred_from_kdc_flags(krb5_context context,  	    return ret;  	}      } -    if(krb5_realm_compare(context, in_creds->client, in_creds->server)) { -	not_found(context, in_creds->server); -	return KRB5_CC_NOTFOUND; -    } +    if(krb5_realm_compare(context, in_creds->client, in_creds->server)) +	return not_found(context, in_creds->server, KRB5_CC_NOTFOUND); +      /* XXX this can loop forever */      while(1){  	heim_general_string tgt_inst; -	ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds,  -				      NULL, NULL, &tgt, ret_tgts); +	ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds, +				  NULL, NULL, &tgt, ret_tgts);  	if(ret) {  	    krb5_free_principal(context, tmp_creds.server);  	    krb5_free_principal(context, tmp_creds.client);  	    return ret;  	} -	ret = add_cred(context, ret_tgts, tgt); +	/* +	 * if either of the chain or the ok_as_delegate was stripped +	 * by the kdc, make sure we strip it too. +	 */ +	if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) { +	    ok_as_delegate = 0; +	    tgt->flags.b.ok_as_delegate = 0; +	} + +	ret = add_cred(context, tgt, ret_tgts);  	if(ret) {  	    krb5_free_principal(context, tmp_creds.server);  	    krb5_free_principal(context, tmp_creds.client); @@ -846,7 +831,7 @@ get_cred_from_kdc_flags(krb5_context context,  	if(strcmp(tgt_inst, server_realm) == 0)  	    break;  	krb5_free_principal(context, tmp_creds.server); -	ret = krb5_make_principal(context, &tmp_creds.server,  +	ret = krb5_make_principal(context, &tmp_creds.server,  				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);  	if(ret) {  	    krb5_free_principal(context, tmp_creds.server); @@ -860,27 +845,17 @@ get_cred_from_kdc_flags(krb5_context context,  	    return ret;  	}      } -	 +      krb5_free_principal(context, tmp_creds.server);      krb5_free_principal(context, tmp_creds.client);      *out_creds = calloc(1, sizeof(**out_creds));      if(*out_creds == NULL) { -	krb5_set_error_string(context, "malloc: out of memory");  	ret = ENOMEM; +	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));      } else { -	krb5_boolean noaddr; - -	krb5_appdefault_boolean(context, NULL, tgt->server->realm, -				"no-addresses", KRB5_ADDRESSLESS_DEFAULT, -				&noaddr); -	if (noaddr) -	    ret = get_cred_kdc (context, ccache, flags, NULL, -				in_creds, tgt, NULL, NULL, -				*out_creds); -	else -	    ret = get_cred_kdc_la(context, ccache, flags,  -				  in_creds, tgt, NULL, NULL, -				  *out_creds); +	ret = get_cred_kdc_address (context, ccache, flags, NULL, +				    in_creds, tgt, impersonate_principal, +				    second_ticket, *out_creds);  	if (ret) {  	    free (*out_creds);  	    *out_creds = NULL; @@ -890,34 +865,259 @@ get_cred_from_kdc_flags(krb5_context context,      return ret;  } -krb5_error_code KRB5_LIB_FUNCTION -krb5_get_cred_from_kdc_opt(krb5_context context, -			   krb5_ccache ccache, -			   krb5_creds *in_creds, -			   krb5_creds **out_creds, -			   krb5_creds ***ret_tgts, -			   krb5_flags flags) +/* +get_cred(server) +	creds = cc_get_cred(server) +	if(creds) return creds +	tgt = cc_get_cred(krbtgt/server_realm@any_realm) +	if(tgt) +		return get_cred_tgt(server, tgt) +	if(client_realm == server_realm) +		return NULL +	tgt = get_cred(krbtgt/server_realm@client_realm) +	while(tgt_inst != server_realm) +		tgt = get_cred(krbtgt/server_realm@tgt_inst) +	return get_cred_tgt(server, tgt) +	*/ + +static krb5_error_code +get_cred_kdc_capath(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) +{ +    krb5_error_code ret; +    krb5_const_realm client_realm, server_realm, try_realm; + +    client_realm = krb5_principal_get_realm(context, in_creds->client); +    server_realm = krb5_principal_get_realm(context, in_creds->server); + +    try_realm = client_realm; +    ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, try_realm, +                                     impersonate_principal, second_ticket, out_creds, +                                     ret_tgts); + +    if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) { +        try_realm = krb5_config_get_string(context, NULL, "capaths", +                                           client_realm, server_realm, NULL); + +        if (try_realm != NULL && strcmp(try_realm, client_realm)) { +            ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, +                                             try_realm, impersonate_principal, +                                             second_ticket, out_creds, ret_tgts); +        } +    } + +    return ret; +} + +static krb5_error_code +get_cred_kdc_referral(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)  { -    krb5_kdc_flags f; -    f.i = flags; -    return get_cred_from_kdc_flags(context, f, ccache,  -				   in_creds, NULL, NULL, -				   out_creds, ret_tgts); +    krb5_const_realm client_realm; +    krb5_error_code ret; +    krb5_creds tgt, referral, ticket; +    int loop = 0; +    int ok_as_delegate = 1; + +    if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) { +	krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED, +			       N_("Name too short to do referals, skipping", "")); +	return KRB5KDC_ERR_PATH_NOT_ACCEPTED; +    } + +    memset(&tgt, 0, sizeof(tgt)); +    memset(&ticket, 0, sizeof(ticket)); + +    flags.b.canonicalize = 1; + +    *out_creds = NULL; + +    client_realm = krb5_principal_get_realm(context, in_creds->client); + +    /* find tgt for the clients base realm */ +    { +	krb5_principal tgtname; + +	ret = krb5_make_principal(context, &tgtname, +				  client_realm, +				  KRB5_TGS_NAME, +				  client_realm, +				  NULL); +	if(ret) +	    return ret; + +	ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt); +	krb5_free_principal(context, tgtname); +	if (ret) +	    return ret; +    } + +    referral = *in_creds; +    ret = krb5_copy_principal(context, in_creds->server, &referral.server); +    if (ret) { +	krb5_free_cred_contents(context, &tgt); +	return ret; +    } +    ret = krb5_principal_set_realm(context, referral.server, client_realm); +    if (ret) { +	krb5_free_cred_contents(context, &tgt); +	krb5_free_principal(context, referral.server); +	return ret; +    } + +    while (loop++ < 17) { +	krb5_creds **tickets; +	krb5_creds mcreds; +	char *referral_realm; + +	/* Use cache if we are not doing impersonation or contrainte deleg */ +	if (impersonate_principal == NULL || flags.b.constrained_delegation) { +	    krb5_cc_clear_mcred(&mcreds); +	    mcreds.server = referral.server; +	    ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket); +	} else +	    ret = EINVAL; + +	if (ret) { +	    ret = get_cred_kdc_address(context, ccache, flags, NULL, +				       &referral, &tgt, impersonate_principal, +				       second_ticket, &ticket); +	    if (ret) +		goto out; +	} + +	/* Did we get the right ticket ? */ +	if (krb5_principal_compare_any_realm(context, +					     referral.server, +					     ticket.server)) +	    break; + +	if (!krb5_principal_is_krbtgt(context, ticket.server)) { +	    krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US, +				   N_("Got back an non krbtgt " +				      "ticket referrals", "")); +	    ret = KRB5KRB_AP_ERR_NOT_US; +	    goto out; +	} + +	referral_realm = ticket.server->name.name_string.val[1]; + +	/* check that there are no referrals loops */ +	tickets = *ret_tgts; + +	krb5_cc_clear_mcred(&mcreds); +	mcreds.server = ticket.server; + +	while(tickets && *tickets){ +	    if(krb5_compare_creds(context, +				  KRB5_TC_DONT_MATCH_REALM, +				  &mcreds, +				  *tickets)) +	    { +		krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, +				       N_("Referral from %s " +					  "loops back to realm %s", ""), +				       tgt.server->realm, +				       referral_realm); +		ret = KRB5_GET_IN_TKT_LOOP; +                goto out; +	    } +	    tickets++; +	} + +	/* +	 * if either of the chain or the ok_as_delegate was stripped +	 * by the kdc, make sure we strip it too. +	 */ + +	if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) { +	    ok_as_delegate = 0; +	    ticket.flags.b.ok_as_delegate = 0; +	} + +	ret = add_cred(context, &ticket, ret_tgts); +	if (ret) +	    goto out; + +	/* try realm in the referral */ +	ret = krb5_principal_set_realm(context, +				       referral.server, +				       referral_realm); +	krb5_free_cred_contents(context, &tgt); +	tgt = ticket; +	memset(&ticket, 0, sizeof(ticket)); +	if (ret) +	    goto out; +    } + +    ret = krb5_copy_creds(context, &ticket, out_creds); + +out: +    krb5_free_principal(context, referral.server); +    krb5_free_cred_contents(context, &tgt); +    krb5_free_cred_contents(context, &ticket); +    return ret;  } -krb5_error_code KRB5_LIB_FUNCTION -krb5_get_cred_from_kdc(krb5_context context, + +/* + * Glue function between referrals version and old client chasing + * codebase. + */ + +krb5_error_code +_krb5_get_cred_kdc_any(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)  { -    return krb5_get_cred_from_kdc_opt(context, ccache,  -				      in_creds, out_creds, ret_tgts, 0); +    krb5_error_code ret; +    krb5_deltat offset; + +    ret = krb5_cc_get_kdc_offset(context, ccache, &offset); +    if (ret) { +	context->kdc_sec_offset = offset; +	context->kdc_usec_offset = 0; +    } + +    ret = get_cred_kdc_referral(context, +				flags, +				ccache, +				in_creds, +				impersonate_principal, +				second_ticket, +				out_creds, +				ret_tgts); +    if (ret == 0 || flags.b.canonicalize) +	return ret; +    return get_cred_kdc_capath(context, +				flags, +				ccache, +				in_creds, +				impersonate_principal, +				second_ticket, +				out_creds, +				ret_tgts);  } -      -krb5_error_code KRB5_LIB_FUNCTION + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  krb5_get_credentials_with_flags(krb5_context context,  				krb5_flags options,  				krb5_kdc_flags flags, @@ -929,18 +1129,25 @@ krb5_get_credentials_with_flags(krb5_context context,      krb5_creds **tgts;      krb5_creds *res_creds;      int i; -     + +    if (in_creds->session.keytype) { +	ret = krb5_enctype_valid(context, in_creds->session.keytype); +	if (ret) +	    return ret; +    } +      *out_creds = NULL;      res_creds = calloc(1, sizeof(*res_creds));      if (res_creds == NULL) { -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM;      }      if (in_creds->session.keytype)  	options |= KRB5_TC_MATCH_KEYTYPE; -    /*  +    /*       * If we got a credential, check if credential is expired before       * returning it.       */ @@ -949,7 +1156,7 @@ krb5_get_credentials_with_flags(krb5_context context,                                  in_creds->session.keytype ?                                  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.       */ @@ -961,7 +1168,7 @@ krb5_get_credentials_with_flags(krb5_context context,              *out_creds = res_creds;              return 0;          } -	     +  	krb5_timeofday(context, &timeret);  	if(res_creds->times.endtime > timeret) {  	    *out_creds = res_creds; @@ -975,18 +1182,17 @@ krb5_get_credentials_with_flags(krb5_context context,          return ret;      }      free(res_creds); -    if(options & KRB5_GC_CACHED) { -	not_found(context, in_creds->server); -        return KRB5_CC_NOTFOUND; -    } +    if(options & KRB5_GC_CACHED) +	return not_found(context, in_creds->server, KRB5_CC_NOTFOUND); +      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, NULL, NULL, out_creds, &tgts); +    ret = _krb5_get_cred_kdc_any(context, flags, ccache, +				 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]); @@ -997,7 +1203,7 @@ krb5_get_credentials_with_flags(krb5_context context,      return ret;  } -krb5_error_code KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  krb5_get_credentials(krb5_context context,  		     krb5_flags options,  		     krb5_ccache ccache, @@ -1018,27 +1224,32 @@ struct krb5_get_creds_opt_data {  }; -krb5_error_code KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  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"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM;      }      return 0;  } -void KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION void KRB5_LIB_CALL  krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)  {      if (opt->self)  	krb5_free_principal(context, opt->self); +    if (opt->ticket) { +	free_Ticket(opt->ticket); +	free(opt->ticket); +    }      memset(opt, 0, sizeof(*opt));      free(opt);  } -void KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION void KRB5_LIB_CALL  krb5_get_creds_opt_set_options(krb5_context context,  			       krb5_get_creds_opt opt,  			       krb5_flags options) @@ -1046,7 +1257,7 @@ krb5_get_creds_opt_set_options(krb5_context context,      opt->options = options;  } -void KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION void KRB5_LIB_CALL  krb5_get_creds_opt_add_options(krb5_context context,  			       krb5_get_creds_opt opt,  			       krb5_flags options) @@ -1054,7 +1265,7 @@ krb5_get_creds_opt_add_options(krb5_context context,      opt->options |= options;  } -void KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION void KRB5_LIB_CALL  krb5_get_creds_opt_set_enctype(krb5_context context,  			       krb5_get_creds_opt opt,  			       krb5_enctype enctype) @@ -1062,7 +1273,7 @@ krb5_get_creds_opt_set_enctype(krb5_context context,      opt->enctype = enctype;  } -krb5_error_code KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  krb5_get_creds_opt_set_impersonate(krb5_context context,  				   krb5_get_creds_opt opt,  				   krb5_const_principal self) @@ -1072,7 +1283,7 @@ krb5_get_creds_opt_set_impersonate(krb5_context context,      return krb5_copy_principal(context, self, &opt->self);  } -krb5_error_code KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  krb5_get_creds_opt_set_ticket(krb5_context context,  			      krb5_get_creds_opt opt,  			      const Ticket *ticket) @@ -1087,14 +1298,16 @@ krb5_get_creds_opt_set_ticket(krb5_context context,  	opt->ticket = malloc(sizeof(*ticket));  	if (opt->ticket == NULL) { -	    krb5_set_error_string(context, "malloc: out of memory"); +	    krb5_set_error_message(context, ENOMEM, +				   N_("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"); +	    krb5_set_error_message(context, ret, +				   N_("malloc: out of memory", ""));  	    return ret;  	}      } @@ -1103,7 +1316,7 @@ krb5_get_creds_opt_set_ticket(krb5_context context, -krb5_error_code KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  krb5_get_creds(krb5_context context,  	       krb5_get_creds_opt opt,  	       krb5_ccache ccache, @@ -1117,7 +1330,13 @@ krb5_get_creds(krb5_context context,      krb5_creds **tgts;      krb5_creds *res_creds;      int i; -     + +    if (opt && opt->enctype) { +	ret = krb5_enctype_valid(context, opt->enctype); +	if (ret) +	    return ret; +    } +      memset(&in_creds, 0, sizeof(in_creds));      in_creds.server = rk_UNCONST(inprinc); @@ -1125,31 +1344,35 @@ krb5_get_creds(krb5_context context,      if (ret)  	return ret; -    options = opt->options; +    if (opt) +	options = opt->options; +    else +	options = 0;      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"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM;      } -    if (opt->enctype) { +    if (opt && 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, +				options & KRB5_TC_MATCH_KEYTYPE,                                  &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.       */ @@ -1160,14 +1383,14 @@ krb5_get_creds(krb5_context context,          if(options & KRB5_GC_EXPIRED_OK) {              *out_creds = res_creds;  	    krb5_free_principal(context, in_creds.client); -            return 0; +            goto out;          } -	     +  	krb5_timeofday(context, &timeret);  	if(res_creds->times.endtime > timeret) {  	    *out_creds = res_creds;  	    krb5_free_principal(context, in_creds.client); -	    return 0; +            goto out;  	}  	if(options & KRB5_GC_CACHED)  	    krb5_cc_remove_cred(context, ccache, 0, res_creds); @@ -1175,13 +1398,13 @@ krb5_get_creds(krb5_context context,      } else if(ret != KRB5_CC_END) {          free(res_creds);  	krb5_free_principal(context, in_creds.client); -        return ret; +	goto out;      }      free(res_creds);      if(options & KRB5_GC_CACHED) { -	not_found(context, in_creds.server);  	krb5_free_principal(context, in_creds.client); -        return KRB5_CC_NOTFOUND; +	ret = not_found(context, in_creds.server, KRB5_CC_NOTFOUND); +	goto out;      }      if(options & KRB5_GC_USER_USER) {  	flags.b.enc_tkt_in_skey = 1; @@ -1195,11 +1418,13 @@ krb5_get_creds(krb5_context context,  	flags.b.request_anonymous = 1; /* XXX ARGH confusion */  	flags.b.constrained_delegation = 1;      } +    if (options & KRB5_GC_CANONICALIZE) +	flags.b.canonicalize = 1;      tgts = NULL; -    ret = get_cred_from_kdc_flags(context, flags, ccache,  -				  &in_creds, opt->self, opt->ticket, -				  out_creds, &tgts); +    ret = _krb5_get_cred_kdc_any(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]); @@ -1208,6 +1433,10 @@ krb5_get_creds(krb5_context context,      free(tgts);      if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)  	krb5_cc_store_cred(context, ccache, *out_creds); + + out: +    _krb5_debug(context, 5, "krb5_get_creds: ret = %d", ret); +      return ret;  } @@ -1215,7 +1444,7 @@ krb5_get_creds(krb5_context context,   *   */ -krb5_error_code KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  krb5_get_renewed_creds(krb5_context context,  		       krb5_creds *creds,  		       krb5_const_principal client, @@ -1241,7 +1470,7 @@ krb5_get_renewed_creds(krb5_context context,  	}      } else {  	const char *realm = krb5_principal_get_realm(context, client); -	 +  	ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,  				  realm, NULL);  	if (ret) { | 
