#include "k5-int.h"

static krb5_error_code
krb5_get_as_key_keytab(context, client, etype, prompter, prompter_data,
		       salt, as_key, gak_data)
     krb5_context context;
     krb5_principal client;
     krb5_enctype etype;
     krb5_prompter_fct prompter;
     void *prompter_data;
     krb5_data *salt;
     krb5_keyblock *as_key;
     void *gak_data;
{
    krb5_keytab keytab = (krb5_keytab) gak_data;
    krb5_error_code ret;
    krb5_keytab_entry kt_ent;
    krb5_keyblock *kt_key;

    /* if there's already a key of the correct etype, we're done.
       if the etype is wrong, free the existing key, and make
       a new one. */

    if (as_key->length) {
	if (as_key->enctype == etype)
	    return(0);

	krb5_free_keyblock(context, as_key);
	as_key->length = 0;
    }

    if (!valid_enctype(etype))
	return(KRB5_PROG_ETYPE_NOSUPP);

    if ((ret = krb5_kt_get_entry(context, keytab, client,
				 0, /* don't have vno available */
				 etype, &kt_ent)))
	return(ret);

    ret = krb5_copy_keyblock(context, &kt_ent.key, &kt_key);

    /* again, krb5's memory management is lame... */

    *as_key = *kt_key;
    krb5_xfree(kt_key);

    (void) krb5_kt_free_entry(context, &kt_ent);

    return(ret);
}

KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_keytab(context, creds, client, arg_keytab,
			   start_time, in_tkt_service, options)
     krb5_context context;
     krb5_creds *creds;
     krb5_principal client;
     krb5_keytab arg_keytab;
     krb5_deltat start_time;
     char *in_tkt_service;
     krb5_get_init_creds_opt *options;
{
   krb5_error_code ret, ret2;
   int use_master;
   krb5_keytab keytab;

   if (arg_keytab == NULL) {
       if (ret = krb5_kt_default(context, &keytab))
	   return ret;
   } else {
       keytab = arg_keytab;
   }

   use_master = 0;

   /* first try: get the requested tkt from any kdc */

   ret = krb5_get_init_creds(context, creds, client, NULL, NULL,
			     start_time, in_tkt_service, options,
			     krb5_get_as_key_keytab, (void *) keytab,
			     use_master,NULL);

   /* check for success */

   if (ret == 0)
      goto cleanup;

   /* If all the kdc's are unavailable fail */

   if ((ret == KRB5_KDC_UNREACH) || (ret == KRB5_REALM_CANT_RESOLVE))
      goto cleanup;

   /* if the reply did not come from the master kdc, try again with
      the master kdc */

   if (!use_master) {
      use_master = 1;

      ret2 = krb5_get_init_creds(context, creds, client, NULL, NULL,
				 start_time, in_tkt_service, options,
				 krb5_get_as_key_keytab, (void *) keytab,
				 use_master, NULL);
      
      if (ret2 == 0) {
	 ret = 0;
	 goto cleanup;
      }

      /* if the master is unreachable, return the error from the
	 slave we were able to contact */

      if ((ret2 == KRB5_KDC_UNREACH) || (ret == KRB5_REALM_CANT_RESOLVE))
	 goto cleanup;

      ret = ret2;
   }

   /* at this point, we have a response from the master.  Since we don't
      do any prompting or changing for keytabs, that's it. */

cleanup:
   if (arg_keytab == NULL)
       krb5_kt_close(context, keytab);

   return(ret);
}
