#include <stdio.h>
#include <krb5.h>
#include <kadm5/admin.h>

#if	HAVE_SRAND48
#define	RAND()		lrand48()
#define	SRAND(a)	srand48(a)
#define	RAND_TYPE	long
#elif	HAVE_SRAND
#define	RAND()		rand()
#define	SRAND(a)	srand(a)
#define	RAND_TYPE	int
#elif	HAVE_SRANDOM
#define	RAND()		random()
#define	SRAND(a)	srandom(a)
#define	RAND_TYPE	long
#else	/* no random */
need a random number generator
#endif	/* no random */

krb5_keyblock test1[] = {
     0, ENCTYPE_DES_CBC_CRC, 0, 0,
     -1,
};
krb5_keyblock test2[] = {
     0, ENCTYPE_DES_CBC_RAW, 0, 0,
     -1,
};
krb5_keyblock test3[] = {
     0, ENCTYPE_DES_CBC_MD5, 0, 0,
     -1,
};

krb5_keyblock *tests[] = { 
  test1, test2, test3, NULL
};

int keyblocks_equal(krb5_keyblock *kb1, krb5_keyblock *kb2)
{
  return (kb1->enctype == kb2->enctype &&
	  kb1->length == kb2->length &&
	  memcmp(kb1->contents, kb2->contents, kb1->length) == 0);
}

krb5_data tgtname = {
    0,
    KRB5_TGS_NAME_SIZE,
    KRB5_TGS_NAME
};

unsigned int ktypes[] = { 0, 0 };

extern krb5_kt_ops krb5_ktf_writable_ops;

main(int argc, char **argv)
{
  krb5_context context;
  krb5_keytab kt;
  krb5_keytab_entry ktent;
  krb5_encrypt_block eblock;
  krb5_creds my_creds;
  kadm5_principal_ent_rec princ_ent;
  krb5_principal princ, server;
  char pw[16];
  char *whoami, *principal, *authprinc;
  krb5_data pwdata;
  void *handle;
  int ret, i, test, encnum;

  whoami = argv[0];

  if (argc != 2 && argc != 3) {
    fprintf(stderr, "Usage: %s principal [authuser]\n", whoami);
    exit(1);
  }
  principal = argv[1];
  authprinc = argv[2] ? argv[2] : argv[0];

  /*
   * Setup.  Initialize data structures, open keytab, open connection
   * to kadm5 server.
   */

  memset((char *) &context, 0, sizeof(context));
  krb5_init_context(&context);

  ret = krb5_parse_name(context, principal, &princ);
  if (ret) {
    com_err(whoami, ret, "while parsing principal name %s", principal);
    exit(1);
  }
    
  if((ret = krb5_build_principal_ext(context, &server,
				     krb5_princ_realm(kcontext, princ)->length,
				     krb5_princ_realm(kcontext, princ)->data,
				     tgtname.length, tgtname.data,
				     krb5_princ_realm(kcontext, princ)->length,
				     krb5_princ_realm(kcontext, princ)->data,
				     0))) {
       com_err(whoami, ret, "while building server name");
       exit(1);
  }

  /* register the WRFILE keytab type  */
  if (ret = krb5_kt_register(context, &krb5_ktf_writable_ops)) {
       com_err(whoami, ret,
	       "while registering writable key table functions");
       exit(1);
  }

  ret = krb5_kt_default(context, &kt);
  if (ret) {
       com_err(whoami, ret, "while opening keytab");
       exit(1);
  }

  ret = kadm5_init(authprinc, NULL, KADM5_ADMIN_SERVICE, NULL,
		   KADM5_STRUCT_VERSION, KADM5_API_VERSION_2,
		   &handle);
  if (ret) {
    com_err(whoami, ret, "while initializing connection");
    exit(1);
  }

  /* these pw's don't need to be secure, just different every time */
  SRAND((RAND_TYPE)time((void *) NULL));
  pwdata.data = pw;
  pwdata.length = sizeof(pw);
  
  /*
   * For each test:
   *
   * For each enctype in the test, construct a random password/key.
   * Assign all keys to principal with kadm5_setkey_principal.  Add
   * each key to the keytab, and acquire an initial ticket with the
   * keytab (XXX can I specify the enctype & kvno explicitly?).  If
   * krb5_get_in_tkt_with_keytab succeeds, then the keys were set
   * successfully.
   */
  for (test = 0; tests[test] != NULL; test++) {
       krb5_keyblock *testp = tests[test];
       printf("+ Test %d:\n", test);

       for (encnum = 0; testp[encnum].magic != -1; encnum++) {
	    for (i = 0; i < sizeof(pw); i++)
		 pw[i] = (RAND() % 26) + '0'; /* XXX */

	    krb5_use_enctype(context, &eblock, testp[encnum].enctype);
	    if (ret = krb5_string_to_key(context, &eblock, &testp[encnum],
					 &pwdata, NULL)) {
		 com_err(whoami, ret, "while converting string to key");
		 exit(1);
	    }
       }
       
       /* now, encnum == # of keyblocks in testp */
       ret = kadm5_setkey_principal(handle, princ, testp, encnum);
       if (ret) {
	    com_err(whoami, ret, "while setting keys");
	    exit(1);
       }

       ret = kadm5_get_principal(handle, princ, &princ_ent, KADM5_KVNO);
       if (ret) {
	    com_err(whoami, ret, "while retrieving principal");
	    exit(1);
       }

       for (encnum = 0; testp[encnum].magic != -1; encnum++) {
	    printf("+   enctype %d\n", testp[encnum].enctype);
		   
	    memset((char *) &ktent, 0, sizeof(ktent));
	    ktent.principal = princ;
	    ktent.key = testp[encnum];
	    ktent.vno = princ_ent.kvno;

	    ret = krb5_kt_add_entry(context, kt, &ktent);
	    if (ret) {
		 com_err(whoami, ret, "while adding keytab entry");
		 exit(1);
	    }

	    memset((char *)&my_creds, 0, sizeof(my_creds));
	    my_creds.client = princ;
	    my_creds.server = server;
	    
	    ktypes[0] = testp[encnum].enctype;
	    ret = krb5_get_in_tkt_with_keytab(context,
					      0 /* options */,
					      NULL /* addrs */,
					      ktypes,
					      NULL /* preauth */,
					      kt, 0,
					      &my_creds, 0);
	    if (ret) {
		 com_err(whoami, ret, "while acquiring initial ticket");
		 exit(1);
	    }

	    /* since I can't specify enctype explicitly ... */
	    ret = krb5_kt_remove_entry(context, kt, &ktent);
	    if (ret) {
		 com_err(whoami, ret, "while removing keytab entry");
		 exit(1);
	    }
       }
  }
  
  ret = krb5_kt_close(context, kt);
  if (ret) {
       com_err(whoami, ret, "while closing keytab");
       exit(1);
  }

  ret = kadm5_destroy(handle);
  if (ret) {
       com_err(whoami, ret, "while closing kadmin connection");
       exit(1);
  }

  return 0;
}

  
  
    
    
  
