/* * lib/kdb/t_kdb.c * * Copyright 1995 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. 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. * */ /* * t_kdb.c - Test [and optionally obtain timing information about] the * Kerberos database functions. */ #define KDB5_DISPATCH #include "k5-int.h" #include #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 */ #define T_KDB_N_PASSES 100 #define T_KDB_DEF_DB "test_db" #define MAX_PNAME_LEN 1024 #define MAX_PRINC_COMPS 8 #define MAX_COMP_SIZE 32 #define RANDOM(a,b) (a + (RAND() % (b-a))) enum dbtype { DB_UFO, DB_DEFAULT, DB_BERKELEY, DB_DBM }; char *programname = (char *) NULL; krb5_data mprinc_data_entries[] = { { 0, sizeof("master")-1, "master"}, { 0, sizeof("key")-1, "key"} }; krb5_principal_data master_princ_data = { 0, /* Magic number */ { 0, sizeof("test.realm")-1, "test.realm"}, /* Realm */ mprinc_data_entries, /* Name/instance */ sizeof(mprinc_data_entries)/ sizeof(mprinc_data_entries[0]), /* Number */ KRB5_NT_SRV_INST /* Type */ }; struct timeval tstart_time, tend_time; struct timezone dontcare; krb5_principal *recorded_principals = (krb5_principal *) NULL; char **recorded_names = (char **) NULL; extern DBM *db_dbm_open PROTOTYPE((char *, int, int)); extern void db_dbm_close PROTOTYPE((DBM *)); extern datum db_dbm_fetch PROTOTYPE((DBM *, datum)); extern datum db_dbm_firstkey PROTOTYPE((DBM *)); extern datum db_dbm_nextkey PROTOTYPE((DBM *)); extern int db_dbm_delete PROTOTYPE((DBM *, datum)); extern int db_dbm_store PROTOTYPE((DBM *, datum, datum, int)); extern int db_dbm_error PROTOTYPE((DBM *)); extern int db_dbm_clearerr PROTOTYPE((DBM *)); extern int db_dbm_dirfno PROTOTYPE((DBM *)); static kdb5_dispatch_table berkeley_dispatch = { "Berkeley Hashed Database", ".db", /* Index file name ext */ (char *) NULL, /* Data file name ext */ ".ok", /* Lock file name ext */ db_dbm_open, /* Open Database */ db_dbm_close, /* Close Database */ db_dbm_fetch, /* Fetch Key */ db_dbm_firstkey, /* Fetch First Key */ db_dbm_nextkey, /* Fetch Next Key */ db_dbm_delete, /* Delete Key */ db_dbm_store, /* Store Key */ db_dbm_error, /* Get Database Error */ db_dbm_clearerr, /* Clear Database Error */ db_dbm_dirfno, /* Get Database FD num */ (int (*)()) NULL /* Get Database FD num */ }; /* * The following prototypes are necessary in case dbm_error and * dbm_clearerr are in the library but not prototyped * (e.g. NetBSD-1.0) */ #ifdef MISSING_ERROR_PROTO int dbm_error PROTOTYPE((DBM *)); #endif #ifdef MISSING_CLEARERR_PROTO int dbm_clearerr PROTOTYPE((DBM *)); #endif static kdb5_dispatch_table dbm_dispatch = { "Stock [N]DBM Database", ".dir", /* Index file name ext */ ".pag", /* Data file name ext */ ".ok", /* Lock file name ext */ dbm_open, /* Open Database */ dbm_close, /* Close Database */ dbm_fetch, /* Fetch Key */ dbm_firstkey, /* Fetch First Key */ dbm_nextkey, /* Fetch Next Key */ dbm_delete, /* Delete Key */ dbm_store, /* Store Key */ /* * The following are #ifdef'd because they have the potential to be * macros rather than functions. */ #ifdef dbm_error (int (*)()) NULL, /* Get Database Error */ #else /* dbm_error */ #ifdef HAVE_DBM_ERROR dbm_error, /* Get Database Error */ #else (int (*)()) NULL, /* Get Database Error */ #endif #endif /* dbm_error */ #ifdef dbm_clearerr (int (*)()) NULL, /* Clear Database Error */ #else /* dbm_clearerr */ #ifdef HAVE_DBM_CLEARERR dbm_clearerr, /* Clear Database Error */ #else (int (*)()) NULL, /* Clear Database Error */ #endif #endif /* dbm_clearerr */ #ifdef dbm_dirfno (int (*)()) NULL, /* Get Database FD num */ #else /* dbm_dirfno */ dbm_dirfno, /* Get Database FD num */ #endif /* dbm_dirfno */ #ifdef dbm_pagfno (int (*)()) NULL, /* Get Database FD num */ #else /* dbm_pagfno */ dbm_pagfno, /* Get Database FD num */ #endif /* dbm_pagfno */ }; /* * Timer macros. */ #define swatch_on() ((void) gettimeofday(&tstart_time, &dontcare)) #define swatch_eltime() ((gettimeofday(&tend_time, &dontcare)) ? -1.0 : \ (((float) (tend_time.tv_sec - \ tstart_time.tv_sec)) + \ (((float) (tend_time.tv_usec - \ tstart_time.tv_usec))/1000000.0))) /* * Free all principals and names in the recorded names list. */ static void free_principals(kcontext, nentries) krb5_context kcontext; int nentries; { int i; if (recorded_principals) { for (i=0; imod_princ)) kret = KRB5_PRINC_NOMATCH; } krb5_db_free_principal(kcontext, &dbent, how_many); krb5_free_principal(kcontext, mod_princ->mod_princ); krb5_xfree(mod_princ); if (!kret) return(((how_many == 1) && (more == 0)) ? 0 : KRB5KRB_ERR_GENERIC); else return(kret); } /* * Delete a principal. */ static krb5_error_code delete_principal(kcontext, principal) krb5_context kcontext; krb5_principal principal; { krb5_error_code kret; int num2delete; num2delete = 1; if (kret = krb5_db_delete_principal(kcontext, principal, &num2delete)) return(kret); return((num2delete == 1) ? 0 : KRB5KRB_ERR_GENERIC); } static int do_testing(db, passes, verbose, timing, rcases, check, save_db, dontclean, db_type, ptest) char *db; int passes; int verbose; int timing; int rcases; int check; int save_db; int dontclean; enum dbtype db_type; int ptest; { krb5_error_code kret; krb5_context kcontext; char *op, *linkage, *oparg; krb5_principal master_princ; char *mkey_name; char *realm; char *mkey_fullname; char *master_passwd; krb5_data salt_data; krb5_encrypt_block master_encblock; krb5_keyblock master_keyblock; krb5_data passwd; krb5_pointer rseed; krb5_boolean db_open, db_created; int passno; krb5_principal principal; char *pname; float elapsed; krb5_keyblock stat_kb; mkey_name = "master/key"; realm = master_princ_data.realm.data; mkey_fullname = (char *) NULL; master_princ = (krb5_principal) NULL; master_passwd = "master_password"; db_open = 0; db_created = 0; linkage = ""; oparg = ""; /* Set up some initial context */ krb5_init_context(&kcontext); krb5_init_ets(kcontext); switch (db_type) { case DB_BERKELEY: op = "setting up Berkeley database operations"; if (kret = kdb5_db_set_dbops(kcontext, &berkeley_dispatch)) goto goodbye; break; case DB_DBM: op = "setting up DBM database operations"; if (kret = kdb5_db_set_dbops(kcontext, &dbm_dispatch)) goto goodbye; break; case DB_DEFAULT: break; default: op = "checking database type"; kret = EINVAL; goto goodbye; break; } /* * The database had better not exist. */ op = "making sure database doesn't exist"; if (!(kret = krb5_db_set_name(kcontext, db))) { kret = EEXIST; goto goodbye; } /* Set up the master key name */ op = "setting up master key name"; if (kret = krb5_db_setup_mkey_name(kcontext, mkey_name, realm, &mkey_fullname, &master_princ)) goto goodbye; if (verbose) fprintf(stdout, "%s: Initializing '%s', master key is '%s'\n", programname, db, mkey_fullname); op = "salting master key"; if (kret = krb5_principal2salt(kcontext, master_princ, &salt_data)) goto goodbye; op = "converting master key"; krb5_use_enctype(kcontext, &master_encblock, DEFAULT_KDC_ENCTYPE); master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; passwd.length = strlen(master_passwd); passwd.data = master_passwd; if (kret = krb5_string_to_key(kcontext, &master_encblock, &master_keyblock, &passwd, &salt_data)) goto goodbye; /* Clean up */ free(salt_data.data); /* Process master key */ op = "processing master key"; if (kret = krb5_process_key(kcontext, &master_encblock, &master_keyblock)) goto goodbye; /* Initialize random key generator */ op = "initializing random key generator"; if (kret = krb5_init_random_key(kcontext, &master_encblock, &master_keyblock, &rseed)) goto goodbye; /* Create database */ op = "creating database"; if (kret = krb5_db_create(kcontext, db)) goto goodbye; db_created = 1; /* Set this database as active. */ op = "setting active database"; if (kret = krb5_db_set_name(kcontext, db)) goto goodbye; /* Initialize database */ op = "initializing database"; if (kret = krb5_db_init(kcontext)) goto goodbye; db_open = 1; op = "adding master principal"; if (kret = add_principal(kcontext, master_princ, &master_encblock, &master_keyblock, rseed)) goto goodbye; stat_kb.enctype = DEFAULT_KDC_ENCTYPE; stat_kb.length = 8; stat_kb.contents = (krb5_octet *) "helpmeee"; /* We are now ready to proceed to test. */ if (verbose) fprintf(stdout, "%s: Beginning %stest\n", programname, (rcases) ? "random " : ""); init_princ_recording(kcontext, passes); if (rcases) { struct tacc { float t_time; int t_number; } accumulated[3]; int i, nvalid, discrim, highwater, coinflip; krb5_keyblock *kbp; /* Generate random cases */ for (i=0; i<3; i++) { accumulated[i].t_time = 0.0; accumulated[i].t_number = 0; } /* * Generate principal names. */ if (verbose > 1) fprintf(stdout, "%s: generating %d names\n", programname, passes); for (passno=0; passno passes) nvalid = passes; if (verbose > 1) fprintf(stdout, "%s: priming database with %d principals\n", programname, nvalid); highwater = 0; for (passno=0; passno 4) fprintf(stderr, "*A(%s)\n", playback_name(passno)); highwater++; } if (verbose > 1) fprintf(stderr, "%s: beginning random loop\n", programname); /* Loop through some number of times and pick random operations */ for (i=0; i<3*passes; i++) { discrim = RANDOM(0,100); /* Add a principal 25% of the time, if possible */ if ((discrim < 25) && (nvalid < passes)) { op = "adding principal"; coinflip = RANDOM(0,2); kbp = (coinflip) ? &stat_kb : (krb5_keyblock *) NULL; if (timing) { swatch_on(); } if (kret = add_principal(kcontext, playback_principal(nvalid), &master_encblock, kbp, rseed)) { oparg = playback_name(nvalid); goto cya; } if (timing) { elapsed = swatch_eltime(); accumulated[0].t_time += elapsed; accumulated[0].t_number++; } if (verbose > 4) fprintf(stderr, "*A(%s)\n", playback_name(nvalid)); nvalid++; if (nvalid > highwater) highwater = nvalid; } /* Delete a principal 15% of the time, if possible */ else if ((discrim > 85) && (nvalid > 10)) { op = "deleting principal"; if (timing) { swatch_on(); } if (kret = delete_principal(kcontext, playback_principal(nvalid-1))) { oparg = playback_name(nvalid-1); goto cya; } if (timing) { elapsed = swatch_eltime(); accumulated[2].t_time += elapsed; accumulated[2].t_number++; } if (verbose > 4) fprintf(stderr, "XD(%s)\n", playback_name(nvalid-1)); nvalid--; } /* Otherwise, find a principal */ else { op = "looking up principal"; passno = RANDOM(0, nvalid); if (timing) { swatch_on(); } if (kret = find_principal(kcontext, playback_principal(passno), check)) { oparg = playback_name(passno); goto cya; } if (timing) { elapsed = swatch_eltime(); accumulated[1].t_time += elapsed; accumulated[1].t_number++; } if (verbose > 4) fprintf(stderr, "-S(%s)\n", playback_name(passno)); } } if (!dontclean) { /* Clean up the remaining principals */ if (verbose > 1) fprintf(stdout, "%s: deleting remaining %d principals\n", programname, nvalid); for (passno=0; passno 4) fprintf(stderr, "XD(%s)\n", playback_name(passno)); } } cya: if (verbose) fprintf(stdout, "%s: highwater mark was %d principals\n", programname, highwater); if (accumulated[0].t_number && timing) fprintf(stdout, "%s: performed %8d additions in %9.4f seconds (%9.4f/add)\n", programname, accumulated[0].t_number, accumulated[0].t_time, accumulated[0].t_time / (float) accumulated[0].t_number); if (accumulated[1].t_number && timing) fprintf(stdout, "%s: performed %8d lookups in %9.4f seconds (%9.4f/search)\n", programname, accumulated[1].t_number, accumulated[1].t_time, accumulated[1].t_time / (float) accumulated[1].t_number); if (accumulated[2].t_number && timing) fprintf(stdout, "%s: performed %8d deletions in %9.4f seconds (%9.4f/delete)\n", programname, accumulated[2].t_number, accumulated[2].t_time, accumulated[2].t_time / (float) accumulated[2].t_number); if (kret) goto goodbye; } else { /* * Generate principal names. */ for (passno=0; passno 4) fprintf(stderr, "*A(%s)\n", playback_name(passno)); } if (timing) { elapsed = swatch_eltime(); fprintf(stdout, "%s: added %d principals in %9.4f seconds (%9.4f/add)\n", programname, passes, elapsed, elapsed/((float) passes)); } /* * Lookup principals. */ if (timing) { swatch_on(); } for (passno=0; passno 4) fprintf(stderr, "-S(%s)\n", playback_name(passno)); } if (timing) { elapsed = swatch_eltime(); fprintf(stdout, "%s: found %d principals in %9.4f seconds (%9.4f/search)\n", programname, passes, elapsed, elapsed/((float) passes)); } /* * Delete principals. */ if (!dontclean) { if (timing) { swatch_on(); } for (passno=passes-1; passno>=0; passno--) { op = "deleting principal"; if (kret = delete_principal(kcontext, playback_principal(passno))) goto goodbye; if (verbose > 4) fprintf(stderr, "XD(%s)\n", playback_name(passno)); } if (timing) { elapsed = swatch_eltime(); fprintf(stdout, "%s: deleted %d principals in %9.4f seconds (%9.4f/delete)\n", programname, passes, elapsed, elapsed/((float) passes)); } } } goodbye: if (kret) fprintf(stderr, "%s: error while %s %s%s(%s)\n", programname, op, linkage, oparg, error_message(kret)); if (!kret && ptest) { int nper; pid_t children[32], child; int nprocs, existat, i, j, fd; nprocs = ptest + 1; if (nprocs > 32) nprocs = 32; nper = passes / nprocs; unlink("./test.lock"); for (i=0; i 4) fprintf(stderr, "*A[%d](%s)\n", getpid(), playback_name(base+j)); } for (j=0; (j 4) fprintf(stderr, "-S[%d](%s)\n", getpid(), playback_name(base+j)); } for (j=0; (j 4) fprintf(stderr, "XD[%d](%s)\n", getpid(), playback_name(base+j)); } krb5_db_fini(ccontext); krb5_free_context(ccontext); exit((kret) ? 1 : 0); } else children[i] = child; } fd = open("./test.lock", O_CREAT|O_RDWR|O_EXCL, 0666); close(fd); sleep(1); unlink("./test.lock"); for (i=0; i] - Use as the number of passes. * [-c] - Check contents. * [-v] - Verbose output. * [-d ] - Database name. * [-s] - Save database even on successful completion. * [-D] - Leave database dirty. * [-o] - Use dbm instead of default. * [-O] - Use Berkeley db instead of default. */ int main(argc, argv) int argc; char *argv[]; { int option; extern char *optarg; int do_time, do_random, num_passes, check_cont, verbose, error; int save_db, dont_clean, do_ptest; enum dbtype db_type; char *db_name; programname = argv[0]; if (strrchr(programname, (int) '/')) programname = strrchr(programname, (int) '/') + 1; SRAND((RAND_TYPE)time((void *) NULL)); /* Default values. */ do_time = 0; do_random = 0; num_passes = T_KDB_N_PASSES; check_cont = 0; verbose = 0; db_name = T_KDB_DEF_DB; save_db = 0; dont_clean = 0; db_type = DB_DEFAULT; error = 0; do_ptest = 0; /* Parse argument list */ while ((option = getopt(argc, argv, "cd:n:oprstvDO")) != EOF) { switch (option) { case 'c': check_cont = 1; break; case 'd': db_name = optarg; break; case 'n': if (sscanf(optarg, "%d", &num_passes) != 1) { fprintf(stderr, "%s: %s is not a valid number for %c option\n", programname, optarg, option); error++; } break; case 'p': do_ptest++; break; case 'r': do_random = 1; break; case 's': save_db = 1; break; case 't': do_time = 1; break; case 'v': verbose++; break; case 'D': dont_clean = 1; break; case 'o': db_type = DB_DBM; break; case 'O': db_type = DB_BERKELEY; break; default: error++; break; } } if (error) fprintf(stderr, "%s: usage is %s [-cprstv] [-d ] [-n ]\n", programname, programname); else error = do_testing(db_name, num_passes, verbose, do_time, do_random, check_cont, save_db, dont_clean, db_type, do_ptest); return(error); }