t_keytab.c   [plain text]


/*
 * lib/krb5/keytab/t_keytab.c
 *
 * Copyright (C) 2007 by the Massachusetts Institute of Technology.
 * All rights reserved.
 *
 * Export of this software from the United States of America may
 *   require a specific license from the United States Government.
 *   It is the responsibility of any person or organization contemplating
 *   export to obtain such a license before exporting.
 * 
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of M.I.T. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T. software.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 * 
 * 
 *
 * A set of tests for the keytab interface
 */


#include "k5-int.h"
#include "autoconf.h"
#include <stdio.h>
#include <errno.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>


int debug=0;

extern const krb5_kt_ops krb5_ktf_writable_ops;

#define KRB5_OK 0

#define CHECK(kret,msg) \
     if (kret != KRB5_OK) {\
	  com_err(msg, kret, ""); \
          fflush(stderr);\
          exit(1);\
     } else if(debug) printf("%s went ok\n", msg);


#define CHECK_STR(str,msg) \
     if (str == 0) {\
	  com_err(msg, kret, "");\
          exit(1);\
     } else if(debug) printf("%s went ok\n", msg);

static void test_misc(krb5_context context)
{
  /* Tests for certain error returns */
  krb5_error_code	kret;
  krb5_keytab ktid;
  char defname[BUFSIZ];
  char *name;

  fprintf(stderr, "Testing miscellaneous error conditions\n");

  kret = krb5_kt_resolve(context, "unknown_method_ep:/tmp/name", &ktid);
  if (kret != KRB5_KT_UNKNOWN_TYPE) {
    CHECK(kret, "resolve unknown type");
  }

  /* Test length limits on krb5_kt_default_name */
  kret = krb5_kt_default_name(context, defname, sizeof(defname));
  CHECK(kret, "krb5_kt_default_name error");

  /* Now allocate space - without the null... */
  name = malloc(strlen(defname));
  if(!name) {
	  fprintf(stderr, "Out of memory in testing\n");
	  exit(1);
  }
  kret = krb5_kt_default_name(context, name, strlen(defname));
  free(name);
  if (kret != KRB5_CONFIG_NOTENUFSPACE) {
	  CHECK(kret, "krb5_kt_default_name limited");
  }
}

static void kt_test(krb5_context context, const char *name)
{
     krb5_error_code kret;
     krb5_keytab kt;
     const char *type;
     char buf[BUFSIZ];
     char *p;
     krb5_keytab_entry kent, kent2;
     krb5_principal princ;
     krb5_kt_cursor cursor, cursor2;
     int cnt;

     kret = krb5_kt_resolve(context, name, &kt);
     CHECK(kret, "resolve");

     type = krb5_kt_get_type(context, kt);
     CHECK_STR(type, "getting kt type");
     printf("  Type is: %s\n", type);

     kret = krb5_kt_get_name(context, kt, buf, sizeof(buf));
     CHECK(kret, "get_name");
     printf("  Name is: %s\n", buf);

     /* Check that length checks fail */
     /* The buffer is allocated too small - to allow for valgrind test of
	overflows
     */
     p = malloc(strlen(buf));
     kret = krb5_kt_get_name(context, kt, p, 1);
     if(kret != KRB5_KT_NAME_TOOLONG) {
	     CHECK(kret, "get_name - size 1");
     }


     kret = krb5_kt_get_name(context, kt, p, strlen(buf));
     if(kret != KRB5_KT_NAME_TOOLONG) {
	     CHECK(kret, "get_name");
     }
     free(p);

     /* Try to lookup unknown principal - when keytab does not exist*/
     kret = krb5_parse_name(context, "test/test2@TEST.MIT.EDU", &princ);
     CHECK(kret, "parsing principal");

     
     kret = krb5_kt_get_entry(context, kt, princ, 0, 0, &kent);
     if((kret != KRB5_KT_NOTFOUND) && (kret != ENOENT)) {
	     CHECK(kret, "Getting non-existant entry");
     }


     /* ===================   Add entries to keytab ================= */
     /*
      * Add the following for this principal
      * enctype 1, kvno 1, key = "1"
      * enctype 2, kvno 1, key = "1"
      * enctype 1, kvno 2, key = "2"
      */
     memset(&kent, 0, sizeof(kent));
     kent.magic = KV5M_KEYTAB_ENTRY;
     kent.principal = princ;
     kent.timestamp = 327689;
     kent.vno = 1;
     kent.key.magic = KV5M_KEYBLOCK;
     kent.key.enctype = 1;
     kent.key.length = 1;
     kent.key.contents = (krb5_octet *) "1";


     kret = krb5_kt_add_entry(context, kt, &kent);
     CHECK(kret, "Adding initial entry");

     kent.key.enctype = 2;
     kret = krb5_kt_add_entry(context, kt, &kent);
     CHECK(kret, "Adding second entry");

     kent.key.enctype = 1;
     kent.vno = 2;
     kent.key.contents = (krb5_octet *) "2";
     kret = krb5_kt_add_entry(context, kt, &kent);
     CHECK(kret, "Adding third entry");
     
     /* Free memory */
     krb5_free_principal(context, princ);

     /* ==============   Test iterating over contents of keytab ========= */

     kret = krb5_kt_start_seq_get(context, kt, &cursor);
     CHECK(kret, "Start sequence get");


     memset(&kent, 0, sizeof(kent));
     cnt = 0;
     while((kret = krb5_kt_next_entry(context, kt, &kent, &cursor)) == 0) {
	     if(((kent.vno != 1) && (kent.vno != 2)) || 
		((kent.key.enctype != 1) && (kent.key.enctype != 2)) || 
		(kent.key.length != 1) ||
		(kent.key.contents[0] != kent.vno +'0')) {
		     fprintf(stderr, "Error in read contents\n");
		     exit(1);
	     }

	     if((kent.magic != KV5M_KEYTAB_ENTRY) || 
		(kent.key.magic != KV5M_KEYBLOCK)) {
		     fprintf(stderr, "Magic number in sequence not proper\n");
		     exit(1);
	     }

	     cnt++;
	     krb5_free_keytab_entry_contents(context, &kent);
     }
     if (kret != KRB5_KT_END) {
	     CHECK(kret, "getting next entry");
     }

     if(cnt != 3) {
	     fprintf(stderr, "Mismatch in number of entries in keytab");
     }

     kret = krb5_kt_end_seq_get(context, kt, &cursor);
     CHECK(kret, "End sequence get");


     /* ==========================   get_entry tests ============== */

     /* Try to lookup unknown principal  - now that keytab exists*/
     kret = krb5_parse_name(context, "test3/test2@TEST.MIT.EDU", &princ);
     CHECK(kret, "parsing principal");

     
     kret = krb5_kt_get_entry(context, kt, princ, 0, 0, &kent);
     if((kret != KRB5_KT_NOTFOUND)) {
	     CHECK(kret, "Getting non-existant entry");
     }

     krb5_free_principal(context, princ);

     /* Try to lookup known principal */
     kret = krb5_parse_name(context, "test/test2@TEST.MIT.EDU", &princ);
     CHECK(kret, "parsing principal");

     kret = krb5_kt_get_entry(context, kt, princ, 0, 0, &kent);
     CHECK(kret, "looking up principal");

     /* Ensure a valid answer  - we did not specify an enctype or kvno */
     if (!krb5_principal_compare(context, princ, kent.principal) ||
	 ((kent.vno != 1) && (kent.vno != 2)) ||
	 ((kent.key.enctype != 1) && (kent.key.enctype != 2)) || 
	 (kent.key.length != 1) ||
	 (kent.key.contents[0] != kent.vno +'0')) {
	     fprintf(stderr, "Retrieved principal does not check\n");
	     exit(1);
     }

     krb5_free_keytab_entry_contents(context, &kent);

     /* Try to lookup a specific enctype - but unspecified kvno - should give
      * max kvno 
      */
     kret = krb5_kt_get_entry(context, kt, princ, 0, 1, &kent);
     CHECK(kret, "looking up principal");

     /* Ensure a valid answer  - we did specified an enctype */
     if (!krb5_principal_compare(context, princ, kent.principal) ||
	 (kent.vno != 2) || (kent.key.enctype != 1) ||
	 (kent.key.length != 1) ||
	 (kent.key.contents[0] != kent.vno +'0')) {
	     fprintf(stderr, "Retrieved principal does not check\n");

	     exit(1);

     }

     krb5_free_keytab_entry_contents(context, &kent);

     /* Try to lookup unspecified enctype, but a specified kvno */

     kret = krb5_kt_get_entry(context, kt, princ, 2, 0, &kent);
     CHECK(kret, "looking up principal");

     /* Ensure a valid answer  - we did not specify a kvno */
     if (!krb5_principal_compare(context, princ, kent.principal) ||
	 (kent.vno != 2) || (kent.key.enctype != 1) ||
	 (kent.key.length != 1) ||
	 (kent.key.contents[0] != kent.vno +'0')) {
	     fprintf(stderr, "Retrieved principal does not check\n");

	     exit(1);

     }

     krb5_free_keytab_entry_contents(context, &kent);



     /* Try to lookup specified enctype and kvno */

     kret = krb5_kt_get_entry(context, kt, princ, 1, 1, &kent);
     CHECK(kret, "looking up principal");

     if (!krb5_principal_compare(context, princ, kent.principal) ||
	 (kent.vno != 1) || (kent.key.enctype != 1) ||
	 (kent.key.length != 1) ||
	 (kent.key.contents[0] != kent.vno +'0')) {
	     fprintf(stderr, "Retrieved principal does not check\n");

	     exit(1);

     }

     krb5_free_keytab_entry_contents(context, &kent);


     /* Try lookup with active iterators.  */
     kret = krb5_kt_start_seq_get(context, kt, &cursor);
     CHECK(kret, "Start sequence get(2)");
     kret = krb5_kt_start_seq_get(context, kt, &cursor2);
     CHECK(kret, "Start sequence get(3)");
     kret = krb5_kt_next_entry(context, kt, &kent, &cursor);
     CHECK(kret, "getting next entry(2)");
     krb5_free_keytab_entry_contents(context, &kent);
     kret = krb5_kt_next_entry(context, kt, &kent, &cursor);
     CHECK(kret, "getting next entry(3)");
     kret = krb5_kt_next_entry(context, kt, &kent2, &cursor2);
     CHECK(kret, "getting next entry(4)");
     krb5_free_keytab_entry_contents(context, &kent2);
     kret = krb5_kt_get_entry(context, kt, kent.principal, 0, 0, &kent2);
     CHECK(kret, "looking up principal(2)");
     krb5_free_keytab_entry_contents(context, &kent2);
     kret = krb5_kt_next_entry(context, kt, &kent2, &cursor2);
     CHECK(kret, "getting next entry(5)");
     if (!krb5_principal_compare(context, kent.principal, kent2.principal)) {
	 fprintf(stderr, "iterators not in sync\n");
	 exit(1);
     }
     krb5_free_keytab_entry_contents(context, &kent);
     krb5_free_keytab_entry_contents(context, &kent2);
     kret = krb5_kt_next_entry(context, kt, &kent, &cursor);
     CHECK(kret, "getting next entry(6)");
     kret = krb5_kt_next_entry(context, kt, &kent2, &cursor2);
     CHECK(kret, "getting next entry(7)");
     krb5_free_keytab_entry_contents(context, &kent);
     krb5_free_keytab_entry_contents(context, &kent2);
     kret = krb5_kt_end_seq_get(context, kt, &cursor);
     CHECK(kret, "ending sequence get(1)");
     kret = krb5_kt_end_seq_get(context, kt, &cursor2);
     CHECK(kret, "ending sequence get(2)");

     /* Try to lookup specified enctype and kvno  - that does not exist*/

     kret = krb5_kt_get_entry(context, kt, princ, 3, 1, &kent);
     if(kret != KRB5_KT_KVNONOTFOUND) {
	     CHECK(kret, "looking up specific principal, kvno, enctype");
     }

     krb5_free_principal(context, princ);


     /* =========================   krb5_kt_remove_entry =========== */
     /* Lookup the keytab entry w/ 2 kvno - and delete version 2 - 
	ensure gone */
     kret = krb5_parse_name(context, "test/test2@TEST.MIT.EDU", &princ);
     CHECK(kret, "parsing principal");

     kret = krb5_kt_get_entry(context, kt, princ, 0, 1, &kent);
     CHECK(kret, "looking up principal");

     /* Ensure a valid answer  - we are looking for max(kvno) and enc=1 */
     if (!krb5_principal_compare(context, princ, kent.principal) ||
	 (kent.vno != 2) || (kent.key.enctype != 1) ||
	 (kent.key.length != 1) ||
	 (kent.key.contents[0] != kent.vno +'0')) {
	     fprintf(stderr, "Retrieved principal does not check\n");

	     exit(1);

     }

     /* Delete it */
     kret = krb5_kt_remove_entry(context, kt, &kent);
     CHECK(kret, "Removing entry");

     krb5_free_keytab_entry_contents(context, &kent);
     /* And ensure gone */

     kret = krb5_kt_get_entry(context, kt, princ, 0, 1, &kent);
     CHECK(kret, "looking up principal");

     /* Ensure a valid answer - kvno should now be 1 - we deleted 2 */
     if (!krb5_principal_compare(context, princ, kent.principal) ||
	 (kent.vno != 1) || (kent.key.enctype != 1) ||
	 (kent.key.length != 1) ||
	 (kent.key.contents[0] != kent.vno +'0')) {
	     fprintf(stderr, "Delete principal check failed\n");

	     exit(1);

     }
     krb5_free_keytab_entry_contents(context, &kent);

     krb5_free_principal(context, princ);
     
     /* =======================  Finally close =======================  */

     kret = krb5_kt_close(context, kt);
     CHECK(kret, "close");

}

static void do_test(krb5_context context, const char *prefix, 
		    krb5_boolean delete)
{
  char *name, *filename;

  if (asprintf(&filename, "/tmp/kttest.%ld", (long) getpid()) < 0) {
      perror("asprintf");
      exit(1);
  }
  if (asprintf(&name, "%s%s", prefix, filename) < 0) {
      perror("asprintf");
      exit(1);
  }
  printf("Starting test on %s\n", name);
  kt_test(context, name);
  printf("Test on %s passed\n", name);
  if(delete)
	  unlink(filename);
  free(filename);
  free(name);

}

int 
main (void)
{
	krb5_context context;
	krb5_error_code	kret;


	if ((kret = krb5_init_context(&context))) {
		printf("Couldn't initialize krb5 library: %s\n",
		       error_message(kret));
		exit(1);
	}

	/* All keytab types are registered by default -- test for 
	   redundant error */
	kret = krb5_kt_register(context, &krb5_ktf_writable_ops);
	if(kret && kret != KRB5_KT_TYPE_EXISTS) {
		CHECK(kret, "register ktf_writable");
	}

	test_misc(context);
	do_test(context, "WRFILE:", FALSE);
	do_test(context, "MEMORY:", TRUE);

	krb5_free_context(context);
	return 0;

}


#if 0
/* remove and add are functions, so that they can return NOWRITE
   if not a writable keytab */
krb5_error_code KRB5_CALLCONV krb5_kt_remove_entry
	(krb5_context,
		krb5_keytab,
		krb5_keytab_entry * );



#endif